@go-labs-sg/bb 1.18.1 → 1.20.0
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 +13 -7
- package/dist/api-client.js +3 -3
- package/dist/cli-trace.js +30 -0
- package/dist/commands.js +373 -13
- package/dist/index.js +358 -18
- package/dist/parse-cli-enums.js +41 -3
- package/dist/parse-mutation-payload.js +62 -0
- package/dist/prisma-enums.js +16 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import "./load-env.js";
|
|
3
3
|
import { requireApiKey } from "./api-client.js";
|
|
4
4
|
import { consumeCliQuietFlags, logCliAction, sanitizeFlagsForTrace, setCliQuiet, shouldLogCliActions, } from "./cli-trace.js";
|
|
5
|
-
import { addBudgetItems, approveBill, approveBudget, approveQuotation, approveSupplier, createApiKeyForUser, createBillApproval, createBillFromPayload, createBudgetApproval, createBudgetCategory, createBudgetFromPayload, createCompany, createContactPersonFromPayload, createEstimate, createItemCategory, createItemFromPayload, createProject, createQuotationFromPayload, createSupplierCertification, createSupplierFromPayload, createSupplierPaymentMethod, createSupplierRoleOption, createSupplierTagOption, createUser, deleteBillById, deleteBudgetById, deleteBudgetCategory, deleteCompanyById, deleteItemById, deleteItemCategoriesByIds, deleteProjectById, deleteSuppliersByIds, downloadQuotationPdf, getApprovedBudgets, getBillAttachments, getBillDetails, getBudget, getBudgetCategories, getBudgetCategoryBenchmarks, getBudgetDetails, getBudgetItemsOnly, getBudgetVersions, getCompany, getDashboard, getErrorMetrics, getEstimatePerformance, getFinancialOverview, getItem, getItemPricingHistory, getMonthlyMetrics, getProject, getQuotationDetails, getRecentErrors, getSupplierAnalytics, getSupplierDetails, getSupplierPricingHistory, getSystemOverview, getUserPerformance, listApiKeysForUser, listApprovals, listBills, listBudgets, listCompanies, listContacts, listItemCategories, listItems, listProjects, listQuotations, listSuppliers, listUsers, markBudgetWonWithProof, patchBillInvoiceNumber, patchBillPayment, reactivateSuppliersByIds, rejectBill, rejectBudget, rejectQuotation, rejectSupplier, removeBudgetItem, reorderBudgetItemsCli, revokeApiKeyForUser, stageBillAttachmentsFromPaths, submitQuotation, updateBillFromPayload, updateBillStatus, updateBudgetCategory, updateBudgetCommissionFromPayload, updateBudgetDiscountFromPayload, updateBudgetFromPayload, updateBudgetItem, updateBudgetItemSupplierCli, updateBudgetStatus, updateCompanyFromPayload, updateContactPersonFromPayload, updateItemCategory, updateItemFromPayload, updateProjectFromPayload, updateProjectStatus, updateSupplierFromPayload, uploadBillAttachmentFromPath, uploadBillAttachmentsFromPaths, uploadBillDocumentsFromPaths, uploadBudgetAttachmentFromPath, uploadQuotationAttachmentFromPath, } from "./commands.js";
|
|
5
|
+
import { addBudgetItems, approveBill, approveBudget, approveCustomerInvoice, approveQuotation, approveSupplier, checkCustomerInvoiceReadiness, checkProjectReconciliation, cleanupStagedBillAttachments, cleanupStagedQuotationAttachments, completeProject, createApiKeyForUser, createBillApproval, createBillFromPayload, createBudgetApproval, createBudgetCategory, createBudgetFromPayload, createCompany, createContactPersonFromPayload, createCustomerInvoice, createEstimate, createItemCategory, createItemFromPayload, createPlaceholderBillForBudgetItem, createProject, createQuotationFromPayload, createSupplierCertification, createSupplierFromPayload, createSupplierPaymentMethod, createSupplierRoleOption, createSupplierTagOption, createUser, deleteBillById, deleteBudgetById, deleteBudgetCategory, deleteBudgetCommission, deleteBudgetDiscount, deleteCompanyById, deleteCustomerInvoice, deleteItemById, deleteItemCategoriesByIds, deleteProjectById, deleteQuotationById, deleteSuppliersByIds, discardCreatingCustomerInvoice, downloadCustomerInvoicePdf, downloadQuotationPdf, getApprovedBudgets, getBillAttachments, getBillDetails, getBudget, getBudgetCategories, getBudgetCategoryBenchmarks, getBudgetDetails, getBudgetItemsOnly, getBudgetVersions, getCompany, getCustomerInvoice, getDashboard, getErrorMetrics, getEstimatePerformance, getFinancialOverview, getItem, getItemPricingHistory, getMonthlyMetrics, getProject, getQuotationDetails, getRecentErrors, getSupplierAnalytics, getSupplierDetails, getSupplierPricingHistory, getSystemOverview, getUserPerformance, importQuickBooksProjectId, listApiKeysForUser, listApprovals, listBills, listBudgets, listCompanies, listContacts, listCustomerInvoices, listIntegrationOperations, listItemCategories, listItems, listProjects, listQuotations, listSuppliers, listUsers, markBudgetWonWithProof, patchBillInvoiceNumber, patchBillPayment, reactivateSuppliersByIds, reconcileProject, rejectBill, rejectBudget, rejectCustomerInvoice, rejectQuotation, rejectSupplier, removeBudgetItem, renameBudgetVersion, reorderBudgetItemsCli, restoreBudgetVersion, retryIntegrationOperation, revokeApiKeyForUser, sendEstimateToContactPersonFromPayload, setBudgetItemsNotUtilized, stageBillAttachmentsFromPaths, submitQuotation, syncCustomerInvoice, updateBillFromPayload, updateBillPaymentEvidenceFromPayload, updateBillStatus, updateBudgetCategory, updateBudgetCommissionFromPayload, updateBudgetDiscountFromPayload, updateBudgetFromPayload, updateBudgetItem, updateBudgetItemSupplierCli, updateBudgetStatus, updateCompanyFromPayload, updateContactPersonFromPayload, updateItemCategory, updateItemFromPayload, updateProjectFromPayload, updateProjectStatus, updateQuotationFromPayload, updateSupplierFromPayload, uploadBillAttachmentFromPath, uploadBillAttachmentsFromPaths, uploadBillDocumentsFromPaths, uploadBudgetAttachmentFromPath, uploadQuotationAttachmentFromPath, validateBillSelectionFromPayload, voidCustomerInvoice, } from "./commands.js";
|
|
6
6
|
import { getFlag, parseArgs } from "./parse-args.js";
|
|
7
|
-
import { billStatusesForUpdateHelp, budgetStatusesForHelp, isBudgetStatusUpdate, parseApprovalTypeFlag, parseBillStatusForUpdate, parseCommaSeparatedBillStatuses, parseCommaSeparatedBudgetStatuses, parseCommaSeparatedIds, parseCommaSeparatedSupplierStatuses, parseOptionalBillListSortBy, parseOptionalBillListSortDir, parseOptionalDashboardRole, parseOptionalDeals, parseOptionalErrorSeverity, parseOptionalErrorStatus, parseOptionalExtendedProjectStatus, parseOptionalFinancialRole, parseOptionalSupplierAnalyticsTimeFrame, parseOptionalTimeFrame, parseProjectStatusForUpdate, parseUserRole, projectStatusesForHelp, userRolesForHelp, } from "./parse-cli-enums.js";
|
|
7
|
+
import { billStatusesForUpdateHelp, budgetStatusesForHelp, isBudgetStatusUpdate, parseApprovalTypeFlag, parseBillStatusForUpdate, parseCommaSeparatedBillStatuses, parseCommaSeparatedBudgetStatuses, parseCommaSeparatedCustomerInvoiceStatuses, parseCommaSeparatedIds, parseCommaSeparatedQuotationStatuses, parseCommaSeparatedSupplierStatuses, parseOptionalBillListSortBy, parseOptionalBillListSortDir, parseOptionalDashboardRole, parseOptionalDeals, parseOptionalErrorSeverity, parseOptionalErrorStatus, parseOptionalExtendedProjectStatus, parseOptionalFinancialRole, parseOptionalSupplierAnalyticsTimeFrame, parseOptionalTimeFrame, parseProjectStatusForUpdate, parseUserRole, projectStatusesForHelp, userRolesForHelp, } from "./parse-cli-enums.js";
|
|
8
8
|
import { parseJsonFlag, parseOptionalNumber as parseOptNum, } from "./parse-json-flag.js";
|
|
9
9
|
function parsePositiveIntFlag(value, flagName) {
|
|
10
10
|
if (value === undefined)
|
|
@@ -26,6 +26,35 @@ function parseOptionalBoolFlag(flags, key) {
|
|
|
26
26
|
return false;
|
|
27
27
|
throw new Error(`--${key} must be true or false.`);
|
|
28
28
|
}
|
|
29
|
+
const CUSTOMER_INVOICE_SORT_FIELDS = [
|
|
30
|
+
"createdAt",
|
|
31
|
+
"status",
|
|
32
|
+
"totalNetAmount",
|
|
33
|
+
"approvalExpiresAt",
|
|
34
|
+
];
|
|
35
|
+
function parseCustomerInvoiceSortBy(value) {
|
|
36
|
+
if (value === undefined)
|
|
37
|
+
return "createdAt";
|
|
38
|
+
if (!CUSTOMER_INVOICE_SORT_FIELDS.includes(value)) {
|
|
39
|
+
throw new Error(`Invalid --sortBy "${value}". Use one of: ${CUSTOMER_INVOICE_SORT_FIELDS.join(", ")}.`);
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
const INTEGRATION_OPERATION_STATUSES = [
|
|
44
|
+
"PENDING",
|
|
45
|
+
"PROCESSING",
|
|
46
|
+
"FAILED",
|
|
47
|
+
"COMPLETED",
|
|
48
|
+
];
|
|
49
|
+
function parseIntegrationOperationStatus(value) {
|
|
50
|
+
if (value === undefined)
|
|
51
|
+
return undefined;
|
|
52
|
+
const normalized = value.toUpperCase();
|
|
53
|
+
if (!INTEGRATION_OPERATION_STATUSES.includes(normalized)) {
|
|
54
|
+
throw new Error(`Invalid --status "${value}". Use one of: ${INTEGRATION_OPERATION_STATUSES.join(", ")}.`);
|
|
55
|
+
}
|
|
56
|
+
return normalized;
|
|
57
|
+
}
|
|
29
58
|
function parseCommercialRejectionProjectStatus(flags) {
|
|
30
59
|
const raw = flags.projectStatusOnCommercialRejection;
|
|
31
60
|
if (raw === undefined)
|
|
@@ -120,8 +149,9 @@ Global: --quiet | -q | BB_CLI_QUIET=1 — hide action logs (default: log command
|
|
|
120
149
|
Auth: Set BB_API_KEY in the environment (same API key as MCP / Goracle).
|
|
121
150
|
|
|
122
151
|
Sensitive workflow changes: approval decisions/requests, entity status updates,
|
|
123
|
-
|
|
124
|
-
|
|
152
|
+
estimate emails, customer invoices, integration retries, mark-budget-won,
|
|
153
|
+
submit-quotation, and supplier approval request side effects require an
|
|
154
|
+
interactive CONFIRM prompt and abort in non-interactive shells.
|
|
125
155
|
AI agents must inspect the entity first, summarize the exact entity, target
|
|
126
156
|
state, and side effects, then wait for explicit user confirmation before running
|
|
127
157
|
the sensitive command.
|
|
@@ -133,6 +163,8 @@ Budgets
|
|
|
133
163
|
get-budget-details <id> Full detail (margins, suppliers, etc.)
|
|
134
164
|
get-budget-categories
|
|
135
165
|
get-budget-versions <budgetId>
|
|
166
|
+
rename-budget-version <versionId> --name <text>
|
|
167
|
+
restore-budget-version <versionId> Replace current budget data and return it to DRAFT.
|
|
136
168
|
update-budget-status <id> <status> [--projectManagerId <id>] [--markProjectWon true|false] [--projectStatusOnCommercialRejection PITCH|LOST]
|
|
137
169
|
Budget-changing commands require interactive "yes" confirmation when the current budget is ESTIMATE_CREATED, ESTIMATE_SENT, ESTIMATE_ACCEPTED, ESTIMATE_REJECTED, or ESTIMATE_CLOSED.
|
|
138
170
|
ESTIMATE_ACCEPTED requires win proof, auto-marks PITCH or LOST projects WON, requires --projectManagerId when the project has no PM, and auto-creates the Asana project/section when enabled.
|
|
@@ -144,6 +176,7 @@ Budgets
|
|
|
144
176
|
delete-budget <budgetId>
|
|
145
177
|
create-budget-approval <budgetId> (also sends approval request emails)
|
|
146
178
|
create-estimate <budgetId> (creates QuickBooks estimate; supports preflight updates)
|
|
179
|
+
send-estimate-to-contact-person --payload '<json>' Send using the web contact-person workflow; payload includes recipient fields plus HTML content/signature.
|
|
147
180
|
add-budget-items --budgetId --items '[{"itemId":"…","quantity":1,"markup":30},…]'
|
|
148
181
|
update-budget-item --id <budgetItemId> [--description plain-text] [--note plain-text] [--quantity] [--markup] [--cost] [--unitPrice] [--isFreeOfCharge true|false] [--gstInclusive true|false] [--gstOutOfScope true|false]
|
|
149
182
|
remove-budget-item <budgetItemId>
|
|
@@ -154,6 +187,11 @@ Budgets
|
|
|
154
187
|
delete-budget-category <id>
|
|
155
188
|
update-budget-commission --payload '<json>' (budget.updateBudgetCommission)
|
|
156
189
|
update-budget-discount --payload '<json>' (budget.updateBudgetDiscount; type percentage|amount)
|
|
190
|
+
delete-budget-commission <budgetId> Remove commission and return the budget to DRAFT.
|
|
191
|
+
delete-budget-discount <budgetId> Remove discount and return the budget to DRAFT.
|
|
192
|
+
mark-budget-items-not-utilized --ids <csv> Remove unused lines from reconciliation and clear QBO placeholders.
|
|
193
|
+
restore-budget-item <budgetItemId> Restore one Not Utilized line and its QBO placeholder when required.
|
|
194
|
+
create-placeholder-bill <budgetItemId> Admin-only placeholder recovery.
|
|
157
195
|
upload-budget-attachment <budgetId> <filePath> Presigned S3 upload + confirm
|
|
158
196
|
|
|
159
197
|
Bills
|
|
@@ -163,12 +201,16 @@ Bills
|
|
|
163
201
|
New supplier bills from 1 Jul 2026 00:00 SGT require approved quotation coverage for each non-legacy line item.
|
|
164
202
|
create-bill-approval <billId> (also sends approval request emails)
|
|
165
203
|
create-bill --payload '<json>' (bill.create; payload.isClaimable false = bill, true = claim)
|
|
204
|
+
validate-bill-selection --payload '<json>' Check projectId, supplierId, budgetItemIds, and optional alreadyPaid before creation.
|
|
166
205
|
stage-bill-attachment <projectId> <filePath...> [--file <path>] [--files <csv>] Upload files before create-bill; returns attachment JSON for attachments/paymentProofAttachments
|
|
206
|
+
cleanup-staged-bill-attachments <projectId> --keys <csv> Delete unattached staged bill/claim uploads.
|
|
167
207
|
update-bill --payload '<json>' (bill.update; must include id)
|
|
208
|
+
update-bill-payment-evidence --payload '<json>' Replace paymentReference/paymentProofAttachments for an already-paid bill.
|
|
168
209
|
delete-bill <billId>
|
|
169
|
-
update-bill-status <id> <status> [--rejectionReason] [--paymentTrackingUrl] [--paymentReference]
|
|
210
|
+
update-bill-status <id> <status> [--rejectionReason] [--paymentTrackingUrl] [--paymentReference] [--paymentProof <receipt.pdf>]
|
|
170
211
|
status (${billStatusesForUpdateHelp.join(", ")}) — not PENDING_APPROVAL; use create-bill-approval
|
|
171
|
-
|
|
212
|
+
PAID requires --paymentReference and a payment-proof PDF. Use --paymentProof to stage and submit the PDF atomically, or omit it only when BB already has a BILL_PAYMENT_PROOF attachment.
|
|
213
|
+
Moving a bill or claim to PAID runs the server's paid-bill notification workflow.
|
|
172
214
|
patch-bill-payment <billId> [--paymentTrackingUrl] [--paymentReference] [--quickbooksBillId] [--paymentDate <ISO>]
|
|
173
215
|
Clear a field: --clearPaymentTrackingUrl true | --clearPaymentReference true | --clearQuickbooksBillId true | --clearPaymentDate true
|
|
174
216
|
patch-bill-invoice-number <billId> <invoiceNumber>
|
|
@@ -179,17 +221,33 @@ Bills
|
|
|
179
221
|
get-bill-details <billId>
|
|
180
222
|
|
|
181
223
|
Quotations
|
|
182
|
-
list-quotations [--projectId] [--budgetId] [--supplierId] [--status CSV] [--page] [--pageSize]
|
|
224
|
+
list-quotations [--projectId] [--budgetId] [--supplierId] [--status CSV] [--requesterIds CSV] [--search] [--page] [--pageSize]
|
|
183
225
|
get-quotation <quotationId>
|
|
184
|
-
upload-quotation-attachment <projectId> <filePath>
|
|
226
|
+
upload-quotation-attachment <projectId> <filePath> Upload one PDF, JPEG, or PNG up to 20MB; returns attachment JSON for a quotation payload.
|
|
227
|
+
cleanup-staged-quotation-attachments <projectId> --keys <csv> Delete unattached staged quotation uploads.
|
|
185
228
|
create-quotation --payload '<json>' (quotation.createDraft; requires amountWithoutGst and gstAmount)
|
|
229
|
+
update-quotation --payload '<json>' Update a DRAFT or REJECTED quotation; same payload plus id.
|
|
230
|
+
delete-quotation <quotationId>
|
|
186
231
|
submit-quotation <quotationId>
|
|
187
232
|
approve-quotation <quotationId>
|
|
188
233
|
reject-quotation <quotationId> --reason <text>
|
|
189
234
|
download-quotation-pdf <quotationId> [original|staff|final]
|
|
190
235
|
|
|
236
|
+
Customer invoices
|
|
237
|
+
check-customer-invoice-readiness <budgetId>
|
|
238
|
+
list-customer-invoices <budgetId> [--status CSV] [--search] [--sortBy createdAt|status|totalNetAmount|approvalExpiresAt] [--sortDir asc|desc] [--page] [--perPage]
|
|
239
|
+
get-customer-invoice <batchId>
|
|
240
|
+
create-customer-invoice --payload '<json>' budgetId + one split with label, percentage, and dueDate.
|
|
241
|
+
discard-customer-invoice <batchId> Discard a reserved CREATING batch with no created QBO invoices.
|
|
242
|
+
delete-customer-invoice <batchId> Delete QBO invoices and the local batch.
|
|
243
|
+
void-customer-invoice <batchId> Void the invoice batch in QuickBooks.
|
|
244
|
+
approve-customer-invoice <batchId> Admin approval; notifies the creator.
|
|
245
|
+
reject-customer-invoice <batchId> --reason <text> Admin rejection; voids QBO invoices and notifies the creator.
|
|
246
|
+
download-customer-invoice-pdf <invoiceId> [--output <path>]
|
|
247
|
+
sync-customer-invoice <invoiceId> Refresh local status and balance from QuickBooks.
|
|
248
|
+
|
|
191
249
|
Approvals
|
|
192
|
-
list-approvals | get-pending-approvals [--type budget|supplier|bill|quotation|all]
|
|
250
|
+
list-approvals | get-pending-approvals [--type budget|supplier|bill|quotation|customer_invoice|all]
|
|
193
251
|
approve-bill <billId> (also sends requester reply email)
|
|
194
252
|
reject-bill <billId> --reason <text> (also sends requester reply email)
|
|
195
253
|
approve-budget <budgetId> (also sends requester reply email)
|
|
@@ -211,8 +269,12 @@ Companies & projects
|
|
|
211
269
|
create-project --name --companyId --contactPersonId --insideSalesId --businessDevelopmentId --venue --startDate <ISO> [--projectManagerId <userId>] [--asanaTaskId] [--asanaSearch <text>] [--slackChannelId --slackChannelUrl --slackChannelName] [--pax] [--endDate <ISO>] [--description]
|
|
212
270
|
update-project --payload '<json>' (project.updateProject; optional projectManagerId and requestQboAccountantNotification; default notify like web)
|
|
213
271
|
delete-project <id>
|
|
272
|
+
check-project-reconciliation <id> Run the same live reconciliation review as the web app without changing status.
|
|
273
|
+
reconcile-project <id> Rerun all checks transactionally and mark an eligible WON project RECONCILED.
|
|
274
|
+
complete-project <id> Rerun reconciliation validation, mark a RECONCILED project COMPLETED, and queue QBO placeholder cleanup.
|
|
275
|
+
import-qbo-project <projectId> --url <quickBooksProjectUrl>
|
|
214
276
|
update-project-status <id> <status> [--projectManagerId <userId>] [--projectManagerEmail <email>] [--wonOverrideReason <text>]
|
|
215
|
-
|
|
277
|
+
Required when moving PITCH → WON: supply --projectManagerId or --projectManagerEmail.
|
|
216
278
|
Required when moving to WON without accepted/closed budget or win proof: --wonOverrideReason (10+ chars).
|
|
217
279
|
status: ${projectStatusesForHelp.join(", ")}
|
|
218
280
|
|
|
@@ -261,6 +323,10 @@ Errors
|
|
|
261
323
|
get-recent-errors [--page] [--perPage] [--severity] [--status]
|
|
262
324
|
get-error-metrics
|
|
263
325
|
|
|
326
|
+
Automation (admin)
|
|
327
|
+
list-integration-operations [--destination] [--status PENDING|PROCESSING|FAILED|COMPLETED] [--page] [--perPage]
|
|
328
|
+
retry-integration-operation <operationId> [--confirmExternalStateReconciled true|false]
|
|
329
|
+
|
|
264
330
|
Historical / benchmarks
|
|
265
331
|
get-approved-budgets [--projectId] [--limit] [--minRevenue] [--maxRevenue]
|
|
266
332
|
get-budget-category-benchmarks <categoryId> [--limit]
|
|
@@ -377,6 +443,23 @@ async function main() {
|
|
|
377
443
|
await getBudgetVersions(id);
|
|
378
444
|
break;
|
|
379
445
|
}
|
|
446
|
+
case "rename-budget-version": {
|
|
447
|
+
const versionId = positional[0];
|
|
448
|
+
const newName = getFlag(flags, "name");
|
|
449
|
+
if (!versionId || newName === undefined) {
|
|
450
|
+
throw new Error("rename-budget-version requires <versionId> --name <text>");
|
|
451
|
+
}
|
|
452
|
+
await renameBudgetVersion(versionId, String(newName));
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
case "restore-budget-version": {
|
|
456
|
+
const versionId = positional[0];
|
|
457
|
+
if (!versionId) {
|
|
458
|
+
throw new Error("restore-budget-version requires <versionId>");
|
|
459
|
+
}
|
|
460
|
+
await restoreBudgetVersion(versionId);
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
380
463
|
case "update-budget-status": {
|
|
381
464
|
const [id, status] = positional;
|
|
382
465
|
if (!id || !status) {
|
|
@@ -434,6 +517,16 @@ async function main() {
|
|
|
434
517
|
});
|
|
435
518
|
break;
|
|
436
519
|
}
|
|
520
|
+
case "send-estimate-email":
|
|
521
|
+
case "send-estimate-to-contact-person": {
|
|
522
|
+
const payloadRaw = getFlag(flags, "payload");
|
|
523
|
+
if (!payloadRaw) {
|
|
524
|
+
throw new Error("send-estimate-to-contact-person requires --payload '<json>'");
|
|
525
|
+
}
|
|
526
|
+
const raw = parseJsonFlag(String(payloadRaw), "--payload");
|
|
527
|
+
await sendEstimateToContactPersonFromPayload(raw);
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
437
530
|
case "create-budget": {
|
|
438
531
|
const payloadRaw = getFlag(flags, "payload");
|
|
439
532
|
if (!payloadRaw) {
|
|
@@ -560,6 +653,46 @@ async function main() {
|
|
|
560
653
|
await updateBudgetDiscountFromPayload(raw);
|
|
561
654
|
break;
|
|
562
655
|
}
|
|
656
|
+
case "delete-budget-commission": {
|
|
657
|
+
const budgetId = positional[0];
|
|
658
|
+
if (!budgetId) {
|
|
659
|
+
throw new Error("delete-budget-commission requires <budgetId>");
|
|
660
|
+
}
|
|
661
|
+
await deleteBudgetCommission(budgetId);
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
664
|
+
case "delete-budget-discount": {
|
|
665
|
+
const budgetId = positional[0];
|
|
666
|
+
if (!budgetId) {
|
|
667
|
+
throw new Error("delete-budget-discount requires <budgetId>");
|
|
668
|
+
}
|
|
669
|
+
await deleteBudgetDiscount(budgetId);
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
case "mark-budget-items-not-utilized": {
|
|
673
|
+
const budgetItemIds = parseCommaSeparatedIds(getFlag(flags, "ids"));
|
|
674
|
+
if (!budgetItemIds?.length) {
|
|
675
|
+
throw new Error("mark-budget-items-not-utilized requires --ids <csv>");
|
|
676
|
+
}
|
|
677
|
+
await setBudgetItemsNotUtilized(budgetItemIds, true);
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
case "restore-budget-item": {
|
|
681
|
+
const budgetItemId = positional[0];
|
|
682
|
+
if (!budgetItemId) {
|
|
683
|
+
throw new Error("restore-budget-item requires <budgetItemId>");
|
|
684
|
+
}
|
|
685
|
+
await setBudgetItemsNotUtilized([budgetItemId], false);
|
|
686
|
+
break;
|
|
687
|
+
}
|
|
688
|
+
case "create-placeholder-bill": {
|
|
689
|
+
const budgetItemId = positional[0];
|
|
690
|
+
if (!budgetItemId) {
|
|
691
|
+
throw new Error("create-placeholder-bill requires <budgetItemId>");
|
|
692
|
+
}
|
|
693
|
+
await createPlaceholderBillForBudgetItem(budgetItemId);
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
563
696
|
case "list-bills": {
|
|
564
697
|
await listBills(parseListBillsFlags(flags));
|
|
565
698
|
break;
|
|
@@ -587,6 +720,15 @@ async function main() {
|
|
|
587
720
|
await createBillFromPayload(raw);
|
|
588
721
|
break;
|
|
589
722
|
}
|
|
723
|
+
case "validate-bill-selection": {
|
|
724
|
+
const payloadRaw = getFlag(flags, "payload");
|
|
725
|
+
if (!payloadRaw) {
|
|
726
|
+
throw new Error("validate-bill-selection requires --payload '<json>'");
|
|
727
|
+
}
|
|
728
|
+
const raw = parseJsonFlag(String(payloadRaw), "--payload");
|
|
729
|
+
await validateBillSelectionFromPayload(raw);
|
|
730
|
+
break;
|
|
731
|
+
}
|
|
590
732
|
case "update-bill": {
|
|
591
733
|
const payloadRaw = getFlag(flags, "payload");
|
|
592
734
|
if (!payloadRaw) {
|
|
@@ -596,6 +738,15 @@ async function main() {
|
|
|
596
738
|
await updateBillFromPayload(raw);
|
|
597
739
|
break;
|
|
598
740
|
}
|
|
741
|
+
case "update-bill-payment-evidence": {
|
|
742
|
+
const payloadRaw = getFlag(flags, "payload");
|
|
743
|
+
if (!payloadRaw) {
|
|
744
|
+
throw new Error("update-bill-payment-evidence requires --payload '<json>'");
|
|
745
|
+
}
|
|
746
|
+
const raw = parseJsonFlag(String(payloadRaw), "--payload");
|
|
747
|
+
await updateBillPaymentEvidenceFromPayload(raw);
|
|
748
|
+
break;
|
|
749
|
+
}
|
|
599
750
|
case "delete-bill": {
|
|
600
751
|
const id = positional[0];
|
|
601
752
|
if (!id)
|
|
@@ -606,7 +757,11 @@ async function main() {
|
|
|
606
757
|
case "update-bill-status": {
|
|
607
758
|
const [id, statusRaw] = positional;
|
|
608
759
|
if (!id || !statusRaw) {
|
|
609
|
-
throw new Error("update-bill-status requires <billId> <status> [--rejectionReason] [--paymentTrackingUrl] [--paymentReference]");
|
|
760
|
+
throw new Error("update-bill-status requires <billId> <status> [--rejectionReason] [--paymentTrackingUrl] [--paymentReference] [--paymentProof <receipt.pdf>]");
|
|
761
|
+
}
|
|
762
|
+
const paymentProofFlag = flags.paymentProof ?? flags["payment-proof"] ?? flags.receipt;
|
|
763
|
+
if (paymentProofFlag === true) {
|
|
764
|
+
throw new Error("--paymentProof requires a PDF file path.");
|
|
610
765
|
}
|
|
611
766
|
const status = parseBillStatusForUpdate(statusRaw);
|
|
612
767
|
await updateBillStatus({
|
|
@@ -615,6 +770,7 @@ async function main() {
|
|
|
615
770
|
rejectionReason: getFlag(flags, "rejectionReason"),
|
|
616
771
|
paymentTrackingUrl: getFlag(flags, "paymentTrackingUrl"),
|
|
617
772
|
paymentReference: getFlag(flags, "paymentReference"),
|
|
773
|
+
paymentProofPath: paymentProofFlag,
|
|
618
774
|
});
|
|
619
775
|
break;
|
|
620
776
|
}
|
|
@@ -714,6 +870,15 @@ async function main() {
|
|
|
714
870
|
await stageBillAttachmentsFromPaths(projectId, filePaths);
|
|
715
871
|
break;
|
|
716
872
|
}
|
|
873
|
+
case "cleanup-staged-bill-attachments": {
|
|
874
|
+
const projectId = positional[0];
|
|
875
|
+
const keys = parseCommaSeparatedIds(getFlag(flags, "keys"));
|
|
876
|
+
if (!projectId || !keys) {
|
|
877
|
+
throw new Error("cleanup-staged-bill-attachments requires <projectId> --keys <csv>");
|
|
878
|
+
}
|
|
879
|
+
await cleanupStagedBillAttachments(projectId, keys);
|
|
880
|
+
break;
|
|
881
|
+
}
|
|
717
882
|
case "upload-bill-attachment":
|
|
718
883
|
case "attach-bill-attachment":
|
|
719
884
|
case "attach-bill-payment-receipt": {
|
|
@@ -794,12 +959,9 @@ async function main() {
|
|
|
794
959
|
projectId: getFlag(flags, "projectId"),
|
|
795
960
|
budgetId: getFlag(flags, "budgetId"),
|
|
796
961
|
supplierId: getFlag(flags, "supplierId"),
|
|
797
|
-
status: statusRaw
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
.map((value) => value.trim().toUpperCase())
|
|
801
|
-
.filter(Boolean)
|
|
802
|
-
: undefined,
|
|
962
|
+
status: parseCommaSeparatedQuotationStatuses(statusRaw),
|
|
963
|
+
requesterIds: parseCommaSeparatedIds(getFlag(flags, "requesterIds")),
|
|
964
|
+
search: getFlag(flags, "search"),
|
|
803
965
|
page: getFlag(flags, "page") !== undefined
|
|
804
966
|
? Number(getFlag(flags, "page"))
|
|
805
967
|
: undefined,
|
|
@@ -824,6 +986,15 @@ async function main() {
|
|
|
824
986
|
await uploadQuotationAttachmentFromPath(projectId, filePath);
|
|
825
987
|
break;
|
|
826
988
|
}
|
|
989
|
+
case "cleanup-staged-quotation-attachments": {
|
|
990
|
+
const projectId = positional[0];
|
|
991
|
+
const keys = parseCommaSeparatedIds(getFlag(flags, "keys"));
|
|
992
|
+
if (!projectId || !keys) {
|
|
993
|
+
throw new Error("cleanup-staged-quotation-attachments requires <projectId> --keys <csv>");
|
|
994
|
+
}
|
|
995
|
+
await cleanupStagedQuotationAttachments(projectId, keys);
|
|
996
|
+
break;
|
|
997
|
+
}
|
|
827
998
|
case "create-quotation": {
|
|
828
999
|
const payloadRaw = getFlag(flags, "payload");
|
|
829
1000
|
if (!payloadRaw) {
|
|
@@ -833,6 +1004,22 @@ async function main() {
|
|
|
833
1004
|
await createQuotationFromPayload(raw);
|
|
834
1005
|
break;
|
|
835
1006
|
}
|
|
1007
|
+
case "update-quotation": {
|
|
1008
|
+
const payloadRaw = getFlag(flags, "payload");
|
|
1009
|
+
if (!payloadRaw) {
|
|
1010
|
+
throw new Error("update-quotation requires --payload '<json>'");
|
|
1011
|
+
}
|
|
1012
|
+
const raw = parseJsonFlag(String(payloadRaw), "--payload");
|
|
1013
|
+
await updateQuotationFromPayload(raw);
|
|
1014
|
+
break;
|
|
1015
|
+
}
|
|
1016
|
+
case "delete-quotation": {
|
|
1017
|
+
const id = positional[0];
|
|
1018
|
+
if (!id)
|
|
1019
|
+
throw new Error("delete-quotation requires <quotationId>");
|
|
1020
|
+
await deleteQuotationById(id);
|
|
1021
|
+
break;
|
|
1022
|
+
}
|
|
836
1023
|
case "submit-quotation": {
|
|
837
1024
|
const id = positional[0];
|
|
838
1025
|
if (!id)
|
|
@@ -866,10 +1053,114 @@ async function main() {
|
|
|
866
1053
|
? "ORIGINAL"
|
|
867
1054
|
: roleRaw === "staff"
|
|
868
1055
|
? "STAFF_SIGNED"
|
|
869
|
-
: "
|
|
1056
|
+
: roleRaw === "final"
|
|
1057
|
+
? "FINAL_SIGNED"
|
|
1058
|
+
: null;
|
|
1059
|
+
if (!fileRole) {
|
|
1060
|
+
throw new Error("download-quotation-pdf role must be original, staff, or final");
|
|
1061
|
+
}
|
|
870
1062
|
await downloadQuotationPdf(id, fileRole);
|
|
871
1063
|
break;
|
|
872
1064
|
}
|
|
1065
|
+
case "check-customer-invoice-readiness": {
|
|
1066
|
+
const budgetId = positional[0];
|
|
1067
|
+
if (!budgetId) {
|
|
1068
|
+
throw new Error("check-customer-invoice-readiness requires <budgetId>");
|
|
1069
|
+
}
|
|
1070
|
+
await checkCustomerInvoiceReadiness(budgetId);
|
|
1071
|
+
break;
|
|
1072
|
+
}
|
|
1073
|
+
case "list-customer-invoices": {
|
|
1074
|
+
const budgetId = positional[0];
|
|
1075
|
+
if (!budgetId) {
|
|
1076
|
+
throw new Error("list-customer-invoices requires <budgetId>");
|
|
1077
|
+
}
|
|
1078
|
+
const statusRaw = getFlag(flags, "status");
|
|
1079
|
+
const sortDir = parseOptionalBillListSortDir(getFlag(flags, "sortDir"));
|
|
1080
|
+
await listCustomerInvoices({
|
|
1081
|
+
budgetId,
|
|
1082
|
+
page: parsePositiveIntFlag(getFlag(flags, "page"), "--page") ?? 1,
|
|
1083
|
+
perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage") ?? 12,
|
|
1084
|
+
q: getFlag(flags, "search") ?? "",
|
|
1085
|
+
statuses: parseCommaSeparatedCustomerInvoiceStatuses(statusRaw !== undefined ? String(statusRaw) : undefined),
|
|
1086
|
+
sortBy: parseCustomerInvoiceSortBy(getFlag(flags, "sortBy")),
|
|
1087
|
+
sortDir: sortDir ?? "desc",
|
|
1088
|
+
});
|
|
1089
|
+
break;
|
|
1090
|
+
}
|
|
1091
|
+
case "get-customer-invoice": {
|
|
1092
|
+
const batchId = positional[0];
|
|
1093
|
+
if (!batchId) {
|
|
1094
|
+
throw new Error("get-customer-invoice requires <batchId>");
|
|
1095
|
+
}
|
|
1096
|
+
await getCustomerInvoice(batchId);
|
|
1097
|
+
break;
|
|
1098
|
+
}
|
|
1099
|
+
case "create-customer-invoice": {
|
|
1100
|
+
const payloadRaw = getFlag(flags, "payload");
|
|
1101
|
+
if (!payloadRaw) {
|
|
1102
|
+
throw new Error("create-customer-invoice requires --payload '<json>'");
|
|
1103
|
+
}
|
|
1104
|
+
await createCustomerInvoice(parseJsonFlag(String(payloadRaw), "--payload"));
|
|
1105
|
+
break;
|
|
1106
|
+
}
|
|
1107
|
+
case "discard-customer-invoice": {
|
|
1108
|
+
const batchId = positional[0];
|
|
1109
|
+
if (!batchId) {
|
|
1110
|
+
throw new Error("discard-customer-invoice requires <batchId>");
|
|
1111
|
+
}
|
|
1112
|
+
await discardCreatingCustomerInvoice(batchId);
|
|
1113
|
+
break;
|
|
1114
|
+
}
|
|
1115
|
+
case "delete-customer-invoice": {
|
|
1116
|
+
const batchId = positional[0];
|
|
1117
|
+
if (!batchId) {
|
|
1118
|
+
throw new Error("delete-customer-invoice requires <batchId>");
|
|
1119
|
+
}
|
|
1120
|
+
await deleteCustomerInvoice(batchId);
|
|
1121
|
+
break;
|
|
1122
|
+
}
|
|
1123
|
+
case "void-customer-invoice": {
|
|
1124
|
+
const batchId = positional[0];
|
|
1125
|
+
if (!batchId) {
|
|
1126
|
+
throw new Error("void-customer-invoice requires <batchId>");
|
|
1127
|
+
}
|
|
1128
|
+
await voidCustomerInvoice(batchId);
|
|
1129
|
+
break;
|
|
1130
|
+
}
|
|
1131
|
+
case "approve-customer-invoice": {
|
|
1132
|
+
const batchId = positional[0];
|
|
1133
|
+
if (!batchId) {
|
|
1134
|
+
throw new Error("approve-customer-invoice requires <batchId>");
|
|
1135
|
+
}
|
|
1136
|
+
await approveCustomerInvoice(batchId);
|
|
1137
|
+
break;
|
|
1138
|
+
}
|
|
1139
|
+
case "reject-customer-invoice": {
|
|
1140
|
+
const batchId = positional[0];
|
|
1141
|
+
const reason = getFlag(flags, "reason");
|
|
1142
|
+
if (!batchId || reason === undefined) {
|
|
1143
|
+
throw new Error("reject-customer-invoice requires <batchId> --reason <text>");
|
|
1144
|
+
}
|
|
1145
|
+
await rejectCustomerInvoice(batchId, String(reason));
|
|
1146
|
+
break;
|
|
1147
|
+
}
|
|
1148
|
+
case "download-customer-invoice-pdf": {
|
|
1149
|
+
const invoiceId = positional[0];
|
|
1150
|
+
if (!invoiceId) {
|
|
1151
|
+
throw new Error("download-customer-invoice-pdf requires <invoiceId> [--output <path>]");
|
|
1152
|
+
}
|
|
1153
|
+
await downloadCustomerInvoicePdf(invoiceId, getFlag(flags, "output"));
|
|
1154
|
+
break;
|
|
1155
|
+
}
|
|
1156
|
+
case "sync-customer-invoice": {
|
|
1157
|
+
const invoiceId = positional[0];
|
|
1158
|
+
if (!invoiceId) {
|
|
1159
|
+
throw new Error("sync-customer-invoice requires <invoiceId>");
|
|
1160
|
+
}
|
|
1161
|
+
await syncCustomerInvoice(invoiceId);
|
|
1162
|
+
break;
|
|
1163
|
+
}
|
|
873
1164
|
case "list-approvals":
|
|
874
1165
|
case "get-pending-approvals": {
|
|
875
1166
|
const typeRaw = getFlag(flags, "type");
|
|
@@ -1057,6 +1348,37 @@ async function main() {
|
|
|
1057
1348
|
await deleteProjectById(id);
|
|
1058
1349
|
break;
|
|
1059
1350
|
}
|
|
1351
|
+
case "check-project-reconciliation": {
|
|
1352
|
+
const id = positional[0];
|
|
1353
|
+
if (!id) {
|
|
1354
|
+
throw new Error("check-project-reconciliation requires <id>");
|
|
1355
|
+
}
|
|
1356
|
+
await checkProjectReconciliation(id);
|
|
1357
|
+
break;
|
|
1358
|
+
}
|
|
1359
|
+
case "reconcile-project": {
|
|
1360
|
+
const id = positional[0];
|
|
1361
|
+
if (!id)
|
|
1362
|
+
throw new Error("reconcile-project requires <id>");
|
|
1363
|
+
await reconcileProject(id);
|
|
1364
|
+
break;
|
|
1365
|
+
}
|
|
1366
|
+
case "complete-project": {
|
|
1367
|
+
const id = positional[0];
|
|
1368
|
+
if (!id)
|
|
1369
|
+
throw new Error("complete-project requires <id>");
|
|
1370
|
+
await completeProject(id);
|
|
1371
|
+
break;
|
|
1372
|
+
}
|
|
1373
|
+
case "import-qbo-project": {
|
|
1374
|
+
const projectId = positional[0];
|
|
1375
|
+
const projectUrl = getFlag(flags, "url");
|
|
1376
|
+
if (!projectId || projectUrl === undefined) {
|
|
1377
|
+
throw new Error("import-qbo-project requires <projectId> --url <quickBooksProjectUrl>");
|
|
1378
|
+
}
|
|
1379
|
+
await importQuickBooksProjectId(projectId, String(projectUrl));
|
|
1380
|
+
break;
|
|
1381
|
+
}
|
|
1060
1382
|
case "update-project-status": {
|
|
1061
1383
|
const [id, statusRaw] = positional;
|
|
1062
1384
|
if (!id || !statusRaw) {
|
|
@@ -1344,6 +1666,24 @@ async function main() {
|
|
|
1344
1666
|
await getErrorMetrics();
|
|
1345
1667
|
break;
|
|
1346
1668
|
}
|
|
1669
|
+
case "list-integration-operations": {
|
|
1670
|
+
await listIntegrationOperations({
|
|
1671
|
+
destination: getFlag(flags, "destination"),
|
|
1672
|
+
page: parsePositiveIntFlag(getFlag(flags, "page"), "--page") ?? 1,
|
|
1673
|
+
perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage") ?? 25,
|
|
1674
|
+
status: parseIntegrationOperationStatus(getFlag(flags, "status")),
|
|
1675
|
+
});
|
|
1676
|
+
break;
|
|
1677
|
+
}
|
|
1678
|
+
case "retry-integration-operation": {
|
|
1679
|
+
const operationId = positional[0];
|
|
1680
|
+
if (!operationId) {
|
|
1681
|
+
throw new Error("retry-integration-operation requires <operationId>");
|
|
1682
|
+
}
|
|
1683
|
+
await retryIntegrationOperation(operationId, parseOptionalBoolFlag(flags, "confirmExternalStateReconciled") ??
|
|
1684
|
+
false);
|
|
1685
|
+
break;
|
|
1686
|
+
}
|
|
1347
1687
|
case "get-approved-budgets": {
|
|
1348
1688
|
await getApprovedBudgets({
|
|
1349
1689
|
projectId: getFlag(flags, "projectId"),
|
package/dist/parse-cli-enums.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { BudgetRole, Deals, ExtendedApprovalStatus, ExtendedBillStatus, ExtendedErrorSeverity, ExtendedErrorStatus, ExtendedProjectStatus, TimeFrame, } from "./filter-enums.js";
|
|
2
|
-
import { BillStatus, BudgetStatus, ProjectStatus, UserRole, } from "./prisma-enums.js";
|
|
2
|
+
import { BillStatus, BudgetStatus, CustomerInvoiceBatchStatus, ProjectStatus, SupplierQuotationStatus, UserRole, } from "./prisma-enums.js";
|
|
3
3
|
const BUDGET_STATUS_VALUES = new Set(Object.values(BudgetStatus));
|
|
4
4
|
const BILL_STATUS_VALUES = new Set(Object.values(BillStatus));
|
|
5
5
|
const PROJECT_STATUS_VALUES = new Set(Object.values(ProjectStatus));
|
|
6
|
+
const SUPPLIER_QUOTATION_STATUS_VALUES = new Set(Object.values(SupplierQuotationStatus));
|
|
7
|
+
const CUSTOMER_INVOICE_BATCH_STATUS_VALUES = new Set(Object.values(CustomerInvoiceBatchStatus));
|
|
6
8
|
const USER_ROLE_VALUES = new Set(Object.values(UserRole));
|
|
7
9
|
const EXTENDED_PROJECT_STATUS_VALUES = new Set(Object.values(ExtendedProjectStatus));
|
|
8
10
|
const DASHBOARD_ROLE_VALUES = new Set([
|
|
@@ -173,6 +175,38 @@ export function parseCommaSeparatedSupplierStatuses(raw) {
|
|
|
173
175
|
}
|
|
174
176
|
return out;
|
|
175
177
|
}
|
|
178
|
+
export function parseCommaSeparatedQuotationStatuses(raw) {
|
|
179
|
+
if (raw === undefined)
|
|
180
|
+
return undefined;
|
|
181
|
+
const parts = raw
|
|
182
|
+
.split(",")
|
|
183
|
+
.map((value) => value.trim().toUpperCase())
|
|
184
|
+
.filter(Boolean);
|
|
185
|
+
if (parts.length === 0)
|
|
186
|
+
return undefined;
|
|
187
|
+
for (const status of parts) {
|
|
188
|
+
if (!SUPPLIER_QUOTATION_STATUS_VALUES.has(status)) {
|
|
189
|
+
throw new Error(`Invalid quotation status "${status}". Use one of: ${[...SUPPLIER_QUOTATION_STATUS_VALUES].join(", ")}.`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return parts;
|
|
193
|
+
}
|
|
194
|
+
export function parseCommaSeparatedCustomerInvoiceStatuses(raw) {
|
|
195
|
+
if (raw === undefined)
|
|
196
|
+
return undefined;
|
|
197
|
+
const parts = raw
|
|
198
|
+
.split(",")
|
|
199
|
+
.map((value) => value.trim().toUpperCase())
|
|
200
|
+
.filter(Boolean);
|
|
201
|
+
if (parts.length === 0)
|
|
202
|
+
return undefined;
|
|
203
|
+
for (const status of parts) {
|
|
204
|
+
if (!CUSTOMER_INVOICE_BATCH_STATUS_VALUES.has(status)) {
|
|
205
|
+
throw new Error(`Invalid customer invoice status "${status}". Use one of: ${[...CUSTOMER_INVOICE_BATCH_STATUS_VALUES].join(", ")}.`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return parts;
|
|
209
|
+
}
|
|
176
210
|
export function parseBillStatusFlag(raw) {
|
|
177
211
|
if (raw === undefined)
|
|
178
212
|
return undefined;
|
|
@@ -225,7 +259,11 @@ export function parseOptionalBillListSortDir(raw) {
|
|
|
225
259
|
return raw;
|
|
226
260
|
}
|
|
227
261
|
function isPendingRequestType(s) {
|
|
228
|
-
return (s === "BUDGET" ||
|
|
262
|
+
return (s === "BUDGET" ||
|
|
263
|
+
s === "BILL" ||
|
|
264
|
+
s === "SUPPLIER" ||
|
|
265
|
+
s === "QUOTATION" ||
|
|
266
|
+
s === "CUSTOMER_INVOICE");
|
|
229
267
|
}
|
|
230
268
|
export function parseApprovalTypeFlag(raw) {
|
|
231
269
|
if (raw === undefined)
|
|
@@ -234,7 +272,7 @@ export function parseApprovalTypeFlag(raw) {
|
|
|
234
272
|
if (u === "ALL")
|
|
235
273
|
return "ALL";
|
|
236
274
|
if (!isPendingRequestType(u)) {
|
|
237
|
-
throw new Error(`Invalid --type "${raw}". Use budget, supplier, bill, quotation, or all.`);
|
|
275
|
+
throw new Error(`Invalid --type "${raw}". Use budget, supplier, bill, quotation, customer_invoice, or all.`);
|
|
238
276
|
}
|
|
239
277
|
return u;
|
|
240
278
|
}
|