@go-labs-sg/bb 1.2.8 → 1.2.9
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 +4 -4
- package/dist/commands.js +198 -15
- package/dist/index.js +9 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,12 +56,12 @@ Global options and flags use `--key=value` or `--key value` (see `bb help`).
|
|
|
56
56
|
|
|
57
57
|
| Area | Commands (non-exhaustive) |
|
|
58
58
|
| --- | --- |
|
|
59
|
-
| **Budgets** | `list-budgets` (full payload by default; `--summary` or `--includeDetails false` for slim list), `get-budget`, `get-budget-items`, `get-budget-details`, `get-budget-categories`, `get-budget-versions`, `update-budget-status`, `create-budget` / `update-budget` (`--payload`), `delete-budget`, `create-budget-approval
|
|
60
|
-
| **Bills** | `list-bills`, `list-claims`, `create-bill` (`--payload`), `update-bill` (`--payload`), `delete-bill`, `create-bill-approval
|
|
61
|
-
| **Approvals** | `list-approvals` / `get-pending-approvals`, `approve-bill
|
|
59
|
+
| **Budgets** | `list-budgets` (full payload by default; `--summary` or `--includeDetails false` for slim list), `get-budget`, `get-budget-items`, `get-budget-details`, `get-budget-categories`, `get-budget-versions`, `update-budget-status`, `create-budget` / `update-budget` (`--payload`), `delete-budget`, `create-budget-approval` (also sends approval request emails), `add-budget-items`, `update-budget-item`, `remove-budget-item`, `reorder-budget-items`, `update-budget-item-supplier`, `create-budget-category`, `update-budget-category`, `delete-budget-category`, `update-budget-commission`, `update-budget-discount` (`--payload` where noted), `upload-budget-attachment` (`<budgetId>` + local file path; uses `attachment.requestBudgetAttachmentUpload` + PUT + `attachment.confirmBudgetAttachment`) |
|
|
60
|
+
| **Bills** | `list-bills`, `list-claims`, `create-bill` (`--payload`), `update-bill` (`--payload`), `delete-bill`, `create-bill-approval` (also sends approval request emails), `update-bill-status`, `patch-bill-payment` (PAID bills: `--paymentTrackingUrl`, `--paymentReference`, `--quickbooksBillId`, `--paymentDate` ISO; clear with `--clearPaymentTrackingUrl` / `--clearPaymentReference` / `--clearQuickbooksBillId` / `--clearPaymentDate` `true`), `patch-bill-invoice-number`, `get-bill-attachments`, `upload-bill-attachment` (`<billId>` + local path), `get-bill-details` |
|
|
61
|
+
| **Approvals** | `list-approvals` / `get-pending-approvals`, `approve-bill` / `reject-bill` (send reply email), `approve-budget` / `reject-budget` (send reply email), `approve-supplier` / `reject-supplier` (send reply email) |
|
|
62
62
|
| **Companies & projects** | `list-companies`, `get-company`, `create-company`, `update-company` (`--payload`), `delete-company`, `list-projects`, `get-project`, `create-project` (`--asanaTaskId`, `--slackChannelId`, `--slackChannelUrl`, `--slackChannelName`, …; optional `--requestQboAccountantNotification false` to skip QBO accountant emails), `update-project` (`--payload`, optional `requestQboAccountantNotification` in JSON), `delete-project`, `update-project-status` (`<id>` `<status>`: `PITCH` \| `EVENT` \| `COMPLETED` \| `LOST`) |
|
|
63
63
|
| **Contacts** | `list-contacts`, `create-contact-person` (`--payload`), `update-contact-person` (`--payload`) |
|
|
64
|
-
| **Suppliers & items** | `list-suppliers`, `create-supplier` (`--payload`; when supplier status is `PENDING_APPROVAL`, also runs `supplier.createSupplierApproval` and `email.sendSupplierApprovalRequestEmail`), `update-supplier` (`--payload`), `delete-suppliers` (`--ids` CSV; admin), `create-certification` / `create-payment-method` / `create-supplier-role` / `create-supplier-tag` (`--name`), `get-supplier-details`, `get-supplier-analytics`, `list-items`, `create-item` (`--payload`), `update-item` (`--payload`), `delete-item`, `get-item`, `list-item-categories`, `create-item-category`, `update-item-category`, `delete-item-categories` (`--ids` CSV; admin) |
|
|
64
|
+
| **Suppliers & items** | `list-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), `create-certification` / `create-payment-method` / `create-supplier-role` / `create-supplier-tag` (`--name`), `get-supplier-details`, `get-supplier-analytics`, `list-items`, `create-item` (`--payload`), `update-item` (`--payload`), `delete-item`, `get-item`, `list-item-categories`, `create-item-category`, `update-item-category`, `delete-item-categories` (`--ids` CSV; admin) |
|
|
65
65
|
| **Dashboard & users** | `list-users`, `get-user-performance`, `get-dashboard`, `get-monthly-metrics`, `get-system-overview`, `get-estimate-performance`, `get-financial-overview` |
|
|
66
66
|
| **Errors** | `get-recent-errors`, `get-error-metrics` |
|
|
67
67
|
| **Historical / benchmarks** | `get-approved-budgets`, `get-budget-category-benchmarks`, `get-item-pricing-history`, `get-supplier-pricing-history` |
|
package/dist/commands.js
CHANGED
|
@@ -81,7 +81,21 @@ export async function approveBill(billId) {
|
|
|
81
81
|
billApprovalId: approval.id,
|
|
82
82
|
status: "APPROVED",
|
|
83
83
|
});
|
|
84
|
-
|
|
84
|
+
let email;
|
|
85
|
+
try {
|
|
86
|
+
await api.email.sendReplyBillApprovalEmail.mutate(result.updatedApproval);
|
|
87
|
+
email = { sent: true };
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
email = {
|
|
91
|
+
sent: false,
|
|
92
|
+
error: error instanceof Error ? error.message : String(error),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
out({
|
|
96
|
+
...result,
|
|
97
|
+
email,
|
|
98
|
+
});
|
|
85
99
|
}
|
|
86
100
|
export async function listApprovals(opts) {
|
|
87
101
|
const notifications = await api.budget.getNotifications.query();
|
|
@@ -137,7 +151,28 @@ export async function listSuppliers(opts) {
|
|
|
137
151
|
// --- Bills (MCP parity) ---
|
|
138
152
|
export async function createBillApproval(billId) {
|
|
139
153
|
const result = await api.bill.createApproval.mutate({ billId });
|
|
140
|
-
|
|
154
|
+
const approvalIds = (result.results ?? []).map((item) => ({ id: item.id }));
|
|
155
|
+
let email;
|
|
156
|
+
if (approvalIds.length === 0) {
|
|
157
|
+
email = { sent: false, count: 0, error: "No approval ids returned" };
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
try {
|
|
161
|
+
await api.email.sendBillApprovalRequestEmail.mutate(approvalIds);
|
|
162
|
+
email = { sent: true, count: approvalIds.length };
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
email = {
|
|
166
|
+
sent: false,
|
|
167
|
+
count: approvalIds.length,
|
|
168
|
+
error: error instanceof Error ? error.message : String(error),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
out({
|
|
173
|
+
...result,
|
|
174
|
+
email,
|
|
175
|
+
});
|
|
141
176
|
}
|
|
142
177
|
export async function updateBillStatus(opts) {
|
|
143
178
|
const result = await api.bill.updateStatus.mutate({
|
|
@@ -264,7 +299,28 @@ export async function getBudgetVersions(budgetId) {
|
|
|
264
299
|
}
|
|
265
300
|
export async function createBudgetApproval(budgetId) {
|
|
266
301
|
const result = await api.budget.createBudgetApproval.mutate({ budgetId });
|
|
267
|
-
|
|
302
|
+
const approvalIds = (result.results ?? []).map((item) => ({ id: item.id }));
|
|
303
|
+
let email;
|
|
304
|
+
if (approvalIds.length === 0) {
|
|
305
|
+
email = { sent: false, count: 0, error: "No approval ids returned" };
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
try {
|
|
309
|
+
await api.email.sendApprovalRequestEmail.mutate(approvalIds);
|
|
310
|
+
email = { sent: true, count: approvalIds.length };
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
email = {
|
|
314
|
+
sent: false,
|
|
315
|
+
count: approvalIds.length,
|
|
316
|
+
error: error instanceof Error ? error.message : String(error),
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
out({
|
|
321
|
+
...result,
|
|
322
|
+
email,
|
|
323
|
+
});
|
|
268
324
|
}
|
|
269
325
|
export async function getBudgetDetails(budgetId) {
|
|
270
326
|
const detail = await api.budget.getBudgetDetail.query({ id: budgetId });
|
|
@@ -412,8 +468,59 @@ export async function createSupplierFromPayload(raw) {
|
|
|
412
468
|
}
|
|
413
469
|
export async function updateSupplierFromPayload(raw) {
|
|
414
470
|
const input = parseSupplierUpdatePayload(raw);
|
|
415
|
-
const
|
|
416
|
-
|
|
471
|
+
const updated = await api.supplier.updateSupplier.mutate(input);
|
|
472
|
+
const supplier = updated.result;
|
|
473
|
+
if (supplier.status !== "PENDING_APPROVAL") {
|
|
474
|
+
out(updated);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
let approvalResult;
|
|
478
|
+
try {
|
|
479
|
+
const approval = await api.supplier.createSupplierApproval.mutate({
|
|
480
|
+
supplierId: supplier.id,
|
|
481
|
+
});
|
|
482
|
+
const approvalIds = (approval.results ?? []).map((item) => ({
|
|
483
|
+
id: item.id,
|
|
484
|
+
}));
|
|
485
|
+
if (approvalIds.length === 0) {
|
|
486
|
+
approvalResult = {
|
|
487
|
+
requested: true,
|
|
488
|
+
approvalCount: 0,
|
|
489
|
+
email: { sent: false, count: 0, error: "No approval ids returned" },
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
try {
|
|
494
|
+
await api.email.sendSupplierApprovalRequestEmail.mutate(approvalIds);
|
|
495
|
+
approvalResult = {
|
|
496
|
+
requested: true,
|
|
497
|
+
approvalCount: approvalIds.length,
|
|
498
|
+
email: { sent: true, count: approvalIds.length },
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
catch (error) {
|
|
502
|
+
approvalResult = {
|
|
503
|
+
requested: true,
|
|
504
|
+
approvalCount: approvalIds.length,
|
|
505
|
+
email: {
|
|
506
|
+
sent: false,
|
|
507
|
+
count: approvalIds.length,
|
|
508
|
+
error: error instanceof Error ? error.message : String(error),
|
|
509
|
+
},
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
catch (error) {
|
|
515
|
+
approvalResult = {
|
|
516
|
+
requested: false,
|
|
517
|
+
error: error instanceof Error ? error.message : String(error),
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
out({
|
|
521
|
+
...updated,
|
|
522
|
+
supplierApproval: approvalResult,
|
|
523
|
+
});
|
|
417
524
|
}
|
|
418
525
|
export async function createItemFromPayload(raw) {
|
|
419
526
|
const input = parseItemCreatePayload(raw);
|
|
@@ -457,11 +564,25 @@ export async function approveBudget(budgetId) {
|
|
|
457
564
|
if (!approval) {
|
|
458
565
|
throw new Error("No pending budget approval found for this budget and approver.");
|
|
459
566
|
}
|
|
460
|
-
await api.budget.updateBudgetApproval.mutate({
|
|
567
|
+
const result = await api.budget.updateBudgetApproval.mutate({
|
|
461
568
|
budgetApprovalId: approval.id,
|
|
462
569
|
status: ExtendedApprovalStatus.APPROVED,
|
|
463
570
|
});
|
|
464
|
-
|
|
571
|
+
let email;
|
|
572
|
+
try {
|
|
573
|
+
await api.email.sendReplyApprovalEmail.mutate(result.updatedApproval);
|
|
574
|
+
email = { sent: true };
|
|
575
|
+
}
|
|
576
|
+
catch (error) {
|
|
577
|
+
email = {
|
|
578
|
+
sent: false,
|
|
579
|
+
error: error instanceof Error ? error.message : String(error),
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
out({
|
|
583
|
+
...result,
|
|
584
|
+
email,
|
|
585
|
+
});
|
|
465
586
|
}
|
|
466
587
|
export async function rejectBudget(budgetId, reason) {
|
|
467
588
|
const notifications = await api.budget.getNotifications.query();
|
|
@@ -474,12 +595,26 @@ export async function rejectBudget(budgetId, reason) {
|
|
|
474
595
|
if (!approval) {
|
|
475
596
|
throw new Error("No pending budget approval found for this budget and approver.");
|
|
476
597
|
}
|
|
477
|
-
await api.budget.updateBudgetApproval.mutate({
|
|
598
|
+
const result = await api.budget.updateBudgetApproval.mutate({
|
|
478
599
|
budgetApprovalId: approval.id,
|
|
479
600
|
status: ExtendedApprovalStatus.REJECTED,
|
|
480
601
|
rejectionReason: reason,
|
|
481
602
|
});
|
|
482
|
-
|
|
603
|
+
let email;
|
|
604
|
+
try {
|
|
605
|
+
await api.email.sendReplyApprovalEmail.mutate(result.updatedApproval);
|
|
606
|
+
email = { sent: true };
|
|
607
|
+
}
|
|
608
|
+
catch (error) {
|
|
609
|
+
email = {
|
|
610
|
+
sent: false,
|
|
611
|
+
error: error instanceof Error ? error.message : String(error),
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
out({
|
|
615
|
+
...result,
|
|
616
|
+
email,
|
|
617
|
+
});
|
|
483
618
|
}
|
|
484
619
|
export async function getSupplierDetails(supplierId) {
|
|
485
620
|
const supplier = await api.supplier.getSupplierById.query({
|
|
@@ -497,11 +632,28 @@ export async function approveSupplier(supplierId) {
|
|
|
497
632
|
if (!approval) {
|
|
498
633
|
throw new Error("No pending supplier approval found.");
|
|
499
634
|
}
|
|
500
|
-
await api.supplier.updateSupplierApproval.mutate({
|
|
635
|
+
const result = await api.supplier.updateSupplierApproval.mutate({
|
|
501
636
|
supplierApprovalId: approval.id,
|
|
502
637
|
status: ExtendedApprovalStatus.APPROVED,
|
|
503
638
|
});
|
|
504
|
-
|
|
639
|
+
let email;
|
|
640
|
+
try {
|
|
641
|
+
await api.email.sendReplySupplierApprovalEmail.mutate({
|
|
642
|
+
...result.updatedApproval,
|
|
643
|
+
rejectionReason: result.updatedApproval.rejectionReason ?? undefined,
|
|
644
|
+
});
|
|
645
|
+
email = { sent: true };
|
|
646
|
+
}
|
|
647
|
+
catch (error) {
|
|
648
|
+
email = {
|
|
649
|
+
sent: false,
|
|
650
|
+
error: error instanceof Error ? error.message : String(error),
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
out({
|
|
654
|
+
...result,
|
|
655
|
+
email,
|
|
656
|
+
});
|
|
505
657
|
}
|
|
506
658
|
export async function rejectSupplier(supplierId, reason) {
|
|
507
659
|
const notifications = await api.budget.getNotifications.query();
|
|
@@ -513,12 +665,29 @@ export async function rejectSupplier(supplierId, reason) {
|
|
|
513
665
|
if (!approval) {
|
|
514
666
|
throw new Error("No pending supplier approval found.");
|
|
515
667
|
}
|
|
516
|
-
await api.supplier.updateSupplierApproval.mutate({
|
|
668
|
+
const result = await api.supplier.updateSupplierApproval.mutate({
|
|
517
669
|
supplierApprovalId: approval.id,
|
|
518
670
|
status: ExtendedApprovalStatus.REJECTED,
|
|
519
671
|
rejectionReason: reason,
|
|
520
672
|
});
|
|
521
|
-
|
|
673
|
+
let email;
|
|
674
|
+
try {
|
|
675
|
+
await api.email.sendReplySupplierApprovalEmail.mutate({
|
|
676
|
+
...result.updatedApproval,
|
|
677
|
+
rejectionReason: result.updatedApproval.rejectionReason ?? undefined,
|
|
678
|
+
});
|
|
679
|
+
email = { sent: true };
|
|
680
|
+
}
|
|
681
|
+
catch (error) {
|
|
682
|
+
email = {
|
|
683
|
+
sent: false,
|
|
684
|
+
error: error instanceof Error ? error.message : String(error),
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
out({
|
|
688
|
+
...result,
|
|
689
|
+
email,
|
|
690
|
+
});
|
|
522
691
|
}
|
|
523
692
|
export async function rejectBill(billId, reason) {
|
|
524
693
|
const notifications = await api.budget.getNotifications.query();
|
|
@@ -530,12 +699,26 @@ export async function rejectBill(billId, reason) {
|
|
|
530
699
|
if (!approval) {
|
|
531
700
|
throw new Error("No pending bill approval found.");
|
|
532
701
|
}
|
|
533
|
-
await api.bill.updateApproval.mutate({
|
|
702
|
+
const result = await api.bill.updateApproval.mutate({
|
|
534
703
|
billApprovalId: approval.id,
|
|
535
704
|
status: ExtendedApprovalStatus.REJECTED,
|
|
536
705
|
rejectionReason: reason,
|
|
537
706
|
});
|
|
538
|
-
|
|
707
|
+
let email;
|
|
708
|
+
try {
|
|
709
|
+
await api.email.sendReplyBillApprovalEmail.mutate(result.updatedApproval);
|
|
710
|
+
email = { sent: true };
|
|
711
|
+
}
|
|
712
|
+
catch (error) {
|
|
713
|
+
email = {
|
|
714
|
+
sent: false,
|
|
715
|
+
error: error instanceof Error ? error.message : String(error),
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
out({
|
|
719
|
+
...result,
|
|
720
|
+
email,
|
|
721
|
+
});
|
|
539
722
|
}
|
|
540
723
|
// --- Companies & projects ---
|
|
541
724
|
export async function listCompanies(opts) {
|
package/dist/index.js
CHANGED
|
@@ -79,7 +79,7 @@ Budgets
|
|
|
79
79
|
create-budget --payload '<json>' (budget.createBudget; ISO dates for date/startTime/endTime)
|
|
80
80
|
update-budget --payload '<json>' (budget.updateBudget; must include id)
|
|
81
81
|
delete-budget <budgetId>
|
|
82
|
-
create-budget-approval <budgetId>
|
|
82
|
+
create-budget-approval <budgetId> (also sends approval request emails)
|
|
83
83
|
add-budget-items --budgetId --items '[{"itemId":"…","quantity":1,"markup":30},…]'
|
|
84
84
|
update-budget-item --id <budgetItemId> [--description] [--note] [--quantity] [--markup] [--cost] [--unitPrice] [--isFreeOfCharge true|false] [--gstInclusive true|false]
|
|
85
85
|
remove-budget-item <budgetItemId>
|
|
@@ -95,7 +95,7 @@ Budgets
|
|
|
95
95
|
Bills
|
|
96
96
|
list-bills [--projectId] [--budgetId] [--status CSV] [--search <text>] [--isClaimable true|false] [--createdByIds <csv>] [--sortBy createdAt|amount|status] [--sortDir asc|desc] [--page] [--pageSize]
|
|
97
97
|
list-claims same flags as list-bills; only reimbursable claims (ignores --isClaimable)
|
|
98
|
-
create-bill-approval <billId>
|
|
98
|
+
create-bill-approval <billId> (also sends approval request emails)
|
|
99
99
|
create-bill --payload '<json>' (bill.create; use upload-bill-attachment after create to add files)
|
|
100
100
|
update-bill --payload '<json>' (bill.update; must include id)
|
|
101
101
|
delete-bill <billId>
|
|
@@ -110,12 +110,12 @@ Bills
|
|
|
110
110
|
|
|
111
111
|
Approvals
|
|
112
112
|
list-approvals | get-pending-approvals [--type budget|supplier|bill|all]
|
|
113
|
-
approve-bill <billId>
|
|
114
|
-
reject-bill <billId> --reason <text>
|
|
115
|
-
approve-budget <budgetId>
|
|
116
|
-
reject-budget <budgetId> --reason <text>
|
|
117
|
-
approve-supplier <supplierId>
|
|
118
|
-
reject-supplier <supplierId> --reason <text>
|
|
113
|
+
approve-bill <billId> (also sends requester reply email)
|
|
114
|
+
reject-bill <billId> --reason <text> (also sends requester reply email)
|
|
115
|
+
approve-budget <budgetId> (also sends requester reply email)
|
|
116
|
+
reject-budget <budgetId> --reason <text> (also sends requester reply email)
|
|
117
|
+
approve-supplier <supplierId> (also sends requester reply email)
|
|
118
|
+
reject-supplier <supplierId> --reason <text> (also sends requester reply email)
|
|
119
119
|
|
|
120
120
|
Companies & projects
|
|
121
121
|
list-companies [--name] [--createdBy] [--page] [--perPage]
|
|
@@ -139,7 +139,7 @@ Contacts
|
|
|
139
139
|
Suppliers & items
|
|
140
140
|
list-suppliers [--name] [--page] [--perPage]
|
|
141
141
|
create-supplier --payload '<json>' (supplier.createSupplier; auto-requests approval + emails when status is PENDING_APPROVAL)
|
|
142
|
-
update-supplier --payload '<json>' (supplier.updateSupplier; must include id)
|
|
142
|
+
update-supplier --payload '<json>' (supplier.updateSupplier; must include id; auto-requests approval + emails when status is PENDING_APPROVAL)
|
|
143
143
|
delete-suppliers --ids <csv> (supplier.deleteSuppliers; admin)
|
|
144
144
|
create-certification --name (supplier.createCertification)
|
|
145
145
|
create-payment-method --name (supplier.createPaymentMethod)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@go-labs-sg/bb",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.9",
|
|
4
4
|
"description": "Budget Builder CLI — list budgets, bills, approvals, suppliers; approve bills; change status. For AI agents (e.g. Chuck/OpenClaw).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|