@go-labs-sg/bb 2.34.0 → 2.35.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 +46 -0
- package/command-manifest.json +269 -0
- package/command-reference.md +40 -0
- package/dist/index.js +2117 -663
- package/package.json +1 -1
- package/role-aware-agent-guide.md +2 -0
package/dist/index.js
CHANGED
|
@@ -19625,77 +19625,1786 @@ 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
|
+
budgetFieldId: optionalExternalIdSchema,
|
|
20467
|
+
contactFieldId: optionalExternalIdSchema,
|
|
20468
|
+
emailFieldId: optionalExternalIdSchema,
|
|
20469
|
+
eventDateFieldId: optionalExternalIdSchema,
|
|
20470
|
+
eventTypeFieldId: optionalExternalIdSchema,
|
|
20471
|
+
eventTypeOptions: asanaOptionMapSchema.optional(),
|
|
20472
|
+
leadInFieldId: optionalExternalIdSchema,
|
|
20473
|
+
leadSourceFieldId: optionalExternalIdSchema,
|
|
20474
|
+
leadSourceOptions: asanaOptionMapSchema.optional(),
|
|
20475
|
+
organisationFieldId: optionalExternalIdSchema,
|
|
20476
|
+
phoneFieldId: optionalExternalIdSchema,
|
|
20477
|
+
slackChannelFieldId: optionalExternalIdSchema,
|
|
20478
|
+
higherOptionsFieldId: optionalExternalIdSchema,
|
|
20479
|
+
higherOptionsOptions: asanaOptionMapSchema.optional()
|
|
20480
|
+
}).strict().transform(({
|
|
20481
|
+
higherOptionsFieldId: _field,
|
|
20482
|
+
higherOptionsOptions: _options,
|
|
20483
|
+
...config3
|
|
20484
|
+
}) => config3);
|
|
20485
|
+
var saveInboundAutomationConfigInputSchema = inboundSourceIdInputSchema.extend({
|
|
20486
|
+
asanaCustomFields: inboundAsanaCustomFieldConfigSchema,
|
|
20487
|
+
asanaDefaultAssigneeId: exports_external.string().trim().min(1).max(255).nullable(),
|
|
20488
|
+
asanaDefaultAssigneeName: exports_external.string().trim().min(1).max(160).nullable(),
|
|
20489
|
+
asanaDiscussionSectionId: exports_external.string().trim().min(1).max(255).nullable().optional(),
|
|
20490
|
+
asanaFirstContactSectionId: exports_external.string().trim().min(1).max(255).nullable().optional(),
|
|
20491
|
+
asanaFirstProposalSentSectionId: exports_external.string().trim().min(1).max(255).nullable().optional(),
|
|
20492
|
+
asanaFollowerIds: exports_external.array(exports_external.string().trim().min(1).max(255)).max(50),
|
|
20493
|
+
asanaProjectId: exports_external.string().trim().min(1).max(255),
|
|
20494
|
+
asanaProposalWipSectionId: exports_external.string().trim().min(1).max(255).nullable().optional(),
|
|
20495
|
+
asanaSectionId: exports_external.string().trim().min(1).max(255),
|
|
20496
|
+
asanaWonSectionId: exports_external.string().trim().min(1).max(255),
|
|
20497
|
+
asanaLostSectionId: exports_external.string().trim().min(1).max(255),
|
|
20498
|
+
googleCalendarId: exports_external.string().trim().min(1).max(512),
|
|
20499
|
+
googleCalendarTimeZone: exports_external.string().trim().min(1).max(100),
|
|
20500
|
+
goshBriefBaseUrl: exports_external.url().max(2000).refine((value) => ["http:", "https:"].includes(new URL(value).protocol), {
|
|
20501
|
+
message: "GOSH brief URL must use HTTP or HTTPS"
|
|
20502
|
+
}),
|
|
20503
|
+
slackDefaultAssigneeId: exports_external.string().trim().min(1).max(255).nullable(),
|
|
20504
|
+
slackInboundChannelId: exports_external.string().trim().min(1).max(255),
|
|
20505
|
+
slackInviteUserIds: exports_external.array(exports_external.string().trim().min(1).max(255)).max(100),
|
|
20506
|
+
slackPitchChannelPrefix: exports_external.string().trim().regex(/^[a-z0-9-]+$/).min(1).max(30)
|
|
20507
|
+
}).strict();
|
|
20508
|
+
var updateInboundProcessingInputSchema = inboundSourceIdInputSchema.extend({
|
|
20509
|
+
processingEnabled: exports_external.boolean(),
|
|
20510
|
+
processingMode: exports_external.enum(["COLLECT_ONLY", "SHADOW", "ACTIVE"])
|
|
20511
|
+
}).superRefine(({ processingEnabled, processingMode }, ctx) => {
|
|
20512
|
+
if (processingEnabled && processingMode === "COLLECT_ONLY") {
|
|
20513
|
+
ctx.addIssue({
|
|
20514
|
+
code: "custom",
|
|
20515
|
+
message: "Collect-only sources cannot enable downstream processing.",
|
|
20516
|
+
path: ["processingEnabled"]
|
|
20517
|
+
});
|
|
20518
|
+
}
|
|
20519
|
+
});
|
|
20520
|
+
var retryInboundProvisioningInputSchema = exports_external.object({
|
|
20521
|
+
provisioningId: exports_external.string().cuid()
|
|
20522
|
+
});
|
|
20523
|
+
var typeformFieldMappingInputSchema = exports_external.object({
|
|
20524
|
+
canonicalField: typeformAdminMappableFieldSchema,
|
|
20525
|
+
required: exports_external.boolean().default(false),
|
|
20526
|
+
typeformFieldId: exports_external.string().trim().min(1).max(255),
|
|
20527
|
+
typeformFieldRef: exports_external.string().trim().min(1).max(255).nullable().optional()
|
|
20528
|
+
});
|
|
20529
|
+
var saveTypeformMappingVersionInputSchema = exports_external.object({
|
|
20530
|
+
expectedActiveMappingVersion: exports_external.number().int().min(1).nullable(),
|
|
20531
|
+
mappings: exports_external.array(typeformFieldMappingInputSchema).min(1).max(50),
|
|
20532
|
+
sourceId: exports_external.string().cuid()
|
|
20533
|
+
}).superRefine(({ mappings }, ctx) => {
|
|
20534
|
+
const canonicalFields = new Set;
|
|
20535
|
+
const typeformFieldIds = new Set;
|
|
20536
|
+
for (const [index, mapping] of mappings.entries()) {
|
|
20537
|
+
if (canonicalFields.has(mapping.canonicalField)) {
|
|
20538
|
+
ctx.addIssue({
|
|
20539
|
+
code: "custom",
|
|
20540
|
+
message: "Each canonical field can only be mapped once.",
|
|
20541
|
+
path: ["mappings", index, "canonicalField"]
|
|
20542
|
+
});
|
|
20543
|
+
}
|
|
20544
|
+
if (typeformFieldIds.has(mapping.typeformFieldId)) {
|
|
20545
|
+
ctx.addIssue({
|
|
20546
|
+
code: "custom",
|
|
20547
|
+
message: "Each Typeform field can only be mapped once.",
|
|
20548
|
+
path: ["mappings", index, "typeformFieldId"]
|
|
20549
|
+
});
|
|
20550
|
+
}
|
|
20551
|
+
canonicalFields.add(mapping.canonicalField);
|
|
20552
|
+
typeformFieldIds.add(mapping.typeformFieldId);
|
|
20553
|
+
}
|
|
20554
|
+
});
|
|
20555
|
+
var listInboundSubmissionsInputSchema = exports_external.object({
|
|
20556
|
+
page: exports_external.number().int().min(1).default(1),
|
|
20557
|
+
perPage: exports_external.number().int().min(1).max(100).default(25),
|
|
20558
|
+
sourceId: exports_external.string().cuid().optional()
|
|
20559
|
+
});
|
|
20560
|
+
// ../validators/src/inbound-lead-review.ts
|
|
20561
|
+
var inboundLeadListStatusSchema = exports_external.enum([
|
|
20562
|
+
"ACTIVE",
|
|
20563
|
+
"WON",
|
|
20564
|
+
"LOST",
|
|
20565
|
+
"DISQUALIFIED",
|
|
20566
|
+
"ALL"
|
|
20567
|
+
]);
|
|
20568
|
+
var inboundLeadListSortSchema = exports_external.enum([
|
|
20569
|
+
"ADDED_NEWEST",
|
|
20570
|
+
"ADDED_OLDEST"
|
|
20571
|
+
]);
|
|
20572
|
+
var listInboundLeadsInputSchema = exports_external.object({
|
|
20573
|
+
ownerId: exports_external.union([exports_external.literal(""), exports_external.string().cuid()]).default(""),
|
|
20574
|
+
stage: exports_external.string().max(200).default(""),
|
|
20575
|
+
sort: inboundLeadListSortSchema.default("ADDED_NEWEST"),
|
|
20576
|
+
page: exports_external.number().int().min(1).default(1),
|
|
20577
|
+
perPage: exports_external.number().int().min(1).max(100).default(25),
|
|
20578
|
+
search: exports_external.string().trim().max(200).default(""),
|
|
20579
|
+
status: inboundLeadListStatusSchema.default("ACTIVE")
|
|
20580
|
+
});
|
|
20581
|
+
var closeInboundLeadInputSchema = exports_external.object({
|
|
20582
|
+
projectId: exports_external.string().cuid(),
|
|
20583
|
+
outcome: exports_external.enum(InboundLeadCloseOutcome),
|
|
20584
|
+
reason: exports_external.string().trim().min(3).max(1000)
|
|
20585
|
+
});
|
|
20586
|
+
// ../validators/src/integration-credential/index.ts
|
|
20587
|
+
var id = exports_external.string().trim().min(1).max(128);
|
|
20588
|
+
var revision = exports_external.number().int().min(1);
|
|
20589
|
+
var integrationCredentialListInput = exports_external.object({
|
|
20590
|
+
cursor: id.optional(),
|
|
20591
|
+
limit: exports_external.number().int().min(1).max(100).default(50)
|
|
20592
|
+
}).strict().default({ limit: 50 });
|
|
20593
|
+
var integrationCredentialIdInput = exports_external.object({ id }).strict();
|
|
20594
|
+
var createIntegrationCredentialInput = exports_external.object({
|
|
20595
|
+
fields: exports_external.record(exports_external.string(), exports_external.unknown()),
|
|
20596
|
+
name: exports_external.string().trim().min(1).max(256),
|
|
20597
|
+
type: exports_external.enum(IntegrationCredentialType)
|
|
20598
|
+
}).strict();
|
|
20599
|
+
var updateIntegrationCredentialInput = exports_external.object({
|
|
20600
|
+
fields: exports_external.record(exports_external.string(), exports_external.unknown()),
|
|
20601
|
+
id,
|
|
20602
|
+
name: exports_external.string().trim().min(1).max(256).optional(),
|
|
20603
|
+
revision
|
|
20604
|
+
}).strict();
|
|
20605
|
+
var versionedIntegrationCredentialInput = exports_external.object({ id, revision }).strict();
|
|
20606
|
+
var bindingShape = {
|
|
20607
|
+
capability: exports_external.enum(IntegrationCapability),
|
|
20608
|
+
credentialId: id,
|
|
20609
|
+
inboundSourceId: id.nullish(),
|
|
20610
|
+
scope: exports_external.enum(IntegrationCredentialScope)
|
|
20611
|
+
};
|
|
20612
|
+
var validateBindingScope = (value, ctx) => {
|
|
20613
|
+
if (value.scope === IntegrationCredentialScope.GLOBAL && value.inboundSourceId != null)
|
|
20614
|
+
ctx.addIssue({
|
|
20615
|
+
code: "custom",
|
|
20616
|
+
message: "Global bindings cannot target an inbound source",
|
|
20617
|
+
path: ["inboundSourceId"]
|
|
20618
|
+
});
|
|
20619
|
+
if (value.scope === IntegrationCredentialScope.INBOUND_SOURCE && !value.inboundSourceId)
|
|
20620
|
+
ctx.addIssue({
|
|
20621
|
+
code: "custom",
|
|
20622
|
+
message: "Inbound-source bindings require an inbound source",
|
|
20623
|
+
path: ["inboundSourceId"]
|
|
20624
|
+
});
|
|
20625
|
+
};
|
|
20626
|
+
var setIntegrationCredentialBindingInput = exports_external.object(bindingShape).strict().superRefine(validateBindingScope);
|
|
20627
|
+
var removeIntegrationCredentialBindingInput = exports_external.object({
|
|
20628
|
+
capability: bindingShape.capability,
|
|
20629
|
+
inboundSourceId: bindingShape.inboundSourceId,
|
|
20630
|
+
scope: bindingShape.scope
|
|
20631
|
+
}).strict().superRefine(validateBindingScope);
|
|
20632
|
+
// ../validators/src/item/create-item.ts
|
|
20633
|
+
var MIN_GO_LABS_PRODUCT_COST = 1000;
|
|
20634
|
+
var createItemInputSchema = exports_external.object({
|
|
20635
|
+
name: quickBooksNameSchema,
|
|
20636
|
+
description: exports_external.string().optional(),
|
|
20637
|
+
descriptionRichText: exports_external.custom().optional().nullable(),
|
|
20638
|
+
categoryId: exports_external.string().min(1, { error: "Please select a category." }),
|
|
20639
|
+
cost: exports_external.string({ error: "Cost is required" }).min(1, { error: "Cost is required" }).refine((val) => {
|
|
20640
|
+
const numValue = Number.parseFloat(val);
|
|
20641
|
+
return !Number.isNaN(numValue);
|
|
20642
|
+
}, {
|
|
20643
|
+
error: "Cost must be a valid number"
|
|
20644
|
+
}).refine((val) => {
|
|
20645
|
+
const numValue = Number.parseFloat(val);
|
|
20646
|
+
return numValue >= 0;
|
|
20647
|
+
}, {
|
|
20648
|
+
error: "Cost cannot be negative"
|
|
20649
|
+
}),
|
|
20650
|
+
gstInclusive: exports_external.boolean().optional(),
|
|
20651
|
+
gstOutOfScope: exports_external.boolean().optional().nullable(),
|
|
20652
|
+
goLabsProduct: exports_external.boolean().optional(),
|
|
20653
|
+
supplierId: exports_external.string().min(1, { error: "Please select a supplier." }),
|
|
20654
|
+
attachments: exports_external.array(exports_external.object({
|
|
20655
|
+
id: exports_external.string(),
|
|
20656
|
+
name: exports_external.string().nullable(),
|
|
20657
|
+
key: exports_external.string(),
|
|
20658
|
+
size: exports_external.number()
|
|
20659
|
+
})).optional()
|
|
20660
|
+
}).refine((data) => {
|
|
20661
|
+
if (data.goLabsProduct === true && Number(data.cost) < MIN_GO_LABS_PRODUCT_COST) {
|
|
20662
|
+
return false;
|
|
20663
|
+
}
|
|
20664
|
+
return true;
|
|
20665
|
+
}, {
|
|
20666
|
+
error: `Go Labs products must have a cost of at least $${MIN_GO_LABS_PRODUCT_COST}`,
|
|
20667
|
+
path: ["cost"]
|
|
20668
|
+
}).refine((data) => !(data.gstOutOfScope && data.gstInclusive), {
|
|
20669
|
+
error: "GST-inclusive cost cannot be used for an out-of-scope item",
|
|
20670
|
+
path: ["gstInclusive"]
|
|
20671
|
+
});
|
|
20672
|
+
// ../validators/src/item/update-item.ts
|
|
20673
|
+
var updateItemInputSchema = createItemInputSchema.extend({
|
|
20674
|
+
id: exports_external.string().min(1, { error: "ID is required" })
|
|
20675
|
+
});
|
|
20676
|
+
// ../validators/src/item-category/item-category.ts
|
|
20677
|
+
var createItemCategoryInputSchema = exports_external.object({
|
|
20678
|
+
name: quickBooksNameSchema
|
|
20679
|
+
});
|
|
20680
|
+
var updateItemCategoryInputSchema = createItemCategoryInputSchema.extend({
|
|
20681
|
+
id: exports_external.string().min(1, { error: "ID is required" })
|
|
20682
|
+
});
|
|
20683
|
+
// ../validators/src/project/date-range.ts
|
|
20684
|
+
var refineProjectDateRange = (dateRange, ctx) => {
|
|
20685
|
+
if (dateRange.to === undefined)
|
|
20686
|
+
return;
|
|
20687
|
+
if (dateRange.to.getTime() >= dateRange.from.getTime())
|
|
20688
|
+
return;
|
|
20689
|
+
ctx.addIssue({
|
|
20690
|
+
code: "custom",
|
|
20691
|
+
message: "End date must be on or after start date.",
|
|
20692
|
+
path: ["to"]
|
|
20693
|
+
});
|
|
20694
|
+
};
|
|
20695
|
+
|
|
20696
|
+
// ../validators/src/project/create-project.ts
|
|
20697
|
+
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." });
|
|
20698
|
+
var slackChannelIdSchema = exports_external.string().trim().min(1, { error: "Please select a Slack channel." }).regex(/^[CG][A-Z0-9]+$/, {
|
|
20699
|
+
error: "Slack channel id must start with C or G."
|
|
20700
|
+
});
|
|
20701
|
+
var positiveWholeProjectPaxPattern = /^[1-9]\d*$/;
|
|
20702
|
+
var optionalProjectPaxSchema = exports_external.string().trim().nullish().refine((value) => value == null || value === "" || positiveWholeProjectPaxPattern.test(value), {
|
|
20703
|
+
error: "Pax must be a positive whole number when provided"
|
|
20704
|
+
});
|
|
20705
|
+
var createProjectInputSchema = exports_external.object({
|
|
20706
|
+
name: quickBooksNameSchema,
|
|
20707
|
+
description: exports_external.string().optional(),
|
|
20708
|
+
dateRange: exports_external.object({
|
|
20709
|
+
from: exports_external.date(),
|
|
20710
|
+
to: exports_external.date().optional()
|
|
20711
|
+
}).refine((dr) => dr.from != null, {
|
|
20712
|
+
error: "Please select an event date.",
|
|
20713
|
+
path: ["from"]
|
|
20714
|
+
}).superRefine(refineProjectDateRange),
|
|
20715
|
+
companyId: exports_external.string().min(1, { error: "Please select a company." }),
|
|
20716
|
+
contactPersonId: exports_external.string().min(1, { error: "Please select a contact person." }),
|
|
20717
|
+
requestQboAccountantNotification: exports_external.boolean().optional(),
|
|
20718
|
+
asanaTaskId: asanaTaskIdSchema,
|
|
20719
|
+
slackChannelId: slackChannelIdSchema,
|
|
20720
|
+
slackChannelUrl: exports_external.url({ error: "Slack channel URL must be a valid URL." }),
|
|
20721
|
+
slackChannelName: exports_external.string().trim().min(1, { error: "Slack channel name is required." }),
|
|
20722
|
+
insideSalesId: exports_external.string().min(1, { error: "Please select an inside sales." }),
|
|
20723
|
+
businessDevelopmentId: exports_external.string().min(1, { error: "Please select a business development." }),
|
|
20724
|
+
projectManagerId: exports_external.string().trim().optional(),
|
|
20725
|
+
venue: exports_external.string().min(1, { error: "Venue is required" }),
|
|
20726
|
+
pax: optionalProjectPaxSchema
|
|
20727
|
+
});
|
|
20728
|
+
|
|
20729
|
+
// ../validators/src/project/update-project.ts
|
|
20730
|
+
var PROJECT_INTEGRATION_REQUIREMENT_CUTOFF = new Date("2026-05-27T08:03:55.000Z");
|
|
20731
|
+
var updateProjectNameSchema = exports_external.string().trim().min(1, { error: "Name is required" }).max(100, { error: "Name cannot be longer than 100 characters" });
|
|
20732
|
+
var optionalAsanaTaskIdSchema = exports_external.union([asanaTaskIdSchema, exports_external.literal("")]).optional();
|
|
20733
|
+
var optionalSlackChannelIdSchema = exports_external.union([slackChannelIdSchema, exports_external.literal("")]).optional();
|
|
20734
|
+
var optionalSlackChannelUrlSchema = exports_external.union([
|
|
20735
|
+
exports_external.url({ error: "Slack channel URL must be a valid URL." }),
|
|
20736
|
+
exports_external.literal("")
|
|
20737
|
+
]).optional();
|
|
20738
|
+
var optionalSlackChannelNameSchema = exports_external.union([exports_external.string().trim().min(1).max(80), exports_external.literal("")]).optional();
|
|
20739
|
+
var updateProjectInputBaseSchema = createProjectInputSchema.extend({
|
|
20740
|
+
id: exports_external.string().min(1, { error: "ID is required" }),
|
|
20741
|
+
name: updateProjectNameSchema,
|
|
20742
|
+
confirmEstimateInvalidation: exports_external.boolean().optional(),
|
|
20743
|
+
asanaTaskId: optionalAsanaTaskIdSchema,
|
|
20744
|
+
slackChannelId: optionalSlackChannelIdSchema,
|
|
20745
|
+
slackChannelUrl: optionalSlackChannelUrlSchema,
|
|
20746
|
+
slackChannelName: optionalSlackChannelNameSchema
|
|
20747
|
+
}).partial({
|
|
20748
|
+
slackChannelId: true,
|
|
20749
|
+
slackChannelUrl: true,
|
|
20750
|
+
slackChannelName: true
|
|
20751
|
+
});
|
|
20752
|
+
var refinePartialSlackChannelGroup = (data, ctx) => {
|
|
20753
|
+
const id2 = data.slackChannelId?.trim() ?? "";
|
|
20754
|
+
const url2 = data.slackChannelUrl?.trim() ?? "";
|
|
20755
|
+
const name = data.slackChannelName?.trim() ?? "";
|
|
20756
|
+
const allEmpty = id2 === "" && url2 === "" && name === "";
|
|
20757
|
+
if (allEmpty)
|
|
20758
|
+
return;
|
|
20759
|
+
const idOk = slackChannelIdSchema.safeParse(id2).success;
|
|
20760
|
+
const urlOk = exports_external.url().safeParse(url2).success;
|
|
20761
|
+
const nameOk = exports_external.string().min(1).max(80).safeParse(name).success;
|
|
20762
|
+
if (!(idOk && urlOk && nameOk)) {
|
|
20763
|
+
ctx.addIssue({
|
|
20764
|
+
code: "custom",
|
|
20765
|
+
message: "Slack channel id, URL, and name must all be valid when any Slack field is set."
|
|
20766
|
+
});
|
|
20767
|
+
}
|
|
20768
|
+
};
|
|
20769
|
+
var updateProjectInputSchema = updateProjectInputBaseSchema.superRefine(refinePartialSlackChannelGroup);
|
|
20770
|
+
|
|
20771
|
+
// ../validators/src/supplier/create-supplier.ts
|
|
20772
|
+
var createSupplierInputSchema = exports_external.object({
|
|
20773
|
+
name: quickBooksNameSchema.min(2, {
|
|
20774
|
+
error: "Name must be at least 2 characters"
|
|
20775
|
+
}),
|
|
20776
|
+
gstRegistered: exports_external.boolean().optional(),
|
|
20777
|
+
primaryContactPerson: exports_external.string().min(2, { error: "Primary contact person must be at least 2 characters" }),
|
|
20778
|
+
secondaryContactPerson: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20779
|
+
email: exports_external.email({ error: "Invalid email address" }),
|
|
20780
|
+
phone: exports_external.string().min(8, { error: "Phone number must be at least 8 characters" }),
|
|
20781
|
+
bankAccountName: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20782
|
+
bankAccountNumber: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20783
|
+
bankBranchCode: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20784
|
+
bankCode: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20785
|
+
bankName: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20786
|
+
bankSwiftCode: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20787
|
+
payNowIdentifierType: exports_external.enum(PayNowIdentifierType).nullish().transform((val) => val ?? undefined),
|
|
20788
|
+
paymentMethodId: exports_external.string().min(1, { error: "Payment method is required" }),
|
|
20789
|
+
paymentTerm: exports_external.string().min(1, { error: "Payment term is required" }),
|
|
20790
|
+
roleId: exports_external.string().min(1, { error: "Role is required" }),
|
|
20791
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
20792
|
+
website: exports_external.union([exports_external.url({ error: "Please enter a valid URL" }), exports_external.literal("")]).nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20793
|
+
uenNumber: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20794
|
+
certificationId: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20795
|
+
address: exports_external.object({
|
|
20796
|
+
id: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20797
|
+
streetAddress1: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20798
|
+
streetAddress2: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20799
|
+
city: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20800
|
+
state: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20801
|
+
postalCode: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val),
|
|
20802
|
+
country: exports_external.string().nullish().transform((val) => val === "" || val === null ? undefined : val)
|
|
20803
|
+
}).optional(),
|
|
20804
|
+
attachments: exports_external.array(exports_external.object({
|
|
20805
|
+
id: exports_external.string(),
|
|
20806
|
+
name: exports_external.string().optional().nullable(),
|
|
20807
|
+
key: exports_external.string(),
|
|
20808
|
+
size: exports_external.number()
|
|
20809
|
+
})).optional()
|
|
20810
|
+
});
|
|
20811
|
+
|
|
20812
|
+
// ../validators/src/supplier/update-supplier.ts
|
|
20813
|
+
var updateSupplierInputSchema = createSupplierInputSchema.and(exports_external.object({
|
|
20814
|
+
id: exports_external.string()
|
|
20815
|
+
}));
|
|
20816
|
+
|
|
20817
|
+
// ../validators/src/mutation-inputs-json.ts
|
|
20818
|
+
var jsonCreateBillInputSchema = createBillInputSchemaBase.omit({
|
|
20819
|
+
invoiceDate: true
|
|
20820
|
+
}).extend({
|
|
20821
|
+
invoiceDate: exports_external.coerce.date().optional()
|
|
20822
|
+
}).superRefine(validateAlreadyPaidBillInput);
|
|
20823
|
+
var jsonUpdateBillInputSchema = updateBillInputSchemaBase.omit({
|
|
20824
|
+
invoiceDate: true
|
|
20825
|
+
}).extend({
|
|
20826
|
+
invoiceDate: exports_external.coerce.date().optional()
|
|
20827
|
+
}).superRefine(validateAlreadyPaidBillInput);
|
|
20828
|
+
var jsonCreateProjectInputSchema = createProjectInputSchema.omit({
|
|
20829
|
+
dateRange: true
|
|
20830
|
+
}).extend({
|
|
20831
|
+
dateRange: exports_external.object({
|
|
20832
|
+
from: exports_external.coerce.date(),
|
|
20833
|
+
to: exports_external.coerce.date().optional()
|
|
20834
|
+
}).superRefine(refineProjectDateRange)
|
|
20835
|
+
});
|
|
20836
|
+
var jsonUpdateProjectInputSchema = updateProjectInputBaseSchema.omit({
|
|
20837
|
+
dateRange: true
|
|
20838
|
+
}).extend({
|
|
20839
|
+
dateRange: exports_external.object({
|
|
20840
|
+
from: exports_external.coerce.date(),
|
|
20841
|
+
to: exports_external.coerce.date().optional()
|
|
20842
|
+
}).superRefine(refineProjectDateRange)
|
|
20843
|
+
}).superRefine(refinePartialSlackChannelGroup);
|
|
20844
|
+
// ../validators/src/project/update-project-status.ts
|
|
20845
|
+
var updateProjectStatusInputSchema = exports_external.object({
|
|
20846
|
+
id: exports_external.string(),
|
|
20847
|
+
status: exports_external.enum(ProjectStatus),
|
|
20848
|
+
projectManagerId: exports_external.string().trim().min(1).optional(),
|
|
20849
|
+
projectManagerEmail: exports_external.email().optional(),
|
|
20850
|
+
lostReason: exports_external.string().optional(),
|
|
20851
|
+
wonOverrideReason: exports_external.string().trim().min(10).max(1000).optional()
|
|
20852
|
+
});
|
|
20853
|
+
// ../validators/src/project-hub.ts
|
|
20854
|
+
var PROJECT_HUB_ALWAYS_ALLOWED_DOMAIN = "getout.sg";
|
|
20855
|
+
var projectHubConfiguredDomainSchema = exports_external.string().trim().toLowerCase().transform((value) => value.startsWith("@") ? value.slice(1) : value).pipe(exports_external.hostname());
|
|
20856
|
+
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());
|
|
20857
|
+
var projectHubDomainSettingsFormSchema = exports_external.object({
|
|
20858
|
+
domains: projectHubConfiguredDomainsSchema
|
|
20859
|
+
});
|
|
20860
|
+
// ../validators/src/quickbooks/qbo-project-id.ts
|
|
20861
|
+
var qboProjectIdInputSchema = exports_external.object({
|
|
20862
|
+
projectId: exports_external.string(),
|
|
20863
|
+
projectUrl: exports_external.url({ error: "Please enter a valid URL" })
|
|
20864
|
+
});
|
|
20865
|
+
// ../validators/src/quotation/index.ts
|
|
20866
|
+
var QUOTATION_ATTACHMENT_MAX_SIZE = 20 * 1024 * 1024;
|
|
20867
|
+
var quotationAttachmentSchema = exports_external.object({
|
|
20868
|
+
id: exports_external.string(),
|
|
20869
|
+
name: exports_external.string().nullable(),
|
|
20870
|
+
key: exports_external.string().min(1),
|
|
20871
|
+
size: exports_external.number().int().positive().max(QUOTATION_ATTACHMENT_MAX_SIZE, "Quotation must be 20MB or smaller")
|
|
20872
|
+
});
|
|
20873
|
+
var quotationStatusSchema = exports_external.enum([
|
|
20874
|
+
"DRAFT",
|
|
20875
|
+
"PENDING_APPROVAL",
|
|
20876
|
+
"APPROVED",
|
|
20877
|
+
"REJECTED",
|
|
20878
|
+
"SUPERSEDED"
|
|
20879
|
+
]);
|
|
20880
|
+
var hasCurrencyPrecision = (value) => Math.abs(value * 100 - Math.round(value * 100)) < 0.00000001;
|
|
20881
|
+
var quotationSubtotalSchema = exports_external.number().finite().positive().max(99999999.99).refine(hasCurrencyPrecision, "Use no more than two decimal places");
|
|
20882
|
+
var quotationGstAmountSchema = exports_external.number().finite().nonnegative().max(99999999.99).refine(hasCurrencyPrecision, "Use no more than two decimal places");
|
|
20883
|
+
var createSupplierQuotationInputSchema = exports_external.object({
|
|
20884
|
+
projectId: exports_external.string(),
|
|
20885
|
+
budgetId: exports_external.string(),
|
|
20886
|
+
supplierId: exports_external.string(),
|
|
20887
|
+
amountWithoutGst: quotationSubtotalSchema,
|
|
20888
|
+
gstAmount: quotationGstAmountSchema,
|
|
20889
|
+
lineAmounts: exports_external.array(exports_external.object({
|
|
20890
|
+
budgetItemId: exports_external.string().min(1),
|
|
20891
|
+
amount: quotationGstAmountSchema
|
|
20892
|
+
})).optional(),
|
|
20893
|
+
budgetItemIds: exports_external.array(exports_external.string()).min(1, "At least one line item is required"),
|
|
20894
|
+
attachments: exports_external.array(quotationAttachmentSchema).min(1, "Quotation file is required").max(1, "Upload one quotation file")
|
|
20895
|
+
});
|
|
20896
|
+
var updateSupplierQuotationInputSchema = createSupplierQuotationInputSchema.extend({
|
|
20897
|
+
id: exports_external.string()
|
|
20898
|
+
});
|
|
20899
|
+
var submitSupplierQuotationInputSchema = exports_external.object({
|
|
20900
|
+
id: exports_external.string(),
|
|
20901
|
+
overBudgetOverrideReason: exports_external.string().trim().min(10).max(2000).optional()
|
|
20902
|
+
});
|
|
20903
|
+
var approveSupplierQuotationInputSchema = exports_external.object({
|
|
20904
|
+
quotationApprovalId: exports_external.string(),
|
|
20905
|
+
overBudgetOverrideReason: exports_external.string().trim().min(10).max(2000).optional()
|
|
20906
|
+
});
|
|
20907
|
+
var rejectSupplierQuotationInputSchema = exports_external.object({
|
|
20908
|
+
quotationApprovalId: exports_external.string(),
|
|
20909
|
+
rejectionReason: exports_external.string().min(1, "Rejection reason is required")
|
|
20910
|
+
});
|
|
20911
|
+
var listSupplierQuotationsInputSchemaBase = exports_external.object({
|
|
20912
|
+
projectId: exports_external.string().optional(),
|
|
20913
|
+
budgetId: exports_external.string().optional(),
|
|
20914
|
+
supplierId: exports_external.string().optional(),
|
|
20915
|
+
status: exports_external.array(quotationStatusSchema).optional(),
|
|
20916
|
+
createdBy: exports_external.array(exports_external.string()).optional(),
|
|
20917
|
+
perPage: exports_external.number().int().positive().max(100).optional(),
|
|
20918
|
+
requesterIds: exports_external.array(exports_external.string()).optional(),
|
|
20919
|
+
pageSize: exports_external.number().int().positive().max(100).optional(),
|
|
20920
|
+
search: exports_external.string().trim().optional(),
|
|
20921
|
+
page: exports_external.number().int().positive().optional(),
|
|
20922
|
+
includeMetrics: exports_external.boolean().optional()
|
|
20923
|
+
});
|
|
20924
|
+
var listSupplierQuotationsInputSchema = listSupplierQuotationsInputSchemaBase.transform(({ requesterIds, pageSize, ...input2 }) => ({
|
|
20925
|
+
...input2,
|
|
20926
|
+
createdBy: input2.createdBy ?? requesterIds,
|
|
20927
|
+
perPage: input2.perPage ?? pageSize
|
|
20928
|
+
}));
|
|
20929
|
+
var getSupplierQuotationByIdInputSchema = exports_external.object({
|
|
20930
|
+
id: exports_external.string()
|
|
20931
|
+
});
|
|
20932
|
+
var deleteSupplierQuotationInputSchema = exports_external.object({
|
|
20933
|
+
id: exports_external.string()
|
|
20934
|
+
});
|
|
20935
|
+
var getSupplierQuotationSignedDownloadUrlInputSchema = exports_external.object({
|
|
20936
|
+
id: exports_external.string(),
|
|
20937
|
+
fileRole: exports_external.enum(["ORIGINAL", "STAFF_SIGNED", "FINAL_SIGNED"])
|
|
20938
|
+
});
|
|
20939
|
+
// ../validators/src/typeform/typeform-webhook.ts
|
|
20940
|
+
var typeformJsonObjectSchema = exports_external.record(exports_external.string(), exports_external.json());
|
|
20941
|
+
var typeformTimestampSchema = exports_external.string().trim().min(1).refine((value) => !Number.isNaN(Date.parse(value)));
|
|
20942
|
+
var typeformWebhookPayloadSchema = exports_external.object({
|
|
20943
|
+
event_id: exports_external.string().trim().min(1),
|
|
20944
|
+
event_type: exports_external.literal("form_response"),
|
|
20945
|
+
form_response: exports_external.object({
|
|
20946
|
+
answers: exports_external.array(typeformJsonObjectSchema).optional(),
|
|
20947
|
+
definition: typeformJsonObjectSchema.optional(),
|
|
20948
|
+
form_id: exports_external.string().trim().min(1),
|
|
20949
|
+
landed_at: typeformTimestampSchema.optional(),
|
|
20950
|
+
submitted_at: typeformTimestampSchema,
|
|
20951
|
+
token: exports_external.string().trim().min(1)
|
|
20952
|
+
}).catchall(exports_external.json())
|
|
20953
|
+
}).catchall(exports_external.json());
|
|
20954
|
+
// ../api/src/lib/payment-schedule-input.ts
|
|
20955
|
+
var paymentScheduleBillingSchema = exports_external.object({
|
|
20956
|
+
legalEntityName: exports_external.string().trim().min(1).max(500),
|
|
20957
|
+
purchaseOrder: exports_external.string().trim().max(500),
|
|
20958
|
+
billingAddress: exports_external.string().trim().max(2500),
|
|
20959
|
+
to: exports_external.union([exports_external.email(), exports_external.literal("")]),
|
|
20960
|
+
cc: exports_external.array(exports_external.email()).max(50),
|
|
20961
|
+
replyTo: exports_external.union([exports_external.email(), exports_external.literal("")]),
|
|
20962
|
+
deliveryMethod: exports_external.enum(["EMAIL", "PORTAL_UPLOAD"]),
|
|
20963
|
+
uploadUrl: exports_external.url().nullable()
|
|
20964
|
+
});
|
|
20965
|
+
var paymentScheduleIdInput = exports_external.object({ budgetId: exports_external.string().min(1) });
|
|
20966
|
+
var paymentScheduleRevisionInput = paymentScheduleIdInput.extend({
|
|
20967
|
+
expectedRevision: exports_external.int().positive()
|
|
20968
|
+
});
|
|
20969
|
+
var paymentScheduleSaveInput = paymentScheduleIdInput.extend({
|
|
20970
|
+
expectedRevision: exports_external.int().positive().nullable(),
|
|
20971
|
+
definition: paymentScheduleSchema,
|
|
20972
|
+
billing: paymentScheduleBillingSchema,
|
|
20973
|
+
scope: exports_external.enum(["BASE", "REPLACEMENT", "VARIATION"]),
|
|
20974
|
+
relatedScheduleId: exports_external.string().min(1).optional(),
|
|
20975
|
+
changeReason: exports_external.string().trim().min(1).max(2000)
|
|
20976
|
+
});
|
|
20977
|
+
var paymentScheduleExecutionInput = paymentScheduleRevisionInput.extend({
|
|
20978
|
+
instalmentIds: exports_external.array(exports_external.string().min(1)).min(1).max(20),
|
|
20979
|
+
send: exports_external.boolean().default(false)
|
|
20980
|
+
});
|
|
20981
|
+
|
|
20982
|
+
// src/runtime/error.ts
|
|
20983
|
+
var invocationErrorCodes = new Set([
|
|
20984
|
+
"USAGE",
|
|
20985
|
+
"VALIDATION",
|
|
20986
|
+
"CONFIRMATION_REQUIRED"
|
|
20987
|
+
]);
|
|
20988
|
+
var trpcCodeMap = {
|
|
20989
|
+
BAD_REQUEST: "VALIDATION",
|
|
20990
|
+
CONFLICT: "CONFLICT",
|
|
20991
|
+
FORBIDDEN: "AUTHORIZATION",
|
|
20992
|
+
INTERNAL_SERVER_ERROR: "API",
|
|
20993
|
+
METHOD_NOT_SUPPORTED: "API",
|
|
20994
|
+
NOT_FOUND: "NOT_FOUND",
|
|
20995
|
+
PARSE_ERROR: "VALIDATION",
|
|
20996
|
+
PAYLOAD_TOO_LARGE: "VALIDATION",
|
|
20997
|
+
PRECONDITION_FAILED: "CONFLICT",
|
|
20998
|
+
TIMEOUT: "NETWORK",
|
|
20999
|
+
TOO_MANY_REQUESTS: "API",
|
|
21000
|
+
UNAUTHORIZED: "AUTHENTICATION",
|
|
21001
|
+
UNPROCESSABLE_CONTENT: "VALIDATION"
|
|
21002
|
+
};
|
|
21003
|
+
var cliErrorCodes = new Set([
|
|
21004
|
+
"USAGE",
|
|
21005
|
+
"VALIDATION",
|
|
21006
|
+
"CONFIRMATION_REQUIRED",
|
|
21007
|
+
"AUTHENTICATION",
|
|
21008
|
+
"AUTHORIZATION",
|
|
21009
|
+
"NOT_FOUND",
|
|
21010
|
+
"CONFLICT",
|
|
21011
|
+
"API",
|
|
21012
|
+
"NETWORK",
|
|
21013
|
+
"ABORTED",
|
|
21014
|
+
"INTERRUPTED",
|
|
21015
|
+
"INTERNAL"
|
|
21016
|
+
]);
|
|
21017
|
+
|
|
21018
|
+
class CliRuntimeError extends Error {
|
|
21019
|
+
code;
|
|
21020
|
+
details;
|
|
21021
|
+
requestId;
|
|
21022
|
+
constructor(code, message, options = {}) {
|
|
21023
|
+
super(message);
|
|
21024
|
+
this.name = "CliRuntimeError";
|
|
21025
|
+
this.code = code;
|
|
21026
|
+
this.details = options.details;
|
|
21027
|
+
this.requestId = options.requestId;
|
|
21028
|
+
}
|
|
21029
|
+
}
|
|
21030
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21031
|
+
var asNonEmptyString = (value) => typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
21032
|
+
var asDetails = (value) => {
|
|
21033
|
+
if (!isRecord(value))
|
|
21034
|
+
return;
|
|
21035
|
+
const details = {};
|
|
21036
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
21037
|
+
if (isJsonValue(nestedValue))
|
|
21038
|
+
details[key] = nestedValue;
|
|
21039
|
+
}
|
|
21040
|
+
return Object.keys(details).length > 0 ? details : undefined;
|
|
21041
|
+
};
|
|
21042
|
+
var isJsonValue = (value) => {
|
|
21043
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
|
|
21044
|
+
return true;
|
|
21045
|
+
}
|
|
21046
|
+
if (Array.isArray(value))
|
|
21047
|
+
return value.every(isJsonValue);
|
|
21048
|
+
return isRecord(value) && Object.values(value).every(isJsonValue);
|
|
21049
|
+
};
|
|
21050
|
+
var mapErrorCode = (value) => {
|
|
21051
|
+
if (typeof value !== "string")
|
|
21052
|
+
return;
|
|
21053
|
+
if (cliErrorCodes.has(value))
|
|
21054
|
+
return value;
|
|
21055
|
+
if (value in trpcCodeMap)
|
|
21056
|
+
return trpcCodeMap[value];
|
|
21057
|
+
return;
|
|
21058
|
+
};
|
|
21059
|
+
var networkMessagePattern = /\b(fetch failed|network error|unable to connect|econnrefused|econnreset|enotfound|etimedout|socket hang up)\b/i;
|
|
21060
|
+
var isNetworkError = (error61, depth = 0) => {
|
|
21061
|
+
if (depth > 3 || !isRecord(error61))
|
|
21062
|
+
return false;
|
|
21063
|
+
const message = asNonEmptyString(error61.message);
|
|
21064
|
+
if (message !== undefined && networkMessagePattern.test(message))
|
|
21065
|
+
return true;
|
|
21066
|
+
const code = asNonEmptyString(error61.code);
|
|
21067
|
+
if (code !== undefined && networkMessagePattern.test(code))
|
|
21068
|
+
return true;
|
|
21069
|
+
return isNetworkError(error61.cause, depth + 1);
|
|
21070
|
+
};
|
|
21071
|
+
var normalizeCliError = (error61) => {
|
|
21072
|
+
if (error61 instanceof CliRuntimeError) {
|
|
21073
|
+
return {
|
|
21074
|
+
code: error61.code,
|
|
21075
|
+
message: error61.message,
|
|
21076
|
+
...error61.details === undefined ? {} : { details: error61.details },
|
|
21077
|
+
...error61.requestId === undefined ? {} : { requestId: error61.requestId }
|
|
21078
|
+
};
|
|
21079
|
+
}
|
|
21080
|
+
if (isNetworkError(error61)) {
|
|
21081
|
+
return {
|
|
21082
|
+
code: "NETWORK",
|
|
21083
|
+
message: error61 instanceof Error ? error61.message : "The Budget Builder API could not be reached."
|
|
21084
|
+
};
|
|
21085
|
+
}
|
|
21086
|
+
if (isRecord(error61)) {
|
|
21087
|
+
const data = isRecord(error61.data) ? error61.data : undefined;
|
|
21088
|
+
const transportCode = mapErrorCode(data?.code ?? error61.code);
|
|
21089
|
+
if (error61 instanceof Error && data === undefined && transportCode === undefined) {
|
|
21090
|
+
return { code: "INTERNAL", message: error61.message };
|
|
21091
|
+
}
|
|
21092
|
+
const code = transportCode ?? "API";
|
|
21093
|
+
const message = asNonEmptyString(error61.message) ?? "The Budget Builder API request failed.";
|
|
21094
|
+
const requestId = asNonEmptyString(data?.requestId) ?? asNonEmptyString(data?.requestID) ?? asNonEmptyString(error61.requestId);
|
|
21095
|
+
const details = asDetails(data?.details ?? error61.details);
|
|
21096
|
+
return {
|
|
21097
|
+
code,
|
|
21098
|
+
message,
|
|
21099
|
+
...details === undefined ? {} : { details },
|
|
21100
|
+
...requestId === undefined ? {} : { requestId }
|
|
21101
|
+
};
|
|
21102
|
+
}
|
|
21103
|
+
if (error61 instanceof Error) {
|
|
21104
|
+
return { code: "INTERNAL", message: error61.message };
|
|
21105
|
+
}
|
|
21106
|
+
return { code: "INTERNAL", message: "An unexpected CLI error occurred." };
|
|
21107
|
+
};
|
|
21108
|
+
var exitCodeForCliError = (error61) => {
|
|
21109
|
+
if (error61.code === "INTERRUPTED")
|
|
21110
|
+
return 130;
|
|
21111
|
+
return invocationErrorCodes.has(error61.code) ? 2 : 1;
|
|
21112
|
+
};
|
|
21113
|
+
|
|
21114
|
+
// src/runtime/confirmation.ts
|
|
21115
|
+
var effectLabels = {
|
|
21116
|
+
"state-change": "changes Budget Builder state",
|
|
21117
|
+
email: "sends email",
|
|
21118
|
+
"external-write": "writes to an external system",
|
|
21119
|
+
delete: "deletes data",
|
|
21120
|
+
"financial-write": "creates or changes financial records"
|
|
21121
|
+
};
|
|
21122
|
+
var isEffectAllowed = (effect, permissions) => {
|
|
21123
|
+
switch (effect) {
|
|
21124
|
+
case "state-change":
|
|
21125
|
+
return permissions.allowStateChange === true;
|
|
21126
|
+
case "email":
|
|
21127
|
+
return permissions.allowEmail === true;
|
|
21128
|
+
case "external-write":
|
|
21129
|
+
return permissions.allowExternalWrite === true;
|
|
21130
|
+
case "delete":
|
|
21131
|
+
return permissions.allowDelete === true;
|
|
21132
|
+
case "financial-write":
|
|
21133
|
+
return permissions.allowFinancialWrite === true;
|
|
21134
|
+
}
|
|
21135
|
+
};
|
|
21136
|
+
var uniqueEffects = (effects) => [...new Set(effects)];
|
|
21137
|
+
var getRequiredEffectPermissions = (effects, permissions) => uniqueEffects(effects).filter((effect) => !isEffectAllowed(effect, permissions));
|
|
21138
|
+
var formatConfirmationPreview = (request) => [
|
|
21139
|
+
"Budget Builder workflow preview",
|
|
21140
|
+
`Action: ${request.action}`,
|
|
21141
|
+
`Target: ${request.target?.trim() || "not specified"}`,
|
|
21142
|
+
`Effects: ${uniqueEffects(request.effects).map((effect) => effectLabels[effect]).join("; ") || "none"}`,
|
|
21143
|
+
...request.details === undefined ? [] : [`Details: ${request.details}`]
|
|
21144
|
+
].join(`
|
|
21145
|
+
`);
|
|
21146
|
+
var confirmSensitiveCommand = async (runtime2, request) => {
|
|
21147
|
+
const requiredPermissions = getRequiredEffectPermissions(request.effects, request.permissions);
|
|
21148
|
+
if (request.effects.length === 0) {
|
|
21149
|
+
return { confirmed: true, requiredPermissions };
|
|
21150
|
+
}
|
|
21151
|
+
const target = request.target?.trim();
|
|
21152
|
+
if (!runtime2.isTTY) {
|
|
21153
|
+
if (target === undefined) {
|
|
21154
|
+
throw new CliRuntimeError("CONFIRMATION_REQUIRED", "Non-interactive sensitive commands require an explicit target.", {
|
|
21155
|
+
details: {
|
|
21156
|
+
action: request.action,
|
|
21157
|
+
effects: requiredPermissions,
|
|
21158
|
+
missingFlags: requiredPermissions.map((effect) => `--allow-${effect}`),
|
|
21159
|
+
target: "required"
|
|
21160
|
+
}
|
|
21161
|
+
});
|
|
21162
|
+
}
|
|
21163
|
+
if (requiredPermissions.length === 0) {
|
|
21164
|
+
return { confirmed: true, requiredPermissions };
|
|
21165
|
+
}
|
|
21166
|
+
const missing = requiredPermissions.map((effect) => `--allow-${effect}`);
|
|
21167
|
+
throw new CliRuntimeError("CONFIRMATION_REQUIRED", "Non-interactive sensitive commands require an explicit target and all applicable allow flags.", {
|
|
21168
|
+
details: {
|
|
21169
|
+
action: request.action,
|
|
21170
|
+
effects: requiredPermissions,
|
|
21171
|
+
missingFlags: missing
|
|
21172
|
+
}
|
|
21173
|
+
});
|
|
21174
|
+
}
|
|
21175
|
+
if (requiredPermissions.length === 0) {
|
|
21176
|
+
return { confirmed: true, requiredPermissions };
|
|
21177
|
+
}
|
|
21178
|
+
const preview = formatConfirmationPreview(request);
|
|
21179
|
+
runtime2.stderr.write(`${preview}
|
|
21180
|
+
`);
|
|
21181
|
+
if (runtime2.prompt === undefined) {
|
|
21182
|
+
throw new CliRuntimeError("CONFIRMATION_REQUIRED", "This command needs an interactive confirmation prompt.");
|
|
21183
|
+
}
|
|
21184
|
+
const answer = await runtime2.prompt("Type CONFIRM to continue: ");
|
|
21185
|
+
if (answer !== "CONFIRM") {
|
|
21186
|
+
throw new CliRuntimeError("ABORTED", "Command aborted by user.");
|
|
21187
|
+
}
|
|
21188
|
+
return { confirmed: true, preview, requiredPermissions };
|
|
21189
|
+
};
|
|
21190
|
+
// src/runtime/sanitize.ts
|
|
21191
|
+
var sensitiveKeys = new Set([
|
|
21192
|
+
"apikey",
|
|
21193
|
+
"authorization",
|
|
21194
|
+
"bb_api_key",
|
|
21195
|
+
"password",
|
|
21196
|
+
"paymentproof",
|
|
21197
|
+
"paymentproofattachments",
|
|
21198
|
+
"paymentproofpath",
|
|
21199
|
+
"paymentreference",
|
|
21200
|
+
"payload",
|
|
21201
|
+
"proof",
|
|
21202
|
+
"receipt",
|
|
21203
|
+
"secret",
|
|
21204
|
+
"token"
|
|
21205
|
+
]);
|
|
21206
|
+
|
|
21207
|
+
// src/runtime/output.ts
|
|
21208
|
+
var serialize = (value) => `${JSON.stringify(value, null, 2)}
|
|
21209
|
+
`;
|
|
21210
|
+
var createSuccessEnvelope = (data, invocation) => ({
|
|
21211
|
+
ok: true,
|
|
21212
|
+
data,
|
|
21213
|
+
meta: {
|
|
21214
|
+
command: invocation.command,
|
|
21215
|
+
cliVersion: invocation.cliVersion,
|
|
21216
|
+
apiBaseUrl: invocation.apiBaseUrl
|
|
21217
|
+
}
|
|
21218
|
+
});
|
|
21219
|
+
var emitCliSuccess = (runtime2, invocation, data) => {
|
|
21220
|
+
runtime2.stdout.write(serialize(invocation.legacy ? data : createSuccessEnvelope(data, invocation)));
|
|
21221
|
+
return 0;
|
|
21222
|
+
};
|
|
21223
|
+
var emitCliError = (runtime2, invocation, error61) => {
|
|
21224
|
+
const normalized = normalizeCliError(error61);
|
|
21225
|
+
if (invocation.legacy) {
|
|
21226
|
+
runtime2.stderr.write(serialize({ error: normalized.message }));
|
|
21227
|
+
} else {
|
|
21228
|
+
const envelope = {
|
|
21229
|
+
ok: false,
|
|
21230
|
+
error: normalized,
|
|
21231
|
+
meta: { command: invocation.command }
|
|
21232
|
+
};
|
|
21233
|
+
runtime2.stderr.write(serialize(envelope));
|
|
21234
|
+
}
|
|
21235
|
+
return exitCodeForCliError(normalized);
|
|
21236
|
+
};
|
|
21237
|
+
// src/runtime/process-runtime.ts
|
|
21238
|
+
import { createInterface } from "readline/promises";
|
|
21239
|
+
var createProcessRuntime = () => ({
|
|
21240
|
+
stdout: {
|
|
21241
|
+
write: (value) => {
|
|
21242
|
+
process.stdout.write(value);
|
|
21243
|
+
}
|
|
21244
|
+
},
|
|
21245
|
+
stderr: {
|
|
21246
|
+
write: (value) => {
|
|
21247
|
+
process.stderr.write(value);
|
|
21248
|
+
}
|
|
21249
|
+
},
|
|
21250
|
+
isTTY: Boolean(process.stdin.isTTY && process.stderr.isTTY),
|
|
21251
|
+
env: process.env,
|
|
21252
|
+
prompt: async (message) => {
|
|
21253
|
+
const prompt = createInterface({
|
|
21254
|
+
input: process.stdin,
|
|
21255
|
+
output: process.stderr
|
|
21256
|
+
});
|
|
21257
|
+
try {
|
|
21258
|
+
return await prompt.question(message);
|
|
21259
|
+
} finally {
|
|
21260
|
+
prompt.close();
|
|
21261
|
+
}
|
|
21262
|
+
},
|
|
21263
|
+
now: () => new Date
|
|
21264
|
+
});
|
|
21265
|
+
// src/runtime/session.ts
|
|
21266
|
+
var activeSession;
|
|
21267
|
+
var setActiveCliSession = (session) => {
|
|
21268
|
+
activeSession = {
|
|
21269
|
+
...session,
|
|
21270
|
+
confirmedEffects: new Set
|
|
21271
|
+
};
|
|
21272
|
+
};
|
|
21273
|
+
var clearActiveCliSession = () => {
|
|
21274
|
+
activeSession = undefined;
|
|
21275
|
+
};
|
|
21276
|
+
var getActiveCliSession = () => activeSession;
|
|
21277
|
+
var requireSession = () => {
|
|
21278
|
+
if (activeSession === undefined) {
|
|
21279
|
+
throw new Error("Budget Builder CLI runtime session is not configured.");
|
|
21280
|
+
}
|
|
21281
|
+
return activeSession;
|
|
21282
|
+
};
|
|
21283
|
+
var toJsonValue = (value) => {
|
|
21284
|
+
const serialized = JSON.stringify(value, (_key, nestedValue) => typeof nestedValue === "bigint" ? nestedValue.toString() : nestedValue);
|
|
21285
|
+
if (serialized === undefined)
|
|
21286
|
+
return null;
|
|
21287
|
+
return JSON.parse(serialized);
|
|
19645
21288
|
};
|
|
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"
|
|
21289
|
+
var emitCommandResult = (data) => {
|
|
21290
|
+
const session = requireSession();
|
|
21291
|
+
return emitCliSuccess(session.runtime, session.invocation, toJsonValue(data));
|
|
19656
21292
|
};
|
|
19657
|
-
var
|
|
19658
|
-
|
|
19659
|
-
|
|
19660
|
-
REJECTED: "REJECTED",
|
|
19661
|
-
SUPERSEDED: "SUPERSEDED"
|
|
21293
|
+
var emitCommandError = (error61) => {
|
|
21294
|
+
const session = requireSession();
|
|
21295
|
+
return emitCliError(session.runtime, session.invocation, error61);
|
|
19662
21296
|
};
|
|
19663
|
-
var
|
|
19664
|
-
|
|
19665
|
-
|
|
19666
|
-
|
|
19667
|
-
|
|
19668
|
-
|
|
21297
|
+
var confirmCurrentCommand = async (input2) => {
|
|
21298
|
+
const session = requireSession();
|
|
21299
|
+
const effects = input2.effects ?? session.effects;
|
|
21300
|
+
const unconfirmedEffects = effects.filter((effect) => !session.confirmedEffects.has(effect));
|
|
21301
|
+
if (unconfirmedEffects.length === 0)
|
|
21302
|
+
return;
|
|
21303
|
+
await confirmSensitiveCommand(session.runtime, {
|
|
21304
|
+
action: input2.action,
|
|
21305
|
+
target: input2.target,
|
|
21306
|
+
details: input2.details,
|
|
21307
|
+
effects: unconfirmedEffects,
|
|
21308
|
+
permissions: session.permissions
|
|
21309
|
+
});
|
|
21310
|
+
for (const effect of unconfirmedEffects) {
|
|
21311
|
+
session.confirmedEffects.add(effect);
|
|
21312
|
+
}
|
|
19669
21313
|
};
|
|
19670
|
-
|
|
19671
|
-
|
|
19672
|
-
|
|
19673
|
-
|
|
19674
|
-
|
|
21314
|
+
// src/commands/invoice-splits.ts
|
|
21315
|
+
var invoiceSplitSetupNextAction = (budgetId) => ({
|
|
21316
|
+
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.",
|
|
21317
|
+
reviewCommand: `bb customer-invoice splits ${budgetId}`,
|
|
21318
|
+
previewCommand: "bb customer-invoice preview-splits --payload '<reviewed-splits-json>'",
|
|
21319
|
+
saveCommand: "bb customer-invoice save-splits --payload '<reviewed-splits-json>'",
|
|
21320
|
+
createCommand: "bb customer-invoice create-splits --payload '<budgetId-expectedRevision-instalmentIds-json>'"
|
|
21321
|
+
});
|
|
21322
|
+
var parseInvoiceSplitSelection = (raw) => {
|
|
21323
|
+
const input2 = paymentScheduleExecutionInput.strict().parse(raw);
|
|
21324
|
+
if (input2.send) {
|
|
21325
|
+
throw new Error("Creation and sending are separate actions. Use customer-invoice send-group after approval.");
|
|
21326
|
+
}
|
|
21327
|
+
if (new Set(input2.instalmentIds).size !== input2.instalmentIds.length) {
|
|
21328
|
+
throw new Error("Select each instalment only once.");
|
|
21329
|
+
}
|
|
21330
|
+
return {
|
|
21331
|
+
budgetId: input2.budgetId,
|
|
21332
|
+
expectedRevision: input2.expectedRevision,
|
|
21333
|
+
instalmentIds: input2.instalmentIds
|
|
21334
|
+
};
|
|
19675
21335
|
};
|
|
19676
|
-
var
|
|
19677
|
-
|
|
19678
|
-
INVESTIGATING: "INVESTIGATING",
|
|
19679
|
-
RESOLVED: "RESOLVED",
|
|
19680
|
-
IGNORED: "IGNORED"
|
|
21336
|
+
var getCustomerInvoiceSplits = async (budgetId) => {
|
|
21337
|
+
emitCommandResult(await api2.paymentSchedule.get.query({ budgetId }));
|
|
19681
21338
|
};
|
|
19682
|
-
var
|
|
19683
|
-
|
|
19684
|
-
|
|
19685
|
-
APPROVED: "APPROVED",
|
|
19686
|
-
REJECTED_VOIDED: "REJECTED_VOIDED",
|
|
19687
|
-
EXPIRED_VOIDED: "EXPIRED_VOIDED",
|
|
19688
|
-
PARTIAL_QBO_FAILURE: "PARTIAL_QBO_FAILURE"
|
|
21339
|
+
var previewCustomerInvoiceSplits = async (raw) => {
|
|
21340
|
+
const input2 = paymentScheduleSaveInput.strict().parse(raw);
|
|
21341
|
+
emitCommandResult(await api2.paymentSchedule.preview.query(input2));
|
|
19689
21342
|
};
|
|
19690
|
-
var
|
|
19691
|
-
|
|
19692
|
-
|
|
19693
|
-
|
|
19694
|
-
|
|
19695
|
-
|
|
19696
|
-
|
|
21343
|
+
var saveCustomerInvoiceSplits = async (raw) => {
|
|
21344
|
+
const input2 = paymentScheduleSaveInput.strict().parse(raw);
|
|
21345
|
+
const preview = await api2.paymentSchedule.preview.query(input2);
|
|
21346
|
+
await confirmCurrentCommand({
|
|
21347
|
+
action: "Save invoice splits and contractual due dates",
|
|
21348
|
+
target: `budget ${input2.budgetId}`,
|
|
21349
|
+
details: `${preview.printedTerms}; ${preview.changes.join("; ")}. Saves the split setup only; does not create QBO invoices or request invoice approval.`
|
|
21350
|
+
});
|
|
21351
|
+
emitCommandResult(await api2.paymentSchedule.save.mutate(input2));
|
|
21352
|
+
};
|
|
21353
|
+
var createCustomerInvoiceSplits = async (raw) => {
|
|
21354
|
+
const input2 = parseInvoiceSplitSelection(raw);
|
|
21355
|
+
const current = await api2.paymentSchedule.get.query({
|
|
21356
|
+
budgetId: input2.budgetId
|
|
21357
|
+
});
|
|
21358
|
+
if (current.schedule?.revision !== input2.expectedRevision) {
|
|
21359
|
+
throw new Error("Invoice split revision changed; refresh and review it again.");
|
|
21360
|
+
}
|
|
21361
|
+
const rows = current.schedule.instalments.filter((row) => input2.instalmentIds.includes(row.id));
|
|
21362
|
+
if (rows.length !== input2.instalmentIds.length) {
|
|
21363
|
+
throw new Error("Every selected instalment must belong to this budget.");
|
|
21364
|
+
}
|
|
21365
|
+
await confirmCurrentCommand({
|
|
21366
|
+
action: "Create split invoices in QuickBooks",
|
|
21367
|
+
target: `budget ${input2.budgetId}, revision ${input2.expectedRevision}`,
|
|
21368
|
+
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.`
|
|
21369
|
+
});
|
|
21370
|
+
emitCommandResult(await api2.paymentSchedule.execute.mutate({ ...input2, send: false }));
|
|
21371
|
+
};
|
|
21372
|
+
var sendCustomerInvoiceGroup = async (raw, allApproved = false) => {
|
|
21373
|
+
const selection = allApproved ? paymentScheduleExecutionInput.omit({ instalmentIds: true, send: true }).strict().parse(raw) : parseInvoiceSplitSelection(raw);
|
|
21374
|
+
const current = await api2.paymentSchedule.get.query({
|
|
21375
|
+
budgetId: selection.budgetId
|
|
21376
|
+
});
|
|
21377
|
+
if (current.schedule?.revision !== selection.expectedRevision) {
|
|
21378
|
+
throw new Error("Invoice split revision changed; refresh and review it again.");
|
|
21379
|
+
}
|
|
21380
|
+
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);
|
|
21381
|
+
const input2 = parseInvoiceSplitSelection({
|
|
21382
|
+
budgetId: selection.budgetId,
|
|
21383
|
+
expectedRevision: selection.expectedRevision,
|
|
21384
|
+
instalmentIds: selectedIds
|
|
21385
|
+
});
|
|
21386
|
+
const rows = current.schedule.instalments.filter((row) => input2.instalmentIds.includes(row.id));
|
|
21387
|
+
if (rows.length !== input2.instalmentIds.length || rows.some((row) => row.invoice?.status !== "APPROVED" || row.invoice.sentAt)) {
|
|
21388
|
+
throw new Error("Select only approved, unsent invoices from this budget.");
|
|
21389
|
+
}
|
|
21390
|
+
const billing = rows[0]?.invoice?.deliveryBilling;
|
|
21391
|
+
if (billing?.deliveryMethod !== "EMAIL" || !billing.to) {
|
|
21392
|
+
throw new Error("Review the invoice email recipient before sending.");
|
|
21393
|
+
}
|
|
21394
|
+
await confirmCurrentCommand({
|
|
21395
|
+
action: "Send approved invoices together",
|
|
21396
|
+
target: `${rows.length} invoice(s) for budget ${input2.budgetId}`,
|
|
21397
|
+
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.`
|
|
21398
|
+
});
|
|
21399
|
+
emitCommandResult(await api2.paymentSchedule.send.mutate(input2));
|
|
19697
21400
|
};
|
|
19698
21401
|
|
|
21402
|
+
// src/commands/index.ts
|
|
21403
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
21404
|
+
import { readFile as readFile2, writeFile } from "fs/promises";
|
|
21405
|
+
import { basename } from "path";
|
|
21406
|
+
import { createInterface as createInterface2 } from "readline/promises";
|
|
21407
|
+
|
|
19699
21408
|
// ../utils/src/filter-enums.ts
|
|
19700
21409
|
var ExtendedApprovalStatus = {
|
|
19701
21410
|
...ApprovalStatus,
|
|
@@ -20102,162 +21811,30 @@ var createRichTextFromPlainText = (text) => {
|
|
|
20102
21811
|
const paragraphs = lines.length ? lines : [""];
|
|
20103
21812
|
return {
|
|
20104
21813
|
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." };
|
|
21814
|
+
type: "doc",
|
|
21815
|
+
content: paragraphs.map((line) => ({
|
|
21816
|
+
type: "paragraph",
|
|
21817
|
+
...line ? { content: [{ type: "text", text: line }] } : {}
|
|
21818
|
+
}))
|
|
21819
|
+
},
|
|
21820
|
+
html: paragraphs.map((line) => `<p>${escapeHtml(line)}</p>`).join("")
|
|
21821
|
+
};
|
|
20256
21822
|
};
|
|
20257
|
-
var
|
|
20258
|
-
|
|
20259
|
-
|
|
20260
|
-
|
|
21823
|
+
var isRichTextJson = (value) => Boolean(value && typeof value === "object" && "json" in value && "html" in value && typeof value.html === "string");
|
|
21824
|
+
|
|
21825
|
+
// src/rich-text.ts
|
|
21826
|
+
var normalizePlainTextRichTextField = ({
|
|
21827
|
+
input: input2,
|
|
21828
|
+
textField,
|
|
21829
|
+
richTextField
|
|
21830
|
+
}) => {
|
|
21831
|
+
if (isRichTextJson(input2[richTextField]) || input2[textField] === undefined) {
|
|
21832
|
+
return input2;
|
|
21833
|
+
}
|
|
21834
|
+
return {
|
|
21835
|
+
...input2,
|
|
21836
|
+
[richTextField]: createRichTextFromPlainText(String(input2[textField] ?? ""))
|
|
21837
|
+
};
|
|
20261
21838
|
};
|
|
20262
21839
|
|
|
20263
21840
|
// src/parse-mutation-payload.ts
|
|
@@ -20316,7 +21893,7 @@ var parseQuotationReferences = (input2, commandName) => {
|
|
|
20316
21893
|
if (!Array.isArray(rawQuotationIds)) {
|
|
20317
21894
|
throw new Error(`${commandName} payload.quotationIds must be a string array.`);
|
|
20318
21895
|
}
|
|
20319
|
-
quotationIds = rawQuotationIds.map((
|
|
21896
|
+
quotationIds = rawQuotationIds.map((id2, index) => requiredString(id2, `${commandName} payload.quotationIds[${index}]`));
|
|
20320
21897
|
if (new Set(quotationIds).size !== quotationIds.length) {
|
|
20321
21898
|
throw new Error(`${commandName} payload.quotationIds must not contain duplicates.`);
|
|
20322
21899
|
}
|
|
@@ -20469,9 +22046,9 @@ function parseCreateBudgetPayload(raw) {
|
|
|
20469
22046
|
}
|
|
20470
22047
|
function parseUpdateBudgetPayload(raw) {
|
|
20471
22048
|
const o = requireObject(raw, "update-budget payload");
|
|
20472
|
-
const
|
|
22049
|
+
const id2 = requiredString(o.id, "update-budget payload.id");
|
|
20473
22050
|
const base = parseCreateBudgetPayload(raw);
|
|
20474
|
-
return { ...base, id };
|
|
22051
|
+
return { ...base, id: id2 };
|
|
20475
22052
|
}
|
|
20476
22053
|
function parseUpdateBudgetCommissionPayload(raw) {
|
|
20477
22054
|
const o = requireObject(raw, "update-budget-commission payload");
|
|
@@ -20563,7 +22140,7 @@ function parseCreateBillPayload(raw, draftOnlyFlag, quotationExempt = false, req
|
|
|
20563
22140
|
throw new Error("create-bill payload.amount must be a number >= 0.");
|
|
20564
22141
|
}
|
|
20565
22142
|
const quotationReferences = parseQuotationReferences(o, "create-bill");
|
|
20566
|
-
const parsedBudgetItemIds = budgetItemIds.map((
|
|
22143
|
+
const parsedBudgetItemIds = budgetItemIds.map((id2, i) => requiredString(id2, `create-bill payload.budgetItemIds[${i}]`));
|
|
20567
22144
|
const lineAmounts = parseBillLineAmounts(o, "create-bill");
|
|
20568
22145
|
validateExplicitBillAllocations({
|
|
20569
22146
|
amount,
|
|
@@ -20635,7 +22212,7 @@ function parseValidateBillSelectionPayload(raw, quotationFlags = {}) {
|
|
|
20635
22212
|
throw new Error("Employee claims cannot include quotation source inputs.");
|
|
20636
22213
|
}
|
|
20637
22214
|
const lineAmounts = parseBillLineAmounts(input2, "validate-bill-selection");
|
|
20638
|
-
const budgetItemIds = Array.isArray(input2.budgetItemIds) ? input2.budgetItemIds.map((
|
|
22215
|
+
const budgetItemIds = Array.isArray(input2.budgetItemIds) ? input2.budgetItemIds.map((id2, index) => requiredString(id2, `validate-bill-selection payload.budgetItemIds[${index}]`)) : [];
|
|
20639
22216
|
const amount = input2.amount === undefined || input2.amount === null ? undefined : Number(input2.amount);
|
|
20640
22217
|
if (lineAmounts || payloadReferences.quotationAllocations) {
|
|
20641
22218
|
if (amount === undefined || !Number.isFinite(amount) || budgetItemIds.length === 0) {
|
|
@@ -20680,7 +22257,7 @@ function parseUpdateBillPaymentEvidencePayload(raw) {
|
|
|
20680
22257
|
var validateQuotationAllocations = (raw, command) => {
|
|
20681
22258
|
const input2 = requireObject(raw, `${command} payload`);
|
|
20682
22259
|
const ids = input2.budgetItemIds;
|
|
20683
|
-
if (!Array.isArray(ids) || ids.length === 0 || ids.some((
|
|
22260
|
+
if (!Array.isArray(ids) || ids.length === 0 || ids.some((id2) => typeof id2 !== "string" || id2.length === 0) || new Set(ids).size !== ids.length) {
|
|
20684
22261
|
throw new CliRuntimeError("VALIDATION", `${command} payload.budgetItemIds must contain each selected budget-item ID exactly once.`);
|
|
20685
22262
|
}
|
|
20686
22263
|
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 +22400,6 @@ function parseCompanyUpdatePayload(raw) {
|
|
|
20823
22400
|
return raw;
|
|
20824
22401
|
}
|
|
20825
22402
|
|
|
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
22403
|
// src/commands/index.ts
|
|
21027
22404
|
import { contentType } from "mime-types";
|
|
21028
22405
|
var BUDGET = "BUDGET";
|
|
@@ -21250,7 +22627,7 @@ var combineDateAndTime = (day, time3) => {
|
|
|
21250
22627
|
d.setHours(time3.getHours(), time3.getMinutes(), time3.getSeconds(), 0);
|
|
21251
22628
|
return d;
|
|
21252
22629
|
};
|
|
21253
|
-
var
|
|
22630
|
+
var formatOptionalProjectPaxInput2 = (pax) => {
|
|
21254
22631
|
if (typeof pax !== "number" || !Number.isFinite(pax) || pax <= 0) {
|
|
21255
22632
|
return "";
|
|
21256
22633
|
}
|
|
@@ -21282,7 +22659,7 @@ var billAttachmentContentTypeForFileName = (fileName) => {
|
|
|
21282
22659
|
return resolvedContentType;
|
|
21283
22660
|
};
|
|
21284
22661
|
var PAYMENT_PROOF_MAX_SIZE = 20 * 1024 * 1024;
|
|
21285
|
-
var
|
|
22662
|
+
var QUOTATION_ATTACHMENT_MAX_SIZE2 = 20 * 1024 * 1024;
|
|
21286
22663
|
var quotationAttachmentContentTypeForFileName = (fileName) => {
|
|
21287
22664
|
const resolvedContentType = contentTypeHeaderForFileName(fileName);
|
|
21288
22665
|
if (resolvedContentType === "application/pdf" || resolvedContentType === "image/jpeg" || resolvedContentType === "image/png") {
|
|
@@ -21349,31 +22726,31 @@ Confirm the user explicitly approved this exact action, then type "${expected}"
|
|
|
21349
22726
|
}
|
|
21350
22727
|
};
|
|
21351
22728
|
var assertLockedBudgetChangeConfirmed = async ({
|
|
21352
|
-
budget,
|
|
22729
|
+
budget: budget2,
|
|
21353
22730
|
action
|
|
21354
22731
|
}) => {
|
|
21355
|
-
if (!LOCKED_BUDGET_STATUSES_REQUIRING_CONFIRMATION.has(
|
|
22732
|
+
if (!LOCKED_BUDGET_STATUSES_REQUIRING_CONFIRMATION.has(budget2.status)) {
|
|
21356
22733
|
return;
|
|
21357
22734
|
}
|
|
21358
|
-
const statusLabel = lockedBudgetStatusLabel(
|
|
22735
|
+
const statusLabel = lockedBudgetStatusLabel(budget2.status);
|
|
21359
22736
|
if (getActiveCliSession()?.invocation.legacy !== true) {
|
|
21360
22737
|
await confirmCurrentCommand({
|
|
21361
22738
|
action,
|
|
21362
|
-
target: `budget ${
|
|
21363
|
-
details: `Changes locked budget "${
|
|
22739
|
+
target: `budget ${budget2.id}`,
|
|
22740
|
+
details: `Changes locked budget "${budget2.name}" in status ${statusLabel}.`,
|
|
21364
22741
|
effects: ["state-change"]
|
|
21365
22742
|
});
|
|
21366
22743
|
return;
|
|
21367
22744
|
}
|
|
21368
22745
|
if (!process.stdin.isTTY || !process.stderr.isTTY) {
|
|
21369
|
-
throw new Error(`${action} would change locked budget "${
|
|
22746
|
+
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
22747
|
}
|
|
21371
22748
|
const rl = createInterface2({
|
|
21372
22749
|
input: process.stdin,
|
|
21373
22750
|
output: process.stderr
|
|
21374
22751
|
});
|
|
21375
22752
|
try {
|
|
21376
|
-
const answer = await rl.question(`${action} will change locked budget "${
|
|
22753
|
+
const answer = await rl.question(`${action} will change locked budget "${budget2.name}" (${budget2.id}) with status ${statusLabel}. Type "yes" to continue: `);
|
|
21377
22754
|
if (answer.trim().toLowerCase() !== "yes") {
|
|
21378
22755
|
throw new Error("Aborted.");
|
|
21379
22756
|
}
|
|
@@ -21382,21 +22759,21 @@ var assertLockedBudgetChangeConfirmed = async ({
|
|
|
21382
22759
|
}
|
|
21383
22760
|
};
|
|
21384
22761
|
var confirmLockedBudgetChangeByBudgetId = async (budgetId, action) => {
|
|
21385
|
-
const
|
|
22762
|
+
const budget2 = await api2.budget.getBudget.query({ id: budgetId });
|
|
21386
22763
|
await assertLockedBudgetChangeConfirmed({
|
|
21387
22764
|
budget: {
|
|
21388
|
-
id:
|
|
21389
|
-
name:
|
|
21390
|
-
status:
|
|
22765
|
+
id: budget2.id,
|
|
22766
|
+
name: budget2.name,
|
|
22767
|
+
status: budget2.status
|
|
21391
22768
|
},
|
|
21392
22769
|
action
|
|
21393
22770
|
});
|
|
21394
22771
|
};
|
|
21395
22772
|
var confirmLockedBudgetChangeByBudgetItemId = async (budgetItemId, action) => {
|
|
21396
|
-
const { budget } = await api2.budgetItem.getBudgetItemContext.query({
|
|
22773
|
+
const { budget: budget2 } = await api2.budgetItem.getBudgetItemContext.query({
|
|
21397
22774
|
budgetItemId
|
|
21398
22775
|
});
|
|
21399
|
-
await assertLockedBudgetChangeConfirmed({ budget, action });
|
|
22776
|
+
await assertLockedBudgetChangeConfirmed({ budget: budget2, action });
|
|
21400
22777
|
};
|
|
21401
22778
|
var updateProjectWithEstimateInvalidationConfirmation = async (input2) => {
|
|
21402
22779
|
try {
|
|
@@ -21433,12 +22810,12 @@ async function listBudgets(opts) {
|
|
|
21433
22810
|
const result = await api2.budget.getAllBudgets.query(input2);
|
|
21434
22811
|
out(result);
|
|
21435
22812
|
}
|
|
21436
|
-
async function getBudget(
|
|
21437
|
-
const [
|
|
21438
|
-
api2.budget.getBudget.query({ id }),
|
|
21439
|
-
api2.budget.getBudgetItems.query({ budgetId:
|
|
22813
|
+
async function getBudget(id2) {
|
|
22814
|
+
const [budget2, items] = await Promise.all([
|
|
22815
|
+
api2.budget.getBudget.query({ id: id2 }),
|
|
22816
|
+
api2.budget.getBudgetItems.query({ budgetId: id2 })
|
|
21440
22817
|
]);
|
|
21441
|
-
out({ budget, items });
|
|
22818
|
+
out({ budget: budget2, items });
|
|
21442
22819
|
}
|
|
21443
22820
|
async function updateBudgetStatus(budgetId, status, opts) {
|
|
21444
22821
|
await assertSensitiveWorkflowConfirmed({
|
|
@@ -21463,28 +22840,31 @@ async function updateBudgetStatus(budgetId, status, opts) {
|
|
|
21463
22840
|
projectInvoiceSettings: opts.projectInvoiceSettings
|
|
21464
22841
|
}
|
|
21465
22842
|
});
|
|
21466
|
-
out(
|
|
22843
|
+
out({
|
|
22844
|
+
...result,
|
|
22845
|
+
invoiceSetup: result.newStatus === "ESTIMATE_ACCEPTED" && result.hasQuickbooksEstimate ? invoiceSplitSetupNextAction(budgetId) : null
|
|
22846
|
+
});
|
|
21467
22847
|
}
|
|
21468
22848
|
var assertBudgetActionReadiness = async ({
|
|
21469
22849
|
budgetId,
|
|
21470
22850
|
requireQuickbooksProject,
|
|
21471
22851
|
actionLabel
|
|
21472
22852
|
}) => {
|
|
21473
|
-
const
|
|
21474
|
-
const hasNoItems =
|
|
22853
|
+
const budget2 = await api2.budget.getBudget.query({ id: budgetId });
|
|
22854
|
+
const hasNoItems = budget2.budgetItems.length === 0;
|
|
21475
22855
|
if (hasNoItems) {
|
|
21476
22856
|
throw new Error(`Cannot ${actionLabel}: there are no line items in this budget.`);
|
|
21477
22857
|
}
|
|
21478
|
-
const hasZeroCostItems =
|
|
22858
|
+
const hasZeroCostItems = budget2.budgetItems.some((item2) => Number(item2.cost ?? 0) === 0);
|
|
21479
22859
|
if (hasZeroCostItems) {
|
|
21480
22860
|
throw new Error(`Cannot ${actionLabel}: all line items must have a cost greater than zero.`);
|
|
21481
22861
|
}
|
|
21482
|
-
const inactiveSuppliers =
|
|
22862
|
+
const inactiveSuppliers = budget2.budgetItems.filter((item2) => !item2.supplier.active).map((item2) => item2.supplier.name);
|
|
21483
22863
|
const uniqueInactiveSuppliers = [...new Set(inactiveSuppliers)];
|
|
21484
22864
|
if (uniqueInactiveSuppliers.length > 0) {
|
|
21485
22865
|
throw new Error(`Cannot ${actionLabel}: inactive suppliers found (${uniqueInactiveSuppliers.join(", ")}).`);
|
|
21486
22866
|
}
|
|
21487
|
-
if (requireQuickbooksProject && !
|
|
22867
|
+
if (requireQuickbooksProject && !budget2.project.quickbooksProjectId) {
|
|
21488
22868
|
throw new Error("Cannot create estimate: project is not linked to QuickBooks yet.");
|
|
21489
22869
|
}
|
|
21490
22870
|
};
|
|
@@ -21498,32 +22878,32 @@ var runBudgetPreflight = async (budgetId, preflight) => {
|
|
|
21498
22878
|
await api2.contactPerson.updateContactPerson.mutate(preflight.contactPerson);
|
|
21499
22879
|
}
|
|
21500
22880
|
if (preflight.eventDetails) {
|
|
21501
|
-
const
|
|
21502
|
-
const
|
|
21503
|
-
id:
|
|
22881
|
+
const budget2 = await api2.budget.getBudget.query({ id: budgetId });
|
|
22882
|
+
const project2 = await api2.project.getProjectById.query({
|
|
22883
|
+
id: budget2.projectId
|
|
21504
22884
|
});
|
|
21505
22885
|
const day = new Date(preflight.eventDetails.date);
|
|
21506
22886
|
const start = new Date(preflight.eventDetails.startTime);
|
|
21507
22887
|
const end = new Date(preflight.eventDetails.endTime);
|
|
21508
22888
|
const payload = {
|
|
21509
|
-
id:
|
|
21510
|
-
name:
|
|
21511
|
-
description:
|
|
22889
|
+
id: project2.id,
|
|
22890
|
+
name: project2.name,
|
|
22891
|
+
description: project2.description ?? undefined,
|
|
21512
22892
|
dateRange: {
|
|
21513
22893
|
from: combineDateAndTime(day, start),
|
|
21514
22894
|
to: combineDateAndTime(day, end)
|
|
21515
22895
|
},
|
|
21516
|
-
companyId:
|
|
21517
|
-
contactPersonId:
|
|
21518
|
-
asanaTaskId:
|
|
21519
|
-
insideSalesId:
|
|
21520
|
-
businessDevelopmentId:
|
|
21521
|
-
venue:
|
|
21522
|
-
pax:
|
|
21523
|
-
...
|
|
21524
|
-
slackChannelId:
|
|
21525
|
-
slackChannelUrl:
|
|
21526
|
-
slackChannelName:
|
|
22896
|
+
companyId: project2.companyId,
|
|
22897
|
+
contactPersonId: project2.contactPersonId,
|
|
22898
|
+
asanaTaskId: project2.asanaTaskId ?? "",
|
|
22899
|
+
insideSalesId: project2.insideSalesId ?? "",
|
|
22900
|
+
businessDevelopmentId: project2.businessDevelopmentId ?? "",
|
|
22901
|
+
venue: project2.venue ?? "",
|
|
22902
|
+
pax: formatOptionalProjectPaxInput2(project2.pax),
|
|
22903
|
+
...project2.slackChannelId && project2.slackChannelUrl && project2.slackChannelName ? {
|
|
22904
|
+
slackChannelId: project2.slackChannelId,
|
|
22905
|
+
slackChannelUrl: project2.slackChannelUrl,
|
|
22906
|
+
slackChannelName: project2.slackChannelName
|
|
21527
22907
|
} : {}
|
|
21528
22908
|
};
|
|
21529
22909
|
await updateProjectWithEstimateInvalidationConfirmation(payload);
|
|
@@ -21584,31 +22964,31 @@ async function listApprovals(opts) {
|
|
|
21584
22964
|
const typeFilter = opts.type === undefined || opts.type === "ALL" ? (_n) => true : (n) => n.type === opts.type;
|
|
21585
22965
|
const filtered = pending.filter(typeFilter).filter((n) => n.type !== BUDGET || !n.budget?.quickbooksEstimateId);
|
|
21586
22966
|
const items = filtered.map((n) => {
|
|
21587
|
-
let
|
|
22967
|
+
let id2;
|
|
21588
22968
|
let name;
|
|
21589
22969
|
if (n.type === BUDGET && n.budget) {
|
|
21590
|
-
|
|
22970
|
+
id2 = n.budgetId ?? n.id;
|
|
21591
22971
|
name = n.budget.name;
|
|
21592
22972
|
} else if (n.type === BILL && n.bill) {
|
|
21593
|
-
|
|
22973
|
+
id2 = n.bill.id;
|
|
21594
22974
|
name = `Bill ${n.bill.invoiceNumber ?? n.bill.id.slice(0, 8)}`;
|
|
21595
22975
|
} else if (n.type === SUPPLIER && n.supplier) {
|
|
21596
|
-
|
|
22976
|
+
id2 = n.supplier.id;
|
|
21597
22977
|
name = n.supplier.name;
|
|
21598
22978
|
} else if (n.type === QUOTATION && n.supplierQuotation) {
|
|
21599
|
-
|
|
22979
|
+
id2 = n.supplierQuotation.id;
|
|
21600
22980
|
name = `Quotation ${n.supplierQuotation.supplier.name}`;
|
|
21601
22981
|
} else if (n.type === CUSTOMER_INVOICE && n.customerInvoiceBatch) {
|
|
21602
|
-
|
|
22982
|
+
id2 = n.customerInvoiceBatch.id;
|
|
21603
22983
|
name = `Customer invoice ${n.customerInvoiceBatch.project.name}`;
|
|
21604
22984
|
} else {
|
|
21605
|
-
|
|
22985
|
+
id2 = n.id;
|
|
21606
22986
|
name = "Unknown";
|
|
21607
22987
|
}
|
|
21608
22988
|
return {
|
|
21609
22989
|
approvalId: n.id,
|
|
21610
22990
|
type: n.type,
|
|
21611
|
-
id,
|
|
22991
|
+
id: id2,
|
|
21612
22992
|
name,
|
|
21613
22993
|
requestedBy: n.requester?.name ?? n.requester?.email ?? "Unknown",
|
|
21614
22994
|
requestedAt: typeof n.createdAt === "string" ? n.createdAt : n.createdAt instanceof Date ? n.createdAt.toISOString() : String(n.createdAt)
|
|
@@ -21637,7 +23017,7 @@ async function createBillApproval(billId) {
|
|
|
21637
23017
|
details: "and send approval request emails"
|
|
21638
23018
|
});
|
|
21639
23019
|
const result = await api2.bill.createApproval.mutate({ billId });
|
|
21640
|
-
const approvalIds = (result.results ?? []).map((
|
|
23020
|
+
const approvalIds = (result.results ?? []).map((item2) => ({ id: item2.id }));
|
|
21641
23021
|
let email3;
|
|
21642
23022
|
if (approvalIds.length === 0) {
|
|
21643
23023
|
email3 = createApprovalEmailDeliveryFailure("No approval ids returned");
|
|
@@ -21730,8 +23110,8 @@ async function updateBillStatus(opts) {
|
|
|
21730
23110
|
entity: `bill ${opts.id}`,
|
|
21731
23111
|
details: `to ${opts.status}`
|
|
21732
23112
|
});
|
|
21733
|
-
const
|
|
21734
|
-
const paymentProof =
|
|
23113
|
+
const bill2 = opts.paymentProofPath ? await api2.bill.getById.query({ id: opts.id }) : null;
|
|
23114
|
+
const paymentProof = bill2 && opts.paymentProofPath ? await stageBillPaymentProof(bill2.projectId, opts.paymentProofPath) : undefined;
|
|
21735
23115
|
try {
|
|
21736
23116
|
const result = await api2.bill.updateStatus.mutate({
|
|
21737
23117
|
id: opts.id,
|
|
@@ -21744,8 +23124,8 @@ async function updateBillStatus(opts) {
|
|
|
21744
23124
|
});
|
|
21745
23125
|
out(result);
|
|
21746
23126
|
} catch (error61) {
|
|
21747
|
-
await rethrowAfterStagedPaymentProofCleanup(error61,
|
|
21748
|
-
await deleteStagedPaymentProof(
|
|
23127
|
+
await rethrowAfterStagedPaymentProofCleanup(error61, bill2 && paymentProof ? async () => {
|
|
23128
|
+
await deleteStagedPaymentProof(bill2.projectId, paymentProof.key);
|
|
21749
23129
|
} : undefined);
|
|
21750
23130
|
}
|
|
21751
23131
|
}
|
|
@@ -21763,15 +23143,15 @@ async function patchBillPayment(opts) {
|
|
|
21763
23143
|
const result = await api2.bill.patchPayment.mutate(input2);
|
|
21764
23144
|
out(result);
|
|
21765
23145
|
}
|
|
21766
|
-
async function patchBillInvoiceNumber(
|
|
23146
|
+
async function patchBillInvoiceNumber(id2, invoiceNumber) {
|
|
21767
23147
|
const result = await api2.bill.patchInvoiceNumber.mutate({
|
|
21768
|
-
id,
|
|
23148
|
+
id: id2,
|
|
21769
23149
|
invoiceNumber
|
|
21770
23150
|
});
|
|
21771
23151
|
out(result);
|
|
21772
23152
|
}
|
|
21773
|
-
async function getBillAttachments(
|
|
21774
|
-
const result = await api2.bill.getAttachments.query({ id });
|
|
23153
|
+
async function getBillAttachments(id2) {
|
|
23154
|
+
const result = await api2.bill.getAttachments.query({ id: id2 });
|
|
21775
23155
|
out(result);
|
|
21776
23156
|
}
|
|
21777
23157
|
async function stageBillAttachmentsFromPaths(projectId, filePaths) {
|
|
@@ -21887,7 +23267,7 @@ async function uploadBillDocumentsFromPaths({
|
|
|
21887
23267
|
async function uploadQuotationAttachmentFromPath(projectId, filePath) {
|
|
21888
23268
|
const buf = await readFile2(filePath);
|
|
21889
23269
|
const fileName = basename(filePath);
|
|
21890
|
-
if (buf.byteLength >
|
|
23270
|
+
if (buf.byteLength > QUOTATION_ATTACHMENT_MAX_SIZE2) {
|
|
21891
23271
|
throw new Error("Quotation attachment must be 20MB or smaller.");
|
|
21892
23272
|
}
|
|
21893
23273
|
const attachmentContentType = quotationAttachmentContentTypeForFileName(fileName);
|
|
@@ -21992,13 +23372,16 @@ async function uploadBudgetWinProofFromPath(budgetId, filePath, opts) {
|
|
|
21992
23372
|
projectInvoiceSettings: opts.projectInvoiceSettings
|
|
21993
23373
|
}
|
|
21994
23374
|
});
|
|
21995
|
-
out(
|
|
23375
|
+
out({
|
|
23376
|
+
...result,
|
|
23377
|
+
invoiceSetup: result.hasQuickbooksEstimate ? invoiceSplitSetupNextAction(budgetId) : null
|
|
23378
|
+
});
|
|
21996
23379
|
}
|
|
21997
23380
|
async function markBudgetWonWithProof(budgetId, filePath, opts) {
|
|
21998
23381
|
await uploadBudgetWinProofFromPath(budgetId, filePath, opts);
|
|
21999
23382
|
}
|
|
22000
|
-
async function getBillDetails(
|
|
22001
|
-
const result = await api2.bill.getById.query({ id });
|
|
23383
|
+
async function getBillDetails(id2) {
|
|
23384
|
+
const result = await api2.bill.getById.query({ id: id2 });
|
|
22002
23385
|
out(result);
|
|
22003
23386
|
}
|
|
22004
23387
|
async function getBudgetItemsOnly(budgetId) {
|
|
@@ -22064,7 +23447,7 @@ async function createBudgetApproval(budgetId, preflight) {
|
|
|
22064
23447
|
actionLabel: "request approval"
|
|
22065
23448
|
});
|
|
22066
23449
|
const result = await api2.budget.createBudgetApproval.mutate({ budgetId });
|
|
22067
|
-
const approvalIds = (result.results ?? []).map((
|
|
23450
|
+
const approvalIds = (result.results ?? []).map((item2) => ({ id: item2.id }));
|
|
22068
23451
|
let email3;
|
|
22069
23452
|
if (approvalIds.length === 0) {
|
|
22070
23453
|
email3 = createApprovalEmailDeliveryFailure("No approval ids returned");
|
|
@@ -22149,8 +23532,8 @@ async function updateBudgetCategory(opts) {
|
|
|
22149
23532
|
const result = await api2.budget.updateBudgetCategory.mutate(opts);
|
|
22150
23533
|
out(result);
|
|
22151
23534
|
}
|
|
22152
|
-
async function deleteBudgetCategory(
|
|
22153
|
-
const result = await api2.budget.deleteBudgetCategory.mutate({ id });
|
|
23535
|
+
async function deleteBudgetCategory(id2) {
|
|
23536
|
+
const result = await api2.budget.deleteBudgetCategory.mutate({ id: id2 });
|
|
22154
23537
|
out(result);
|
|
22155
23538
|
}
|
|
22156
23539
|
async function updateBudgetCommissionFromPayload(raw) {
|
|
@@ -22291,8 +23674,8 @@ async function listQuotations(opts) {
|
|
|
22291
23674
|
const result = await api2.quotation.list.query(opts);
|
|
22292
23675
|
out(result);
|
|
22293
23676
|
}
|
|
22294
|
-
async function getQuotationDetails(
|
|
22295
|
-
const result = await api2.quotation.getById.query({ id });
|
|
23677
|
+
async function getQuotationDetails(id2) {
|
|
23678
|
+
const result = await api2.quotation.getById.query({ id: id2 });
|
|
22296
23679
|
out(result);
|
|
22297
23680
|
}
|
|
22298
23681
|
async function createQuotationFromPayload(raw) {
|
|
@@ -22305,24 +23688,24 @@ var updateQuotationFromPayload = async (raw) => {
|
|
|
22305
23688
|
const result = await api2.quotation.updateDraftOrRejected.mutate(input2);
|
|
22306
23689
|
out(result);
|
|
22307
23690
|
};
|
|
22308
|
-
var deleteQuotationById = async (
|
|
22309
|
-
const result = await api2.quotation.delete.mutate({ id });
|
|
23691
|
+
var deleteQuotationById = async (id2) => {
|
|
23692
|
+
const result = await api2.quotation.delete.mutate({ id: id2 });
|
|
22310
23693
|
out(result);
|
|
22311
23694
|
};
|
|
22312
|
-
async function submitQuotation(
|
|
23695
|
+
async function submitQuotation(id2, overBudgetOverrideReason) {
|
|
22313
23696
|
await assertSensitiveWorkflowConfirmed({
|
|
22314
23697
|
action: "Submit quotation for approval",
|
|
22315
|
-
entity: `quotation ${
|
|
23698
|
+
entity: `quotation ${id2}`
|
|
22316
23699
|
});
|
|
22317
23700
|
const result = await api2.quotation.submitForApproval.mutate({
|
|
22318
|
-
id,
|
|
23701
|
+
id: id2,
|
|
22319
23702
|
overBudgetOverrideReason
|
|
22320
23703
|
});
|
|
22321
23704
|
out(result);
|
|
22322
23705
|
}
|
|
22323
|
-
async function downloadQuotationPdf(
|
|
23706
|
+
async function downloadQuotationPdf(id2, fileRole) {
|
|
22324
23707
|
const result = await api2.quotation.getSignedDownloadUrl.query({
|
|
22325
|
-
id,
|
|
23708
|
+
id: id2,
|
|
22326
23709
|
fileRole
|
|
22327
23710
|
});
|
|
22328
23711
|
out(result);
|
|
@@ -22484,28 +23867,28 @@ var updateBillPaymentEvidenceFromPayload = async (raw) => {
|
|
|
22484
23867
|
const result = await api2.bill.updatePaymentEvidence.mutate(input2);
|
|
22485
23868
|
out(result);
|
|
22486
23869
|
};
|
|
22487
|
-
async function deleteBillById(
|
|
22488
|
-
const result = await api2.bill.delete.mutate({ id });
|
|
23870
|
+
async function deleteBillById(id2) {
|
|
23871
|
+
const result = await api2.bill.delete.mutate({ id: id2 });
|
|
22489
23872
|
out(result);
|
|
22490
23873
|
}
|
|
22491
23874
|
async function createSupplierFromPayload(raw) {
|
|
22492
23875
|
const input2 = parseSupplierCreatePayload(raw);
|
|
22493
23876
|
const created = await api2.supplier.createSupplier.mutate(input2);
|
|
22494
|
-
const
|
|
22495
|
-
if (
|
|
23877
|
+
const supplier2 = created.result;
|
|
23878
|
+
if (supplier2.status !== "PENDING_APPROVAL") {
|
|
22496
23879
|
out(created);
|
|
22497
23880
|
return;
|
|
22498
23881
|
}
|
|
22499
23882
|
await assertSensitiveWorkflowConfirmed({
|
|
22500
23883
|
action: "Create supplier approval request",
|
|
22501
|
-
entity: `supplier ${
|
|
23884
|
+
entity: `supplier ${supplier2.id}`,
|
|
22502
23885
|
details: "and send approval request emails"
|
|
22503
23886
|
});
|
|
22504
23887
|
const approval = await api2.supplier.createSupplierApproval.mutate({
|
|
22505
|
-
supplierId:
|
|
23888
|
+
supplierId: supplier2.id
|
|
22506
23889
|
});
|
|
22507
23890
|
let emailResult;
|
|
22508
|
-
const approvalIds = (approval.results ?? []).map((
|
|
23891
|
+
const approvalIds = (approval.results ?? []).map((item2) => ({ id: item2.id }));
|
|
22509
23892
|
if (approvalIds.length === 0) {
|
|
22510
23893
|
emailResult = createApprovalEmailDeliveryFailure("No approval ids returned");
|
|
22511
23894
|
} else {
|
|
@@ -22623,7 +24006,7 @@ async function rejectBudget(budgetId, reason) {
|
|
|
22623
24006
|
});
|
|
22624
24007
|
}
|
|
22625
24008
|
async function getSupplierDetails(supplierId) {
|
|
22626
|
-
const
|
|
24009
|
+
const supplier2 = await api2.supplier.getSupplierById.query({
|
|
22627
24010
|
id: supplierId
|
|
22628
24011
|
});
|
|
22629
24012
|
const respondedStatuses = new Set([
|
|
@@ -22631,14 +24014,14 @@ async function getSupplierDetails(supplierId) {
|
|
|
22631
24014
|
ExtendedApprovalStatus.REJECTED
|
|
22632
24015
|
]);
|
|
22633
24016
|
const latestRespondedApproval = [
|
|
22634
|
-
...
|
|
24017
|
+
...supplier2.approvals.filter((approval) => respondedStatuses.has(approval.status))
|
|
22635
24018
|
].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 =
|
|
24019
|
+
const pendingApprovals = supplier2.approvals.filter((approval) => approval.status === ExtendedApprovalStatus.PENDING_APPROVAL);
|
|
24020
|
+
const supersededApprovals = supplier2.approvals.filter((approval) => approval.status === ExtendedApprovalStatus.SUPERSEDED);
|
|
22638
24021
|
const respondedByApproval = latestRespondedApproval ?? null;
|
|
22639
24022
|
const responseUser = respondedByApproval ? respondedByApproval.responder ?? respondedByApproval.approver : null;
|
|
22640
24023
|
out({
|
|
22641
|
-
...
|
|
24024
|
+
...supplier2,
|
|
22642
24025
|
supplierApprovalSummary: {
|
|
22643
24026
|
pendingApprovalCount: pendingApprovals.length,
|
|
22644
24027
|
pendingApprovers: pendingApprovals.map((approval) => ({
|
|
@@ -22810,9 +24193,9 @@ async function listCompanies(opts) {
|
|
|
22810
24193
|
});
|
|
22811
24194
|
out(result);
|
|
22812
24195
|
}
|
|
22813
|
-
async function getCompany(
|
|
22814
|
-
const
|
|
22815
|
-
out(
|
|
24196
|
+
async function getCompany(id2) {
|
|
24197
|
+
const company2 = await api2.company.getCompanyById.query({ id: id2 });
|
|
24198
|
+
out(company2);
|
|
22816
24199
|
}
|
|
22817
24200
|
async function createCompany(opts) {
|
|
22818
24201
|
const result = await api2.company.createCompany.mutate(opts);
|
|
@@ -22843,13 +24226,13 @@ var listProjectHubs = async (opts) => {
|
|
|
22843
24226
|
});
|
|
22844
24227
|
out(result);
|
|
22845
24228
|
};
|
|
22846
|
-
async function getProject(
|
|
22847
|
-
const [
|
|
22848
|
-
api2.project.getProjectDetailsById.query({ id }),
|
|
22849
|
-
api2.project.getProjectBudgetsOverview.query({ projectId:
|
|
22850
|
-
api2.project.getProjectBillsOverview.query({ projectId:
|
|
24229
|
+
async function getProject(id2) {
|
|
24230
|
+
const [project2, budgetsOverview, billsOverview] = await Promise.all([
|
|
24231
|
+
api2.project.getProjectDetailsById.query({ id: id2 }),
|
|
24232
|
+
api2.project.getProjectBudgetsOverview.query({ projectId: id2 }),
|
|
24233
|
+
api2.project.getProjectBillsOverview.query({ projectId: id2 })
|
|
22851
24234
|
]);
|
|
22852
|
-
out({ project, budgetsOverview, billsOverview });
|
|
24235
|
+
out({ project: project2, budgetsOverview, billsOverview });
|
|
22853
24236
|
}
|
|
22854
24237
|
var getProjectHubStatus = async (projectId) => {
|
|
22855
24238
|
const result = await api2.project.getProjectHubShareLink.query({ projectId });
|
|
@@ -22905,14 +24288,14 @@ async function createProject(opts) {
|
|
|
22905
24288
|
});
|
|
22906
24289
|
out(result);
|
|
22907
24290
|
}
|
|
22908
|
-
async function updateProjectStatus(
|
|
24291
|
+
async function updateProjectStatus(id2, status, opts) {
|
|
22909
24292
|
await assertSensitiveWorkflowConfirmed({
|
|
22910
24293
|
action: "Update project status",
|
|
22911
|
-
entity: `project ${
|
|
24294
|
+
entity: `project ${id2}`,
|
|
22912
24295
|
details: `to ${status}`
|
|
22913
24296
|
});
|
|
22914
24297
|
const result = await api2.project.updateProjectStatus.mutate({
|
|
22915
|
-
id,
|
|
24298
|
+
id: id2,
|
|
22916
24299
|
status,
|
|
22917
24300
|
...opts?.projectManagerId && { projectManagerId: opts.projectManagerId },
|
|
22918
24301
|
...opts?.projectManagerEmail && {
|
|
@@ -23021,9 +24404,9 @@ async function listItems(opts) {
|
|
|
23021
24404
|
});
|
|
23022
24405
|
out(result);
|
|
23023
24406
|
}
|
|
23024
|
-
async function getItem(
|
|
23025
|
-
const
|
|
23026
|
-
out(
|
|
24407
|
+
async function getItem(id2) {
|
|
24408
|
+
const item2 = await api2.item.getItem.query({ id: id2 });
|
|
24409
|
+
out(item2);
|
|
23027
24410
|
}
|
|
23028
24411
|
async function listItemCategories(opts) {
|
|
23029
24412
|
const result = await api2.itemCategory.getItemCategories.query({
|
|
@@ -23714,6 +25097,47 @@ var registry2 = [
|
|
|
23714
25097
|
"customer-invoice",
|
|
23715
25098
|
"import-qbo"
|
|
23716
25099
|
]),
|
|
25100
|
+
{
|
|
25101
|
+
...legacyCommand("get-customer-invoice-splits", [
|
|
25102
|
+
"customer-invoice",
|
|
25103
|
+
"splits"
|
|
25104
|
+
]),
|
|
25105
|
+
summary: "Review acceptance invoice splits, due dates, approval states and saved billing details. Arguments: <budgetId>."
|
|
25106
|
+
},
|
|
25107
|
+
{
|
|
25108
|
+
...legacyCommand("preview-customer-invoice-splits", [
|
|
25109
|
+
"customer-invoice",
|
|
25110
|
+
"preview-splits"
|
|
25111
|
+
]),
|
|
25112
|
+
summary: "Preview splits, GST totals and due-date changes without writes. Requires --payload with the schedule save input.",
|
|
25113
|
+
effects: []
|
|
25114
|
+
},
|
|
25115
|
+
{
|
|
25116
|
+
...legacyCommand("save-customer-invoice-splits", [
|
|
25117
|
+
"customer-invoice",
|
|
25118
|
+
"save-splits"
|
|
25119
|
+
]),
|
|
25120
|
+
summary: "Save split amounts and separate due dates after estimate acceptance. Requires --payload; does not create invoices or request approval.",
|
|
25121
|
+
effects: ["state-change"]
|
|
25122
|
+
},
|
|
25123
|
+
{
|
|
25124
|
+
...legacyCommand("create-customer-invoice-splits", [
|
|
25125
|
+
"customer-invoice",
|
|
25126
|
+
"create-splits"
|
|
25127
|
+
]),
|
|
25128
|
+
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.",
|
|
25129
|
+
effects: ["state-change", "financial-write", "external-write", "email"]
|
|
25130
|
+
},
|
|
25131
|
+
{
|
|
25132
|
+
...legacyCommand("send-customer-invoice-group", ["customer-invoice", "send-group"], [], [
|
|
25133
|
+
{
|
|
25134
|
+
name: "--all-approved",
|
|
25135
|
+
description: "Select all approved unsent invoices; payload must contain only budgetId and expectedRevision."
|
|
25136
|
+
}
|
|
25137
|
+
]),
|
|
25138
|
+
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.",
|
|
25139
|
+
effects: ["state-change", "email", "external-write"]
|
|
25140
|
+
},
|
|
23717
25141
|
legacyCommand("create-customer-invoice", ["customer-invoice", "create"]),
|
|
23718
25142
|
legacyCommand("discard-customer-invoice", ["customer-invoice", "discard"]),
|
|
23719
25143
|
legacyCommand("delete-customer-invoice", ["customer-invoice", "delete"]),
|
|
@@ -24287,7 +25711,12 @@ Customer invoices
|
|
|
24287
25711
|
get-customer-invoice-email-context <batchId> Contact, required CC, budget, and project context for composing an invoice email.
|
|
24288
25712
|
preview-qbo-customer-invoice <budgetId> <qboInvoiceId> Preview linking an existing QBO transaction (numeric ID, not invoice number); works on accepted/closed estimates.
|
|
24289
25713
|
import-qbo-customer-invoice <budgetId> <qboInvoiceId> --token <previewToken> Confirm the reviewed BB-only link; requires --allow-state-change --allow-financial-write in automation.
|
|
24290
|
-
|
|
25714
|
+
customer-invoice splits <budgetId> Review invoice split setup after estimate acceptance.
|
|
25715
|
+
customer-invoice preview-splits --payload '<json>' Preview amounts including GST and separate contractual due dates.
|
|
25716
|
+
customer-invoice save-splits --payload '<json>' Save split/date setup only, without QBO writes or approval requests.
|
|
25717
|
+
customer-invoice create-splits --payload '<json>' Create selected QBO invoices; non-admin requests invoice approval, admin auto-approves. Never sends to customer.
|
|
25718
|
+
customer-invoice send-group --payload '<json>' [--all-approved] Send approved invoices together in one email; one selected instalment sends individually.
|
|
25719
|
+
create-customer-invoice --payload '<json>' Legacy budget without a schedule: one split with label, percentage, and dueDate.
|
|
24291
25720
|
discard-customer-invoice <batchId> Discard a reserved CREATING batch with no created QBO invoices.
|
|
24292
25721
|
delete-customer-invoice <batchId> Delete QBO invoices and the local batch.
|
|
24293
25722
|
void-customer-invoice <batchId> Void the invoice batch in QuickBooks.
|
|
@@ -24769,10 +26198,10 @@ Waiting for approval\u2026
|
|
|
24769
26198
|
break;
|
|
24770
26199
|
}
|
|
24771
26200
|
case "get-budget": {
|
|
24772
|
-
const
|
|
24773
|
-
if (!
|
|
26201
|
+
const id2 = positional[0];
|
|
26202
|
+
if (!id2)
|
|
24774
26203
|
throw new Error("get-budget requires <id>");
|
|
24775
|
-
await getBudget(
|
|
26204
|
+
await getBudget(id2);
|
|
24776
26205
|
break;
|
|
24777
26206
|
}
|
|
24778
26207
|
case "upload-budget-attachment": {
|
|
@@ -24796,17 +26225,17 @@ Waiting for approval\u2026
|
|
|
24796
26225
|
break;
|
|
24797
26226
|
}
|
|
24798
26227
|
case "get-budget-items": {
|
|
24799
|
-
const
|
|
24800
|
-
if (!
|
|
26228
|
+
const id2 = positional[0];
|
|
26229
|
+
if (!id2)
|
|
24801
26230
|
throw new Error("get-budget-items requires <budgetId>");
|
|
24802
|
-
await getBudgetItemsOnly(
|
|
26231
|
+
await getBudgetItemsOnly(id2);
|
|
24803
26232
|
break;
|
|
24804
26233
|
}
|
|
24805
26234
|
case "get-budget-details": {
|
|
24806
|
-
const
|
|
24807
|
-
if (!
|
|
26235
|
+
const id2 = positional[0];
|
|
26236
|
+
if (!id2)
|
|
24808
26237
|
throw new Error("get-budget-details requires <budgetId>");
|
|
24809
|
-
await getBudgetDetails(
|
|
26238
|
+
await getBudgetDetails(id2);
|
|
24810
26239
|
break;
|
|
24811
26240
|
}
|
|
24812
26241
|
case "get-budget-categories": {
|
|
@@ -24814,10 +26243,10 @@ Waiting for approval\u2026
|
|
|
24814
26243
|
break;
|
|
24815
26244
|
}
|
|
24816
26245
|
case "get-budget-versions": {
|
|
24817
|
-
const
|
|
24818
|
-
if (!
|
|
26246
|
+
const id2 = positional[0];
|
|
26247
|
+
if (!id2)
|
|
24819
26248
|
throw new Error("get-budget-versions requires <budgetId>");
|
|
24820
|
-
await getBudgetVersions(
|
|
26249
|
+
await getBudgetVersions(id2);
|
|
24821
26250
|
break;
|
|
24822
26251
|
}
|
|
24823
26252
|
case "rename-budget-version": {
|
|
@@ -24838,14 +26267,14 @@ Waiting for approval\u2026
|
|
|
24838
26267
|
break;
|
|
24839
26268
|
}
|
|
24840
26269
|
case "update-budget-status": {
|
|
24841
|
-
const [
|
|
24842
|
-
if (!
|
|
26270
|
+
const [id2, status] = positional;
|
|
26271
|
+
if (!id2 || !status) {
|
|
24843
26272
|
throw new Error("update-budget-status requires <id> <status>");
|
|
24844
26273
|
}
|
|
24845
26274
|
if (!isBudgetStatusUpdate(status)) {
|
|
24846
26275
|
throw new Error(`Invalid status. Use one of: ${budgetStatusesForHelp.join(", ")}`);
|
|
24847
26276
|
}
|
|
24848
|
-
await updateBudgetStatus(
|
|
26277
|
+
await updateBudgetStatus(id2, status, {
|
|
24849
26278
|
projectManagerId: getFlag(flags, "projectManagerId") !== undefined ? String(getFlag(flags, "projectManagerId")) : undefined,
|
|
24850
26279
|
markProjectWon: parseOptionalBoolFlag(flags, "markProjectWon"),
|
|
24851
26280
|
projectStatusOnCommercialRejection: parseCommercialRejectionProjectStatus(flags),
|
|
@@ -24854,13 +26283,13 @@ Waiting for approval\u2026
|
|
|
24854
26283
|
break;
|
|
24855
26284
|
}
|
|
24856
26285
|
case "create-budget-approval": {
|
|
24857
|
-
const
|
|
24858
|
-
if (!
|
|
26286
|
+
const id2 = positional[0];
|
|
26287
|
+
if (!id2)
|
|
24859
26288
|
throw new Error("create-budget-approval requires <budgetId>");
|
|
24860
26289
|
const companyPayloadRaw = getFlag(flags, "companyPayload");
|
|
24861
26290
|
const contactPayloadRaw = getFlag(flags, "contactPayload");
|
|
24862
26291
|
const eventPayloadRaw = getFlag(flags, "eventPayload");
|
|
24863
|
-
await createBudgetApproval(
|
|
26292
|
+
await createBudgetApproval(id2, {
|
|
24864
26293
|
company: companyPayloadRaw !== undefined ? parseJsonFlag(String(companyPayloadRaw), "--companyPayload") : undefined,
|
|
24865
26294
|
contactPerson: contactPayloadRaw !== undefined ? parseJsonFlag(String(contactPayloadRaw), "--contactPayload") : undefined,
|
|
24866
26295
|
eventDetails: eventPayloadRaw !== undefined ? parseJsonFlag(String(eventPayloadRaw), "--eventPayload") : undefined
|
|
@@ -24868,13 +26297,13 @@ Waiting for approval\u2026
|
|
|
24868
26297
|
break;
|
|
24869
26298
|
}
|
|
24870
26299
|
case "create-estimate": {
|
|
24871
|
-
const
|
|
24872
|
-
if (!
|
|
26300
|
+
const id2 = positional[0];
|
|
26301
|
+
if (!id2)
|
|
24873
26302
|
throw new Error("create-estimate requires <budgetId>");
|
|
24874
26303
|
const companyPayloadRaw = getFlag(flags, "companyPayload");
|
|
24875
26304
|
const contactPayloadRaw = getFlag(flags, "contactPayload");
|
|
24876
26305
|
const eventPayloadRaw = getFlag(flags, "eventPayload");
|
|
24877
|
-
await createEstimate(
|
|
26306
|
+
await createEstimate(id2, {
|
|
24878
26307
|
company: companyPayloadRaw !== undefined ? parseJsonFlag(String(companyPayloadRaw), "--companyPayload") : undefined,
|
|
24879
26308
|
contactPerson: contactPayloadRaw !== undefined ? parseJsonFlag(String(contactPayloadRaw), "--contactPayload") : undefined,
|
|
24880
26309
|
eventDetails: eventPayloadRaw !== undefined ? parseJsonFlag(String(eventPayloadRaw), "--eventPayload") : undefined
|
|
@@ -24910,10 +26339,10 @@ Waiting for approval\u2026
|
|
|
24910
26339
|
break;
|
|
24911
26340
|
}
|
|
24912
26341
|
case "delete-budget": {
|
|
24913
|
-
const
|
|
24914
|
-
if (!
|
|
26342
|
+
const id2 = positional[0];
|
|
26343
|
+
if (!id2)
|
|
24915
26344
|
throw new Error("delete-budget requires <budgetId>");
|
|
24916
|
-
await deleteBudgetById(
|
|
26345
|
+
await deleteBudgetById(id2);
|
|
24917
26346
|
break;
|
|
24918
26347
|
}
|
|
24919
26348
|
case "add-budget-items": {
|
|
@@ -24925,11 +26354,11 @@ Waiting for approval\u2026
|
|
|
24925
26354
|
break;
|
|
24926
26355
|
}
|
|
24927
26356
|
case "update-budget-item": {
|
|
24928
|
-
const
|
|
24929
|
-
if (!
|
|
26357
|
+
const id2 = getFlag(flags, "id");
|
|
26358
|
+
if (!id2)
|
|
24930
26359
|
throw new Error("update-budget-item requires --id");
|
|
24931
26360
|
await updateBudgetItem({
|
|
24932
|
-
id: String(
|
|
26361
|
+
id: String(id2),
|
|
24933
26362
|
description: getFlag(flags, "description"),
|
|
24934
26363
|
note: getFlag(flags, "note"),
|
|
24935
26364
|
quantity: parseOptionalNumber(getFlag(flags, "quantity"), "--quantity"),
|
|
@@ -24943,10 +26372,10 @@ Waiting for approval\u2026
|
|
|
24943
26372
|
break;
|
|
24944
26373
|
}
|
|
24945
26374
|
case "remove-budget-item": {
|
|
24946
|
-
const
|
|
24947
|
-
if (!
|
|
26375
|
+
const id2 = positional[0];
|
|
26376
|
+
if (!id2)
|
|
24948
26377
|
throw new Error("remove-budget-item requires <budgetItemId>");
|
|
24949
|
-
await removeBudgetItem(
|
|
26378
|
+
await removeBudgetItem(id2);
|
|
24950
26379
|
break;
|
|
24951
26380
|
}
|
|
24952
26381
|
case "replace-budget-item": {
|
|
@@ -25007,20 +26436,20 @@ Waiting for approval\u2026
|
|
|
25007
26436
|
break;
|
|
25008
26437
|
}
|
|
25009
26438
|
case "update-budget-category": {
|
|
25010
|
-
const
|
|
26439
|
+
const id2 = getFlag(flags, "id");
|
|
25011
26440
|
const name = getFlag(flags, "name");
|
|
25012
|
-
if (!
|
|
26441
|
+
if (!id2 || !name) {
|
|
25013
26442
|
throw new Error("update-budget-category requires --id and --name");
|
|
25014
26443
|
}
|
|
25015
|
-
await updateBudgetCategory({ id: String(
|
|
26444
|
+
await updateBudgetCategory({ id: String(id2), name: String(name) });
|
|
25016
26445
|
break;
|
|
25017
26446
|
}
|
|
25018
26447
|
case "delete-budget-category": {
|
|
25019
|
-
const
|
|
25020
|
-
if (!
|
|
26448
|
+
const id2 = positional[0];
|
|
26449
|
+
if (!id2) {
|
|
25021
26450
|
throw new Error("delete-budget-category requires <id>");
|
|
25022
26451
|
}
|
|
25023
|
-
await deleteBudgetCategory(
|
|
26452
|
+
await deleteBudgetCategory(id2);
|
|
25024
26453
|
break;
|
|
25025
26454
|
}
|
|
25026
26455
|
case "update-budget-commission": {
|
|
@@ -25093,10 +26522,10 @@ Waiting for approval\u2026
|
|
|
25093
26522
|
break;
|
|
25094
26523
|
}
|
|
25095
26524
|
case "create-bill-approval": {
|
|
25096
|
-
const
|
|
25097
|
-
if (!
|
|
26525
|
+
const id2 = positional[0];
|
|
26526
|
+
if (!id2)
|
|
25098
26527
|
throw new Error("create-bill-approval requires <billId>");
|
|
25099
|
-
await createBillApproval(
|
|
26528
|
+
await createBillApproval(id2);
|
|
25100
26529
|
break;
|
|
25101
26530
|
}
|
|
25102
26531
|
case "create-bill": {
|
|
@@ -25171,15 +26600,15 @@ Waiting for approval\u2026
|
|
|
25171
26600
|
break;
|
|
25172
26601
|
}
|
|
25173
26602
|
case "delete-bill": {
|
|
25174
|
-
const
|
|
25175
|
-
if (!
|
|
26603
|
+
const id2 = positional[0];
|
|
26604
|
+
if (!id2)
|
|
25176
26605
|
throw new Error("delete-bill requires <billId>");
|
|
25177
|
-
await deleteBillById(
|
|
26606
|
+
await deleteBillById(id2);
|
|
25178
26607
|
break;
|
|
25179
26608
|
}
|
|
25180
26609
|
case "update-bill-status": {
|
|
25181
|
-
const [
|
|
25182
|
-
if (!
|
|
26610
|
+
const [id2, statusRaw] = positional;
|
|
26611
|
+
if (!id2 || !statusRaw) {
|
|
25183
26612
|
throw new Error("update-bill-status requires <billId> <status> [--rejectionReason] [--paymentTrackingUrl] [--paymentReference] [--paymentProof <receipt.pdf>]");
|
|
25184
26613
|
}
|
|
25185
26614
|
const paymentProofFlag = flags.paymentProof ?? flags["payment-proof"] ?? flags.receipt;
|
|
@@ -25188,7 +26617,7 @@ Waiting for approval\u2026
|
|
|
25188
26617
|
}
|
|
25189
26618
|
const status = parseBillStatusForUpdate(statusRaw);
|
|
25190
26619
|
await updateBillStatus({
|
|
25191
|
-
id,
|
|
26620
|
+
id: id2,
|
|
25192
26621
|
status,
|
|
25193
26622
|
rejectionReason: getFlag(flags, "rejectionReason"),
|
|
25194
26623
|
paymentTrackingUrl: getFlag(flags, "paymentTrackingUrl"),
|
|
@@ -25199,8 +26628,8 @@ Waiting for approval\u2026
|
|
|
25199
26628
|
break;
|
|
25200
26629
|
}
|
|
25201
26630
|
case "patch-bill-payment": {
|
|
25202
|
-
const
|
|
25203
|
-
if (!
|
|
26631
|
+
const id2 = positional[0];
|
|
26632
|
+
if (!id2)
|
|
25204
26633
|
throw new Error("patch-bill-payment requires <billId>");
|
|
25205
26634
|
const paymentTrackingUrl = (() => {
|
|
25206
26635
|
const clear = parseOptionalBoolFlag(flags, "clearPaymentTrackingUrl");
|
|
@@ -25251,7 +26680,7 @@ Waiting for approval\u2026
|
|
|
25251
26680
|
return;
|
|
25252
26681
|
})();
|
|
25253
26682
|
await patchBillPayment({
|
|
25254
|
-
id,
|
|
26683
|
+
id: id2,
|
|
25255
26684
|
paymentTrackingUrl,
|
|
25256
26685
|
paymentReference,
|
|
25257
26686
|
quickbooksBillId,
|
|
@@ -25260,18 +26689,18 @@ Waiting for approval\u2026
|
|
|
25260
26689
|
break;
|
|
25261
26690
|
}
|
|
25262
26691
|
case "patch-bill-invoice-number": {
|
|
25263
|
-
const [
|
|
25264
|
-
if (!
|
|
26692
|
+
const [id2, inv] = positional;
|
|
26693
|
+
if (!id2 || !inv) {
|
|
25265
26694
|
throw new Error("patch-bill-invoice-number requires <billId> <invoiceNumber>");
|
|
25266
26695
|
}
|
|
25267
|
-
await patchBillInvoiceNumber(
|
|
26696
|
+
await patchBillInvoiceNumber(id2, inv);
|
|
25268
26697
|
break;
|
|
25269
26698
|
}
|
|
25270
26699
|
case "get-bill-attachments": {
|
|
25271
|
-
const
|
|
25272
|
-
if (!
|
|
26700
|
+
const id2 = positional[0];
|
|
26701
|
+
if (!id2)
|
|
25273
26702
|
throw new Error("get-bill-attachments requires <billId>");
|
|
25274
|
-
await getBillAttachments(
|
|
26703
|
+
await getBillAttachments(id2);
|
|
25275
26704
|
break;
|
|
25276
26705
|
}
|
|
25277
26706
|
case "stage-bill-attachment": {
|
|
@@ -25357,10 +26786,10 @@ Waiting for approval\u2026
|
|
|
25357
26786
|
break;
|
|
25358
26787
|
}
|
|
25359
26788
|
case "get-bill-details": {
|
|
25360
|
-
const
|
|
25361
|
-
if (!
|
|
26789
|
+
const id2 = positional[0];
|
|
26790
|
+
if (!id2)
|
|
25362
26791
|
throw new Error("get-bill-details requires <billId>");
|
|
25363
|
-
await getBillDetails(
|
|
26792
|
+
await getBillDetails(id2);
|
|
25364
26793
|
break;
|
|
25365
26794
|
}
|
|
25366
26795
|
case "list-quotations": {
|
|
@@ -25380,10 +26809,10 @@ Waiting for approval\u2026
|
|
|
25380
26809
|
break;
|
|
25381
26810
|
}
|
|
25382
26811
|
case "get-quotation": {
|
|
25383
|
-
const
|
|
25384
|
-
if (!
|
|
26812
|
+
const id2 = positional[0];
|
|
26813
|
+
if (!id2)
|
|
25385
26814
|
throw new Error("get-quotation requires <quotationId>");
|
|
25386
|
-
await getQuotationDetails(
|
|
26815
|
+
await getQuotationDetails(id2);
|
|
25387
26816
|
break;
|
|
25388
26817
|
}
|
|
25389
26818
|
case "upload-quotation-attachment": {
|
|
@@ -25422,38 +26851,38 @@ Waiting for approval\u2026
|
|
|
25422
26851
|
break;
|
|
25423
26852
|
}
|
|
25424
26853
|
case "delete-quotation": {
|
|
25425
|
-
const
|
|
25426
|
-
if (!
|
|
26854
|
+
const id2 = positional[0];
|
|
26855
|
+
if (!id2)
|
|
25427
26856
|
throw new Error("delete-quotation requires <quotationId>");
|
|
25428
|
-
await deleteQuotationById(
|
|
26857
|
+
await deleteQuotationById(id2);
|
|
25429
26858
|
break;
|
|
25430
26859
|
}
|
|
25431
26860
|
case "submit-quotation": {
|
|
25432
|
-
const
|
|
25433
|
-
if (!
|
|
26861
|
+
const id2 = positional[0];
|
|
26862
|
+
if (!id2)
|
|
25434
26863
|
throw new Error("submit-quotation requires <quotationId>");
|
|
25435
|
-
await submitQuotation(
|
|
26864
|
+
await submitQuotation(id2, getFlag(flags, "overBudgetOverrideReason"));
|
|
25436
26865
|
break;
|
|
25437
26866
|
}
|
|
25438
26867
|
case "approve-quotation": {
|
|
25439
|
-
const
|
|
25440
|
-
if (!
|
|
26868
|
+
const id2 = positional[0];
|
|
26869
|
+
if (!id2)
|
|
25441
26870
|
throw new Error("approve-quotation requires <quotationId>");
|
|
25442
|
-
await approveQuotation(
|
|
26871
|
+
await approveQuotation(id2, getFlag(flags, "overBudgetOverrideReason"));
|
|
25443
26872
|
break;
|
|
25444
26873
|
}
|
|
25445
26874
|
case "reject-quotation": {
|
|
25446
|
-
const
|
|
26875
|
+
const id2 = positional[0];
|
|
25447
26876
|
const reason = getFlag(flags, "reason");
|
|
25448
|
-
if (!
|
|
26877
|
+
if (!id2 || reason === undefined) {
|
|
25449
26878
|
throw new Error("reject-quotation requires <quotationId> --reason <text>");
|
|
25450
26879
|
}
|
|
25451
|
-
await rejectQuotation(
|
|
26880
|
+
await rejectQuotation(id2, reason);
|
|
25452
26881
|
break;
|
|
25453
26882
|
}
|
|
25454
26883
|
case "download-quotation-pdf": {
|
|
25455
|
-
const
|
|
25456
|
-
if (!
|
|
26884
|
+
const id2 = positional[0];
|
|
26885
|
+
if (!id2) {
|
|
25457
26886
|
throw new Error("download-quotation-pdf requires <quotationId> [original|staff|final]");
|
|
25458
26887
|
}
|
|
25459
26888
|
const roleRaw = (positional[1] ?? "final").toLowerCase();
|
|
@@ -25461,7 +26890,7 @@ Waiting for approval\u2026
|
|
|
25461
26890
|
if (!fileRole) {
|
|
25462
26891
|
throw new Error("download-quotation-pdf role must be original, staff, or final");
|
|
25463
26892
|
}
|
|
25464
|
-
await downloadQuotationPdf(
|
|
26893
|
+
await downloadQuotationPdf(id2, fileRole);
|
|
25465
26894
|
break;
|
|
25466
26895
|
}
|
|
25467
26896
|
case "check-customer-invoice-readiness": {
|
|
@@ -25544,6 +26973,31 @@ Waiting for approval\u2026
|
|
|
25544
26973
|
}
|
|
25545
26974
|
break;
|
|
25546
26975
|
}
|
|
26976
|
+
case "get-customer-invoice-splits": {
|
|
26977
|
+
const budgetId = positional[0];
|
|
26978
|
+
if (!budgetId)
|
|
26979
|
+
throw new Error("customer-invoice splits requires <budgetId>");
|
|
26980
|
+
await getCustomerInvoiceSplits(budgetId);
|
|
26981
|
+
break;
|
|
26982
|
+
}
|
|
26983
|
+
case "preview-customer-invoice-splits":
|
|
26984
|
+
case "save-customer-invoice-splits":
|
|
26985
|
+
case "create-customer-invoice-splits":
|
|
26986
|
+
case "send-customer-invoice-group": {
|
|
26987
|
+
const payloadRaw = getFlag(flags, "payload");
|
|
26988
|
+
if (!payloadRaw)
|
|
26989
|
+
throw new Error(`${command} requires --payload '<json>'`);
|
|
26990
|
+
const raw = parseJsonFlag(payloadRaw, "--payload");
|
|
26991
|
+
if (command === "preview-customer-invoice-splits")
|
|
26992
|
+
await previewCustomerInvoiceSplits(raw);
|
|
26993
|
+
else if (command === "save-customer-invoice-splits")
|
|
26994
|
+
await saveCustomerInvoiceSplits(raw);
|
|
26995
|
+
else if (command === "create-customer-invoice-splits")
|
|
26996
|
+
await createCustomerInvoiceSplits(raw);
|
|
26997
|
+
else
|
|
26998
|
+
await sendCustomerInvoiceGroup(raw, parseOptionalBoolFlag(flags, "all-approved") ?? false);
|
|
26999
|
+
break;
|
|
27000
|
+
}
|
|
25547
27001
|
case "create-customer-invoice": {
|
|
25548
27002
|
const payloadRaw = getFlag(flags, "payload");
|
|
25549
27003
|
if (!payloadRaw) {
|
|
@@ -25626,58 +27080,58 @@ Waiting for approval\u2026
|
|
|
25626
27080
|
break;
|
|
25627
27081
|
}
|
|
25628
27082
|
case "approve-bill": {
|
|
25629
|
-
const
|
|
25630
|
-
if (!
|
|
27083
|
+
const id2 = positional[0];
|
|
27084
|
+
if (!id2)
|
|
25631
27085
|
throw new Error("approve-bill requires <billId>");
|
|
25632
|
-
await approveBill(
|
|
27086
|
+
await approveBill(id2, getFlag(flags, "overBudgetOverrideReason"));
|
|
25633
27087
|
break;
|
|
25634
27088
|
}
|
|
25635
27089
|
case "reject-bill": {
|
|
25636
|
-
const
|
|
27090
|
+
const id2 = positional[0];
|
|
25637
27091
|
const reason = getFlag(flags, "reason");
|
|
25638
|
-
if (!
|
|
27092
|
+
if (!id2 || reason === undefined) {
|
|
25639
27093
|
throw new Error("reject-bill requires <billId> --reason <text>");
|
|
25640
27094
|
}
|
|
25641
|
-
await rejectBill(
|
|
27095
|
+
await rejectBill(id2, reason);
|
|
25642
27096
|
break;
|
|
25643
27097
|
}
|
|
25644
27098
|
case "approve-budget": {
|
|
25645
|
-
const
|
|
25646
|
-
if (!
|
|
27099
|
+
const id2 = positional[0];
|
|
27100
|
+
if (!id2)
|
|
25647
27101
|
throw new Error("approve-budget requires <budgetId>");
|
|
25648
|
-
await approveBudget(
|
|
27102
|
+
await approveBudget(id2);
|
|
25649
27103
|
break;
|
|
25650
27104
|
}
|
|
25651
27105
|
case "reject-budget": {
|
|
25652
|
-
const
|
|
27106
|
+
const id2 = positional[0];
|
|
25653
27107
|
const reason = getFlag(flags, "reason");
|
|
25654
|
-
if (!
|
|
27108
|
+
if (!id2 || reason === undefined) {
|
|
25655
27109
|
throw new Error("reject-budget requires <budgetId> --reason <text>");
|
|
25656
27110
|
}
|
|
25657
|
-
await rejectBudget(
|
|
27111
|
+
await rejectBudget(id2, reason);
|
|
25658
27112
|
break;
|
|
25659
27113
|
}
|
|
25660
27114
|
case "approve-supplier": {
|
|
25661
|
-
const
|
|
25662
|
-
if (!
|
|
27115
|
+
const id2 = positional[0];
|
|
27116
|
+
if (!id2)
|
|
25663
27117
|
throw new Error("approve-supplier requires <supplierId>");
|
|
25664
|
-
await approveSupplier(
|
|
27118
|
+
await approveSupplier(id2);
|
|
25665
27119
|
break;
|
|
25666
27120
|
}
|
|
25667
27121
|
case "reject-supplier": {
|
|
25668
|
-
const
|
|
27122
|
+
const id2 = positional[0];
|
|
25669
27123
|
const reason = getFlag(flags, "reason");
|
|
25670
|
-
if (!
|
|
27124
|
+
if (!id2 || reason === undefined) {
|
|
25671
27125
|
throw new Error("reject-supplier requires <supplierId> --reason <text>");
|
|
25672
27126
|
}
|
|
25673
|
-
await rejectSupplier(
|
|
27127
|
+
await rejectSupplier(id2, reason);
|
|
25674
27128
|
break;
|
|
25675
27129
|
}
|
|
25676
27130
|
case "get-supplier-details": {
|
|
25677
|
-
const
|
|
25678
|
-
if (!
|
|
27131
|
+
const id2 = positional[0];
|
|
27132
|
+
if (!id2)
|
|
25679
27133
|
throw new Error("get-supplier-details requires <supplierId>");
|
|
25680
|
-
await getSupplierDetails(
|
|
27134
|
+
await getSupplierDetails(id2);
|
|
25681
27135
|
break;
|
|
25682
27136
|
}
|
|
25683
27137
|
case "list-companies": {
|
|
@@ -25691,10 +27145,10 @@ Waiting for approval\u2026
|
|
|
25691
27145
|
break;
|
|
25692
27146
|
}
|
|
25693
27147
|
case "get-company": {
|
|
25694
|
-
const
|
|
25695
|
-
if (!
|
|
27148
|
+
const id2 = positional[0];
|
|
27149
|
+
if (!id2)
|
|
25696
27150
|
throw new Error("get-company requires <id>");
|
|
25697
|
-
await getCompany(
|
|
27151
|
+
await getCompany(id2);
|
|
25698
27152
|
break;
|
|
25699
27153
|
}
|
|
25700
27154
|
case "create-company": {
|
|
@@ -25748,10 +27202,10 @@ Waiting for approval\u2026
|
|
|
25748
27202
|
break;
|
|
25749
27203
|
}
|
|
25750
27204
|
case "delete-company": {
|
|
25751
|
-
const
|
|
25752
|
-
if (!
|
|
27205
|
+
const id2 = positional[0];
|
|
27206
|
+
if (!id2)
|
|
25753
27207
|
throw new Error("delete-company requires <id>");
|
|
25754
|
-
await deleteCompanyById(
|
|
27208
|
+
await deleteCompanyById(id2);
|
|
25755
27209
|
break;
|
|
25756
27210
|
}
|
|
25757
27211
|
case "list-projects": {
|
|
@@ -25778,10 +27232,10 @@ Waiting for approval\u2026
|
|
|
25778
27232
|
break;
|
|
25779
27233
|
}
|
|
25780
27234
|
case "get-project": {
|
|
25781
|
-
const
|
|
25782
|
-
if (!
|
|
27235
|
+
const id2 = positional[0];
|
|
27236
|
+
if (!id2)
|
|
25783
27237
|
throw new Error("get-project requires <id>");
|
|
25784
|
-
await getProject(
|
|
27238
|
+
await getProject(id2);
|
|
25785
27239
|
break;
|
|
25786
27240
|
}
|
|
25787
27241
|
case "get-project-hub-status": {
|
|
@@ -25859,32 +27313,32 @@ Waiting for approval\u2026
|
|
|
25859
27313
|
break;
|
|
25860
27314
|
}
|
|
25861
27315
|
case "delete-project": {
|
|
25862
|
-
const
|
|
25863
|
-
if (!
|
|
27316
|
+
const id2 = positional[0];
|
|
27317
|
+
if (!id2)
|
|
25864
27318
|
throw new Error("delete-project requires <id>");
|
|
25865
|
-
await deleteProjectById(
|
|
27319
|
+
await deleteProjectById(id2);
|
|
25866
27320
|
break;
|
|
25867
27321
|
}
|
|
25868
27322
|
case "check-project-reconciliation": {
|
|
25869
|
-
const
|
|
25870
|
-
if (!
|
|
27323
|
+
const id2 = positional[0];
|
|
27324
|
+
if (!id2) {
|
|
25871
27325
|
throw new Error("check-project-reconciliation requires <id>");
|
|
25872
27326
|
}
|
|
25873
|
-
await checkProjectReconciliation(
|
|
27327
|
+
await checkProjectReconciliation(id2);
|
|
25874
27328
|
break;
|
|
25875
27329
|
}
|
|
25876
27330
|
case "reconcile-project": {
|
|
25877
|
-
const
|
|
25878
|
-
if (!
|
|
27331
|
+
const id2 = positional[0];
|
|
27332
|
+
if (!id2)
|
|
25879
27333
|
throw new Error("reconcile-project requires <id>");
|
|
25880
|
-
await reconcileProject(
|
|
27334
|
+
await reconcileProject(id2);
|
|
25881
27335
|
break;
|
|
25882
27336
|
}
|
|
25883
27337
|
case "complete-project": {
|
|
25884
|
-
const
|
|
25885
|
-
if (!
|
|
27338
|
+
const id2 = positional[0];
|
|
27339
|
+
if (!id2)
|
|
25886
27340
|
throw new Error("complete-project requires <id>");
|
|
25887
|
-
await completeProject(
|
|
27341
|
+
await completeProject(id2);
|
|
25888
27342
|
break;
|
|
25889
27343
|
}
|
|
25890
27344
|
case "import-qbo-project": {
|
|
@@ -25897,14 +27351,14 @@ Waiting for approval\u2026
|
|
|
25897
27351
|
break;
|
|
25898
27352
|
}
|
|
25899
27353
|
case "update-project-status": {
|
|
25900
|
-
const [
|
|
25901
|
-
if (!
|
|
27354
|
+
const [id2, statusRaw] = positional;
|
|
27355
|
+
if (!id2 || !statusRaw) {
|
|
25902
27356
|
throw new Error("update-project-status requires <id> <status>");
|
|
25903
27357
|
}
|
|
25904
27358
|
const pmIdFlag = getFlag(flags, "projectManagerId");
|
|
25905
27359
|
const pmEmailFlag = getFlag(flags, "projectManagerEmail");
|
|
25906
27360
|
const wonOverrideReasonFlag = getFlag(flags, "wonOverrideReason");
|
|
25907
|
-
await updateProjectStatus(
|
|
27361
|
+
await updateProjectStatus(id2, parseProjectStatusForUpdate(statusRaw), {
|
|
25908
27362
|
...pmIdFlag && { projectManagerId: String(pmIdFlag) },
|
|
25909
27363
|
...pmEmailFlag && {
|
|
25910
27364
|
projectManagerEmail: String(pmEmailFlag)
|
|
@@ -25916,10 +27370,10 @@ Waiting for approval\u2026
|
|
|
25916
27370
|
break;
|
|
25917
27371
|
}
|
|
25918
27372
|
case "list-contacts": {
|
|
25919
|
-
const
|
|
25920
|
-
if (!
|
|
27373
|
+
const id2 = positional[0];
|
|
27374
|
+
if (!id2)
|
|
25921
27375
|
throw new Error("list-contacts requires <companyId>");
|
|
25922
|
-
await listContacts(
|
|
27376
|
+
await listContacts(id2);
|
|
25923
27377
|
break;
|
|
25924
27378
|
}
|
|
25925
27379
|
case "create-contact-person": {
|
|
@@ -26034,10 +27488,10 @@ Waiting for approval\u2026
|
|
|
26034
27488
|
break;
|
|
26035
27489
|
}
|
|
26036
27490
|
case "get-item": {
|
|
26037
|
-
const
|
|
26038
|
-
if (!
|
|
27491
|
+
const id2 = positional[0];
|
|
27492
|
+
if (!id2)
|
|
26039
27493
|
throw new Error("get-item requires <id>");
|
|
26040
|
-
await getItem(
|
|
27494
|
+
await getItem(id2);
|
|
26041
27495
|
break;
|
|
26042
27496
|
}
|
|
26043
27497
|
case "create-item": {
|
|
@@ -26059,10 +27513,10 @@ Waiting for approval\u2026
|
|
|
26059
27513
|
break;
|
|
26060
27514
|
}
|
|
26061
27515
|
case "delete-item": {
|
|
26062
|
-
const
|
|
26063
|
-
if (!
|
|
27516
|
+
const id2 = positional[0];
|
|
27517
|
+
if (!id2)
|
|
26064
27518
|
throw new Error("delete-item requires <id>");
|
|
26065
|
-
await deleteItemById(
|
|
27519
|
+
await deleteItemById(id2);
|
|
26066
27520
|
break;
|
|
26067
27521
|
}
|
|
26068
27522
|
case "archive-items": {
|
|
@@ -26092,12 +27546,12 @@ Waiting for approval\u2026
|
|
|
26092
27546
|
break;
|
|
26093
27547
|
}
|
|
26094
27548
|
case "update-item-category": {
|
|
26095
|
-
const
|
|
27549
|
+
const id2 = getFlag(flags, "id");
|
|
26096
27550
|
const name = getFlag(flags, "name");
|
|
26097
|
-
if (!
|
|
27551
|
+
if (!id2 || !name) {
|
|
26098
27552
|
throw new Error("update-item-category requires --id and --name");
|
|
26099
27553
|
}
|
|
26100
|
-
await updateItemCategory({ id: String(
|
|
27554
|
+
await updateItemCategory({ id: String(id2), name: String(name) });
|
|
26101
27555
|
break;
|
|
26102
27556
|
}
|
|
26103
27557
|
case "delete-item-categories": {
|
|
@@ -26142,12 +27596,12 @@ Waiting for approval\u2026
|
|
|
26142
27596
|
break;
|
|
26143
27597
|
}
|
|
26144
27598
|
case "revoke-api-key": {
|
|
26145
|
-
const
|
|
26146
|
-
if (!
|
|
27599
|
+
const id2 = positional[0];
|
|
27600
|
+
if (!id2) {
|
|
26147
27601
|
throw new Error("revoke-api-key requires <apiKeyId> [--userId <userId>]");
|
|
26148
27602
|
}
|
|
26149
27603
|
await revokeApiKeyForUser({
|
|
26150
|
-
id,
|
|
27604
|
+
id: id2,
|
|
26151
27605
|
userId: getFlag(flags, "userId")
|
|
26152
27606
|
});
|
|
26153
27607
|
break;
|
|
@@ -26310,12 +27764,12 @@ Waiting for approval\u2026
|
|
|
26310
27764
|
break;
|
|
26311
27765
|
}
|
|
26312
27766
|
case "get-budget-category-benchmarks": {
|
|
26313
|
-
const
|
|
26314
|
-
if (!
|
|
27767
|
+
const id2 = positional[0];
|
|
27768
|
+
if (!id2) {
|
|
26315
27769
|
throw new Error("get-budget-category-benchmarks requires <categoryId> [--limit]");
|
|
26316
27770
|
}
|
|
26317
27771
|
await getBudgetCategoryBenchmarks({
|
|
26318
|
-
categoryId:
|
|
27772
|
+
categoryId: id2,
|
|
26319
27773
|
limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit")
|
|
26320
27774
|
});
|
|
26321
27775
|
break;
|
|
@@ -26330,12 +27784,12 @@ Waiting for approval\u2026
|
|
|
26330
27784
|
break;
|
|
26331
27785
|
}
|
|
26332
27786
|
case "get-supplier-pricing-history": {
|
|
26333
|
-
const
|
|
26334
|
-
if (!
|
|
27787
|
+
const id2 = positional[0];
|
|
27788
|
+
if (!id2) {
|
|
26335
27789
|
throw new Error("get-supplier-pricing-history requires <supplierId> [--itemCategoryId] [--limit]");
|
|
26336
27790
|
}
|
|
26337
27791
|
await getSupplierPricingHistory({
|
|
26338
|
-
supplierId:
|
|
27792
|
+
supplierId: id2,
|
|
26339
27793
|
itemCategoryId: getFlag(flags, "itemCategoryId"),
|
|
26340
27794
|
limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit")
|
|
26341
27795
|
});
|