@go-labs-sg/bb 1.13.0 → 1.14.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 +3 -2
- package/dist/commands.js +87 -0
- package/dist/index.js +95 -2
- package/dist/parse-cli-enums.js +2 -2
- package/dist/prisma-enums.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,8 +63,9 @@ Global options and flags use `--key=value` or `--key value` (see `bb help`).
|
|
|
63
63
|
| Area | Commands (non-exhaustive) |
|
|
64
64
|
| --- | --- |
|
|
65
65
|
| **Budgets** | `list-budgets` (full payload by default; `--summary` or `--includeDetails false` for slim list), `get-budget`, `get-budget-items`, `get-budget-details`, `get-budget-categories`, `get-budget-versions`, `update-budget-status` (`ESTIMATE_ACCEPTED` requires win proof, auto-marks `PITCH` or `LOST` projects `WON`, and auto-creates Asana project/section/tasks when enabled; `--projectStatusOnCommercialRejection PITCH\|LOST` is required when rejecting the only accepted/closed budget on a commercial project), `mark-budget-won` (`<budgetId>` + proof file path; `PITCH` or `LOST` projects become `WON` automatically), `create-budget` / `update-budget` (`--payload`), `delete-budget`, `create-budget-approval` (also sends approval request emails), `add-budget-items`, `update-budget-item`, `remove-budget-item`, `reorder-budget-items`, `update-budget-item-supplier`, `create-budget-category`, `update-budget-category`, `delete-budget-category`, `update-budget-commission`, `update-budget-discount` (`--payload` where noted), `upload-budget-attachment` (`<budgetId>` + local file path; uses `attachment.requestBudgetAttachmentUpload` + PUT + `attachment.confirmBudgetAttachment`) |
|
|
66
|
-
| **Bills / claims** | `list-bills` (`--isClaimable false` for bills, `--isClaimable true` for claims, omit for both), `list-claims` (claims only), `create-bill` (`--payload`; set `isClaimable=false` for a bill or `isClaimable=true` for a claim), `update-bill` (`--payload`), `delete-bill`, `create-bill-approval` (also sends approval request emails), `update-bill-status`, `patch-bill-payment` (PAID bills: `--paymentTrackingUrl`, `--paymentReference`, `--quickbooksBillId`, `--paymentDate` ISO; clear with `--clearPaymentTrackingUrl` / `--clearPaymentReference` / `--clearQuickbooksBillId` / `--clearPaymentDate` `true`), `patch-bill-invoice-number`, `get-bill-attachments`, `upload-bill-attachment` (`<billId>` + local path), `get-bill-details` |
|
|
67
|
-
| **
|
|
66
|
+
| **Bills / claims** | `list-bills` (`--isClaimable false` for bills, `--isClaimable true` for claims, omit for both), `list-claims` (claims only), `create-bill` (`--payload`; set `isClaimable=false` for a bill or `isClaimable=true` for a claim; non-legacy supplier bills from 1 Jul 2026 00:00 SGT require approved quotation coverage), `update-bill` (`--payload`), `delete-bill`, `create-bill-approval` (also sends approval request emails), `update-bill-status`, `patch-bill-payment` (PAID bills: `--paymentTrackingUrl`, `--paymentReference`, `--quickbooksBillId`, `--paymentDate` ISO; clear with `--clearPaymentTrackingUrl` / `--clearPaymentReference` / `--clearQuickbooksBillId` / `--clearPaymentDate` `true`), `patch-bill-invoice-number`, `get-bill-attachments`, `upload-bill-attachment` (`<billId>` + local path), `get-bill-details` |
|
|
67
|
+
| **Quotations** | `list-quotations`, `get-quotation`, `upload-quotation-attachment` (`<projectId>` + local PDF path; returns attachment JSON for payload use), `create-quotation` (`--payload` for `quotation.createDraft`), `submit-quotation`, `approve-quotation`, `reject-quotation`, `download-quotation-pdf` (`original`, `staff`, or `final`) |
|
|
68
|
+
| **Approvals** | `list-approvals` / `get-pending-approvals` (`--type budget\|supplier\|bill\|quotation\|all`), `approve-bill` / `reject-bill` (send reply email), `approve-budget` / `reject-budget` (send reply email), `approve-supplier` / `reject-supplier` (send reply email), `approve-quotation` / `reject-quotation` |
|
|
68
69
|
| **Companies & projects** | `list-companies`, `get-company`, `create-company`, `update-company` (`--payload`), `delete-company`, `list-projects`, `get-project`, `create-project` (required: `--name`, `--companyId`, `--contactPersonId`, `--insideSalesId`, `--businessDevelopmentId`, `--venue`, `--asanaTaskId`, `--slackChannelId`, `--slackChannelUrl`, `--slackChannelName`, `--startDate` as ISO datetime for project/window start; optional `--pax`; optional `--endDate`; optional `--description`; optional `--requestQboAccountantNotification false` to skip QBO accountant emails), `update-project` (`--payload` with `dateRange.from` / `dateRange.to` for the project window; optional `requestQboAccountantNotification` in JSON), `delete-project`, `update-project-status` (`<id>` `<status>`: `PITCH` \| `WON` \| `COMPLETED` \| `LOST`; for `PITCH` → `WON` also pass `--projectManagerId` or `--projectManagerName`; when marking `WON` without an accepted/closed budget or proof, pass `--wonOverrideReason`) |
|
|
69
70
|
| **Contacts** | `list-contacts`, `create-contact-person` (`--payload`), `update-contact-person` (`--payload`) |
|
|
70
71
|
| **Suppliers & items** | `list-suppliers` (defaults to active suppliers, `--perPage 10`, sorted by `createdAt` desc; supports `--name`, `--sortBy` for scalar supplier fields, `--sortDir`, `--createdBy`, `--gstRegistered`, `--status`, `--supplierTags`, `--active false` for archived suppliers), `create-supplier` (`--payload`; when supplier status is `PENDING_APPROVAL`, also runs `supplier.createSupplierApproval` and `email.sendSupplierApprovalRequestEmail`), `update-supplier` (`--payload`; when supplier status is `PENDING_APPROVAL`, also runs `supplier.createSupplierApproval` and `email.sendSupplierApprovalRequestEmail`), `delete-suppliers` (`--ids` CSV; admin; archives/deactivates related items), `reactivate-suppliers` (`--ids` CSV; admin; reactivates related items), `create-certification` / `create-payment-method` / `create-supplier-role` / `create-supplier-tag` (`--name`), `get-supplier-details` (includes `supplierApprovalSummary` for pending approvers, superseded approvers, and the actual responder/respondedAt metadata), `get-supplier-analytics`, `list-items`, `create-item` (`--payload`), `update-item` (`--payload`), `delete-item`, `get-item`, `list-item-categories`, `create-item-category`, `update-item-category`, `delete-item-categories` (`--ids` CSV; admin) |
|
package/dist/commands.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { readFile } from "node:fs/promises";
|
|
2
3
|
import { basename } from "node:path";
|
|
3
4
|
import { contentType } from "mime-types";
|
|
@@ -9,6 +10,7 @@ import { createRichTextFromPlainText } from "./rich-text.js";
|
|
|
9
10
|
const BUDGET = "BUDGET";
|
|
10
11
|
const BILL = "BILL";
|
|
11
12
|
const SUPPLIER = "SUPPLIER";
|
|
13
|
+
const QUOTATION = "QUOTATION";
|
|
12
14
|
function isPendingRequest(n) {
|
|
13
15
|
return n.notificationType === "request" && n.status === "PENDING_APPROVAL";
|
|
14
16
|
}
|
|
@@ -199,6 +201,10 @@ export async function listApprovals(opts) {
|
|
|
199
201
|
id = n.supplier.id;
|
|
200
202
|
name = n.supplier.name;
|
|
201
203
|
}
|
|
204
|
+
else if (n.type === QUOTATION && n.supplierQuotation) {
|
|
205
|
+
id = n.supplierQuotation.id;
|
|
206
|
+
name = `Quotation ${n.supplierQuotation.supplier.name}`;
|
|
207
|
+
}
|
|
202
208
|
else {
|
|
203
209
|
id = n.id;
|
|
204
210
|
name = "Unknown";
|
|
@@ -323,6 +329,33 @@ export async function uploadBillAttachmentFromPath(billId, filePath) {
|
|
|
323
329
|
});
|
|
324
330
|
out(confirmed);
|
|
325
331
|
}
|
|
332
|
+
export async function uploadQuotationAttachmentFromPath(projectId, filePath) {
|
|
333
|
+
const buf = await readFile(filePath);
|
|
334
|
+
const fileName = basename(filePath);
|
|
335
|
+
const ext = fileName
|
|
336
|
+
.split(".")
|
|
337
|
+
.pop()
|
|
338
|
+
?.toLowerCase()
|
|
339
|
+
.replace(/[^a-z0-9]/g, "");
|
|
340
|
+
const key = `quotations/${projectId}/${randomUUID()}.${ext || "pdf"}`;
|
|
341
|
+
const uploadUrl = await api.attachment.getPresignedUrlToUpload.mutate({
|
|
342
|
+
key,
|
|
343
|
+
});
|
|
344
|
+
const res = await fetch(uploadUrl, {
|
|
345
|
+
method: "PUT",
|
|
346
|
+
body: buf,
|
|
347
|
+
headers: { "Content-Type": contentTypeHeaderForFileName(fileName) },
|
|
348
|
+
});
|
|
349
|
+
if (!res.ok) {
|
|
350
|
+
throw new Error(`S3 upload failed: HTTP ${res.status} ${(await res.text()).slice(0, 500)}`);
|
|
351
|
+
}
|
|
352
|
+
out({
|
|
353
|
+
id: randomUUID(),
|
|
354
|
+
name: fileName,
|
|
355
|
+
key,
|
|
356
|
+
size: buf.byteLength,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
326
359
|
export async function uploadBudgetAttachmentFromPath(budgetId, filePath) {
|
|
327
360
|
const buf = await readFile(filePath);
|
|
328
361
|
const fileName = basename(filePath);
|
|
@@ -568,6 +601,29 @@ export async function createBillFromPayload(raw) {
|
|
|
568
601
|
const result = await api.bill.create.mutate(input);
|
|
569
602
|
out(result);
|
|
570
603
|
}
|
|
604
|
+
export async function listQuotations(opts) {
|
|
605
|
+
const result = await api.quotation.list.query(opts);
|
|
606
|
+
out(result);
|
|
607
|
+
}
|
|
608
|
+
export async function getQuotationDetails(id) {
|
|
609
|
+
const result = await api.quotation.getById.query({ id });
|
|
610
|
+
out(result);
|
|
611
|
+
}
|
|
612
|
+
export async function createQuotationFromPayload(raw) {
|
|
613
|
+
const result = await api.quotation.createDraft.mutate(raw);
|
|
614
|
+
out(result);
|
|
615
|
+
}
|
|
616
|
+
export async function submitQuotation(id) {
|
|
617
|
+
const result = await api.quotation.submitForApproval.mutate({ id });
|
|
618
|
+
out(result);
|
|
619
|
+
}
|
|
620
|
+
export async function downloadQuotationPdf(id, fileRole) {
|
|
621
|
+
const result = await api.quotation.getSignedDownloadUrl.query({
|
|
622
|
+
id,
|
|
623
|
+
fileRole,
|
|
624
|
+
});
|
|
625
|
+
out(result);
|
|
626
|
+
}
|
|
571
627
|
export async function updateBillFromPayload(raw) {
|
|
572
628
|
const input = parseUpdateBillPayload(raw);
|
|
573
629
|
const result = await api.bill.update.mutate(input);
|
|
@@ -924,6 +980,37 @@ export async function rejectBill(billId, reason) {
|
|
|
924
980
|
email,
|
|
925
981
|
});
|
|
926
982
|
}
|
|
983
|
+
export async function approveQuotation(quotationId) {
|
|
984
|
+
const notifications = await api.budget.getNotifications.query();
|
|
985
|
+
const pending = notifications.filter((n) => isPendingRequest(n) &&
|
|
986
|
+
n.type === QUOTATION &&
|
|
987
|
+
(n.supplierQuotationId === quotationId ||
|
|
988
|
+
n.supplierQuotation?.id === quotationId));
|
|
989
|
+
const approval = pending[0];
|
|
990
|
+
if (!approval) {
|
|
991
|
+
throw new Error("No pending quotation approval found.");
|
|
992
|
+
}
|
|
993
|
+
const result = await api.quotation.approve.mutate({
|
|
994
|
+
quotationApprovalId: approval.id,
|
|
995
|
+
});
|
|
996
|
+
out(result);
|
|
997
|
+
}
|
|
998
|
+
export async function rejectQuotation(quotationId, reason) {
|
|
999
|
+
const notifications = await api.budget.getNotifications.query();
|
|
1000
|
+
const pending = notifications.filter((n) => isPendingRequest(n) &&
|
|
1001
|
+
n.type === QUOTATION &&
|
|
1002
|
+
(n.supplierQuotationId === quotationId ||
|
|
1003
|
+
n.supplierQuotation?.id === quotationId));
|
|
1004
|
+
const approval = pending[0];
|
|
1005
|
+
if (!approval) {
|
|
1006
|
+
throw new Error("No pending quotation approval found.");
|
|
1007
|
+
}
|
|
1008
|
+
const result = await api.quotation.reject.mutate({
|
|
1009
|
+
quotationApprovalId: approval.id,
|
|
1010
|
+
rejectionReason: reason,
|
|
1011
|
+
});
|
|
1012
|
+
out(result);
|
|
1013
|
+
}
|
|
927
1014
|
// --- Companies & projects ---
|
|
928
1015
|
export async function listCompanies(opts) {
|
|
929
1016
|
const result = await api.company.getCompanies.query({
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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, approveSupplier, createBillApproval, createBillFromPayload, createBudgetApproval, createBudgetCategory, createBudgetFromPayload, createCompany, createContactPersonFromPayload, createEstimate, createItemCategory, createItemFromPayload, createProject, createSupplierCertification, createSupplierFromPayload, createSupplierPaymentMethod, createSupplierRoleOption, createSupplierTagOption, deleteBillById, deleteBudgetById, deleteBudgetCategory, deleteCompanyById, deleteItemById, deleteItemCategoriesByIds, deleteProjectById, deleteSuppliersByIds, getApprovedBudgets, getBillAttachments, getBillDetails, getBudget, getBudgetCategories, getBudgetCategoryBenchmarks, getBudgetDetails, getBudgetItemsOnly, getBudgetVersions, getCompany, getDashboard, getErrorMetrics, getEstimatePerformance, getFinancialOverview, getItem, getItemPricingHistory, getMonthlyMetrics, getProject, getRecentErrors, getSupplierAnalytics, getSupplierDetails, getSupplierPricingHistory, getSystemOverview, getUserPerformance, listApprovals, listBills, listBudgets, listCompanies, listContacts, listItemCategories, listItems, listProjects, listSuppliers, listUsers, markBudgetWonWithProof, patchBillInvoiceNumber, patchBillPayment, reactivateSuppliersByIds, rejectBill, rejectBudget, rejectSupplier, removeBudgetItem, reorderBudgetItemsCli, updateBillFromPayload, updateBillStatus, updateBudgetCategory, updateBudgetCommissionFromPayload, updateBudgetDiscountFromPayload, updateBudgetFromPayload, updateBudgetItem, updateBudgetItemSupplierCli, updateBudgetStatus, updateCompanyFromPayload, updateContactPersonFromPayload, updateItemCategory, updateItemFromPayload, updateProjectFromPayload, updateProjectStatus, updateSupplierFromPayload, uploadBillAttachmentFromPath, uploadBudgetAttachmentFromPath, } from "./commands.js";
|
|
5
|
+
import { addBudgetItems, approveBill, approveBudget, approveQuotation, approveSupplier, createBillApproval, createBillFromPayload, createBudgetApproval, createBudgetCategory, createBudgetFromPayload, createCompany, createContactPersonFromPayload, createEstimate, createItemCategory, createItemFromPayload, createProject, createQuotationFromPayload, createSupplierCertification, createSupplierFromPayload, createSupplierPaymentMethod, createSupplierRoleOption, createSupplierTagOption, 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, listApprovals, listBills, listBudgets, listCompanies, listContacts, listItemCategories, listItems, listProjects, listQuotations, listSuppliers, listUsers, markBudgetWonWithProof, patchBillInvoiceNumber, patchBillPayment, reactivateSuppliersByIds, rejectBill, rejectBudget, rejectQuotation, rejectSupplier, removeBudgetItem, reorderBudgetItemsCli, submitQuotation, updateBillFromPayload, updateBillStatus, updateBudgetCategory, updateBudgetCommissionFromPayload, updateBudgetDiscountFromPayload, updateBudgetFromPayload, updateBudgetItem, updateBudgetItemSupplierCli, updateBudgetStatus, updateCompanyFromPayload, updateContactPersonFromPayload, updateItemCategory, updateItemFromPayload, updateProjectFromPayload, updateProjectStatus, updateSupplierFromPayload, uploadBillAttachmentFromPath, uploadBudgetAttachmentFromPath, uploadQuotationAttachmentFromPath, } from "./commands.js";
|
|
6
6
|
import { getFlag, parseArgs } from "./parse-args.js";
|
|
7
7
|
import { billStatusesForUpdateHelp, budgetStatusesForHelp, isBudgetStatusUpdate, parseApprovalTypeFlag, parseBillStatusForUpdate, parseCommaSeparatedBillStatuses, parseCommaSeparatedBudgetStatuses, parseCommaSeparatedIds, parseCommaSeparatedSupplierStatuses, parseOptionalBillListSortBy, parseOptionalBillListSortDir, parseOptionalDashboardRole, parseOptionalDeals, parseOptionalErrorSeverity, parseOptionalErrorStatus, parseOptionalExtendedProjectStatus, parseOptionalFinancialRole, parseOptionalSupplierAnalyticsTimeFrame, parseOptionalTimeFrame, parseProjectStatusForUpdate, projectStatusesForHelp, } from "./parse-cli-enums.js";
|
|
8
8
|
import { parseJsonFlag, parseOptionalNumber as parseOptNum, } from "./parse-json-flag.js";
|
|
@@ -152,6 +152,7 @@ Bills
|
|
|
152
152
|
list-bills [--projectId] [--budgetId] [--status CSV] [--search <text>] [--isClaimable true|false] [--createdByIds <csv>] [--sortBy createdAt|amount|status] [--sortDir asc|desc] [--page] [--pageSize]
|
|
153
153
|
list-claims same flags as list-bills; only reimbursable claims (ignores --isClaimable)
|
|
154
154
|
isClaimable differentiates the shared bill/claim records: false = bill, true = claim.
|
|
155
|
+
New supplier bills from 1 Jul 2026 00:00 SGT require approved quotation coverage for each non-legacy line item.
|
|
155
156
|
create-bill-approval <billId> (also sends approval request emails)
|
|
156
157
|
create-bill --payload '<json>' (bill.create; payload.isClaimable false = bill, true = claim; use upload-bill-attachment after create to add files)
|
|
157
158
|
update-bill --payload '<json>' (bill.update; must include id)
|
|
@@ -165,8 +166,18 @@ Bills
|
|
|
165
166
|
upload-bill-attachment <billId> <filePath> Presigned S3 upload + confirm (same flow as web UI)
|
|
166
167
|
get-bill-details <billId>
|
|
167
168
|
|
|
169
|
+
Quotations
|
|
170
|
+
list-quotations [--projectId] [--budgetId] [--supplierId] [--status CSV] [--page] [--pageSize]
|
|
171
|
+
get-quotation <quotationId>
|
|
172
|
+
upload-quotation-attachment <projectId> <filePath> Presigned S3 upload; returns attachment JSON for create-quotation payload
|
|
173
|
+
create-quotation --payload '<json>' (quotation.createDraft)
|
|
174
|
+
submit-quotation <quotationId>
|
|
175
|
+
approve-quotation <quotationId>
|
|
176
|
+
reject-quotation <quotationId> --reason <text>
|
|
177
|
+
download-quotation-pdf <quotationId> [original|staff|final]
|
|
178
|
+
|
|
168
179
|
Approvals
|
|
169
|
-
list-approvals | get-pending-approvals [--type budget|supplier|bill|all]
|
|
180
|
+
list-approvals | get-pending-approvals [--type budget|supplier|bill|quotation|all]
|
|
170
181
|
approve-bill <billId> (also sends requester reply email)
|
|
171
182
|
reject-bill <billId> --reason <text> (also sends requester reply email)
|
|
172
183
|
approve-budget <budgetId> (also sends requester reply email)
|
|
@@ -674,6 +685,88 @@ async function main() {
|
|
|
674
685
|
await getBillDetails(id);
|
|
675
686
|
break;
|
|
676
687
|
}
|
|
688
|
+
case "list-quotations": {
|
|
689
|
+
const statusRaw = getFlag(flags, "status");
|
|
690
|
+
await listQuotations({
|
|
691
|
+
projectId: getFlag(flags, "projectId"),
|
|
692
|
+
budgetId: getFlag(flags, "budgetId"),
|
|
693
|
+
supplierId: getFlag(flags, "supplierId"),
|
|
694
|
+
status: statusRaw !== undefined
|
|
695
|
+
? String(statusRaw)
|
|
696
|
+
.split(",")
|
|
697
|
+
.map((value) => value.trim().toUpperCase())
|
|
698
|
+
.filter(Boolean)
|
|
699
|
+
: undefined,
|
|
700
|
+
page: getFlag(flags, "page") !== undefined
|
|
701
|
+
? Number(getFlag(flags, "page"))
|
|
702
|
+
: undefined,
|
|
703
|
+
pageSize: getFlag(flags, "pageSize") !== undefined
|
|
704
|
+
? Number(getFlag(flags, "pageSize"))
|
|
705
|
+
: undefined,
|
|
706
|
+
});
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
case "get-quotation": {
|
|
710
|
+
const id = positional[0];
|
|
711
|
+
if (!id)
|
|
712
|
+
throw new Error("get-quotation requires <quotationId>");
|
|
713
|
+
await getQuotationDetails(id);
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
case "upload-quotation-attachment": {
|
|
717
|
+
const [projectId, filePath] = positional;
|
|
718
|
+
if (!projectId || !filePath) {
|
|
719
|
+
throw new Error("upload-quotation-attachment requires <projectId> <filePath>");
|
|
720
|
+
}
|
|
721
|
+
await uploadQuotationAttachmentFromPath(projectId, filePath);
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
case "create-quotation": {
|
|
725
|
+
const payloadRaw = getFlag(flags, "payload");
|
|
726
|
+
if (!payloadRaw) {
|
|
727
|
+
throw new Error("create-quotation requires --payload '<json>'");
|
|
728
|
+
}
|
|
729
|
+
const raw = parseJsonFlag(String(payloadRaw), "--payload");
|
|
730
|
+
await createQuotationFromPayload(raw);
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
case "submit-quotation": {
|
|
734
|
+
const id = positional[0];
|
|
735
|
+
if (!id)
|
|
736
|
+
throw new Error("submit-quotation requires <quotationId>");
|
|
737
|
+
await submitQuotation(id);
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
case "approve-quotation": {
|
|
741
|
+
const id = positional[0];
|
|
742
|
+
if (!id)
|
|
743
|
+
throw new Error("approve-quotation requires <quotationId>");
|
|
744
|
+
await approveQuotation(id);
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
747
|
+
case "reject-quotation": {
|
|
748
|
+
const id = positional[0];
|
|
749
|
+
const reason = getFlag(flags, "reason");
|
|
750
|
+
if (!id || reason === undefined) {
|
|
751
|
+
throw new Error("reject-quotation requires <quotationId> --reason <text>");
|
|
752
|
+
}
|
|
753
|
+
await rejectQuotation(id, reason);
|
|
754
|
+
break;
|
|
755
|
+
}
|
|
756
|
+
case "download-quotation-pdf": {
|
|
757
|
+
const id = positional[0];
|
|
758
|
+
if (!id) {
|
|
759
|
+
throw new Error("download-quotation-pdf requires <quotationId> [original|staff|final]");
|
|
760
|
+
}
|
|
761
|
+
const roleRaw = (positional[1] ?? "final").toLowerCase();
|
|
762
|
+
const fileRole = roleRaw === "original"
|
|
763
|
+
? "ORIGINAL"
|
|
764
|
+
: roleRaw === "staff"
|
|
765
|
+
? "STAFF_SIGNED"
|
|
766
|
+
: "FINAL_SIGNED";
|
|
767
|
+
await downloadQuotationPdf(id, fileRole);
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
677
770
|
case "list-approvals":
|
|
678
771
|
case "get-pending-approvals": {
|
|
679
772
|
const typeRaw = getFlag(flags, "type");
|
package/dist/parse-cli-enums.js
CHANGED
|
@@ -215,7 +215,7 @@ export function parseOptionalBillListSortDir(raw) {
|
|
|
215
215
|
return raw;
|
|
216
216
|
}
|
|
217
217
|
function isPendingRequestType(s) {
|
|
218
|
-
return s === "BUDGET" || s === "BILL" || s === "SUPPLIER";
|
|
218
|
+
return (s === "BUDGET" || s === "BILL" || s === "SUPPLIER" || s === "QUOTATION");
|
|
219
219
|
}
|
|
220
220
|
export function parseApprovalTypeFlag(raw) {
|
|
221
221
|
if (raw === undefined)
|
|
@@ -224,7 +224,7 @@ export function parseApprovalTypeFlag(raw) {
|
|
|
224
224
|
if (u === "ALL")
|
|
225
225
|
return "ALL";
|
|
226
226
|
if (!isPendingRequestType(u)) {
|
|
227
|
-
throw new Error(`Invalid --type "${raw}". Use budget, supplier, bill, or all.`);
|
|
227
|
+
throw new Error(`Invalid --type "${raw}". Use budget, supplier, bill, quotation, or all.`);
|
|
228
228
|
}
|
|
229
229
|
return u;
|
|
230
230
|
}
|
package/dist/prisma-enums.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@go-labs-sg/bb",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "Budget Builder CLI for AI agents — manage budgets, bills, and claims; bill records use isClaimable=false for bills and isClaimable=true for claims.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|