@go-labs-sg/bb 2.34.0 → 2.36.0

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