@go-labs-sg/bb 2.10.4 → 2.11.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 +21 -5
- package/command-manifest.json +141 -98
- package/command-reference.md +24 -16
- package/dist/commands.js +24 -91
- package/dist/index.js +25 -27
- package/dist/registry/index.js +10 -12
- package/package.json +1 -1
- package/role-aware-agent-guide.md +2 -1
package/README.md
CHANGED
|
@@ -193,15 +193,31 @@ Every canonical command is classified by effect: `state-change`, `email`, `exter
|
|
|
193
193
|
|
|
194
194
|
**Contact-person estimate email:** `bb budget estimate send --payload '<json>'` calls the same `email.sendEstimateToContactPerson` procedure as the web composer. The payload requires `budgetId`, `estimateId`, optional `estimateDocNumber`, `to`, `cc`, `replyTo`, `subject`, HTML `content`, and HTML `signature`. It sends the raw PDF returned by QuickBooks together with the standard terms and Budget Builder budget attachments, then marks the budget `ESTIMATE_SENT`. The command requires interactive `CONFIRM`; inspect the budget, recipients, and HTML first, and do not retry blindly after an ambiguous delivery failure.
|
|
195
195
|
|
|
196
|
-
**Customer-invoice workflow parity:** `bb customer-invoice list` uses the same global/project list procedure and metrics as the web pages; omit filters for the global list or use `--projectId` for project scope. `bb customer-invoice send` uses the same protected email workflow as the web composer and marks a successful invoice `SENT`. `bb customer-invoice approve` and `bb customer-invoice reject` are Admin-only batch operations: callers select a batch ID, and the API resolves its internal pending Admin approval record. `bb customer-invoice
|
|
196
|
+
**Customer-invoice workflow parity:** `bb customer-invoice list` uses the same global/project list procedure and metrics as the web pages; omit filters for the global list or use `--projectId` for project scope. `bb customer-invoice send` uses the same protected email workflow as the web composer and marks a successful invoice `SENT`. `bb customer-invoice approve` and `bb customer-invoice reject` are Admin-only batch operations: callers select a batch ID, and the API resolves its internal pending Admin approval record. `bb customer-invoice sync` follows QuickBooks' paid state and zero balance, restoring `SENT` or `APPROVED` if that payment is reversed. Invoice approval refuses voided invoices and closes the estimate only when approved invoice coverage totals 100%; deletion, voiding, rejection, expiry, and QBO synchronization use the same estimate-reopening and live-payment guards as the web app.
|
|
197
197
|
|
|
198
198
|
For agent-driven invoice work, use this read-before-write sequence:
|
|
199
199
|
|
|
200
200
|
1. Run `bb customer-invoice get <batchId>` and, when composing email, `bb customer-invoice email-context get <batchId>`.
|
|
201
201
|
2. For creation, run `bb customer-invoice eligible-budget list <projectId>` or `bb customer-invoice readiness check <budgetId>` first.
|
|
202
|
-
3. State the exact invoice or batch, current status, intended mutation, QuickBooks effect, email recipients,
|
|
202
|
+
3. State the exact invoice or batch, current status, intended mutation, QuickBooks effect, email recipients, and estimate-closing/reopening effect to the user.
|
|
203
203
|
4. Wait for explicit confirmation, then run the sensitive command interactively and verify its JSON result. Do not treat the runtime `CONFIRM` prompt as user authorization.
|
|
204
204
|
|
|
205
|
+
### Project Hub operations
|
|
206
|
+
|
|
207
|
+
Project Hub setup and commercial-document reconciliation are durable, asynchronous Budget Builder operations. Read status before requesting either mutation:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
bb project hub status <project-id>
|
|
211
|
+
bb project hub setup <project-id>
|
|
212
|
+
bb project hub sync <project-id>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
`status` reports eligibility, whether setup can be requested, the current setup status, the latest setup error, and the client Hub URL. `setup` uses the same eligibility and accepted-estimate checks as the web button and may create the operational Asana project before provisioning Drive and Project Hub. `sync` requires an existing `READY` native-layout integration and queues a complete reconciliation of accepted estimates, commercial attachments, win proofs, and eligible customer invoices into Asana and Drive.
|
|
216
|
+
|
|
217
|
+
Both mutation commands write external state asynchronously and therefore require `state-change` and `external-write` confirmation. In non-interactive use, pass both `--allow-state-change` and `--allow-external-write` only after obtaining explicit user approval. A successful `queued` response means the durable operation was accepted. `status` tracks setup readiness; the sync response returns its outbox operation ID, which an Admin can inspect with the integration-operation commands when completion or failure diagnostics are required.
|
|
218
|
+
|
|
219
|
+
The fleet-wide legacy-layout migration is intentionally not exposed through the normal CLI. It remains a restricted deployment script that defaults to a read-only dry run.
|
|
220
|
+
|
|
205
221
|
### Command overview
|
|
206
222
|
|
|
207
223
|
The table below is a behavior index keyed by the compatibility dispatcher's legacy handler labels. It is not invocation syntax. AI agents and new automation must resolve and use the grouped v2 route from `bb help`, [`command-reference.md`](./command-reference.md), or [`command-manifest.json`](./command-manifest.json); never copy a flat label from this table into a new command.
|
|
@@ -213,9 +229,9 @@ Project and budget query payloads keep the Asana identities explicit: `asanaTask
|
|
|
213
229
|
| **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`), `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`, `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`) |
|
|
214
230
|
| **Bills / claims** | `list-bills` (`--isClaimable false` for bills, `--isClaimable true` for claims, omit for both), `list-claims` (claims only), `validate-bill-selection` (`--payload`; runs the same supplier, line-item, and quotation-coverage checks as the web flow; `alreadyPaid` never bypasses them), `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, `isClaimable=true` for a claim; 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; non-legacy supplier bills from 1 Jul 2026 00:00 SGT require approved quotation coverage even when already paid), `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` |
|
|
215
231
|
| **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`) |
|
|
216
|
-
| **Customer invoices** | `check-customer-invoice-readiness`, `list-eligible-customer-invoice-budgets`, `list-customer-invoices` (global/project/budget filters plus summary metrics), `get-customer-invoice`, `get-customer-invoice-email-context`, `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`, `
|
|
232
|
+
| **Customer invoices** | `check-customer-invoice-readiness`, `list-eligible-customer-invoice-budgets`, `list-customer-invoices` (global/project/budget filters plus summary metrics), `get-customer-invoice`, `get-customer-invoice-email-context`, `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`, `sync-customer-invoice` |
|
|
217
233
|
| **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` |
|
|
218
|
-
| **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), `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`) |
|
|
234
|
+
| **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`) |
|
|
219
235
|
| **Contacts** | `list-contacts`, `create-contact-person` (`--payload`), `update-contact-person` (`--payload`) |
|
|
220
236
|
| **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` (admin), `list-items`, `create-item` (`--payload`), `update-item` (`--payload`), `archive-items` (`--ids` CSV, max 100; Budget Builder only; users can archive their own items, leads/admins any item; blocked by Draft/Pending Approval/Approved budgets), `delete-item` (admin; archives in Budget Builder and makes the item inactive in QuickBooks), `get-item`, `list-item-categories`, `create-item-category` / `update-item-category` / `delete-item-categories` (admin; `--ids` CSV for delete) |
|
|
221
237
|
| **Dashboard & users** | `whoami` (current API-key owner identity and role), `list-users`, `create-user` (admin; provisions an API-only service identity with no Google sign-in; `--email`, optional `--name`, optional `--role` defaulting to `USER`), `create-api-key` (`--name`; defaults to the caller; admin-only `--userId` for another user; raw key shown once), `list-api-keys` (defaults to the caller; admin-only `--userId` for another user), `revoke-api-key` (key ID; admin-only `--userId` for another user), `get-user-performance`, `get-dashboard`, `get-monthly-metrics`, `get-system-overview`, `get-estimate-performance`, `get-financial-overview` (performance/dashboard commands are admin-only) |
|
|
@@ -241,7 +257,7 @@ From the monorepo root (after `bun install`):
|
|
|
241
257
|
|
|
242
258
|
```bash
|
|
243
259
|
export BB_API_KEY=...
|
|
244
|
-
bun run
|
|
260
|
+
bun run packages/budget-builder/cli/src/index.ts budget list
|
|
245
261
|
```
|
|
246
262
|
|
|
247
263
|
Or from `packages/budget-builder/cli`:
|
package/command-manifest.json
CHANGED
|
@@ -3446,104 +3446,6 @@
|
|
|
3446
3446
|
"effects": ["state-change", "email"],
|
|
3447
3447
|
"legacyTarget": "send-customer-invoice-to-contact-person"
|
|
3448
3448
|
},
|
|
3449
|
-
{
|
|
3450
|
-
"path": ["customer-invoice", "payment", "mark-paid"],
|
|
3451
|
-
"legacyAliases": [
|
|
3452
|
-
"mark-customer-invoice-paid",
|
|
3453
|
-
"mark_customer_invoice_paid"
|
|
3454
|
-
],
|
|
3455
|
-
"summary": "Mark Customer Invoice Paid.",
|
|
3456
|
-
"globalOptions": [
|
|
3457
|
-
{
|
|
3458
|
-
"name": "--help",
|
|
3459
|
-
"description": "Show help for this command."
|
|
3460
|
-
},
|
|
3461
|
-
{
|
|
3462
|
-
"name": "--quiet",
|
|
3463
|
-
"description": "Suppress non-error diagnostics."
|
|
3464
|
-
},
|
|
3465
|
-
{
|
|
3466
|
-
"name": "--debug",
|
|
3467
|
-
"description": "Emit sanitized diagnostic traces."
|
|
3468
|
-
},
|
|
3469
|
-
{
|
|
3470
|
-
"name": "--api-url",
|
|
3471
|
-
"description": "Override the Budget Builder API base URL."
|
|
3472
|
-
},
|
|
3473
|
-
{
|
|
3474
|
-
"name": "--allow-state-change",
|
|
3475
|
-
"description": "Allow a Budget Builder state change in non-interactive use."
|
|
3476
|
-
},
|
|
3477
|
-
{
|
|
3478
|
-
"name": "--allow-email",
|
|
3479
|
-
"description": "Allow sending email in non-interactive use."
|
|
3480
|
-
},
|
|
3481
|
-
{
|
|
3482
|
-
"name": "--allow-external-write",
|
|
3483
|
-
"description": "Allow writes to external systems in non-interactive use."
|
|
3484
|
-
},
|
|
3485
|
-
{
|
|
3486
|
-
"name": "--allow-delete",
|
|
3487
|
-
"description": "Allow deleting data in non-interactive use."
|
|
3488
|
-
},
|
|
3489
|
-
{
|
|
3490
|
-
"name": "--allow-financial-write",
|
|
3491
|
-
"description": "Allow financial-record changes in non-interactive use."
|
|
3492
|
-
}
|
|
3493
|
-
],
|
|
3494
|
-
"argumentMode": "legacy-passthrough",
|
|
3495
|
-
"effects": ["state-change", "external-write", "financial-write"],
|
|
3496
|
-
"legacyTarget": "mark-customer-invoice-paid"
|
|
3497
|
-
},
|
|
3498
|
-
{
|
|
3499
|
-
"path": ["customer-invoice", "payment-proof", "download"],
|
|
3500
|
-
"legacyAliases": [
|
|
3501
|
-
"download-customer-invoice-payment-proof",
|
|
3502
|
-
"download_customer_invoice_payment_proof"
|
|
3503
|
-
],
|
|
3504
|
-
"summary": "Download Customer Invoice Payment Proof.",
|
|
3505
|
-
"globalOptions": [
|
|
3506
|
-
{
|
|
3507
|
-
"name": "--help",
|
|
3508
|
-
"description": "Show help for this command."
|
|
3509
|
-
},
|
|
3510
|
-
{
|
|
3511
|
-
"name": "--quiet",
|
|
3512
|
-
"description": "Suppress non-error diagnostics."
|
|
3513
|
-
},
|
|
3514
|
-
{
|
|
3515
|
-
"name": "--debug",
|
|
3516
|
-
"description": "Emit sanitized diagnostic traces."
|
|
3517
|
-
},
|
|
3518
|
-
{
|
|
3519
|
-
"name": "--api-url",
|
|
3520
|
-
"description": "Override the Budget Builder API base URL."
|
|
3521
|
-
},
|
|
3522
|
-
{
|
|
3523
|
-
"name": "--allow-state-change",
|
|
3524
|
-
"description": "Allow a Budget Builder state change in non-interactive use."
|
|
3525
|
-
},
|
|
3526
|
-
{
|
|
3527
|
-
"name": "--allow-email",
|
|
3528
|
-
"description": "Allow sending email in non-interactive use."
|
|
3529
|
-
},
|
|
3530
|
-
{
|
|
3531
|
-
"name": "--allow-external-write",
|
|
3532
|
-
"description": "Allow writes to external systems in non-interactive use."
|
|
3533
|
-
},
|
|
3534
|
-
{
|
|
3535
|
-
"name": "--allow-delete",
|
|
3536
|
-
"description": "Allow deleting data in non-interactive use."
|
|
3537
|
-
},
|
|
3538
|
-
{
|
|
3539
|
-
"name": "--allow-financial-write",
|
|
3540
|
-
"description": "Allow financial-record changes in non-interactive use."
|
|
3541
|
-
}
|
|
3542
|
-
],
|
|
3543
|
-
"argumentMode": "legacy-passthrough",
|
|
3544
|
-
"effects": [],
|
|
3545
|
-
"legacyTarget": "download-customer-invoice-payment-proof"
|
|
3546
|
-
},
|
|
3547
3449
|
{
|
|
3548
3450
|
"path": ["customer-invoice", "pdf", "download"],
|
|
3549
3451
|
"legacyAliases": [
|
|
@@ -4334,6 +4236,147 @@
|
|
|
4334
4236
|
"effects": [],
|
|
4335
4237
|
"legacyTarget": "get-project"
|
|
4336
4238
|
},
|
|
4239
|
+
{
|
|
4240
|
+
"path": ["project", "hub", "status"],
|
|
4241
|
+
"legacyAliases": ["get-project-hub-status", "get_project_hub_status"],
|
|
4242
|
+
"summary": "Get project hub status.",
|
|
4243
|
+
"globalOptions": [
|
|
4244
|
+
{
|
|
4245
|
+
"name": "--help",
|
|
4246
|
+
"description": "Show help for this command."
|
|
4247
|
+
},
|
|
4248
|
+
{
|
|
4249
|
+
"name": "--quiet",
|
|
4250
|
+
"description": "Suppress non-error diagnostics."
|
|
4251
|
+
},
|
|
4252
|
+
{
|
|
4253
|
+
"name": "--debug",
|
|
4254
|
+
"description": "Emit sanitized diagnostic traces."
|
|
4255
|
+
},
|
|
4256
|
+
{
|
|
4257
|
+
"name": "--api-url",
|
|
4258
|
+
"description": "Override the Budget Builder API base URL."
|
|
4259
|
+
},
|
|
4260
|
+
{
|
|
4261
|
+
"name": "--allow-state-change",
|
|
4262
|
+
"description": "Allow a Budget Builder state change in non-interactive use."
|
|
4263
|
+
},
|
|
4264
|
+
{
|
|
4265
|
+
"name": "--allow-email",
|
|
4266
|
+
"description": "Allow sending email in non-interactive use."
|
|
4267
|
+
},
|
|
4268
|
+
{
|
|
4269
|
+
"name": "--allow-external-write",
|
|
4270
|
+
"description": "Allow writes to external systems in non-interactive use."
|
|
4271
|
+
},
|
|
4272
|
+
{
|
|
4273
|
+
"name": "--allow-delete",
|
|
4274
|
+
"description": "Allow deleting data in non-interactive use."
|
|
4275
|
+
},
|
|
4276
|
+
{
|
|
4277
|
+
"name": "--allow-financial-write",
|
|
4278
|
+
"description": "Allow financial-record changes in non-interactive use."
|
|
4279
|
+
}
|
|
4280
|
+
],
|
|
4281
|
+
"argumentMode": "legacy-passthrough",
|
|
4282
|
+
"effects": [],
|
|
4283
|
+
"legacyTarget": "get-project-hub-status"
|
|
4284
|
+
},
|
|
4285
|
+
{
|
|
4286
|
+
"path": ["project", "hub", "setup"],
|
|
4287
|
+
"legacyAliases": ["setup-project-hub", "setup_project_hub"],
|
|
4288
|
+
"summary": "Setup Project Hub.",
|
|
4289
|
+
"globalOptions": [
|
|
4290
|
+
{
|
|
4291
|
+
"name": "--help",
|
|
4292
|
+
"description": "Show help for this command."
|
|
4293
|
+
},
|
|
4294
|
+
{
|
|
4295
|
+
"name": "--quiet",
|
|
4296
|
+
"description": "Suppress non-error diagnostics."
|
|
4297
|
+
},
|
|
4298
|
+
{
|
|
4299
|
+
"name": "--debug",
|
|
4300
|
+
"description": "Emit sanitized diagnostic traces."
|
|
4301
|
+
},
|
|
4302
|
+
{
|
|
4303
|
+
"name": "--api-url",
|
|
4304
|
+
"description": "Override the Budget Builder API base URL."
|
|
4305
|
+
},
|
|
4306
|
+
{
|
|
4307
|
+
"name": "--allow-state-change",
|
|
4308
|
+
"description": "Allow a Budget Builder state change in non-interactive use."
|
|
4309
|
+
},
|
|
4310
|
+
{
|
|
4311
|
+
"name": "--allow-email",
|
|
4312
|
+
"description": "Allow sending email in non-interactive use."
|
|
4313
|
+
},
|
|
4314
|
+
{
|
|
4315
|
+
"name": "--allow-external-write",
|
|
4316
|
+
"description": "Allow writes to external systems in non-interactive use."
|
|
4317
|
+
},
|
|
4318
|
+
{
|
|
4319
|
+
"name": "--allow-delete",
|
|
4320
|
+
"description": "Allow deleting data in non-interactive use."
|
|
4321
|
+
},
|
|
4322
|
+
{
|
|
4323
|
+
"name": "--allow-financial-write",
|
|
4324
|
+
"description": "Allow financial-record changes in non-interactive use."
|
|
4325
|
+
}
|
|
4326
|
+
],
|
|
4327
|
+
"argumentMode": "legacy-passthrough",
|
|
4328
|
+
"effects": ["state-change", "external-write"],
|
|
4329
|
+
"legacyTarget": "setup-project-hub"
|
|
4330
|
+
},
|
|
4331
|
+
{
|
|
4332
|
+
"path": ["project", "hub", "sync"],
|
|
4333
|
+
"legacyAliases": [
|
|
4334
|
+
"sync-project-hub-commercial-documents",
|
|
4335
|
+
"sync_project_hub_commercial_documents"
|
|
4336
|
+
],
|
|
4337
|
+
"summary": "Sync Project Hub Commercial Documents.",
|
|
4338
|
+
"globalOptions": [
|
|
4339
|
+
{
|
|
4340
|
+
"name": "--help",
|
|
4341
|
+
"description": "Show help for this command."
|
|
4342
|
+
},
|
|
4343
|
+
{
|
|
4344
|
+
"name": "--quiet",
|
|
4345
|
+
"description": "Suppress non-error diagnostics."
|
|
4346
|
+
},
|
|
4347
|
+
{
|
|
4348
|
+
"name": "--debug",
|
|
4349
|
+
"description": "Emit sanitized diagnostic traces."
|
|
4350
|
+
},
|
|
4351
|
+
{
|
|
4352
|
+
"name": "--api-url",
|
|
4353
|
+
"description": "Override the Budget Builder API base URL."
|
|
4354
|
+
},
|
|
4355
|
+
{
|
|
4356
|
+
"name": "--allow-state-change",
|
|
4357
|
+
"description": "Allow a Budget Builder state change in non-interactive use."
|
|
4358
|
+
},
|
|
4359
|
+
{
|
|
4360
|
+
"name": "--allow-email",
|
|
4361
|
+
"description": "Allow sending email in non-interactive use."
|
|
4362
|
+
},
|
|
4363
|
+
{
|
|
4364
|
+
"name": "--allow-external-write",
|
|
4365
|
+
"description": "Allow writes to external systems in non-interactive use."
|
|
4366
|
+
},
|
|
4367
|
+
{
|
|
4368
|
+
"name": "--allow-delete",
|
|
4369
|
+
"description": "Allow deleting data in non-interactive use."
|
|
4370
|
+
},
|
|
4371
|
+
{
|
|
4372
|
+
"name": "--allow-financial-write",
|
|
4373
|
+
"description": "Allow financial-record changes in non-interactive use."
|
|
4374
|
+
}
|
|
4375
|
+
],
|
|
4376
|
+
"argumentMode": "legacy-passthrough",
|
|
4377
|
+
"effects": ["state-change", "external-write"],
|
|
4378
|
+
"legacyTarget": "sync-project-hub-commercial-documents"
|
|
4379
|
+
},
|
|
4337
4380
|
{
|
|
4338
4381
|
"path": ["project", "create"],
|
|
4339
4382
|
"legacyAliases": ["create-project", "create_project"],
|
package/command-reference.md
CHANGED
|
@@ -608,22 +608,6 @@ Legacy aliases: `send-customer-invoice-to-contact-person`, `send_customer_invoic
|
|
|
608
608
|
|
|
609
609
|
Effects: state-change, email.
|
|
610
610
|
|
|
611
|
-
## `bb customer-invoice payment mark-paid`
|
|
612
|
-
|
|
613
|
-
Mark Customer Invoice Paid.
|
|
614
|
-
|
|
615
|
-
Legacy aliases: `mark-customer-invoice-paid`, `mark_customer_invoice_paid`.
|
|
616
|
-
|
|
617
|
-
Effects: state-change, external-write, financial-write.
|
|
618
|
-
|
|
619
|
-
## `bb customer-invoice payment-proof download`
|
|
620
|
-
|
|
621
|
-
Download Customer Invoice Payment Proof.
|
|
622
|
-
|
|
623
|
-
Legacy aliases: `download-customer-invoice-payment-proof`, `download_customer_invoice_payment_proof`.
|
|
624
|
-
|
|
625
|
-
Effects: none.
|
|
626
|
-
|
|
627
611
|
## `bb customer-invoice pdf download`
|
|
628
612
|
|
|
629
613
|
Download Customer Invoice Pdf.
|
|
@@ -760,6 +744,30 @@ Legacy aliases: `get-project`, `get_project`.
|
|
|
760
744
|
|
|
761
745
|
Effects: none.
|
|
762
746
|
|
|
747
|
+
## `bb project hub status`
|
|
748
|
+
|
|
749
|
+
Get project hub status.
|
|
750
|
+
|
|
751
|
+
Legacy aliases: `get-project-hub-status`, `get_project_hub_status`.
|
|
752
|
+
|
|
753
|
+
Effects: none.
|
|
754
|
+
|
|
755
|
+
## `bb project hub setup`
|
|
756
|
+
|
|
757
|
+
Setup Project Hub.
|
|
758
|
+
|
|
759
|
+
Legacy aliases: `setup-project-hub`, `setup_project_hub`.
|
|
760
|
+
|
|
761
|
+
Effects: state-change, external-write.
|
|
762
|
+
|
|
763
|
+
## `bb project hub sync`
|
|
764
|
+
|
|
765
|
+
Sync Project Hub Commercial Documents.
|
|
766
|
+
|
|
767
|
+
Legacy aliases: `sync-project-hub-commercial-documents`, `sync_project_hub_commercial_documents`.
|
|
768
|
+
|
|
769
|
+
Effects: state-change, external-write.
|
|
770
|
+
|
|
763
771
|
## `bb project create`
|
|
764
772
|
|
|
765
773
|
Create Project.
|
package/dist/commands.js
CHANGED
|
@@ -264,18 +264,7 @@ const billAttachmentContentTypeForFileName = (fileName) => {
|
|
|
264
264
|
return resolvedContentType;
|
|
265
265
|
};
|
|
266
266
|
const PAYMENT_PROOF_MAX_SIZE = 20 * 1024 * 1024;
|
|
267
|
-
const CUSTOMER_INVOICE_PAYMENT_PROOF_MAX_SIZE = 20 * 1024 * 1024;
|
|
268
267
|
const QUOTATION_ATTACHMENT_MAX_SIZE = 20 * 1024 * 1024;
|
|
269
|
-
const customerInvoicePaymentProofContentTypeForFileName = (fileName) => {
|
|
270
|
-
const resolvedContentType = contentTypeHeaderForFileName(fileName);
|
|
271
|
-
if (resolvedContentType === "application/pdf" ||
|
|
272
|
-
resolvedContentType === "image/gif" ||
|
|
273
|
-
resolvedContentType === "image/jpeg" ||
|
|
274
|
-
resolvedContentType === "image/png") {
|
|
275
|
-
return resolvedContentType;
|
|
276
|
-
}
|
|
277
|
-
throw new Error(`Unsupported customer invoice payment-proof type: ${fileName}. Use PDF, GIF, JPEG, or PNG.`);
|
|
278
|
-
};
|
|
279
268
|
const quotationAttachmentContentTypeForFileName = (fileName) => {
|
|
280
269
|
const resolvedContentType = contentTypeHeaderForFileName(fileName);
|
|
281
270
|
if (resolvedContentType === "application/pdf" ||
|
|
@@ -1447,86 +1436,6 @@ export const sendCustomerInvoiceToContactPersonFromPayload = async (raw) => {
|
|
|
1447
1436
|
const result = await api.customerInvoice.sendInvoiceToContactPerson.mutate(input);
|
|
1448
1437
|
out(result);
|
|
1449
1438
|
};
|
|
1450
|
-
export const describeCustomerInvoicePaymentConfirmation = ({ invoiceId, paymentDate, paymentReference, proofFileName, proofSize, }) => `for invoice ${invoiceId}, records payment date ${paymentDate}${paymentReference
|
|
1451
|
-
? " with a payment reference"
|
|
1452
|
-
: " without a payment reference"}; uploads ${proofFileName} (${proofSize} bytes); creates a QuickBooks Payment for the live outstanding balance when non-zero, attaches the proof to QBO when possible, and records the payment in Budget Builder`;
|
|
1453
|
-
const assertPaymentDate = (paymentDate) => {
|
|
1454
|
-
if (!/^\d{4}-\d{2}-\d{2}$/.test(paymentDate)) {
|
|
1455
|
-
throw new Error("--paymentDate must use YYYY-MM-DD.");
|
|
1456
|
-
}
|
|
1457
|
-
const parsed = new Date(`${paymentDate}T00:00:00.000Z`);
|
|
1458
|
-
if (Number.isNaN(parsed.getTime()) ||
|
|
1459
|
-
parsed.toISOString().slice(0, 10) !== paymentDate) {
|
|
1460
|
-
throw new Error("--paymentDate must be a valid calendar date.");
|
|
1461
|
-
}
|
|
1462
|
-
return paymentDate;
|
|
1463
|
-
};
|
|
1464
|
-
export const markCustomerInvoicePaidFromPath = async ({ invoiceId, paymentDate, paymentProofPath, paymentReference, }) => {
|
|
1465
|
-
const normalizedPaymentDate = assertPaymentDate(paymentDate);
|
|
1466
|
-
const proofContents = await readFile(paymentProofPath);
|
|
1467
|
-
const proofFileName = basename(paymentProofPath);
|
|
1468
|
-
if (proofContents.byteLength === 0) {
|
|
1469
|
-
throw new Error("Customer invoice payment proof must not be empty.");
|
|
1470
|
-
}
|
|
1471
|
-
if (proofContents.byteLength > CUSTOMER_INVOICE_PAYMENT_PROOF_MAX_SIZE) {
|
|
1472
|
-
throw new Error("Customer invoice payment proof must be 20MB or smaller.");
|
|
1473
|
-
}
|
|
1474
|
-
const proofContentType = customerInvoicePaymentProofContentTypeForFileName(proofFileName);
|
|
1475
|
-
await assertSensitiveWorkflowConfirmed({
|
|
1476
|
-
action: "Mark customer invoice paid",
|
|
1477
|
-
entity: `invoice ${invoiceId}`,
|
|
1478
|
-
details: describeCustomerInvoicePaymentConfirmation({
|
|
1479
|
-
invoiceId,
|
|
1480
|
-
paymentDate: normalizedPaymentDate,
|
|
1481
|
-
paymentReference,
|
|
1482
|
-
proofFileName,
|
|
1483
|
-
proofSize: proofContents.byteLength,
|
|
1484
|
-
}),
|
|
1485
|
-
});
|
|
1486
|
-
const proofUpload = await api.customerInvoice.requestPaymentProofUpload.mutate({
|
|
1487
|
-
invoiceId,
|
|
1488
|
-
fileName: proofFileName,
|
|
1489
|
-
size: proofContents.byteLength,
|
|
1490
|
-
contentType: proofContentType,
|
|
1491
|
-
});
|
|
1492
|
-
const uploadResponse = await fetch(proofUpload.uploadUrl, {
|
|
1493
|
-
method: "PUT",
|
|
1494
|
-
body: proofContents,
|
|
1495
|
-
headers: { "Content-Type": proofUpload.contentType },
|
|
1496
|
-
});
|
|
1497
|
-
if (!uploadResponse.ok) {
|
|
1498
|
-
throw new Error(`Payment-proof upload failed: HTTP ${uploadResponse.status} ${(await uploadResponse.text()).slice(0, 500)}`);
|
|
1499
|
-
}
|
|
1500
|
-
const result = await api.customerInvoice.markPaid.mutate({
|
|
1501
|
-
invoiceId,
|
|
1502
|
-
paymentDate: normalizedPaymentDate,
|
|
1503
|
-
paymentReference,
|
|
1504
|
-
proof: {
|
|
1505
|
-
key: proofUpload.key,
|
|
1506
|
-
name: proofUpload.name,
|
|
1507
|
-
size: proofContents.byteLength,
|
|
1508
|
-
contentType: proofUpload.contentType,
|
|
1509
|
-
},
|
|
1510
|
-
});
|
|
1511
|
-
out(result);
|
|
1512
|
-
};
|
|
1513
|
-
export const downloadCustomerInvoicePaymentProof = async (invoiceId, outputPath) => {
|
|
1514
|
-
const proof = await api.customerInvoice.getPaymentProofDownloadUrl.mutate({
|
|
1515
|
-
invoiceId,
|
|
1516
|
-
});
|
|
1517
|
-
const response = await fetch(proof.url);
|
|
1518
|
-
if (!response.ok) {
|
|
1519
|
-
throw new Error(`Payment-proof download failed: HTTP ${response.status} ${(await response.text()).slice(0, 500)}`);
|
|
1520
|
-
}
|
|
1521
|
-
const contents = Buffer.from(await response.arrayBuffer());
|
|
1522
|
-
const resolvedOutputPath = outputPath?.trim() || basename(proof.fileName);
|
|
1523
|
-
await writeFile(resolvedOutputPath, contents);
|
|
1524
|
-
out({
|
|
1525
|
-
fileName: proof.fileName,
|
|
1526
|
-
outputPath: resolvedOutputPath,
|
|
1527
|
-
size: contents.byteLength,
|
|
1528
|
-
});
|
|
1529
|
-
};
|
|
1530
1439
|
export async function updateBillFromPayload(raw) {
|
|
1531
1440
|
const input = parseUpdateBillPayload(raw);
|
|
1532
1441
|
const result = await api.bill.update.mutate(input);
|
|
@@ -2006,6 +1915,30 @@ export async function getProject(id) {
|
|
|
2006
1915
|
]);
|
|
2007
1916
|
out({ project, budgetsOverview, billsOverview });
|
|
2008
1917
|
}
|
|
1918
|
+
export const getProjectHubStatus = async (projectId) => {
|
|
1919
|
+
const result = await api.project.getProjectHubShareLink.query({ projectId });
|
|
1920
|
+
out(result);
|
|
1921
|
+
};
|
|
1922
|
+
export const setupProjectHub = async (projectId) => {
|
|
1923
|
+
await assertSensitiveWorkflowConfirmed({
|
|
1924
|
+
action: "Set up Project Hub",
|
|
1925
|
+
entity: `project ${projectId}`,
|
|
1926
|
+
details: "queue Asana, Drive, and client Hub provisioning",
|
|
1927
|
+
});
|
|
1928
|
+
const result = await api.project.setupProjectHub.mutate({ projectId });
|
|
1929
|
+
out(result);
|
|
1930
|
+
};
|
|
1931
|
+
export const syncProjectHubCommercialDocuments = async (projectId) => {
|
|
1932
|
+
await assertSensitiveWorkflowConfirmed({
|
|
1933
|
+
action: "Sync Project Hub commercial documents",
|
|
1934
|
+
entity: `project ${projectId}`,
|
|
1935
|
+
details: "queue a full Budget Builder to Google Drive and Asana reconciliation",
|
|
1936
|
+
});
|
|
1937
|
+
const result = await api.project.syncProjectHubCommercialDocuments.mutate({
|
|
1938
|
+
projectId,
|
|
1939
|
+
});
|
|
1940
|
+
out(result);
|
|
1941
|
+
};
|
|
2009
1942
|
export async function createProject(opts) {
|
|
2010
1943
|
const links = await resolveCreateProjectLinks({
|
|
2011
1944
|
name: opts.name,
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { resolve } from "node:path";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { BUDGET_BUILDER_API_BASE_URL, configureBudgetBuilderApiBaseUrl, DEFAULT_BUDGET_BUILDER_API_BASE_URL, requireApiKey, } from "./api-client.js";
|
|
7
7
|
import { consumeCliQuietFlags, logCliAction, sanitizeFlagsForTrace, setCliQuiet, shouldLogCliActions, } from "./cli-trace.js";
|
|
8
|
-
import { addBudgetItems, approveBill, approveBudget, approveCustomerInvoice, approveQuotation, approveSupplier, archiveItemsByIds, checkCustomerInvoiceReadiness, checkProjectReconciliation, cleanupStagedBillAttachments, cleanupStagedQuotationAttachments, completeProject, createApiKeyForUser, createBillApproval, createBillFromPayload, createBudgetApproval, createBudgetCategory, createBudgetFromPayload, createCompany, createContactPersonFromPayload, createCustomerInvoice, createEstimate, createInboundSource, createItemCategory, createItemFromPayload, createPlaceholderBillForBudgetItem, createProject, createQuotationFromPayload, createSupplierCertification, createSupplierFromPayload, createSupplierPaymentMethod, createSupplierRoleOption, createSupplierTagOption, createUser, deleteBillById, deleteBudgetById, deleteBudgetCategory, deleteBudgetCommission, deleteBudgetDiscount, deleteCompanyById, deleteCustomerInvoice, deleteItemById, deleteItemCategoriesByIds, deleteProjectById, deleteQuotationById, deleteSuppliersByIds, discardCreatingCustomerInvoice,
|
|
8
|
+
import { addBudgetItems, approveBill, approveBudget, approveCustomerInvoice, approveQuotation, approveSupplier, archiveItemsByIds, checkCustomerInvoiceReadiness, checkProjectReconciliation, cleanupStagedBillAttachments, cleanupStagedQuotationAttachments, completeProject, createApiKeyForUser, createBillApproval, createBillFromPayload, createBudgetApproval, createBudgetCategory, createBudgetFromPayload, createCompany, createContactPersonFromPayload, createCustomerInvoice, createEstimate, createInboundSource, createItemCategory, createItemFromPayload, createPlaceholderBillForBudgetItem, createProject, createQuotationFromPayload, createSupplierCertification, createSupplierFromPayload, createSupplierPaymentMethod, createSupplierRoleOption, createSupplierTagOption, createUser, deleteBillById, deleteBudgetById, deleteBudgetCategory, deleteBudgetCommission, deleteBudgetDiscount, deleteCompanyById, deleteCustomerInvoice, deleteItemById, deleteItemCategoriesByIds, deleteProjectById, deleteQuotationById, deleteSuppliersByIds, discardCreatingCustomerInvoice, downloadCustomerInvoicePdf, downloadQuotationPdf, getApprovedBudgets, getBillAttachments, getBillDetails, getBudget, getBudgetCategories, getBudgetCategoryBenchmarks, getBudgetDetails, getBudgetItemsOnly, getBudgetVersions, getCompany, getCustomerInvoice, getCustomerInvoiceEmailContext, getDashboard, getErrorMetrics, getEstimatePerformance, getFinancialOverview, getInboundSource, getInboundSubmission, getItem, getItemPricingHistory, getMonthlyMetrics, getProject, getProjectHubStatus, getQuotationDetails, getRecentErrors, getSupplierAnalytics, getSupplierDetails, getSupplierPricingHistory, getSystemOverview, getUserPerformance, importQuickBooksProjectId, listApiKeysForUser, listApprovals, listBills, listBudgets, listCompanies, listContacts, listCustomerInvoices, listEligibleCustomerInvoiceBudgets, listInboundSources, listInboundSubmissions, listIntegrationOperations, listItemCategories, listItems, listProjects, listQuotations, listSuppliers, listUsers, markBudgetWonWithProof, patchBillInvoiceNumber, patchBillPayment, reactivateSuppliersByIds, reconcileProject, rejectBill, rejectBudget, rejectCustomerInvoice, rejectQuotation, rejectSupplier, removeBudgetItem, renameBudgetVersion, reorderBudgetItemsCli, restoreBudgetVersion, retryIntegrationOperation, revokeApiKeyForUser, saveInboundAutomationConfig, saveInboundMappingVersion, sendCustomerInvoiceToContactPersonFromPayload, sendEstimateToContactPersonFromPayload, setBudgetItemsNotUtilized, setupProjectHub, stageBillAttachmentsFromPaths, submitQuotation, syncCustomerInvoice, syncProjectHubCommercialDocuments, updateBillFromPayload, updateBillPaymentEvidenceFromPayload, updateBillStatus, updateBudgetCategory, updateBudgetCommissionFromPayload, updateBudgetDiscountFromPayload, updateBudgetFromPayload, updateBudgetItem, updateBudgetItemSupplierCli, updateBudgetStatus, updateCompanyFromPayload, updateContactPersonFromPayload, updateInboundProcessing, updateInboundSource, updateItemCategory, updateItemFromPayload, updateProjectFromPayload, updateProjectStatus, updateQuotationFromPayload, updateSupplierFromPayload, uploadBillAttachmentFromPath, uploadBillAttachmentsFromPaths, uploadBillDocumentsFromPaths, uploadBudgetAttachmentFromPath, uploadQuotationAttachmentFromPath, validateBillSelectionFromPayload, voidCustomerInvoice, whoAmI, } from "./commands.js";
|
|
9
9
|
import { getFlag, parseArgs } from "./parse-args.js";
|
|
10
10
|
import { billStatusesForUpdateHelp, budgetStatusesForHelp, isBudgetStatusUpdate, parseApprovalTypeFlag, parseBillStatusForUpdate, parseCommaSeparatedBillStatuses, parseCommaSeparatedBudgetStatuses, parseCommaSeparatedCustomerInvoiceStatuses, parseCommaSeparatedIds, parseCommaSeparatedQuotationStatuses, parseCommaSeparatedSupplierStatuses, parseOptionalBillListSortBy, parseOptionalBillListSortDir, parseOptionalDashboardRole, parseOptionalDeals, parseOptionalErrorSeverity, parseOptionalErrorStatus, parseOptionalExtendedProjectStatus, parseOptionalFinancialRole, parseOptionalSupplierAnalyticsTimeFrame, parseOptionalTimeFrame, parseProjectStatusForUpdate, parseUserRole, projectStatusesForHelp, userRolesForHelp, } from "./parse-cli-enums.js";
|
|
11
11
|
import { parseJsonFlag, parseOptionalNumber as parseOptNum, } from "./parse-json-flag.js";
|
|
@@ -261,9 +261,6 @@ Customer invoices
|
|
|
261
261
|
approve-customer-invoice <batchId> Admin approval; rejects voided invoices, notifies the creator, and closes the estimate at cumulative 100% coverage.
|
|
262
262
|
reject-customer-invoice <batchId> --reason <text> Admin rejection; voids QBO invoices and notifies the creator.
|
|
263
263
|
send-customer-invoice-to-contact-person --payload '<json>' Same composer workflow as web; requires invoiceId, to, cc, replyTo, subject, HTML content, and HTML signature.
|
|
264
|
-
mark-customer-invoice-paid <invoiceId> --paymentDate YYYY-MM-DD --paymentProof <path> [--paymentReference <text>]
|
|
265
|
-
Admin-only. Uploads proof, creates a QBO Payment for the live balance when needed, attaches proof in QBO when possible, and records payment history in BB.
|
|
266
|
-
download-customer-invoice-payment-proof <invoiceId> [--output <path>]
|
|
267
264
|
download-customer-invoice-pdf <invoiceId> [--output <path>]
|
|
268
265
|
sync-customer-invoice <invoiceId> Refresh local status and balance from QuickBooks; paid invoices become PAID, reversals restore delivery/approval, and QBO voids can reopen an estimate.
|
|
269
266
|
|
|
@@ -1453,29 +1450,6 @@ export async function runCli(argv = process.argv, runtime = createProcessRuntime
|
|
|
1453
1450
|
await sendCustomerInvoiceToContactPersonFromPayload(parseJsonFlag(String(payloadRaw), "--payload"));
|
|
1454
1451
|
break;
|
|
1455
1452
|
}
|
|
1456
|
-
case "mark-customer-invoice-paid": {
|
|
1457
|
-
const invoiceId = positional[0];
|
|
1458
|
-
const paymentDate = getFlag(flags, "paymentDate");
|
|
1459
|
-
const paymentProofPath = getFlag(flags, "paymentProof");
|
|
1460
|
-
if (!invoiceId || !paymentDate || !paymentProofPath) {
|
|
1461
|
-
throw new Error("mark-customer-invoice-paid requires <invoiceId> --paymentDate YYYY-MM-DD --paymentProof <path> [--paymentReference <text>]");
|
|
1462
|
-
}
|
|
1463
|
-
await markCustomerInvoicePaidFromPath({
|
|
1464
|
-
invoiceId,
|
|
1465
|
-
paymentDate,
|
|
1466
|
-
paymentProofPath,
|
|
1467
|
-
paymentReference: getFlag(flags, "paymentReference"),
|
|
1468
|
-
});
|
|
1469
|
-
break;
|
|
1470
|
-
}
|
|
1471
|
-
case "download-customer-invoice-payment-proof": {
|
|
1472
|
-
const invoiceId = positional[0];
|
|
1473
|
-
if (!invoiceId) {
|
|
1474
|
-
throw new Error("download-customer-invoice-payment-proof requires <invoiceId> [--output <path>]");
|
|
1475
|
-
}
|
|
1476
|
-
await downloadCustomerInvoicePaymentProof(invoiceId, getFlag(flags, "output"));
|
|
1477
|
-
break;
|
|
1478
|
-
}
|
|
1479
1453
|
case "download-customer-invoice-pdf": {
|
|
1480
1454
|
const invoiceId = positional[0];
|
|
1481
1455
|
if (!invoiceId) {
|
|
@@ -1616,6 +1590,30 @@ export async function runCli(argv = process.argv, runtime = createProcessRuntime
|
|
|
1616
1590
|
await getProject(id);
|
|
1617
1591
|
break;
|
|
1618
1592
|
}
|
|
1593
|
+
case "get-project-hub-status": {
|
|
1594
|
+
const projectId = positional[0];
|
|
1595
|
+
if (!projectId) {
|
|
1596
|
+
throw new Error("get-project-hub-status requires <projectId>");
|
|
1597
|
+
}
|
|
1598
|
+
await getProjectHubStatus(projectId);
|
|
1599
|
+
break;
|
|
1600
|
+
}
|
|
1601
|
+
case "setup-project-hub": {
|
|
1602
|
+
const projectId = positional[0];
|
|
1603
|
+
if (!projectId) {
|
|
1604
|
+
throw new Error("setup-project-hub requires <projectId>");
|
|
1605
|
+
}
|
|
1606
|
+
await setupProjectHub(projectId);
|
|
1607
|
+
break;
|
|
1608
|
+
}
|
|
1609
|
+
case "sync-project-hub-commercial-documents": {
|
|
1610
|
+
const projectId = positional[0];
|
|
1611
|
+
if (!projectId) {
|
|
1612
|
+
throw new Error("sync-project-hub-commercial-documents requires <projectId>");
|
|
1613
|
+
}
|
|
1614
|
+
await syncProjectHubCommercialDocuments(projectId);
|
|
1615
|
+
break;
|
|
1616
|
+
}
|
|
1619
1617
|
case "create-project": {
|
|
1620
1618
|
const name = getFlag(flags, "name");
|
|
1621
1619
|
const companyId = getFlag(flags, "companyId");
|
package/dist/registry/index.js
CHANGED
|
@@ -59,6 +59,7 @@ const mutationPrefixes = [
|
|
|
59
59
|
"save-",
|
|
60
60
|
"send-",
|
|
61
61
|
"stage-",
|
|
62
|
+
"setup-",
|
|
62
63
|
"submit-",
|
|
63
64
|
"sync-",
|
|
64
65
|
"update-",
|
|
@@ -110,13 +111,14 @@ const externalWriteTargets = new Set([
|
|
|
110
111
|
"import-qbo-project",
|
|
111
112
|
"mark-budget-items-not-utilized",
|
|
112
113
|
"mark-budget-won",
|
|
113
|
-
"mark-customer-invoice-paid",
|
|
114
114
|
"reconcile-project",
|
|
115
115
|
"reject-customer-invoice",
|
|
116
116
|
"reactivate-suppliers",
|
|
117
117
|
"restore-budget-item",
|
|
118
118
|
"restore-budget-version",
|
|
119
119
|
"retry-integration-operation",
|
|
120
|
+
"setup-project-hub",
|
|
121
|
+
"sync-project-hub-commercial-documents",
|
|
120
122
|
"sync-customer-invoice",
|
|
121
123
|
"update-bill",
|
|
122
124
|
"update-budget-status",
|
|
@@ -143,7 +145,6 @@ const financialWriteTargets = new Set([
|
|
|
143
145
|
"delete-customer-invoice",
|
|
144
146
|
"mark-budget-items-not-utilized",
|
|
145
147
|
"mark-budget-won",
|
|
146
|
-
"mark-customer-invoice-paid",
|
|
147
148
|
"patch-bill-payment",
|
|
148
149
|
"reject-customer-invoice",
|
|
149
150
|
"restore-budget-item",
|
|
@@ -343,16 +344,6 @@ const registry = [
|
|
|
343
344
|
"customer-invoice",
|
|
344
345
|
"send",
|
|
345
346
|
]),
|
|
346
|
-
legacyCommand("mark-customer-invoice-paid", [
|
|
347
|
-
"customer-invoice",
|
|
348
|
-
"payment",
|
|
349
|
-
"mark-paid",
|
|
350
|
-
]),
|
|
351
|
-
legacyCommand("download-customer-invoice-payment-proof", [
|
|
352
|
-
"customer-invoice",
|
|
353
|
-
"payment-proof",
|
|
354
|
-
"download",
|
|
355
|
-
]),
|
|
356
347
|
legacyCommand("download-customer-invoice-pdf", [
|
|
357
348
|
"customer-invoice",
|
|
358
349
|
"pdf",
|
|
@@ -374,6 +365,13 @@ const registry = [
|
|
|
374
365
|
legacyCommand("delete-company", ["company", "delete"]),
|
|
375
366
|
legacyCommand("list-projects", ["project", "list"]),
|
|
376
367
|
legacyCommand("get-project", ["project", "get"]),
|
|
368
|
+
legacyCommand("get-project-hub-status", ["project", "hub", "status"]),
|
|
369
|
+
legacyCommand("setup-project-hub", ["project", "hub", "setup"]),
|
|
370
|
+
legacyCommand("sync-project-hub-commercial-documents", [
|
|
371
|
+
"project",
|
|
372
|
+
"hub",
|
|
373
|
+
"sync",
|
|
374
|
+
]),
|
|
377
375
|
legacyCommand("create-project", ["project", "create"]),
|
|
378
376
|
legacyCommand("update-project", ["project", "update"]),
|
|
379
377
|
legacyCommand("delete-project", ["project", "delete"]),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@go-labs-sg/bb",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.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",
|
|
@@ -89,6 +89,7 @@ This table highlights the role-sensitive command groups an agent is most likely
|
|
|
89
89
|
| `bb auth whoami`; own `bb user api-key create`, `bb user api-key list`, and `bb user api-key revoke` | Any active role; omit `--userId` to act as the current user. |
|
|
90
90
|
| Cross-user API-key commands; `bb user create` | `ADMIN`. |
|
|
91
91
|
| Budget/project mutations, estimates, and budget attachments | `ADMIN`, budget creator, or project assignment as Business Development, Inside Sales, or Project Manager, depending on the endpoint. |
|
|
92
|
+
| `bb project hub status`, `bb project hub setup`, and `bb project hub sync` | `ADMIN`, project creator, or project assignment as Business Development, Inside Sales, or Project Manager. Setup and sync also enforce Project Hub lifecycle/readiness checks. |
|
|
92
93
|
| Edit/delete a bill or quotation | Usually creator or `ADMIN`, with a supported status. |
|
|
93
94
|
| `bb budget approve`, `bb budget reject`, `bb supplier approve`, and `bb supplier reject` | Current user must be the pending approver and currently have role `LEAD` or `ADMIN`. |
|
|
94
95
|
| `bb bill approve` and `bb bill reject` | Current user must be the pending approver and currently have role `ACCOUNTING_TEAM` for the finance/check stage or `ADMIN` for the final approval stage. |
|
|
@@ -97,7 +98,6 @@ This table highlights the role-sensitive command groups an agent is most likely
|
|
|
97
98
|
| `bb bill status update ... APPROVED` or `... PAID` | `ADMIN`; state, integration, payment-reference, and payment-proof requirements still apply. |
|
|
98
99
|
| `bb bill payment patch`; `bb bill invoice-number patch` | `ACCOUNTING_TEAM` or `ADMIN`, with the procedure's bill-state constraints. |
|
|
99
100
|
| `bb customer-invoice approve` and `bb customer-invoice reject` | `ADMIN` batch operations selected by batch ID; the API resolves the internal pending Admin approval record. |
|
|
100
|
-
| `bb customer-invoice payment mark-paid` | `ADMIN`. |
|
|
101
101
|
| Dashboard/performance, supplier analytics, errors, integration operations | `ADMIN`. |
|
|
102
102
|
| `bb item category create`, `bb item category update`, `bb item category delete`, `bb supplier delete`, `bb supplier reactivate`, and `bb budget item placeholder-bill create` | `ADMIN`. |
|
|
103
103
|
|
|
@@ -121,6 +121,7 @@ The following distinctions are especially important:
|
|
|
121
121
|
- Budget, supplier, bill, and quotation decisions require both the matching live role and a pending approval assigned to the current identity. Customer-invoice approve/reject are Admin batch operations selected by batch ID; the API resolves their internal approval record.
|
|
122
122
|
- Some company, contact, project, supplier, and catalog collaboration procedures intentionally allow every active authenticated role. Treat these endpoint-specific rules as exceptions rather than inferring access to adjacent mutations.
|
|
123
123
|
- Budget and project commands often depend on creator/assignment access even when the role itself is valid.
|
|
124
|
+
- Project Hub setup and sync are asynchronous external writes. Read `bb project hub status <project-id>` first, obtain explicit approval, and request the mutation once instead of repeatedly queueing it. Setup readiness remains visible through `status`; commercial sync returns an outbox operation ID for Admin diagnostics.
|
|
124
125
|
- Bill/claim status commands have separate role rules for `CHECKED`, `APPROVED`, and `PAID`.
|
|
125
126
|
- `bb bill create --payload` uses `isClaimable=false`; `isClaimable=true` creates a claim. Claims intentionally permit some base-user flows that supplier bills do not.
|
|
126
127
|
- Staged attachments belong to the user in the database. Do not reuse, share, or manually construct staged keys.
|