@go-labs-sg/bb 2.36.0 → 2.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -19625,1787 +19625,77 @@ var api2 = new Proxy({}, {
19625
19625
  get: (_target, property, receiver) => Reflect.get(activeClient, property, receiver)
19626
19626
  });
19627
19627
 
19628
- // ../validators/src/attachment/attachment.ts
19629
- var attachmentInputSchema = exports_external.object({
19630
- id: exports_external.string(),
19631
- attachments: exports_external.array(exports_external.object({
19632
- id: exports_external.string(),
19633
- name: exports_external.string().nullable(),
19634
- key: exports_external.string(),
19635
- size: exports_external.number()
19636
- })).optional()
19637
- });
19638
- // ../db/src/generated/prisma/enums.ts
19639
- var InboundLeadCloseOutcome = {
19640
- LOST: "LOST",
19641
- DISQUALIFIED: "DISQUALIFIED"
19642
- };
19643
- var PayNowIdentifierType = {
19644
- MOBILE_NUMBER: "MOBILE_NUMBER",
19645
- UEN: "UEN"
19646
- };
19647
- var EstimationMode = {
19648
- MICRO: "MICRO",
19649
- MANPOWER_SPECIFIED: "MANPOWER_SPECIFIED"
19650
- };
19651
- var IntegrationCredentialType = {
19652
- GOOGLE_CALENDAR_OAUTH2: "GOOGLE_CALENDAR_OAUTH2",
19653
- GOOGLE_SERVICE_ACCOUNT: "GOOGLE_SERVICE_ACCOUNT",
19654
- ASANA_ACCESS_TOKEN: "ASANA_ACCESS_TOKEN",
19655
- ASANA_OAUTH2: "ASANA_OAUTH2",
19656
- SLACK_APP: "SLACK_APP",
19657
- TYPEFORM_WEBHOOK: "TYPEFORM_WEBHOOK",
19658
- RESEND_API_KEY: "RESEND_API_KEY",
19659
- OPENAI_API_KEY: "OPENAI_API_KEY",
19660
- S3_ACCESS_KEY: "S3_ACCESS_KEY",
19661
- QUICKBOOKS_OAUTH2: "QUICKBOOKS_OAUTH2"
19662
- };
19663
- var IntegrationCapability = {
19664
- GOOGLE_CALENDAR_EVENTS: "GOOGLE_CALENDAR_EVENTS",
19665
- GOOGLE_DRIVE: "GOOGLE_DRIVE",
19666
- ASANA: "ASANA",
19667
- SLACK_WEB_API: "SLACK_WEB_API",
19668
- SLACK_EVENT_VERIFICATION: "SLACK_EVENT_VERIFICATION",
19669
- TYPEFORM_WEBHOOK_VERIFICATION: "TYPEFORM_WEBHOOK_VERIFICATION",
19670
- RESEND_EMAIL: "RESEND_EMAIL",
19671
- OPENAI: "OPENAI",
19672
- S3_OBJECT_STORAGE: "S3_OBJECT_STORAGE",
19673
- QUICKBOOKS_API: "QUICKBOOKS_API"
19674
- };
19675
- var IntegrationCredentialScope = {
19676
- GLOBAL: "GLOBAL",
19677
- INBOUND_SOURCE: "INBOUND_SOURCE"
19678
- };
19679
- var ProjectStatus = {
19680
- PITCH: "PITCH",
19681
- WON: "WON",
19682
- COMPLETED: "COMPLETED",
19683
- RECONCILED: "RECONCILED",
19684
- LOST: "LOST"
19685
- };
19686
- var BudgetStatus = {
19687
- DRAFT: "DRAFT",
19688
- PENDING_APPROVAL: "PENDING_APPROVAL",
19689
- APPROVED: "APPROVED",
19690
- REJECTED: "REJECTED",
19691
- ESTIMATE_CREATED: "ESTIMATE_CREATED",
19692
- ESTIMATE_SENT: "ESTIMATE_SENT",
19693
- ESTIMATE_ACCEPTED: "ESTIMATE_ACCEPTED",
19694
- ESTIMATE_REJECTED: "ESTIMATE_REJECTED",
19695
- ESTIMATE_CLOSED: "ESTIMATE_CLOSED"
19696
- };
19697
- var ApprovalStatus = {
19698
- PENDING_APPROVAL: "PENDING_APPROVAL",
19699
- APPROVED: "APPROVED",
19700
- REJECTED: "REJECTED",
19701
- SUPERSEDED: "SUPERSEDED"
19702
- };
19703
- var ApprovalType = {
19704
- BUDGET: "BUDGET",
19705
- SUPPLIER: "SUPPLIER",
19706
- BILL: "BILL",
19707
- CUSTOMER_INVOICE: "CUSTOMER_INVOICE",
19708
- QUOTATION: "QUOTATION"
19709
- };
19710
- var Title = {
19711
- MR: "MR",
19712
- MRS: "MRS",
19713
- MS: "MS",
19714
- DR: "DR"
19715
- };
19716
- var ErrorSeverity = {
19717
- LOW: "LOW",
19718
- MEDIUM: "MEDIUM",
19719
- HIGH: "HIGH",
19720
- CRITICAL: "CRITICAL"
19721
- };
19722
- var ErrorStatus = {
19723
- UNRESOLVED: "UNRESOLVED",
19724
- INVESTIGATING: "INVESTIGATING",
19725
- RESOLVED: "RESOLVED",
19726
- IGNORED: "IGNORED"
19727
- };
19728
- var CustomerInvoiceBatchStatus = {
19729
- CREATING: "CREATING",
19730
- PENDING_APPROVAL: "PENDING_APPROVAL",
19731
- APPROVED: "APPROVED",
19732
- REJECTED_VOIDED: "REJECTED_VOIDED",
19733
- EXPIRED_VOIDED: "EXPIRED_VOIDED",
19734
- PARTIAL_QBO_FAILURE: "PARTIAL_QBO_FAILURE"
19735
- };
19736
- var BillStatus = {
19737
- DRAFT: "DRAFT",
19738
- PENDING_APPROVAL: "PENDING_APPROVAL",
19739
- CHECKED: "CHECKED",
19740
- APPROVED: "APPROVED",
19741
- PAID: "PAID",
19742
- REJECTED: "REJECTED"
19743
- };
19744
- var BillVerificationStatus = {
19745
- PENDING: "PENDING",
19746
- PASS: "PASS",
19747
- FAIL: "FAIL",
19748
- MANUAL_REVIEW: "MANUAL_REVIEW"
19749
- };
19750
-
19751
- // ../validators/src/bill/invoice-number.ts
19752
- var BILL_INVOICE_NUMBER_MAX_LENGTH = 21;
19753
- var BILL_INVOICE_NUMBER_PATTERN = /^[A-Za-z0-9]*$/;
19754
- var billInvoiceNumberFieldSchema = exports_external.string().max(BILL_INVOICE_NUMBER_MAX_LENGTH, {
19755
- error: "Invoice number must be 21 characters or fewer (QuickBooks limit)"
19756
- }).refine((val) => BILL_INVOICE_NUMBER_PATTERN.test(val), {
19757
- error: "Invoice number may only contain English letters and numbers (A-Z, a-z, 0-9)"
19758
- });
19759
- var patchBillInvoiceNumberValueSchema = billInvoiceNumberFieldSchema.min(1, {
19760
- error: "Invoice number is required"
19761
- });
19762
- var patchBillInvoiceNumberInputSchema = exports_external.object({
19763
- id: exports_external.string(),
19764
- invoiceNumber: patchBillInvoiceNumberValueSchema
19765
- });
19766
-
19767
- // ../validators/src/bill/create-bill.ts
19768
- var billAttachmentInputSchema = exports_external.object({
19769
- id: exports_external.string(),
19770
- name: exports_external.string(),
19771
- key: exports_external.string(),
19772
- size: exports_external.number().int().positive().max(50 * 1024 * 1024)
19773
- });
19774
- var billLineAmountInputSchema = exports_external.object({
19775
- budgetItemId: exports_external.string().min(1),
19776
- amount: exports_external.number().finite().nonnegative()
19777
- });
19778
- var billQuotationAllocationInputSchema = exports_external.object({
19779
- budgetItemId: exports_external.string().min(1),
19780
- quotationId: exports_external.string().min(1),
19781
- amount: exports_external.number().finite().nonnegative()
19782
- });
19783
- var createBillInputSchemaBase = exports_external.object({
19784
- budgetId: exports_external.string(),
19785
- projectId: exports_external.string(),
19786
- supplierId: exports_external.string(),
19787
- quotationId: exports_external.string().optional(),
19788
- quotationIds: exports_external.array(exports_external.string().min(1)).optional(),
19789
- quotationAllocations: exports_external.array(billQuotationAllocationInputSchema).optional(),
19790
- overBudgetOverrideReason: exports_external.string().trim().transform((reason) => reason || undefined).pipe(exports_external.string().min(10, "Enter at least 10 characters for the allowance-overrun approval reason, or leave it blank.").max(2000, "The allowance-overrun approval reason must be 2,000 characters or fewer.").optional()).optional(),
19791
- amount: exports_external.number().min(0),
19792
- budgetItemIds: exports_external.array(exports_external.string()).min(1, "At least one line item is required"),
19793
- lineAmounts: exports_external.array(billLineAmountInputSchema).optional(),
19794
- comment: exports_external.string().optional(),
19795
- isClaimable: exports_external.boolean(),
19796
- attachments: exports_external.array(billAttachmentInputSchema),
19797
- alreadyPaid: exports_external.boolean().optional(),
19798
- paymentReference: exports_external.string().trim().optional(),
19799
- paymentProofAttachments: exports_external.array(billAttachmentInputSchema).optional(),
19800
- extractedSupplierName: exports_external.string().optional(),
19801
- extractedAmount: exports_external.number().optional(),
19802
- invoiceNumber: billInvoiceNumberFieldSchema,
19803
- invoiceDate: exports_external.date().optional(),
19804
- verificationStatus: exports_external.enum(BillVerificationStatus).optional(),
19805
- verificationResults: exports_external.object({
19806
- status: exports_external.enum(BillVerificationStatus),
19807
- extractedAmount: exports_external.number().nullable(),
19808
- difference: exports_external.number().nullable(),
19809
- confidence: exports_external.number().nullable(),
19810
- failureReason: exports_external.string().nullable().optional()
19811
- }).optional()
19812
- });
19813
- var validateAlreadyPaidBillInput = (input2, ctx) => {
19814
- if (input2.quotationIds) {
19815
- if (new Set(input2.quotationIds).size !== input2.quotationIds.length) {
19816
- ctx.addIssue({
19817
- code: "custom",
19818
- message: "Select each quotation only once",
19819
- path: ["quotationIds"]
19820
- });
19821
- }
19822
- if (input2.quotationId && (input2.quotationIds.length !== 1 || input2.quotationIds[0] !== input2.quotationId)) {
19823
- ctx.addIssue({
19824
- code: "custom",
19825
- message: "Use quotationIds or a matching single quotationId",
19826
- path: ["quotationIds"]
19827
- });
19828
- }
19829
- }
19830
- if (input2.isClaimable && (input2.quotationId || input2.quotationIds?.length || input2.quotationAllocations?.length)) {
19831
- ctx.addIssue({
19832
- code: "custom",
19833
- message: "Employee claims cannot be linked to supplier quotations",
19834
- path: ["quotationIds"]
19835
- });
19836
- }
19837
- if (!input2.alreadyPaid)
19838
- return;
19839
- if (input2.isClaimable) {
19840
- ctx.addIssue({
19841
- code: "custom",
19842
- message: "Already-paid reporting is only available for supplier bills",
19843
- path: ["alreadyPaid"]
19844
- });
19845
- }
19846
- if (!input2.paymentReference?.trim()) {
19847
- ctx.addIssue({
19848
- code: "custom",
19849
- message: "Payment reference is required for an already-paid bill",
19850
- path: ["paymentReference"]
19851
- });
19852
- }
19853
- if ((input2.paymentProofAttachments?.length ?? 0) === 0) {
19854
- ctx.addIssue({
19855
- code: "custom",
19856
- message: "Payment-proof PDF is required for an already-paid bill",
19857
- path: ["paymentProofAttachments"]
19858
- });
19859
- }
19860
- for (const attachment2 of input2.paymentProofAttachments ?? []) {
19861
- if (!attachment2.name.toLowerCase().endsWith(".pdf")) {
19862
- ctx.addIssue({
19863
- code: "custom",
19864
- message: "Payment proof must be a PDF",
19865
- path: ["paymentProofAttachments"]
19866
- });
19867
- break;
19868
- }
19869
- }
19870
- };
19871
- var createBillInputSchemaWithDraftOnly = createBillInputSchemaBase.extend({
19872
- draftOnly: exports_external.boolean().optional()
19873
- });
19874
- var validateCreateBillInput = (input2, ctx) => {
19875
- validateAlreadyPaidBillInput(input2, ctx);
19876
- if (input2.draftOnly && input2.alreadyPaid) {
19877
- ctx.addIssue({
19878
- code: "custom",
19879
- message: "Draft-only creation is not available for already-paid bills",
19880
- path: ["draftOnly"]
19881
- });
19882
- }
19883
- };
19884
- var createBillInputSchema = createBillInputSchemaWithDraftOnly.superRefine(validateCreateBillInput);
19885
- var createBillDraftInputSchema = createBillInputSchemaWithDraftOnly.extend({
19886
- draftOnly: exports_external.literal(true).default(true),
19887
- alreadyPaid: exports_external.literal(false).optional()
19888
- }).superRefine(validateCreateBillInput);
19889
- // ../validators/src/bill/update-bill.ts
19890
- var updateBillInputSchemaBase = createBillInputSchemaBase.extend({
19891
- id: exports_external.string()
19892
- });
19893
- var updateBillInputSchema = updateBillInputSchemaBase.superRefine(validateAlreadyPaidBillInput);
19894
- // ../validators/src/budget/budget-commission.ts
19895
- var updateBudgetCommissionInputSchema = exports_external.object({
19896
- budgetId: exports_external.string(),
19897
- name: exports_external.string().min(1, { error: "Name is required" }),
19898
- percentage: exports_external.string({ error: "Percentage is required" }).min(1, { error: "Percentage is required" }).refine((val) => {
19899
- const numValue = Number.parseFloat(val);
19900
- return !Number.isNaN(numValue);
19901
- }, {
19902
- error: "Percentage must be a valid number"
19903
- }).refine((val) => {
19904
- const numValue = Number.parseFloat(val);
19905
- return numValue >= 0;
19906
- }, {
19907
- error: "Percentage must be 0 or greater"
19908
- }).refine((val) => {
19909
- const numValue = Number.parseFloat(val);
19910
- return numValue <= 100;
19911
- }, {
19912
- error: "Percentage cannot exceed 100"
19913
- }),
19914
- totalSellingAfterDiscount: exports_external.number()
19915
- });
19916
- // ../validators/src/budget/budget-discount.ts
19917
- var budgetDiscountInputSchema = exports_external.object({
19918
- budgetId: exports_external.string(),
19919
- name: exports_external.string().min(1, { error: "Name is required" }),
19920
- type: exports_external.enum(["percentage", "amount"], { error: "Type is required" }),
19921
- value: exports_external.string({ error: "Value is required" }).min(1, { error: "Value is required" }).refine((val) => {
19922
- const numValue = Number.parseFloat(val);
19923
- return !Number.isNaN(numValue);
19924
- }, {
19925
- error: "Value must be a valid number"
19926
- }).refine((val) => {
19927
- const numValue = Number.parseFloat(val);
19928
- return numValue >= 0;
19929
- }, {
19930
- error: "Value must be 0 or greater"
19931
- }),
19932
- totalSellingBeforeDiscount: exports_external.number()
19933
- }).refine((data) => {
19934
- if (data.type === "percentage") {
19935
- const numValue = Number.parseFloat(data.value);
19936
- return numValue <= 100;
19937
- }
19938
- return true;
19939
- }, {
19940
- error: "Percentage value cannot exceed 100",
19941
- path: ["value"]
19942
- });
19943
- // ../validators/src/budget/budget-event-details.ts
19944
- var budgetEventDetailsSchema = exports_external.object({
19945
- date: exports_external.date({ error: "Please select the actual event date" }),
19946
- startTime: exports_external.date({ error: "Please select the actual event start time" }),
19947
- endTime: exports_external.date({ error: "Please select the actual event end time" })
19948
- });
19949
- // ../validators/src/customer-invoice/project-invoice-settings.ts
19950
- var CUSTOMER_INVOICE_REVENUE_THRESHOLD = 5000;
19951
- var customerInvoiceDeliveryMethodSchema = exports_external.enum([
19952
- "EMAIL",
19953
- "PORTAL_UPLOAD"
19954
- ]);
19955
- var percentageSchema = exports_external.number().min(0).max(100).refine((value) => Math.abs(value - Math.round(value * 100) / 100) < Number.EPSILON * 100, {
19956
- error: "Percentage can have at most two decimal places"
19957
- });
19958
- var nullableTrimmedStringSchema = exports_external.string().trim().nullish().transform((value) => value || null);
19959
- var projectInvoiceSettingsFieldsObject = exports_external.object({
19960
- depositPercentage: percentageSchema,
19961
- balancePercentage: percentageSchema,
19962
- deliveryMethod: customerInvoiceDeliveryMethodSchema,
19963
- billingEmail: nullableTrimmedStringSchema,
19964
- uploadUrl: nullableTrimmedStringSchema
19965
- });
19966
- var refineDeliveryFields = (data, ctx) => {
19967
- if (data.deliveryMethod === "EMAIL") {
19968
- if (!data.billingEmail || !exports_external.email().safeParse(data.billingEmail).success) {
19969
- ctx.addIssue({
19970
- code: "custom",
19971
- message: "A valid billing email is required",
19972
- path: ["billingEmail"]
19973
- });
19974
- }
19975
- return;
19976
- }
19977
- if (!data.uploadUrl || !exports_external.url().safeParse(data.uploadUrl).success) {
19978
- ctx.addIssue({
19979
- code: "custom",
19980
- message: "A valid upload URL is required",
19981
- path: ["uploadUrl"]
19982
- });
19983
- return;
19984
- }
19985
- if (new URL(data.uploadUrl).protocol !== "https:") {
19986
- ctx.addIssue({
19987
- code: "custom",
19988
- message: "Upload URL must use HTTPS",
19989
- path: ["uploadUrl"]
19990
- });
19991
- }
19992
- };
19993
- var projectInvoiceSettingsFieldsSchema = projectInvoiceSettingsFieldsObject.superRefine(refineDeliveryFields).transform((data) => ({
19994
- ...data,
19995
- billingEmail: data.deliveryMethod === "EMAIL" ? data.billingEmail?.toLowerCase() ?? null : null,
19996
- uploadUrl: data.deliveryMethod === "PORTAL_UPLOAD" ? data.uploadUrl : null
19997
- }));
19998
- var projectInvoiceSettingsForRevenueSchema = exports_external.object({
19999
- revenueBasisAmount: exports_external.number().nonnegative(),
20000
- ...projectInvoiceSettingsFieldsObject.shape
20001
- }).superRefine((data, ctx) => {
20002
- refineDeliveryFields(data, ctx);
20003
- const total = Math.round((data.depositPercentage + data.balancePercentage) * 100);
20004
- if (total !== 1e4) {
20005
- ctx.addIssue({
20006
- code: "custom",
20007
- message: "Deposit and balance percentages must total 100%",
20008
- path: ["balancePercentage"]
20009
- });
20010
- }
20011
- if (data.revenueBasisAmount <= CUSTOMER_INVOICE_REVENUE_THRESHOLD) {
20012
- if (data.depositPercentage !== 100 || data.balancePercentage !== 0) {
20013
- ctx.addIssue({
20014
- code: "custom",
20015
- message: "Projects with revenue of S$5,000 or below require 100% upfront payment",
20016
- path: ["depositPercentage"]
20017
- });
20018
- }
20019
- return;
20020
- }
20021
- const usesUpfrontPayment = data.depositPercentage === 100 && data.balancePercentage === 0;
20022
- const usesSplitPayment = data.depositPercentage > 0 && data.balancePercentage > 0;
20023
- if (!usesUpfrontPayment && !usesSplitPayment) {
20024
- ctx.addIssue({
20025
- code: "custom",
20026
- message: "Choose 100% upfront, or set both deposit and balance above 0%",
20027
- path: ["depositPercentage"]
20028
- });
20029
- }
20030
- }).transform((data) => ({
20031
- ...data,
20032
- billingEmail: data.deliveryMethod === "EMAIL" ? data.billingEmail?.toLowerCase() ?? null : null,
20033
- uploadUrl: data.deliveryMethod === "PORTAL_UPLOAD" ? data.uploadUrl : null
20034
- }));
20035
- var getProjectInvoiceSettingsInputSchema = exports_external.object({
20036
- projectId: exports_external.string().min(1)
20037
- });
20038
- var updateProjectInvoiceSettingsInputSchema = exports_external.object({
20039
- projectId: exports_external.string().min(1),
20040
- settings: projectInvoiceSettingsFieldsSchema
20041
- });
20042
- var markCustomerInvoiceUploadedInputSchema = exports_external.object({
20043
- invoiceId: exports_external.string().min(1),
20044
- expectedUploadUrl: exports_external.url()
20045
- });
20046
-
20047
- // ../validators/src/budget/budget-status.ts
20048
- var budgetApprovalRejectionReasonSchema = exports_external.string().trim().min(2, { error: "Reason for rejection is required" });
20049
- var budgetApprovalDecisionBaseSchema = exports_external.object({
20050
- budgetApprovalId: exports_external.string()
20051
- });
20052
- var updateBudgetApprovalInputSchema = exports_external.discriminatedUnion("status", [
20053
- budgetApprovalDecisionBaseSchema.extend({
20054
- status: exports_external.literal(ApprovalStatus.APPROVED),
20055
- rejectionReason: exports_external.string().optional()
20056
- }),
20057
- budgetApprovalDecisionBaseSchema.extend({
20058
- status: exports_external.literal(ApprovalStatus.REJECTED),
20059
- rejectionReason: budgetApprovalRejectionReasonSchema
20060
- })
20061
- ]);
20062
- var budgetStatusInputSchema = exports_external.object({
20063
- id: exports_external.string(),
20064
- status: exports_external.enum(BudgetStatus).optional(),
20065
- projectManagerId: exports_external.string().optional(),
20066
- markProjectWon: exports_external.boolean().optional(),
20067
- projectStatusOnCommercialRejection: exports_external.enum([ProjectStatus.PITCH, ProjectStatus.LOST]).optional(),
20068
- projectInvoiceSettings: projectInvoiceSettingsFieldsSchema.optional()
20069
- });
20070
- // ../validators/src/budget/create-budget.ts
20071
- var createBudgetInputSchema = exports_external.object({
20072
- name: exports_external.string().min(1, { error: "Name is required" }),
20073
- budget: exports_external.string({ error: "Budget is required" }).min(1, { error: "Budget is required" }).refine((val) => {
20074
- const numValue = Number.parseFloat(val);
20075
- return !Number.isNaN(numValue);
20076
- }, {
20077
- error: "Budget must be a valid number"
20078
- }).refine((val) => {
20079
- const numValue = Number.parseFloat(val);
20080
- return numValue >= 0;
20081
- }, {
20082
- error: "Budget must be 0 or greater"
20083
- }),
20084
- paymentTerm: exports_external.string().min(1, { error: "Payment term is required" }),
20085
- sourceBudgetId: exports_external.string().optional(),
20086
- unavailableItemReviewAcknowledged: exports_external.boolean().optional().default(false),
20087
- categoryId: exports_external.string().min(1, { error: "Please select a category." }),
20088
- projectId: exports_external.string().min(1, { error: "Please select a project." }),
20089
- pipedriveDealId: exports_external.string().optional()
20090
- });
20091
- // ../validators/src/budget/import-budget.ts
20092
- var importBudgetInputSchema = exports_external.object({
20093
- name: exports_external.string().min(2, { error: "Name is required" }),
20094
- date: exports_external.date({ error: "Please select the actual event date" }),
20095
- startTime: exports_external.date({ error: "Please select the actual event start time" }),
20096
- endTime: exports_external.date({ error: "Please select the actual event end time" }),
20097
- pax: exports_external.string({ error: "Pax is required" }).min(1, { error: "Pax is required" }).refine((val) => {
20098
- const numValue = Number.parseInt(val, 10);
20099
- return !Number.isNaN(numValue) && Number.isInteger(numValue);
20100
- }, {
20101
- error: "Pax must be a valid whole number"
20102
- }).refine((val) => {
20103
- const numValue = Number.parseInt(val, 10);
20104
- return numValue >= 1;
20105
- }, {
20106
- error: "Minimum 1 guest required"
20107
- }),
20108
- venue: exports_external.string().min(1, { error: "Venue is required" }),
20109
- budget: exports_external.string({ error: "Budget is required" }).min(1, { error: "Budget is required" }).refine((val) => {
20110
- const numValue = Number.parseFloat(val);
20111
- return !Number.isNaN(numValue);
20112
- }, {
20113
- error: "Budget must be a valid number"
20114
- }).refine((val) => {
20115
- const numValue = Number.parseFloat(val);
20116
- return numValue >= 0;
20117
- }, {
20118
- error: "Budget must be 0 or greater"
20119
- }),
20120
- categoryId: exports_external.string().min(1, { error: "Please select a category." }),
20121
- items: exports_external.array(exports_external.object({
20122
- id: exports_external.string(),
20123
- categoryName: exports_external.string().min(2, { error: "Category name is required" }),
20124
- name: exports_external.string(),
20125
- description: exports_external.string().optional(),
20126
- descriptionRichText: exports_external.custom().optional().nullable(),
20127
- quantity: exports_external.number(),
20128
- cost: exports_external.number(),
20129
- markup: exports_external.number(),
20130
- unitPrice: exports_external.number(),
20131
- isFreeOfCharge: exports_external.boolean(),
20132
- supplier: exports_external.object({
20133
- name: exports_external.string(),
20134
- gstRegistered: exports_external.boolean(),
20135
- email: exports_external.string().nullable(),
20136
- contactPerson: exports_external.string().nullable(),
20137
- phone: exports_external.string().nullable(),
20138
- address: exports_external.string().nullable()
20139
- })
20140
- })),
20141
- projectId: exports_external.string().min(1, { error: "Please select a project." })
20142
- });
20143
- // ../validators/src/budget/update-budget.ts
20144
- var updateBudgetInputSchema = createBudgetInputSchema.omit({ unavailableItemReviewAcknowledged: true }).extend({
20145
- id: exports_external.string().min(1, { error: "ID is required" })
20146
- });
20147
- // ../validators/src/company/company-discovery.ts
20148
- var searchExistingCompaniesInputSchema = exports_external.object({
20149
- query: exports_external.string().trim().min(2).max(200),
20150
- page: exports_external.number().int().min(1).max(5000).default(1)
20151
- });
20152
- var useExistingCompanyInputSchema = exports_external.discriminatedUnion("source", [
20153
- exports_external.object({
20154
- source: exports_external.literal("bb"),
20155
- companyId: exports_external.string().min(1),
20156
- quickbooksId: exports_external.string().regex(/^\d+$/).optional(),
20157
- confirmDistinct: exports_external.boolean().optional()
20158
- }),
20159
- exports_external.object({
20160
- source: exports_external.literal("qbo"),
20161
- quickbooksId: exports_external.string().regex(/^\d+$/),
20162
- website: exports_external.url().optional(),
20163
- confirmDistinct: exports_external.boolean().optional()
20164
- })
20165
- ]);
20166
- // ../validators/src/quickbooks/qbo-name.ts
20167
- var QUICKBOOKS_NAME_VALIDATION_MESSAGE = "Name can only contain letters, numbers, spaces, and these QuickBooks-supported characters: , . ? @ & ! # ' ~ * _ - ; +";
20168
- var quickBooksNameSchema = exports_external.string().trim().min(1, { error: "Name is required" }).max(100, { error: "Name cannot be longer than 100 characters" }).regex(/^[A-Za-z0-9,.?@&!#'~* _;+-]+$/, {
20169
- error: QUICKBOOKS_NAME_VALIDATION_MESSAGE
20170
- });
20171
-
20172
- // ../validators/src/company/create-company.ts
20173
- var createCompanyInputSchema = exports_external.object({
20174
- name: quickBooksNameSchema.min(2, {
20175
- error: "Name must be at least 2 characters"
20176
- }),
20177
- website: exports_external.url({ error: "Please enter a valid URL" }),
20178
- confirmDistinct: exports_external.boolean().optional()
20179
- });
20180
- // ../validators/src/company/update-company.ts
20181
- var updateCompanyInputSchema = createCompanyInputSchema.extend({
20182
- id: exports_external.string().min(1, { error: "ID is required" })
20183
- });
20184
- // ../validators/src/contact-person/create-contact-person.ts
20185
- var createContactPersonInputSchema = exports_external.object({
20186
- companyId: exports_external.string().min(1, { error: "Company is required" }),
20187
- title: exports_external.enum(Title),
20188
- firstName: exports_external.string().min(1, { error: "First name is required" }),
20189
- lastName: exports_external.string().trim().transform((lastName) => lastName || null).nullable(),
20190
- email: exports_external.email({ error: "Invalid email address" }),
20191
- mobile: exports_external.string().min(8, { error: "Mobile number must be at least 8 characters" }).regex(/^\+?[0-9\s\-()]+$/, {
20192
- error: "Please enter a valid phone number format"
20193
- }),
20194
- address: exports_external.object({
20195
- id: exports_external.string().optional(),
20196
- streetAddress1: exports_external.string().optional(),
20197
- city: exports_external.string().optional(),
20198
- state: exports_external.string().optional(),
20199
- postalCode: exports_external.string().optional(),
20200
- country: exports_external.string().optional()
20201
- }).optional()
20202
- });
20203
- // ../validators/src/contact-person/update-contact-person.ts
20204
- var updateContactPersonInputSchema = createContactPersonInputSchema.extend({
20205
- id: exports_external.string().min(1, { error: "ID is required" })
20206
- });
20207
- // ../validators/src/customer-invoice/customer-invoice.ts
20208
- var customerInvoiceDetailsSchema = exports_external.object({
20209
- billingAddress: exports_external.string().trim().max(2500).refine((value) => {
20210
- const lines = value.split(/\r?\n/).filter(Boolean);
20211
- return lines.length <= 5 && lines.every((line) => line.length <= 500);
20212
- }, {
20213
- error: "Billing address supports up to 5 lines of 500 characters each"
20214
- }),
20215
- customerMemo: exports_external.string().trim().min(1, { error: "Customer memo is required" }).max(1000)
20216
- });
20217
- var customerInvoiceSplitInputSchema = exports_external.object({
20218
- label: exports_external.string().trim().min(1, { error: "Invoice label is required" }),
20219
- percentage: exports_external.number().positive().max(100),
20220
- dueDate: exports_external.coerce.date()
20221
- });
20222
- var createCustomerInvoiceBatchInputSchema = exports_external.object({
20223
- budgetId: exports_external.string().min(1),
20224
- isCustomPercentage: exports_external.boolean().default(false),
20225
- details: customerInvoiceDetailsSchema.optional(),
20226
- splits: exports_external.array(customerInvoiceSplitInputSchema).min(1, { error: "At least one invoice is required" }).max(1, { error: "Create one invoice at a time" })
20227
- });
20228
- var updateCustomerInvoiceDetailsInputSchema = customerInvoiceDetailsSchema.extend({
20229
- invoiceId: exports_external.string().min(1)
20230
- });
20231
- var customerInvoiceBatchIdInputSchema = exports_external.object({
20232
- batchId: exports_external.string().min(1)
20233
- });
20234
- var rejectCustomerInvoiceBatchInputSchema = customerInvoiceBatchIdInputSchema.extend({
20235
- rejectionReason: exports_external.string().trim().min(1, { error: "Rejection reason is required" })
20236
- });
20237
- var sendCustomerInvoiceEmailInputSchema = exports_external.object({
20238
- invoiceId: exports_external.string().min(1),
20239
- to: exports_external.email(),
20240
- cc: exports_external.array(exports_external.email()).max(50),
20241
- replyTo: exports_external.email(),
20242
- subject: exports_external.string().trim().min(1).max(200),
20243
- content: exports_external.string().trim().min(1).max(1e5),
20244
- signature: exports_external.string().trim().min(1).max(50000)
20245
- });
20246
- // ../validators/src/customer-invoice/existing-invoice.ts
20247
- var existingCustomerInvoiceInputSchema = exports_external.object({
20248
- budgetId: exports_external.string().min(1),
20249
- qboInvoiceId: exports_external.string().regex(/^\d+$/, "Enter the numeric QuickBooks invoice transaction ID")
20250
- });
20251
- // ../validators/src/customer-invoice/payment-schedule.ts
20252
- var paymentScheduleAmountSchema = exports_external.discriminatedUnion("type", [
20253
- exports_external.object({
20254
- type: exports_external.literal("PERCENTAGE"),
20255
- value: exports_external.number().positive().max(100).refine((value) => Number(value.toFixed(2)) === value, {
20256
- message: "Percentage supports at most two decimal places"
20257
- })
20258
- }),
20259
- exports_external.object({ type: exports_external.literal("FIXED"), value: exports_external.int().positive() }),
20260
- exports_external.object({ type: exports_external.literal("UNRESOLVED") })
20261
- ]);
20262
- var paymentScheduleDueSchema = exports_external.discriminatedUnion("type", [
20263
- exports_external.object({ type: exports_external.literal("CONFIRMATION") }),
20264
- exports_external.object({ type: exports_external.literal("EVENT_START"), offsetDays: exports_external.int() }),
20265
- exports_external.object({ type: exports_external.literal("EVENT_END"), offsetDays: exports_external.int() }),
20266
- exports_external.object({ type: exports_external.literal("FIXED_DATE"), date: exports_external.iso.date() })
20267
- ]);
20268
- var paymentScheduleIssueSchema = exports_external.discriminatedUnion("type", [
20269
- exports_external.object({ type: exports_external.literal("ACCEPTANCE") }),
20270
- exports_external.object({ type: exports_external.literal("DATE"), date: exports_external.iso.date() }),
20271
- exports_external.object({
20272
- type: exports_external.literal("CONDITION"),
20273
- condition: exports_external.enum([
20274
- "EVENT_COMPLETED",
20275
- "CLIENT_SIGN_OFF",
20276
- "FINAL_QUANTITIES"
20277
- ])
20278
- })
20279
- ]);
20280
- var paymentScheduleRowSchema = exports_external.object({
20281
- key: exports_external.string().trim().min(1).max(100),
20282
- label: exports_external.string().trim().min(1).max(200),
20283
- amount: paymentScheduleAmountSchema,
20284
- due: paymentScheduleDueSchema,
20285
- issue: paymentScheduleIssueSchema,
20286
- ownerId: exports_external.string().trim().min(1).max(100).optional(),
20287
- reason: exports_external.string().trim().min(1).max(1000).optional()
20288
- }).superRefine((row, context) => {
20289
- if ((row.amount.type === "UNRESOLVED" || row.issue.type !== "ACCEPTANCE") && (!row.ownerId || !row.reason)) {
20290
- context.addIssue({
20291
- code: "custom",
20292
- message: "Deferred or unresolved instalments need an owner and reason",
20293
- path: ["ownerId"]
20294
- });
20295
- }
20296
- });
20297
- var paymentScheduleSchema = exports_external.object({
20298
- rows: exports_external.array(paymentScheduleRowSchema).min(1).max(20),
20299
- reminderLeadDays: exports_external.int().min(0).max(365).default(7),
20300
- billingRestriction: exports_external.string().trim().max(1000).default(""),
20301
- exceptionReason: exports_external.string().trim().min(1).max(1000).optional()
20302
- }).superRefine((schedule, context) => {
20303
- const keys = new Set;
20304
- for (const [index, row] of schedule.rows.entries()) {
20305
- if (keys.has(row.key)) {
20306
- context.addIssue({
20307
- code: "custom",
20308
- message: "Each payment schedule row needs a unique key",
20309
- path: ["rows", index, "key"]
20310
- });
20311
- }
20312
- keys.add(row.key);
20313
- }
20314
- if (schedule.billingRestriction && !schedule.exceptionReason) {
20315
- context.addIssue({
20316
- code: "custom",
20317
- message: "An exception reason is required for a billing restriction",
20318
- path: ["exceptionReason"]
20319
- });
20320
- }
20321
- });
20322
- // ../validators/src/feature-flag/feature-flag.ts
20323
- var featureFlagNameSchema = exports_external.string().trim().min(1, { error: "Name is required" }).max(100, { error: "Name must be 100 characters or fewer" }).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u, {
20324
- error: "Use lowercase letters, numbers, and single hyphens only"
20325
- });
20326
- var createFeatureFlagInputSchema = exports_external.object({
20327
- name: featureFlagNameSchema,
20328
- enabled: exports_external.boolean()
20329
- });
20330
- var updateFeatureFlagInputSchema = exports_external.object({
20331
- id: exports_external.string().min(1, { error: "ID is required" }),
20332
- enabled: exports_external.boolean()
20333
- });
20334
- // ../validators/src/inbound/gosh-brief.ts
20335
- var goshBriefJsonValueSchema = exports_external.lazy(() => exports_external.union([
20336
- exports_external.string(),
20337
- exports_external.number().finite(),
20338
- exports_external.boolean(),
20339
- exports_external.null(),
20340
- exports_external.array(goshBriefJsonValueSchema),
20341
- exports_external.record(exports_external.string(), goshBriefJsonValueSchema)
20342
- ]));
20343
- var goshBriefSubmissionSchema = exports_external.object({
20344
- company: exports_external.string().max(500).default(""),
20345
- duration: exports_external.string().max(500).default(""),
20346
- estimate: exports_external.object({ totalLabel: exports_external.string().max(500).default("") }).catchall(goshBriefJsonValueSchema).default({ totalLabel: "" }),
20347
- eventType: exports_external.string().max(500).default(""),
20348
- facilitatorSupport: exports_external.string().max(500).default(""),
20349
- mode: exports_external.literal("enrich"),
20350
- notes: exports_external.string().max(20000).default(""),
20351
- participants: exports_external.string().max(500).default(""),
20352
- planningSupport: exports_external.string().max(500).default(""),
20353
- reporting: exports_external.string().max(500).default(""),
20354
- token: exports_external.string().trim().min(20).max(500),
20355
- userAgent: exports_external.string().max(2000).default("")
20356
- }).catchall(goshBriefJsonValueSchema);
20357
- // ../validators/src/inbound/n8n-legacy-import.ts
20358
- var legacyAnswerValueSchema = exports_external.union([
20359
- exports_external.string(),
20360
- exports_external.number(),
20361
- exports_external.boolean(),
20362
- exports_external.array(exports_external.string()),
20363
- exports_external.null()
20364
- ]);
20365
- var legacyN8nInboundRowSchema = exports_external.object({
20366
- asanaTaskId: exports_external.string().trim().min(1).max(255).nullable().optional(),
20367
- asanaTaskName: exports_external.string().trim().min(1).max(500).nullable().optional(),
20368
- asanaTaskUrl: exports_external.url().max(2000).nullable().optional(),
20369
- bdName: exports_external.string().trim().min(1).max(160).nullable().optional(),
20370
- createdAt: exports_external.iso.datetime().nullable().optional(),
20371
- flatAnswers: exports_external.record(exports_external.string(), legacyAnswerValueSchema),
20372
- googleCalendarEventId: exports_external.string().trim().min(1).max(1024).nullable().optional(),
20373
- googleCalendarEventUrl: exports_external.url().max(2000).nullable().optional(),
20374
- inboundSlackChannelId: exports_external.string().trim().min(1).max(255).nullable().optional(),
20375
- legacyRowId: exports_external.string().trim().min(1).max(255),
20376
- lostReason: exports_external.string().trim().max(2000).nullable().optional(),
20377
- messageTs: exports_external.string().trim().regex(/^\d+(?:\.\d+)?$/).max(255),
20378
- pitchSlackChannelId: exports_external.string().trim().min(1).max(255).nullable().optional(),
20379
- pitchSlackChannelName: exports_external.string().trim().min(1).max(255).nullable().optional(),
20380
- pitchSlackChannelUrl: exports_external.url().max(2000).nullable().optional(),
20381
- projectId: exports_external.string().cuid().nullable().optional(),
20382
- status: exports_external.enum(["PITCH", "WON", "LOST"]).nullable().optional(),
20383
- typeformResponseId: exports_external.string().trim().min(1).max(255).nullable().optional()
20384
- }).strict();
20385
- var legacyN8nInboundImportInputSchema = exports_external.object({
20386
- rows: exports_external.array(legacyN8nInboundRowSchema).min(1).max(1000),
20387
- sourceId: exports_external.string().cuid()
20388
- });
20389
- // ../validators/src/inbound/slack-event.ts
20390
- var slackMessageEventSchema = exports_external.object({
20391
- bot_id: exports_external.string().optional(),
20392
- channel: exports_external.string().trim().min(1).max(255),
20393
- subtype: exports_external.string().optional(),
20394
- text: exports_external.string().max(4000),
20395
- thread_ts: exports_external.string().trim().min(1).max(255).optional(),
20396
- ts: exports_external.string().trim().min(1).max(255),
20397
- type: exports_external.enum(["app_mention", "message"]),
20398
- user: exports_external.string().trim().min(1).max(255).optional()
20399
- }).passthrough();
20400
- var slackUrlVerificationPayloadSchema = exports_external.object({
20401
- challenge: exports_external.string().min(1).max(4000),
20402
- type: exports_external.literal("url_verification")
20403
- }).passthrough();
20404
- var slackEventCallbackPayloadSchema = exports_external.object({
20405
- event: slackMessageEventSchema,
20406
- event_id: exports_external.string().trim().min(1).max(255),
20407
- team_id: exports_external.string().trim().min(1).max(255),
20408
- type: exports_external.literal("event_callback")
20409
- }).passthrough();
20410
- var slackEventCommandSchema = exports_external.discriminatedUnion("type", [
20411
- exports_external.object({
20412
- assigneeName: exports_external.string().trim().min(1).max(100),
20413
- type: exports_external.literal("BD_ASSIGNMENT")
20414
- }),
20415
- exports_external.object({ type: exports_external.literal("DELETE_LEAD") }),
20416
- exports_external.object({
20417
- type: exports_external.literal("GOSH_LINK"),
20418
- variant: exports_external.string().trim().min(1).max(100)
20419
- })
20420
- ]);
20421
- // ../validators/src/inbound/typeform-admin.ts
20422
- var TYPEFORM_CANONICAL_FIELDS = [
20423
- "COMPANY_NAME",
20424
- "CONTACT_FIRST_NAME",
20425
- "CONTACT_LAST_NAME",
20426
- "CONTACT_EMAIL",
20427
- "CONTACT_PHONE",
20428
- "PROJECT_NAME",
20429
- "EVENT_TYPE",
20430
- "EVENT_START_DATE",
20431
- "EVENT_END_DATE",
20432
- "VENUE",
20433
- "EXPECTED_PAX",
20434
- "ESTIMATED_BUDGET",
20435
- "HIGHER_OPTIONS",
20436
- "LEAD_SOURCE",
20437
- "REQUIREMENTS"
20438
- ];
20439
- var typeformCanonicalFieldSchema = exports_external.enum(TYPEFORM_CANONICAL_FIELDS);
20440
- var TYPEFORM_ADMIN_MAPPABLE_FIELDS = TYPEFORM_CANONICAL_FIELDS.filter((field) => field !== "PROJECT_NAME" && field !== "HIGHER_OPTIONS");
20441
- var typeformAdminMappableFieldSchema = exports_external.enum(TYPEFORM_ADMIN_MAPPABLE_FIELDS);
20442
- var listInboundSourcesInputSchema = exports_external.object({
20443
- page: exports_external.number().int().min(1).default(1),
20444
- perPage: exports_external.number().int().min(1).max(100).default(25)
20445
- });
20446
- var inboundSourceIdInputSchema = exports_external.object({
20447
- sourceId: exports_external.string().cuid()
20448
- });
20449
- var getInboundAutomationOptionsInputSchema = inboundSourceIdInputSchema.extend({
20450
- asanaProjectId: exports_external.string().trim().min(1).max(255).optional()
20451
- });
20452
- var inboundSubmissionIdInputSchema = exports_external.object({
20453
- submissionId: exports_external.string().cuid()
20454
- });
20455
- var createInboundSourceInputSchema = exports_external.object({
20456
- externalSourceId: exports_external.string().trim().min(1).max(255),
20457
- name: exports_external.string().trim().min(1).max(160)
20458
- }).strict();
20459
- var updateInboundSourceInputSchema = inboundSourceIdInputSchema.extend({
20460
- name: exports_external.string().trim().min(1).max(160)
20461
- });
20462
- var optionalExternalIdSchema = exports_external.string().trim().min(1).max(255).optional();
20463
- var asanaOptionMapSchema = exports_external.record(exports_external.string().trim().min(1).max(160), exports_external.string().trim().min(1).max(255));
20464
- var inboundAsanaCustomFieldConfigSchema = exports_external.object({
20465
- attendeesFieldId: optionalExternalIdSchema,
20466
- bbLinkFieldId: optionalExternalIdSchema,
20467
- budgetFieldId: optionalExternalIdSchema,
20468
- contactFieldId: optionalExternalIdSchema,
20469
- emailFieldId: optionalExternalIdSchema,
20470
- eventDateFieldId: optionalExternalIdSchema,
20471
- eventTypeFieldId: optionalExternalIdSchema,
20472
- eventTypeOptions: asanaOptionMapSchema.optional(),
20473
- leadInFieldId: optionalExternalIdSchema,
20474
- leadSourceFieldId: optionalExternalIdSchema,
20475
- leadSourceOptions: asanaOptionMapSchema.optional(),
20476
- organisationFieldId: optionalExternalIdSchema,
20477
- phoneFieldId: optionalExternalIdSchema,
20478
- slackChannelFieldId: optionalExternalIdSchema,
20479
- higherOptionsFieldId: optionalExternalIdSchema,
20480
- higherOptionsOptions: asanaOptionMapSchema.optional()
20481
- }).strict().transform(({
20482
- higherOptionsFieldId: _field,
20483
- higherOptionsOptions: _options,
20484
- ...config3
20485
- }) => config3);
20486
- var saveInboundAutomationConfigInputSchema = inboundSourceIdInputSchema.extend({
20487
- asanaCustomFields: inboundAsanaCustomFieldConfigSchema,
20488
- asanaDefaultAssigneeId: exports_external.string().trim().min(1).max(255).nullable(),
20489
- asanaDefaultAssigneeName: exports_external.string().trim().min(1).max(160).nullable(),
20490
- asanaDiscussionSectionId: exports_external.string().trim().min(1).max(255).nullable().optional(),
20491
- asanaFirstContactSectionId: exports_external.string().trim().min(1).max(255).nullable().optional(),
20492
- asanaFirstProposalSentSectionId: exports_external.string().trim().min(1).max(255).nullable().optional(),
20493
- asanaFollowerIds: exports_external.array(exports_external.string().trim().min(1).max(255)).max(50),
20494
- asanaProjectId: exports_external.string().trim().min(1).max(255),
20495
- asanaProposalWipSectionId: exports_external.string().trim().min(1).max(255).nullable().optional(),
20496
- asanaSectionId: exports_external.string().trim().min(1).max(255),
20497
- asanaWonSectionId: exports_external.string().trim().min(1).max(255),
20498
- asanaLostSectionId: exports_external.string().trim().min(1).max(255),
20499
- googleCalendarId: exports_external.string().trim().min(1).max(512),
20500
- googleCalendarTimeZone: exports_external.string().trim().min(1).max(100),
20501
- goshBriefBaseUrl: exports_external.url().max(2000).refine((value) => ["http:", "https:"].includes(new URL(value).protocol), {
20502
- message: "GOSH brief URL must use HTTP or HTTPS"
20503
- }),
20504
- slackDefaultAssigneeId: exports_external.string().trim().min(1).max(255).nullable(),
20505
- slackInboundChannelId: exports_external.string().trim().min(1).max(255),
20506
- slackInviteUserIds: exports_external.array(exports_external.string().trim().min(1).max(255)).max(100),
20507
- slackPitchChannelPrefix: exports_external.string().trim().regex(/^[a-z0-9-]+$/).min(1).max(30)
20508
- }).strict();
20509
- var updateInboundProcessingInputSchema = inboundSourceIdInputSchema.extend({
20510
- processingEnabled: exports_external.boolean(),
20511
- processingMode: exports_external.enum(["COLLECT_ONLY", "SHADOW", "ACTIVE"])
20512
- }).superRefine(({ processingEnabled, processingMode }, ctx) => {
20513
- if (processingEnabled && processingMode === "COLLECT_ONLY") {
20514
- ctx.addIssue({
20515
- code: "custom",
20516
- message: "Collect-only sources cannot enable downstream processing.",
20517
- path: ["processingEnabled"]
20518
- });
20519
- }
20520
- });
20521
- var retryInboundProvisioningInputSchema = exports_external.object({
20522
- provisioningId: exports_external.string().cuid()
20523
- });
20524
- var typeformFieldMappingInputSchema = exports_external.object({
20525
- canonicalField: typeformAdminMappableFieldSchema,
20526
- required: exports_external.boolean().default(false),
20527
- typeformFieldId: exports_external.string().trim().min(1).max(255),
20528
- typeformFieldRef: exports_external.string().trim().min(1).max(255).nullable().optional()
20529
- });
20530
- var saveTypeformMappingVersionInputSchema = exports_external.object({
20531
- expectedActiveMappingVersion: exports_external.number().int().min(1).nullable(),
20532
- mappings: exports_external.array(typeformFieldMappingInputSchema).min(1).max(50),
20533
- sourceId: exports_external.string().cuid()
20534
- }).superRefine(({ mappings }, ctx) => {
20535
- const canonicalFields = new Set;
20536
- const typeformFieldIds = new Set;
20537
- for (const [index, mapping] of mappings.entries()) {
20538
- if (canonicalFields.has(mapping.canonicalField)) {
20539
- ctx.addIssue({
20540
- code: "custom",
20541
- message: "Each canonical field can only be mapped once.",
20542
- path: ["mappings", index, "canonicalField"]
20543
- });
20544
- }
20545
- if (typeformFieldIds.has(mapping.typeformFieldId)) {
20546
- ctx.addIssue({
20547
- code: "custom",
20548
- message: "Each Typeform field can only be mapped once.",
20549
- path: ["mappings", index, "typeformFieldId"]
20550
- });
20551
- }
20552
- canonicalFields.add(mapping.canonicalField);
20553
- typeformFieldIds.add(mapping.typeformFieldId);
20554
- }
20555
- });
20556
- var listInboundSubmissionsInputSchema = exports_external.object({
20557
- page: exports_external.number().int().min(1).default(1),
20558
- perPage: exports_external.number().int().min(1).max(100).default(25),
20559
- sourceId: exports_external.string().cuid().optional()
20560
- });
20561
- // ../validators/src/inbound-lead-review.ts
20562
- var inboundLeadListStatusSchema = exports_external.enum([
20563
- "ACTIVE",
20564
- "WON",
20565
- "LOST",
20566
- "DISQUALIFIED",
20567
- "ALL"
20568
- ]);
20569
- var inboundLeadListSortSchema = exports_external.enum([
20570
- "ADDED_NEWEST",
20571
- "ADDED_OLDEST"
20572
- ]);
20573
- var listInboundLeadsInputSchema = exports_external.object({
20574
- ownerId: exports_external.union([exports_external.literal(""), exports_external.string().cuid()]).default(""),
20575
- stage: exports_external.string().max(200).default(""),
20576
- sort: inboundLeadListSortSchema.default("ADDED_NEWEST"),
20577
- page: exports_external.number().int().min(1).default(1),
20578
- perPage: exports_external.number().int().min(1).max(100).default(25),
20579
- search: exports_external.string().trim().max(200).default(""),
20580
- status: inboundLeadListStatusSchema.default("ACTIVE")
20581
- });
20582
- var closeInboundLeadInputSchema = exports_external.object({
20583
- projectId: exports_external.string().cuid(),
20584
- outcome: exports_external.enum(InboundLeadCloseOutcome),
20585
- reason: exports_external.string().trim().min(3).max(1000)
20586
- });
20587
- // ../validators/src/integration-credential/index.ts
20588
- var id = exports_external.string().trim().min(1).max(128);
20589
- var revision = exports_external.number().int().min(1);
20590
- var integrationCredentialListInput = exports_external.object({
20591
- cursor: id.optional(),
20592
- limit: exports_external.number().int().min(1).max(100).default(50)
20593
- }).strict().default({ limit: 50 });
20594
- var integrationCredentialIdInput = exports_external.object({ id }).strict();
20595
- var createIntegrationCredentialInput = exports_external.object({
20596
- fields: exports_external.record(exports_external.string(), exports_external.unknown()),
20597
- name: exports_external.string().trim().min(1).max(256),
20598
- type: exports_external.enum(IntegrationCredentialType)
20599
- }).strict();
20600
- var updateIntegrationCredentialInput = exports_external.object({
20601
- fields: exports_external.record(exports_external.string(), exports_external.unknown()),
20602
- id,
20603
- name: exports_external.string().trim().min(1).max(256).optional(),
20604
- revision
20605
- }).strict();
20606
- var versionedIntegrationCredentialInput = exports_external.object({ id, revision }).strict();
20607
- var bindingShape = {
20608
- capability: exports_external.enum(IntegrationCapability),
20609
- credentialId: id,
20610
- inboundSourceId: id.nullish(),
20611
- scope: exports_external.enum(IntegrationCredentialScope)
20612
- };
20613
- var validateBindingScope = (value, ctx) => {
20614
- if (value.scope === IntegrationCredentialScope.GLOBAL && value.inboundSourceId != null)
20615
- ctx.addIssue({
20616
- code: "custom",
20617
- message: "Global bindings cannot target an inbound source",
20618
- path: ["inboundSourceId"]
20619
- });
20620
- if (value.scope === IntegrationCredentialScope.INBOUND_SOURCE && !value.inboundSourceId)
20621
- ctx.addIssue({
20622
- code: "custom",
20623
- message: "Inbound-source bindings require an inbound source",
20624
- path: ["inboundSourceId"]
20625
- });
20626
- };
20627
- var setIntegrationCredentialBindingInput = exports_external.object(bindingShape).strict().superRefine(validateBindingScope);
20628
- var removeIntegrationCredentialBindingInput = exports_external.object({
20629
- capability: bindingShape.capability,
20630
- inboundSourceId: bindingShape.inboundSourceId,
20631
- scope: bindingShape.scope
20632
- }).strict().superRefine(validateBindingScope);
20633
- // ../validators/src/item/create-item.ts
20634
- var MIN_GO_LABS_PRODUCT_COST = 1000;
20635
- var createItemInputSchema = exports_external.object({
20636
- name: quickBooksNameSchema,
20637
- description: exports_external.string().optional(),
20638
- descriptionRichText: exports_external.custom().optional().nullable(),
20639
- categoryId: exports_external.string().min(1, { error: "Please select a category." }),
20640
- cost: exports_external.string({ error: "Cost is required" }).min(1, { error: "Cost is required" }).refine((val) => {
20641
- const numValue = Number.parseFloat(val);
20642
- return !Number.isNaN(numValue);
20643
- }, {
20644
- error: "Cost must be a valid number"
20645
- }).refine((val) => {
20646
- const numValue = Number.parseFloat(val);
20647
- return numValue >= 0;
20648
- }, {
20649
- error: "Cost cannot be negative"
20650
- }),
20651
- gstInclusive: exports_external.boolean().optional(),
20652
- gstOutOfScope: exports_external.boolean().optional().nullable(),
20653
- goLabsProduct: exports_external.boolean().optional(),
20654
- supplierId: exports_external.string().min(1, { error: "Please select a supplier." }),
20655
- attachments: exports_external.array(exports_external.object({
20656
- id: exports_external.string(),
20657
- name: exports_external.string().nullable(),
20658
- key: exports_external.string(),
20659
- size: exports_external.number()
20660
- })).optional()
20661
- }).refine((data) => {
20662
- if (data.goLabsProduct === true && Number(data.cost) < MIN_GO_LABS_PRODUCT_COST) {
20663
- return false;
20664
- }
20665
- return true;
20666
- }, {
20667
- error: `Go Labs products must have a cost of at least $${MIN_GO_LABS_PRODUCT_COST}`,
20668
- path: ["cost"]
20669
- }).refine((data) => !(data.gstOutOfScope && data.gstInclusive), {
20670
- error: "GST-inclusive cost cannot be used for an out-of-scope item",
20671
- path: ["gstInclusive"]
20672
- });
20673
- // ../validators/src/item/update-item.ts
20674
- var updateItemInputSchema = createItemInputSchema.extend({
20675
- id: exports_external.string().min(1, { error: "ID is required" })
20676
- });
20677
- // ../validators/src/item-category/item-category.ts
20678
- var createItemCategoryInputSchema = exports_external.object({
20679
- name: quickBooksNameSchema
20680
- });
20681
- var updateItemCategoryInputSchema = createItemCategoryInputSchema.extend({
20682
- id: exports_external.string().min(1, { error: "ID is required" })
20683
- });
20684
- // ../validators/src/project/date-range.ts
20685
- var refineProjectDateRange = (dateRange, ctx) => {
20686
- if (dateRange.to === undefined)
20687
- return;
20688
- if (dateRange.to.getTime() >= dateRange.from.getTime())
20689
- return;
20690
- ctx.addIssue({
20691
- code: "custom",
20692
- message: "End date must be on or after start date.",
20693
- path: ["to"]
20694
- });
20695
- };
20696
-
20697
- // ../validators/src/project/create-project.ts
20698
- var asanaTaskIdSchema = exports_external.string().trim().min(1, { error: "Please select an Asana deal card." }).regex(/^\d+$/, { error: "Asana task id must be a numeric gid." });
20699
- var slackChannelIdSchema = exports_external.string().trim().min(1, { error: "Please select a Slack channel." }).regex(/^[CG][A-Z0-9]+$/, {
20700
- error: "Slack channel id must start with C or G."
20701
- });
20702
- var positiveWholeProjectPaxPattern = /^[1-9]\d*$/;
20703
- var optionalProjectPaxSchema = exports_external.string().trim().nullish().refine((value) => value == null || value === "" || positiveWholeProjectPaxPattern.test(value), {
20704
- error: "Pax must be a positive whole number when provided"
20705
- });
20706
- var createProjectInputSchema = exports_external.object({
20707
- name: quickBooksNameSchema,
20708
- description: exports_external.string().optional(),
20709
- dateRange: exports_external.object({
20710
- from: exports_external.date(),
20711
- to: exports_external.date().optional()
20712
- }).refine((dr) => dr.from != null, {
20713
- error: "Please select an event date.",
20714
- path: ["from"]
20715
- }).superRefine(refineProjectDateRange),
20716
- companyId: exports_external.string().min(1, { error: "Please select a company." }),
20717
- contactPersonId: exports_external.string().min(1, { error: "Please select a contact person." }),
20718
- requestQboAccountantNotification: exports_external.boolean().optional(),
20719
- asanaTaskId: asanaTaskIdSchema,
20720
- slackChannelId: slackChannelIdSchema,
20721
- slackChannelUrl: exports_external.url({ error: "Slack channel URL must be a valid URL." }),
20722
- slackChannelName: exports_external.string().trim().min(1, { error: "Slack channel name is required." }),
20723
- insideSalesId: exports_external.string().min(1, { error: "Please select an inside sales." }),
20724
- businessDevelopmentId: exports_external.string().min(1, { error: "Please select a business development." }),
20725
- projectManagerId: exports_external.string().trim().optional(),
20726
- venue: exports_external.string().min(1, { error: "Venue is required" }),
20727
- pax: optionalProjectPaxSchema
20728
- });
20729
-
20730
- // ../validators/src/project/update-project.ts
20731
- var PROJECT_INTEGRATION_REQUIREMENT_CUTOFF = new Date("2026-05-27T08:03:55.000Z");
20732
- var updateProjectNameSchema = exports_external.string().trim().min(1, { error: "Name is required" }).max(100, { error: "Name cannot be longer than 100 characters" });
20733
- var optionalAsanaTaskIdSchema = exports_external.union([asanaTaskIdSchema, exports_external.literal("")]).optional();
20734
- var optionalSlackChannelIdSchema = exports_external.union([slackChannelIdSchema, exports_external.literal("")]).optional();
20735
- var optionalSlackChannelUrlSchema = exports_external.union([
20736
- exports_external.url({ error: "Slack channel URL must be a valid URL." }),
20737
- exports_external.literal("")
20738
- ]).optional();
20739
- var optionalSlackChannelNameSchema = exports_external.union([exports_external.string().trim().min(1).max(80), exports_external.literal("")]).optional();
20740
- var updateProjectInputBaseSchema = createProjectInputSchema.extend({
20741
- id: exports_external.string().min(1, { error: "ID is required" }),
20742
- name: updateProjectNameSchema,
20743
- confirmEstimateInvalidation: exports_external.boolean().optional(),
20744
- asanaTaskId: optionalAsanaTaskIdSchema,
20745
- slackChannelId: optionalSlackChannelIdSchema,
20746
- slackChannelUrl: optionalSlackChannelUrlSchema,
20747
- slackChannelName: optionalSlackChannelNameSchema
20748
- }).partial({
20749
- slackChannelId: true,
20750
- slackChannelUrl: true,
20751
- slackChannelName: true
20752
- });
20753
- var refinePartialSlackChannelGroup = (data, ctx) => {
20754
- const id2 = data.slackChannelId?.trim() ?? "";
20755
- const url2 = data.slackChannelUrl?.trim() ?? "";
20756
- const name = data.slackChannelName?.trim() ?? "";
20757
- const allEmpty = id2 === "" && url2 === "" && name === "";
20758
- if (allEmpty)
20759
- return;
20760
- const idOk = slackChannelIdSchema.safeParse(id2).success;
20761
- const urlOk = exports_external.url().safeParse(url2).success;
20762
- const nameOk = exports_external.string().min(1).max(80).safeParse(name).success;
20763
- if (!(idOk && urlOk && nameOk)) {
20764
- ctx.addIssue({
20765
- code: "custom",
20766
- message: "Slack channel id, URL, and name must all be valid when any Slack field is set."
20767
- });
20768
- }
20769
- };
20770
- var updateProjectInputSchema = updateProjectInputBaseSchema.superRefine(refinePartialSlackChannelGroup);
20771
-
20772
- // ../validators/src/supplier/create-supplier.ts
20773
- var createSupplierInputSchema = exports_external.object({
20774
- name: quickBooksNameSchema.min(2, {
20775
- error: "Name must be at least 2 characters"
20776
- }),
20777
- gstRegistered: exports_external.boolean().optional(),
20778
- primaryContactPerson: exports_external.string().min(2, { error: "Primary contact person must be at least 2 characters" }),
20779
- secondaryContactPerson: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20780
- email: exports_external.email({ error: "Invalid email address" }),
20781
- phone: exports_external.string().min(8, { error: "Phone number must be at least 8 characters" }),
20782
- bankAccountName: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20783
- bankAccountNumber: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20784
- bankBranchCode: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20785
- bankCode: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20786
- bankName: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20787
- bankSwiftCode: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20788
- payNowIdentifierType: exports_external.enum(PayNowIdentifierType).nullish().transform((val) => val ?? undefined),
20789
- paymentMethodId: exports_external.string().min(1, { error: "Payment method is required" }),
20790
- paymentTerm: exports_external.string().min(1, { error: "Payment term is required" }),
20791
- roleId: exports_external.string().min(1, { error: "Role is required" }),
20792
- tags: exports_external.array(exports_external.string()).optional(),
20793
- website: exports_external.union([exports_external.url({ error: "Please enter a valid URL" }), exports_external.literal("")]).nullish().transform((val) => val === "" || val === null ? undefined : val),
20794
- uenNumber: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20795
- certificationId: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20796
- address: exports_external.object({
20797
- id: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20798
- streetAddress1: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20799
- streetAddress2: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20800
- city: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20801
- state: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20802
- postalCode: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
20803
- country: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val)
20804
- }).optional(),
20805
- attachments: exports_external.array(exports_external.object({
20806
- id: exports_external.string(),
20807
- name: exports_external.string().optional().nullable(),
20808
- key: exports_external.string(),
20809
- size: exports_external.number()
20810
- })).optional()
20811
- });
20812
-
20813
- // ../validators/src/supplier/update-supplier.ts
20814
- var updateSupplierInputSchema = createSupplierInputSchema.and(exports_external.object({
20815
- id: exports_external.string()
20816
- }));
20817
-
20818
- // ../validators/src/mutation-inputs-json.ts
20819
- var jsonCreateBillInputSchema = createBillInputSchemaBase.omit({
20820
- invoiceDate: true
20821
- }).extend({
20822
- invoiceDate: exports_external.coerce.date().optional()
20823
- }).superRefine(validateAlreadyPaidBillInput);
20824
- var jsonUpdateBillInputSchema = updateBillInputSchemaBase.omit({
20825
- invoiceDate: true
20826
- }).extend({
20827
- invoiceDate: exports_external.coerce.date().optional()
20828
- }).superRefine(validateAlreadyPaidBillInput);
20829
- var jsonCreateProjectInputSchema = createProjectInputSchema.omit({
20830
- dateRange: true
20831
- }).extend({
20832
- dateRange: exports_external.object({
20833
- from: exports_external.coerce.date(),
20834
- to: exports_external.coerce.date().optional()
20835
- }).superRefine(refineProjectDateRange)
20836
- });
20837
- var jsonUpdateProjectInputSchema = updateProjectInputBaseSchema.omit({
20838
- dateRange: true
20839
- }).extend({
20840
- dateRange: exports_external.object({
20841
- from: exports_external.coerce.date(),
20842
- to: exports_external.coerce.date().optional()
20843
- }).superRefine(refineProjectDateRange)
20844
- }).superRefine(refinePartialSlackChannelGroup);
20845
- // ../validators/src/project/update-project-status.ts
20846
- var updateProjectStatusInputSchema = exports_external.object({
20847
- id: exports_external.string(),
20848
- status: exports_external.enum(ProjectStatus),
20849
- projectManagerId: exports_external.string().trim().min(1).optional(),
20850
- projectManagerEmail: exports_external.email().optional(),
20851
- lostReason: exports_external.string().optional(),
20852
- wonOverrideReason: exports_external.string().trim().min(10).max(1000).optional()
20853
- });
20854
- // ../validators/src/project-hub.ts
20855
- var PROJECT_HUB_ALWAYS_ALLOWED_DOMAIN = "getout.sg";
20856
- var projectHubConfiguredDomainSchema = exports_external.string().trim().toLowerCase().transform((value) => value.startsWith("@") ? value.slice(1) : value).pipe(exports_external.hostname());
20857
- var projectHubConfiguredDomainsSchema = exports_external.array(projectHubConfiguredDomainSchema).max(20, "Add at most 20 domains").transform((domains) => [...new Set(domains)].filter((domain2) => domain2 !== PROJECT_HUB_ALWAYS_ALLOWED_DOMAIN).sort());
20858
- var projectHubDomainSettingsFormSchema = exports_external.object({
20859
- domains: projectHubConfiguredDomainsSchema
20860
- });
20861
- // ../validators/src/quickbooks/qbo-project-id.ts
20862
- var qboProjectIdInputSchema = exports_external.object({
20863
- projectId: exports_external.string(),
20864
- projectUrl: exports_external.url({ error: "Please enter a valid URL" })
20865
- });
20866
- // ../validators/src/quotation/index.ts
20867
- var QUOTATION_ATTACHMENT_MAX_SIZE = 20 * 1024 * 1024;
20868
- var quotationAttachmentSchema = exports_external.object({
20869
- id: exports_external.string(),
20870
- name: exports_external.string().nullable(),
20871
- key: exports_external.string().min(1),
20872
- size: exports_external.number().int().positive().max(QUOTATION_ATTACHMENT_MAX_SIZE, "Quotation must be 20MB or smaller")
20873
- });
20874
- var quotationStatusSchema = exports_external.enum([
20875
- "DRAFT",
20876
- "PENDING_APPROVAL",
20877
- "APPROVED",
20878
- "REJECTED",
20879
- "SUPERSEDED"
20880
- ]);
20881
- var hasCurrencyPrecision = (value) => Math.abs(value * 100 - Math.round(value * 100)) < 0.00000001;
20882
- var quotationSubtotalSchema = exports_external.number().finite().positive().max(99999999.99).refine(hasCurrencyPrecision, "Use no more than two decimal places");
20883
- var quotationGstAmountSchema = exports_external.number().finite().nonnegative().max(99999999.99).refine(hasCurrencyPrecision, "Use no more than two decimal places");
20884
- var createSupplierQuotationInputSchema = exports_external.object({
20885
- projectId: exports_external.string(),
20886
- budgetId: exports_external.string(),
20887
- supplierId: exports_external.string(),
20888
- amountWithoutGst: quotationSubtotalSchema,
20889
- gstAmount: quotationGstAmountSchema,
20890
- lineAmounts: exports_external.array(exports_external.object({
20891
- budgetItemId: exports_external.string().min(1),
20892
- amount: quotationGstAmountSchema
20893
- })).optional(),
20894
- budgetItemIds: exports_external.array(exports_external.string()).min(1, "At least one line item is required"),
20895
- attachments: exports_external.array(quotationAttachmentSchema).min(1, "Quotation file is required").max(1, "Upload one quotation file")
20896
- });
20897
- var updateSupplierQuotationInputSchema = createSupplierQuotationInputSchema.extend({
20898
- id: exports_external.string()
20899
- });
20900
- var submitSupplierQuotationInputSchema = exports_external.object({
20901
- id: exports_external.string(),
20902
- overBudgetOverrideReason: exports_external.string().trim().min(10).max(2000).optional()
20903
- });
20904
- var approveSupplierQuotationInputSchema = exports_external.object({
20905
- quotationApprovalId: exports_external.string(),
20906
- overBudgetOverrideReason: exports_external.string().trim().min(10).max(2000).optional()
20907
- });
20908
- var rejectSupplierQuotationInputSchema = exports_external.object({
20909
- quotationApprovalId: exports_external.string(),
20910
- rejectionReason: exports_external.string().min(1, "Rejection reason is required")
20911
- });
20912
- var listSupplierQuotationsInputSchemaBase = exports_external.object({
20913
- projectId: exports_external.string().optional(),
20914
- budgetId: exports_external.string().optional(),
20915
- supplierId: exports_external.string().optional(),
20916
- status: exports_external.array(quotationStatusSchema).optional(),
20917
- createdBy: exports_external.array(exports_external.string()).optional(),
20918
- perPage: exports_external.number().int().positive().max(100).optional(),
20919
- requesterIds: exports_external.array(exports_external.string()).optional(),
20920
- pageSize: exports_external.number().int().positive().max(100).optional(),
20921
- search: exports_external.string().trim().optional(),
20922
- page: exports_external.number().int().positive().optional(),
20923
- includeMetrics: exports_external.boolean().optional()
20924
- });
20925
- var listSupplierQuotationsInputSchema = listSupplierQuotationsInputSchemaBase.transform(({ requesterIds, pageSize, ...input2 }) => ({
20926
- ...input2,
20927
- createdBy: input2.createdBy ?? requesterIds,
20928
- perPage: input2.perPage ?? pageSize
20929
- }));
20930
- var getSupplierQuotationByIdInputSchema = exports_external.object({
20931
- id: exports_external.string()
20932
- });
20933
- var deleteSupplierQuotationInputSchema = exports_external.object({
20934
- id: exports_external.string()
20935
- });
20936
- var getSupplierQuotationSignedDownloadUrlInputSchema = exports_external.object({
20937
- id: exports_external.string(),
20938
- fileRole: exports_external.enum(["ORIGINAL", "STAFF_SIGNED", "FINAL_SIGNED"])
20939
- });
20940
- // ../validators/src/typeform/typeform-webhook.ts
20941
- var typeformJsonObjectSchema = exports_external.record(exports_external.string(), exports_external.json());
20942
- var typeformTimestampSchema = exports_external.string().trim().min(1).refine((value) => !Number.isNaN(Date.parse(value)));
20943
- var typeformWebhookPayloadSchema = exports_external.object({
20944
- event_id: exports_external.string().trim().min(1),
20945
- event_type: exports_external.literal("form_response"),
20946
- form_response: exports_external.object({
20947
- answers: exports_external.array(typeformJsonObjectSchema).optional(),
20948
- definition: typeformJsonObjectSchema.optional(),
20949
- form_id: exports_external.string().trim().min(1),
20950
- landed_at: typeformTimestampSchema.optional(),
20951
- submitted_at: typeformTimestampSchema,
20952
- token: exports_external.string().trim().min(1)
20953
- }).catchall(exports_external.json())
20954
- }).catchall(exports_external.json());
20955
- // ../api/src/lib/payment-schedule-input.ts
20956
- var paymentScheduleBillingSchema = exports_external.object({
20957
- legalEntityName: exports_external.string().trim().min(1).max(500),
20958
- purchaseOrder: exports_external.string().trim().max(500),
20959
- billingAddress: exports_external.string().trim().max(2500),
20960
- to: exports_external.union([exports_external.email(), exports_external.literal("")]),
20961
- cc: exports_external.array(exports_external.email()).max(50),
20962
- replyTo: exports_external.union([exports_external.email(), exports_external.literal("")]),
20963
- deliveryMethod: exports_external.enum(["EMAIL", "PORTAL_UPLOAD"]),
20964
- uploadUrl: exports_external.url().nullable()
20965
- });
20966
- var paymentScheduleIdInput = exports_external.object({ budgetId: exports_external.string().min(1) });
20967
- var paymentScheduleRevisionInput = paymentScheduleIdInput.extend({
20968
- expectedRevision: exports_external.int().positive()
20969
- });
20970
- var paymentScheduleSaveInput = paymentScheduleIdInput.extend({
20971
- expectedRevision: exports_external.int().positive().nullable(),
20972
- definition: paymentScheduleSchema,
20973
- billing: paymentScheduleBillingSchema,
20974
- scope: exports_external.enum(["BASE", "REPLACEMENT", "VARIATION"]),
20975
- relatedScheduleId: exports_external.string().min(1).optional(),
20976
- changeReason: exports_external.string().trim().min(1).max(2000)
20977
- });
20978
- var paymentScheduleExecutionInput = paymentScheduleRevisionInput.extend({
20979
- instalmentIds: exports_external.array(exports_external.string().min(1)).min(1).max(20),
20980
- send: exports_external.boolean().default(false)
20981
- });
20982
-
20983
- // src/runtime/error.ts
20984
- var invocationErrorCodes = new Set([
20985
- "USAGE",
20986
- "VALIDATION",
20987
- "CONFIRMATION_REQUIRED"
20988
- ]);
20989
- var trpcCodeMap = {
20990
- BAD_REQUEST: "VALIDATION",
20991
- CONFLICT: "CONFLICT",
20992
- FORBIDDEN: "AUTHORIZATION",
20993
- INTERNAL_SERVER_ERROR: "API",
20994
- METHOD_NOT_SUPPORTED: "API",
20995
- NOT_FOUND: "NOT_FOUND",
20996
- PARSE_ERROR: "VALIDATION",
20997
- PAYLOAD_TOO_LARGE: "VALIDATION",
20998
- PRECONDITION_FAILED: "CONFLICT",
20999
- TIMEOUT: "NETWORK",
21000
- TOO_MANY_REQUESTS: "API",
21001
- UNAUTHORIZED: "AUTHENTICATION",
21002
- UNPROCESSABLE_CONTENT: "VALIDATION"
21003
- };
21004
- var cliErrorCodes = new Set([
21005
- "USAGE",
21006
- "VALIDATION",
21007
- "CONFIRMATION_REQUIRED",
21008
- "AUTHENTICATION",
21009
- "AUTHORIZATION",
21010
- "NOT_FOUND",
21011
- "CONFLICT",
21012
- "API",
21013
- "NETWORK",
21014
- "ABORTED",
21015
- "INTERRUPTED",
21016
- "INTERNAL"
21017
- ]);
21018
-
21019
- class CliRuntimeError extends Error {
21020
- code;
21021
- details;
21022
- requestId;
21023
- constructor(code, message, options = {}) {
21024
- super(message);
21025
- this.name = "CliRuntimeError";
21026
- this.code = code;
21027
- this.details = options.details;
21028
- this.requestId = options.requestId;
21029
- }
21030
- }
21031
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21032
- var asNonEmptyString = (value) => typeof value === "string" && value.trim() !== "" ? value : undefined;
21033
- var asDetails = (value) => {
21034
- if (!isRecord(value))
21035
- return;
21036
- const details = {};
21037
- for (const [key, nestedValue] of Object.entries(value)) {
21038
- if (isJsonValue(nestedValue))
21039
- details[key] = nestedValue;
21040
- }
21041
- return Object.keys(details).length > 0 ? details : undefined;
21042
- };
21043
- var isJsonValue = (value) => {
21044
- if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
21045
- return true;
21046
- }
21047
- if (Array.isArray(value))
21048
- return value.every(isJsonValue);
21049
- return isRecord(value) && Object.values(value).every(isJsonValue);
21050
- };
21051
- var mapErrorCode = (value) => {
21052
- if (typeof value !== "string")
21053
- return;
21054
- if (cliErrorCodes.has(value))
21055
- return value;
21056
- if (value in trpcCodeMap)
21057
- return trpcCodeMap[value];
21058
- return;
21059
- };
21060
- var networkMessagePattern = /\b(fetch failed|network error|unable to connect|econnrefused|econnreset|enotfound|etimedout|socket hang up)\b/i;
21061
- var isNetworkError = (error61, depth = 0) => {
21062
- if (depth > 3 || !isRecord(error61))
21063
- return false;
21064
- const message = asNonEmptyString(error61.message);
21065
- if (message !== undefined && networkMessagePattern.test(message))
21066
- return true;
21067
- const code = asNonEmptyString(error61.code);
21068
- if (code !== undefined && networkMessagePattern.test(code))
21069
- return true;
21070
- return isNetworkError(error61.cause, depth + 1);
21071
- };
21072
- var normalizeCliError = (error61) => {
21073
- if (error61 instanceof CliRuntimeError) {
21074
- return {
21075
- code: error61.code,
21076
- message: error61.message,
21077
- ...error61.details === undefined ? {} : { details: error61.details },
21078
- ...error61.requestId === undefined ? {} : { requestId: error61.requestId }
21079
- };
21080
- }
21081
- if (isNetworkError(error61)) {
21082
- return {
21083
- code: "NETWORK",
21084
- message: error61 instanceof Error ? error61.message : "The Budget Builder API could not be reached."
21085
- };
21086
- }
21087
- if (isRecord(error61)) {
21088
- const data = isRecord(error61.data) ? error61.data : undefined;
21089
- const transportCode = mapErrorCode(data?.code ?? error61.code);
21090
- if (error61 instanceof Error && data === undefined && transportCode === undefined) {
21091
- return { code: "INTERNAL", message: error61.message };
21092
- }
21093
- const code = transportCode ?? "API";
21094
- const message = asNonEmptyString(error61.message) ?? "The Budget Builder API request failed.";
21095
- const requestId = asNonEmptyString(data?.requestId) ?? asNonEmptyString(data?.requestID) ?? asNonEmptyString(error61.requestId);
21096
- const details = asDetails(data?.details ?? error61.details);
21097
- return {
21098
- code,
21099
- message,
21100
- ...details === undefined ? {} : { details },
21101
- ...requestId === undefined ? {} : { requestId }
21102
- };
21103
- }
21104
- if (error61 instanceof Error) {
21105
- return { code: "INTERNAL", message: error61.message };
21106
- }
21107
- return { code: "INTERNAL", message: "An unexpected CLI error occurred." };
21108
- };
21109
- var exitCodeForCliError = (error61) => {
21110
- if (error61.code === "INTERRUPTED")
21111
- return 130;
21112
- return invocationErrorCodes.has(error61.code) ? 2 : 1;
21113
- };
21114
-
21115
- // src/runtime/confirmation.ts
21116
- var effectLabels = {
21117
- "state-change": "changes Budget Builder state",
21118
- email: "sends email",
21119
- "external-write": "writes to an external system",
21120
- delete: "deletes data",
21121
- "financial-write": "creates or changes financial records"
21122
- };
21123
- var isEffectAllowed = (effect, permissions) => {
21124
- switch (effect) {
21125
- case "state-change":
21126
- return permissions.allowStateChange === true;
21127
- case "email":
21128
- return permissions.allowEmail === true;
21129
- case "external-write":
21130
- return permissions.allowExternalWrite === true;
21131
- case "delete":
21132
- return permissions.allowDelete === true;
21133
- case "financial-write":
21134
- return permissions.allowFinancialWrite === true;
21135
- }
21136
- };
21137
- var uniqueEffects = (effects) => [...new Set(effects)];
21138
- var getRequiredEffectPermissions = (effects, permissions) => uniqueEffects(effects).filter((effect) => !isEffectAllowed(effect, permissions));
21139
- var formatConfirmationPreview = (request) => [
21140
- "Budget Builder workflow preview",
21141
- `Action: ${request.action}`,
21142
- `Target: ${request.target?.trim() || "not specified"}`,
21143
- `Effects: ${uniqueEffects(request.effects).map((effect) => effectLabels[effect]).join("; ") || "none"}`,
21144
- ...request.details === undefined ? [] : [`Details: ${request.details}`]
21145
- ].join(`
21146
- `);
21147
- var confirmSensitiveCommand = async (runtime2, request) => {
21148
- const requiredPermissions = getRequiredEffectPermissions(request.effects, request.permissions);
21149
- if (request.effects.length === 0) {
21150
- return { confirmed: true, requiredPermissions };
21151
- }
21152
- const target = request.target?.trim();
21153
- if (!runtime2.isTTY) {
21154
- if (target === undefined) {
21155
- throw new CliRuntimeError("CONFIRMATION_REQUIRED", "Non-interactive sensitive commands require an explicit target.", {
21156
- details: {
21157
- action: request.action,
21158
- effects: requiredPermissions,
21159
- missingFlags: requiredPermissions.map((effect) => `--allow-${effect}`),
21160
- target: "required"
21161
- }
21162
- });
21163
- }
21164
- if (requiredPermissions.length === 0) {
21165
- return { confirmed: true, requiredPermissions };
21166
- }
21167
- const missing = requiredPermissions.map((effect) => `--allow-${effect}`);
21168
- throw new CliRuntimeError("CONFIRMATION_REQUIRED", "Non-interactive sensitive commands require an explicit target and all applicable allow flags.", {
21169
- details: {
21170
- action: request.action,
21171
- effects: requiredPermissions,
21172
- missingFlags: missing
21173
- }
21174
- });
21175
- }
21176
- if (requiredPermissions.length === 0) {
21177
- return { confirmed: true, requiredPermissions };
21178
- }
21179
- const preview = formatConfirmationPreview(request);
21180
- runtime2.stderr.write(`${preview}
21181
- `);
21182
- if (runtime2.prompt === undefined) {
21183
- throw new CliRuntimeError("CONFIRMATION_REQUIRED", "This command needs an interactive confirmation prompt.");
21184
- }
21185
- const answer = await runtime2.prompt("Type CONFIRM to continue: ");
21186
- if (answer !== "CONFIRM") {
21187
- throw new CliRuntimeError("ABORTED", "Command aborted by user.");
21188
- }
21189
- return { confirmed: true, preview, requiredPermissions };
21190
- };
21191
- // src/runtime/sanitize.ts
21192
- var sensitiveKeys = new Set([
21193
- "apikey",
21194
- "authorization",
21195
- "bb_api_key",
21196
- "password",
21197
- "paymentproof",
21198
- "paymentproofattachments",
21199
- "paymentproofpath",
21200
- "paymentreference",
21201
- "payload",
21202
- "proof",
21203
- "receipt",
21204
- "secret",
21205
- "token"
21206
- ]);
21207
-
21208
- // src/runtime/output.ts
21209
- var serialize = (value) => `${JSON.stringify(value, null, 2)}
21210
- `;
21211
- var createSuccessEnvelope = (data, invocation) => ({
21212
- ok: true,
21213
- data,
21214
- meta: {
21215
- command: invocation.command,
21216
- cliVersion: invocation.cliVersion,
21217
- apiBaseUrl: invocation.apiBaseUrl
21218
- }
21219
- });
21220
- var emitCliSuccess = (runtime2, invocation, data) => {
21221
- runtime2.stdout.write(serialize(invocation.legacy ? data : createSuccessEnvelope(data, invocation)));
21222
- return 0;
21223
- };
21224
- var emitCliError = (runtime2, invocation, error61) => {
21225
- const normalized = normalizeCliError(error61);
21226
- if (invocation.legacy) {
21227
- runtime2.stderr.write(serialize({ error: normalized.message }));
21228
- } else {
21229
- const envelope = {
21230
- ok: false,
21231
- error: normalized,
21232
- meta: { command: invocation.command }
21233
- };
21234
- runtime2.stderr.write(serialize(envelope));
21235
- }
21236
- return exitCodeForCliError(normalized);
21237
- };
21238
- // src/runtime/process-runtime.ts
21239
- import { createInterface } from "readline/promises";
21240
- var createProcessRuntime = () => ({
21241
- stdout: {
21242
- write: (value) => {
21243
- process.stdout.write(value);
21244
- }
21245
- },
21246
- stderr: {
21247
- write: (value) => {
21248
- process.stderr.write(value);
21249
- }
21250
- },
21251
- isTTY: Boolean(process.stdin.isTTY && process.stderr.isTTY),
21252
- env: process.env,
21253
- prompt: async (message) => {
21254
- const prompt = createInterface({
21255
- input: process.stdin,
21256
- output: process.stderr
21257
- });
21258
- try {
21259
- return await prompt.question(message);
21260
- } finally {
21261
- prompt.close();
21262
- }
21263
- },
21264
- now: () => new Date
21265
- });
21266
- // src/runtime/session.ts
21267
- var activeSession;
21268
- var setActiveCliSession = (session) => {
21269
- activeSession = {
21270
- ...session,
21271
- confirmedEffects: new Set
21272
- };
21273
- };
21274
- var clearActiveCliSession = () => {
21275
- activeSession = undefined;
21276
- };
21277
- var getActiveCliSession = () => activeSession;
21278
- var requireSession = () => {
21279
- if (activeSession === undefined) {
21280
- throw new Error("Budget Builder CLI runtime session is not configured.");
21281
- }
21282
- return activeSession;
21283
- };
21284
- var toJsonValue = (value) => {
21285
- const serialized = JSON.stringify(value, (_key, nestedValue) => typeof nestedValue === "bigint" ? nestedValue.toString() : nestedValue);
21286
- if (serialized === undefined)
21287
- return null;
21288
- return JSON.parse(serialized);
21289
- };
21290
- var emitCommandResult = (data) => {
21291
- const session = requireSession();
21292
- return emitCliSuccess(session.runtime, session.invocation, toJsonValue(data));
19628
+ // src/commands/index.ts
19629
+ import { randomUUID as randomUUID2 } from "crypto";
19630
+ import { readFile as readFile2, writeFile } from "fs/promises";
19631
+ import { basename } from "path";
19632
+ import { createInterface as createInterface2 } from "readline/promises";
19633
+
19634
+ // ../db/src/generated/prisma/enums.ts
19635
+ var EstimationMode = {
19636
+ MICRO: "MICRO",
19637
+ MANPOWER_SPECIFIED: "MANPOWER_SPECIFIED"
21293
19638
  };
21294
- var emitCommandError = (error61) => {
21295
- const session = requireSession();
21296
- return emitCliError(session.runtime, session.invocation, error61);
19639
+ var ProjectStatus = {
19640
+ PITCH: "PITCH",
19641
+ WON: "WON",
19642
+ COMPLETED: "COMPLETED",
19643
+ RECONCILED: "RECONCILED",
19644
+ LOST: "LOST"
21297
19645
  };
21298
- var confirmCurrentCommand = async (input2) => {
21299
- const session = requireSession();
21300
- const effects = input2.effects ?? session.effects;
21301
- const unconfirmedEffects = effects.filter((effect) => !session.confirmedEffects.has(effect));
21302
- if (unconfirmedEffects.length === 0)
21303
- return;
21304
- await confirmSensitiveCommand(session.runtime, {
21305
- action: input2.action,
21306
- target: input2.target,
21307
- details: input2.details,
21308
- effects: unconfirmedEffects,
21309
- permissions: session.permissions
21310
- });
21311
- for (const effect of unconfirmedEffects) {
21312
- session.confirmedEffects.add(effect);
21313
- }
19646
+ var BudgetStatus = {
19647
+ DRAFT: "DRAFT",
19648
+ PENDING_APPROVAL: "PENDING_APPROVAL",
19649
+ APPROVED: "APPROVED",
19650
+ REJECTED: "REJECTED",
19651
+ ESTIMATE_CREATED: "ESTIMATE_CREATED",
19652
+ ESTIMATE_SENT: "ESTIMATE_SENT",
19653
+ ESTIMATE_ACCEPTED: "ESTIMATE_ACCEPTED",
19654
+ ESTIMATE_REJECTED: "ESTIMATE_REJECTED",
19655
+ ESTIMATE_CLOSED: "ESTIMATE_CLOSED"
21314
19656
  };
21315
- // src/commands/invoice-splits.ts
21316
- var invoiceSplitSetupNextAction = (budgetId) => ({
21317
- prompt: "Choose invoice split amounts and each payment due date, then explicitly create the invoices. Non-admin creation requests admin approval; admin creation auto-approves. Send separately after approval.",
21318
- reviewCommand: `bb customer-invoice splits ${budgetId}`,
21319
- previewCommand: "bb customer-invoice preview-splits --payload '<reviewed-splits-json>'",
21320
- saveCommand: "bb customer-invoice save-splits --payload '<reviewed-splits-json>'",
21321
- createCommand: "bb customer-invoice create-splits --payload '<budgetId-expectedRevision-instalmentIds-json>'"
21322
- });
21323
- var parseInvoiceSplitSelection = (raw) => {
21324
- const input2 = paymentScheduleExecutionInput.strict().parse(raw);
21325
- if (input2.send) {
21326
- throw new Error("Creation and sending are separate actions. Use customer-invoice send-group after approval.");
21327
- }
21328
- if (new Set(input2.instalmentIds).size !== input2.instalmentIds.length) {
21329
- throw new Error("Select each instalment only once.");
21330
- }
21331
- return {
21332
- budgetId: input2.budgetId,
21333
- expectedRevision: input2.expectedRevision,
21334
- instalmentIds: input2.instalmentIds
21335
- };
19657
+ var ApprovalStatus = {
19658
+ PENDING_APPROVAL: "PENDING_APPROVAL",
19659
+ APPROVED: "APPROVED",
19660
+ REJECTED: "REJECTED",
19661
+ SUPERSEDED: "SUPERSEDED"
21336
19662
  };
21337
- var getCustomerInvoiceSplits = async (budgetId) => {
21338
- emitCommandResult(await api2.paymentSchedule.get.query({ budgetId }));
19663
+ var ApprovalType = {
19664
+ BUDGET: "BUDGET",
19665
+ SUPPLIER: "SUPPLIER",
19666
+ BILL: "BILL",
19667
+ CUSTOMER_INVOICE: "CUSTOMER_INVOICE",
19668
+ QUOTATION: "QUOTATION"
21339
19669
  };
21340
- var previewCustomerInvoiceSplits = async (raw) => {
21341
- const input2 = paymentScheduleSaveInput.strict().parse(raw);
21342
- emitCommandResult(await api2.paymentSchedule.preview.query(input2));
19670
+ var ErrorSeverity = {
19671
+ LOW: "LOW",
19672
+ MEDIUM: "MEDIUM",
19673
+ HIGH: "HIGH",
19674
+ CRITICAL: "CRITICAL"
21343
19675
  };
21344
- var saveCustomerInvoiceSplits = async (raw) => {
21345
- const input2 = paymentScheduleSaveInput.strict().parse(raw);
21346
- const preview = await api2.paymentSchedule.preview.query(input2);
21347
- await confirmCurrentCommand({
21348
- action: "Save invoice splits and contractual due dates",
21349
- target: `budget ${input2.budgetId}`,
21350
- details: `${preview.printedTerms}; ${preview.changes.join("; ")}. Saves the split setup only; does not create QBO invoices or request invoice approval.`
21351
- });
21352
- emitCommandResult(await api2.paymentSchedule.save.mutate(input2));
19676
+ var ErrorStatus = {
19677
+ UNRESOLVED: "UNRESOLVED",
19678
+ INVESTIGATING: "INVESTIGATING",
19679
+ RESOLVED: "RESOLVED",
19680
+ IGNORED: "IGNORED"
21353
19681
  };
21354
- var createCustomerInvoiceSplits = async (raw) => {
21355
- const input2 = parseInvoiceSplitSelection(raw);
21356
- const current = await api2.paymentSchedule.get.query({
21357
- budgetId: input2.budgetId
21358
- });
21359
- if (current.schedule?.revision !== input2.expectedRevision) {
21360
- throw new Error("Invoice split revision changed; refresh and review it again.");
21361
- }
21362
- const rows = current.schedule.instalments.filter((row) => input2.instalmentIds.includes(row.id));
21363
- if (rows.length !== input2.instalmentIds.length) {
21364
- throw new Error("Every selected instalment must belong to this budget.");
21365
- }
21366
- await confirmCurrentCommand({
21367
- action: "Create split invoices in QuickBooks",
21368
- target: `budget ${input2.budgetId}, revision ${input2.expectedRevision}`,
21369
- details: `${rows.map((row) => `${row.label}: ${row.totalCents === null ? "unresolved" : `SGD ${(row.totalCents / 100).toFixed(2)}`} including GST, due ${row.dueDate?.toISOString().slice(0, 10) ?? "unresolved"}`).join("; ")}. Non-admin creation requests admin approval after QBO creation; admin creation is automatically approved. Does not send invoices to the customer.`
21370
- });
21371
- emitCommandResult(await api2.paymentSchedule.execute.mutate({ ...input2, send: false }));
19682
+ var CustomerInvoiceBatchStatus = {
19683
+ CREATING: "CREATING",
19684
+ PENDING_APPROVAL: "PENDING_APPROVAL",
19685
+ APPROVED: "APPROVED",
19686
+ REJECTED_VOIDED: "REJECTED_VOIDED",
19687
+ EXPIRED_VOIDED: "EXPIRED_VOIDED",
19688
+ PARTIAL_QBO_FAILURE: "PARTIAL_QBO_FAILURE"
21372
19689
  };
21373
- var sendCustomerInvoiceGroup = async (raw, allApproved = false) => {
21374
- const selection = allApproved ? paymentScheduleExecutionInput.omit({ instalmentIds: true, send: true }).strict().parse(raw) : parseInvoiceSplitSelection(raw);
21375
- const current = await api2.paymentSchedule.get.query({
21376
- budgetId: selection.budgetId
21377
- });
21378
- if (current.schedule?.revision !== selection.expectedRevision) {
21379
- throw new Error("Invoice split revision changed; refresh and review it again.");
21380
- }
21381
- const selectedIds = "instalmentIds" in selection ? selection.instalmentIds : current.schedule.instalments.filter((row) => row.status === "CREATED" && row.invoice?.status === "APPROVED" && !row.invoice.sentAt).map((row) => row.id);
21382
- const input2 = parseInvoiceSplitSelection({
21383
- budgetId: selection.budgetId,
21384
- expectedRevision: selection.expectedRevision,
21385
- instalmentIds: selectedIds
21386
- });
21387
- const rows = current.schedule.instalments.filter((row) => input2.instalmentIds.includes(row.id));
21388
- if (rows.length !== input2.instalmentIds.length || rows.some((row) => row.invoice?.status !== "APPROVED" || row.invoice.sentAt)) {
21389
- throw new Error("Select only approved, unsent invoices from this budget.");
21390
- }
21391
- const billing = rows[0]?.invoice?.deliveryBilling;
21392
- if (billing?.deliveryMethod !== "EMAIL" || !billing.to) {
21393
- throw new Error("Review the invoice email recipient before sending.");
21394
- }
21395
- await confirmCurrentCommand({
21396
- action: "Send approved invoices together",
21397
- target: `${rows.length} invoice(s) for budget ${input2.budgetId}`,
21398
- details: `One email to ${billing.to} with PDFs for ${rows.map((row) => row.invoice?.qboDocNumber ?? row.label).join(", ")}; requested CC ${billing.cc.join(", ") || "none"}; reply-to ${billing.replyTo}; required business CC recipients are added by the server. Approval alone never sends. Do not retry blindly after an uncertain delivery.`
21399
- });
21400
- emitCommandResult(await api2.paymentSchedule.send.mutate(input2));
19690
+ var BillStatus = {
19691
+ DRAFT: "DRAFT",
19692
+ PENDING_APPROVAL: "PENDING_APPROVAL",
19693
+ CHECKED: "CHECKED",
19694
+ APPROVED: "APPROVED",
19695
+ PAID: "PAID",
19696
+ REJECTED: "REJECTED"
21401
19697
  };
21402
19698
 
21403
- // src/commands/index.ts
21404
- import { randomUUID as randomUUID2 } from "crypto";
21405
- import { readFile as readFile2, writeFile } from "fs/promises";
21406
- import { basename } from "path";
21407
- import { createInterface as createInterface2 } from "readline/promises";
21408
-
21409
19699
  // ../utils/src/filter-enums.ts
21410
19700
  var ExtendedApprovalStatus = {
21411
19701
  ...ApprovalStatus,
@@ -21458,6 +19748,13 @@ var ExtendedErrorStatus = {
21458
19748
  ...ErrorStatus
21459
19749
  };
21460
19750
 
19751
+ // src/commands/invoice-splits.ts
19752
+ var invoiceSplitSetupNextAction = (budgetId) => ({
19753
+ prompt: "Configure project invoice settings, then supply a due date for each invoice. Create the setting-derived invoices together. Non-admin creation requests admin approval; admin creation auto-approves. Sending remains separate.",
19754
+ reviewCommand: `bb customer-invoice readiness check ${budgetId}`,
19755
+ createCommand: "bb customer-invoice create --payload '<budgetId-and-splits-with-dueDates-json>'"
19756
+ });
19757
+
21461
19758
  // ../utils/src/customer-invoice-lifecycle.ts
21462
19759
  var customerInvoiceLifecycleStatuses = [
21463
19760
  ...Object.values(CustomerInvoiceBatchStatus),
@@ -21832,10 +20129,142 @@ var normalizePlainTextRichTextField = ({
21832
20129
  if (isRichTextJson(input2[richTextField]) || input2[textField] === undefined) {
21833
20130
  return input2;
21834
20131
  }
21835
- return {
21836
- ...input2,
21837
- [richTextField]: createRichTextFromPlainText(String(input2[textField] ?? ""))
21838
- };
20132
+ return {
20133
+ ...input2,
20134
+ [richTextField]: createRichTextFromPlainText(String(input2[textField] ?? ""))
20135
+ };
20136
+ };
20137
+
20138
+ // src/runtime/error.ts
20139
+ var invocationErrorCodes = new Set([
20140
+ "USAGE",
20141
+ "VALIDATION",
20142
+ "CONFIRMATION_REQUIRED"
20143
+ ]);
20144
+ var trpcCodeMap = {
20145
+ BAD_REQUEST: "VALIDATION",
20146
+ CONFLICT: "CONFLICT",
20147
+ FORBIDDEN: "AUTHORIZATION",
20148
+ INTERNAL_SERVER_ERROR: "API",
20149
+ METHOD_NOT_SUPPORTED: "API",
20150
+ NOT_FOUND: "NOT_FOUND",
20151
+ PARSE_ERROR: "VALIDATION",
20152
+ PAYLOAD_TOO_LARGE: "VALIDATION",
20153
+ PRECONDITION_FAILED: "CONFLICT",
20154
+ TIMEOUT: "NETWORK",
20155
+ TOO_MANY_REQUESTS: "API",
20156
+ UNAUTHORIZED: "AUTHENTICATION",
20157
+ UNPROCESSABLE_CONTENT: "VALIDATION"
20158
+ };
20159
+ var cliErrorCodes = new Set([
20160
+ "USAGE",
20161
+ "VALIDATION",
20162
+ "CONFIRMATION_REQUIRED",
20163
+ "AUTHENTICATION",
20164
+ "AUTHORIZATION",
20165
+ "NOT_FOUND",
20166
+ "CONFLICT",
20167
+ "API",
20168
+ "NETWORK",
20169
+ "ABORTED",
20170
+ "INTERRUPTED",
20171
+ "INTERNAL"
20172
+ ]);
20173
+
20174
+ class CliRuntimeError extends Error {
20175
+ code;
20176
+ details;
20177
+ requestId;
20178
+ constructor(code, message, options = {}) {
20179
+ super(message);
20180
+ this.name = "CliRuntimeError";
20181
+ this.code = code;
20182
+ this.details = options.details;
20183
+ this.requestId = options.requestId;
20184
+ }
20185
+ }
20186
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
20187
+ var asNonEmptyString = (value) => typeof value === "string" && value.trim() !== "" ? value : undefined;
20188
+ var asDetails = (value) => {
20189
+ if (!isRecord(value))
20190
+ return;
20191
+ const details = {};
20192
+ for (const [key, nestedValue] of Object.entries(value)) {
20193
+ if (isJsonValue(nestedValue))
20194
+ details[key] = nestedValue;
20195
+ }
20196
+ return Object.keys(details).length > 0 ? details : undefined;
20197
+ };
20198
+ var isJsonValue = (value) => {
20199
+ if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
20200
+ return true;
20201
+ }
20202
+ if (Array.isArray(value))
20203
+ return value.every(isJsonValue);
20204
+ return isRecord(value) && Object.values(value).every(isJsonValue);
20205
+ };
20206
+ var mapErrorCode = (value) => {
20207
+ if (typeof value !== "string")
20208
+ return;
20209
+ if (cliErrorCodes.has(value))
20210
+ return value;
20211
+ if (value in trpcCodeMap)
20212
+ return trpcCodeMap[value];
20213
+ return;
20214
+ };
20215
+ var networkMessagePattern = /\b(fetch failed|network error|unable to connect|econnrefused|econnreset|enotfound|etimedout|socket hang up)\b/i;
20216
+ var isNetworkError = (error61, depth = 0) => {
20217
+ if (depth > 3 || !isRecord(error61))
20218
+ return false;
20219
+ const message = asNonEmptyString(error61.message);
20220
+ if (message !== undefined && networkMessagePattern.test(message))
20221
+ return true;
20222
+ const code = asNonEmptyString(error61.code);
20223
+ if (code !== undefined && networkMessagePattern.test(code))
20224
+ return true;
20225
+ return isNetworkError(error61.cause, depth + 1);
20226
+ };
20227
+ var normalizeCliError = (error61) => {
20228
+ if (error61 instanceof CliRuntimeError) {
20229
+ return {
20230
+ code: error61.code,
20231
+ message: error61.message,
20232
+ ...error61.details === undefined ? {} : { details: error61.details },
20233
+ ...error61.requestId === undefined ? {} : { requestId: error61.requestId }
20234
+ };
20235
+ }
20236
+ if (isNetworkError(error61)) {
20237
+ return {
20238
+ code: "NETWORK",
20239
+ message: error61 instanceof Error ? error61.message : "The Budget Builder API could not be reached."
20240
+ };
20241
+ }
20242
+ if (isRecord(error61)) {
20243
+ const data = isRecord(error61.data) ? error61.data : undefined;
20244
+ const transportCode = mapErrorCode(data?.code ?? error61.code);
20245
+ if (error61 instanceof Error && data === undefined && transportCode === undefined) {
20246
+ return { code: "INTERNAL", message: error61.message };
20247
+ }
20248
+ const code = transportCode ?? "API";
20249
+ const message = asNonEmptyString(error61.message) ?? "The Budget Builder API request failed.";
20250
+ const requestId = asNonEmptyString(data?.requestId) ?? asNonEmptyString(data?.requestID) ?? asNonEmptyString(error61.requestId);
20251
+ const details = asDetails(data?.details ?? error61.details);
20252
+ return {
20253
+ code,
20254
+ message,
20255
+ ...details === undefined ? {} : { details },
20256
+ ...requestId === undefined ? {} : { requestId }
20257
+ };
20258
+ }
20259
+ if (error61 instanceof Error) {
20260
+ return { code: "INTERNAL", message: error61.message };
20261
+ }
20262
+ return { code: "INTERNAL", message: "An unexpected CLI error occurred." };
20263
+ };
20264
+ var exitCodeForCliError = (error61) => {
20265
+ if (error61.code === "INTERRUPTED")
20266
+ return 130;
20267
+ return invocationErrorCodes.has(error61.code) ? 2 : 1;
21839
20268
  };
21840
20269
 
21841
20270
  // src/parse-mutation-payload.ts
@@ -21894,7 +20323,7 @@ var parseQuotationReferences = (input2, commandName) => {
21894
20323
  if (!Array.isArray(rawQuotationIds)) {
21895
20324
  throw new Error(`${commandName} payload.quotationIds must be a string array.`);
21896
20325
  }
21897
- quotationIds = rawQuotationIds.map((id2, index) => requiredString(id2, `${commandName} payload.quotationIds[${index}]`));
20326
+ quotationIds = rawQuotationIds.map((id, index) => requiredString(id, `${commandName} payload.quotationIds[${index}]`));
21898
20327
  if (new Set(quotationIds).size !== quotationIds.length) {
21899
20328
  throw new Error(`${commandName} payload.quotationIds must not contain duplicates.`);
21900
20329
  }
@@ -22047,9 +20476,9 @@ function parseCreateBudgetPayload(raw) {
22047
20476
  }
22048
20477
  function parseUpdateBudgetPayload(raw) {
22049
20478
  const o = requireObject(raw, "update-budget payload");
22050
- const id2 = requiredString(o.id, "update-budget payload.id");
20479
+ const id = requiredString(o.id, "update-budget payload.id");
22051
20480
  const base = parseCreateBudgetPayload(raw);
22052
- return { ...base, id: id2 };
20481
+ return { ...base, id };
22053
20482
  }
22054
20483
  function parseUpdateBudgetCommissionPayload(raw) {
22055
20484
  const o = requireObject(raw, "update-budget-commission payload");
@@ -22141,7 +20570,7 @@ function parseCreateBillPayload(raw, draftOnlyFlag, quotationExempt = false, req
22141
20570
  throw new Error("create-bill payload.amount must be a number >= 0.");
22142
20571
  }
22143
20572
  const quotationReferences = parseQuotationReferences(o, "create-bill");
22144
- const parsedBudgetItemIds = budgetItemIds.map((id2, i) => requiredString(id2, `create-bill payload.budgetItemIds[${i}]`));
20573
+ const parsedBudgetItemIds = budgetItemIds.map((id, i) => requiredString(id, `create-bill payload.budgetItemIds[${i}]`));
22145
20574
  const lineAmounts = parseBillLineAmounts(o, "create-bill");
22146
20575
  validateExplicitBillAllocations({
22147
20576
  amount,
@@ -22213,7 +20642,7 @@ function parseValidateBillSelectionPayload(raw, quotationFlags = {}) {
22213
20642
  throw new Error("Employee claims cannot include quotation source inputs.");
22214
20643
  }
22215
20644
  const lineAmounts = parseBillLineAmounts(input2, "validate-bill-selection");
22216
- const budgetItemIds = Array.isArray(input2.budgetItemIds) ? input2.budgetItemIds.map((id2, index) => requiredString(id2, `validate-bill-selection payload.budgetItemIds[${index}]`)) : [];
20645
+ const budgetItemIds = Array.isArray(input2.budgetItemIds) ? input2.budgetItemIds.map((id, index) => requiredString(id, `validate-bill-selection payload.budgetItemIds[${index}]`)) : [];
22217
20646
  const amount = input2.amount === undefined || input2.amount === null ? undefined : Number(input2.amount);
22218
20647
  if (lineAmounts || payloadReferences.quotationAllocations) {
22219
20648
  if (amount === undefined || !Number.isFinite(amount) || budgetItemIds.length === 0) {
@@ -22258,7 +20687,7 @@ function parseUpdateBillPaymentEvidencePayload(raw) {
22258
20687
  var validateQuotationAllocations = (raw, command) => {
22259
20688
  const input2 = requireObject(raw, `${command} payload`);
22260
20689
  const ids = input2.budgetItemIds;
22261
- if (!Array.isArray(ids) || ids.length === 0 || ids.some((id2) => typeof id2 !== "string" || id2.length === 0) || new Set(ids).size !== ids.length) {
20690
+ if (!Array.isArray(ids) || ids.length === 0 || ids.some((id) => typeof id !== "string" || id.length === 0) || new Set(ids).size !== ids.length) {
22262
20691
  throw new CliRuntimeError("VALIDATION", `${command} payload.budgetItemIds must contain each selected budget-item ID exactly once.`);
22263
20692
  }
22264
20693
  const isCurrency = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 99999999.99 && Math.abs(value * 100 - Math.round(value * 100)) < 0.00000001;
@@ -22293,7 +20722,7 @@ function parseUpdateQuotationPayload(raw) {
22293
20722
  validateQuotationAllocations(raw, "update-quotation");
22294
20723
  return raw;
22295
20724
  }
22296
- function parseCreateCustomerInvoicePayload(raw) {
20725
+ var parseCreateCustomerInvoicePayload = (raw) => {
22297
20726
  const input2 = requireObject(raw, "create-customer-invoice payload");
22298
20727
  if (!Array.isArray(input2.splits) || input2.splits.length === 0) {
22299
20728
  throw new Error("create-customer-invoice payload.splits must be a non-empty array.");
@@ -22309,7 +20738,7 @@ function parseCreateCustomerInvoicePayload(raw) {
22309
20738
  };
22310
20739
  })
22311
20740
  };
22312
- }
20741
+ };
22313
20742
  function parseUpdateProjectPayload(raw) {
22314
20743
  const o = requireObject(raw, "update-project payload");
22315
20744
  const dr = o.dateRange;
@@ -22401,6 +20830,206 @@ function parseCompanyUpdatePayload(raw) {
22401
20830
  return raw;
22402
20831
  }
22403
20832
 
20833
+ // src/runtime/confirmation.ts
20834
+ var effectLabels = {
20835
+ "state-change": "changes Budget Builder state",
20836
+ email: "sends email",
20837
+ "external-write": "writes to an external system",
20838
+ delete: "deletes data",
20839
+ "financial-write": "creates or changes financial records"
20840
+ };
20841
+ var isEffectAllowed = (effect, permissions) => {
20842
+ switch (effect) {
20843
+ case "state-change":
20844
+ return permissions.allowStateChange === true;
20845
+ case "email":
20846
+ return permissions.allowEmail === true;
20847
+ case "external-write":
20848
+ return permissions.allowExternalWrite === true;
20849
+ case "delete":
20850
+ return permissions.allowDelete === true;
20851
+ case "financial-write":
20852
+ return permissions.allowFinancialWrite === true;
20853
+ }
20854
+ };
20855
+ var uniqueEffects = (effects) => [...new Set(effects)];
20856
+ var getRequiredEffectPermissions = (effects, permissions) => uniqueEffects(effects).filter((effect) => !isEffectAllowed(effect, permissions));
20857
+ var formatConfirmationPreview = (request) => [
20858
+ "Budget Builder workflow preview",
20859
+ `Action: ${request.action}`,
20860
+ `Target: ${request.target?.trim() || "not specified"}`,
20861
+ `Effects: ${uniqueEffects(request.effects).map((effect) => effectLabels[effect]).join("; ") || "none"}`,
20862
+ ...request.details === undefined ? [] : [`Details: ${request.details}`]
20863
+ ].join(`
20864
+ `);
20865
+ var confirmSensitiveCommand = async (runtime2, request) => {
20866
+ const requiredPermissions = getRequiredEffectPermissions(request.effects, request.permissions);
20867
+ if (request.effects.length === 0) {
20868
+ return { confirmed: true, requiredPermissions };
20869
+ }
20870
+ const target = request.target?.trim();
20871
+ if (!runtime2.isTTY) {
20872
+ if (target === undefined) {
20873
+ throw new CliRuntimeError("CONFIRMATION_REQUIRED", "Non-interactive sensitive commands require an explicit target.", {
20874
+ details: {
20875
+ action: request.action,
20876
+ effects: requiredPermissions,
20877
+ missingFlags: requiredPermissions.map((effect) => `--allow-${effect}`),
20878
+ target: "required"
20879
+ }
20880
+ });
20881
+ }
20882
+ if (requiredPermissions.length === 0) {
20883
+ return { confirmed: true, requiredPermissions };
20884
+ }
20885
+ const missing = requiredPermissions.map((effect) => `--allow-${effect}`);
20886
+ throw new CliRuntimeError("CONFIRMATION_REQUIRED", "Non-interactive sensitive commands require an explicit target and all applicable allow flags.", {
20887
+ details: {
20888
+ action: request.action,
20889
+ effects: requiredPermissions,
20890
+ missingFlags: missing
20891
+ }
20892
+ });
20893
+ }
20894
+ if (requiredPermissions.length === 0) {
20895
+ return { confirmed: true, requiredPermissions };
20896
+ }
20897
+ const preview = formatConfirmationPreview(request);
20898
+ runtime2.stderr.write(`${preview}
20899
+ `);
20900
+ if (runtime2.prompt === undefined) {
20901
+ throw new CliRuntimeError("CONFIRMATION_REQUIRED", "This command needs an interactive confirmation prompt.");
20902
+ }
20903
+ const answer = await runtime2.prompt("Type CONFIRM to continue: ");
20904
+ if (answer !== "CONFIRM") {
20905
+ throw new CliRuntimeError("ABORTED", "Command aborted by user.");
20906
+ }
20907
+ return { confirmed: true, preview, requiredPermissions };
20908
+ };
20909
+ // src/runtime/sanitize.ts
20910
+ var sensitiveKeys = new Set([
20911
+ "apikey",
20912
+ "authorization",
20913
+ "bb_api_key",
20914
+ "password",
20915
+ "paymentproof",
20916
+ "paymentproofattachments",
20917
+ "paymentproofpath",
20918
+ "paymentreference",
20919
+ "payload",
20920
+ "proof",
20921
+ "receipt",
20922
+ "secret",
20923
+ "token"
20924
+ ]);
20925
+
20926
+ // src/runtime/output.ts
20927
+ var serialize = (value) => `${JSON.stringify(value, null, 2)}
20928
+ `;
20929
+ var createSuccessEnvelope = (data, invocation) => ({
20930
+ ok: true,
20931
+ data,
20932
+ meta: {
20933
+ command: invocation.command,
20934
+ cliVersion: invocation.cliVersion,
20935
+ apiBaseUrl: invocation.apiBaseUrl
20936
+ }
20937
+ });
20938
+ var emitCliSuccess = (runtime2, invocation, data) => {
20939
+ runtime2.stdout.write(serialize(invocation.legacy ? data : createSuccessEnvelope(data, invocation)));
20940
+ return 0;
20941
+ };
20942
+ var emitCliError = (runtime2, invocation, error61) => {
20943
+ const normalized = normalizeCliError(error61);
20944
+ if (invocation.legacy) {
20945
+ runtime2.stderr.write(serialize({ error: normalized.message }));
20946
+ } else {
20947
+ const envelope = {
20948
+ ok: false,
20949
+ error: normalized,
20950
+ meta: { command: invocation.command }
20951
+ };
20952
+ runtime2.stderr.write(serialize(envelope));
20953
+ }
20954
+ return exitCodeForCliError(normalized);
20955
+ };
20956
+ // src/runtime/process-runtime.ts
20957
+ import { createInterface } from "readline/promises";
20958
+ var createProcessRuntime = () => ({
20959
+ stdout: {
20960
+ write: (value) => {
20961
+ process.stdout.write(value);
20962
+ }
20963
+ },
20964
+ stderr: {
20965
+ write: (value) => {
20966
+ process.stderr.write(value);
20967
+ }
20968
+ },
20969
+ isTTY: Boolean(process.stdin.isTTY && process.stderr.isTTY),
20970
+ env: process.env,
20971
+ prompt: async (message) => {
20972
+ const prompt = createInterface({
20973
+ input: process.stdin,
20974
+ output: process.stderr
20975
+ });
20976
+ try {
20977
+ return await prompt.question(message);
20978
+ } finally {
20979
+ prompt.close();
20980
+ }
20981
+ },
20982
+ now: () => new Date
20983
+ });
20984
+ // src/runtime/session.ts
20985
+ var activeSession;
20986
+ var setActiveCliSession = (session) => {
20987
+ activeSession = {
20988
+ ...session,
20989
+ confirmedEffects: new Set
20990
+ };
20991
+ };
20992
+ var clearActiveCliSession = () => {
20993
+ activeSession = undefined;
20994
+ };
20995
+ var getActiveCliSession = () => activeSession;
20996
+ var requireSession = () => {
20997
+ if (activeSession === undefined) {
20998
+ throw new Error("Budget Builder CLI runtime session is not configured.");
20999
+ }
21000
+ return activeSession;
21001
+ };
21002
+ var toJsonValue = (value) => {
21003
+ const serialized = JSON.stringify(value, (_key, nestedValue) => typeof nestedValue === "bigint" ? nestedValue.toString() : nestedValue);
21004
+ if (serialized === undefined)
21005
+ return null;
21006
+ return JSON.parse(serialized);
21007
+ };
21008
+ var emitCommandResult = (data) => {
21009
+ const session = requireSession();
21010
+ return emitCliSuccess(session.runtime, session.invocation, toJsonValue(data));
21011
+ };
21012
+ var emitCommandError = (error61) => {
21013
+ const session = requireSession();
21014
+ return emitCliError(session.runtime, session.invocation, error61);
21015
+ };
21016
+ var confirmCurrentCommand = async (input2) => {
21017
+ const session = requireSession();
21018
+ const effects = input2.effects ?? session.effects;
21019
+ const unconfirmedEffects = effects.filter((effect) => !session.confirmedEffects.has(effect));
21020
+ if (unconfirmedEffects.length === 0)
21021
+ return;
21022
+ await confirmSensitiveCommand(session.runtime, {
21023
+ action: input2.action,
21024
+ target: input2.target,
21025
+ details: input2.details,
21026
+ effects: unconfirmedEffects,
21027
+ permissions: session.permissions
21028
+ });
21029
+ for (const effect of unconfirmedEffects) {
21030
+ session.confirmedEffects.add(effect);
21031
+ }
21032
+ };
22404
21033
  // src/commands/index.ts
22405
21034
  import { contentType } from "mime-types";
22406
21035
  var BUDGET = "BUDGET";
@@ -22628,7 +21257,7 @@ var combineDateAndTime = (day, time3) => {
22628
21257
  d.setHours(time3.getHours(), time3.getMinutes(), time3.getSeconds(), 0);
22629
21258
  return d;
22630
21259
  };
22631
- var formatOptionalProjectPaxInput2 = (pax) => {
21260
+ var formatOptionalProjectPaxInput = (pax) => {
22632
21261
  if (typeof pax !== "number" || !Number.isFinite(pax) || pax <= 0) {
22633
21262
  return "";
22634
21263
  }
@@ -22660,7 +21289,7 @@ var billAttachmentContentTypeForFileName = (fileName) => {
22660
21289
  return resolvedContentType;
22661
21290
  };
22662
21291
  var PAYMENT_PROOF_MAX_SIZE = 20 * 1024 * 1024;
22663
- var QUOTATION_ATTACHMENT_MAX_SIZE2 = 20 * 1024 * 1024;
21292
+ var QUOTATION_ATTACHMENT_MAX_SIZE = 20 * 1024 * 1024;
22664
21293
  var quotationAttachmentContentTypeForFileName = (fileName) => {
22665
21294
  const resolvedContentType = contentTypeHeaderForFileName(fileName);
22666
21295
  if (resolvedContentType === "application/pdf" || resolvedContentType === "image/jpeg" || resolvedContentType === "image/png") {
@@ -22727,31 +21356,31 @@ Confirm the user explicitly approved this exact action, then type "${expected}"
22727
21356
  }
22728
21357
  };
22729
21358
  var assertLockedBudgetChangeConfirmed = async ({
22730
- budget: budget2,
21359
+ budget,
22731
21360
  action
22732
21361
  }) => {
22733
- if (!LOCKED_BUDGET_STATUSES_REQUIRING_CONFIRMATION.has(budget2.status)) {
21362
+ if (!LOCKED_BUDGET_STATUSES_REQUIRING_CONFIRMATION.has(budget.status)) {
22734
21363
  return;
22735
21364
  }
22736
- const statusLabel = lockedBudgetStatusLabel(budget2.status);
21365
+ const statusLabel = lockedBudgetStatusLabel(budget.status);
22737
21366
  if (getActiveCliSession()?.invocation.legacy !== true) {
22738
21367
  await confirmCurrentCommand({
22739
21368
  action,
22740
- target: `budget ${budget2.id}`,
22741
- details: `Changes locked budget "${budget2.name}" in status ${statusLabel}.`,
21369
+ target: `budget ${budget.id}`,
21370
+ details: `Changes locked budget "${budget.name}" in status ${statusLabel}.`,
22742
21371
  effects: ["state-change"]
22743
21372
  });
22744
21373
  return;
22745
21374
  }
22746
21375
  if (!process.stdin.isTTY || !process.stderr.isTTY) {
22747
- throw new Error(`${action} would change locked budget "${budget2.name}" (${budget2.id}) with status ${statusLabel}. Run this command in an interactive terminal and type "yes" to confirm.`);
21376
+ throw new Error(`${action} would change locked budget "${budget.name}" (${budget.id}) with status ${statusLabel}. Run this command in an interactive terminal and type "yes" to confirm.`);
22748
21377
  }
22749
21378
  const rl = createInterface2({
22750
21379
  input: process.stdin,
22751
21380
  output: process.stderr
22752
21381
  });
22753
21382
  try {
22754
- const answer = await rl.question(`${action} will change locked budget "${budget2.name}" (${budget2.id}) with status ${statusLabel}. Type "yes" to continue: `);
21383
+ const answer = await rl.question(`${action} will change locked budget "${budget.name}" (${budget.id}) with status ${statusLabel}. Type "yes" to continue: `);
22755
21384
  if (answer.trim().toLowerCase() !== "yes") {
22756
21385
  throw new Error("Aborted.");
22757
21386
  }
@@ -22760,21 +21389,21 @@ var assertLockedBudgetChangeConfirmed = async ({
22760
21389
  }
22761
21390
  };
22762
21391
  var confirmLockedBudgetChangeByBudgetId = async (budgetId, action) => {
22763
- const budget2 = await api2.budget.getBudget.query({ id: budgetId });
21392
+ const budget = await api2.budget.getBudget.query({ id: budgetId });
22764
21393
  await assertLockedBudgetChangeConfirmed({
22765
21394
  budget: {
22766
- id: budget2.id,
22767
- name: budget2.name,
22768
- status: budget2.status
21395
+ id: budget.id,
21396
+ name: budget.name,
21397
+ status: budget.status
22769
21398
  },
22770
21399
  action
22771
21400
  });
22772
21401
  };
22773
21402
  var confirmLockedBudgetChangeByBudgetItemId = async (budgetItemId, action) => {
22774
- const { budget: budget2 } = await api2.budgetItem.getBudgetItemContext.query({
21403
+ const { budget } = await api2.budgetItem.getBudgetItemContext.query({
22775
21404
  budgetItemId
22776
21405
  });
22777
- await assertLockedBudgetChangeConfirmed({ budget: budget2, action });
21406
+ await assertLockedBudgetChangeConfirmed({ budget, action });
22778
21407
  };
22779
21408
  var updateProjectWithEstimateInvalidationConfirmation = async (input2) => {
22780
21409
  try {
@@ -22811,12 +21440,12 @@ async function listBudgets(opts) {
22811
21440
  const result = await api2.budget.getAllBudgets.query(input2);
22812
21441
  out(result);
22813
21442
  }
22814
- async function getBudget(id2) {
22815
- const [budget2, items] = await Promise.all([
22816
- api2.budget.getBudget.query({ id: id2 }),
22817
- api2.budget.getBudgetItems.query({ budgetId: id2 })
21443
+ async function getBudget(id) {
21444
+ const [budget, items] = await Promise.all([
21445
+ api2.budget.getBudget.query({ id }),
21446
+ api2.budget.getBudgetItems.query({ budgetId: id })
22818
21447
  ]);
22819
- out({ budget: budget2, items });
21448
+ out({ budget, items });
22820
21449
  }
22821
21450
  async function updateBudgetStatus(budgetId, status, opts) {
22822
21451
  await assertSensitiveWorkflowConfirmed({
@@ -22851,21 +21480,21 @@ var assertBudgetActionReadiness = async ({
22851
21480
  requireQuickbooksProject,
22852
21481
  actionLabel
22853
21482
  }) => {
22854
- const budget2 = await api2.budget.getBudget.query({ id: budgetId });
22855
- const hasNoItems = budget2.budgetItems.length === 0;
21483
+ const budget = await api2.budget.getBudget.query({ id: budgetId });
21484
+ const hasNoItems = budget.budgetItems.length === 0;
22856
21485
  if (hasNoItems) {
22857
21486
  throw new Error(`Cannot ${actionLabel}: there are no line items in this budget.`);
22858
21487
  }
22859
- const hasZeroCostItems = budget2.budgetItems.some((item2) => Number(item2.cost ?? 0) === 0);
21488
+ const hasZeroCostItems = budget.budgetItems.some((item) => Number(item.cost ?? 0) === 0);
22860
21489
  if (hasZeroCostItems) {
22861
21490
  throw new Error(`Cannot ${actionLabel}: all line items must have a cost greater than zero.`);
22862
21491
  }
22863
- const inactiveSuppliers = budget2.budgetItems.filter((item2) => !item2.supplier.active).map((item2) => item2.supplier.name);
21492
+ const inactiveSuppliers = budget.budgetItems.filter((item) => !item.supplier.active).map((item) => item.supplier.name);
22864
21493
  const uniqueInactiveSuppliers = [...new Set(inactiveSuppliers)];
22865
21494
  if (uniqueInactiveSuppliers.length > 0) {
22866
21495
  throw new Error(`Cannot ${actionLabel}: inactive suppliers found (${uniqueInactiveSuppliers.join(", ")}).`);
22867
21496
  }
22868
- if (requireQuickbooksProject && !budget2.project.quickbooksProjectId) {
21497
+ if (requireQuickbooksProject && !budget.project.quickbooksProjectId) {
22869
21498
  throw new Error("Cannot create estimate: project is not linked to QuickBooks yet.");
22870
21499
  }
22871
21500
  };
@@ -22879,32 +21508,32 @@ var runBudgetPreflight = async (budgetId, preflight) => {
22879
21508
  await api2.contactPerson.updateContactPerson.mutate(preflight.contactPerson);
22880
21509
  }
22881
21510
  if (preflight.eventDetails) {
22882
- const budget2 = await api2.budget.getBudget.query({ id: budgetId });
22883
- const project2 = await api2.project.getProjectById.query({
22884
- id: budget2.projectId
21511
+ const budget = await api2.budget.getBudget.query({ id: budgetId });
21512
+ const project = await api2.project.getProjectById.query({
21513
+ id: budget.projectId
22885
21514
  });
22886
21515
  const day = new Date(preflight.eventDetails.date);
22887
21516
  const start = new Date(preflight.eventDetails.startTime);
22888
21517
  const end = new Date(preflight.eventDetails.endTime);
22889
21518
  const payload = {
22890
- id: project2.id,
22891
- name: project2.name,
22892
- description: project2.description ?? undefined,
21519
+ id: project.id,
21520
+ name: project.name,
21521
+ description: project.description ?? undefined,
22893
21522
  dateRange: {
22894
21523
  from: combineDateAndTime(day, start),
22895
21524
  to: combineDateAndTime(day, end)
22896
21525
  },
22897
- companyId: project2.companyId,
22898
- contactPersonId: project2.contactPersonId,
22899
- asanaTaskId: project2.asanaTaskId ?? "",
22900
- insideSalesId: project2.insideSalesId ?? "",
22901
- businessDevelopmentId: project2.businessDevelopmentId ?? "",
22902
- venue: project2.venue ?? "",
22903
- pax: formatOptionalProjectPaxInput2(project2.pax),
22904
- ...project2.slackChannelId && project2.slackChannelUrl && project2.slackChannelName ? {
22905
- slackChannelId: project2.slackChannelId,
22906
- slackChannelUrl: project2.slackChannelUrl,
22907
- slackChannelName: project2.slackChannelName
21526
+ companyId: project.companyId,
21527
+ contactPersonId: project.contactPersonId,
21528
+ asanaTaskId: project.asanaTaskId ?? "",
21529
+ insideSalesId: project.insideSalesId ?? "",
21530
+ businessDevelopmentId: project.businessDevelopmentId ?? "",
21531
+ venue: project.venue ?? "",
21532
+ pax: formatOptionalProjectPaxInput(project.pax),
21533
+ ...project.slackChannelId && project.slackChannelUrl && project.slackChannelName ? {
21534
+ slackChannelId: project.slackChannelId,
21535
+ slackChannelUrl: project.slackChannelUrl,
21536
+ slackChannelName: project.slackChannelName
22908
21537
  } : {}
22909
21538
  };
22910
21539
  await updateProjectWithEstimateInvalidationConfirmation(payload);
@@ -22965,31 +21594,31 @@ async function listApprovals(opts) {
22965
21594
  const typeFilter = opts.type === undefined || opts.type === "ALL" ? (_n) => true : (n) => n.type === opts.type;
22966
21595
  const filtered = pending.filter(typeFilter).filter((n) => n.type !== BUDGET || !n.budget?.quickbooksEstimateId);
22967
21596
  const items = filtered.map((n) => {
22968
- let id2;
21597
+ let id;
22969
21598
  let name;
22970
21599
  if (n.type === BUDGET && n.budget) {
22971
- id2 = n.budgetId ?? n.id;
21600
+ id = n.budgetId ?? n.id;
22972
21601
  name = n.budget.name;
22973
21602
  } else if (n.type === BILL && n.bill) {
22974
- id2 = n.bill.id;
21603
+ id = n.bill.id;
22975
21604
  name = `Bill ${n.bill.invoiceNumber ?? n.bill.id.slice(0, 8)}`;
22976
21605
  } else if (n.type === SUPPLIER && n.supplier) {
22977
- id2 = n.supplier.id;
21606
+ id = n.supplier.id;
22978
21607
  name = n.supplier.name;
22979
21608
  } else if (n.type === QUOTATION && n.supplierQuotation) {
22980
- id2 = n.supplierQuotation.id;
21609
+ id = n.supplierQuotation.id;
22981
21610
  name = `Quotation ${n.supplierQuotation.supplier.name}`;
22982
21611
  } else if (n.type === CUSTOMER_INVOICE && n.customerInvoiceBatch) {
22983
- id2 = n.customerInvoiceBatch.id;
21612
+ id = n.customerInvoiceBatch.id;
22984
21613
  name = `Customer invoice ${n.customerInvoiceBatch.project.name}`;
22985
21614
  } else {
22986
- id2 = n.id;
21615
+ id = n.id;
22987
21616
  name = "Unknown";
22988
21617
  }
22989
21618
  return {
22990
21619
  approvalId: n.id,
22991
21620
  type: n.type,
22992
- id: id2,
21621
+ id,
22993
21622
  name,
22994
21623
  requestedBy: n.requester?.name ?? n.requester?.email ?? "Unknown",
22995
21624
  requestedAt: typeof n.createdAt === "string" ? n.createdAt : n.createdAt instanceof Date ? n.createdAt.toISOString() : String(n.createdAt)
@@ -23018,7 +21647,7 @@ async function createBillApproval(billId) {
23018
21647
  details: "and send approval request emails"
23019
21648
  });
23020
21649
  const result = await api2.bill.createApproval.mutate({ billId });
23021
- const approvalIds = (result.results ?? []).map((item2) => ({ id: item2.id }));
21650
+ const approvalIds = (result.results ?? []).map((item) => ({ id: item.id }));
23022
21651
  let email3;
23023
21652
  if (approvalIds.length === 0) {
23024
21653
  email3 = createApprovalEmailDeliveryFailure("No approval ids returned");
@@ -23111,8 +21740,8 @@ async function updateBillStatus(opts) {
23111
21740
  entity: `bill ${opts.id}`,
23112
21741
  details: `to ${opts.status}`
23113
21742
  });
23114
- const bill2 = opts.paymentProofPath ? await api2.bill.getById.query({ id: opts.id }) : null;
23115
- const paymentProof = bill2 && opts.paymentProofPath ? await stageBillPaymentProof(bill2.projectId, opts.paymentProofPath) : undefined;
21743
+ const bill = opts.paymentProofPath ? await api2.bill.getById.query({ id: opts.id }) : null;
21744
+ const paymentProof = bill && opts.paymentProofPath ? await stageBillPaymentProof(bill.projectId, opts.paymentProofPath) : undefined;
23116
21745
  try {
23117
21746
  const result = await api2.bill.updateStatus.mutate({
23118
21747
  id: opts.id,
@@ -23125,8 +21754,8 @@ async function updateBillStatus(opts) {
23125
21754
  });
23126
21755
  out(result);
23127
21756
  } catch (error61) {
23128
- await rethrowAfterStagedPaymentProofCleanup(error61, bill2 && paymentProof ? async () => {
23129
- await deleteStagedPaymentProof(bill2.projectId, paymentProof.key);
21757
+ await rethrowAfterStagedPaymentProofCleanup(error61, bill && paymentProof ? async () => {
21758
+ await deleteStagedPaymentProof(bill.projectId, paymentProof.key);
23130
21759
  } : undefined);
23131
21760
  }
23132
21761
  }
@@ -23144,15 +21773,15 @@ async function patchBillPayment(opts) {
23144
21773
  const result = await api2.bill.patchPayment.mutate(input2);
23145
21774
  out(result);
23146
21775
  }
23147
- async function patchBillInvoiceNumber(id2, invoiceNumber) {
21776
+ async function patchBillInvoiceNumber(id, invoiceNumber) {
23148
21777
  const result = await api2.bill.patchInvoiceNumber.mutate({
23149
- id: id2,
21778
+ id,
23150
21779
  invoiceNumber
23151
21780
  });
23152
21781
  out(result);
23153
21782
  }
23154
- async function getBillAttachments(id2) {
23155
- const result = await api2.bill.getAttachments.query({ id: id2 });
21783
+ async function getBillAttachments(id) {
21784
+ const result = await api2.bill.getAttachments.query({ id });
23156
21785
  out(result);
23157
21786
  }
23158
21787
  async function stageBillAttachmentsFromPaths(projectId, filePaths) {
@@ -23268,7 +21897,7 @@ async function uploadBillDocumentsFromPaths({
23268
21897
  async function uploadQuotationAttachmentFromPath(projectId, filePath) {
23269
21898
  const buf = await readFile2(filePath);
23270
21899
  const fileName = basename(filePath);
23271
- if (buf.byteLength > QUOTATION_ATTACHMENT_MAX_SIZE2) {
21900
+ if (buf.byteLength > QUOTATION_ATTACHMENT_MAX_SIZE) {
23272
21901
  throw new Error("Quotation attachment must be 20MB or smaller.");
23273
21902
  }
23274
21903
  const attachmentContentType = quotationAttachmentContentTypeForFileName(fileName);
@@ -23381,8 +22010,8 @@ async function uploadBudgetWinProofFromPath(budgetId, filePath, opts) {
23381
22010
  async function markBudgetWonWithProof(budgetId, filePath, opts) {
23382
22011
  await uploadBudgetWinProofFromPath(budgetId, filePath, opts);
23383
22012
  }
23384
- async function getBillDetails(id2) {
23385
- const result = await api2.bill.getById.query({ id: id2 });
22013
+ async function getBillDetails(id) {
22014
+ const result = await api2.bill.getById.query({ id });
23386
22015
  out(result);
23387
22016
  }
23388
22017
  async function getBudgetItemsOnly(budgetId) {
@@ -23448,7 +22077,7 @@ async function createBudgetApproval(budgetId, preflight) {
23448
22077
  actionLabel: "request approval"
23449
22078
  });
23450
22079
  const result = await api2.budget.createBudgetApproval.mutate({ budgetId });
23451
- const approvalIds = (result.results ?? []).map((item2) => ({ id: item2.id }));
22080
+ const approvalIds = (result.results ?? []).map((item) => ({ id: item.id }));
23452
22081
  let email3;
23453
22082
  if (approvalIds.length === 0) {
23454
22083
  email3 = createApprovalEmailDeliveryFailure("No approval ids returned");
@@ -23533,8 +22162,8 @@ async function updateBudgetCategory(opts) {
23533
22162
  const result = await api2.budget.updateBudgetCategory.mutate(opts);
23534
22163
  out(result);
23535
22164
  }
23536
- async function deleteBudgetCategory(id2) {
23537
- const result = await api2.budget.deleteBudgetCategory.mutate({ id: id2 });
22165
+ async function deleteBudgetCategory(id) {
22166
+ const result = await api2.budget.deleteBudgetCategory.mutate({ id });
23538
22167
  out(result);
23539
22168
  }
23540
22169
  async function updateBudgetCommissionFromPayload(raw) {
@@ -23675,8 +22304,8 @@ async function listQuotations(opts) {
23675
22304
  const result = await api2.quotation.list.query(opts);
23676
22305
  out(result);
23677
22306
  }
23678
- async function getQuotationDetails(id2) {
23679
- const result = await api2.quotation.getById.query({ id: id2 });
22307
+ async function getQuotationDetails(id) {
22308
+ const result = await api2.quotation.getById.query({ id });
23680
22309
  out(result);
23681
22310
  }
23682
22311
  async function createQuotationFromPayload(raw) {
@@ -23689,24 +22318,24 @@ var updateQuotationFromPayload = async (raw) => {
23689
22318
  const result = await api2.quotation.updateDraftOrRejected.mutate(input2);
23690
22319
  out(result);
23691
22320
  };
23692
- var deleteQuotationById = async (id2) => {
23693
- const result = await api2.quotation.delete.mutate({ id: id2 });
22321
+ var deleteQuotationById = async (id) => {
22322
+ const result = await api2.quotation.delete.mutate({ id });
23694
22323
  out(result);
23695
22324
  };
23696
- async function submitQuotation(id2, overBudgetOverrideReason) {
22325
+ async function submitQuotation(id, overBudgetOverrideReason) {
23697
22326
  await assertSensitiveWorkflowConfirmed({
23698
22327
  action: "Submit quotation for approval",
23699
- entity: `quotation ${id2}`
22328
+ entity: `quotation ${id}`
23700
22329
  });
23701
22330
  const result = await api2.quotation.submitForApproval.mutate({
23702
- id: id2,
22331
+ id,
23703
22332
  overBudgetOverrideReason
23704
22333
  });
23705
22334
  out(result);
23706
22335
  }
23707
- async function downloadQuotationPdf(id2, fileRole) {
22336
+ async function downloadQuotationPdf(id, fileRole) {
23708
22337
  const result = await api2.quotation.getSignedDownloadUrl.query({
23709
- id: id2,
22338
+ id,
23710
22339
  fileRole
23711
22340
  });
23712
22341
  out(result);
@@ -23755,7 +22384,7 @@ var createCustomerInvoice = async (raw) => {
23755
22384
  await assertSensitiveWorkflowConfirmed({
23756
22385
  action: "Create customer invoice",
23757
22386
  entity: `budget ${input2.budgetId}`,
23758
- details: "creates one invoice in QuickBooks; admin-created invoices are approved immediately, while other invoices request approval; approved cumulative coverage of 100% closes the QuickBooks estimate"
22387
+ details: `creates ${input2.splits.length} invoice(s) in QuickBooks: ${input2.splits.map((split) => `${split.label} (${split.percentage}%), due ${new Date(split.dueDate).toISOString().slice(0, 10)}`).join("; ")}. Admin creation is auto-approved; other creators request approval. No customer email is sent.`
23759
22388
  });
23760
22389
  const result = await api2.customerInvoice.createInvoiceBatch.mutate(input2);
23761
22390
  out(result);
@@ -23868,28 +22497,28 @@ var updateBillPaymentEvidenceFromPayload = async (raw) => {
23868
22497
  const result = await api2.bill.updatePaymentEvidence.mutate(input2);
23869
22498
  out(result);
23870
22499
  };
23871
- async function deleteBillById(id2) {
23872
- const result = await api2.bill.delete.mutate({ id: id2 });
22500
+ async function deleteBillById(id) {
22501
+ const result = await api2.bill.delete.mutate({ id });
23873
22502
  out(result);
23874
22503
  }
23875
22504
  async function createSupplierFromPayload(raw) {
23876
22505
  const input2 = parseSupplierCreatePayload(raw);
23877
22506
  const created = await api2.supplier.createSupplier.mutate(input2);
23878
- const supplier2 = created.result;
23879
- if (supplier2.status !== "PENDING_APPROVAL") {
22507
+ const supplier = created.result;
22508
+ if (supplier.status !== "PENDING_APPROVAL") {
23880
22509
  out(created);
23881
22510
  return;
23882
22511
  }
23883
22512
  await assertSensitiveWorkflowConfirmed({
23884
22513
  action: "Create supplier approval request",
23885
- entity: `supplier ${supplier2.id}`,
22514
+ entity: `supplier ${supplier.id}`,
23886
22515
  details: "and send approval request emails"
23887
22516
  });
23888
22517
  const approval = await api2.supplier.createSupplierApproval.mutate({
23889
- supplierId: supplier2.id
22518
+ supplierId: supplier.id
23890
22519
  });
23891
22520
  let emailResult;
23892
- const approvalIds = (approval.results ?? []).map((item2) => ({ id: item2.id }));
22521
+ const approvalIds = (approval.results ?? []).map((item) => ({ id: item.id }));
23893
22522
  if (approvalIds.length === 0) {
23894
22523
  emailResult = createApprovalEmailDeliveryFailure("No approval ids returned");
23895
22524
  } else {
@@ -24007,7 +22636,7 @@ async function rejectBudget(budgetId, reason) {
24007
22636
  });
24008
22637
  }
24009
22638
  async function getSupplierDetails(supplierId) {
24010
- const supplier2 = await api2.supplier.getSupplierById.query({
22639
+ const supplier = await api2.supplier.getSupplierById.query({
24011
22640
  id: supplierId
24012
22641
  });
24013
22642
  const respondedStatuses = new Set([
@@ -24015,14 +22644,14 @@ async function getSupplierDetails(supplierId) {
24015
22644
  ExtendedApprovalStatus.REJECTED
24016
22645
  ]);
24017
22646
  const latestRespondedApproval = [
24018
- ...supplier2.approvals.filter((approval) => respondedStatuses.has(approval.status))
22647
+ ...supplier.approvals.filter((approval) => respondedStatuses.has(approval.status))
24019
22648
  ].sort((a, b) => new Date(b.respondedAt ?? b.updatedAt).getTime() - new Date(a.respondedAt ?? a.updatedAt).getTime() || new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())[0];
24020
- const pendingApprovals = supplier2.approvals.filter((approval) => approval.status === ExtendedApprovalStatus.PENDING_APPROVAL);
24021
- const supersededApprovals = supplier2.approvals.filter((approval) => approval.status === ExtendedApprovalStatus.SUPERSEDED);
22649
+ const pendingApprovals = supplier.approvals.filter((approval) => approval.status === ExtendedApprovalStatus.PENDING_APPROVAL);
22650
+ const supersededApprovals = supplier.approvals.filter((approval) => approval.status === ExtendedApprovalStatus.SUPERSEDED);
24022
22651
  const respondedByApproval = latestRespondedApproval ?? null;
24023
22652
  const responseUser = respondedByApproval ? respondedByApproval.responder ?? respondedByApproval.approver : null;
24024
22653
  out({
24025
- ...supplier2,
22654
+ ...supplier,
24026
22655
  supplierApprovalSummary: {
24027
22656
  pendingApprovalCount: pendingApprovals.length,
24028
22657
  pendingApprovers: pendingApprovals.map((approval) => ({
@@ -24194,9 +22823,9 @@ async function listCompanies(opts) {
24194
22823
  });
24195
22824
  out(result);
24196
22825
  }
24197
- async function getCompany(id2) {
24198
- const company2 = await api2.company.getCompanyById.query({ id: id2 });
24199
- out(company2);
22826
+ async function getCompany(id) {
22827
+ const company = await api2.company.getCompanyById.query({ id });
22828
+ out(company);
24200
22829
  }
24201
22830
  async function createCompany(opts) {
24202
22831
  const result = await api2.company.createCompany.mutate(opts);
@@ -24227,13 +22856,13 @@ var listProjectHubs = async (opts) => {
24227
22856
  });
24228
22857
  out(result);
24229
22858
  };
24230
- async function getProject(id2) {
24231
- const [project2, budgetsOverview, billsOverview] = await Promise.all([
24232
- api2.project.getProjectDetailsById.query({ id: id2 }),
24233
- api2.project.getProjectBudgetsOverview.query({ projectId: id2 }),
24234
- api2.project.getProjectBillsOverview.query({ projectId: id2 })
22859
+ async function getProject(id) {
22860
+ const [project, budgetsOverview, billsOverview] = await Promise.all([
22861
+ api2.project.getProjectDetailsById.query({ id }),
22862
+ api2.project.getProjectBudgetsOverview.query({ projectId: id }),
22863
+ api2.project.getProjectBillsOverview.query({ projectId: id })
24235
22864
  ]);
24236
- out({ project: project2, budgetsOverview, billsOverview });
22865
+ out({ project, budgetsOverview, billsOverview });
24237
22866
  }
24238
22867
  var getProjectHubStatus = async (projectId) => {
24239
22868
  const result = await api2.project.getProjectHubShareLink.query({ projectId });
@@ -24289,14 +22918,14 @@ async function createProject(opts) {
24289
22918
  });
24290
22919
  out(result);
24291
22920
  }
24292
- async function updateProjectStatus(id2, status, opts) {
22921
+ async function updateProjectStatus(id, status, opts) {
24293
22922
  await assertSensitiveWorkflowConfirmed({
24294
22923
  action: "Update project status",
24295
- entity: `project ${id2}`,
22924
+ entity: `project ${id}`,
24296
22925
  details: `to ${status}`
24297
22926
  });
24298
22927
  const result = await api2.project.updateProjectStatus.mutate({
24299
- id: id2,
22928
+ id,
24300
22929
  status,
24301
22930
  ...opts?.projectManagerId && { projectManagerId: opts.projectManagerId },
24302
22931
  ...opts?.projectManagerEmail && {
@@ -24405,9 +23034,9 @@ async function listItems(opts) {
24405
23034
  });
24406
23035
  out(result);
24407
23036
  }
24408
- async function getItem(id2) {
24409
- const item2 = await api2.item.getItem.query({ id: id2 });
24410
- out(item2);
23037
+ async function getItem(id) {
23038
+ const item = await api2.item.getItem.query({ id });
23039
+ out(item);
24411
23040
  }
24412
23041
  async function listItemCategories(opts) {
24413
23042
  const result = await api2.itemCategory.getItemCategories.query({
@@ -25098,47 +23727,6 @@ var registry2 = [
25098
23727
  "customer-invoice",
25099
23728
  "import-qbo"
25100
23729
  ]),
25101
- {
25102
- ...legacyCommand("get-customer-invoice-splits", [
25103
- "customer-invoice",
25104
- "splits"
25105
- ]),
25106
- summary: "Review acceptance invoice splits, due dates, approval states and saved billing details. Arguments: <budgetId>."
25107
- },
25108
- {
25109
- ...legacyCommand("preview-customer-invoice-splits", [
25110
- "customer-invoice",
25111
- "preview-splits"
25112
- ]),
25113
- summary: "Preview splits, GST totals and due-date changes without writes. Requires --payload with the schedule save input.",
25114
- effects: []
25115
- },
25116
- {
25117
- ...legacyCommand("save-customer-invoice-splits", [
25118
- "customer-invoice",
25119
- "save-splits"
25120
- ]),
25121
- summary: "Save split amounts and separate due dates after estimate acceptance. Requires --payload; does not create invoices or request approval.",
25122
- effects: ["state-change"]
25123
- },
25124
- {
25125
- ...legacyCommand("create-customer-invoice-splits", [
25126
- "customer-invoice",
25127
- "create-splits"
25128
- ]),
25129
- summary: "Create selected split invoices in QBO, then request admin approval for non-admin creators or auto-approve admin creators. Requires --payload with budgetId, expectedRevision, instalmentIds. Never sends customer invoices.",
25130
- effects: ["state-change", "financial-write", "external-write", "email"]
25131
- },
25132
- {
25133
- ...legacyCommand("send-customer-invoice-group", ["customer-invoice", "send-group"], [], [
25134
- {
25135
- name: "--all-approved",
25136
- description: "Select all approved unsent invoices; payload must contain only budgetId and expectedRevision."
25137
- }
25138
- ]),
25139
- summary: "Send selected approved invoices together in one email. Requires --payload with budgetId, expectedRevision, instalmentIds, or --all-approved without instalmentIds. Pass one instalment for individual sending.",
25140
- effects: ["state-change", "email", "external-write"]
25141
- },
25142
23730
  legacyCommand("create-customer-invoice", ["customer-invoice", "create"]),
25143
23731
  legacyCommand("discard-customer-invoice", ["customer-invoice", "discard"]),
25144
23732
  legacyCommand("delete-customer-invoice", ["customer-invoice", "delete"]),
@@ -25712,12 +24300,7 @@ Customer invoices
25712
24300
  get-customer-invoice-email-context <batchId> Contact, required CC, budget, and project context for composing an invoice email.
25713
24301
  preview-qbo-customer-invoice <budgetId> <qboInvoiceId> Preview linking an existing QBO transaction (numeric ID, not invoice number); works on accepted/closed estimates.
25714
24302
  import-qbo-customer-invoice <budgetId> <qboInvoiceId> --token <previewToken> Confirm the reviewed BB-only link; requires --allow-state-change --allow-financial-write in automation.
25715
- customer-invoice splits <budgetId> Review invoice split setup after estimate acceptance.
25716
- customer-invoice preview-splits --payload '<json>' Preview amounts including GST and separate contractual due dates.
25717
- customer-invoice save-splits --payload '<json>' Save split/date setup only, without QBO writes or approval requests.
25718
- customer-invoice create-splits --payload '<json>' Create selected QBO invoices; non-admin requests invoice approval, admin auto-approves. Never sends to customer.
25719
- customer-invoice send-group --payload '<json>' [--all-approved] Send approved invoices together in one email; one selected instalment sends individually.
25720
- create-customer-invoice --payload '<json>' Legacy budget without a schedule: one split with label, percentage, and dueDate.
24303
+ create-customer-invoice --payload '<json>' budgetId + setting-derived splits, each with label, percentage, and dueDate; creates invoices and requests approval (admin auto-approves). Never sends.
25721
24304
  discard-customer-invoice <batchId> Discard a reserved CREATING batch with no created QBO invoices.
25722
24305
  delete-customer-invoice <batchId> Delete QBO invoices and the local batch.
25723
24306
  void-customer-invoice <batchId> Void the invoice batch in QuickBooks.
@@ -26199,10 +24782,10 @@ Waiting for approval\u2026
26199
24782
  break;
26200
24783
  }
26201
24784
  case "get-budget": {
26202
- const id2 = positional[0];
26203
- if (!id2)
24785
+ const id = positional[0];
24786
+ if (!id)
26204
24787
  throw new Error("get-budget requires <id>");
26205
- await getBudget(id2);
24788
+ await getBudget(id);
26206
24789
  break;
26207
24790
  }
26208
24791
  case "upload-budget-attachment": {
@@ -26226,17 +24809,17 @@ Waiting for approval\u2026
26226
24809
  break;
26227
24810
  }
26228
24811
  case "get-budget-items": {
26229
- const id2 = positional[0];
26230
- if (!id2)
24812
+ const id = positional[0];
24813
+ if (!id)
26231
24814
  throw new Error("get-budget-items requires <budgetId>");
26232
- await getBudgetItemsOnly(id2);
24815
+ await getBudgetItemsOnly(id);
26233
24816
  break;
26234
24817
  }
26235
24818
  case "get-budget-details": {
26236
- const id2 = positional[0];
26237
- if (!id2)
24819
+ const id = positional[0];
24820
+ if (!id)
26238
24821
  throw new Error("get-budget-details requires <budgetId>");
26239
- await getBudgetDetails(id2);
24822
+ await getBudgetDetails(id);
26240
24823
  break;
26241
24824
  }
26242
24825
  case "get-budget-categories": {
@@ -26244,10 +24827,10 @@ Waiting for approval\u2026
26244
24827
  break;
26245
24828
  }
26246
24829
  case "get-budget-versions": {
26247
- const id2 = positional[0];
26248
- if (!id2)
24830
+ const id = positional[0];
24831
+ if (!id)
26249
24832
  throw new Error("get-budget-versions requires <budgetId>");
26250
- await getBudgetVersions(id2);
24833
+ await getBudgetVersions(id);
26251
24834
  break;
26252
24835
  }
26253
24836
  case "rename-budget-version": {
@@ -26268,14 +24851,14 @@ Waiting for approval\u2026
26268
24851
  break;
26269
24852
  }
26270
24853
  case "update-budget-status": {
26271
- const [id2, status] = positional;
26272
- if (!id2 || !status) {
24854
+ const [id, status] = positional;
24855
+ if (!id || !status) {
26273
24856
  throw new Error("update-budget-status requires <id> <status>");
26274
24857
  }
26275
24858
  if (!isBudgetStatusUpdate(status)) {
26276
24859
  throw new Error(`Invalid status. Use one of: ${budgetStatusesForHelp.join(", ")}`);
26277
24860
  }
26278
- await updateBudgetStatus(id2, status, {
24861
+ await updateBudgetStatus(id, status, {
26279
24862
  projectManagerId: getFlag(flags, "projectManagerId") !== undefined ? String(getFlag(flags, "projectManagerId")) : undefined,
26280
24863
  markProjectWon: parseOptionalBoolFlag(flags, "markProjectWon"),
26281
24864
  projectStatusOnCommercialRejection: parseCommercialRejectionProjectStatus(flags),
@@ -26284,13 +24867,13 @@ Waiting for approval\u2026
26284
24867
  break;
26285
24868
  }
26286
24869
  case "create-budget-approval": {
26287
- const id2 = positional[0];
26288
- if (!id2)
24870
+ const id = positional[0];
24871
+ if (!id)
26289
24872
  throw new Error("create-budget-approval requires <budgetId>");
26290
24873
  const companyPayloadRaw = getFlag(flags, "companyPayload");
26291
24874
  const contactPayloadRaw = getFlag(flags, "contactPayload");
26292
24875
  const eventPayloadRaw = getFlag(flags, "eventPayload");
26293
- await createBudgetApproval(id2, {
24876
+ await createBudgetApproval(id, {
26294
24877
  company: companyPayloadRaw !== undefined ? parseJsonFlag(String(companyPayloadRaw), "--companyPayload") : undefined,
26295
24878
  contactPerson: contactPayloadRaw !== undefined ? parseJsonFlag(String(contactPayloadRaw), "--contactPayload") : undefined,
26296
24879
  eventDetails: eventPayloadRaw !== undefined ? parseJsonFlag(String(eventPayloadRaw), "--eventPayload") : undefined
@@ -26298,13 +24881,13 @@ Waiting for approval\u2026
26298
24881
  break;
26299
24882
  }
26300
24883
  case "create-estimate": {
26301
- const id2 = positional[0];
26302
- if (!id2)
24884
+ const id = positional[0];
24885
+ if (!id)
26303
24886
  throw new Error("create-estimate requires <budgetId>");
26304
24887
  const companyPayloadRaw = getFlag(flags, "companyPayload");
26305
24888
  const contactPayloadRaw = getFlag(flags, "contactPayload");
26306
24889
  const eventPayloadRaw = getFlag(flags, "eventPayload");
26307
- await createEstimate(id2, {
24890
+ await createEstimate(id, {
26308
24891
  company: companyPayloadRaw !== undefined ? parseJsonFlag(String(companyPayloadRaw), "--companyPayload") : undefined,
26309
24892
  contactPerson: contactPayloadRaw !== undefined ? parseJsonFlag(String(contactPayloadRaw), "--contactPayload") : undefined,
26310
24893
  eventDetails: eventPayloadRaw !== undefined ? parseJsonFlag(String(eventPayloadRaw), "--eventPayload") : undefined
@@ -26340,10 +24923,10 @@ Waiting for approval\u2026
26340
24923
  break;
26341
24924
  }
26342
24925
  case "delete-budget": {
26343
- const id2 = positional[0];
26344
- if (!id2)
24926
+ const id = positional[0];
24927
+ if (!id)
26345
24928
  throw new Error("delete-budget requires <budgetId>");
26346
- await deleteBudgetById(id2);
24929
+ await deleteBudgetById(id);
26347
24930
  break;
26348
24931
  }
26349
24932
  case "add-budget-items": {
@@ -26355,11 +24938,11 @@ Waiting for approval\u2026
26355
24938
  break;
26356
24939
  }
26357
24940
  case "update-budget-item": {
26358
- const id2 = getFlag(flags, "id");
26359
- if (!id2)
24941
+ const id = getFlag(flags, "id");
24942
+ if (!id)
26360
24943
  throw new Error("update-budget-item requires --id");
26361
24944
  await updateBudgetItem({
26362
- id: String(id2),
24945
+ id: String(id),
26363
24946
  description: getFlag(flags, "description"),
26364
24947
  note: getFlag(flags, "note"),
26365
24948
  quantity: parseOptionalNumber(getFlag(flags, "quantity"), "--quantity"),
@@ -26373,10 +24956,10 @@ Waiting for approval\u2026
26373
24956
  break;
26374
24957
  }
26375
24958
  case "remove-budget-item": {
26376
- const id2 = positional[0];
26377
- if (!id2)
24959
+ const id = positional[0];
24960
+ if (!id)
26378
24961
  throw new Error("remove-budget-item requires <budgetItemId>");
26379
- await removeBudgetItem(id2);
24962
+ await removeBudgetItem(id);
26380
24963
  break;
26381
24964
  }
26382
24965
  case "replace-budget-item": {
@@ -26437,20 +25020,20 @@ Waiting for approval\u2026
26437
25020
  break;
26438
25021
  }
26439
25022
  case "update-budget-category": {
26440
- const id2 = getFlag(flags, "id");
25023
+ const id = getFlag(flags, "id");
26441
25024
  const name = getFlag(flags, "name");
26442
- if (!id2 || !name) {
25025
+ if (!id || !name) {
26443
25026
  throw new Error("update-budget-category requires --id and --name");
26444
25027
  }
26445
- await updateBudgetCategory({ id: String(id2), name: String(name) });
25028
+ await updateBudgetCategory({ id: String(id), name: String(name) });
26446
25029
  break;
26447
25030
  }
26448
25031
  case "delete-budget-category": {
26449
- const id2 = positional[0];
26450
- if (!id2) {
25032
+ const id = positional[0];
25033
+ if (!id) {
26451
25034
  throw new Error("delete-budget-category requires <id>");
26452
25035
  }
26453
- await deleteBudgetCategory(id2);
25036
+ await deleteBudgetCategory(id);
26454
25037
  break;
26455
25038
  }
26456
25039
  case "update-budget-commission": {
@@ -26523,10 +25106,10 @@ Waiting for approval\u2026
26523
25106
  break;
26524
25107
  }
26525
25108
  case "create-bill-approval": {
26526
- const id2 = positional[0];
26527
- if (!id2)
25109
+ const id = positional[0];
25110
+ if (!id)
26528
25111
  throw new Error("create-bill-approval requires <billId>");
26529
- await createBillApproval(id2);
25112
+ await createBillApproval(id);
26530
25113
  break;
26531
25114
  }
26532
25115
  case "create-bill": {
@@ -26601,15 +25184,15 @@ Waiting for approval\u2026
26601
25184
  break;
26602
25185
  }
26603
25186
  case "delete-bill": {
26604
- const id2 = positional[0];
26605
- if (!id2)
25187
+ const id = positional[0];
25188
+ if (!id)
26606
25189
  throw new Error("delete-bill requires <billId>");
26607
- await deleteBillById(id2);
25190
+ await deleteBillById(id);
26608
25191
  break;
26609
25192
  }
26610
25193
  case "update-bill-status": {
26611
- const [id2, statusRaw] = positional;
26612
- if (!id2 || !statusRaw) {
25194
+ const [id, statusRaw] = positional;
25195
+ if (!id || !statusRaw) {
26613
25196
  throw new Error("update-bill-status requires <billId> <status> [--rejectionReason] [--paymentTrackingUrl] [--paymentReference] [--paymentProof <receipt.pdf>]");
26614
25197
  }
26615
25198
  const paymentProofFlag = flags.paymentProof ?? flags["payment-proof"] ?? flags.receipt;
@@ -26618,7 +25201,7 @@ Waiting for approval\u2026
26618
25201
  }
26619
25202
  const status = parseBillStatusForUpdate(statusRaw);
26620
25203
  await updateBillStatus({
26621
- id: id2,
25204
+ id,
26622
25205
  status,
26623
25206
  rejectionReason: getFlag(flags, "rejectionReason"),
26624
25207
  paymentTrackingUrl: getFlag(flags, "paymentTrackingUrl"),
@@ -26629,8 +25212,8 @@ Waiting for approval\u2026
26629
25212
  break;
26630
25213
  }
26631
25214
  case "patch-bill-payment": {
26632
- const id2 = positional[0];
26633
- if (!id2)
25215
+ const id = positional[0];
25216
+ if (!id)
26634
25217
  throw new Error("patch-bill-payment requires <billId>");
26635
25218
  const paymentTrackingUrl = (() => {
26636
25219
  const clear = parseOptionalBoolFlag(flags, "clearPaymentTrackingUrl");
@@ -26681,7 +25264,7 @@ Waiting for approval\u2026
26681
25264
  return;
26682
25265
  })();
26683
25266
  await patchBillPayment({
26684
- id: id2,
25267
+ id,
26685
25268
  paymentTrackingUrl,
26686
25269
  paymentReference,
26687
25270
  quickbooksBillId,
@@ -26690,18 +25273,18 @@ Waiting for approval\u2026
26690
25273
  break;
26691
25274
  }
26692
25275
  case "patch-bill-invoice-number": {
26693
- const [id2, inv] = positional;
26694
- if (!id2 || !inv) {
25276
+ const [id, inv] = positional;
25277
+ if (!id || !inv) {
26695
25278
  throw new Error("patch-bill-invoice-number requires <billId> <invoiceNumber>");
26696
25279
  }
26697
- await patchBillInvoiceNumber(id2, inv);
25280
+ await patchBillInvoiceNumber(id, inv);
26698
25281
  break;
26699
25282
  }
26700
25283
  case "get-bill-attachments": {
26701
- const id2 = positional[0];
26702
- if (!id2)
25284
+ const id = positional[0];
25285
+ if (!id)
26703
25286
  throw new Error("get-bill-attachments requires <billId>");
26704
- await getBillAttachments(id2);
25287
+ await getBillAttachments(id);
26705
25288
  break;
26706
25289
  }
26707
25290
  case "stage-bill-attachment": {
@@ -26787,10 +25370,10 @@ Waiting for approval\u2026
26787
25370
  break;
26788
25371
  }
26789
25372
  case "get-bill-details": {
26790
- const id2 = positional[0];
26791
- if (!id2)
25373
+ const id = positional[0];
25374
+ if (!id)
26792
25375
  throw new Error("get-bill-details requires <billId>");
26793
- await getBillDetails(id2);
25376
+ await getBillDetails(id);
26794
25377
  break;
26795
25378
  }
26796
25379
  case "list-quotations": {
@@ -26810,10 +25393,10 @@ Waiting for approval\u2026
26810
25393
  break;
26811
25394
  }
26812
25395
  case "get-quotation": {
26813
- const id2 = positional[0];
26814
- if (!id2)
25396
+ const id = positional[0];
25397
+ if (!id)
26815
25398
  throw new Error("get-quotation requires <quotationId>");
26816
- await getQuotationDetails(id2);
25399
+ await getQuotationDetails(id);
26817
25400
  break;
26818
25401
  }
26819
25402
  case "upload-quotation-attachment": {
@@ -26852,38 +25435,38 @@ Waiting for approval\u2026
26852
25435
  break;
26853
25436
  }
26854
25437
  case "delete-quotation": {
26855
- const id2 = positional[0];
26856
- if (!id2)
25438
+ const id = positional[0];
25439
+ if (!id)
26857
25440
  throw new Error("delete-quotation requires <quotationId>");
26858
- await deleteQuotationById(id2);
25441
+ await deleteQuotationById(id);
26859
25442
  break;
26860
25443
  }
26861
25444
  case "submit-quotation": {
26862
- const id2 = positional[0];
26863
- if (!id2)
25445
+ const id = positional[0];
25446
+ if (!id)
26864
25447
  throw new Error("submit-quotation requires <quotationId>");
26865
- await submitQuotation(id2, getFlag(flags, "overBudgetOverrideReason"));
25448
+ await submitQuotation(id, getFlag(flags, "overBudgetOverrideReason"));
26866
25449
  break;
26867
25450
  }
26868
25451
  case "approve-quotation": {
26869
- const id2 = positional[0];
26870
- if (!id2)
25452
+ const id = positional[0];
25453
+ if (!id)
26871
25454
  throw new Error("approve-quotation requires <quotationId>");
26872
- await approveQuotation(id2, getFlag(flags, "overBudgetOverrideReason"));
25455
+ await approveQuotation(id, getFlag(flags, "overBudgetOverrideReason"));
26873
25456
  break;
26874
25457
  }
26875
25458
  case "reject-quotation": {
26876
- const id2 = positional[0];
25459
+ const id = positional[0];
26877
25460
  const reason = getFlag(flags, "reason");
26878
- if (!id2 || reason === undefined) {
25461
+ if (!id || reason === undefined) {
26879
25462
  throw new Error("reject-quotation requires <quotationId> --reason <text>");
26880
25463
  }
26881
- await rejectQuotation(id2, reason);
25464
+ await rejectQuotation(id, reason);
26882
25465
  break;
26883
25466
  }
26884
25467
  case "download-quotation-pdf": {
26885
- const id2 = positional[0];
26886
- if (!id2) {
25468
+ const id = positional[0];
25469
+ if (!id) {
26887
25470
  throw new Error("download-quotation-pdf requires <quotationId> [original|staff|final]");
26888
25471
  }
26889
25472
  const roleRaw = (positional[1] ?? "final").toLowerCase();
@@ -26891,7 +25474,7 @@ Waiting for approval\u2026
26891
25474
  if (!fileRole) {
26892
25475
  throw new Error("download-quotation-pdf role must be original, staff, or final");
26893
25476
  }
26894
- await downloadQuotationPdf(id2, fileRole);
25477
+ await downloadQuotationPdf(id, fileRole);
26895
25478
  break;
26896
25479
  }
26897
25480
  case "check-customer-invoice-readiness": {
@@ -26974,31 +25557,6 @@ Waiting for approval\u2026
26974
25557
  }
26975
25558
  break;
26976
25559
  }
26977
- case "get-customer-invoice-splits": {
26978
- const budgetId = positional[0];
26979
- if (!budgetId)
26980
- throw new Error("customer-invoice splits requires <budgetId>");
26981
- await getCustomerInvoiceSplits(budgetId);
26982
- break;
26983
- }
26984
- case "preview-customer-invoice-splits":
26985
- case "save-customer-invoice-splits":
26986
- case "create-customer-invoice-splits":
26987
- case "send-customer-invoice-group": {
26988
- const payloadRaw = getFlag(flags, "payload");
26989
- if (!payloadRaw)
26990
- throw new Error(`${command} requires --payload '<json>'`);
26991
- const raw = parseJsonFlag(payloadRaw, "--payload");
26992
- if (command === "preview-customer-invoice-splits")
26993
- await previewCustomerInvoiceSplits(raw);
26994
- else if (command === "save-customer-invoice-splits")
26995
- await saveCustomerInvoiceSplits(raw);
26996
- else if (command === "create-customer-invoice-splits")
26997
- await createCustomerInvoiceSplits(raw);
26998
- else
26999
- await sendCustomerInvoiceGroup(raw, parseOptionalBoolFlag(flags, "all-approved") ?? false);
27000
- break;
27001
- }
27002
25560
  case "create-customer-invoice": {
27003
25561
  const payloadRaw = getFlag(flags, "payload");
27004
25562
  if (!payloadRaw) {
@@ -27081,58 +25639,58 @@ Waiting for approval\u2026
27081
25639
  break;
27082
25640
  }
27083
25641
  case "approve-bill": {
27084
- const id2 = positional[0];
27085
- if (!id2)
25642
+ const id = positional[0];
25643
+ if (!id)
27086
25644
  throw new Error("approve-bill requires <billId>");
27087
- await approveBill(id2, getFlag(flags, "overBudgetOverrideReason"));
25645
+ await approveBill(id, getFlag(flags, "overBudgetOverrideReason"));
27088
25646
  break;
27089
25647
  }
27090
25648
  case "reject-bill": {
27091
- const id2 = positional[0];
25649
+ const id = positional[0];
27092
25650
  const reason = getFlag(flags, "reason");
27093
- if (!id2 || reason === undefined) {
25651
+ if (!id || reason === undefined) {
27094
25652
  throw new Error("reject-bill requires <billId> --reason <text>");
27095
25653
  }
27096
- await rejectBill(id2, reason);
25654
+ await rejectBill(id, reason);
27097
25655
  break;
27098
25656
  }
27099
25657
  case "approve-budget": {
27100
- const id2 = positional[0];
27101
- if (!id2)
25658
+ const id = positional[0];
25659
+ if (!id)
27102
25660
  throw new Error("approve-budget requires <budgetId>");
27103
- await approveBudget(id2);
25661
+ await approveBudget(id);
27104
25662
  break;
27105
25663
  }
27106
25664
  case "reject-budget": {
27107
- const id2 = positional[0];
25665
+ const id = positional[0];
27108
25666
  const reason = getFlag(flags, "reason");
27109
- if (!id2 || reason === undefined) {
25667
+ if (!id || reason === undefined) {
27110
25668
  throw new Error("reject-budget requires <budgetId> --reason <text>");
27111
25669
  }
27112
- await rejectBudget(id2, reason);
25670
+ await rejectBudget(id, reason);
27113
25671
  break;
27114
25672
  }
27115
25673
  case "approve-supplier": {
27116
- const id2 = positional[0];
27117
- if (!id2)
25674
+ const id = positional[0];
25675
+ if (!id)
27118
25676
  throw new Error("approve-supplier requires <supplierId>");
27119
- await approveSupplier(id2);
25677
+ await approveSupplier(id);
27120
25678
  break;
27121
25679
  }
27122
25680
  case "reject-supplier": {
27123
- const id2 = positional[0];
25681
+ const id = positional[0];
27124
25682
  const reason = getFlag(flags, "reason");
27125
- if (!id2 || reason === undefined) {
25683
+ if (!id || reason === undefined) {
27126
25684
  throw new Error("reject-supplier requires <supplierId> --reason <text>");
27127
25685
  }
27128
- await rejectSupplier(id2, reason);
25686
+ await rejectSupplier(id, reason);
27129
25687
  break;
27130
25688
  }
27131
25689
  case "get-supplier-details": {
27132
- const id2 = positional[0];
27133
- if (!id2)
25690
+ const id = positional[0];
25691
+ if (!id)
27134
25692
  throw new Error("get-supplier-details requires <supplierId>");
27135
- await getSupplierDetails(id2);
25693
+ await getSupplierDetails(id);
27136
25694
  break;
27137
25695
  }
27138
25696
  case "list-companies": {
@@ -27146,10 +25704,10 @@ Waiting for approval\u2026
27146
25704
  break;
27147
25705
  }
27148
25706
  case "get-company": {
27149
- const id2 = positional[0];
27150
- if (!id2)
25707
+ const id = positional[0];
25708
+ if (!id)
27151
25709
  throw new Error("get-company requires <id>");
27152
- await getCompany(id2);
25710
+ await getCompany(id);
27153
25711
  break;
27154
25712
  }
27155
25713
  case "create-company": {
@@ -27203,10 +25761,10 @@ Waiting for approval\u2026
27203
25761
  break;
27204
25762
  }
27205
25763
  case "delete-company": {
27206
- const id2 = positional[0];
27207
- if (!id2)
25764
+ const id = positional[0];
25765
+ if (!id)
27208
25766
  throw new Error("delete-company requires <id>");
27209
- await deleteCompanyById(id2);
25767
+ await deleteCompanyById(id);
27210
25768
  break;
27211
25769
  }
27212
25770
  case "list-projects": {
@@ -27233,10 +25791,10 @@ Waiting for approval\u2026
27233
25791
  break;
27234
25792
  }
27235
25793
  case "get-project": {
27236
- const id2 = positional[0];
27237
- if (!id2)
25794
+ const id = positional[0];
25795
+ if (!id)
27238
25796
  throw new Error("get-project requires <id>");
27239
- await getProject(id2);
25797
+ await getProject(id);
27240
25798
  break;
27241
25799
  }
27242
25800
  case "get-project-hub-status": {
@@ -27314,32 +25872,32 @@ Waiting for approval\u2026
27314
25872
  break;
27315
25873
  }
27316
25874
  case "delete-project": {
27317
- const id2 = positional[0];
27318
- if (!id2)
25875
+ const id = positional[0];
25876
+ if (!id)
27319
25877
  throw new Error("delete-project requires <id>");
27320
- await deleteProjectById(id2);
25878
+ await deleteProjectById(id);
27321
25879
  break;
27322
25880
  }
27323
25881
  case "check-project-reconciliation": {
27324
- const id2 = positional[0];
27325
- if (!id2) {
25882
+ const id = positional[0];
25883
+ if (!id) {
27326
25884
  throw new Error("check-project-reconciliation requires <id>");
27327
25885
  }
27328
- await checkProjectReconciliation(id2);
25886
+ await checkProjectReconciliation(id);
27329
25887
  break;
27330
25888
  }
27331
25889
  case "reconcile-project": {
27332
- const id2 = positional[0];
27333
- if (!id2)
25890
+ const id = positional[0];
25891
+ if (!id)
27334
25892
  throw new Error("reconcile-project requires <id>");
27335
- await reconcileProject(id2);
25893
+ await reconcileProject(id);
27336
25894
  break;
27337
25895
  }
27338
25896
  case "complete-project": {
27339
- const id2 = positional[0];
27340
- if (!id2)
25897
+ const id = positional[0];
25898
+ if (!id)
27341
25899
  throw new Error("complete-project requires <id>");
27342
- await completeProject(id2);
25900
+ await completeProject(id);
27343
25901
  break;
27344
25902
  }
27345
25903
  case "import-qbo-project": {
@@ -27352,14 +25910,14 @@ Waiting for approval\u2026
27352
25910
  break;
27353
25911
  }
27354
25912
  case "update-project-status": {
27355
- const [id2, statusRaw] = positional;
27356
- if (!id2 || !statusRaw) {
25913
+ const [id, statusRaw] = positional;
25914
+ if (!id || !statusRaw) {
27357
25915
  throw new Error("update-project-status requires <id> <status>");
27358
25916
  }
27359
25917
  const pmIdFlag = getFlag(flags, "projectManagerId");
27360
25918
  const pmEmailFlag = getFlag(flags, "projectManagerEmail");
27361
25919
  const wonOverrideReasonFlag = getFlag(flags, "wonOverrideReason");
27362
- await updateProjectStatus(id2, parseProjectStatusForUpdate(statusRaw), {
25920
+ await updateProjectStatus(id, parseProjectStatusForUpdate(statusRaw), {
27363
25921
  ...pmIdFlag && { projectManagerId: String(pmIdFlag) },
27364
25922
  ...pmEmailFlag && {
27365
25923
  projectManagerEmail: String(pmEmailFlag)
@@ -27371,10 +25929,10 @@ Waiting for approval\u2026
27371
25929
  break;
27372
25930
  }
27373
25931
  case "list-contacts": {
27374
- const id2 = positional[0];
27375
- if (!id2)
25932
+ const id = positional[0];
25933
+ if (!id)
27376
25934
  throw new Error("list-contacts requires <companyId>");
27377
- await listContacts(id2);
25935
+ await listContacts(id);
27378
25936
  break;
27379
25937
  }
27380
25938
  case "create-contact-person": {
@@ -27489,10 +26047,10 @@ Waiting for approval\u2026
27489
26047
  break;
27490
26048
  }
27491
26049
  case "get-item": {
27492
- const id2 = positional[0];
27493
- if (!id2)
26050
+ const id = positional[0];
26051
+ if (!id)
27494
26052
  throw new Error("get-item requires <id>");
27495
- await getItem(id2);
26053
+ await getItem(id);
27496
26054
  break;
27497
26055
  }
27498
26056
  case "create-item": {
@@ -27514,10 +26072,10 @@ Waiting for approval\u2026
27514
26072
  break;
27515
26073
  }
27516
26074
  case "delete-item": {
27517
- const id2 = positional[0];
27518
- if (!id2)
26075
+ const id = positional[0];
26076
+ if (!id)
27519
26077
  throw new Error("delete-item requires <id>");
27520
- await deleteItemById(id2);
26078
+ await deleteItemById(id);
27521
26079
  break;
27522
26080
  }
27523
26081
  case "archive-items": {
@@ -27547,12 +26105,12 @@ Waiting for approval\u2026
27547
26105
  break;
27548
26106
  }
27549
26107
  case "update-item-category": {
27550
- const id2 = getFlag(flags, "id");
26108
+ const id = getFlag(flags, "id");
27551
26109
  const name = getFlag(flags, "name");
27552
- if (!id2 || !name) {
26110
+ if (!id || !name) {
27553
26111
  throw new Error("update-item-category requires --id and --name");
27554
26112
  }
27555
- await updateItemCategory({ id: String(id2), name: String(name) });
26113
+ await updateItemCategory({ id: String(id), name: String(name) });
27556
26114
  break;
27557
26115
  }
27558
26116
  case "delete-item-categories": {
@@ -27597,12 +26155,12 @@ Waiting for approval\u2026
27597
26155
  break;
27598
26156
  }
27599
26157
  case "revoke-api-key": {
27600
- const id2 = positional[0];
27601
- if (!id2) {
26158
+ const id = positional[0];
26159
+ if (!id) {
27602
26160
  throw new Error("revoke-api-key requires <apiKeyId> [--userId <userId>]");
27603
26161
  }
27604
26162
  await revokeApiKeyForUser({
27605
- id: id2,
26163
+ id,
27606
26164
  userId: getFlag(flags, "userId")
27607
26165
  });
27608
26166
  break;
@@ -27765,12 +26323,12 @@ Waiting for approval\u2026
27765
26323
  break;
27766
26324
  }
27767
26325
  case "get-budget-category-benchmarks": {
27768
- const id2 = positional[0];
27769
- if (!id2) {
26326
+ const id = positional[0];
26327
+ if (!id) {
27770
26328
  throw new Error("get-budget-category-benchmarks requires <categoryId> [--limit]");
27771
26329
  }
27772
26330
  await getBudgetCategoryBenchmarks({
27773
- categoryId: id2,
26331
+ categoryId: id,
27774
26332
  limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit")
27775
26333
  });
27776
26334
  break;
@@ -27785,12 +26343,12 @@ Waiting for approval\u2026
27785
26343
  break;
27786
26344
  }
27787
26345
  case "get-supplier-pricing-history": {
27788
- const id2 = positional[0];
27789
- if (!id2) {
26346
+ const id = positional[0];
26347
+ if (!id) {
27790
26348
  throw new Error("get-supplier-pricing-history requires <supplierId> [--itemCategoryId] [--limit]");
27791
26349
  }
27792
26350
  await getSupplierPricingHistory({
27793
- supplierId: id2,
26351
+ supplierId: id,
27794
26352
  itemCategoryId: getFlag(flags, "itemCategoryId"),
27795
26353
  limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit")
27796
26354
  });