@go-labs-sg/bb 2.34.0 → 2.35.0

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