@go-labs-sg/bb 1.0.4 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -12
- package/dist/commands.js +398 -16
- package/dist/filter-enums.js +58 -0
- package/dist/index.js +500 -66
- package/dist/parse-args.js +3 -1
- package/dist/parse-cli-enums.js +216 -0
- package/dist/parse-json-flag.js +20 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,19 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import "./load-env.js";
|
|
3
3
|
import { requireApiKey } from "./api-client.js";
|
|
4
|
-
import { approveBill, getBudget, listApprovals, listBills, listBudgets, listSuppliers, updateBudgetStatus, } from "./commands.js";
|
|
4
|
+
import { addBudgetItems, approveBill, approveBudget, approveSupplier, createBillApproval, createBudgetApproval, createCompany, createProject, getApprovedBudgets, getBillAttachments, getBillDetails, getBudget, getBudgetCategories, getBudgetCategoryBenchmarks, getBudgetDetails, getBudgetItemsOnly, getBudgetVersions, getCompany, getDashboard, getErrorMetrics, getFinancialOverview, getItem, getItemPricingHistory, getProject, getRecentErrors, getSupplierAnalytics, getSupplierDetails, getSupplierPricingHistory, getUserPerformance, listApprovals, listBills, listBudgets, listCompanies, listContacts, listItemCategories, listItems, listProjects, listSuppliers, listUsers, patchBillInvoiceNumber, patchBillPayment, rejectBill, rejectBudget, rejectSupplier, removeBudgetItem, updateBillStatus, updateBudgetItem, updateBudgetStatus, updateProjectStatus, } from "./commands.js";
|
|
5
5
|
import { getFlag, parseArgs } from "./parse-args.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"PENDING_APPROVAL",
|
|
9
|
-
"APPROVED",
|
|
10
|
-
"REJECTED",
|
|
11
|
-
"ESTIMATE_CREATED",
|
|
12
|
-
"ESTIMATE_SENT",
|
|
13
|
-
"ESTIMATE_ACCEPTED",
|
|
14
|
-
"ESTIMATE_REJECTED",
|
|
15
|
-
"ESTIMATE_CLOSED",
|
|
16
|
-
];
|
|
6
|
+
import { billStatusesForUpdateHelp, budgetStatusesForHelp, isBudgetStatusUpdate, parseApprovalTypeFlag, parseBillStatusForUpdate, parseCommaSeparatedBillStatuses, parseCommaSeparatedBudgetStatuses, parseCommaSeparatedIds, parseOptionalBillListSortBy, parseOptionalBillListSortDir, parseOptionalDashboardRole, parseOptionalDeals, parseOptionalErrorSeverity, parseOptionalErrorStatus, parseOptionalExtendedProjectStatus, parseOptionalFinancialRole, parseOptionalSupplierAnalyticsTimeFrame, parseOptionalTimeFrame, parseProjectStatusForUpdate, projectStatusesForHelp, } from "./parse-cli-enums.js";
|
|
7
|
+
import { parseJsonFlag, parseOptionalNumber as parseOptNum, } from "./parse-json-flag.js";
|
|
17
8
|
function parsePositiveIntFlag(value, flagName) {
|
|
18
9
|
if (value === undefined)
|
|
19
10
|
return undefined;
|
|
@@ -23,59 +14,152 @@ function parsePositiveIntFlag(value, flagName) {
|
|
|
23
14
|
}
|
|
24
15
|
return parsed;
|
|
25
16
|
}
|
|
17
|
+
function parseOptionalBoolFlag(flags, key) {
|
|
18
|
+
const v = flags[key];
|
|
19
|
+
if (v === undefined || v === true)
|
|
20
|
+
return undefined;
|
|
21
|
+
const s = String(v).toLowerCase();
|
|
22
|
+
if (s === "true" || s === "1")
|
|
23
|
+
return true;
|
|
24
|
+
if (s === "false" || s === "0")
|
|
25
|
+
return false;
|
|
26
|
+
throw new Error(`--${key} must be true or false.`);
|
|
27
|
+
}
|
|
28
|
+
function parseListBillsFlags(flags) {
|
|
29
|
+
const statusRaw = getFlag(flags, "status");
|
|
30
|
+
const createdByIdsRaw = getFlag(flags, "createdByIds");
|
|
31
|
+
const searchRaw = getFlag(flags, "search");
|
|
32
|
+
const sortByRaw = getFlag(flags, "sortBy");
|
|
33
|
+
const sortDirRaw = getFlag(flags, "sortDir");
|
|
34
|
+
return {
|
|
35
|
+
projectId: getFlag(flags, "projectId"),
|
|
36
|
+
budgetId: getFlag(flags, "budgetId"),
|
|
37
|
+
statuses: parseCommaSeparatedBillStatuses(statusRaw !== undefined ? String(statusRaw) : undefined),
|
|
38
|
+
search: searchRaw !== undefined ? String(searchRaw) : undefined,
|
|
39
|
+
isClaimable: parseOptionalBoolFlag(flags, "isClaimable"),
|
|
40
|
+
createdByIds: parseCommaSeparatedIds(createdByIdsRaw !== undefined ? String(createdByIdsRaw) : undefined),
|
|
41
|
+
sortBy: parseOptionalBillListSortBy(sortByRaw !== undefined ? String(sortByRaw) : undefined),
|
|
42
|
+
sortDir: parseOptionalBillListSortDir(sortDirRaw !== undefined ? String(sortDirRaw) : undefined),
|
|
43
|
+
page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
|
|
44
|
+
pageSize: parsePositiveIntFlag(getFlag(flags, "pageSize"), "--pageSize"),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
26
47
|
function printHelp() {
|
|
27
48
|
const help = `
|
|
28
|
-
bb — Budget Builder CLI (
|
|
49
|
+
bb — Budget Builder CLI (parity with MCP tools)
|
|
29
50
|
|
|
30
51
|
Usage: bb <command> [options] [args]
|
|
31
52
|
|
|
32
53
|
Auth: Set BB_API_KEY in the environment (same API key as MCP / Goracle).
|
|
33
54
|
|
|
34
|
-
|
|
35
|
-
list-budgets
|
|
36
|
-
|
|
55
|
+
Budgets
|
|
56
|
+
list-budgets [--projectId] [--name] [--status CSV] [--createdBy] [--dateFrom] [--dateTo] [--sortBy] [--sortDir] [--page] [--perPage]
|
|
57
|
+
get-budget <id> Budget + line items
|
|
58
|
+
get-budget-items <id> Line items only
|
|
59
|
+
get-budget-details <id> Full detail (margins, suppliers, etc.)
|
|
60
|
+
get-budget-categories
|
|
61
|
+
get-budget-versions <budgetId>
|
|
62
|
+
update-budget-status <id> <status>
|
|
63
|
+
status: ${budgetStatusesForHelp.join(", ")}
|
|
64
|
+
create-budget-approval <budgetId>
|
|
65
|
+
add-budget-items --budgetId --items '[{"itemId":"…","quantity":1,"markup":30},…]'
|
|
66
|
+
update-budget-item --id <budgetItemId> [--description] [--note] [--quantity] [--markup] [--cost] [--unitPrice] [--isFreeOfCharge true|false] [--gstInclusive true|false]
|
|
67
|
+
remove-budget-item <budgetItemId>
|
|
68
|
+
|
|
69
|
+
Bills
|
|
70
|
+
list-bills [--projectId] [--budgetId] [--status CSV] [--search <text>] [--isClaimable true|false] [--createdByIds <csv>] [--sortBy createdAt|amount|status] [--sortDir asc|desc] [--page] [--pageSize]
|
|
71
|
+
list-claims same flags as list-bills; only reimbursable claims (ignores --isClaimable)
|
|
72
|
+
create-bill-approval <billId>
|
|
73
|
+
update-bill-status <id> <status> [--rejectionReason] [--paymentTrackingUrl] [--paymentReference]
|
|
74
|
+
status (${billStatusesForUpdateHelp.join(", ")}) — not PENDING_APPROVAL; use create-bill-approval
|
|
75
|
+
patch-bill-payment <billId> [--paymentTrackingUrl] [--paymentReference] [--quickbooksBillId]
|
|
76
|
+
patch-bill-invoice-number <billId> <invoiceNumber>
|
|
77
|
+
get-bill-attachments <billId>
|
|
78
|
+
get-bill-details <billId>
|
|
79
|
+
|
|
80
|
+
Approvals
|
|
81
|
+
list-approvals | get-pending-approvals [--type budget|supplier|bill|all]
|
|
82
|
+
approve-bill <billId>
|
|
83
|
+
reject-bill <billId> --reason <text>
|
|
84
|
+
approve-budget <budgetId>
|
|
85
|
+
reject-budget <budgetId> --reason <text>
|
|
86
|
+
approve-supplier <supplierId>
|
|
87
|
+
reject-supplier <supplierId> --reason <text>
|
|
88
|
+
|
|
89
|
+
Companies & projects
|
|
90
|
+
list-companies [--name] [--createdBy] [--page] [--perPage]
|
|
91
|
+
get-company <id>
|
|
92
|
+
create-company --name --website <url>
|
|
93
|
+
list-projects [--companyId] [--name] [--status] [--page] [--perPage]
|
|
94
|
+
get-project <id>
|
|
95
|
+
create-project --name --companyId --contactPersonId --startDate <ISO> [--endDate] [--description]
|
|
96
|
+
update-project-status <id> <status>
|
|
97
|
+
status: ${projectStatusesForHelp.join(", ")}
|
|
37
98
|
|
|
38
|
-
|
|
99
|
+
Contacts
|
|
100
|
+
list-contacts <companyId>
|
|
39
101
|
|
|
40
|
-
|
|
41
|
-
|
|
102
|
+
Suppliers & items
|
|
103
|
+
list-suppliers [--name] [--page] [--perPage]
|
|
104
|
+
get-supplier-details <supplierId>
|
|
105
|
+
get-supplier-analytics [--name] [--page] [--perPage] [--timeFrame ALL|LAST_YEAR|…]
|
|
106
|
+
list-items [--name] [--page] [--perPage]
|
|
107
|
+
get-item <id>
|
|
108
|
+
list-item-categories [--page] [--perPage]
|
|
42
109
|
|
|
43
|
-
|
|
44
|
-
|
|
110
|
+
Dashboard & users
|
|
111
|
+
list-users
|
|
112
|
+
get-user-performance [--userId]
|
|
113
|
+
get-dashboard [--userId] [--role BD|CREATOR|INSIDE_SALES|ALL] [--deals ALL|SUCCESSFUL|LOST] [--timeFrame] [--startDate] [--endDate]
|
|
114
|
+
get-financial-overview [same optional flags as dashboard; role uses BudgetRole enum]
|
|
45
115
|
|
|
46
|
-
|
|
116
|
+
Errors
|
|
117
|
+
get-recent-errors [--page] [--perPage] [--severity] [--status]
|
|
118
|
+
get-error-metrics
|
|
47
119
|
|
|
48
|
-
|
|
49
|
-
|
|
120
|
+
Historical / benchmarks
|
|
121
|
+
get-approved-budgets [--projectId] [--limit] [--minRevenue] [--maxRevenue]
|
|
122
|
+
get-budget-category-benchmarks <categoryId> [--limit]
|
|
123
|
+
get-item-pricing-history [--itemName] [--categoryId] [--supplierId] [--limit]
|
|
124
|
+
get-supplier-pricing-history <supplierId> [--itemCategoryId] [--limit]
|
|
50
125
|
|
|
51
|
-
|
|
52
|
-
--name, --page, --perPage
|
|
126
|
+
Output: JSON to stdout. Errors to stderr; exit 1 on failure.
|
|
53
127
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
128
|
+
MCP parity: every MCP tool maps to one command above. You may use MCP-style
|
|
129
|
+
snake_case (e.g. bb list_bills) — it is normalized to kebab-case.
|
|
130
|
+
|
|
131
|
+
Note: MCP also exposes a Prisma schema resource; the CLI has no equivalent.
|
|
132
|
+
|
|
133
|
+
Not covered vs MCP get_budget: "get-budget" also fetches line items in one call.
|
|
134
|
+
`.trim();
|
|
135
|
+
console.log(help);
|
|
136
|
+
}
|
|
137
|
+
/** MCP registers tools with snake_case; CLI commands are kebab-case. */
|
|
138
|
+
function normalizeCliCommand(raw) {
|
|
139
|
+
return raw.replace(/_/g, "-");
|
|
57
140
|
}
|
|
58
141
|
async function main() {
|
|
59
142
|
const { command, positional, flags } = parseArgs(process.argv);
|
|
60
|
-
|
|
143
|
+
const cmd = normalizeCliCommand(command ?? "");
|
|
144
|
+
if (!cmd || cmd === "help" || flags.help === true) {
|
|
61
145
|
printHelp();
|
|
62
146
|
process.exit(0);
|
|
63
147
|
}
|
|
64
148
|
requireApiKey();
|
|
65
149
|
try {
|
|
66
|
-
switch (
|
|
150
|
+
switch (cmd) {
|
|
67
151
|
case "list-budgets": {
|
|
68
152
|
const statusStr = getFlag(flags, "status");
|
|
69
|
-
const statuses = statusStr !== undefined
|
|
70
|
-
? String(statusStr)
|
|
71
|
-
.split(",")
|
|
72
|
-
.map((s) => s.trim())
|
|
73
|
-
.filter((s) => s.length > 0)
|
|
74
|
-
: undefined;
|
|
153
|
+
const statuses = parseCommaSeparatedBudgetStatuses(statusStr !== undefined ? String(statusStr) : undefined);
|
|
75
154
|
await listBudgets({
|
|
76
155
|
projectId: getFlag(flags, "projectId"),
|
|
77
156
|
name: getFlag(flags, "name"),
|
|
78
157
|
statuses,
|
|
158
|
+
createdBy: getFlag(flags, "createdBy"),
|
|
159
|
+
dateFrom: getFlag(flags, "dateFrom"),
|
|
160
|
+
dateTo: getFlag(flags, "dateTo"),
|
|
161
|
+
sortBy: getFlag(flags, "sortBy"),
|
|
162
|
+
sortDir: getFlag(flags, "sortDir"),
|
|
79
163
|
page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
|
|
80
164
|
perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage"),
|
|
81
165
|
});
|
|
@@ -83,55 +167,292 @@ async function main() {
|
|
|
83
167
|
}
|
|
84
168
|
case "get-budget": {
|
|
85
169
|
const id = positional[0];
|
|
86
|
-
if (!id)
|
|
87
|
-
|
|
88
|
-
process.exit(1);
|
|
89
|
-
}
|
|
170
|
+
if (!id)
|
|
171
|
+
throw new Error("get-budget requires <id>");
|
|
90
172
|
await getBudget(id);
|
|
91
173
|
break;
|
|
92
174
|
}
|
|
175
|
+
case "get-budget-items": {
|
|
176
|
+
const id = positional[0];
|
|
177
|
+
if (!id)
|
|
178
|
+
throw new Error("get-budget-items requires <budgetId>");
|
|
179
|
+
await getBudgetItemsOnly(id);
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
case "get-budget-details": {
|
|
183
|
+
const id = positional[0];
|
|
184
|
+
if (!id)
|
|
185
|
+
throw new Error("get-budget-details requires <budgetId>");
|
|
186
|
+
await getBudgetDetails(id);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
case "get-budget-categories": {
|
|
190
|
+
await getBudgetCategories();
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
case "get-budget-versions": {
|
|
194
|
+
const id = positional[0];
|
|
195
|
+
if (!id)
|
|
196
|
+
throw new Error("get-budget-versions requires <budgetId>");
|
|
197
|
+
await getBudgetVersions(id);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
93
200
|
case "update-budget-status": {
|
|
94
201
|
const [id, status] = positional;
|
|
95
202
|
if (!id || !status) {
|
|
96
|
-
|
|
97
|
-
error: "update-budget-status requires <id> <status>",
|
|
98
|
-
}));
|
|
99
|
-
process.exit(1);
|
|
203
|
+
throw new Error("update-budget-status requires <id> <status>");
|
|
100
204
|
}
|
|
101
|
-
if (!
|
|
102
|
-
|
|
103
|
-
error: `Invalid status. Use one of: ${BUDGET_STATUSES.join(", ")}`,
|
|
104
|
-
}));
|
|
105
|
-
process.exit(1);
|
|
205
|
+
if (!isBudgetStatusUpdate(status)) {
|
|
206
|
+
throw new Error(`Invalid status. Use one of: ${budgetStatusesForHelp.join(", ")}`);
|
|
106
207
|
}
|
|
107
208
|
await updateBudgetStatus(id, status);
|
|
108
209
|
break;
|
|
109
210
|
}
|
|
211
|
+
case "create-budget-approval": {
|
|
212
|
+
const id = positional[0];
|
|
213
|
+
if (!id)
|
|
214
|
+
throw new Error("create-budget-approval requires <budgetId>");
|
|
215
|
+
await createBudgetApproval(id);
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case "add-budget-items": {
|
|
219
|
+
const budgetId = getFlag(flags, "budgetId");
|
|
220
|
+
if (!budgetId)
|
|
221
|
+
throw new Error("add-budget-items requires --budgetId");
|
|
222
|
+
const items = parseJsonFlag(String(getFlag(flags, "items")), "--items");
|
|
223
|
+
await addBudgetItems({ budgetId: String(budgetId), items });
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
case "update-budget-item": {
|
|
227
|
+
const id = getFlag(flags, "id");
|
|
228
|
+
if (!id)
|
|
229
|
+
throw new Error("update-budget-item requires --id");
|
|
230
|
+
await updateBudgetItem({
|
|
231
|
+
id: String(id),
|
|
232
|
+
description: getFlag(flags, "description"),
|
|
233
|
+
note: getFlag(flags, "note"),
|
|
234
|
+
quantity: parseOptNum(getFlag(flags, "quantity"), "--quantity"),
|
|
235
|
+
markup: parseOptNum(getFlag(flags, "markup"), "--markup"),
|
|
236
|
+
cost: parseOptNum(getFlag(flags, "cost"), "--cost"),
|
|
237
|
+
unitPrice: parseOptNum(getFlag(flags, "unitPrice"), "--unitPrice"),
|
|
238
|
+
isFreeOfCharge: parseOptionalBoolFlag(flags, "isFreeOfCharge"),
|
|
239
|
+
gstInclusive: parseOptionalBoolFlag(flags, "gstInclusive"),
|
|
240
|
+
});
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
case "remove-budget-item": {
|
|
244
|
+
const id = positional[0];
|
|
245
|
+
if (!id)
|
|
246
|
+
throw new Error("remove-budget-item requires <budgetItemId>");
|
|
247
|
+
await removeBudgetItem(id);
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
110
250
|
case "list-bills": {
|
|
251
|
+
await listBills(parseListBillsFlags(flags));
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
case "list-claims": {
|
|
111
255
|
await listBills({
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
status: getFlag(flags, "status"),
|
|
115
|
-
page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
|
|
116
|
-
pageSize: parsePositiveIntFlag(getFlag(flags, "pageSize"), "--pageSize"),
|
|
256
|
+
...parseListBillsFlags(flags),
|
|
257
|
+
isClaimable: true,
|
|
117
258
|
});
|
|
118
259
|
break;
|
|
119
260
|
}
|
|
120
|
-
case "
|
|
261
|
+
case "create-bill-approval": {
|
|
121
262
|
const id = positional[0];
|
|
122
|
-
if (!id)
|
|
123
|
-
|
|
124
|
-
|
|
263
|
+
if (!id)
|
|
264
|
+
throw new Error("create-bill-approval requires <billId>");
|
|
265
|
+
await createBillApproval(id);
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
case "update-bill-status": {
|
|
269
|
+
const [id, statusRaw] = positional;
|
|
270
|
+
if (!id || !statusRaw) {
|
|
271
|
+
throw new Error("update-bill-status requires <billId> <status> [--rejectionReason] [--paymentTrackingUrl] [--paymentReference]");
|
|
125
272
|
}
|
|
126
|
-
|
|
273
|
+
const status = parseBillStatusForUpdate(statusRaw);
|
|
274
|
+
await updateBillStatus({
|
|
275
|
+
id,
|
|
276
|
+
status,
|
|
277
|
+
rejectionReason: getFlag(flags, "rejectionReason"),
|
|
278
|
+
paymentTrackingUrl: getFlag(flags, "paymentTrackingUrl"),
|
|
279
|
+
paymentReference: getFlag(flags, "paymentReference"),
|
|
280
|
+
});
|
|
127
281
|
break;
|
|
128
282
|
}
|
|
129
|
-
case "
|
|
283
|
+
case "patch-bill-payment": {
|
|
284
|
+
const id = positional[0];
|
|
285
|
+
if (!id)
|
|
286
|
+
throw new Error("patch-bill-payment requires <billId>");
|
|
287
|
+
await patchBillPayment({
|
|
288
|
+
id,
|
|
289
|
+
paymentTrackingUrl: getFlag(flags, "paymentTrackingUrl"),
|
|
290
|
+
paymentReference: getFlag(flags, "paymentReference"),
|
|
291
|
+
quickbooksBillId: getFlag(flags, "quickbooksBillId"),
|
|
292
|
+
});
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
case "patch-bill-invoice-number": {
|
|
296
|
+
const [id, inv] = positional;
|
|
297
|
+
if (!id || !inv) {
|
|
298
|
+
throw new Error("patch-bill-invoice-number requires <billId> <invoiceNumber>");
|
|
299
|
+
}
|
|
300
|
+
await patchBillInvoiceNumber(id, inv);
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
case "get-bill-attachments": {
|
|
304
|
+
const id = positional[0];
|
|
305
|
+
if (!id)
|
|
306
|
+
throw new Error("get-bill-attachments requires <billId>");
|
|
307
|
+
await getBillAttachments(id);
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
case "get-bill-details": {
|
|
311
|
+
const id = positional[0];
|
|
312
|
+
if (!id)
|
|
313
|
+
throw new Error("get-bill-details requires <billId>");
|
|
314
|
+
await getBillDetails(id);
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
case "list-approvals":
|
|
318
|
+
case "get-pending-approvals": {
|
|
319
|
+
const typeRaw = getFlag(flags, "type");
|
|
130
320
|
await listApprovals({
|
|
131
|
-
type:
|
|
321
|
+
type: parseApprovalTypeFlag(typeRaw !== undefined ? String(typeRaw) : undefined),
|
|
132
322
|
});
|
|
133
323
|
break;
|
|
134
324
|
}
|
|
325
|
+
case "approve-bill": {
|
|
326
|
+
const id = positional[0];
|
|
327
|
+
if (!id)
|
|
328
|
+
throw new Error("approve-bill requires <billId>");
|
|
329
|
+
await approveBill(id);
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
case "reject-bill": {
|
|
333
|
+
const id = positional[0];
|
|
334
|
+
const reason = getFlag(flags, "reason");
|
|
335
|
+
if (!id || reason === undefined) {
|
|
336
|
+
throw new Error("reject-bill requires <billId> --reason <text>");
|
|
337
|
+
}
|
|
338
|
+
await rejectBill(id, reason);
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
case "approve-budget": {
|
|
342
|
+
const id = positional[0];
|
|
343
|
+
if (!id)
|
|
344
|
+
throw new Error("approve-budget requires <budgetId>");
|
|
345
|
+
await approveBudget(id);
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
case "reject-budget": {
|
|
349
|
+
const id = positional[0];
|
|
350
|
+
const reason = getFlag(flags, "reason");
|
|
351
|
+
if (!id || reason === undefined) {
|
|
352
|
+
throw new Error("reject-budget requires <budgetId> --reason <text>");
|
|
353
|
+
}
|
|
354
|
+
await rejectBudget(id, reason);
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
case "approve-supplier": {
|
|
358
|
+
const id = positional[0];
|
|
359
|
+
if (!id)
|
|
360
|
+
throw new Error("approve-supplier requires <supplierId>");
|
|
361
|
+
await approveSupplier(id);
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
case "reject-supplier": {
|
|
365
|
+
const id = positional[0];
|
|
366
|
+
const reason = getFlag(flags, "reason");
|
|
367
|
+
if (!id || reason === undefined) {
|
|
368
|
+
throw new Error("reject-supplier requires <supplierId> --reason <text>");
|
|
369
|
+
}
|
|
370
|
+
await rejectSupplier(id, reason);
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
case "get-supplier-details": {
|
|
374
|
+
const id = positional[0];
|
|
375
|
+
if (!id)
|
|
376
|
+
throw new Error("get-supplier-details requires <supplierId>");
|
|
377
|
+
await getSupplierDetails(id);
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
case "list-companies": {
|
|
381
|
+
await listCompanies({
|
|
382
|
+
name: getFlag(flags, "name"),
|
|
383
|
+
createdBy: getFlag(flags, "createdBy"),
|
|
384
|
+
page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
|
|
385
|
+
perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage"),
|
|
386
|
+
});
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
case "get-company": {
|
|
390
|
+
const id = positional[0];
|
|
391
|
+
if (!id)
|
|
392
|
+
throw new Error("get-company requires <id>");
|
|
393
|
+
await getCompany(id);
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
case "create-company": {
|
|
397
|
+
const name = getFlag(flags, "name");
|
|
398
|
+
const website = getFlag(flags, "website");
|
|
399
|
+
if (!name || !website) {
|
|
400
|
+
throw new Error("create-company requires --name and --website <url>");
|
|
401
|
+
}
|
|
402
|
+
await createCompany({ name, website });
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
case "list-projects": {
|
|
406
|
+
const statusRaw = getFlag(flags, "status");
|
|
407
|
+
await listProjects({
|
|
408
|
+
companyId: getFlag(flags, "companyId"),
|
|
409
|
+
name: getFlag(flags, "name"),
|
|
410
|
+
status: parseOptionalExtendedProjectStatus(statusRaw !== undefined ? String(statusRaw) : undefined),
|
|
411
|
+
page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
|
|
412
|
+
perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage"),
|
|
413
|
+
});
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
case "get-project": {
|
|
417
|
+
const id = positional[0];
|
|
418
|
+
if (!id)
|
|
419
|
+
throw new Error("get-project requires <id>");
|
|
420
|
+
await getProject(id);
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
case "create-project": {
|
|
424
|
+
const name = getFlag(flags, "name");
|
|
425
|
+
const companyId = getFlag(flags, "companyId");
|
|
426
|
+
const contactPersonId = getFlag(flags, "contactPersonId");
|
|
427
|
+
const startDate = getFlag(flags, "startDate");
|
|
428
|
+
if (!name || !companyId || !contactPersonId || !startDate) {
|
|
429
|
+
throw new Error("create-project requires --name --companyId --contactPersonId --startDate (ISO)");
|
|
430
|
+
}
|
|
431
|
+
await createProject({
|
|
432
|
+
name,
|
|
433
|
+
companyId,
|
|
434
|
+
contactPersonId,
|
|
435
|
+
startDate,
|
|
436
|
+
endDate: getFlag(flags, "endDate"),
|
|
437
|
+
description: getFlag(flags, "description"),
|
|
438
|
+
});
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
case "update-project-status": {
|
|
442
|
+
const [id, statusRaw] = positional;
|
|
443
|
+
if (!id || !statusRaw) {
|
|
444
|
+
throw new Error("update-project-status requires <id> <status>");
|
|
445
|
+
}
|
|
446
|
+
await updateProjectStatus(id, parseProjectStatusForUpdate(statusRaw));
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
case "list-contacts": {
|
|
450
|
+
const id = positional[0];
|
|
451
|
+
if (!id)
|
|
452
|
+
throw new Error("list-contacts requires <companyId>");
|
|
453
|
+
await listContacts(id);
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
135
456
|
case "list-suppliers": {
|
|
136
457
|
await listSuppliers({
|
|
137
458
|
name: getFlag(flags, "name"),
|
|
@@ -140,11 +461,124 @@ async function main() {
|
|
|
140
461
|
});
|
|
141
462
|
break;
|
|
142
463
|
}
|
|
464
|
+
case "get-supplier-analytics": {
|
|
465
|
+
const tfRaw = getFlag(flags, "timeFrame");
|
|
466
|
+
await getSupplierAnalytics({
|
|
467
|
+
name: getFlag(flags, "name"),
|
|
468
|
+
page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
|
|
469
|
+
perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage"),
|
|
470
|
+
timeFrame: parseOptionalSupplierAnalyticsTimeFrame(tfRaw !== undefined ? String(tfRaw) : undefined),
|
|
471
|
+
});
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
case "list-items": {
|
|
475
|
+
await listItems({
|
|
476
|
+
name: getFlag(flags, "name"),
|
|
477
|
+
page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
|
|
478
|
+
perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage"),
|
|
479
|
+
});
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
case "get-item": {
|
|
483
|
+
const id = positional[0];
|
|
484
|
+
if (!id)
|
|
485
|
+
throw new Error("get-item requires <id>");
|
|
486
|
+
await getItem(id);
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
case "list-item-categories": {
|
|
490
|
+
await listItemCategories({
|
|
491
|
+
page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
|
|
492
|
+
perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage"),
|
|
493
|
+
});
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
case "list-users": {
|
|
497
|
+
await listUsers();
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
case "get-user-performance": {
|
|
501
|
+
await getUserPerformance(getFlag(flags, "userId"));
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
case "get-dashboard": {
|
|
505
|
+
await getDashboard({
|
|
506
|
+
userId: getFlag(flags, "userId"),
|
|
507
|
+
role: parseOptionalDashboardRole(getFlag(flags, "role")),
|
|
508
|
+
deals: parseOptionalDeals(getFlag(flags, "deals")),
|
|
509
|
+
timeFrame: parseOptionalTimeFrame(getFlag(flags, "timeFrame")),
|
|
510
|
+
startDate: getFlag(flags, "startDate"),
|
|
511
|
+
endDate: getFlag(flags, "endDate"),
|
|
512
|
+
});
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
case "get-financial-overview": {
|
|
516
|
+
await getFinancialOverview({
|
|
517
|
+
userId: getFlag(flags, "userId"),
|
|
518
|
+
role: parseOptionalFinancialRole(getFlag(flags, "role")),
|
|
519
|
+
deals: parseOptionalDeals(getFlag(flags, "deals")),
|
|
520
|
+
timeFrame: parseOptionalTimeFrame(getFlag(flags, "timeFrame")),
|
|
521
|
+
startDate: getFlag(flags, "startDate"),
|
|
522
|
+
endDate: getFlag(flags, "endDate"),
|
|
523
|
+
});
|
|
524
|
+
break;
|
|
525
|
+
}
|
|
526
|
+
case "get-recent-errors": {
|
|
527
|
+
await getRecentErrors({
|
|
528
|
+
page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
|
|
529
|
+
perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage"),
|
|
530
|
+
severity: parseOptionalErrorSeverity(getFlag(flags, "severity")),
|
|
531
|
+
status: parseOptionalErrorStatus(getFlag(flags, "status")),
|
|
532
|
+
});
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
case "get-error-metrics": {
|
|
536
|
+
await getErrorMetrics();
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
case "get-approved-budgets": {
|
|
540
|
+
await getApprovedBudgets({
|
|
541
|
+
projectId: getFlag(flags, "projectId"),
|
|
542
|
+
limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit"),
|
|
543
|
+
minRevenue: parseOptNum(getFlag(flags, "minRevenue"), "--minRevenue"),
|
|
544
|
+
maxRevenue: parseOptNum(getFlag(flags, "maxRevenue"), "--maxRevenue"),
|
|
545
|
+
});
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
case "get-budget-category-benchmarks": {
|
|
549
|
+
const id = positional[0];
|
|
550
|
+
if (!id) {
|
|
551
|
+
throw new Error("get-budget-category-benchmarks requires <categoryId> [--limit]");
|
|
552
|
+
}
|
|
553
|
+
await getBudgetCategoryBenchmarks({
|
|
554
|
+
categoryId: id,
|
|
555
|
+
limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit"),
|
|
556
|
+
});
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
case "get-item-pricing-history": {
|
|
560
|
+
await getItemPricingHistory({
|
|
561
|
+
itemName: getFlag(flags, "itemName"),
|
|
562
|
+
categoryId: getFlag(flags, "categoryId"),
|
|
563
|
+
supplierId: getFlag(flags, "supplierId"),
|
|
564
|
+
limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit"),
|
|
565
|
+
});
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
case "get-supplier-pricing-history": {
|
|
569
|
+
const id = positional[0];
|
|
570
|
+
if (!id) {
|
|
571
|
+
throw new Error("get-supplier-pricing-history requires <supplierId> [--itemCategoryId] [--limit]");
|
|
572
|
+
}
|
|
573
|
+
await getSupplierPricingHistory({
|
|
574
|
+
supplierId: id,
|
|
575
|
+
itemCategoryId: getFlag(flags, "itemCategoryId"),
|
|
576
|
+
limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit"),
|
|
577
|
+
});
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
143
580
|
default: {
|
|
144
|
-
|
|
145
|
-
error: `Unknown command: ${command}. Run 'bb help'.`,
|
|
146
|
-
}));
|
|
147
|
-
process.exit(1);
|
|
581
|
+
throw new Error(`Unknown command: ${cmd}. Run 'bb help'.`);
|
|
148
582
|
}
|
|
149
583
|
}
|
|
150
584
|
}
|