@go-labs-sg/bb 2.33.0 → 2.33.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 CHANGED
@@ -232,6 +232,36 @@ Every canonical command is classified by effect: `state-change`, `email`, `exter
232
232
 
233
233
  **Draft-only bills:** Use `bb bill create --draft-only --payload '<bill-json>' --allow-state-change --allow-financial-write` when the user authorizes a draft only. The equivalent payload field is `draftOnly: true`. This creates `DRAFT` for every role, without auto-checking, auto-approval, approval requests, emails, or QuickBooks writes. It cannot be combined with `alreadyPaid: true`; conflicting flag and payload values are rejected. Draft-only creation uses the dedicated `bill.createDraft` API route and fails on older servers without falling back to ordinary creation. Deploy backend support before releasing the updated CLI. Reconciled projects must be moved to Won separately before draft-only creation, because reopening a project can trigger external integrations. Completed projects retain their existing creation restriction.
234
234
 
235
+ ### Bill amounts and GST (create and update)
236
+
237
+ **All bill amount fields below exclude GST.** This applies to ordinary creation, draft-only creation, and updates. The CLI does not convert a GST-inclusive invoice total into a subtotal. BB calculates applicable GST separately using the supplier's GST registration and each line's out-of-scope setting.
238
+
239
+ | Payload field | Meaning |
240
+ | --- | --- |
241
+ | `amount` | Amount being billed now, **before GST**. For a deposit or partial bill, use only the portion being billed. |
242
+ | `extractedAmount` | Full supplier invoice amount **before GST**, as shown on the attachment. For a partial bill, this can exceed `amount`. |
243
+ | `lineAmounts[].amount` | This bill's allocation to each selected budget item, **before GST**. The entries must sum to `amount`. |
244
+ | `quotationAllocations[].amount` | Quotation coverage for each bill line, **before GST**. Use the same tax basis as `lineAmounts`. |
245
+
246
+ For an invoice showing **S$1,800 subtotal + S$162 GST = S$1,962 total**, a full bill uses the following amount fields (fragment only; include the other required bill fields):
247
+
248
+ ```json
249
+ {
250
+ "amount": 1800,
251
+ "extractedAmount": 1800,
252
+ "lineAmounts": [{ "budgetItemId": "item-a", "amount": 1800 }],
253
+ "quotationAllocations": [
254
+ { "budgetItemId": "item-a", "quotationId": "quote-a", "amount": 1800 }
255
+ ]
256
+ }
257
+ ```
258
+
259
+ Do **not** put `1962` in either `amount` or `extractedAmount` for this full bill: BB would treat it as a before-GST value. A 50% partial bill against the same invoice uses `amount: 900`, `extractedAmount: 1800`, and line/quotation allocations totaling `900`; with 9% GST applicable to that line, its total is S$981.
260
+
261
+ Use the invoice's actual subtotal and tax breakdown. Do not blindly divide every invoice total by 1.09: non-GST-registered suppliers, out-of-scope lines, and mixed-tax invoices need their actual breakdown. If the attachment does not establish the before-GST amount, clarify it before creating or updating the bill.
262
+
263
+ After a mutation, re-read the bill and verify `amount`, `extractedAmount`, line amounts, and quotation allocations against the attachment. Report the subtotal, GST, and total separately; do not describe the stored `amount` as the GST-inclusive total. When correcting a gross/net input mistake, also check `extractedAmount` so the full invoice is not accidentally represented as a partial bill.
264
+
235
265
  **Quotation-first supplier bills:** Use `quotationIds` when approved quotations cover disjoint bill lines. When two quotations fund the same bill line, pass `quotationAllocations` entries with `{ budgetItemId, quotationId, amount }` and an exact `lineAmounts` entry for every selected budget item; `lineAmounts` must sum to the bill amount. Canonical allocation rows are the stored source of every bill-line quotation relationship. A required zero-value bill line needs one or more allocations with `amount: 0`, which record its approved source(s) without consuming quotation capacity. Every quotation must belong to the bill supplier and project and cover the allocated budget item. Legacy `quotationId` remains valid as single-quotation input and maps to the current selection. Supplier bills that qualify for an existing exemption must state that intent with `--quotation-exempt`. Claims share budget allowance but never consume or link quotations, so quotation source fields are rejected for claims.
236
266
 
237
267
  Every selected bill or claim line must belong to an active budget in `ESTIMATE_ACCEPTED` or `ESTIMATE_CLOSED`, including lines drawn from additional budgets in the project. The server checks this during selection, creation, editing, and approval. An unaccepted budget never qualifies a line for a quotation exemption. Existing pending bills expose budget and source-link blockers in `commitmentReview.blockers`.
@@ -327,7 +357,7 @@ Bill, claim, and quotation mutations accept only suppliers whose approval status
327
357
  | Area | Legacy handler labels (reference only; non-exhaustive) |
328
358
  | --- | --- |
329
359
  | **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`, `rename-budget-version`, `restore-budget-version`, `update-budget-status` (`ESTIMATE_ACCEPTED` requires win proof, auto-marks `PITCH` or `LOST` projects `WON`, and auto-creates an Asana Event Ops section from Prompt 5 tasks when enabled; Prompt 5 skips quantity-zero items, combines matching non-GO-internal suppliers, keeps each GO internal item separate, removes generated work that repeats another line item or group, and schedules applicable subtasks relative to the project start date; `--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`; cloned unavailable lines require `unavailableItemReviewAcknowledged: true`), `delete-budget`, `create-budget-approval` (also sends approval request emails), `create-estimate`, `send-estimate-to-contact-person` (same contact-person email workflow as web), `add-budget-items`, `update-budget-item`, `replace-budget-item`, `approve-unavailable-item-exception` (Lead/Admin), `remove-budget-item`, `reorder-budget-items`, `update-budget-item-supplier`, `mark-budget-items-not-utilized`, `restore-budget-item`, `create-placeholder-bill` (admin recovery), `create-budget-category`, `update-budget-category`, `delete-budget-category`, `update-budget-commission`, `delete-budget-commission`, `update-budget-discount`, `delete-budget-discount` (`--payload` where noted), `upload-budget-attachment` (`<budgetId>` + local file path; uses `attachment.requestBudgetAttachmentUpload` + PUT + `attachment.confirmBudgetAttachment`) |
330
- | **Bills / claims** | `list-bills` (`--isClaimable false` for bills, `--isClaimable true` for claims, omit for both), `list-claims` (claims only), `validate-bill-selection` (`--payload`, plus `--quotationIds q1,q2` or legacy `--quotationId q1`; validates the approved supplier and quotation coverage), `link-bill-quotations` (`<billId> --quotationIds q1,q2`; Admin source-link repair for eligible supplier bills), `stage-bill-attachment` (securely uploads invoice/payment-proof files before creation and returns attachment JSON; ownership and one-hour expiry are enforced by a server-side staged-upload record rather than encoded in the object key), `cleanup-staged-bill-attachments`, `create-bill` (`--payload`; set `isClaimable=false` for a bill and provide approved `quotationIds`, or use `--quotation-exempt` for an eligible server-validated exemption; set `isClaimable=true` for a claim without quotations; supplier bills require positive `extractedAmount` and `amount`, with `amount <= extractedAmount`; an already-paid supplier bill sets `alreadyPaid=true` and requires `paymentReference` plus a staged PDF in `paymentProofAttachments`; admin creation automatically queues QBO finalization while other roles remain pending approval), `update-bill` (`--payload`), `update-bill-payment-evidence` (`--payload`; replaces the payment reference and payment-proof PDFs for an already-paid bill), `delete-bill`, `create-bill-approval` (also sends approval request emails), `update-bill-status` (`PAID` requires `--paymentReference`; pass `--paymentProof <receipt.pdf>` to stage and submit a PDF up to 20MB atomically, or omit it only when BB already has payment proof; moving to `PAID` runs the server's paid-bill notification workflow), `patch-bill-payment` (PAID bills: `--paymentTrackingUrl`, `--paymentReference`, `--quickbooksBillId`, `--paymentDate` ISO; clearing a paid bill's QuickBooks link is not allowed), `patch-bill-invoice-number`, `get-bill-attachments`, `upload-bill-attachment` (`<billId>` + local path), `get-bill-details` |
360
+ | **Bills / claims** | `list-bills` (`--isClaimable false` for bills, `--isClaimable true` for claims, omit for both), `list-claims` (claims only), `validate-bill-selection` (`--payload`, plus `--quotationIds q1,q2` or legacy `--quotationId q1`; validates the approved supplier and quotation coverage), `link-bill-quotations` (`<billId> --quotationIds q1,q2`; Admin source-link repair for eligible supplier bills), `stage-bill-attachment` (securely uploads invoice/payment-proof files before creation and returns attachment JSON; ownership and one-hour expiry are enforced by a server-side staged-upload record rather than encoded in the object key), `cleanup-staged-bill-attachments`, `create-bill` (`--payload`; set `isClaimable=false` for a bill and provide approved `quotationIds`, or use `--quotation-exempt` for an eligible server-validated exemption; set `isClaimable=true` for a claim without quotations; supplier bills require positive **before-GST** `extractedAmount` (full invoice subtotal) and `amount` (portion billed), with `amount <= extractedAmount`; line and quotation allocation amounts also exclude GST; an already-paid supplier bill sets `alreadyPaid=true` and requires `paymentReference` plus a staged PDF in `paymentProofAttachments`; admin creation automatically queues QBO finalization while other roles remain pending approval), `update-bill` (`--payload`), `update-bill-payment-evidence` (`--payload`; replaces the payment reference and payment-proof PDFs for an already-paid bill), `delete-bill`, `create-bill-approval` (also sends approval request emails), `update-bill-status` (`PAID` requires `--paymentReference`; pass `--paymentProof <receipt.pdf>` to stage and submit a PDF up to 20MB atomically, or omit it only when BB already has payment proof; moving to `PAID` runs the server's paid-bill notification workflow), `patch-bill-payment` (PAID bills: `--paymentTrackingUrl`, `--paymentReference`, `--quickbooksBillId`, `--paymentDate` ISO; clearing a paid bill's QuickBooks link is not allowed), `patch-bill-invoice-number`, `get-bill-attachments`, `upload-bill-attachment` (`<billId>` + local path), `get-bill-details` |
331
361
  | **Quotations** | `list-quotations` (supports project, budget, supplier, status, requester, and text-search filters), `get-quotation`, `upload-quotation-attachment` (`<projectId>` + PDF/JPEG/PNG path up to 20MB; returns attachment JSON for payload use), `cleanup-staged-quotation-attachments`, `create-quotation` (`--payload` for `quotation.createDraft`; include `amountWithoutGst` and `gstAmount`), `update-quotation` (`--payload`; updates a `DRAFT` or `REJECTED` quotation), `delete-quotation`, `submit-quotation`, `approve-quotation`, `reject-quotation`, `download-quotation-pdf` (`original`, `staff`, or `final`) |
332
362
  | **Customer invoices** | `check-customer-invoice-readiness`, `list-eligible-customer-invoice-budgets`, `list-customer-invoices` (global/project/budget filters plus summary metrics; `--sortBy totalInvoiceAmount` sorts provider-confirmed totals including GST, with unknown amounts last), `get-customer-invoice`, `get-customer-invoice-email-context`, `preview-qbo-customer-invoice`, `import-qbo-customer-invoice` (BB-only link with reviewed token), `create-customer-invoice`, `discard-customer-invoice`, `delete-customer-invoice`, `void-customer-invoice`, `approve-customer-invoice` (admin), `reject-customer-invoice` (admin), `send-customer-invoice-to-contact-person`, `download-customer-invoice-pdf` (non-admins cannot download while approval is pending), `sync-customer-invoice` |
333
363
  | **Approvals** | `list-approvals` / `get-pending-approvals` (`--type budget\|supplier\|bill\|quotation\|customer_invoice\|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`, `approve-customer-invoice` / `reject-customer-invoice` |
@@ -2063,7 +2063,7 @@
2063
2063
  {
2064
2064
  "path": ["bill", "create"],
2065
2065
  "legacyAliases": ["create-bill", "create_bill"],
2066
- "summary": "Create a bill or claim with an approved supplier. --draft-only guarantees DRAFT without email or external writes; otherwise unpaid creation skips email permission but keeps external-write permission.",
2066
+ "summary": "Create a bill or claim with an approved supplier. All amount fields exclude GST: amount is the portion billed; extractedAmount is the full invoice subtotal; lineAmounts and quotationAllocations also exclude GST. Example: 1800 + 162 GST = 1962 total requires amount=1800 and extractedAmount=1800 for a full bill. The CLI does not strip GST from input. --draft-only guarantees DRAFT without email or external writes; otherwise unpaid creation skips email permission but keeps external-write permission.",
2067
2067
  "globalOptions": [
2068
2068
  {
2069
2069
  "name": "--help",
@@ -2237,7 +2237,7 @@
2237
2237
  {
2238
2238
  "path": ["bill", "update"],
2239
2239
  "legacyAliases": ["update-bill", "update_bill"],
2240
- "summary": "Update Bill.",
2240
+ "summary": "Update a bill or claim; payload must include id. All amount fields exclude GST: amount is the portion billed; extractedAmount is the full invoice subtotal; lineAmounts and quotationAllocations also exclude GST. Example: 1800 + 162 GST = 1962 total requires amount=1800 and extractedAmount=1800 for a full bill. The CLI does not strip GST from input. Re-read and verify both amount fields and allocations after correcting a gross/net mistake.",
2241
2241
  "globalOptions": [
2242
2242
  {
2243
2243
  "name": "--help",
@@ -347,7 +347,7 @@ Effects: state-change, email.
347
347
 
348
348
  ## `bb bill create`
349
349
 
350
- Create a bill or claim with an approved supplier. --draft-only guarantees DRAFT without email or external writes; otherwise unpaid creation skips email permission but keeps external-write permission.
350
+ Create a bill or claim with an approved supplier. All amount fields exclude GST: amount is the portion billed; extractedAmount is the full invoice subtotal; lineAmounts and quotationAllocations also exclude GST. Example: 1800 + 162 GST = 1962 total requires amount=1800 and extractedAmount=1800 for a full bill. The CLI does not strip GST from input. --draft-only guarantees DRAFT without email or external writes; otherwise unpaid creation skips email permission but keeps external-write permission.
351
351
 
352
352
  Legacy aliases: `create-bill`, `create_bill`.
353
353
 
@@ -371,7 +371,7 @@ Effects: state-change, financial-write.
371
371
 
372
372
  ## `bb bill update`
373
373
 
374
- Update Bill.
374
+ Update a bill or claim; payload must include id. All amount fields exclude GST: amount is the portion billed; extractedAmount is the full invoice subtotal; lineAmounts and quotationAllocations also exclude GST. Example: 1800 + 162 GST = 1962 total requires amount=1800 and extractedAmount=1800 for a full bill. The CLI does not strip GST from input. Re-read and verify both amount fields and allocations after correcting a gross/net mistake.
375
375
 
376
376
  Legacy aliases: `update-bill`, `update_bill`.
377
377
 
package/dist/index.js CHANGED
@@ -23444,7 +23444,10 @@ var summaryFor = (legacyTarget) => {
23444
23444
  if (legacyTarget === "whoami")
23445
23445
  return "Show the active API-key identity.";
23446
23446
  if (legacyTarget === "create-bill") {
23447
- return "Create a bill or claim with an approved supplier. --draft-only guarantees DRAFT without email or external writes; otherwise unpaid creation skips email permission but keeps external-write permission.";
23447
+ return "Create a bill or claim with an approved supplier. All amount fields exclude GST: amount is the portion billed; extractedAmount is the full invoice subtotal; lineAmounts and quotationAllocations also exclude GST. Example: 1800 + 162 GST = 1962 total requires amount=1800 and extractedAmount=1800 for a full bill. The CLI does not strip GST from input. --draft-only guarantees DRAFT without email or external writes; otherwise unpaid creation skips email permission but keeps external-write permission.";
23448
+ }
23449
+ if (legacyTarget === "update-bill") {
23450
+ return "Update a bill or claim; payload must include id. All amount fields exclude GST: amount is the portion billed; extractedAmount is the full invoice subtotal; lineAmounts and quotationAllocations also exclude GST. Example: 1800 + 162 GST = 1962 total requires amount=1800 and extractedAmount=1800 for a full bill. The CLI does not strip GST from input. Re-read and verify both amount fields and allocations after correcting a gross/net mistake.";
23448
23451
  }
23449
23452
  if (legacyTarget === "validate-bill-selection") {
23450
23453
  return "Validate the approved supplier and selected bill line items.";
@@ -24134,6 +24137,8 @@ Bills
24134
24137
  list-claims same flags as list-bills; only reimbursable claims (ignores --isClaimable)
24135
24138
  isClaimable differentiates the shared bill/claim records: false = bill, true = claim.
24136
24139
  Create supplier bills from approved quotations from the same supplier and project. Use quotationAllocations plus lineAmounts when quotations split one bill line. Eligible exemptions require explicit --quotation-exempt intent; claims do not consume or link quotations.
24140
+ Amounts for create/update (including drafts) exclude GST: amount = portion billed; extractedAmount = full invoice subtotal; lineAmounts[].amount and quotationAllocations[].amount also exclude GST. The CLI does not strip GST from input.
24141
+ Example: invoice subtotal 1800 + GST 162 = total 1962 -> amount: 1800, extractedAmount: 1800, allocations totaling 1800. BB calculates applicable GST separately. Re-read and verify both amount fields after writing.
24137
24142
  create-bill-approval <billId> (also queues approval request emails)
24138
24143
  create-bill --payload '<json>' [--draft-only] [--quotation-exempt] (bill.create; supplier bills use payload.quotationAllocations for split sources or quotationIds for disjoint coverage; explicit allocations require lineAmounts; claims reject quotation sources)
24139
24144
  validate-bill-selection --payload '<json>' [--quotationIds <csv> | --quotationId <id>] Validate an approved supplier and approved quotation coverage for each supplier-bill line. --quotationId is legacy single-quotation syntax.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@go-labs-sg/bb",
3
- "version": "2.33.0",
3
+ "version": "2.33.1",
4
4
  "description": "Budget Builder CLI for AI agents — manage budgets, bills, claims, quotations, and customer invoices with explicit workflow previews for sensitive changes.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -134,6 +134,18 @@ The following distinctions are especially important:
134
134
  - Ordinary unpaid creation does not require `--allow-email`, but remains role-dependent: Accounting auto-checks and Admin auto-approves. It still requires `--allow-external-write`, including for possible placeholder adjustments. Already-paid creation retains the email gate.
135
135
  - Staged attachments belong to the user in the database. Do not reuse, share, or manually construct staged keys.
136
136
 
137
+ ## Bill amounts: always identify the subtotal before writing
138
+
139
+ For bill creation (including `--draft-only`) and updates, `amount`, `extractedAmount`, `lineAmounts[].amount`, and `quotationAllocations[].amount` all **exclude GST**. The CLI does not strip GST from a supplied total. BB calculates applicable GST separately from the supplier and line settings.
140
+
141
+ - `amount` is the portion being billed now; `extractedAmount` is the full invoice subtotal before GST. They are equal for a full bill and may differ for a deposit or partial bill.
142
+ - Example: an invoice for **S$1,800 + S$162 GST = S$1,962** requires `amount: 1800` and `extractedAmount: 1800` for a full bill, with line/quotation allocations totaling `1800`. Entering `1962` as the bill amount charges GST again.
143
+ - Read the attachment's actual subtotal and tax breakdown. Do not assume every invoice includes 9% GST or blindly divide all totals by 1.09. Clarify missing or ambiguous tax information before writing.
144
+ - After creation or correction, re-read the bill and verify both amount fields and the line/quotation allocations. Correcting only `amount` can leave an incorrect `extractedAmount` suggesting a partial bill.
145
+ - Report **subtotal, GST, and total separately**. Keep the original invoice attachment as evidence; recording GST only in a comment does not correct the structured amount fields.
146
+
147
+ See [Bill amounts and GST](README.md#bill-amounts-and-gst-create-and-update) for payload and partial-bill examples.
148
+
137
149
  ## Handling authorization failures
138
150
 
139
151
  On `FORBIDDEN`: