@go-labs-sg/bb 3.2.0 → 3.4.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 +16 -2
- package/command-manifest.json +57 -3
- package/command-reference.md +17 -9
- package/dist/index.js +79 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ Roll out the API and maintained CLI together: `suppliers`/`pageCount` and offset
|
|
|
61
61
|
|
|
62
62
|
**Error-log read shapes:** `bb error recent list` / `bb get-recent-errors` items now carry only the table columns: `id, severity, status, message, errorName, errorType, component, route, action, createdAt` and `user { id, name, email }`. Stack, cause, request details, metadata, tags, AI summary and resolution moved to the new `bb error get <errorId>` / `bb get-error <errorId>` (`errorLog.getErrorById`), which no longer returns alert-group bookkeeping or the raw `userId`/`resolvedById` columns (use `user`/`resolvedByUser`).
|
|
63
63
|
|
|
64
|
-
**Company read shapes:** `company.getCompanyById` / `bb get-company` now return the company's own fields (`id, name, website, active, quickbooksId, createdById, createdAt, updatedAt`) without the embedded full `projects` array; use `bb list-projects --companyId <id>` for its projects. `bb list-companies` rows carry only `id, name, website, active, createdById, createdAt, updatedAt`, `createdBy { id, name, email, image }` and `_count`; use `bb get-company` for `quickbooksId`. Recent companies and the company QuickBooks sync status read only the fields they use; their outputs are unchanged.
|
|
64
|
+
**Company read shapes:** `company.getCompanyById` / `bb get-company` now return the company's own fields (`id, name, website, billingEmails, active, quickbooksId, createdById, createdAt, updatedAt`) without the embedded full `projects` array; use `bb list-projects --companyId <id>` for its projects. `bb list-companies` rows carry only `id, name, website, billingEmails, active, createdById, createdAt, updatedAt`, `createdBy { id, name, email, image }` and `_count`; use `bb get-company` for `quickbooksId`. Recent companies and the company QuickBooks sync status read only the fields they use; their outputs are unchanged.
|
|
65
65
|
|
|
66
66
|
**Customer-invoice readiness and quotation list loading:** `customerInvoice.getInvoiceReadiness`, `getInvoiceAmountPreview`, `getEligibleBudgets` and `getProjectInvoiceReadiness` read the budget through one selected shape (only the budget, project, contact, invoice-settings, discount and item fields the readiness and amount helpers use) with Prisma's default join instead of forced per-relation queries. The quotation list page and metrics reads also use the default join. Their outputs are unchanged.
|
|
67
67
|
|
|
@@ -134,6 +134,14 @@ Cursors are opaque and scoped to the active account, filters, sort, and page wid
|
|
|
134
134
|
|
|
135
135
|
Deploy the matching API before publishing the CLI. Cursor browsing is not a frozen export snapshot; restart without the cursor when the API reports an expired or mismatched boundary.
|
|
136
136
|
|
|
137
|
+
## Company billing/AP invoice copies
|
|
138
|
+
|
|
139
|
+
Company detail and list reads include `billingEmails`. Set them for a new company with `bb company create --name "Example Co" --website "https://example.com" --billingEmails '["ap@example.com"]'`; existing companies reused by create retain their saved defaults. Use `bb company update --payload` with the current `id`, `name`, `website` and optional `billingEmails` to edit an existing company. Omit the field to preserve it or supply `[]` to clear it. The API validates up to 50 addresses, trims/lowercases and de-duplicates them. These state-changing commands retain their existing confirmation and external-write gates; billing emails themselves stay in BB.
|
|
140
|
+
|
|
141
|
+
`bb customer-invoice email-context get <batchId>` returns removable `billingCcEmails` separately from mandatory `requiredCcEmails`. When sending with `bb customer-invoice send --payload`, omit `cc` to use company defaults; an explicit list replaces those defaults and `[]` opts out. Required internal copies are still added. The queued outgoing-email record freezes the selected recipients for delivery/retry. Quotes/estimates and the primary To selection are unchanged; QuickBooks customer emails are not imported.
|
|
142
|
+
|
|
143
|
+
Apply the additive company schema before deploying the matching API/web and publishing this CLI. Production JTP Holdings acceptance remains pending the approved release; see the [billing/AP rollout and acceptance runbook](../../../docs/runbooks/budget-builder-company-billing-emails.md).
|
|
144
|
+
|
|
137
145
|
## Company discovery and reuse
|
|
138
146
|
|
|
139
147
|
`bb company search --query "a star" [--page 2]` searches stored BB companies and QuickBooks company customers, including inactive records. Results identify `bb`, `qbo`, or `both`; punctuation/spacing normalization is for suggestions only. Search does not write to either system.
|
|
@@ -698,7 +706,7 @@ Admin/Lead users can run `bb supplier approve <supplierId>` to directly re-appro
|
|
|
698
706
|
| **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` (forward cursor pages of active categories, name A–Z: `--q` / `--limit` / `--cursor`; returns `{ items, pageInfo }`), `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, adds 5 mandatory supplier subtasks ending in "Get bill and upload to Budget Builder", and sets no subtask due dates; `--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`) |
|
|
699
707
|
| **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` (newest-first cursor pages: `--limit` 1–50, `--cursor`; returns `{ items, pageInfo }`), `upload-bill-attachment` (`<billId>` + local path), `get-bill-details` (the web bill summary: status, amounts, people, payment, line totals), `get-bill-line-items` / `get-bill-approvals` (newest-first cursor pages: `--limit`, `--cursor`; `{ items, pageInfo }`), `get-bill-payment` (payment status, reference, date, tracking URL, QuickBooks link, external payment) |
|
|
700
708
|
| **Quotations** | `list-quotations` (project, budget, supplier, status, requester and text-search filters; newest-first cursor pages via `--limit` / `--cursor` / `--direction` returning `{ items, pageInfo }`; `--page` / `--perPage` / `--pageSize` are rejected), `count-quotations` (`quotation.getCounts`: filters only, returns `totalCount`, status facets that ignore their own status filter, and `--includeMetrics true` amount metrics), `get-quotation` (web quotation shape), `get-quotation-line-items` (`<quotationId>`; newest-first cursor pages, `--limit 1-100`, `--cursor`), `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`, `gstAmount`, and `lineAmounts` entries `{ budgetItemId, amount }` for multiple selected items), `update-quotation` (`--payload`; updates a `DRAFT` or `REJECTED` quotation), `delete-quotation`, `submit-quotation`, `approve-quotation`, `reject-quotation`, `download-quotation-pdf` (`original`, `staff`, or `final`) |
|
|
701
|
-
| **Customer invoices** | `check-customer-invoice-readiness`, `list-eligible-customer-invoice-budgets`, `preview-customer-invoice-amount`, `list-available-qbo-customer-invoices`, `list-customer-invoices` (global/project/budget filters; cursor pages via `--limit` / `--cursor` / `--direction` returning `{ items, pageInfo }`, `--page` / `--perPage` rejected; `--sortBy totalInvoiceAmount` sorts provider-confirmed totals including GST, with unknown amounts last), `count-customer-invoices` (`customerInvoice.getCounts`: filters only; `totalCount`, lifecycle status facets that ignore their own status filter, and amount metrics), `get-customer-invoice` (web-shaped batch), `get-customer-invoice-invoices`, `get-customer-invoice-lines`, `get-customer-invoice-approvals`, `get-customer-invoice-history`, `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` |
|
|
709
|
+
| **Customer invoices** | `check-customer-invoice-readiness`, `list-eligible-customer-invoice-budgets`, `preview-customer-invoice-amount`, `list-available-qbo-customer-invoices`, `list-customer-invoices` (global/project/budget filters; cursor pages via `--limit` / `--cursor` / `--direction` returning `{ items, pageInfo }`, `--page` / `--perPage` rejected; `--sortBy totalInvoiceAmount` sorts provider-confirmed totals including GST, with unknown amounts last), `count-customer-invoices` (`customerInvoice.getCounts`: filters only; `totalCount`, lifecycle status facets that ignore their own status filter, and amount metrics), `get-customer-invoice` (web-shaped batch), `get-customer-invoice-invoices`, `get-customer-invoice-lines`, `get-customer-invoice-approvals`, `get-customer-invoice-history`, `get-customer-invoice-email-context`, `preview-qbo-customer-invoice`, `import-qbo-customer-invoice` (BB-only link with reviewed token), `create-customer-invoice`, `resubmit-customer-invoice` (original requester only; one replacement after confirmed expiry voiding; repeated calls return the same replacement), `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` |
|
|
702
710
|
| **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` |
|
|
703
711
|
| **Companies & projects** | `list-companies`, `get-company`, `create-company`, `update-company` (`--payload`), `delete-company`, `list-projects`, `get-project` (its budget overview returns `totalRevenue`, calculated only from Estimate Accepted and Estimate Closed budgets), `get-project-hub-status`, `setup-project-hub`, `sync-project-hub-commercial-documents`, `create-project` (required: `--name`, `--companyId`, `--contactPersonId`, `--insideSalesId`, `--businessDevelopmentId`, `--venue`, `--startDate` as ISO datetime for project/window start; when `--asanaTaskId` is omitted, the CLI searches open Asana lead tasks in the Deals project, prompts for one of the top five matches, and resolves Slack channel fields from the selected deal card; optional `--asanaSearch`, `--pax`, `--endDate`, `--description`, `--projectManagerId`; always requests QBO project import like the web app), `update-project` (`--payload` with `dateRange.from` / `dateRange.to` for the project window; optional `projectManagerId` and `requestQboAccountantNotification` in JSON), `delete-project`, `check-project-reconciliation <id>` (runs the web app's live checks without changing status), `reconcile-project <id>` (reruns checks transactionally and marks an eligible project `RECONCILED`), `complete-project <id>` (reruns validation, marks a reconciled project `COMPLETED`, and queues QuickBooks placeholder cleanup), `import-qbo-project`, `update-project-status` (`<id>` `<status>`: `PITCH` \| `WON` \| `COMPLETED` \| `RECONCILED` \| `LOST`; close-out follows `WON` → `RECONCILED` → `COMPLETED`, and reconciliation requires every accepted/closed budget line to have an Approved/Paid bill or claim or be explicitly Not Utilized; marking `COMPLETED` queues deletion of every remaining project placeholder bill from QuickBooks; for `PITCH` → `WON` also pass a Budget Builder user `--projectManagerId` or `--projectManagerEmail`; when marking `WON` without an accepted/closed budget or proof, pass `--wonOverrideReason`) |
|
|
704
712
|
| **Contacts** | `list-contacts`, `create-contact-person` (`--payload`), `update-contact-person` (`--payload`) |
|
|
@@ -710,6 +718,12 @@ Admin/Lead users can run `bb supplier approve <supplierId>` to directly re-appro
|
|
|
710
718
|
|
|
711
719
|
The CLI intentionally wraps low-level upload-request/confirm procedures into file-based commands and omits browser-only helpers such as navigation counts, combobox/facet data, recent-page bookkeeping, and live UI subscriptions. Operational workflows for bills, claims, quotations, customer invoices, project reconciliation/completion, and integration recovery are available directly.
|
|
712
720
|
|
|
721
|
+
## Customer invoice approval expiry
|
|
722
|
+
|
|
723
|
+
New requests allow 72 elapsed hours and schedule one approver reminder four hours before the stored deadline. Older deadlines remain unchanged. Invoice lifecycle outputs/`--status` distinguish `EXPIRED` (approval elapsed, void not yet confirmed) from `EXPIRED_VOIDED`.
|
|
724
|
+
|
|
725
|
+
Use `bb customer-invoice resubmit <batchId>` (`resubmit-customer-invoice`) as the original requester after confirmed voiding. The normal financial/external-write/email confirmation gates apply. It queues one replacement from the current eligible estimate using the original percentage, due date and details; retries reuse the same replacement. The API rechecks readiness/coverage and does not send to the customer. Deploy the compatible API before this CLI. Legacy multi-invoice batches require manual creation.
|
|
726
|
+
|
|
713
727
|
## Output
|
|
714
728
|
|
|
715
729
|
- **Canonical stdout:** a pretty-printed `{ "ok": true, "data": ..., "meta": ... }` JSON envelope
|
package/command-manifest.json
CHANGED
|
@@ -4937,6 +4937,56 @@
|
|
|
4937
4937
|
"effects": ["state-change", "email", "external-write", "financial-write"],
|
|
4938
4938
|
"legacyTarget": "create-customer-invoice"
|
|
4939
4939
|
},
|
|
4940
|
+
{
|
|
4941
|
+
"path": ["customer-invoice", "resubmit"],
|
|
4942
|
+
"legacyAliases": ["resubmit-customer-invoice", "resubmit_customer_invoice"],
|
|
4943
|
+
"summary": "Resubmit a confirmed expired-and-voided BB invoice as its original requester. Argument: <batchId>. Queues one replacement using the current estimate, original percentage, due date and details; retries return the same replacement. No customer email.",
|
|
4944
|
+
"globalOptions": [
|
|
4945
|
+
{
|
|
4946
|
+
"name": "--help",
|
|
4947
|
+
"description": "Show help for this command."
|
|
4948
|
+
},
|
|
4949
|
+
{
|
|
4950
|
+
"name": "--quiet",
|
|
4951
|
+
"description": "Suppress non-error diagnostics."
|
|
4952
|
+
},
|
|
4953
|
+
{
|
|
4954
|
+
"name": "--debug",
|
|
4955
|
+
"description": "Emit sanitized diagnostic traces."
|
|
4956
|
+
},
|
|
4957
|
+
{
|
|
4958
|
+
"name": "--api-url",
|
|
4959
|
+
"description": "Override the Budget Builder API base URL."
|
|
4960
|
+
},
|
|
4961
|
+
{
|
|
4962
|
+
"name": "--no-browser",
|
|
4963
|
+
"description": "Print the authorization URL without opening a browser."
|
|
4964
|
+
},
|
|
4965
|
+
{
|
|
4966
|
+
"name": "--allow-state-change",
|
|
4967
|
+
"description": "Allow a Budget Builder state change in non-interactive use."
|
|
4968
|
+
},
|
|
4969
|
+
{
|
|
4970
|
+
"name": "--allow-email",
|
|
4971
|
+
"description": "Allow sending email in non-interactive use."
|
|
4972
|
+
},
|
|
4973
|
+
{
|
|
4974
|
+
"name": "--allow-external-write",
|
|
4975
|
+
"description": "Allow writes to external systems in non-interactive use."
|
|
4976
|
+
},
|
|
4977
|
+
{
|
|
4978
|
+
"name": "--allow-delete",
|
|
4979
|
+
"description": "Allow deleting data in non-interactive use."
|
|
4980
|
+
},
|
|
4981
|
+
{
|
|
4982
|
+
"name": "--allow-financial-write",
|
|
4983
|
+
"description": "Allow financial-record changes in non-interactive use."
|
|
4984
|
+
}
|
|
4985
|
+
],
|
|
4986
|
+
"argumentMode": "legacy-passthrough",
|
|
4987
|
+
"effects": ["state-change", "external-write", "financial-write", "email"],
|
|
4988
|
+
"legacyTarget": "resubmit-customer-invoice"
|
|
4989
|
+
},
|
|
4940
4990
|
{
|
|
4941
4991
|
"path": ["customer-invoice", "discard"],
|
|
4942
4992
|
"legacyAliases": ["discard-customer-invoice", "discard_customer_invoice"],
|
|
@@ -5193,7 +5243,7 @@
|
|
|
5193
5243
|
"send-customer-invoice-to-contact-person",
|
|
5194
5244
|
"send_customer_invoice_to_contact_person"
|
|
5195
5245
|
],
|
|
5196
|
-
"summary": "Send a customer invoice and update the project billing email.",
|
|
5246
|
+
"summary": "Send a customer invoice and update the project billing email. Omit payload cc to use company billing defaults; an explicit list replaces them and [] removes them. Required internal Cc still applies.",
|
|
5197
5247
|
"globalOptions": [
|
|
5198
5248
|
{
|
|
5199
5249
|
"name": "--help",
|
|
@@ -6081,7 +6131,7 @@
|
|
|
6081
6131
|
{
|
|
6082
6132
|
"path": ["company", "create"],
|
|
6083
6133
|
"legacyAliases": ["create-company", "create_company"],
|
|
6084
|
-
"summary": "Create or reuse a company in BB and QuickBooks. --name <name> --website <url
|
|
6134
|
+
"summary": "Create or reuse a company in BB and QuickBooks. --name <name> --website <url> [--billingEmails '<json-array>'] (up to 50 invoice Cc defaults, stored in BB only for new companies; reused company defaults are preserved). Similar names require --confirmDistinct true; inactive companies are never reactivated.",
|
|
6085
6135
|
"globalOptions": [
|
|
6086
6136
|
{
|
|
6087
6137
|
"name": "--help",
|
|
@@ -6123,6 +6173,10 @@
|
|
|
6123
6173
|
"name": "--allow-financial-write",
|
|
6124
6174
|
"description": "Allow financial-record changes in non-interactive use."
|
|
6125
6175
|
},
|
|
6176
|
+
{
|
|
6177
|
+
"name": "--billingEmails",
|
|
6178
|
+
"description": "JSON email array (max 50) for new company invoice Cc defaults; reused companies keep their saved defaults."
|
|
6179
|
+
},
|
|
6126
6180
|
{
|
|
6127
6181
|
"name": "--confirmDistinct",
|
|
6128
6182
|
"description": "Set true to confirm a separate legal entity despite similar names."
|
|
@@ -6135,7 +6189,7 @@
|
|
|
6135
6189
|
{
|
|
6136
6190
|
"path": ["company", "update"],
|
|
6137
6191
|
"legacyAliases": ["update-company", "update_company"],
|
|
6138
|
-
"summary": "Save company fields in BB and queue independent QuickBooks name/website synchronization; returns operation ID, not provider completion.",
|
|
6192
|
+
"summary": "Save company fields in BB and queue independent QuickBooks name/website synchronization; returns operation ID, not provider completion. Optional payload billingEmails: email string array (max 50), omit to preserve defaults or [] to clear. Billing emails stay in BB; estimates are unchanged.",
|
|
6139
6193
|
"globalOptions": [
|
|
6140
6194
|
{
|
|
6141
6195
|
"name": "--help",
|
package/command-reference.md
CHANGED
|
@@ -184,7 +184,7 @@ Effects: state-change.
|
|
|
184
184
|
|
|
185
185
|
Payload fields (--payload JSON)
|
|
186
186
|
Required: name: string; budget: decimal string; paymentTerm: string; categoryId: string; projectId: string
|
|
187
|
-
Optional: sourceBudgetId: string; unavailableItemReviewAcknowledged: boolean (only after review)
|
|
187
|
+
Optional: sourceBudgetId: string; unavailableItemReviewAcknowledged: boolean (only after review)
|
|
188
188
|
Example: bb create-budget --payload '{"name":"Event budget","budget":"1500","paymentTerm":"Net 30","categoryId":"<category-id>","projectId":"<project-id>"}'
|
|
189
189
|
|
|
190
190
|
## `bb budget update`
|
|
@@ -197,7 +197,7 @@ Effects: state-change.
|
|
|
197
197
|
|
|
198
198
|
Payload fields (--payload JSON)
|
|
199
199
|
Required: id: string; name: string; budget: decimal string; paymentTerm: string; categoryId: string; projectId: string
|
|
200
|
-
Optional: sourceBudgetId: string
|
|
200
|
+
Optional: sourceBudgetId: string
|
|
201
201
|
Example: bb update-budget --payload '{"id":"<budget-id>","name":"Event budget","budget":"1500","paymentTerm":"Net 30","categoryId":"<category-id>","projectId":"<project-id>"}'
|
|
202
202
|
|
|
203
203
|
## `bb budget delete`
|
|
@@ -826,6 +826,14 @@ Payload fields (--payload JSON)
|
|
|
826
826
|
Optional: requestId: UUID string (recommended); invoiceDate: ISO date; details: {billingAddress: string, customerMemo: string}; isCustomPercentage: boolean
|
|
827
827
|
Example: bb create-customer-invoice --payload '{"budgetId":"<budget-id>","splits":[{"label":"Deposit","percentage":50,"dueDate":"2026-10-15"}],"details":{"billingAddress":"1 Example St","customerMemo":"Thanks"}}'
|
|
828
828
|
|
|
829
|
+
## `bb customer-invoice resubmit`
|
|
830
|
+
|
|
831
|
+
Resubmit a confirmed expired-and-voided BB invoice as its original requester. Argument: <batchId>. Queues one replacement using the current estimate, original percentage, due date and details; retries return the same replacement. No customer email.
|
|
832
|
+
|
|
833
|
+
Legacy aliases: `resubmit-customer-invoice`, `resubmit_customer_invoice`.
|
|
834
|
+
|
|
835
|
+
Effects: state-change, external-write, financial-write, email.
|
|
836
|
+
|
|
829
837
|
## `bb customer-invoice discard`
|
|
830
838
|
|
|
831
839
|
Discard Customer Invoice.
|
|
@@ -868,15 +876,15 @@ Effects: state-change, email, external-write, financial-write.
|
|
|
868
876
|
|
|
869
877
|
## `bb customer-invoice send`
|
|
870
878
|
|
|
871
|
-
Send a customer invoice and update the project billing email.
|
|
879
|
+
Send a customer invoice and update the project billing email. Omit payload cc to use company billing defaults; an explicit list replaces them and [] removes them. Required internal Cc still applies.
|
|
872
880
|
|
|
873
881
|
Legacy aliases: `send-customer-invoice-to-contact-person`, `send_customer_invoice_to_contact_person`.
|
|
874
882
|
|
|
875
883
|
Effects: state-change, email.
|
|
876
884
|
|
|
877
885
|
Payload fields (--payload JSON)
|
|
878
|
-
Required: invoiceId: string; to: email;
|
|
879
|
-
Optional:
|
|
886
|
+
Required: invoiceId: string; to: email; replyTo: email; subject: string; content: HTML string; signature: HTML string
|
|
887
|
+
Optional: cc: email[] (max 50; omit to use company billing defaults, explicit list replaces defaults, [] removes defaults; required internal Cc still applies)
|
|
880
888
|
Example: bb send-customer-invoice-to-contact-person --payload '{"invoiceId":"<invoice-id>","to":"client@example.com","cc":[],"replyTo":"team@example.com","subject":"Invoice","content":"<p>Attached invoice</p>","signature":"<p>Team</p>"}'
|
|
881
889
|
|
|
882
890
|
## `bb customer-invoice pdf download`
|
|
@@ -1001,7 +1009,7 @@ Effects: state-change, external-write.
|
|
|
1001
1009
|
|
|
1002
1010
|
## `bb company create`
|
|
1003
1011
|
|
|
1004
|
-
Create or reuse a company in BB and QuickBooks. --name <name> --website <url
|
|
1012
|
+
Create or reuse a company in BB and QuickBooks. --name <name> --website <url> [--billingEmails '<json-array>'] (up to 50 invoice Cc defaults, stored in BB only for new companies; reused company defaults are preserved). Similar names require --confirmDistinct true; inactive companies are never reactivated.
|
|
1005
1013
|
|
|
1006
1014
|
Legacy aliases: `create-company`, `create_company`.
|
|
1007
1015
|
|
|
@@ -1009,7 +1017,7 @@ Effects: state-change, external-write.
|
|
|
1009
1017
|
|
|
1010
1018
|
## `bb company update`
|
|
1011
1019
|
|
|
1012
|
-
Save company fields in BB and queue independent QuickBooks name/website synchronization; returns operation ID, not provider completion.
|
|
1020
|
+
Save company fields in BB and queue independent QuickBooks name/website synchronization; returns operation ID, not provider completion. Optional payload billingEmails: email string array (max 50), omit to preserve defaults or [] to clear. Billing emails stay in BB; estimates are unchanged.
|
|
1013
1021
|
|
|
1014
1022
|
Legacy aliases: `update-company`, `update_company`.
|
|
1015
1023
|
|
|
@@ -1017,8 +1025,8 @@ Effects: state-change, external-write.
|
|
|
1017
1025
|
|
|
1018
1026
|
Payload fields (--payload JSON)
|
|
1019
1027
|
Required: id: string; name: string; website: URL string
|
|
1020
|
-
Optional: confirmDistinct: boolean
|
|
1021
|
-
Example: bb update-company --payload '{"id":"<company-id>","name":"Example Co","website":"https://example.com"}'
|
|
1028
|
+
Optional: confirmDistinct: boolean; billingEmails: email string[] (max 50; omit to preserve, [] to clear; trimmed, lowercased and deduplicated by the API)
|
|
1029
|
+
Example: bb update-company --payload '{"id":"<company-id>","name":"Example Co","website":"https://example.com","billingEmails":["ap@example.com","finance@example.com"]}'
|
|
1022
1030
|
|
|
1023
1031
|
## `bb company sync status`
|
|
1024
1032
|
|
package/dist/index.js
CHANGED
|
@@ -7337,8 +7337,13 @@ var invoiceSplitSetupNextAction = (budgetId) => ({
|
|
|
7337
7337
|
createCommand: "bb customer-invoice create --payload '<budgetId-requestId-invoiceDate-details-and-splits-json>'"
|
|
7338
7338
|
});
|
|
7339
7339
|
|
|
7340
|
+
// ../utils/src/customer-invoice-approval-policy.ts
|
|
7341
|
+
var CUSTOMER_INVOICE_APPROVAL_WINDOW_MS = 72 * 60 * 60 * 1000;
|
|
7342
|
+
var CUSTOMER_INVOICE_APPROVAL_REMINDER_LEAD_MS = 4 * 60 * 60 * 1000;
|
|
7343
|
+
|
|
7340
7344
|
// ../utils/src/customer-invoice-lifecycle.ts
|
|
7341
7345
|
var customerInvoiceLifecycleStatuses = [
|
|
7346
|
+
"EXPIRED",
|
|
7342
7347
|
...Object.values(CustomerInvoiceBatchStatus),
|
|
7343
7348
|
"PARTIALLY_SENT",
|
|
7344
7349
|
CustomerInvoiceStatus.SENT,
|
|
@@ -7870,8 +7875,10 @@ var parseSendEstimateToContactPersonPayload = (raw) => {
|
|
|
7870
7875
|
};
|
|
7871
7876
|
var parseSendCustomerInvoiceToContactPersonPayload = (raw) => {
|
|
7872
7877
|
const input = requireObject(raw, "send-customer-invoice-to-contact-person payload");
|
|
7878
|
+
const { cc, ...email } = parseContactPersonEmailPayload({ ...input, cc: input.cc === undefined ? [] : input.cc }, "send-customer-invoice-to-contact-person");
|
|
7873
7879
|
return {
|
|
7874
|
-
...
|
|
7880
|
+
...email,
|
|
7881
|
+
...input.cc === undefined ? {} : { cc },
|
|
7875
7882
|
invoiceId: requiredString(input.invoiceId, "send-customer-invoice-to-contact-person payload.invoiceId")
|
|
7876
7883
|
};
|
|
7877
7884
|
};
|
|
@@ -7889,8 +7896,7 @@ function parseCreateBudgetPayload(raw) {
|
|
|
7889
7896
|
sourceBudgetId: optionalString(o.sourceBudgetId),
|
|
7890
7897
|
...o.unavailableItemReviewAcknowledged !== undefined && {
|
|
7891
7898
|
unavailableItemReviewAcknowledged: o.unavailableItemReviewAcknowledged
|
|
7892
|
-
}
|
|
7893
|
-
pipedriveDealId: optionalString(o.pipedriveDealId)
|
|
7899
|
+
}
|
|
7894
7900
|
};
|
|
7895
7901
|
}
|
|
7896
7902
|
function parseUpdateBudgetPayload(raw) {
|
|
@@ -8369,9 +8375,32 @@ function parseContactUpdatePayload(raw) {
|
|
|
8369
8375
|
return raw;
|
|
8370
8376
|
}
|
|
8371
8377
|
function parseCompanyUpdatePayload(raw) {
|
|
8372
|
-
requireObject(raw, "update-company payload");
|
|
8373
|
-
|
|
8378
|
+
const input = requireObject(raw, "update-company payload");
|
|
8379
|
+
const billingEmails = parseCompanyBillingEmails(input.billingEmails);
|
|
8380
|
+
if (input.confirmDistinct !== undefined && typeof input.confirmDistinct !== "boolean") {
|
|
8381
|
+
throw validationError("confirmDistinct must be a boolean.");
|
|
8382
|
+
}
|
|
8383
|
+
return {
|
|
8384
|
+
id: requiredString(input.id, "id"),
|
|
8385
|
+
name: requiredString(input.name, "name"),
|
|
8386
|
+
website: requiredString(input.website, "website"),
|
|
8387
|
+
...input.confirmDistinct === undefined ? {} : { confirmDistinct: input.confirmDistinct },
|
|
8388
|
+
...billingEmails === undefined ? {} : { billingEmails }
|
|
8389
|
+
};
|
|
8374
8390
|
}
|
|
8391
|
+
var parseCompanyBillingEmails = (raw) => {
|
|
8392
|
+
if (raw === undefined)
|
|
8393
|
+
return;
|
|
8394
|
+
if (!Array.isArray(raw) || raw.length > 50) {
|
|
8395
|
+
throw validationError("billingEmails must be a JSON array of up to 50 email strings.");
|
|
8396
|
+
}
|
|
8397
|
+
return raw.map((email) => {
|
|
8398
|
+
if (typeof email !== "string") {
|
|
8399
|
+
throw validationError("billingEmails must contain only email strings.");
|
|
8400
|
+
}
|
|
8401
|
+
return email;
|
|
8402
|
+
});
|
|
8403
|
+
};
|
|
8375
8404
|
|
|
8376
8405
|
// src/runtime/confirmation.ts
|
|
8377
8406
|
var effectLabels = {
|
|
@@ -10031,6 +10060,14 @@ var createCustomerInvoice = async (raw) => {
|
|
|
10031
10060
|
const result = await api2.customerInvoice.createInvoiceBatch.mutate(input);
|
|
10032
10061
|
out(result);
|
|
10033
10062
|
};
|
|
10063
|
+
var resubmitCustomerInvoice = async (batchId) => {
|
|
10064
|
+
await assertSensitiveWorkflowConfirmed({
|
|
10065
|
+
action: "Resubmit expired customer invoice",
|
|
10066
|
+
entity: `invoice batch ${batchId}`,
|
|
10067
|
+
details: "queues one replacement QuickBooks invoice after confirmed expiry voiding, using the current estimate and the original percentage, due date and details; only the original requester may resubmit; no customer email is sent"
|
|
10068
|
+
});
|
|
10069
|
+
out(await api2.customerInvoice.resubmitInvoiceBatch.mutate({ batchId }));
|
|
10070
|
+
};
|
|
10034
10071
|
var discardCreatingCustomerInvoice = async (batchId) => {
|
|
10035
10072
|
await assertSensitiveWorkflowConfirmed({
|
|
10036
10073
|
action: "Discard unfinished customer invoice",
|
|
@@ -10108,7 +10145,7 @@ var syncCustomerInvoice = async (invoiceId) => {
|
|
|
10108
10145
|
});
|
|
10109
10146
|
out(result);
|
|
10110
10147
|
};
|
|
10111
|
-
var describeCustomerInvoiceEmailConfirmation = (input) => `emails the QBO invoice PDF to ${input.to} and saves that address as the project's billing email; requested CC ${input.cc.length > 0 ? input.cc.join(", ") : "none"}; reply-to ${input.replyTo}; subject "${input.subject}"; server-required admin, creator, business-development, and inside-sales CC recipients are added; success marks the invoice SENT; do not retry blindly after an ambiguous delivery failure`;
|
|
10148
|
+
var describeCustomerInvoiceEmailConfirmation = (input) => `emails the QBO invoice PDF to ${input.to} and saves that address as the project's billing email; requested CC ${input.cc === undefined ? "company billing defaults" : input.cc.length > 0 ? input.cc.join(", ") : "none (company billing defaults removed)"}; reply-to ${input.replyTo}; subject "${input.subject}"; server-required admin, creator, business-development, and inside-sales CC recipients are added; success marks the invoice SENT; do not retry blindly after an ambiguous delivery failure`;
|
|
10112
10149
|
var sendCustomerInvoiceToContactPersonFromPayload = async (raw) => {
|
|
10113
10150
|
const input = parseSendCustomerInvoiceToContactPersonPayload(raw);
|
|
10114
10151
|
await assertSensitiveWorkflowConfirmed({
|
|
@@ -11041,12 +11078,12 @@ function parseOptionalNumber(raw, label) {
|
|
|
11041
11078
|
var payloadHelp = {
|
|
11042
11079
|
"create-budget": {
|
|
11043
11080
|
required: "name: string; budget: decimal string; paymentTerm: string; categoryId: string; projectId: string",
|
|
11044
|
-
optional: "sourceBudgetId: string; unavailableItemReviewAcknowledged: boolean (only after review)
|
|
11081
|
+
optional: "sourceBudgetId: string; unavailableItemReviewAcknowledged: boolean (only after review)",
|
|
11045
11082
|
example: '{"name":"Event budget","budget":"1500","paymentTerm":"Net 30","categoryId":"<category-id>","projectId":"<project-id>"}'
|
|
11046
11083
|
},
|
|
11047
11084
|
"update-budget": {
|
|
11048
11085
|
required: "id: string; name: string; budget: decimal string; paymentTerm: string; categoryId: string; projectId: string",
|
|
11049
|
-
optional: "sourceBudgetId: string
|
|
11086
|
+
optional: "sourceBudgetId: string",
|
|
11050
11087
|
example: '{"id":"<budget-id>","name":"Event budget","budget":"1500","paymentTerm":"Net 30","categoryId":"<category-id>","projectId":"<project-id>"}'
|
|
11051
11088
|
},
|
|
11052
11089
|
"create-item": {
|
|
@@ -11071,8 +11108,8 @@ var payloadHelp = {
|
|
|
11071
11108
|
},
|
|
11072
11109
|
"update-company": {
|
|
11073
11110
|
required: "id: string; name: string; website: URL string",
|
|
11074
|
-
optional: "confirmDistinct: boolean",
|
|
11075
|
-
example: '{"id":"<company-id>","name":"Example Co","website":"https://example.com"}'
|
|
11111
|
+
optional: "confirmDistinct: boolean; billingEmails: email string[] (max 50; omit to preserve, [] to clear; trimmed, lowercased and deduplicated by the API)",
|
|
11112
|
+
example: '{"id":"<company-id>","name":"Example Co","website":"https://example.com","billingEmails":["ap@example.com","finance@example.com"]}'
|
|
11076
11113
|
},
|
|
11077
11114
|
"update-project": {
|
|
11078
11115
|
required: "id: string; name: string; companyId: string; contactPersonId: string; asanaTaskId: string; insideSalesId: string; businessDevelopmentId: string; venue: string; dateRange: {from: ISO date}",
|
|
@@ -11145,8 +11182,8 @@ var payloadHelp = {
|
|
|
11145
11182
|
example: '{"budgetId":"<budget-id>","splits":[{"label":"Deposit","percentage":50,"dueDate":"2026-10-15"}],"details":{"billingAddress":"1 Example St","customerMemo":"Thanks"}}'
|
|
11146
11183
|
},
|
|
11147
11184
|
"send-customer-invoice-to-contact-person": {
|
|
11148
|
-
required: "invoiceId: string; to: email;
|
|
11149
|
-
optional: "
|
|
11185
|
+
required: "invoiceId: string; to: email; replyTo: email; subject: string; content: HTML string; signature: HTML string",
|
|
11186
|
+
optional: "cc: email[] (max 50; omit to use company billing defaults, explicit list replaces defaults, [] removes defaults; required internal Cc still applies)",
|
|
11150
11187
|
example: '{"invoiceId":"<invoice-id>","to":"client@example.com","cc":[],"replyTo":"team@example.com","subject":"Invoice","content":"<p>Attached invoice</p>","signature":"<p>Team</p>"}'
|
|
11151
11188
|
},
|
|
11152
11189
|
"save-inbound-mapping": {
|
|
@@ -11354,6 +11391,12 @@ var deleteTargets = new Set([
|
|
|
11354
11391
|
"update-project-status"
|
|
11355
11392
|
]);
|
|
11356
11393
|
var explicitEffects = {
|
|
11394
|
+
"resubmit-customer-invoice": [
|
|
11395
|
+
"state-change",
|
|
11396
|
+
"external-write",
|
|
11397
|
+
"financial-write",
|
|
11398
|
+
"email"
|
|
11399
|
+
],
|
|
11357
11400
|
"rotate-webhook-secret": ["state-change"],
|
|
11358
11401
|
"replay-webhook-delivery": ["state-change", "external-write"]
|
|
11359
11402
|
};
|
|
@@ -11445,7 +11488,7 @@ var summaryFor = (legacyTarget) => {
|
|
|
11445
11488
|
if (legacyTarget === "use-existing-company")
|
|
11446
11489
|
return "Reuse a company in BB and QuickBooks, creating only its missing counterpart. Supply --companyId <bbId> and/or --quickbooksId <qboId>; both explicitly connect records with different names. --website <url> fills a missing QBO import website only. --confirmDistinct true confirms no similar record represents this entity.";
|
|
11447
11490
|
if (legacyTarget === "create-company")
|
|
11448
|
-
return "Create or reuse a company in BB and QuickBooks. --name <name> --website <url
|
|
11491
|
+
return "Create or reuse a company in BB and QuickBooks. --name <name> --website <url> [--billingEmails '<json-array>'] (up to 50 invoice Cc defaults, stored in BB only for new companies; reused company defaults are preserved). Similar names require --confirmDistinct true; inactive companies are never reactivated.";
|
|
11449
11492
|
if (legacyTarget === "create-project")
|
|
11450
11493
|
return "Create a project linked to an existing Asana Deals lead task. Supply --asanaTaskId in non-interactive use; interactive use searches for and selects a lead task (optionally --asanaSearch). The Slack channel is resolved from the selected Asana task unless all three Slack channel flags are supplied. For a new direct client, create the lead task using the website form first.";
|
|
11451
11494
|
if (legacyTarget === "create-supplier")
|
|
@@ -11457,6 +11500,8 @@ var summaryFor = (legacyTarget) => {
|
|
|
11457
11500
|
return "Preview amount before GST, GST, and total for a percentage of an accepted estimate. Arguments: <budgetId> <percentage-of-estimate>.";
|
|
11458
11501
|
if (legacyTarget === "list-available-qbo-customer-invoices")
|
|
11459
11502
|
return "List unlinked QuickBooks invoices available for an accepted or closed estimate. Argument: <budgetId>.";
|
|
11503
|
+
if (legacyTarget === "resubmit-customer-invoice")
|
|
11504
|
+
return "Resubmit a confirmed expired-and-voided BB invoice as its original requester. Argument: <batchId>. Queues one replacement using the current estimate, original percentage, due date and details; retries return the same replacement. No customer email.";
|
|
11460
11505
|
if (legacyTarget === "create-customer-invoice")
|
|
11461
11506
|
return "Create one invoice from an eligible budget using --payload JSON with invoice date, due date, billing address, customer memo, and a percentage of the original estimate that does not exceed its remaining coverage.";
|
|
11462
11507
|
if (legacyTarget === "import-qbo-customer-invoice") {
|
|
@@ -11517,10 +11562,10 @@ var summaryFor = (legacyTarget) => {
|
|
|
11517
11562
|
return "Download a customer invoice PDF when permitted.";
|
|
11518
11563
|
}
|
|
11519
11564
|
if (legacyTarget === "send-customer-invoice-to-contact-person") {
|
|
11520
|
-
return "Send a customer invoice and update the project billing email.";
|
|
11565
|
+
return "Send a customer invoice and update the project billing email. Omit payload cc to use company billing defaults; an explicit list replaces them and [] removes them. Required internal Cc still applies.";
|
|
11521
11566
|
}
|
|
11522
11567
|
if (legacyTarget === "update-company")
|
|
11523
|
-
return "Save company fields in BB and queue independent QuickBooks name/website synchronization; returns operation ID, not provider completion.";
|
|
11568
|
+
return "Save company fields in BB and queue independent QuickBooks name/website synchronization; returns operation ID, not provider completion. Optional payload billingEmails: email string array (max 50), omit to preserve defaults or [] to clear. Billing emails stay in BB; estimates are unchanged.";
|
|
11524
11569
|
if (legacyTarget === "get-company-quickbooks-sync-status")
|
|
11525
11570
|
return "Read saved company synchronization status without provider calls or polling; use companyId.";
|
|
11526
11571
|
if (legacyTarget === "get-item-quickbooks-sync-status")
|
|
@@ -11983,6 +12028,7 @@ var registry2 = [
|
|
|
11983
12028
|
"import-qbo"
|
|
11984
12029
|
]),
|
|
11985
12030
|
legacyCommand("create-customer-invoice", ["customer-invoice", "create"]),
|
|
12031
|
+
legacyCommand("resubmit-customer-invoice", ["customer-invoice", "resubmit"]),
|
|
11986
12032
|
legacyCommand("discard-customer-invoice", ["customer-invoice", "discard"]),
|
|
11987
12033
|
legacyCommand("delete-customer-invoice", ["customer-invoice", "delete"]),
|
|
11988
12034
|
legacyCommand("void-customer-invoice", ["customer-invoice", "void"]),
|
|
@@ -12096,6 +12142,10 @@ var registry2 = [
|
|
|
12096
12142
|
}
|
|
12097
12143
|
]),
|
|
12098
12144
|
legacyCommand("create-company", ["company", "create"], [], [
|
|
12145
|
+
{
|
|
12146
|
+
name: "--billingEmails",
|
|
12147
|
+
description: "JSON email array (max 50) for new company invoice Cc defaults; reused companies keep their saved defaults."
|
|
12148
|
+
},
|
|
12099
12149
|
{
|
|
12100
12150
|
name: "--confirmDistinct",
|
|
12101
12151
|
description: "Set true to confirm a separate legal entity despite similar names."
|
|
@@ -13442,6 +13492,7 @@ Customer invoices
|
|
|
13442
13492
|
discard-customer-invoice <batchId> Discard a reserved CREATING batch with no created QBO invoices.
|
|
13443
13493
|
delete-customer-invoice <batchId> Delete QBO invoices and the local batch.
|
|
13444
13494
|
void-customer-invoice <batchId> Void the invoice batch in QuickBooks.
|
|
13495
|
+
resubmit-customer-invoice <batchId> Original requester only. Queues one replacement after confirmed expiry voiding; retries return the same replacement. Never sends to the customer.
|
|
13445
13496
|
approve-customer-invoice <batchId> Admin approval; rejects voided invoices, notifies the creator, and closes the estimate at cumulative 100% coverage.
|
|
13446
13497
|
reject-customer-invoice <batchId> --reason <text> Admin rejection; voids QBO invoices and notifies the creator.
|
|
13447
13498
|
send-customer-invoice-to-contact-person --payload '<json>' Queues same composer workflow as web; requires invoiceId, to, cc, replyTo, subject, HTML content, and HTML signature.
|
|
@@ -13467,9 +13518,9 @@ Companies & projects
|
|
|
13467
13518
|
use-existing-company --companyId <bbId> and/or --quickbooksId <qboId> [--website <url>] [--confirmDistinct true]
|
|
13468
13519
|
Supply both IDs to explicitly connect existing records with different names.
|
|
13469
13520
|
Reuses both records, creating only the missing counterpart. Website is needed only for a QBO import without one.
|
|
13470
|
-
create-company --name --website <url> [--confirmDistinct true]
|
|
13521
|
+
create-company --name --website <url> [--confirmDistinct true] [--billingEmails '<json-array>']
|
|
13471
13522
|
Exact existing companies are reused. Similar names require explicit separate-entity confirmation.
|
|
13472
|
-
update-company --payload '<json>' (
|
|
13523
|
+
update-company --payload '<json>' (optional billingEmails: omit to preserve, [] to clear; returns queued QBO operation ID, not completion)
|
|
13473
13524
|
get-company-quickbooks-sync-status <companyId> (saved status only; no provider call or polling)
|
|
13474
13525
|
delete-company <id>
|
|
13475
13526
|
list-projects [--companyId] [--name] [--status|--statuses CSV] [--active true|false] [--wonFrom ISO] [--wonTo ISO] [--wonWithinDays N] [--limit 1-100] [--cursor] [--direction forward|backward] [--sortBy name|createdAt|status|companyName|wonAt] [--sortDir asc|desc]
|
|
@@ -14938,6 +14989,13 @@ Waiting for approval\u2026
|
|
|
14938
14989
|
await createCustomerInvoice(parseJsonFlag(String(payloadRaw), "--payload"));
|
|
14939
14990
|
break;
|
|
14940
14991
|
}
|
|
14992
|
+
case "resubmit-customer-invoice": {
|
|
14993
|
+
const batchId = positional[0];
|
|
14994
|
+
if (!batchId)
|
|
14995
|
+
throw new Error("resubmit-customer-invoice requires <batchId>");
|
|
14996
|
+
await resubmitCustomerInvoice(batchId);
|
|
14997
|
+
break;
|
|
14998
|
+
}
|
|
14941
14999
|
case "discard-customer-invoice": {
|
|
14942
15000
|
const batchId = positional[0];
|
|
14943
15001
|
if (!batchId) {
|
|
@@ -15082,6 +15140,9 @@ Waiting for approval\u2026
|
|
|
15082
15140
|
break;
|
|
15083
15141
|
}
|
|
15084
15142
|
case "create-company": {
|
|
15143
|
+
rejectBareFlag(flags, "billingEmails");
|
|
15144
|
+
const billingEmailsRaw = getFlag(flags, "billingEmails");
|
|
15145
|
+
const billingEmails = billingEmailsRaw === undefined ? undefined : parseCompanyBillingEmails(parseJsonFlag(billingEmailsRaw, "--billingEmails"));
|
|
15085
15146
|
const name = getFlag(flags, "name");
|
|
15086
15147
|
const website = getFlag(flags, "website");
|
|
15087
15148
|
if (!name || !website) {
|
|
@@ -15090,6 +15151,7 @@ Waiting for approval\u2026
|
|
|
15090
15151
|
await createCompany({
|
|
15091
15152
|
name,
|
|
15092
15153
|
website,
|
|
15154
|
+
...billingEmails === undefined ? {} : { billingEmails },
|
|
15093
15155
|
confirmDistinct: parseOptionalBoolFlag(flags, "confirmDistinct")
|
|
15094
15156
|
});
|
|
15095
15157
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@go-labs-sg/bb",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
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",
|