@go-labs-sg/bb 2.34.0 → 2.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -3
- package/command-manifest.json +269 -0
- package/command-reference.md +40 -0
- package/dist/index.js +2118 -663
- package/package.json +1 -1
- package/role-aware-agent-guide.md +2 -0
package/dist/index.js
CHANGED
|
@@ -19625,77 +19625,1787 @@ var api2 = new Proxy({}, {
|
|
|
19625
19625
|
get: (_target, property, receiver) => Reflect.get(activeClient, property, receiver)
|
|
19626
19626
|
});
|
|
19627
19627
|
|
|
19628
|
-
// src/
|
|
19629
|
-
|
|
19630
|
-
|
|
19631
|
-
|
|
19632
|
-
|
|
19633
|
-
|
|
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
|
+
});
|
|
19634
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
|
+
};
|
|
19635
19647
|
var EstimationMode = {
|
|
19636
19648
|
MICRO: "MICRO",
|
|
19637
19649
|
MANPOWER_SPECIFIED: "MANPOWER_SPECIFIED"
|
|
19638
19650
|
};
|
|
19639
|
-
var
|
|
19640
|
-
|
|
19641
|
-
|
|
19642
|
-
|
|
19643
|
-
|
|
19644
|
-
|
|
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);
|
|
19645
21289
|
};
|
|
19646
|
-
var
|
|
19647
|
-
|
|
19648
|
-
|
|
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"
|
|
21290
|
+
var emitCommandResult = (data) => {
|
|
21291
|
+
const session = requireSession();
|
|
21292
|
+
return emitCliSuccess(session.runtime, session.invocation, toJsonValue(data));
|
|
19656
21293
|
};
|
|
19657
|
-
var
|
|
19658
|
-
|
|
19659
|
-
|
|
19660
|
-
REJECTED: "REJECTED",
|
|
19661
|
-
SUPERSEDED: "SUPERSEDED"
|
|
21294
|
+
var emitCommandError = (error61) => {
|
|
21295
|
+
const session = requireSession();
|
|
21296
|
+
return emitCliError(session.runtime, session.invocation, error61);
|
|
19662
21297
|
};
|
|
19663
|
-
var
|
|
19664
|
-
|
|
19665
|
-
|
|
19666
|
-
|
|
19667
|
-
|
|
19668
|
-
|
|
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
|
+
}
|
|
19669
21314
|
};
|
|
19670
|
-
|
|
19671
|
-
|
|
19672
|
-
|
|
19673
|
-
|
|
19674
|
-
|
|
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
|
+
};
|
|
19675
21336
|
};
|
|
19676
|
-
var
|
|
19677
|
-
|
|
19678
|
-
INVESTIGATING: "INVESTIGATING",
|
|
19679
|
-
RESOLVED: "RESOLVED",
|
|
19680
|
-
IGNORED: "IGNORED"
|
|
21337
|
+
var getCustomerInvoiceSplits = async (budgetId) => {
|
|
21338
|
+
emitCommandResult(await api2.paymentSchedule.get.query({ budgetId }));
|
|
19681
21339
|
};
|
|
19682
|
-
var
|
|
19683
|
-
|
|
19684
|
-
|
|
19685
|
-
APPROVED: "APPROVED",
|
|
19686
|
-
REJECTED_VOIDED: "REJECTED_VOIDED",
|
|
19687
|
-
EXPIRED_VOIDED: "EXPIRED_VOIDED",
|
|
19688
|
-
PARTIAL_QBO_FAILURE: "PARTIAL_QBO_FAILURE"
|
|
21340
|
+
var previewCustomerInvoiceSplits = async (raw) => {
|
|
21341
|
+
const input2 = paymentScheduleSaveInput.strict().parse(raw);
|
|
21342
|
+
emitCommandResult(await api2.paymentSchedule.preview.query(input2));
|
|
19689
21343
|
};
|
|
19690
|
-
var
|
|
19691
|
-
|
|
19692
|
-
|
|
19693
|
-
|
|
19694
|
-
|
|
19695
|
-
|
|
19696
|
-
|
|
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));
|
|
21353
|
+
};
|
|
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 }));
|
|
21372
|
+
};
|
|
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));
|
|
19697
21401
|
};
|
|
19698
21402
|
|
|
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
|
+
|
|
19699
21409
|
// ../utils/src/filter-enums.ts
|
|
19700
21410
|
var ExtendedApprovalStatus = {
|
|
19701
21411
|
...ApprovalStatus,
|
|
@@ -20102,162 +21812,30 @@ var createRichTextFromPlainText = (text) => {
|
|
|
20102
21812
|
const paragraphs = lines.length ? lines : [""];
|
|
20103
21813
|
return {
|
|
20104
21814
|
json: {
|
|
20105
|
-
type: "doc",
|
|
20106
|
-
content: paragraphs.map((line) => ({
|
|
20107
|
-
type: "paragraph",
|
|
20108
|
-
...line ? { content: [{ type: "text", text: line }] } : {}
|
|
20109
|
-
}))
|
|
20110
|
-
},
|
|
20111
|
-
html: paragraphs.map((line) => `<p>${escapeHtml(line)}</p>`).join("")
|
|
20112
|
-
};
|
|
20113
|
-
};
|
|
20114
|
-
var isRichTextJson = (value) => Boolean(value && typeof value === "object" && "json" in value && "html" in value && typeof value.html === "string");
|
|
20115
|
-
|
|
20116
|
-
// src/rich-text.ts
|
|
20117
|
-
var normalizePlainTextRichTextField = ({
|
|
20118
|
-
input: input2,
|
|
20119
|
-
textField,
|
|
20120
|
-
richTextField
|
|
20121
|
-
}) => {
|
|
20122
|
-
if (isRichTextJson(input2[richTextField]) || input2[textField] === undefined) {
|
|
20123
|
-
return input2;
|
|
20124
|
-
}
|
|
20125
|
-
return {
|
|
20126
|
-
...input2,
|
|
20127
|
-
[richTextField]: createRichTextFromPlainText(String(input2[textField] ?? ""))
|
|
20128
|
-
};
|
|
20129
|
-
};
|
|
20130
|
-
|
|
20131
|
-
// src/runtime/error.ts
|
|
20132
|
-
var invocationErrorCodes = new Set([
|
|
20133
|
-
"USAGE",
|
|
20134
|
-
"VALIDATION",
|
|
20135
|
-
"CONFIRMATION_REQUIRED"
|
|
20136
|
-
]);
|
|
20137
|
-
var trpcCodeMap = {
|
|
20138
|
-
BAD_REQUEST: "VALIDATION",
|
|
20139
|
-
CONFLICT: "CONFLICT",
|
|
20140
|
-
FORBIDDEN: "AUTHORIZATION",
|
|
20141
|
-
INTERNAL_SERVER_ERROR: "API",
|
|
20142
|
-
METHOD_NOT_SUPPORTED: "API",
|
|
20143
|
-
NOT_FOUND: "NOT_FOUND",
|
|
20144
|
-
PARSE_ERROR: "VALIDATION",
|
|
20145
|
-
PAYLOAD_TOO_LARGE: "VALIDATION",
|
|
20146
|
-
PRECONDITION_FAILED: "CONFLICT",
|
|
20147
|
-
TIMEOUT: "NETWORK",
|
|
20148
|
-
TOO_MANY_REQUESTS: "API",
|
|
20149
|
-
UNAUTHORIZED: "AUTHENTICATION",
|
|
20150
|
-
UNPROCESSABLE_CONTENT: "VALIDATION"
|
|
20151
|
-
};
|
|
20152
|
-
var cliErrorCodes = new Set([
|
|
20153
|
-
"USAGE",
|
|
20154
|
-
"VALIDATION",
|
|
20155
|
-
"CONFIRMATION_REQUIRED",
|
|
20156
|
-
"AUTHENTICATION",
|
|
20157
|
-
"AUTHORIZATION",
|
|
20158
|
-
"NOT_FOUND",
|
|
20159
|
-
"CONFLICT",
|
|
20160
|
-
"API",
|
|
20161
|
-
"NETWORK",
|
|
20162
|
-
"ABORTED",
|
|
20163
|
-
"INTERRUPTED",
|
|
20164
|
-
"INTERNAL"
|
|
20165
|
-
]);
|
|
20166
|
-
|
|
20167
|
-
class CliRuntimeError extends Error {
|
|
20168
|
-
code;
|
|
20169
|
-
details;
|
|
20170
|
-
requestId;
|
|
20171
|
-
constructor(code, message, options = {}) {
|
|
20172
|
-
super(message);
|
|
20173
|
-
this.name = "CliRuntimeError";
|
|
20174
|
-
this.code = code;
|
|
20175
|
-
this.details = options.details;
|
|
20176
|
-
this.requestId = options.requestId;
|
|
20177
|
-
}
|
|
20178
|
-
}
|
|
20179
|
-
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20180
|
-
var asNonEmptyString = (value) => typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
20181
|
-
var asDetails = (value) => {
|
|
20182
|
-
if (!isRecord(value))
|
|
20183
|
-
return;
|
|
20184
|
-
const details = {};
|
|
20185
|
-
for (const [key, nestedValue] of Object.entries(value)) {
|
|
20186
|
-
if (isJsonValue(nestedValue))
|
|
20187
|
-
details[key] = nestedValue;
|
|
20188
|
-
}
|
|
20189
|
-
return Object.keys(details).length > 0 ? details : undefined;
|
|
20190
|
-
};
|
|
20191
|
-
var isJsonValue = (value) => {
|
|
20192
|
-
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
|
|
20193
|
-
return true;
|
|
20194
|
-
}
|
|
20195
|
-
if (Array.isArray(value))
|
|
20196
|
-
return value.every(isJsonValue);
|
|
20197
|
-
return isRecord(value) && Object.values(value).every(isJsonValue);
|
|
20198
|
-
};
|
|
20199
|
-
var mapErrorCode = (value) => {
|
|
20200
|
-
if (typeof value !== "string")
|
|
20201
|
-
return;
|
|
20202
|
-
if (cliErrorCodes.has(value))
|
|
20203
|
-
return value;
|
|
20204
|
-
if (value in trpcCodeMap)
|
|
20205
|
-
return trpcCodeMap[value];
|
|
20206
|
-
return;
|
|
20207
|
-
};
|
|
20208
|
-
var networkMessagePattern = /\b(fetch failed|network error|unable to connect|econnrefused|econnreset|enotfound|etimedout|socket hang up)\b/i;
|
|
20209
|
-
var isNetworkError = (error61, depth = 0) => {
|
|
20210
|
-
if (depth > 3 || !isRecord(error61))
|
|
20211
|
-
return false;
|
|
20212
|
-
const message = asNonEmptyString(error61.message);
|
|
20213
|
-
if (message !== undefined && networkMessagePattern.test(message))
|
|
20214
|
-
return true;
|
|
20215
|
-
const code = asNonEmptyString(error61.code);
|
|
20216
|
-
if (code !== undefined && networkMessagePattern.test(code))
|
|
20217
|
-
return true;
|
|
20218
|
-
return isNetworkError(error61.cause, depth + 1);
|
|
20219
|
-
};
|
|
20220
|
-
var normalizeCliError = (error61) => {
|
|
20221
|
-
if (error61 instanceof CliRuntimeError) {
|
|
20222
|
-
return {
|
|
20223
|
-
code: error61.code,
|
|
20224
|
-
message: error61.message,
|
|
20225
|
-
...error61.details === undefined ? {} : { details: error61.details },
|
|
20226
|
-
...error61.requestId === undefined ? {} : { requestId: error61.requestId }
|
|
20227
|
-
};
|
|
20228
|
-
}
|
|
20229
|
-
if (isNetworkError(error61)) {
|
|
20230
|
-
return {
|
|
20231
|
-
code: "NETWORK",
|
|
20232
|
-
message: error61 instanceof Error ? error61.message : "The Budget Builder API could not be reached."
|
|
20233
|
-
};
|
|
20234
|
-
}
|
|
20235
|
-
if (isRecord(error61)) {
|
|
20236
|
-
const data = isRecord(error61.data) ? error61.data : undefined;
|
|
20237
|
-
const transportCode = mapErrorCode(data?.code ?? error61.code);
|
|
20238
|
-
if (error61 instanceof Error && data === undefined && transportCode === undefined) {
|
|
20239
|
-
return { code: "INTERNAL", message: error61.message };
|
|
20240
|
-
}
|
|
20241
|
-
const code = transportCode ?? "API";
|
|
20242
|
-
const message = asNonEmptyString(error61.message) ?? "The Budget Builder API request failed.";
|
|
20243
|
-
const requestId = asNonEmptyString(data?.requestId) ?? asNonEmptyString(data?.requestID) ?? asNonEmptyString(error61.requestId);
|
|
20244
|
-
const details = asDetails(data?.details ?? error61.details);
|
|
20245
|
-
return {
|
|
20246
|
-
code,
|
|
20247
|
-
message,
|
|
20248
|
-
...details === undefined ? {} : { details },
|
|
20249
|
-
...requestId === undefined ? {} : { requestId }
|
|
20250
|
-
};
|
|
20251
|
-
}
|
|
20252
|
-
if (error61 instanceof Error) {
|
|
20253
|
-
return { code: "INTERNAL", message: error61.message };
|
|
20254
|
-
}
|
|
20255
|
-
return { code: "INTERNAL", message: "An unexpected CLI error occurred." };
|
|
21815
|
+
type: "doc",
|
|
21816
|
+
content: paragraphs.map((line) => ({
|
|
21817
|
+
type: "paragraph",
|
|
21818
|
+
...line ? { content: [{ type: "text", text: line }] } : {}
|
|
21819
|
+
}))
|
|
21820
|
+
},
|
|
21821
|
+
html: paragraphs.map((line) => `<p>${escapeHtml(line)}</p>`).join("")
|
|
21822
|
+
};
|
|
20256
21823
|
};
|
|
20257
|
-
var
|
|
20258
|
-
|
|
20259
|
-
|
|
20260
|
-
|
|
21824
|
+
var isRichTextJson = (value) => Boolean(value && typeof value === "object" && "json" in value && "html" in value && typeof value.html === "string");
|
|
21825
|
+
|
|
21826
|
+
// src/rich-text.ts
|
|
21827
|
+
var normalizePlainTextRichTextField = ({
|
|
21828
|
+
input: input2,
|
|
21829
|
+
textField,
|
|
21830
|
+
richTextField
|
|
21831
|
+
}) => {
|
|
21832
|
+
if (isRichTextJson(input2[richTextField]) || input2[textField] === undefined) {
|
|
21833
|
+
return input2;
|
|
21834
|
+
}
|
|
21835
|
+
return {
|
|
21836
|
+
...input2,
|
|
21837
|
+
[richTextField]: createRichTextFromPlainText(String(input2[textField] ?? ""))
|
|
21838
|
+
};
|
|
20261
21839
|
};
|
|
20262
21840
|
|
|
20263
21841
|
// src/parse-mutation-payload.ts
|
|
@@ -20316,7 +21894,7 @@ var parseQuotationReferences = (input2, commandName) => {
|
|
|
20316
21894
|
if (!Array.isArray(rawQuotationIds)) {
|
|
20317
21895
|
throw new Error(`${commandName} payload.quotationIds must be a string array.`);
|
|
20318
21896
|
}
|
|
20319
|
-
quotationIds = rawQuotationIds.map((
|
|
21897
|
+
quotationIds = rawQuotationIds.map((id2, index) => requiredString(id2, `${commandName} payload.quotationIds[${index}]`));
|
|
20320
21898
|
if (new Set(quotationIds).size !== quotationIds.length) {
|
|
20321
21899
|
throw new Error(`${commandName} payload.quotationIds must not contain duplicates.`);
|
|
20322
21900
|
}
|
|
@@ -20469,9 +22047,9 @@ function parseCreateBudgetPayload(raw) {
|
|
|
20469
22047
|
}
|
|
20470
22048
|
function parseUpdateBudgetPayload(raw) {
|
|
20471
22049
|
const o = requireObject(raw, "update-budget payload");
|
|
20472
|
-
const
|
|
22050
|
+
const id2 = requiredString(o.id, "update-budget payload.id");
|
|
20473
22051
|
const base = parseCreateBudgetPayload(raw);
|
|
20474
|
-
return { ...base, id };
|
|
22052
|
+
return { ...base, id: id2 };
|
|
20475
22053
|
}
|
|
20476
22054
|
function parseUpdateBudgetCommissionPayload(raw) {
|
|
20477
22055
|
const o = requireObject(raw, "update-budget-commission payload");
|
|
@@ -20563,7 +22141,7 @@ function parseCreateBillPayload(raw, draftOnlyFlag, quotationExempt = false, req
|
|
|
20563
22141
|
throw new Error("create-bill payload.amount must be a number >= 0.");
|
|
20564
22142
|
}
|
|
20565
22143
|
const quotationReferences = parseQuotationReferences(o, "create-bill");
|
|
20566
|
-
const parsedBudgetItemIds = budgetItemIds.map((
|
|
22144
|
+
const parsedBudgetItemIds = budgetItemIds.map((id2, i) => requiredString(id2, `create-bill payload.budgetItemIds[${i}]`));
|
|
20567
22145
|
const lineAmounts = parseBillLineAmounts(o, "create-bill");
|
|
20568
22146
|
validateExplicitBillAllocations({
|
|
20569
22147
|
amount,
|
|
@@ -20635,7 +22213,7 @@ function parseValidateBillSelectionPayload(raw, quotationFlags = {}) {
|
|
|
20635
22213
|
throw new Error("Employee claims cannot include quotation source inputs.");
|
|
20636
22214
|
}
|
|
20637
22215
|
const lineAmounts = parseBillLineAmounts(input2, "validate-bill-selection");
|
|
20638
|
-
const budgetItemIds = Array.isArray(input2.budgetItemIds) ? input2.budgetItemIds.map((
|
|
22216
|
+
const budgetItemIds = Array.isArray(input2.budgetItemIds) ? input2.budgetItemIds.map((id2, index) => requiredString(id2, `validate-bill-selection payload.budgetItemIds[${index}]`)) : [];
|
|
20639
22217
|
const amount = input2.amount === undefined || input2.amount === null ? undefined : Number(input2.amount);
|
|
20640
22218
|
if (lineAmounts || payloadReferences.quotationAllocations) {
|
|
20641
22219
|
if (amount === undefined || !Number.isFinite(amount) || budgetItemIds.length === 0) {
|
|
@@ -20680,7 +22258,7 @@ function parseUpdateBillPaymentEvidencePayload(raw) {
|
|
|
20680
22258
|
var validateQuotationAllocations = (raw, command) => {
|
|
20681
22259
|
const input2 = requireObject(raw, `${command} payload`);
|
|
20682
22260
|
const ids = input2.budgetItemIds;
|
|
20683
|
-
if (!Array.isArray(ids) || ids.length === 0 || ids.some((
|
|
22261
|
+
if (!Array.isArray(ids) || ids.length === 0 || ids.some((id2) => typeof id2 !== "string" || id2.length === 0) || new Set(ids).size !== ids.length) {
|
|
20684
22262
|
throw new CliRuntimeError("VALIDATION", `${command} payload.budgetItemIds must contain each selected budget-item ID exactly once.`);
|
|
20685
22263
|
}
|
|
20686
22264
|
const isCurrency = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 99999999.99 && Math.abs(value * 100 - Math.round(value * 100)) < 0.00000001;
|
|
@@ -20823,206 +22401,6 @@ function parseCompanyUpdatePayload(raw) {
|
|
|
20823
22401
|
return raw;
|
|
20824
22402
|
}
|
|
20825
22403
|
|
|
20826
|
-
// src/runtime/confirmation.ts
|
|
20827
|
-
var effectLabels = {
|
|
20828
|
-
"state-change": "changes Budget Builder state",
|
|
20829
|
-
email: "sends email",
|
|
20830
|
-
"external-write": "writes to an external system",
|
|
20831
|
-
delete: "deletes data",
|
|
20832
|
-
"financial-write": "creates or changes financial records"
|
|
20833
|
-
};
|
|
20834
|
-
var isEffectAllowed = (effect, permissions) => {
|
|
20835
|
-
switch (effect) {
|
|
20836
|
-
case "state-change":
|
|
20837
|
-
return permissions.allowStateChange === true;
|
|
20838
|
-
case "email":
|
|
20839
|
-
return permissions.allowEmail === true;
|
|
20840
|
-
case "external-write":
|
|
20841
|
-
return permissions.allowExternalWrite === true;
|
|
20842
|
-
case "delete":
|
|
20843
|
-
return permissions.allowDelete === true;
|
|
20844
|
-
case "financial-write":
|
|
20845
|
-
return permissions.allowFinancialWrite === true;
|
|
20846
|
-
}
|
|
20847
|
-
};
|
|
20848
|
-
var uniqueEffects = (effects) => [...new Set(effects)];
|
|
20849
|
-
var getRequiredEffectPermissions = (effects, permissions) => uniqueEffects(effects).filter((effect) => !isEffectAllowed(effect, permissions));
|
|
20850
|
-
var formatConfirmationPreview = (request) => [
|
|
20851
|
-
"Budget Builder workflow preview",
|
|
20852
|
-
`Action: ${request.action}`,
|
|
20853
|
-
`Target: ${request.target?.trim() || "not specified"}`,
|
|
20854
|
-
`Effects: ${uniqueEffects(request.effects).map((effect) => effectLabels[effect]).join("; ") || "none"}`,
|
|
20855
|
-
...request.details === undefined ? [] : [`Details: ${request.details}`]
|
|
20856
|
-
].join(`
|
|
20857
|
-
`);
|
|
20858
|
-
var confirmSensitiveCommand = async (runtime2, request) => {
|
|
20859
|
-
const requiredPermissions = getRequiredEffectPermissions(request.effects, request.permissions);
|
|
20860
|
-
if (request.effects.length === 0) {
|
|
20861
|
-
return { confirmed: true, requiredPermissions };
|
|
20862
|
-
}
|
|
20863
|
-
const target = request.target?.trim();
|
|
20864
|
-
if (!runtime2.isTTY) {
|
|
20865
|
-
if (target === undefined) {
|
|
20866
|
-
throw new CliRuntimeError("CONFIRMATION_REQUIRED", "Non-interactive sensitive commands require an explicit target.", {
|
|
20867
|
-
details: {
|
|
20868
|
-
action: request.action,
|
|
20869
|
-
effects: requiredPermissions,
|
|
20870
|
-
missingFlags: requiredPermissions.map((effect) => `--allow-${effect}`),
|
|
20871
|
-
target: "required"
|
|
20872
|
-
}
|
|
20873
|
-
});
|
|
20874
|
-
}
|
|
20875
|
-
if (requiredPermissions.length === 0) {
|
|
20876
|
-
return { confirmed: true, requiredPermissions };
|
|
20877
|
-
}
|
|
20878
|
-
const missing = requiredPermissions.map((effect) => `--allow-${effect}`);
|
|
20879
|
-
throw new CliRuntimeError("CONFIRMATION_REQUIRED", "Non-interactive sensitive commands require an explicit target and all applicable allow flags.", {
|
|
20880
|
-
details: {
|
|
20881
|
-
action: request.action,
|
|
20882
|
-
effects: requiredPermissions,
|
|
20883
|
-
missingFlags: missing
|
|
20884
|
-
}
|
|
20885
|
-
});
|
|
20886
|
-
}
|
|
20887
|
-
if (requiredPermissions.length === 0) {
|
|
20888
|
-
return { confirmed: true, requiredPermissions };
|
|
20889
|
-
}
|
|
20890
|
-
const preview = formatConfirmationPreview(request);
|
|
20891
|
-
runtime2.stderr.write(`${preview}
|
|
20892
|
-
`);
|
|
20893
|
-
if (runtime2.prompt === undefined) {
|
|
20894
|
-
throw new CliRuntimeError("CONFIRMATION_REQUIRED", "This command needs an interactive confirmation prompt.");
|
|
20895
|
-
}
|
|
20896
|
-
const answer = await runtime2.prompt("Type CONFIRM to continue: ");
|
|
20897
|
-
if (answer !== "CONFIRM") {
|
|
20898
|
-
throw new CliRuntimeError("ABORTED", "Command aborted by user.");
|
|
20899
|
-
}
|
|
20900
|
-
return { confirmed: true, preview, requiredPermissions };
|
|
20901
|
-
};
|
|
20902
|
-
// src/runtime/sanitize.ts
|
|
20903
|
-
var sensitiveKeys = new Set([
|
|
20904
|
-
"apikey",
|
|
20905
|
-
"authorization",
|
|
20906
|
-
"bb_api_key",
|
|
20907
|
-
"password",
|
|
20908
|
-
"paymentproof",
|
|
20909
|
-
"paymentproofattachments",
|
|
20910
|
-
"paymentproofpath",
|
|
20911
|
-
"paymentreference",
|
|
20912
|
-
"payload",
|
|
20913
|
-
"proof",
|
|
20914
|
-
"receipt",
|
|
20915
|
-
"secret",
|
|
20916
|
-
"token"
|
|
20917
|
-
]);
|
|
20918
|
-
|
|
20919
|
-
// src/runtime/output.ts
|
|
20920
|
-
var serialize = (value) => `${JSON.stringify(value, null, 2)}
|
|
20921
|
-
`;
|
|
20922
|
-
var createSuccessEnvelope = (data, invocation) => ({
|
|
20923
|
-
ok: true,
|
|
20924
|
-
data,
|
|
20925
|
-
meta: {
|
|
20926
|
-
command: invocation.command,
|
|
20927
|
-
cliVersion: invocation.cliVersion,
|
|
20928
|
-
apiBaseUrl: invocation.apiBaseUrl
|
|
20929
|
-
}
|
|
20930
|
-
});
|
|
20931
|
-
var emitCliSuccess = (runtime2, invocation, data) => {
|
|
20932
|
-
runtime2.stdout.write(serialize(invocation.legacy ? data : createSuccessEnvelope(data, invocation)));
|
|
20933
|
-
return 0;
|
|
20934
|
-
};
|
|
20935
|
-
var emitCliError = (runtime2, invocation, error61) => {
|
|
20936
|
-
const normalized = normalizeCliError(error61);
|
|
20937
|
-
if (invocation.legacy) {
|
|
20938
|
-
runtime2.stderr.write(serialize({ error: normalized.message }));
|
|
20939
|
-
} else {
|
|
20940
|
-
const envelope = {
|
|
20941
|
-
ok: false,
|
|
20942
|
-
error: normalized,
|
|
20943
|
-
meta: { command: invocation.command }
|
|
20944
|
-
};
|
|
20945
|
-
runtime2.stderr.write(serialize(envelope));
|
|
20946
|
-
}
|
|
20947
|
-
return exitCodeForCliError(normalized);
|
|
20948
|
-
};
|
|
20949
|
-
// src/runtime/process-runtime.ts
|
|
20950
|
-
import { createInterface } from "readline/promises";
|
|
20951
|
-
var createProcessRuntime = () => ({
|
|
20952
|
-
stdout: {
|
|
20953
|
-
write: (value) => {
|
|
20954
|
-
process.stdout.write(value);
|
|
20955
|
-
}
|
|
20956
|
-
},
|
|
20957
|
-
stderr: {
|
|
20958
|
-
write: (value) => {
|
|
20959
|
-
process.stderr.write(value);
|
|
20960
|
-
}
|
|
20961
|
-
},
|
|
20962
|
-
isTTY: Boolean(process.stdin.isTTY && process.stderr.isTTY),
|
|
20963
|
-
env: process.env,
|
|
20964
|
-
prompt: async (message) => {
|
|
20965
|
-
const prompt = createInterface({
|
|
20966
|
-
input: process.stdin,
|
|
20967
|
-
output: process.stderr
|
|
20968
|
-
});
|
|
20969
|
-
try {
|
|
20970
|
-
return await prompt.question(message);
|
|
20971
|
-
} finally {
|
|
20972
|
-
prompt.close();
|
|
20973
|
-
}
|
|
20974
|
-
},
|
|
20975
|
-
now: () => new Date
|
|
20976
|
-
});
|
|
20977
|
-
// src/runtime/session.ts
|
|
20978
|
-
var activeSession;
|
|
20979
|
-
var setActiveCliSession = (session) => {
|
|
20980
|
-
activeSession = {
|
|
20981
|
-
...session,
|
|
20982
|
-
confirmedEffects: new Set
|
|
20983
|
-
};
|
|
20984
|
-
};
|
|
20985
|
-
var clearActiveCliSession = () => {
|
|
20986
|
-
activeSession = undefined;
|
|
20987
|
-
};
|
|
20988
|
-
var getActiveCliSession = () => activeSession;
|
|
20989
|
-
var requireSession = () => {
|
|
20990
|
-
if (activeSession === undefined) {
|
|
20991
|
-
throw new Error("Budget Builder CLI runtime session is not configured.");
|
|
20992
|
-
}
|
|
20993
|
-
return activeSession;
|
|
20994
|
-
};
|
|
20995
|
-
var toJsonValue = (value) => {
|
|
20996
|
-
const serialized = JSON.stringify(value, (_key, nestedValue) => typeof nestedValue === "bigint" ? nestedValue.toString() : nestedValue);
|
|
20997
|
-
if (serialized === undefined)
|
|
20998
|
-
return null;
|
|
20999
|
-
return JSON.parse(serialized);
|
|
21000
|
-
};
|
|
21001
|
-
var emitCommandResult = (data) => {
|
|
21002
|
-
const session = requireSession();
|
|
21003
|
-
return emitCliSuccess(session.runtime, session.invocation, toJsonValue(data));
|
|
21004
|
-
};
|
|
21005
|
-
var emitCommandError = (error61) => {
|
|
21006
|
-
const session = requireSession();
|
|
21007
|
-
return emitCliError(session.runtime, session.invocation, error61);
|
|
21008
|
-
};
|
|
21009
|
-
var confirmCurrentCommand = async (input2) => {
|
|
21010
|
-
const session = requireSession();
|
|
21011
|
-
const effects = input2.effects ?? session.effects;
|
|
21012
|
-
const unconfirmedEffects = effects.filter((effect) => !session.confirmedEffects.has(effect));
|
|
21013
|
-
if (unconfirmedEffects.length === 0)
|
|
21014
|
-
return;
|
|
21015
|
-
await confirmSensitiveCommand(session.runtime, {
|
|
21016
|
-
action: input2.action,
|
|
21017
|
-
target: input2.target,
|
|
21018
|
-
details: input2.details,
|
|
21019
|
-
effects: unconfirmedEffects,
|
|
21020
|
-
permissions: session.permissions
|
|
21021
|
-
});
|
|
21022
|
-
for (const effect of unconfirmedEffects) {
|
|
21023
|
-
session.confirmedEffects.add(effect);
|
|
21024
|
-
}
|
|
21025
|
-
};
|
|
21026
22404
|
// src/commands/index.ts
|
|
21027
22405
|
import { contentType } from "mime-types";
|
|
21028
22406
|
var BUDGET = "BUDGET";
|
|
@@ -21250,7 +22628,7 @@ var combineDateAndTime = (day, time3) => {
|
|
|
21250
22628
|
d.setHours(time3.getHours(), time3.getMinutes(), time3.getSeconds(), 0);
|
|
21251
22629
|
return d;
|
|
21252
22630
|
};
|
|
21253
|
-
var
|
|
22631
|
+
var formatOptionalProjectPaxInput2 = (pax) => {
|
|
21254
22632
|
if (typeof pax !== "number" || !Number.isFinite(pax) || pax <= 0) {
|
|
21255
22633
|
return "";
|
|
21256
22634
|
}
|
|
@@ -21282,7 +22660,7 @@ var billAttachmentContentTypeForFileName = (fileName) => {
|
|
|
21282
22660
|
return resolvedContentType;
|
|
21283
22661
|
};
|
|
21284
22662
|
var PAYMENT_PROOF_MAX_SIZE = 20 * 1024 * 1024;
|
|
21285
|
-
var
|
|
22663
|
+
var QUOTATION_ATTACHMENT_MAX_SIZE2 = 20 * 1024 * 1024;
|
|
21286
22664
|
var quotationAttachmentContentTypeForFileName = (fileName) => {
|
|
21287
22665
|
const resolvedContentType = contentTypeHeaderForFileName(fileName);
|
|
21288
22666
|
if (resolvedContentType === "application/pdf" || resolvedContentType === "image/jpeg" || resolvedContentType === "image/png") {
|
|
@@ -21349,31 +22727,31 @@ Confirm the user explicitly approved this exact action, then type "${expected}"
|
|
|
21349
22727
|
}
|
|
21350
22728
|
};
|
|
21351
22729
|
var assertLockedBudgetChangeConfirmed = async ({
|
|
21352
|
-
budget,
|
|
22730
|
+
budget: budget2,
|
|
21353
22731
|
action
|
|
21354
22732
|
}) => {
|
|
21355
|
-
if (!LOCKED_BUDGET_STATUSES_REQUIRING_CONFIRMATION.has(
|
|
22733
|
+
if (!LOCKED_BUDGET_STATUSES_REQUIRING_CONFIRMATION.has(budget2.status)) {
|
|
21356
22734
|
return;
|
|
21357
22735
|
}
|
|
21358
|
-
const statusLabel = lockedBudgetStatusLabel(
|
|
22736
|
+
const statusLabel = lockedBudgetStatusLabel(budget2.status);
|
|
21359
22737
|
if (getActiveCliSession()?.invocation.legacy !== true) {
|
|
21360
22738
|
await confirmCurrentCommand({
|
|
21361
22739
|
action,
|
|
21362
|
-
target: `budget ${
|
|
21363
|
-
details: `Changes locked budget "${
|
|
22740
|
+
target: `budget ${budget2.id}`,
|
|
22741
|
+
details: `Changes locked budget "${budget2.name}" in status ${statusLabel}.`,
|
|
21364
22742
|
effects: ["state-change"]
|
|
21365
22743
|
});
|
|
21366
22744
|
return;
|
|
21367
22745
|
}
|
|
21368
22746
|
if (!process.stdin.isTTY || !process.stderr.isTTY) {
|
|
21369
|
-
throw new Error(`${action} would change locked budget "${
|
|
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.`);
|
|
21370
22748
|
}
|
|
21371
22749
|
const rl = createInterface2({
|
|
21372
22750
|
input: process.stdin,
|
|
21373
22751
|
output: process.stderr
|
|
21374
22752
|
});
|
|
21375
22753
|
try {
|
|
21376
|
-
const answer = await rl.question(`${action} will change locked budget "${
|
|
22754
|
+
const answer = await rl.question(`${action} will change locked budget "${budget2.name}" (${budget2.id}) with status ${statusLabel}. Type "yes" to continue: `);
|
|
21377
22755
|
if (answer.trim().toLowerCase() !== "yes") {
|
|
21378
22756
|
throw new Error("Aborted.");
|
|
21379
22757
|
}
|
|
@@ -21382,21 +22760,21 @@ var assertLockedBudgetChangeConfirmed = async ({
|
|
|
21382
22760
|
}
|
|
21383
22761
|
};
|
|
21384
22762
|
var confirmLockedBudgetChangeByBudgetId = async (budgetId, action) => {
|
|
21385
|
-
const
|
|
22763
|
+
const budget2 = await api2.budget.getBudget.query({ id: budgetId });
|
|
21386
22764
|
await assertLockedBudgetChangeConfirmed({
|
|
21387
22765
|
budget: {
|
|
21388
|
-
id:
|
|
21389
|
-
name:
|
|
21390
|
-
status:
|
|
22766
|
+
id: budget2.id,
|
|
22767
|
+
name: budget2.name,
|
|
22768
|
+
status: budget2.status
|
|
21391
22769
|
},
|
|
21392
22770
|
action
|
|
21393
22771
|
});
|
|
21394
22772
|
};
|
|
21395
22773
|
var confirmLockedBudgetChangeByBudgetItemId = async (budgetItemId, action) => {
|
|
21396
|
-
const { budget } = await api2.budgetItem.getBudgetItemContext.query({
|
|
22774
|
+
const { budget: budget2 } = await api2.budgetItem.getBudgetItemContext.query({
|
|
21397
22775
|
budgetItemId
|
|
21398
22776
|
});
|
|
21399
|
-
await assertLockedBudgetChangeConfirmed({ budget, action });
|
|
22777
|
+
await assertLockedBudgetChangeConfirmed({ budget: budget2, action });
|
|
21400
22778
|
};
|
|
21401
22779
|
var updateProjectWithEstimateInvalidationConfirmation = async (input2) => {
|
|
21402
22780
|
try {
|
|
@@ -21433,12 +22811,12 @@ async function listBudgets(opts) {
|
|
|
21433
22811
|
const result = await api2.budget.getAllBudgets.query(input2);
|
|
21434
22812
|
out(result);
|
|
21435
22813
|
}
|
|
21436
|
-
async function getBudget(
|
|
21437
|
-
const [
|
|
21438
|
-
api2.budget.getBudget.query({ id }),
|
|
21439
|
-
api2.budget.getBudgetItems.query({ budgetId:
|
|
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 })
|
|
21440
22818
|
]);
|
|
21441
|
-
out({ budget, items });
|
|
22819
|
+
out({ budget: budget2, items });
|
|
21442
22820
|
}
|
|
21443
22821
|
async function updateBudgetStatus(budgetId, status, opts) {
|
|
21444
22822
|
await assertSensitiveWorkflowConfirmed({
|
|
@@ -21463,28 +22841,31 @@ async function updateBudgetStatus(budgetId, status, opts) {
|
|
|
21463
22841
|
projectInvoiceSettings: opts.projectInvoiceSettings
|
|
21464
22842
|
}
|
|
21465
22843
|
});
|
|
21466
|
-
out(
|
|
22844
|
+
out({
|
|
22845
|
+
...result,
|
|
22846
|
+
invoiceSetup: result.newStatus === "ESTIMATE_ACCEPTED" && result.hasQuickbooksEstimate ? invoiceSplitSetupNextAction(budgetId) : null
|
|
22847
|
+
});
|
|
21467
22848
|
}
|
|
21468
22849
|
var assertBudgetActionReadiness = async ({
|
|
21469
22850
|
budgetId,
|
|
21470
22851
|
requireQuickbooksProject,
|
|
21471
22852
|
actionLabel
|
|
21472
22853
|
}) => {
|
|
21473
|
-
const
|
|
21474
|
-
const hasNoItems =
|
|
22854
|
+
const budget2 = await api2.budget.getBudget.query({ id: budgetId });
|
|
22855
|
+
const hasNoItems = budget2.budgetItems.length === 0;
|
|
21475
22856
|
if (hasNoItems) {
|
|
21476
22857
|
throw new Error(`Cannot ${actionLabel}: there are no line items in this budget.`);
|
|
21477
22858
|
}
|
|
21478
|
-
const hasZeroCostItems =
|
|
22859
|
+
const hasZeroCostItems = budget2.budgetItems.some((item2) => Number(item2.cost ?? 0) === 0);
|
|
21479
22860
|
if (hasZeroCostItems) {
|
|
21480
22861
|
throw new Error(`Cannot ${actionLabel}: all line items must have a cost greater than zero.`);
|
|
21481
22862
|
}
|
|
21482
|
-
const inactiveSuppliers =
|
|
22863
|
+
const inactiveSuppliers = budget2.budgetItems.filter((item2) => !item2.supplier.active).map((item2) => item2.supplier.name);
|
|
21483
22864
|
const uniqueInactiveSuppliers = [...new Set(inactiveSuppliers)];
|
|
21484
22865
|
if (uniqueInactiveSuppliers.length > 0) {
|
|
21485
22866
|
throw new Error(`Cannot ${actionLabel}: inactive suppliers found (${uniqueInactiveSuppliers.join(", ")}).`);
|
|
21486
22867
|
}
|
|
21487
|
-
if (requireQuickbooksProject && !
|
|
22868
|
+
if (requireQuickbooksProject && !budget2.project.quickbooksProjectId) {
|
|
21488
22869
|
throw new Error("Cannot create estimate: project is not linked to QuickBooks yet.");
|
|
21489
22870
|
}
|
|
21490
22871
|
};
|
|
@@ -21498,32 +22879,32 @@ var runBudgetPreflight = async (budgetId, preflight) => {
|
|
|
21498
22879
|
await api2.contactPerson.updateContactPerson.mutate(preflight.contactPerson);
|
|
21499
22880
|
}
|
|
21500
22881
|
if (preflight.eventDetails) {
|
|
21501
|
-
const
|
|
21502
|
-
const
|
|
21503
|
-
id:
|
|
22882
|
+
const budget2 = await api2.budget.getBudget.query({ id: budgetId });
|
|
22883
|
+
const project2 = await api2.project.getProjectById.query({
|
|
22884
|
+
id: budget2.projectId
|
|
21504
22885
|
});
|
|
21505
22886
|
const day = new Date(preflight.eventDetails.date);
|
|
21506
22887
|
const start = new Date(preflight.eventDetails.startTime);
|
|
21507
22888
|
const end = new Date(preflight.eventDetails.endTime);
|
|
21508
22889
|
const payload = {
|
|
21509
|
-
id:
|
|
21510
|
-
name:
|
|
21511
|
-
description:
|
|
22890
|
+
id: project2.id,
|
|
22891
|
+
name: project2.name,
|
|
22892
|
+
description: project2.description ?? undefined,
|
|
21512
22893
|
dateRange: {
|
|
21513
22894
|
from: combineDateAndTime(day, start),
|
|
21514
22895
|
to: combineDateAndTime(day, end)
|
|
21515
22896
|
},
|
|
21516
|
-
companyId:
|
|
21517
|
-
contactPersonId:
|
|
21518
|
-
asanaTaskId:
|
|
21519
|
-
insideSalesId:
|
|
21520
|
-
businessDevelopmentId:
|
|
21521
|
-
venue:
|
|
21522
|
-
pax:
|
|
21523
|
-
...
|
|
21524
|
-
slackChannelId:
|
|
21525
|
-
slackChannelUrl:
|
|
21526
|
-
slackChannelName:
|
|
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
|
|
21527
22908
|
} : {}
|
|
21528
22909
|
};
|
|
21529
22910
|
await updateProjectWithEstimateInvalidationConfirmation(payload);
|
|
@@ -21584,31 +22965,31 @@ async function listApprovals(opts) {
|
|
|
21584
22965
|
const typeFilter = opts.type === undefined || opts.type === "ALL" ? (_n) => true : (n) => n.type === opts.type;
|
|
21585
22966
|
const filtered = pending.filter(typeFilter).filter((n) => n.type !== BUDGET || !n.budget?.quickbooksEstimateId);
|
|
21586
22967
|
const items = filtered.map((n) => {
|
|
21587
|
-
let
|
|
22968
|
+
let id2;
|
|
21588
22969
|
let name;
|
|
21589
22970
|
if (n.type === BUDGET && n.budget) {
|
|
21590
|
-
|
|
22971
|
+
id2 = n.budgetId ?? n.id;
|
|
21591
22972
|
name = n.budget.name;
|
|
21592
22973
|
} else if (n.type === BILL && n.bill) {
|
|
21593
|
-
|
|
22974
|
+
id2 = n.bill.id;
|
|
21594
22975
|
name = `Bill ${n.bill.invoiceNumber ?? n.bill.id.slice(0, 8)}`;
|
|
21595
22976
|
} else if (n.type === SUPPLIER && n.supplier) {
|
|
21596
|
-
|
|
22977
|
+
id2 = n.supplier.id;
|
|
21597
22978
|
name = n.supplier.name;
|
|
21598
22979
|
} else if (n.type === QUOTATION && n.supplierQuotation) {
|
|
21599
|
-
|
|
22980
|
+
id2 = n.supplierQuotation.id;
|
|
21600
22981
|
name = `Quotation ${n.supplierQuotation.supplier.name}`;
|
|
21601
22982
|
} else if (n.type === CUSTOMER_INVOICE && n.customerInvoiceBatch) {
|
|
21602
|
-
|
|
22983
|
+
id2 = n.customerInvoiceBatch.id;
|
|
21603
22984
|
name = `Customer invoice ${n.customerInvoiceBatch.project.name}`;
|
|
21604
22985
|
} else {
|
|
21605
|
-
|
|
22986
|
+
id2 = n.id;
|
|
21606
22987
|
name = "Unknown";
|
|
21607
22988
|
}
|
|
21608
22989
|
return {
|
|
21609
22990
|
approvalId: n.id,
|
|
21610
22991
|
type: n.type,
|
|
21611
|
-
id,
|
|
22992
|
+
id: id2,
|
|
21612
22993
|
name,
|
|
21613
22994
|
requestedBy: n.requester?.name ?? n.requester?.email ?? "Unknown",
|
|
21614
22995
|
requestedAt: typeof n.createdAt === "string" ? n.createdAt : n.createdAt instanceof Date ? n.createdAt.toISOString() : String(n.createdAt)
|
|
@@ -21637,7 +23018,7 @@ async function createBillApproval(billId) {
|
|
|
21637
23018
|
details: "and send approval request emails"
|
|
21638
23019
|
});
|
|
21639
23020
|
const result = await api2.bill.createApproval.mutate({ billId });
|
|
21640
|
-
const approvalIds = (result.results ?? []).map((
|
|
23021
|
+
const approvalIds = (result.results ?? []).map((item2) => ({ id: item2.id }));
|
|
21641
23022
|
let email3;
|
|
21642
23023
|
if (approvalIds.length === 0) {
|
|
21643
23024
|
email3 = createApprovalEmailDeliveryFailure("No approval ids returned");
|
|
@@ -21730,8 +23111,8 @@ async function updateBillStatus(opts) {
|
|
|
21730
23111
|
entity: `bill ${opts.id}`,
|
|
21731
23112
|
details: `to ${opts.status}`
|
|
21732
23113
|
});
|
|
21733
|
-
const
|
|
21734
|
-
const paymentProof =
|
|
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;
|
|
21735
23116
|
try {
|
|
21736
23117
|
const result = await api2.bill.updateStatus.mutate({
|
|
21737
23118
|
id: opts.id,
|
|
@@ -21744,8 +23125,8 @@ async function updateBillStatus(opts) {
|
|
|
21744
23125
|
});
|
|
21745
23126
|
out(result);
|
|
21746
23127
|
} catch (error61) {
|
|
21747
|
-
await rethrowAfterStagedPaymentProofCleanup(error61,
|
|
21748
|
-
await deleteStagedPaymentProof(
|
|
23128
|
+
await rethrowAfterStagedPaymentProofCleanup(error61, bill2 && paymentProof ? async () => {
|
|
23129
|
+
await deleteStagedPaymentProof(bill2.projectId, paymentProof.key);
|
|
21749
23130
|
} : undefined);
|
|
21750
23131
|
}
|
|
21751
23132
|
}
|
|
@@ -21763,15 +23144,15 @@ async function patchBillPayment(opts) {
|
|
|
21763
23144
|
const result = await api2.bill.patchPayment.mutate(input2);
|
|
21764
23145
|
out(result);
|
|
21765
23146
|
}
|
|
21766
|
-
async function patchBillInvoiceNumber(
|
|
23147
|
+
async function patchBillInvoiceNumber(id2, invoiceNumber) {
|
|
21767
23148
|
const result = await api2.bill.patchInvoiceNumber.mutate({
|
|
21768
|
-
id,
|
|
23149
|
+
id: id2,
|
|
21769
23150
|
invoiceNumber
|
|
21770
23151
|
});
|
|
21771
23152
|
out(result);
|
|
21772
23153
|
}
|
|
21773
|
-
async function getBillAttachments(
|
|
21774
|
-
const result = await api2.bill.getAttachments.query({ id });
|
|
23154
|
+
async function getBillAttachments(id2) {
|
|
23155
|
+
const result = await api2.bill.getAttachments.query({ id: id2 });
|
|
21775
23156
|
out(result);
|
|
21776
23157
|
}
|
|
21777
23158
|
async function stageBillAttachmentsFromPaths(projectId, filePaths) {
|
|
@@ -21887,7 +23268,7 @@ async function uploadBillDocumentsFromPaths({
|
|
|
21887
23268
|
async function uploadQuotationAttachmentFromPath(projectId, filePath) {
|
|
21888
23269
|
const buf = await readFile2(filePath);
|
|
21889
23270
|
const fileName = basename(filePath);
|
|
21890
|
-
if (buf.byteLength >
|
|
23271
|
+
if (buf.byteLength > QUOTATION_ATTACHMENT_MAX_SIZE2) {
|
|
21891
23272
|
throw new Error("Quotation attachment must be 20MB or smaller.");
|
|
21892
23273
|
}
|
|
21893
23274
|
const attachmentContentType = quotationAttachmentContentTypeForFileName(fileName);
|
|
@@ -21992,13 +23373,16 @@ async function uploadBudgetWinProofFromPath(budgetId, filePath, opts) {
|
|
|
21992
23373
|
projectInvoiceSettings: opts.projectInvoiceSettings
|
|
21993
23374
|
}
|
|
21994
23375
|
});
|
|
21995
|
-
out(
|
|
23376
|
+
out({
|
|
23377
|
+
...result,
|
|
23378
|
+
invoiceSetup: result.hasQuickbooksEstimate ? invoiceSplitSetupNextAction(budgetId) : null
|
|
23379
|
+
});
|
|
21996
23380
|
}
|
|
21997
23381
|
async function markBudgetWonWithProof(budgetId, filePath, opts) {
|
|
21998
23382
|
await uploadBudgetWinProofFromPath(budgetId, filePath, opts);
|
|
21999
23383
|
}
|
|
22000
|
-
async function getBillDetails(
|
|
22001
|
-
const result = await api2.bill.getById.query({ id });
|
|
23384
|
+
async function getBillDetails(id2) {
|
|
23385
|
+
const result = await api2.bill.getById.query({ id: id2 });
|
|
22002
23386
|
out(result);
|
|
22003
23387
|
}
|
|
22004
23388
|
async function getBudgetItemsOnly(budgetId) {
|
|
@@ -22064,7 +23448,7 @@ async function createBudgetApproval(budgetId, preflight) {
|
|
|
22064
23448
|
actionLabel: "request approval"
|
|
22065
23449
|
});
|
|
22066
23450
|
const result = await api2.budget.createBudgetApproval.mutate({ budgetId });
|
|
22067
|
-
const approvalIds = (result.results ?? []).map((
|
|
23451
|
+
const approvalIds = (result.results ?? []).map((item2) => ({ id: item2.id }));
|
|
22068
23452
|
let email3;
|
|
22069
23453
|
if (approvalIds.length === 0) {
|
|
22070
23454
|
email3 = createApprovalEmailDeliveryFailure("No approval ids returned");
|
|
@@ -22149,8 +23533,8 @@ async function updateBudgetCategory(opts) {
|
|
|
22149
23533
|
const result = await api2.budget.updateBudgetCategory.mutate(opts);
|
|
22150
23534
|
out(result);
|
|
22151
23535
|
}
|
|
22152
|
-
async function deleteBudgetCategory(
|
|
22153
|
-
const result = await api2.budget.deleteBudgetCategory.mutate({ id });
|
|
23536
|
+
async function deleteBudgetCategory(id2) {
|
|
23537
|
+
const result = await api2.budget.deleteBudgetCategory.mutate({ id: id2 });
|
|
22154
23538
|
out(result);
|
|
22155
23539
|
}
|
|
22156
23540
|
async function updateBudgetCommissionFromPayload(raw) {
|
|
@@ -22291,8 +23675,8 @@ async function listQuotations(opts) {
|
|
|
22291
23675
|
const result = await api2.quotation.list.query(opts);
|
|
22292
23676
|
out(result);
|
|
22293
23677
|
}
|
|
22294
|
-
async function getQuotationDetails(
|
|
22295
|
-
const result = await api2.quotation.getById.query({ id });
|
|
23678
|
+
async function getQuotationDetails(id2) {
|
|
23679
|
+
const result = await api2.quotation.getById.query({ id: id2 });
|
|
22296
23680
|
out(result);
|
|
22297
23681
|
}
|
|
22298
23682
|
async function createQuotationFromPayload(raw) {
|
|
@@ -22305,24 +23689,24 @@ var updateQuotationFromPayload = async (raw) => {
|
|
|
22305
23689
|
const result = await api2.quotation.updateDraftOrRejected.mutate(input2);
|
|
22306
23690
|
out(result);
|
|
22307
23691
|
};
|
|
22308
|
-
var deleteQuotationById = async (
|
|
22309
|
-
const result = await api2.quotation.delete.mutate({ id });
|
|
23692
|
+
var deleteQuotationById = async (id2) => {
|
|
23693
|
+
const result = await api2.quotation.delete.mutate({ id: id2 });
|
|
22310
23694
|
out(result);
|
|
22311
23695
|
};
|
|
22312
|
-
async function submitQuotation(
|
|
23696
|
+
async function submitQuotation(id2, overBudgetOverrideReason) {
|
|
22313
23697
|
await assertSensitiveWorkflowConfirmed({
|
|
22314
23698
|
action: "Submit quotation for approval",
|
|
22315
|
-
entity: `quotation ${
|
|
23699
|
+
entity: `quotation ${id2}`
|
|
22316
23700
|
});
|
|
22317
23701
|
const result = await api2.quotation.submitForApproval.mutate({
|
|
22318
|
-
id,
|
|
23702
|
+
id: id2,
|
|
22319
23703
|
overBudgetOverrideReason
|
|
22320
23704
|
});
|
|
22321
23705
|
out(result);
|
|
22322
23706
|
}
|
|
22323
|
-
async function downloadQuotationPdf(
|
|
23707
|
+
async function downloadQuotationPdf(id2, fileRole) {
|
|
22324
23708
|
const result = await api2.quotation.getSignedDownloadUrl.query({
|
|
22325
|
-
id,
|
|
23709
|
+
id: id2,
|
|
22326
23710
|
fileRole
|
|
22327
23711
|
});
|
|
22328
23712
|
out(result);
|
|
@@ -22484,28 +23868,28 @@ var updateBillPaymentEvidenceFromPayload = async (raw) => {
|
|
|
22484
23868
|
const result = await api2.bill.updatePaymentEvidence.mutate(input2);
|
|
22485
23869
|
out(result);
|
|
22486
23870
|
};
|
|
22487
|
-
async function deleteBillById(
|
|
22488
|
-
const result = await api2.bill.delete.mutate({ id });
|
|
23871
|
+
async function deleteBillById(id2) {
|
|
23872
|
+
const result = await api2.bill.delete.mutate({ id: id2 });
|
|
22489
23873
|
out(result);
|
|
22490
23874
|
}
|
|
22491
23875
|
async function createSupplierFromPayload(raw) {
|
|
22492
23876
|
const input2 = parseSupplierCreatePayload(raw);
|
|
22493
23877
|
const created = await api2.supplier.createSupplier.mutate(input2);
|
|
22494
|
-
const
|
|
22495
|
-
if (
|
|
23878
|
+
const supplier2 = created.result;
|
|
23879
|
+
if (supplier2.status !== "PENDING_APPROVAL") {
|
|
22496
23880
|
out(created);
|
|
22497
23881
|
return;
|
|
22498
23882
|
}
|
|
22499
23883
|
await assertSensitiveWorkflowConfirmed({
|
|
22500
23884
|
action: "Create supplier approval request",
|
|
22501
|
-
entity: `supplier ${
|
|
23885
|
+
entity: `supplier ${supplier2.id}`,
|
|
22502
23886
|
details: "and send approval request emails"
|
|
22503
23887
|
});
|
|
22504
23888
|
const approval = await api2.supplier.createSupplierApproval.mutate({
|
|
22505
|
-
supplierId:
|
|
23889
|
+
supplierId: supplier2.id
|
|
22506
23890
|
});
|
|
22507
23891
|
let emailResult;
|
|
22508
|
-
const approvalIds = (approval.results ?? []).map((
|
|
23892
|
+
const approvalIds = (approval.results ?? []).map((item2) => ({ id: item2.id }));
|
|
22509
23893
|
if (approvalIds.length === 0) {
|
|
22510
23894
|
emailResult = createApprovalEmailDeliveryFailure("No approval ids returned");
|
|
22511
23895
|
} else {
|
|
@@ -22623,7 +24007,7 @@ async function rejectBudget(budgetId, reason) {
|
|
|
22623
24007
|
});
|
|
22624
24008
|
}
|
|
22625
24009
|
async function getSupplierDetails(supplierId) {
|
|
22626
|
-
const
|
|
24010
|
+
const supplier2 = await api2.supplier.getSupplierById.query({
|
|
22627
24011
|
id: supplierId
|
|
22628
24012
|
});
|
|
22629
24013
|
const respondedStatuses = new Set([
|
|
@@ -22631,14 +24015,14 @@ async function getSupplierDetails(supplierId) {
|
|
|
22631
24015
|
ExtendedApprovalStatus.REJECTED
|
|
22632
24016
|
]);
|
|
22633
24017
|
const latestRespondedApproval = [
|
|
22634
|
-
...
|
|
24018
|
+
...supplier2.approvals.filter((approval) => respondedStatuses.has(approval.status))
|
|
22635
24019
|
].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];
|
|
22636
|
-
const pendingApprovals =
|
|
22637
|
-
const supersededApprovals =
|
|
24020
|
+
const pendingApprovals = supplier2.approvals.filter((approval) => approval.status === ExtendedApprovalStatus.PENDING_APPROVAL);
|
|
24021
|
+
const supersededApprovals = supplier2.approvals.filter((approval) => approval.status === ExtendedApprovalStatus.SUPERSEDED);
|
|
22638
24022
|
const respondedByApproval = latestRespondedApproval ?? null;
|
|
22639
24023
|
const responseUser = respondedByApproval ? respondedByApproval.responder ?? respondedByApproval.approver : null;
|
|
22640
24024
|
out({
|
|
22641
|
-
...
|
|
24025
|
+
...supplier2,
|
|
22642
24026
|
supplierApprovalSummary: {
|
|
22643
24027
|
pendingApprovalCount: pendingApprovals.length,
|
|
22644
24028
|
pendingApprovers: pendingApprovals.map((approval) => ({
|
|
@@ -22810,9 +24194,9 @@ async function listCompanies(opts) {
|
|
|
22810
24194
|
});
|
|
22811
24195
|
out(result);
|
|
22812
24196
|
}
|
|
22813
|
-
async function getCompany(
|
|
22814
|
-
const
|
|
22815
|
-
out(
|
|
24197
|
+
async function getCompany(id2) {
|
|
24198
|
+
const company2 = await api2.company.getCompanyById.query({ id: id2 });
|
|
24199
|
+
out(company2);
|
|
22816
24200
|
}
|
|
22817
24201
|
async function createCompany(opts) {
|
|
22818
24202
|
const result = await api2.company.createCompany.mutate(opts);
|
|
@@ -22843,13 +24227,13 @@ var listProjectHubs = async (opts) => {
|
|
|
22843
24227
|
});
|
|
22844
24228
|
out(result);
|
|
22845
24229
|
};
|
|
22846
|
-
async function getProject(
|
|
22847
|
-
const [
|
|
22848
|
-
api2.project.getProjectDetailsById.query({ id }),
|
|
22849
|
-
api2.project.getProjectBudgetsOverview.query({ projectId:
|
|
22850
|
-
api2.project.getProjectBillsOverview.query({ projectId:
|
|
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 })
|
|
22851
24235
|
]);
|
|
22852
|
-
out({ project, budgetsOverview, billsOverview });
|
|
24236
|
+
out({ project: project2, budgetsOverview, billsOverview });
|
|
22853
24237
|
}
|
|
22854
24238
|
var getProjectHubStatus = async (projectId) => {
|
|
22855
24239
|
const result = await api2.project.getProjectHubShareLink.query({ projectId });
|
|
@@ -22905,14 +24289,14 @@ async function createProject(opts) {
|
|
|
22905
24289
|
});
|
|
22906
24290
|
out(result);
|
|
22907
24291
|
}
|
|
22908
|
-
async function updateProjectStatus(
|
|
24292
|
+
async function updateProjectStatus(id2, status, opts) {
|
|
22909
24293
|
await assertSensitiveWorkflowConfirmed({
|
|
22910
24294
|
action: "Update project status",
|
|
22911
|
-
entity: `project ${
|
|
24295
|
+
entity: `project ${id2}`,
|
|
22912
24296
|
details: `to ${status}`
|
|
22913
24297
|
});
|
|
22914
24298
|
const result = await api2.project.updateProjectStatus.mutate({
|
|
22915
|
-
id,
|
|
24299
|
+
id: id2,
|
|
22916
24300
|
status,
|
|
22917
24301
|
...opts?.projectManagerId && { projectManagerId: opts.projectManagerId },
|
|
22918
24302
|
...opts?.projectManagerEmail && {
|
|
@@ -23021,9 +24405,9 @@ async function listItems(opts) {
|
|
|
23021
24405
|
});
|
|
23022
24406
|
out(result);
|
|
23023
24407
|
}
|
|
23024
|
-
async function getItem(
|
|
23025
|
-
const
|
|
23026
|
-
out(
|
|
24408
|
+
async function getItem(id2) {
|
|
24409
|
+
const item2 = await api2.item.getItem.query({ id: id2 });
|
|
24410
|
+
out(item2);
|
|
23027
24411
|
}
|
|
23028
24412
|
async function listItemCategories(opts) {
|
|
23029
24413
|
const result = await api2.itemCategory.getItemCategories.query({
|
|
@@ -23714,6 +25098,47 @@ var registry2 = [
|
|
|
23714
25098
|
"customer-invoice",
|
|
23715
25099
|
"import-qbo"
|
|
23716
25100
|
]),
|
|
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
|
+
},
|
|
23717
25142
|
legacyCommand("create-customer-invoice", ["customer-invoice", "create"]),
|
|
23718
25143
|
legacyCommand("discard-customer-invoice", ["customer-invoice", "discard"]),
|
|
23719
25144
|
legacyCommand("delete-customer-invoice", ["customer-invoice", "delete"]),
|
|
@@ -24287,7 +25712,12 @@ Customer invoices
|
|
|
24287
25712
|
get-customer-invoice-email-context <batchId> Contact, required CC, budget, and project context for composing an invoice email.
|
|
24288
25713
|
preview-qbo-customer-invoice <budgetId> <qboInvoiceId> Preview linking an existing QBO transaction (numeric ID, not invoice number); works on accepted/closed estimates.
|
|
24289
25714
|
import-qbo-customer-invoice <budgetId> <qboInvoiceId> --token <previewToken> Confirm the reviewed BB-only link; requires --allow-state-change --allow-financial-write in automation.
|
|
24290
|
-
|
|
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.
|
|
24291
25721
|
discard-customer-invoice <batchId> Discard a reserved CREATING batch with no created QBO invoices.
|
|
24292
25722
|
delete-customer-invoice <batchId> Delete QBO invoices and the local batch.
|
|
24293
25723
|
void-customer-invoice <batchId> Void the invoice batch in QuickBooks.
|
|
@@ -24769,10 +26199,10 @@ Waiting for approval\u2026
|
|
|
24769
26199
|
break;
|
|
24770
26200
|
}
|
|
24771
26201
|
case "get-budget": {
|
|
24772
|
-
const
|
|
24773
|
-
if (!
|
|
26202
|
+
const id2 = positional[0];
|
|
26203
|
+
if (!id2)
|
|
24774
26204
|
throw new Error("get-budget requires <id>");
|
|
24775
|
-
await getBudget(
|
|
26205
|
+
await getBudget(id2);
|
|
24776
26206
|
break;
|
|
24777
26207
|
}
|
|
24778
26208
|
case "upload-budget-attachment": {
|
|
@@ -24796,17 +26226,17 @@ Waiting for approval\u2026
|
|
|
24796
26226
|
break;
|
|
24797
26227
|
}
|
|
24798
26228
|
case "get-budget-items": {
|
|
24799
|
-
const
|
|
24800
|
-
if (!
|
|
26229
|
+
const id2 = positional[0];
|
|
26230
|
+
if (!id2)
|
|
24801
26231
|
throw new Error("get-budget-items requires <budgetId>");
|
|
24802
|
-
await getBudgetItemsOnly(
|
|
26232
|
+
await getBudgetItemsOnly(id2);
|
|
24803
26233
|
break;
|
|
24804
26234
|
}
|
|
24805
26235
|
case "get-budget-details": {
|
|
24806
|
-
const
|
|
24807
|
-
if (!
|
|
26236
|
+
const id2 = positional[0];
|
|
26237
|
+
if (!id2)
|
|
24808
26238
|
throw new Error("get-budget-details requires <budgetId>");
|
|
24809
|
-
await getBudgetDetails(
|
|
26239
|
+
await getBudgetDetails(id2);
|
|
24810
26240
|
break;
|
|
24811
26241
|
}
|
|
24812
26242
|
case "get-budget-categories": {
|
|
@@ -24814,10 +26244,10 @@ Waiting for approval\u2026
|
|
|
24814
26244
|
break;
|
|
24815
26245
|
}
|
|
24816
26246
|
case "get-budget-versions": {
|
|
24817
|
-
const
|
|
24818
|
-
if (!
|
|
26247
|
+
const id2 = positional[0];
|
|
26248
|
+
if (!id2)
|
|
24819
26249
|
throw new Error("get-budget-versions requires <budgetId>");
|
|
24820
|
-
await getBudgetVersions(
|
|
26250
|
+
await getBudgetVersions(id2);
|
|
24821
26251
|
break;
|
|
24822
26252
|
}
|
|
24823
26253
|
case "rename-budget-version": {
|
|
@@ -24838,14 +26268,14 @@ Waiting for approval\u2026
|
|
|
24838
26268
|
break;
|
|
24839
26269
|
}
|
|
24840
26270
|
case "update-budget-status": {
|
|
24841
|
-
const [
|
|
24842
|
-
if (!
|
|
26271
|
+
const [id2, status] = positional;
|
|
26272
|
+
if (!id2 || !status) {
|
|
24843
26273
|
throw new Error("update-budget-status requires <id> <status>");
|
|
24844
26274
|
}
|
|
24845
26275
|
if (!isBudgetStatusUpdate(status)) {
|
|
24846
26276
|
throw new Error(`Invalid status. Use one of: ${budgetStatusesForHelp.join(", ")}`);
|
|
24847
26277
|
}
|
|
24848
|
-
await updateBudgetStatus(
|
|
26278
|
+
await updateBudgetStatus(id2, status, {
|
|
24849
26279
|
projectManagerId: getFlag(flags, "projectManagerId") !== undefined ? String(getFlag(flags, "projectManagerId")) : undefined,
|
|
24850
26280
|
markProjectWon: parseOptionalBoolFlag(flags, "markProjectWon"),
|
|
24851
26281
|
projectStatusOnCommercialRejection: parseCommercialRejectionProjectStatus(flags),
|
|
@@ -24854,13 +26284,13 @@ Waiting for approval\u2026
|
|
|
24854
26284
|
break;
|
|
24855
26285
|
}
|
|
24856
26286
|
case "create-budget-approval": {
|
|
24857
|
-
const
|
|
24858
|
-
if (!
|
|
26287
|
+
const id2 = positional[0];
|
|
26288
|
+
if (!id2)
|
|
24859
26289
|
throw new Error("create-budget-approval requires <budgetId>");
|
|
24860
26290
|
const companyPayloadRaw = getFlag(flags, "companyPayload");
|
|
24861
26291
|
const contactPayloadRaw = getFlag(flags, "contactPayload");
|
|
24862
26292
|
const eventPayloadRaw = getFlag(flags, "eventPayload");
|
|
24863
|
-
await createBudgetApproval(
|
|
26293
|
+
await createBudgetApproval(id2, {
|
|
24864
26294
|
company: companyPayloadRaw !== undefined ? parseJsonFlag(String(companyPayloadRaw), "--companyPayload") : undefined,
|
|
24865
26295
|
contactPerson: contactPayloadRaw !== undefined ? parseJsonFlag(String(contactPayloadRaw), "--contactPayload") : undefined,
|
|
24866
26296
|
eventDetails: eventPayloadRaw !== undefined ? parseJsonFlag(String(eventPayloadRaw), "--eventPayload") : undefined
|
|
@@ -24868,13 +26298,13 @@ Waiting for approval\u2026
|
|
|
24868
26298
|
break;
|
|
24869
26299
|
}
|
|
24870
26300
|
case "create-estimate": {
|
|
24871
|
-
const
|
|
24872
|
-
if (!
|
|
26301
|
+
const id2 = positional[0];
|
|
26302
|
+
if (!id2)
|
|
24873
26303
|
throw new Error("create-estimate requires <budgetId>");
|
|
24874
26304
|
const companyPayloadRaw = getFlag(flags, "companyPayload");
|
|
24875
26305
|
const contactPayloadRaw = getFlag(flags, "contactPayload");
|
|
24876
26306
|
const eventPayloadRaw = getFlag(flags, "eventPayload");
|
|
24877
|
-
await createEstimate(
|
|
26307
|
+
await createEstimate(id2, {
|
|
24878
26308
|
company: companyPayloadRaw !== undefined ? parseJsonFlag(String(companyPayloadRaw), "--companyPayload") : undefined,
|
|
24879
26309
|
contactPerson: contactPayloadRaw !== undefined ? parseJsonFlag(String(contactPayloadRaw), "--contactPayload") : undefined,
|
|
24880
26310
|
eventDetails: eventPayloadRaw !== undefined ? parseJsonFlag(String(eventPayloadRaw), "--eventPayload") : undefined
|
|
@@ -24910,10 +26340,10 @@ Waiting for approval\u2026
|
|
|
24910
26340
|
break;
|
|
24911
26341
|
}
|
|
24912
26342
|
case "delete-budget": {
|
|
24913
|
-
const
|
|
24914
|
-
if (!
|
|
26343
|
+
const id2 = positional[0];
|
|
26344
|
+
if (!id2)
|
|
24915
26345
|
throw new Error("delete-budget requires <budgetId>");
|
|
24916
|
-
await deleteBudgetById(
|
|
26346
|
+
await deleteBudgetById(id2);
|
|
24917
26347
|
break;
|
|
24918
26348
|
}
|
|
24919
26349
|
case "add-budget-items": {
|
|
@@ -24925,11 +26355,11 @@ Waiting for approval\u2026
|
|
|
24925
26355
|
break;
|
|
24926
26356
|
}
|
|
24927
26357
|
case "update-budget-item": {
|
|
24928
|
-
const
|
|
24929
|
-
if (!
|
|
26358
|
+
const id2 = getFlag(flags, "id");
|
|
26359
|
+
if (!id2)
|
|
24930
26360
|
throw new Error("update-budget-item requires --id");
|
|
24931
26361
|
await updateBudgetItem({
|
|
24932
|
-
id: String(
|
|
26362
|
+
id: String(id2),
|
|
24933
26363
|
description: getFlag(flags, "description"),
|
|
24934
26364
|
note: getFlag(flags, "note"),
|
|
24935
26365
|
quantity: parseOptionalNumber(getFlag(flags, "quantity"), "--quantity"),
|
|
@@ -24943,10 +26373,10 @@ Waiting for approval\u2026
|
|
|
24943
26373
|
break;
|
|
24944
26374
|
}
|
|
24945
26375
|
case "remove-budget-item": {
|
|
24946
|
-
const
|
|
24947
|
-
if (!
|
|
26376
|
+
const id2 = positional[0];
|
|
26377
|
+
if (!id2)
|
|
24948
26378
|
throw new Error("remove-budget-item requires <budgetItemId>");
|
|
24949
|
-
await removeBudgetItem(
|
|
26379
|
+
await removeBudgetItem(id2);
|
|
24950
26380
|
break;
|
|
24951
26381
|
}
|
|
24952
26382
|
case "replace-budget-item": {
|
|
@@ -25007,20 +26437,20 @@ Waiting for approval\u2026
|
|
|
25007
26437
|
break;
|
|
25008
26438
|
}
|
|
25009
26439
|
case "update-budget-category": {
|
|
25010
|
-
const
|
|
26440
|
+
const id2 = getFlag(flags, "id");
|
|
25011
26441
|
const name = getFlag(flags, "name");
|
|
25012
|
-
if (!
|
|
26442
|
+
if (!id2 || !name) {
|
|
25013
26443
|
throw new Error("update-budget-category requires --id and --name");
|
|
25014
26444
|
}
|
|
25015
|
-
await updateBudgetCategory({ id: String(
|
|
26445
|
+
await updateBudgetCategory({ id: String(id2), name: String(name) });
|
|
25016
26446
|
break;
|
|
25017
26447
|
}
|
|
25018
26448
|
case "delete-budget-category": {
|
|
25019
|
-
const
|
|
25020
|
-
if (!
|
|
26449
|
+
const id2 = positional[0];
|
|
26450
|
+
if (!id2) {
|
|
25021
26451
|
throw new Error("delete-budget-category requires <id>");
|
|
25022
26452
|
}
|
|
25023
|
-
await deleteBudgetCategory(
|
|
26453
|
+
await deleteBudgetCategory(id2);
|
|
25024
26454
|
break;
|
|
25025
26455
|
}
|
|
25026
26456
|
case "update-budget-commission": {
|
|
@@ -25093,10 +26523,10 @@ Waiting for approval\u2026
|
|
|
25093
26523
|
break;
|
|
25094
26524
|
}
|
|
25095
26525
|
case "create-bill-approval": {
|
|
25096
|
-
const
|
|
25097
|
-
if (!
|
|
26526
|
+
const id2 = positional[0];
|
|
26527
|
+
if (!id2)
|
|
25098
26528
|
throw new Error("create-bill-approval requires <billId>");
|
|
25099
|
-
await createBillApproval(
|
|
26529
|
+
await createBillApproval(id2);
|
|
25100
26530
|
break;
|
|
25101
26531
|
}
|
|
25102
26532
|
case "create-bill": {
|
|
@@ -25171,15 +26601,15 @@ Waiting for approval\u2026
|
|
|
25171
26601
|
break;
|
|
25172
26602
|
}
|
|
25173
26603
|
case "delete-bill": {
|
|
25174
|
-
const
|
|
25175
|
-
if (!
|
|
26604
|
+
const id2 = positional[0];
|
|
26605
|
+
if (!id2)
|
|
25176
26606
|
throw new Error("delete-bill requires <billId>");
|
|
25177
|
-
await deleteBillById(
|
|
26607
|
+
await deleteBillById(id2);
|
|
25178
26608
|
break;
|
|
25179
26609
|
}
|
|
25180
26610
|
case "update-bill-status": {
|
|
25181
|
-
const [
|
|
25182
|
-
if (!
|
|
26611
|
+
const [id2, statusRaw] = positional;
|
|
26612
|
+
if (!id2 || !statusRaw) {
|
|
25183
26613
|
throw new Error("update-bill-status requires <billId> <status> [--rejectionReason] [--paymentTrackingUrl] [--paymentReference] [--paymentProof <receipt.pdf>]");
|
|
25184
26614
|
}
|
|
25185
26615
|
const paymentProofFlag = flags.paymentProof ?? flags["payment-proof"] ?? flags.receipt;
|
|
@@ -25188,7 +26618,7 @@ Waiting for approval\u2026
|
|
|
25188
26618
|
}
|
|
25189
26619
|
const status = parseBillStatusForUpdate(statusRaw);
|
|
25190
26620
|
await updateBillStatus({
|
|
25191
|
-
id,
|
|
26621
|
+
id: id2,
|
|
25192
26622
|
status,
|
|
25193
26623
|
rejectionReason: getFlag(flags, "rejectionReason"),
|
|
25194
26624
|
paymentTrackingUrl: getFlag(flags, "paymentTrackingUrl"),
|
|
@@ -25199,8 +26629,8 @@ Waiting for approval\u2026
|
|
|
25199
26629
|
break;
|
|
25200
26630
|
}
|
|
25201
26631
|
case "patch-bill-payment": {
|
|
25202
|
-
const
|
|
25203
|
-
if (!
|
|
26632
|
+
const id2 = positional[0];
|
|
26633
|
+
if (!id2)
|
|
25204
26634
|
throw new Error("patch-bill-payment requires <billId>");
|
|
25205
26635
|
const paymentTrackingUrl = (() => {
|
|
25206
26636
|
const clear = parseOptionalBoolFlag(flags, "clearPaymentTrackingUrl");
|
|
@@ -25251,7 +26681,7 @@ Waiting for approval\u2026
|
|
|
25251
26681
|
return;
|
|
25252
26682
|
})();
|
|
25253
26683
|
await patchBillPayment({
|
|
25254
|
-
id,
|
|
26684
|
+
id: id2,
|
|
25255
26685
|
paymentTrackingUrl,
|
|
25256
26686
|
paymentReference,
|
|
25257
26687
|
quickbooksBillId,
|
|
@@ -25260,18 +26690,18 @@ Waiting for approval\u2026
|
|
|
25260
26690
|
break;
|
|
25261
26691
|
}
|
|
25262
26692
|
case "patch-bill-invoice-number": {
|
|
25263
|
-
const [
|
|
25264
|
-
if (!
|
|
26693
|
+
const [id2, inv] = positional;
|
|
26694
|
+
if (!id2 || !inv) {
|
|
25265
26695
|
throw new Error("patch-bill-invoice-number requires <billId> <invoiceNumber>");
|
|
25266
26696
|
}
|
|
25267
|
-
await patchBillInvoiceNumber(
|
|
26697
|
+
await patchBillInvoiceNumber(id2, inv);
|
|
25268
26698
|
break;
|
|
25269
26699
|
}
|
|
25270
26700
|
case "get-bill-attachments": {
|
|
25271
|
-
const
|
|
25272
|
-
if (!
|
|
26701
|
+
const id2 = positional[0];
|
|
26702
|
+
if (!id2)
|
|
25273
26703
|
throw new Error("get-bill-attachments requires <billId>");
|
|
25274
|
-
await getBillAttachments(
|
|
26704
|
+
await getBillAttachments(id2);
|
|
25275
26705
|
break;
|
|
25276
26706
|
}
|
|
25277
26707
|
case "stage-bill-attachment": {
|
|
@@ -25357,10 +26787,10 @@ Waiting for approval\u2026
|
|
|
25357
26787
|
break;
|
|
25358
26788
|
}
|
|
25359
26789
|
case "get-bill-details": {
|
|
25360
|
-
const
|
|
25361
|
-
if (!
|
|
26790
|
+
const id2 = positional[0];
|
|
26791
|
+
if (!id2)
|
|
25362
26792
|
throw new Error("get-bill-details requires <billId>");
|
|
25363
|
-
await getBillDetails(
|
|
26793
|
+
await getBillDetails(id2);
|
|
25364
26794
|
break;
|
|
25365
26795
|
}
|
|
25366
26796
|
case "list-quotations": {
|
|
@@ -25380,10 +26810,10 @@ Waiting for approval\u2026
|
|
|
25380
26810
|
break;
|
|
25381
26811
|
}
|
|
25382
26812
|
case "get-quotation": {
|
|
25383
|
-
const
|
|
25384
|
-
if (!
|
|
26813
|
+
const id2 = positional[0];
|
|
26814
|
+
if (!id2)
|
|
25385
26815
|
throw new Error("get-quotation requires <quotationId>");
|
|
25386
|
-
await getQuotationDetails(
|
|
26816
|
+
await getQuotationDetails(id2);
|
|
25387
26817
|
break;
|
|
25388
26818
|
}
|
|
25389
26819
|
case "upload-quotation-attachment": {
|
|
@@ -25422,38 +26852,38 @@ Waiting for approval\u2026
|
|
|
25422
26852
|
break;
|
|
25423
26853
|
}
|
|
25424
26854
|
case "delete-quotation": {
|
|
25425
|
-
const
|
|
25426
|
-
if (!
|
|
26855
|
+
const id2 = positional[0];
|
|
26856
|
+
if (!id2)
|
|
25427
26857
|
throw new Error("delete-quotation requires <quotationId>");
|
|
25428
|
-
await deleteQuotationById(
|
|
26858
|
+
await deleteQuotationById(id2);
|
|
25429
26859
|
break;
|
|
25430
26860
|
}
|
|
25431
26861
|
case "submit-quotation": {
|
|
25432
|
-
const
|
|
25433
|
-
if (!
|
|
26862
|
+
const id2 = positional[0];
|
|
26863
|
+
if (!id2)
|
|
25434
26864
|
throw new Error("submit-quotation requires <quotationId>");
|
|
25435
|
-
await submitQuotation(
|
|
26865
|
+
await submitQuotation(id2, getFlag(flags, "overBudgetOverrideReason"));
|
|
25436
26866
|
break;
|
|
25437
26867
|
}
|
|
25438
26868
|
case "approve-quotation": {
|
|
25439
|
-
const
|
|
25440
|
-
if (!
|
|
26869
|
+
const id2 = positional[0];
|
|
26870
|
+
if (!id2)
|
|
25441
26871
|
throw new Error("approve-quotation requires <quotationId>");
|
|
25442
|
-
await approveQuotation(
|
|
26872
|
+
await approveQuotation(id2, getFlag(flags, "overBudgetOverrideReason"));
|
|
25443
26873
|
break;
|
|
25444
26874
|
}
|
|
25445
26875
|
case "reject-quotation": {
|
|
25446
|
-
const
|
|
26876
|
+
const id2 = positional[0];
|
|
25447
26877
|
const reason = getFlag(flags, "reason");
|
|
25448
|
-
if (!
|
|
26878
|
+
if (!id2 || reason === undefined) {
|
|
25449
26879
|
throw new Error("reject-quotation requires <quotationId> --reason <text>");
|
|
25450
26880
|
}
|
|
25451
|
-
await rejectQuotation(
|
|
26881
|
+
await rejectQuotation(id2, reason);
|
|
25452
26882
|
break;
|
|
25453
26883
|
}
|
|
25454
26884
|
case "download-quotation-pdf": {
|
|
25455
|
-
const
|
|
25456
|
-
if (!
|
|
26885
|
+
const id2 = positional[0];
|
|
26886
|
+
if (!id2) {
|
|
25457
26887
|
throw new Error("download-quotation-pdf requires <quotationId> [original|staff|final]");
|
|
25458
26888
|
}
|
|
25459
26889
|
const roleRaw = (positional[1] ?? "final").toLowerCase();
|
|
@@ -25461,7 +26891,7 @@ Waiting for approval\u2026
|
|
|
25461
26891
|
if (!fileRole) {
|
|
25462
26892
|
throw new Error("download-quotation-pdf role must be original, staff, or final");
|
|
25463
26893
|
}
|
|
25464
|
-
await downloadQuotationPdf(
|
|
26894
|
+
await downloadQuotationPdf(id2, fileRole);
|
|
25465
26895
|
break;
|
|
25466
26896
|
}
|
|
25467
26897
|
case "check-customer-invoice-readiness": {
|
|
@@ -25544,6 +26974,31 @@ Waiting for approval\u2026
|
|
|
25544
26974
|
}
|
|
25545
26975
|
break;
|
|
25546
26976
|
}
|
|
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
|
+
}
|
|
25547
27002
|
case "create-customer-invoice": {
|
|
25548
27003
|
const payloadRaw = getFlag(flags, "payload");
|
|
25549
27004
|
if (!payloadRaw) {
|
|
@@ -25626,58 +27081,58 @@ Waiting for approval\u2026
|
|
|
25626
27081
|
break;
|
|
25627
27082
|
}
|
|
25628
27083
|
case "approve-bill": {
|
|
25629
|
-
const
|
|
25630
|
-
if (!
|
|
27084
|
+
const id2 = positional[0];
|
|
27085
|
+
if (!id2)
|
|
25631
27086
|
throw new Error("approve-bill requires <billId>");
|
|
25632
|
-
await approveBill(
|
|
27087
|
+
await approveBill(id2, getFlag(flags, "overBudgetOverrideReason"));
|
|
25633
27088
|
break;
|
|
25634
27089
|
}
|
|
25635
27090
|
case "reject-bill": {
|
|
25636
|
-
const
|
|
27091
|
+
const id2 = positional[0];
|
|
25637
27092
|
const reason = getFlag(flags, "reason");
|
|
25638
|
-
if (!
|
|
27093
|
+
if (!id2 || reason === undefined) {
|
|
25639
27094
|
throw new Error("reject-bill requires <billId> --reason <text>");
|
|
25640
27095
|
}
|
|
25641
|
-
await rejectBill(
|
|
27096
|
+
await rejectBill(id2, reason);
|
|
25642
27097
|
break;
|
|
25643
27098
|
}
|
|
25644
27099
|
case "approve-budget": {
|
|
25645
|
-
const
|
|
25646
|
-
if (!
|
|
27100
|
+
const id2 = positional[0];
|
|
27101
|
+
if (!id2)
|
|
25647
27102
|
throw new Error("approve-budget requires <budgetId>");
|
|
25648
|
-
await approveBudget(
|
|
27103
|
+
await approveBudget(id2);
|
|
25649
27104
|
break;
|
|
25650
27105
|
}
|
|
25651
27106
|
case "reject-budget": {
|
|
25652
|
-
const
|
|
27107
|
+
const id2 = positional[0];
|
|
25653
27108
|
const reason = getFlag(flags, "reason");
|
|
25654
|
-
if (!
|
|
27109
|
+
if (!id2 || reason === undefined) {
|
|
25655
27110
|
throw new Error("reject-budget requires <budgetId> --reason <text>");
|
|
25656
27111
|
}
|
|
25657
|
-
await rejectBudget(
|
|
27112
|
+
await rejectBudget(id2, reason);
|
|
25658
27113
|
break;
|
|
25659
27114
|
}
|
|
25660
27115
|
case "approve-supplier": {
|
|
25661
|
-
const
|
|
25662
|
-
if (!
|
|
27116
|
+
const id2 = positional[0];
|
|
27117
|
+
if (!id2)
|
|
25663
27118
|
throw new Error("approve-supplier requires <supplierId>");
|
|
25664
|
-
await approveSupplier(
|
|
27119
|
+
await approveSupplier(id2);
|
|
25665
27120
|
break;
|
|
25666
27121
|
}
|
|
25667
27122
|
case "reject-supplier": {
|
|
25668
|
-
const
|
|
27123
|
+
const id2 = positional[0];
|
|
25669
27124
|
const reason = getFlag(flags, "reason");
|
|
25670
|
-
if (!
|
|
27125
|
+
if (!id2 || reason === undefined) {
|
|
25671
27126
|
throw new Error("reject-supplier requires <supplierId> --reason <text>");
|
|
25672
27127
|
}
|
|
25673
|
-
await rejectSupplier(
|
|
27128
|
+
await rejectSupplier(id2, reason);
|
|
25674
27129
|
break;
|
|
25675
27130
|
}
|
|
25676
27131
|
case "get-supplier-details": {
|
|
25677
|
-
const
|
|
25678
|
-
if (!
|
|
27132
|
+
const id2 = positional[0];
|
|
27133
|
+
if (!id2)
|
|
25679
27134
|
throw new Error("get-supplier-details requires <supplierId>");
|
|
25680
|
-
await getSupplierDetails(
|
|
27135
|
+
await getSupplierDetails(id2);
|
|
25681
27136
|
break;
|
|
25682
27137
|
}
|
|
25683
27138
|
case "list-companies": {
|
|
@@ -25691,10 +27146,10 @@ Waiting for approval\u2026
|
|
|
25691
27146
|
break;
|
|
25692
27147
|
}
|
|
25693
27148
|
case "get-company": {
|
|
25694
|
-
const
|
|
25695
|
-
if (!
|
|
27149
|
+
const id2 = positional[0];
|
|
27150
|
+
if (!id2)
|
|
25696
27151
|
throw new Error("get-company requires <id>");
|
|
25697
|
-
await getCompany(
|
|
27152
|
+
await getCompany(id2);
|
|
25698
27153
|
break;
|
|
25699
27154
|
}
|
|
25700
27155
|
case "create-company": {
|
|
@@ -25748,10 +27203,10 @@ Waiting for approval\u2026
|
|
|
25748
27203
|
break;
|
|
25749
27204
|
}
|
|
25750
27205
|
case "delete-company": {
|
|
25751
|
-
const
|
|
25752
|
-
if (!
|
|
27206
|
+
const id2 = positional[0];
|
|
27207
|
+
if (!id2)
|
|
25753
27208
|
throw new Error("delete-company requires <id>");
|
|
25754
|
-
await deleteCompanyById(
|
|
27209
|
+
await deleteCompanyById(id2);
|
|
25755
27210
|
break;
|
|
25756
27211
|
}
|
|
25757
27212
|
case "list-projects": {
|
|
@@ -25778,10 +27233,10 @@ Waiting for approval\u2026
|
|
|
25778
27233
|
break;
|
|
25779
27234
|
}
|
|
25780
27235
|
case "get-project": {
|
|
25781
|
-
const
|
|
25782
|
-
if (!
|
|
27236
|
+
const id2 = positional[0];
|
|
27237
|
+
if (!id2)
|
|
25783
27238
|
throw new Error("get-project requires <id>");
|
|
25784
|
-
await getProject(
|
|
27239
|
+
await getProject(id2);
|
|
25785
27240
|
break;
|
|
25786
27241
|
}
|
|
25787
27242
|
case "get-project-hub-status": {
|
|
@@ -25859,32 +27314,32 @@ Waiting for approval\u2026
|
|
|
25859
27314
|
break;
|
|
25860
27315
|
}
|
|
25861
27316
|
case "delete-project": {
|
|
25862
|
-
const
|
|
25863
|
-
if (!
|
|
27317
|
+
const id2 = positional[0];
|
|
27318
|
+
if (!id2)
|
|
25864
27319
|
throw new Error("delete-project requires <id>");
|
|
25865
|
-
await deleteProjectById(
|
|
27320
|
+
await deleteProjectById(id2);
|
|
25866
27321
|
break;
|
|
25867
27322
|
}
|
|
25868
27323
|
case "check-project-reconciliation": {
|
|
25869
|
-
const
|
|
25870
|
-
if (!
|
|
27324
|
+
const id2 = positional[0];
|
|
27325
|
+
if (!id2) {
|
|
25871
27326
|
throw new Error("check-project-reconciliation requires <id>");
|
|
25872
27327
|
}
|
|
25873
|
-
await checkProjectReconciliation(
|
|
27328
|
+
await checkProjectReconciliation(id2);
|
|
25874
27329
|
break;
|
|
25875
27330
|
}
|
|
25876
27331
|
case "reconcile-project": {
|
|
25877
|
-
const
|
|
25878
|
-
if (!
|
|
27332
|
+
const id2 = positional[0];
|
|
27333
|
+
if (!id2)
|
|
25879
27334
|
throw new Error("reconcile-project requires <id>");
|
|
25880
|
-
await reconcileProject(
|
|
27335
|
+
await reconcileProject(id2);
|
|
25881
27336
|
break;
|
|
25882
27337
|
}
|
|
25883
27338
|
case "complete-project": {
|
|
25884
|
-
const
|
|
25885
|
-
if (!
|
|
27339
|
+
const id2 = positional[0];
|
|
27340
|
+
if (!id2)
|
|
25886
27341
|
throw new Error("complete-project requires <id>");
|
|
25887
|
-
await completeProject(
|
|
27342
|
+
await completeProject(id2);
|
|
25888
27343
|
break;
|
|
25889
27344
|
}
|
|
25890
27345
|
case "import-qbo-project": {
|
|
@@ -25897,14 +27352,14 @@ Waiting for approval\u2026
|
|
|
25897
27352
|
break;
|
|
25898
27353
|
}
|
|
25899
27354
|
case "update-project-status": {
|
|
25900
|
-
const [
|
|
25901
|
-
if (!
|
|
27355
|
+
const [id2, statusRaw] = positional;
|
|
27356
|
+
if (!id2 || !statusRaw) {
|
|
25902
27357
|
throw new Error("update-project-status requires <id> <status>");
|
|
25903
27358
|
}
|
|
25904
27359
|
const pmIdFlag = getFlag(flags, "projectManagerId");
|
|
25905
27360
|
const pmEmailFlag = getFlag(flags, "projectManagerEmail");
|
|
25906
27361
|
const wonOverrideReasonFlag = getFlag(flags, "wonOverrideReason");
|
|
25907
|
-
await updateProjectStatus(
|
|
27362
|
+
await updateProjectStatus(id2, parseProjectStatusForUpdate(statusRaw), {
|
|
25908
27363
|
...pmIdFlag && { projectManagerId: String(pmIdFlag) },
|
|
25909
27364
|
...pmEmailFlag && {
|
|
25910
27365
|
projectManagerEmail: String(pmEmailFlag)
|
|
@@ -25916,10 +27371,10 @@ Waiting for approval\u2026
|
|
|
25916
27371
|
break;
|
|
25917
27372
|
}
|
|
25918
27373
|
case "list-contacts": {
|
|
25919
|
-
const
|
|
25920
|
-
if (!
|
|
27374
|
+
const id2 = positional[0];
|
|
27375
|
+
if (!id2)
|
|
25921
27376
|
throw new Error("list-contacts requires <companyId>");
|
|
25922
|
-
await listContacts(
|
|
27377
|
+
await listContacts(id2);
|
|
25923
27378
|
break;
|
|
25924
27379
|
}
|
|
25925
27380
|
case "create-contact-person": {
|
|
@@ -26034,10 +27489,10 @@ Waiting for approval\u2026
|
|
|
26034
27489
|
break;
|
|
26035
27490
|
}
|
|
26036
27491
|
case "get-item": {
|
|
26037
|
-
const
|
|
26038
|
-
if (!
|
|
27492
|
+
const id2 = positional[0];
|
|
27493
|
+
if (!id2)
|
|
26039
27494
|
throw new Error("get-item requires <id>");
|
|
26040
|
-
await getItem(
|
|
27495
|
+
await getItem(id2);
|
|
26041
27496
|
break;
|
|
26042
27497
|
}
|
|
26043
27498
|
case "create-item": {
|
|
@@ -26059,10 +27514,10 @@ Waiting for approval\u2026
|
|
|
26059
27514
|
break;
|
|
26060
27515
|
}
|
|
26061
27516
|
case "delete-item": {
|
|
26062
|
-
const
|
|
26063
|
-
if (!
|
|
27517
|
+
const id2 = positional[0];
|
|
27518
|
+
if (!id2)
|
|
26064
27519
|
throw new Error("delete-item requires <id>");
|
|
26065
|
-
await deleteItemById(
|
|
27520
|
+
await deleteItemById(id2);
|
|
26066
27521
|
break;
|
|
26067
27522
|
}
|
|
26068
27523
|
case "archive-items": {
|
|
@@ -26092,12 +27547,12 @@ Waiting for approval\u2026
|
|
|
26092
27547
|
break;
|
|
26093
27548
|
}
|
|
26094
27549
|
case "update-item-category": {
|
|
26095
|
-
const
|
|
27550
|
+
const id2 = getFlag(flags, "id");
|
|
26096
27551
|
const name = getFlag(flags, "name");
|
|
26097
|
-
if (!
|
|
27552
|
+
if (!id2 || !name) {
|
|
26098
27553
|
throw new Error("update-item-category requires --id and --name");
|
|
26099
27554
|
}
|
|
26100
|
-
await updateItemCategory({ id: String(
|
|
27555
|
+
await updateItemCategory({ id: String(id2), name: String(name) });
|
|
26101
27556
|
break;
|
|
26102
27557
|
}
|
|
26103
27558
|
case "delete-item-categories": {
|
|
@@ -26142,12 +27597,12 @@ Waiting for approval\u2026
|
|
|
26142
27597
|
break;
|
|
26143
27598
|
}
|
|
26144
27599
|
case "revoke-api-key": {
|
|
26145
|
-
const
|
|
26146
|
-
if (!
|
|
27600
|
+
const id2 = positional[0];
|
|
27601
|
+
if (!id2) {
|
|
26147
27602
|
throw new Error("revoke-api-key requires <apiKeyId> [--userId <userId>]");
|
|
26148
27603
|
}
|
|
26149
27604
|
await revokeApiKeyForUser({
|
|
26150
|
-
id,
|
|
27605
|
+
id: id2,
|
|
26151
27606
|
userId: getFlag(flags, "userId")
|
|
26152
27607
|
});
|
|
26153
27608
|
break;
|
|
@@ -26310,12 +27765,12 @@ Waiting for approval\u2026
|
|
|
26310
27765
|
break;
|
|
26311
27766
|
}
|
|
26312
27767
|
case "get-budget-category-benchmarks": {
|
|
26313
|
-
const
|
|
26314
|
-
if (!
|
|
27768
|
+
const id2 = positional[0];
|
|
27769
|
+
if (!id2) {
|
|
26315
27770
|
throw new Error("get-budget-category-benchmarks requires <categoryId> [--limit]");
|
|
26316
27771
|
}
|
|
26317
27772
|
await getBudgetCategoryBenchmarks({
|
|
26318
|
-
categoryId:
|
|
27773
|
+
categoryId: id2,
|
|
26319
27774
|
limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit")
|
|
26320
27775
|
});
|
|
26321
27776
|
break;
|
|
@@ -26330,12 +27785,12 @@ Waiting for approval\u2026
|
|
|
26330
27785
|
break;
|
|
26331
27786
|
}
|
|
26332
27787
|
case "get-supplier-pricing-history": {
|
|
26333
|
-
const
|
|
26334
|
-
if (!
|
|
27788
|
+
const id2 = positional[0];
|
|
27789
|
+
if (!id2) {
|
|
26335
27790
|
throw new Error("get-supplier-pricing-history requires <supplierId> [--itemCategoryId] [--limit]");
|
|
26336
27791
|
}
|
|
26337
27792
|
await getSupplierPricingHistory({
|
|
26338
|
-
supplierId:
|
|
27793
|
+
supplierId: id2,
|
|
26339
27794
|
itemCategoryId: getFlag(flags, "itemCategoryId"),
|
|
26340
27795
|
limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit")
|
|
26341
27796
|
});
|