@go-labs-sg/bb 1.0.4 → 1.1.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 +26 -12
- package/dist/commands.js +398 -16
- package/dist/filter-enums.js +58 -0
- package/dist/index.js +500 -66
- package/dist/parse-args.js +3 -1
- package/dist/parse-cli-enums.js +216 -0
- package/dist/parse-json-flag.js +20 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# bb
|
|
2
2
|
|
|
3
|
-
Command-line interface for **Budget Builder**. Call budgets, bills, approvals, and
|
|
3
|
+
Command-line interface for **Budget Builder**. Call budgets, bills, approvals, suppliers, dashboard, and more from your terminal or from tools that don’t support MCP (e.g. some AI agents). Responses are JSON on stdout.
|
|
4
4
|
|
|
5
5
|
**Registry:** [`@go-labs-sg/bb`](https://www.npmjs.com/package/@go-labs-sg/bb)
|
|
6
6
|
|
|
7
7
|
## Requirements
|
|
8
8
|
|
|
9
|
-
- **Node.js** 18+
|
|
9
|
+
- **Node.js** 18+ (ESM; relative imports in `dist` use `.js` extensions)
|
|
10
10
|
- A **Budget Builder API key** (same key used for the MCP server)
|
|
11
11
|
|
|
12
12
|
## Install
|
|
@@ -48,17 +48,21 @@ bb list-suppliers
|
|
|
48
48
|
|
|
49
49
|
Global options and flags use `--key=value` or `--key value` (see `bb help`).
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
**Authoritative command list:** run `bb help` — it includes every command, positional args, and flags (kept in sync with the MCP tool surface). You can also use MCP-style `snake_case` (e.g. `bb list_bills`); it is normalized to kebab-case.
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
### Command overview
|
|
54
|
+
|
|
55
|
+
| Area | Commands (non-exhaustive) |
|
|
54
56
|
| --- | --- |
|
|
55
|
-
| `list-budgets`
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
57
|
+
| **Budgets** | `list-budgets`, `get-budget`, `get-budget-items`, `get-budget-details`, `get-budget-categories`, `get-budget-versions`, `update-budget-status`, `create-budget-approval`, `add-budget-items`, `update-budget-item`, `remove-budget-item` |
|
|
58
|
+
| **Bills** | `list-bills`, `list-claims`, `create-bill-approval`, `update-bill-status`, `patch-bill-payment`, `patch-bill-invoice-number`, `get-bill-attachments`, `get-bill-details` |
|
|
59
|
+
| **Approvals** | `list-approvals` / `get-pending-approvals`, `approve-bill`, `reject-bill`, `approve-budget`, `reject-budget`, `approve-supplier`, `reject-supplier` |
|
|
60
|
+
| **Companies & projects** | `list-companies`, `get-company`, `create-company`, `list-projects`, `get-project`, `create-project`, `update-project-status` |
|
|
61
|
+
| **Contacts** | `list-contacts` |
|
|
62
|
+
| **Suppliers & items** | `list-suppliers`, `get-supplier-details`, `get-supplier-analytics`, `list-items`, `get-item`, `list-item-categories` |
|
|
63
|
+
| **Dashboard & users** | `list-users`, `get-user-performance`, `get-dashboard`, `get-financial-overview` |
|
|
64
|
+
| **Errors** | `get-recent-errors`, `get-error-metrics` |
|
|
65
|
+
| **Historical / benchmarks** | `get-approved-budgets`, `get-budget-category-benchmarks`, `get-item-pricing-history`, `get-supplier-pricing-history` |
|
|
62
66
|
|
|
63
67
|
## Output
|
|
64
68
|
|
|
@@ -70,7 +74,7 @@ A `.env` file in the **current working directory** is loaded automatically (for
|
|
|
70
74
|
|
|
71
75
|
## Developing in this repo
|
|
72
76
|
|
|
73
|
-
From the monorepo root:
|
|
77
|
+
From the monorepo root (after `bun install`):
|
|
74
78
|
|
|
75
79
|
```bash
|
|
76
80
|
export BB_API_KEY=...
|
|
@@ -80,5 +84,15 @@ bun run bb -- list-budgets
|
|
|
80
84
|
Or from `packages/cli`:
|
|
81
85
|
|
|
82
86
|
```bash
|
|
87
|
+
bun run build # emit dist/ via tsc
|
|
88
|
+
bun run typecheck # tsc --noEmit
|
|
83
89
|
bun run src/index.ts list-budgets
|
|
84
90
|
```
|
|
91
|
+
|
|
92
|
+
### Layout
|
|
93
|
+
|
|
94
|
+
- **`src/filter-enums.ts`** — “extended” filter enums (ALL + Prisma enums), maintained here so the published package does not depend on unpublished workspace packages. Keep in line with `packages/utils/src/filter-enums.ts` when those values change.
|
|
95
|
+
|
|
96
|
+
### Publish
|
|
97
|
+
|
|
98
|
+
`prepublishOnly` strips `workspace:` / `catalog:` entries from this package’s `package.json`, runs `tsc`, then `postpack` restores the file — so the npm tarball does not contain monorepo protocol dependencies. Types from `@bb/api` are compile-time only (`import type`). Enum **values** in emitted JS import `@bb/db/enums` (workspace `@bb/db` during monorepo development).
|
package/dist/commands.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { api } from "./api-client.js";
|
|
2
|
+
import { BudgetRole, Deals, ExtendedApprovalStatus, ExtendedApprovalType, ExtendedBudgetStatus, TimeFrame, } from "./filter-enums.js";
|
|
3
|
+
import { billStatusesForApi, } from "./parse-cli-enums.js";
|
|
2
4
|
const BUDGET = "BUDGET";
|
|
3
5
|
const BILL = "BILL";
|
|
4
6
|
const SUPPLIER = "SUPPLIER";
|
|
5
7
|
function isPendingRequest(n) {
|
|
6
8
|
return n.notificationType === "request" && n.status === "PENDING_APPROVAL";
|
|
7
9
|
}
|
|
8
|
-
function isApprovalType(s) {
|
|
9
|
-
return s === BUDGET || s === BILL || s === SUPPLIER;
|
|
10
|
-
}
|
|
11
10
|
const out = (data) => {
|
|
12
11
|
console.log(JSON.stringify(data, null, 2));
|
|
13
12
|
};
|
|
@@ -16,6 +15,11 @@ export async function listBudgets(opts) {
|
|
|
16
15
|
projectId: opts.projectId,
|
|
17
16
|
name: opts.name,
|
|
18
17
|
statuses: opts.statuses,
|
|
18
|
+
createdBy: opts.createdBy,
|
|
19
|
+
dateFrom: opts.dateFrom,
|
|
20
|
+
dateTo: opts.dateTo,
|
|
21
|
+
sortBy: opts.sortBy,
|
|
22
|
+
sortDir: opts.sortDir,
|
|
19
23
|
page: opts.page ?? 1,
|
|
20
24
|
perPage: opts.perPage ?? 20,
|
|
21
25
|
};
|
|
@@ -30,18 +34,24 @@ export async function getBudget(id) {
|
|
|
30
34
|
out({ budget, items });
|
|
31
35
|
}
|
|
32
36
|
export async function updateBudgetStatus(budgetId, status) {
|
|
33
|
-
const
|
|
37
|
+
const result = await api.budget.updateBudgetStatus.mutate({
|
|
34
38
|
id: budgetId,
|
|
35
|
-
status
|
|
36
|
-
};
|
|
37
|
-
const result = await api.budget.updateBudgetStatus.mutate(input);
|
|
39
|
+
status,
|
|
40
|
+
});
|
|
38
41
|
out(result);
|
|
39
42
|
}
|
|
40
43
|
export async function listBills(opts) {
|
|
41
44
|
const input = {
|
|
42
45
|
projectId: opts.projectId,
|
|
43
46
|
budgetId: opts.budgetId,
|
|
44
|
-
|
|
47
|
+
statuses: billStatusesForApi(opts.statuses),
|
|
48
|
+
search: opts.search?.trim() || undefined,
|
|
49
|
+
isClaimable: opts.isClaimable,
|
|
50
|
+
createdByIds: opts.createdByIds && opts.createdByIds.length > 0
|
|
51
|
+
? opts.createdByIds
|
|
52
|
+
: undefined,
|
|
53
|
+
sortBy: opts.sortBy,
|
|
54
|
+
sortDir: opts.sortDir,
|
|
45
55
|
page: opts.page ?? 1,
|
|
46
56
|
pageSize: opts.pageSize ?? 20,
|
|
47
57
|
};
|
|
@@ -61,19 +71,17 @@ export async function approveBill(billId) {
|
|
|
61
71
|
billApprovalId: approval.id,
|
|
62
72
|
status: "APPROVED",
|
|
63
73
|
});
|
|
64
|
-
out(
|
|
74
|
+
out(result);
|
|
65
75
|
}
|
|
66
76
|
export async function listApprovals(opts) {
|
|
67
77
|
const notifications = await api.budget.getNotifications.query();
|
|
68
78
|
const pending = notifications.filter(isPendingRequest);
|
|
69
|
-
const
|
|
70
|
-
if (raw !== undefined && raw !== "ALL" && !isApprovalType(raw)) {
|
|
71
|
-
throw new Error("Invalid --type. Use one of: budget, supplier, bill, all.");
|
|
72
|
-
}
|
|
73
|
-
const typeFilter = !opts.type || opts.type.toLowerCase() === "all"
|
|
79
|
+
const typeFilter = opts.type === undefined || opts.type === "ALL"
|
|
74
80
|
? (_n) => true
|
|
75
|
-
: (n) =>
|
|
76
|
-
const filtered = pending
|
|
81
|
+
: (n) => n.type === opts.type;
|
|
82
|
+
const filtered = pending
|
|
83
|
+
.filter(typeFilter)
|
|
84
|
+
.filter((n) => n.type !== BUDGET || !n.budget?.quickbooksEstimateId);
|
|
77
85
|
const items = filtered.map((n) => {
|
|
78
86
|
let id;
|
|
79
87
|
let name;
|
|
@@ -116,3 +124,377 @@ export async function listSuppliers(opts) {
|
|
|
116
124
|
});
|
|
117
125
|
out(result);
|
|
118
126
|
}
|
|
127
|
+
// --- Bills (MCP parity) ---
|
|
128
|
+
export async function createBillApproval(billId) {
|
|
129
|
+
const result = await api.bill.createApproval.mutate({ billId });
|
|
130
|
+
out(result);
|
|
131
|
+
}
|
|
132
|
+
export async function updateBillStatus(opts) {
|
|
133
|
+
const result = await api.bill.updateStatus.mutate({
|
|
134
|
+
id: opts.id,
|
|
135
|
+
status: opts.status,
|
|
136
|
+
rejectionReason: opts.rejectionReason,
|
|
137
|
+
paymentTrackingUrl: opts.paymentTrackingUrl,
|
|
138
|
+
paymentReference: opts.paymentReference,
|
|
139
|
+
});
|
|
140
|
+
out(result);
|
|
141
|
+
}
|
|
142
|
+
export async function patchBillPayment(opts) {
|
|
143
|
+
const result = await api.bill.patchPayment.mutate({
|
|
144
|
+
id: opts.id,
|
|
145
|
+
paymentTrackingUrl: opts.paymentTrackingUrl,
|
|
146
|
+
paymentReference: opts.paymentReference,
|
|
147
|
+
quickbooksBillId: opts.quickbooksBillId,
|
|
148
|
+
});
|
|
149
|
+
out(result);
|
|
150
|
+
}
|
|
151
|
+
export async function patchBillInvoiceNumber(id, invoiceNumber) {
|
|
152
|
+
const result = await api.bill.patchInvoiceNumber.mutate({
|
|
153
|
+
id,
|
|
154
|
+
invoiceNumber,
|
|
155
|
+
});
|
|
156
|
+
out(result);
|
|
157
|
+
}
|
|
158
|
+
export async function getBillAttachments(id) {
|
|
159
|
+
const result = await api.bill.getAttachments.query({ id });
|
|
160
|
+
out(result);
|
|
161
|
+
}
|
|
162
|
+
export async function getBillDetails(id) {
|
|
163
|
+
const result = await api.bill.getById.query({ id });
|
|
164
|
+
out(result);
|
|
165
|
+
}
|
|
166
|
+
// --- Budgets (MCP parity) ---
|
|
167
|
+
export async function getBudgetItemsOnly(budgetId) {
|
|
168
|
+
const items = await api.budget.getBudgetItems.query({ budgetId });
|
|
169
|
+
out(items);
|
|
170
|
+
}
|
|
171
|
+
export async function addBudgetItems(opts) {
|
|
172
|
+
const result = await api.budgetItem.addBudgetItems.mutate(opts);
|
|
173
|
+
out(result);
|
|
174
|
+
}
|
|
175
|
+
export async function updateBudgetItem(input) {
|
|
176
|
+
const result = await api.budgetItem.updateBudgetItem.mutate(input);
|
|
177
|
+
out(result);
|
|
178
|
+
}
|
|
179
|
+
export async function removeBudgetItem(budgetItemId) {
|
|
180
|
+
const result = await api.budgetItem.removeBudgetItem.mutate({
|
|
181
|
+
budgetItemId,
|
|
182
|
+
});
|
|
183
|
+
out(result);
|
|
184
|
+
}
|
|
185
|
+
export async function getBudgetCategories() {
|
|
186
|
+
const categories = await api.budget.getBudgetCategories.query();
|
|
187
|
+
out(categories);
|
|
188
|
+
}
|
|
189
|
+
export async function getBudgetVersions(budgetId) {
|
|
190
|
+
const versions = await api.budgetVersion.getBudgetVersions.query({
|
|
191
|
+
budgetId,
|
|
192
|
+
});
|
|
193
|
+
out(versions);
|
|
194
|
+
}
|
|
195
|
+
export async function createBudgetApproval(budgetId) {
|
|
196
|
+
const result = await api.budget.createBudgetApproval.mutate({ budgetId });
|
|
197
|
+
out(result);
|
|
198
|
+
}
|
|
199
|
+
export async function getBudgetDetails(budgetId) {
|
|
200
|
+
const detail = await api.budget.getBudgetDetail.query({ id: budgetId });
|
|
201
|
+
out(detail);
|
|
202
|
+
}
|
|
203
|
+
// --- Approvals: budgets / suppliers / bills (MCP parity) ---
|
|
204
|
+
export async function approveBudget(budgetId) {
|
|
205
|
+
const notifications = await api.budget.getNotifications.query();
|
|
206
|
+
const pending = notifications.filter((n) => n.notificationType === "request" &&
|
|
207
|
+
n.status === ExtendedApprovalStatus.PENDING_APPROVAL &&
|
|
208
|
+
n.type === ExtendedApprovalType.BUDGET &&
|
|
209
|
+
(n.budgetId === budgetId ||
|
|
210
|
+
n.budget?.id === budgetId));
|
|
211
|
+
const approval = pending[0];
|
|
212
|
+
if (!approval) {
|
|
213
|
+
throw new Error("No pending budget approval found for this budget and approver.");
|
|
214
|
+
}
|
|
215
|
+
await api.budget.updateBudgetApproval.mutate({
|
|
216
|
+
budgetApprovalId: approval.id,
|
|
217
|
+
status: ExtendedApprovalStatus.APPROVED,
|
|
218
|
+
});
|
|
219
|
+
out({ success: true, message: "Budget approved successfully" });
|
|
220
|
+
}
|
|
221
|
+
export async function rejectBudget(budgetId, reason) {
|
|
222
|
+
const notifications = await api.budget.getNotifications.query();
|
|
223
|
+
const pending = notifications.filter((n) => n.notificationType === "request" &&
|
|
224
|
+
n.status === ExtendedApprovalStatus.PENDING_APPROVAL &&
|
|
225
|
+
n.type === ExtendedApprovalType.BUDGET &&
|
|
226
|
+
(n.budgetId === budgetId ||
|
|
227
|
+
n.budget?.id === budgetId));
|
|
228
|
+
const approval = pending[0];
|
|
229
|
+
if (!approval) {
|
|
230
|
+
throw new Error("No pending budget approval found for this budget and approver.");
|
|
231
|
+
}
|
|
232
|
+
await api.budget.updateBudgetApproval.mutate({
|
|
233
|
+
budgetApprovalId: approval.id,
|
|
234
|
+
status: ExtendedApprovalStatus.REJECTED,
|
|
235
|
+
rejectionReason: reason,
|
|
236
|
+
});
|
|
237
|
+
out({ success: true, message: "Budget rejected" });
|
|
238
|
+
}
|
|
239
|
+
export async function getSupplierDetails(supplierId) {
|
|
240
|
+
const supplier = await api.supplier.getSupplierById.query({
|
|
241
|
+
id: supplierId,
|
|
242
|
+
});
|
|
243
|
+
out(supplier);
|
|
244
|
+
}
|
|
245
|
+
export async function approveSupplier(supplierId) {
|
|
246
|
+
const notifications = await api.budget.getNotifications.query();
|
|
247
|
+
const pending = notifications.filter((n) => n.notificationType === "request" &&
|
|
248
|
+
n.status === ExtendedApprovalStatus.PENDING_APPROVAL &&
|
|
249
|
+
n.type === ExtendedApprovalType.SUPPLIER &&
|
|
250
|
+
(n.supplierId === supplierId || n.supplier?.id === supplierId));
|
|
251
|
+
const approval = pending[0];
|
|
252
|
+
if (!approval) {
|
|
253
|
+
throw new Error("No pending supplier approval found.");
|
|
254
|
+
}
|
|
255
|
+
await api.supplier.updateSupplierApproval.mutate({
|
|
256
|
+
supplierApprovalId: approval.id,
|
|
257
|
+
status: ExtendedApprovalStatus.APPROVED,
|
|
258
|
+
});
|
|
259
|
+
out({ success: true, message: "Supplier approved successfully" });
|
|
260
|
+
}
|
|
261
|
+
export async function rejectSupplier(supplierId, reason) {
|
|
262
|
+
const notifications = await api.budget.getNotifications.query();
|
|
263
|
+
const pending = notifications.filter((n) => n.notificationType === "request" &&
|
|
264
|
+
n.status === ExtendedApprovalStatus.PENDING_APPROVAL &&
|
|
265
|
+
n.type === ExtendedApprovalType.SUPPLIER &&
|
|
266
|
+
(n.supplierId === supplierId || n.supplier?.id === supplierId));
|
|
267
|
+
const approval = pending[0];
|
|
268
|
+
if (!approval) {
|
|
269
|
+
throw new Error("No pending supplier approval found.");
|
|
270
|
+
}
|
|
271
|
+
await api.supplier.updateSupplierApproval.mutate({
|
|
272
|
+
supplierApprovalId: approval.id,
|
|
273
|
+
status: ExtendedApprovalStatus.REJECTED,
|
|
274
|
+
rejectionReason: reason,
|
|
275
|
+
});
|
|
276
|
+
out({ success: true, message: "Supplier rejected" });
|
|
277
|
+
}
|
|
278
|
+
export async function rejectBill(billId, reason) {
|
|
279
|
+
const notifications = await api.budget.getNotifications.query();
|
|
280
|
+
const pending = notifications.filter((n) => n.notificationType === "request" &&
|
|
281
|
+
n.status === ExtendedApprovalStatus.PENDING_APPROVAL &&
|
|
282
|
+
n.type === ExtendedApprovalType.BILL &&
|
|
283
|
+
(n.billId === billId || n.bill?.id === billId));
|
|
284
|
+
const approval = pending[0];
|
|
285
|
+
if (!approval) {
|
|
286
|
+
throw new Error("No pending bill approval found.");
|
|
287
|
+
}
|
|
288
|
+
await api.bill.updateApproval.mutate({
|
|
289
|
+
billApprovalId: approval.id,
|
|
290
|
+
status: ExtendedApprovalStatus.REJECTED,
|
|
291
|
+
rejectionReason: reason,
|
|
292
|
+
});
|
|
293
|
+
out({ success: true, message: "Bill rejected" });
|
|
294
|
+
}
|
|
295
|
+
// --- Companies & projects ---
|
|
296
|
+
export async function listCompanies(opts) {
|
|
297
|
+
const result = await api.company.getCompanies.query({
|
|
298
|
+
name: opts.name,
|
|
299
|
+
createdBy: opts.createdBy,
|
|
300
|
+
page: opts.page ?? 1,
|
|
301
|
+
perPage: opts.perPage ?? 20,
|
|
302
|
+
});
|
|
303
|
+
out(result);
|
|
304
|
+
}
|
|
305
|
+
export async function getCompany(id) {
|
|
306
|
+
const company = await api.company.getCompanyById.query({ id });
|
|
307
|
+
out(company);
|
|
308
|
+
}
|
|
309
|
+
export async function createCompany(opts) {
|
|
310
|
+
const result = await api.company.createCompany.mutate(opts);
|
|
311
|
+
out(result);
|
|
312
|
+
}
|
|
313
|
+
export async function listProjects(opts) {
|
|
314
|
+
const result = await api.project.getAllProjects.query({
|
|
315
|
+
companyId: opts.companyId,
|
|
316
|
+
name: opts.name,
|
|
317
|
+
status: opts.status,
|
|
318
|
+
page: opts.page ?? 1,
|
|
319
|
+
perPage: opts.perPage ?? 20,
|
|
320
|
+
});
|
|
321
|
+
out(result);
|
|
322
|
+
}
|
|
323
|
+
export async function getProject(id) {
|
|
324
|
+
const [project, budgetsOverview, billsOverview] = await Promise.all([
|
|
325
|
+
api.project.getProjectDetailsById.query({ id }),
|
|
326
|
+
api.project.getProjectBudgetsOverview.query({ projectId: id }),
|
|
327
|
+
api.project.getProjectBillsOverview.query({ projectId: id }),
|
|
328
|
+
]);
|
|
329
|
+
out({ project, budgetsOverview, billsOverview });
|
|
330
|
+
}
|
|
331
|
+
export async function createProject(opts) {
|
|
332
|
+
const result = await api.project.createProject.mutate({
|
|
333
|
+
name: opts.name,
|
|
334
|
+
description: opts.description,
|
|
335
|
+
companyId: opts.companyId,
|
|
336
|
+
contactPersonId: opts.contactPersonId,
|
|
337
|
+
dateRange: {
|
|
338
|
+
from: new Date(opts.startDate),
|
|
339
|
+
to: opts.endDate ? new Date(opts.endDate) : undefined,
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
out(result);
|
|
343
|
+
}
|
|
344
|
+
export async function updateProjectStatus(id, status) {
|
|
345
|
+
const result = await api.project.updateProjectStatus.mutate({ id, status });
|
|
346
|
+
out(result);
|
|
347
|
+
}
|
|
348
|
+
// --- Contacts ---
|
|
349
|
+
export async function listContacts(companyId) {
|
|
350
|
+
const contacts = await api.contactPerson.getContactPersonByCompanyId.query({
|
|
351
|
+
companyId,
|
|
352
|
+
});
|
|
353
|
+
out(contacts);
|
|
354
|
+
}
|
|
355
|
+
// --- Items ---
|
|
356
|
+
export async function listItems(opts) {
|
|
357
|
+
const result = await api.item.getItems.query({
|
|
358
|
+
page: opts.page ?? 1,
|
|
359
|
+
perPage: opts.perPage ?? 20,
|
|
360
|
+
name: opts.name,
|
|
361
|
+
});
|
|
362
|
+
out(result);
|
|
363
|
+
}
|
|
364
|
+
export async function getItem(id) {
|
|
365
|
+
const item = await api.item.getItem.query({ id });
|
|
366
|
+
out(item);
|
|
367
|
+
}
|
|
368
|
+
export async function listItemCategories(opts) {
|
|
369
|
+
const result = await api.itemCategory.getItemCategories.query({
|
|
370
|
+
page: opts.page ?? 1,
|
|
371
|
+
perPage: opts.perPage ?? 50,
|
|
372
|
+
});
|
|
373
|
+
out(result);
|
|
374
|
+
}
|
|
375
|
+
// --- Supplier analytics ---
|
|
376
|
+
export async function getSupplierAnalytics(opts) {
|
|
377
|
+
const result = await api.dashboard.getSupplierTableData.query({
|
|
378
|
+
page: opts.page ?? 1,
|
|
379
|
+
perPage: opts.perPage ?? 20,
|
|
380
|
+
sort: [],
|
|
381
|
+
name: opts.name,
|
|
382
|
+
timeFrame: (opts.timeFrame ?? "ALL"),
|
|
383
|
+
});
|
|
384
|
+
out(result);
|
|
385
|
+
}
|
|
386
|
+
// --- Dashboard / analytics ---
|
|
387
|
+
export async function listUsers() {
|
|
388
|
+
const users = await api.user.getAllUsers.query();
|
|
389
|
+
out(users);
|
|
390
|
+
}
|
|
391
|
+
export async function getUserPerformance(userId) {
|
|
392
|
+
const result = await api.dashboard.getUserPerformance.query({ userId });
|
|
393
|
+
out(result);
|
|
394
|
+
}
|
|
395
|
+
export async function getDashboard(input) {
|
|
396
|
+
const result = await api.dashboard.getDashboard.query({
|
|
397
|
+
userId: input.userId,
|
|
398
|
+
role: input.role ?? "ALL",
|
|
399
|
+
deals: input.deals ?? "ALL",
|
|
400
|
+
timeFrame: input.timeFrame ?? "ALL",
|
|
401
|
+
startDate: input.startDate,
|
|
402
|
+
endDate: input.endDate,
|
|
403
|
+
});
|
|
404
|
+
out(result);
|
|
405
|
+
}
|
|
406
|
+
export async function getFinancialOverview(input) {
|
|
407
|
+
const result = await api.dashboard.getFinancialOverview.query({
|
|
408
|
+
userId: input.userId,
|
|
409
|
+
role: input.role ?? BudgetRole.ALL,
|
|
410
|
+
deals: input.deals ?? Deals.ALL,
|
|
411
|
+
timeFrame: input.timeFrame ?? TimeFrame.ALL,
|
|
412
|
+
startDate: input.startDate,
|
|
413
|
+
endDate: input.endDate,
|
|
414
|
+
});
|
|
415
|
+
out(result);
|
|
416
|
+
}
|
|
417
|
+
// --- Error logs ---
|
|
418
|
+
export async function getRecentErrors(opts) {
|
|
419
|
+
const result = await api.errorLog.getRecentErrors.query({
|
|
420
|
+
page: opts.page ?? 1,
|
|
421
|
+
perPage: opts.perPage ?? 10,
|
|
422
|
+
severity: opts.severity,
|
|
423
|
+
status: opts.status,
|
|
424
|
+
});
|
|
425
|
+
out(result);
|
|
426
|
+
}
|
|
427
|
+
export async function getErrorMetrics() {
|
|
428
|
+
const result = await api.errorLog.getErrorMetrics.query({});
|
|
429
|
+
out(result);
|
|
430
|
+
}
|
|
431
|
+
// --- Historical / benchmarks ---
|
|
432
|
+
export async function getApprovedBudgets(opts) {
|
|
433
|
+
const perPage = Math.min(opts.limit ?? 50, 100);
|
|
434
|
+
const { budgets, totalCount } = await api.budget.getAllBudgets.query({
|
|
435
|
+
projectId: opts.projectId,
|
|
436
|
+
statuses: [
|
|
437
|
+
ExtendedBudgetStatus.APPROVED,
|
|
438
|
+
ExtendedBudgetStatus.ESTIMATE_ACCEPTED,
|
|
439
|
+
ExtendedBudgetStatus.ESTIMATE_CLOSED,
|
|
440
|
+
],
|
|
441
|
+
page: 1,
|
|
442
|
+
perPage,
|
|
443
|
+
});
|
|
444
|
+
const items = (budgets ?? []).map((b) => {
|
|
445
|
+
const calc = b;
|
|
446
|
+
const revenue = calc.totalSellingAfterDiscount ?? 0;
|
|
447
|
+
const cost = calc.totalCostWithoutGst ?? 0;
|
|
448
|
+
const gp = calc.gpPercentage ?? 0;
|
|
449
|
+
const date = b.date instanceof Date ? b.date.toISOString() : String(b.date);
|
|
450
|
+
const createdAt = b.createdAt instanceof Date
|
|
451
|
+
? b.createdAt.toISOString()
|
|
452
|
+
: String(b.createdAt);
|
|
453
|
+
return {
|
|
454
|
+
id: b.id,
|
|
455
|
+
name: b.name,
|
|
456
|
+
categoryId: b.categoryId,
|
|
457
|
+
company: b.project?.company?.name,
|
|
458
|
+
date,
|
|
459
|
+
pax: b.pax,
|
|
460
|
+
revenue: Number(revenue.toFixed(2)),
|
|
461
|
+
cost: Number(cost.toFixed(2)),
|
|
462
|
+
gpPercentage: Number(gp.toFixed(2)),
|
|
463
|
+
createdAt,
|
|
464
|
+
};
|
|
465
|
+
});
|
|
466
|
+
let filtered = items;
|
|
467
|
+
if (opts.minRevenue != null) {
|
|
468
|
+
const minR = opts.minRevenue;
|
|
469
|
+
filtered = filtered.filter((i) => i.revenue >= minR);
|
|
470
|
+
}
|
|
471
|
+
if (opts.maxRevenue != null) {
|
|
472
|
+
const maxR = opts.maxRevenue;
|
|
473
|
+
filtered = filtered.filter((i) => i.revenue <= maxR);
|
|
474
|
+
}
|
|
475
|
+
out({ budgets: filtered, count: filtered.length, totalCount });
|
|
476
|
+
}
|
|
477
|
+
export async function getBudgetCategoryBenchmarks(opts) {
|
|
478
|
+
const result = await api.budget.getBudgetCategoryBenchmarks.query({
|
|
479
|
+
categoryId: opts.categoryId,
|
|
480
|
+
limit: opts.limit ?? 100,
|
|
481
|
+
});
|
|
482
|
+
out(result);
|
|
483
|
+
}
|
|
484
|
+
export async function getItemPricingHistory(opts) {
|
|
485
|
+
const result = await api.budget.getItemPricingHistory.query({
|
|
486
|
+
itemName: opts.itemName,
|
|
487
|
+
categoryId: opts.categoryId,
|
|
488
|
+
supplierId: opts.supplierId,
|
|
489
|
+
limit: opts.limit ?? 100,
|
|
490
|
+
});
|
|
491
|
+
out(result);
|
|
492
|
+
}
|
|
493
|
+
export async function getSupplierPricingHistory(opts) {
|
|
494
|
+
const result = await api.budget.getSupplierPricingHistory.query({
|
|
495
|
+
supplierId: opts.supplierId,
|
|
496
|
+
itemCategoryId: opts.itemCategoryId,
|
|
497
|
+
limit: opts.limit ?? 100,
|
|
498
|
+
});
|
|
499
|
+
out(result);
|
|
500
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ApprovalStatus, ApprovalType, BillStatus, BudgetStatus, ErrorSeverity, ErrorStatus, EstimationMode, ProjectStatus, } from "@bb/db/enums";
|
|
2
|
+
export const ExtendedApprovalStatus = {
|
|
3
|
+
...ApprovalStatus,
|
|
4
|
+
ALL: "ALL",
|
|
5
|
+
};
|
|
6
|
+
export const ExtendedApprovalType = {
|
|
7
|
+
...ApprovalType,
|
|
8
|
+
ALL: "ALL",
|
|
9
|
+
};
|
|
10
|
+
export const ExtendedProjectStatus = {
|
|
11
|
+
...ProjectStatus,
|
|
12
|
+
ALL: "ALL",
|
|
13
|
+
};
|
|
14
|
+
export const ExtendedBudgetStatus = {
|
|
15
|
+
...BudgetStatus,
|
|
16
|
+
ALL: "ALL",
|
|
17
|
+
};
|
|
18
|
+
export const ExtendedBillStatus = {
|
|
19
|
+
...BillStatus,
|
|
20
|
+
ALL: "ALL",
|
|
21
|
+
};
|
|
22
|
+
export const ExtendedEstimationMode = {
|
|
23
|
+
...EstimationMode,
|
|
24
|
+
ALL: "ALL",
|
|
25
|
+
};
|
|
26
|
+
export const BudgetRole = {
|
|
27
|
+
ALL: "ALL",
|
|
28
|
+
BD: "BD",
|
|
29
|
+
CREATOR: "CREATOR",
|
|
30
|
+
INSIDE_SALES: "INSIDE_SALES",
|
|
31
|
+
};
|
|
32
|
+
export const Deals = {
|
|
33
|
+
ALL: "ALL",
|
|
34
|
+
SUCCESSFUL: "SUCCESSFUL",
|
|
35
|
+
LOST: "LOST",
|
|
36
|
+
};
|
|
37
|
+
export const TimeFrame = {
|
|
38
|
+
ALL: "ALL",
|
|
39
|
+
LAST_YEAR: "LAST_YEAR",
|
|
40
|
+
LAST_6_MONTHS: "LAST_6_MONTHS",
|
|
41
|
+
LAST_3_MONTHS: "LAST_3_MONTHS",
|
|
42
|
+
CUSTOM: "CUSTOM",
|
|
43
|
+
};
|
|
44
|
+
export const ChartTimeFrame = {
|
|
45
|
+
LAST_30_DAYS: "last_30_days",
|
|
46
|
+
LAST_3_MONTHS: "last_3_months",
|
|
47
|
+
LAST_6_MONTHS: "last_6_months",
|
|
48
|
+
LAST_1_YEAR: "last_1_year",
|
|
49
|
+
ALL_TIME: "all_time",
|
|
50
|
+
};
|
|
51
|
+
export const ExtendedErrorSeverity = {
|
|
52
|
+
ALL: "ALL",
|
|
53
|
+
...ErrorSeverity,
|
|
54
|
+
};
|
|
55
|
+
export const ExtendedErrorStatus = {
|
|
56
|
+
ALL: "ALL",
|
|
57
|
+
...ErrorStatus,
|
|
58
|
+
};
|