@go-labs-sg/bb 2.35.0 → 2.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -19625,1786 +19625,77 @@ var api2 = new Proxy({}, {
19625
19625
  get: (_target, property, receiver) => Reflect.get(activeClient, property, receiver)
19626
19626
  });
19627
19627
 
19628
- // ../validators/src/attachment/attachment.ts
19629
- var attachmentInputSchema = exports_external.object({
19630
- id: exports_external.string(),
19631
- attachments: exports_external.array(exports_external.object({
19632
- id: exports_external.string(),
19633
- name: exports_external.string().nullable(),
19634
- key: exports_external.string(),
19635
- size: exports_external.number()
19636
- })).optional()
19637
- });
19638
- // ../db/src/generated/prisma/enums.ts
19639
- var InboundLeadCloseOutcome = {
19640
- LOST: "LOST",
19641
- DISQUALIFIED: "DISQUALIFIED"
19642
- };
19643
- var PayNowIdentifierType = {
19644
- MOBILE_NUMBER: "MOBILE_NUMBER",
19645
- UEN: "UEN"
19646
- };
19647
- var EstimationMode = {
19648
- MICRO: "MICRO",
19649
- MANPOWER_SPECIFIED: "MANPOWER_SPECIFIED"
19650
- };
19651
- var IntegrationCredentialType = {
19652
- GOOGLE_CALENDAR_OAUTH2: "GOOGLE_CALENDAR_OAUTH2",
19653
- GOOGLE_SERVICE_ACCOUNT: "GOOGLE_SERVICE_ACCOUNT",
19654
- ASANA_ACCESS_TOKEN: "ASANA_ACCESS_TOKEN",
19655
- ASANA_OAUTH2: "ASANA_OAUTH2",
19656
- SLACK_APP: "SLACK_APP",
19657
- TYPEFORM_WEBHOOK: "TYPEFORM_WEBHOOK",
19658
- RESEND_API_KEY: "RESEND_API_KEY",
19659
- OPENAI_API_KEY: "OPENAI_API_KEY",
19660
- S3_ACCESS_KEY: "S3_ACCESS_KEY",
19661
- QUICKBOOKS_OAUTH2: "QUICKBOOKS_OAUTH2"
19662
- };
19663
- var IntegrationCapability = {
19664
- GOOGLE_CALENDAR_EVENTS: "GOOGLE_CALENDAR_EVENTS",
19665
- GOOGLE_DRIVE: "GOOGLE_DRIVE",
19666
- ASANA: "ASANA",
19667
- SLACK_WEB_API: "SLACK_WEB_API",
19668
- SLACK_EVENT_VERIFICATION: "SLACK_EVENT_VERIFICATION",
19669
- TYPEFORM_WEBHOOK_VERIFICATION: "TYPEFORM_WEBHOOK_VERIFICATION",
19670
- RESEND_EMAIL: "RESEND_EMAIL",
19671
- OPENAI: "OPENAI",
19672
- S3_OBJECT_STORAGE: "S3_OBJECT_STORAGE",
19673
- QUICKBOOKS_API: "QUICKBOOKS_API"
19674
- };
19675
- var IntegrationCredentialScope = {
19676
- GLOBAL: "GLOBAL",
19677
- INBOUND_SOURCE: "INBOUND_SOURCE"
19678
- };
19679
- var ProjectStatus = {
19680
- PITCH: "PITCH",
19681
- WON: "WON",
19682
- COMPLETED: "COMPLETED",
19683
- RECONCILED: "RECONCILED",
19684
- LOST: "LOST"
19685
- };
19686
- var BudgetStatus = {
19687
- DRAFT: "DRAFT",
19688
- PENDING_APPROVAL: "PENDING_APPROVAL",
19689
- APPROVED: "APPROVED",
19690
- REJECTED: "REJECTED",
19691
- ESTIMATE_CREATED: "ESTIMATE_CREATED",
19692
- ESTIMATE_SENT: "ESTIMATE_SENT",
19693
- ESTIMATE_ACCEPTED: "ESTIMATE_ACCEPTED",
19694
- ESTIMATE_REJECTED: "ESTIMATE_REJECTED",
19695
- ESTIMATE_CLOSED: "ESTIMATE_CLOSED"
19696
- };
19697
- var ApprovalStatus = {
19698
- PENDING_APPROVAL: "PENDING_APPROVAL",
19699
- APPROVED: "APPROVED",
19700
- REJECTED: "REJECTED",
19701
- SUPERSEDED: "SUPERSEDED"
19702
- };
19703
- var ApprovalType = {
19704
- BUDGET: "BUDGET",
19705
- SUPPLIER: "SUPPLIER",
19706
- BILL: "BILL",
19707
- CUSTOMER_INVOICE: "CUSTOMER_INVOICE",
19708
- QUOTATION: "QUOTATION"
19709
- };
19710
- var Title = {
19711
- MR: "MR",
19712
- MRS: "MRS",
19713
- MS: "MS",
19714
- DR: "DR"
19715
- };
19716
- var ErrorSeverity = {
19717
- LOW: "LOW",
19718
- MEDIUM: "MEDIUM",
19719
- HIGH: "HIGH",
19720
- CRITICAL: "CRITICAL"
19721
- };
19722
- var ErrorStatus = {
19723
- UNRESOLVED: "UNRESOLVED",
19724
- INVESTIGATING: "INVESTIGATING",
19725
- RESOLVED: "RESOLVED",
19726
- IGNORED: "IGNORED"
19727
- };
19728
- var CustomerInvoiceBatchStatus = {
19729
- CREATING: "CREATING",
19730
- PENDING_APPROVAL: "PENDING_APPROVAL",
19731
- APPROVED: "APPROVED",
19732
- REJECTED_VOIDED: "REJECTED_VOIDED",
19733
- EXPIRED_VOIDED: "EXPIRED_VOIDED",
19734
- PARTIAL_QBO_FAILURE: "PARTIAL_QBO_FAILURE"
19735
- };
19736
- var BillStatus = {
19737
- DRAFT: "DRAFT",
19738
- PENDING_APPROVAL: "PENDING_APPROVAL",
19739
- CHECKED: "CHECKED",
19740
- APPROVED: "APPROVED",
19741
- PAID: "PAID",
19742
- REJECTED: "REJECTED"
19743
- };
19744
- var BillVerificationStatus = {
19745
- PENDING: "PENDING",
19746
- PASS: "PASS",
19747
- FAIL: "FAIL",
19748
- MANUAL_REVIEW: "MANUAL_REVIEW"
19749
- };
19750
-
19751
- // ../validators/src/bill/invoice-number.ts
19752
- var BILL_INVOICE_NUMBER_MAX_LENGTH = 21;
19753
- var BILL_INVOICE_NUMBER_PATTERN = /^[A-Za-z0-9]*$/;
19754
- var billInvoiceNumberFieldSchema = exports_external.string().max(BILL_INVOICE_NUMBER_MAX_LENGTH, {
19755
- error: "Invoice number must be 21 characters or fewer (QuickBooks limit)"
19756
- }).refine((val) => BILL_INVOICE_NUMBER_PATTERN.test(val), {
19757
- error: "Invoice number may only contain English letters and numbers (A-Z, a-z, 0-9)"
19758
- });
19759
- var patchBillInvoiceNumberValueSchema = billInvoiceNumberFieldSchema.min(1, {
19760
- error: "Invoice number is required"
19761
- });
19762
- var patchBillInvoiceNumberInputSchema = exports_external.object({
19763
- id: exports_external.string(),
19764
- invoiceNumber: patchBillInvoiceNumberValueSchema
19765
- });
19766
-
19767
- // ../validators/src/bill/create-bill.ts
19768
- var billAttachmentInputSchema = exports_external.object({
19769
- id: exports_external.string(),
19770
- name: exports_external.string(),
19771
- key: exports_external.string(),
19772
- size: exports_external.number().int().positive().max(50 * 1024 * 1024)
19773
- });
19774
- var billLineAmountInputSchema = exports_external.object({
19775
- budgetItemId: exports_external.string().min(1),
19776
- amount: exports_external.number().finite().nonnegative()
19777
- });
19778
- var billQuotationAllocationInputSchema = exports_external.object({
19779
- budgetItemId: exports_external.string().min(1),
19780
- quotationId: exports_external.string().min(1),
19781
- amount: exports_external.number().finite().nonnegative()
19782
- });
19783
- var createBillInputSchemaBase = exports_external.object({
19784
- budgetId: exports_external.string(),
19785
- projectId: exports_external.string(),
19786
- supplierId: exports_external.string(),
19787
- quotationId: exports_external.string().optional(),
19788
- quotationIds: exports_external.array(exports_external.string().min(1)).optional(),
19789
- quotationAllocations: exports_external.array(billQuotationAllocationInputSchema).optional(),
19790
- overBudgetOverrideReason: exports_external.string().trim().transform((reason) => reason || undefined).pipe(exports_external.string().min(10, "Enter at least 10 characters for the allowance-overrun approval reason, or leave it blank.").max(2000, "The allowance-overrun approval reason must be 2,000 characters or fewer.").optional()).optional(),
19791
- amount: exports_external.number().min(0),
19792
- budgetItemIds: exports_external.array(exports_external.string()).min(1, "At least one line item is required"),
19793
- lineAmounts: exports_external.array(billLineAmountInputSchema).optional(),
19794
- comment: exports_external.string().optional(),
19795
- isClaimable: exports_external.boolean(),
19796
- attachments: exports_external.array(billAttachmentInputSchema),
19797
- alreadyPaid: exports_external.boolean().optional(),
19798
- paymentReference: exports_external.string().trim().optional(),
19799
- paymentProofAttachments: exports_external.array(billAttachmentInputSchema).optional(),
19800
- extractedSupplierName: exports_external.string().optional(),
19801
- extractedAmount: exports_external.number().optional(),
19802
- invoiceNumber: billInvoiceNumberFieldSchema,
19803
- invoiceDate: exports_external.date().optional(),
19804
- verificationStatus: exports_external.enum(BillVerificationStatus).optional(),
19805
- verificationResults: exports_external.object({
19806
- status: exports_external.enum(BillVerificationStatus),
19807
- extractedAmount: exports_external.number().nullable(),
19808
- difference: exports_external.number().nullable(),
19809
- confidence: exports_external.number().nullable(),
19810
- failureReason: exports_external.string().nullable().optional()
19811
- }).optional()
19812
- });
19813
- var validateAlreadyPaidBillInput = (input2, ctx) => {
19814
- if (input2.quotationIds) {
19815
- if (new Set(input2.quotationIds).size !== input2.quotationIds.length) {
19816
- ctx.addIssue({
19817
- code: "custom",
19818
- message: "Select each quotation only once",
19819
- path: ["quotationIds"]
19820
- });
19821
- }
19822
- if (input2.quotationId && (input2.quotationIds.length !== 1 || input2.quotationIds[0] !== input2.quotationId)) {
19823
- ctx.addIssue({
19824
- code: "custom",
19825
- message: "Use quotationIds or a matching single quotationId",
19826
- path: ["quotationIds"]
19827
- });
19828
- }
19829
- }
19830
- if (input2.isClaimable && (input2.quotationId || input2.quotationIds?.length || input2.quotationAllocations?.length)) {
19831
- ctx.addIssue({
19832
- code: "custom",
19833
- message: "Employee claims cannot be linked to supplier quotations",
19834
- path: ["quotationIds"]
19835
- });
19836
- }
19837
- if (!input2.alreadyPaid)
19838
- return;
19839
- if (input2.isClaimable) {
19840
- ctx.addIssue({
19841
- code: "custom",
19842
- message: "Already-paid reporting is only available for supplier bills",
19843
- path: ["alreadyPaid"]
19844
- });
19845
- }
19846
- if (!input2.paymentReference?.trim()) {
19847
- ctx.addIssue({
19848
- code: "custom",
19849
- message: "Payment reference is required for an already-paid bill",
19850
- path: ["paymentReference"]
19851
- });
19852
- }
19853
- if ((input2.paymentProofAttachments?.length ?? 0) === 0) {
19854
- ctx.addIssue({
19855
- code: "custom",
19856
- message: "Payment-proof PDF is required for an already-paid bill",
19857
- path: ["paymentProofAttachments"]
19858
- });
19859
- }
19860
- for (const attachment2 of input2.paymentProofAttachments ?? []) {
19861
- if (!attachment2.name.toLowerCase().endsWith(".pdf")) {
19862
- ctx.addIssue({
19863
- code: "custom",
19864
- message: "Payment proof must be a PDF",
19865
- path: ["paymentProofAttachments"]
19866
- });
19867
- break;
19868
- }
19869
- }
19870
- };
19871
- var createBillInputSchemaWithDraftOnly = createBillInputSchemaBase.extend({
19872
- draftOnly: exports_external.boolean().optional()
19873
- });
19874
- var validateCreateBillInput = (input2, ctx) => {
19875
- validateAlreadyPaidBillInput(input2, ctx);
19876
- if (input2.draftOnly && input2.alreadyPaid) {
19877
- ctx.addIssue({
19878
- code: "custom",
19879
- message: "Draft-only creation is not available for already-paid bills",
19880
- path: ["draftOnly"]
19881
- });
19882
- }
19883
- };
19884
- var createBillInputSchema = createBillInputSchemaWithDraftOnly.superRefine(validateCreateBillInput);
19885
- var createBillDraftInputSchema = createBillInputSchemaWithDraftOnly.extend({
19886
- draftOnly: exports_external.literal(true).default(true),
19887
- alreadyPaid: exports_external.literal(false).optional()
19888
- }).superRefine(validateCreateBillInput);
19889
- // ../validators/src/bill/update-bill.ts
19890
- var updateBillInputSchemaBase = createBillInputSchemaBase.extend({
19891
- id: exports_external.string()
19892
- });
19893
- var updateBillInputSchema = updateBillInputSchemaBase.superRefine(validateAlreadyPaidBillInput);
19894
- // ../validators/src/budget/budget-commission.ts
19895
- var updateBudgetCommissionInputSchema = exports_external.object({
19896
- budgetId: exports_external.string(),
19897
- name: exports_external.string().min(1, { error: "Name is required" }),
19898
- percentage: exports_external.string({ error: "Percentage is required" }).min(1, { error: "Percentage is required" }).refine((val) => {
19899
- const numValue = Number.parseFloat(val);
19900
- return !Number.isNaN(numValue);
19901
- }, {
19902
- error: "Percentage must be a valid number"
19903
- }).refine((val) => {
19904
- const numValue = Number.parseFloat(val);
19905
- return numValue >= 0;
19906
- }, {
19907
- error: "Percentage must be 0 or greater"
19908
- }).refine((val) => {
19909
- const numValue = Number.parseFloat(val);
19910
- return numValue <= 100;
19911
- }, {
19912
- error: "Percentage cannot exceed 100"
19913
- }),
19914
- totalSellingAfterDiscount: exports_external.number()
19915
- });
19916
- // ../validators/src/budget/budget-discount.ts
19917
- var budgetDiscountInputSchema = exports_external.object({
19918
- budgetId: exports_external.string(),
19919
- name: exports_external.string().min(1, { error: "Name is required" }),
19920
- type: exports_external.enum(["percentage", "amount"], { error: "Type is required" }),
19921
- value: exports_external.string({ error: "Value is required" }).min(1, { error: "Value is required" }).refine((val) => {
19922
- const numValue = Number.parseFloat(val);
19923
- return !Number.isNaN(numValue);
19924
- }, {
19925
- error: "Value must be a valid number"
19926
- }).refine((val) => {
19927
- const numValue = Number.parseFloat(val);
19928
- return numValue >= 0;
19929
- }, {
19930
- error: "Value must be 0 or greater"
19931
- }),
19932
- totalSellingBeforeDiscount: exports_external.number()
19933
- }).refine((data) => {
19934
- if (data.type === "percentage") {
19935
- const numValue = Number.parseFloat(data.value);
19936
- return numValue <= 100;
19937
- }
19938
- return true;
19939
- }, {
19940
- error: "Percentage value cannot exceed 100",
19941
- path: ["value"]
19942
- });
19943
- // ../validators/src/budget/budget-event-details.ts
19944
- var budgetEventDetailsSchema = exports_external.object({
19945
- date: exports_external.date({ error: "Please select the actual event date" }),
19946
- startTime: exports_external.date({ error: "Please select the actual event start time" }),
19947
- endTime: exports_external.date({ error: "Please select the actual event end time" })
19948
- });
19949
- // ../validators/src/customer-invoice/project-invoice-settings.ts
19950
- var CUSTOMER_INVOICE_REVENUE_THRESHOLD = 5000;
19951
- var customerInvoiceDeliveryMethodSchema = exports_external.enum([
19952
- "EMAIL",
19953
- "PORTAL_UPLOAD"
19954
- ]);
19955
- var percentageSchema = exports_external.number().min(0).max(100).refine((value) => Math.abs(value - Math.round(value * 100) / 100) < Number.EPSILON * 100, {
19956
- error: "Percentage can have at most two decimal places"
19957
- });
19958
- var nullableTrimmedStringSchema = exports_external.string().trim().nullish().transform((value) => value || null);
19959
- var projectInvoiceSettingsFieldsObject = exports_external.object({
19960
- depositPercentage: percentageSchema,
19961
- balancePercentage: percentageSchema,
19962
- deliveryMethod: customerInvoiceDeliveryMethodSchema,
19963
- billingEmail: nullableTrimmedStringSchema,
19964
- uploadUrl: nullableTrimmedStringSchema
19965
- });
19966
- var refineDeliveryFields = (data, ctx) => {
19967
- if (data.deliveryMethod === "EMAIL") {
19968
- if (!data.billingEmail || !exports_external.email().safeParse(data.billingEmail).success) {
19969
- ctx.addIssue({
19970
- code: "custom",
19971
- message: "A valid billing email is required",
19972
- path: ["billingEmail"]
19973
- });
19974
- }
19975
- return;
19976
- }
19977
- if (!data.uploadUrl || !exports_external.url().safeParse(data.uploadUrl).success) {
19978
- ctx.addIssue({
19979
- code: "custom",
19980
- message: "A valid upload URL is required",
19981
- path: ["uploadUrl"]
19982
- });
19983
- return;
19984
- }
19985
- if (new URL(data.uploadUrl).protocol !== "https:") {
19986
- ctx.addIssue({
19987
- code: "custom",
19988
- message: "Upload URL must use HTTPS",
19989
- path: ["uploadUrl"]
19990
- });
19991
- }
19992
- };
19993
- var projectInvoiceSettingsFieldsSchema = projectInvoiceSettingsFieldsObject.superRefine(refineDeliveryFields).transform((data) => ({
19994
- ...data,
19995
- billingEmail: data.deliveryMethod === "EMAIL" ? data.billingEmail?.toLowerCase() ?? null : null,
19996
- uploadUrl: data.deliveryMethod === "PORTAL_UPLOAD" ? data.uploadUrl : null
19997
- }));
19998
- var projectInvoiceSettingsForRevenueSchema = exports_external.object({
19999
- revenueBasisAmount: exports_external.number().nonnegative(),
20000
- ...projectInvoiceSettingsFieldsObject.shape
20001
- }).superRefine((data, ctx) => {
20002
- refineDeliveryFields(data, ctx);
20003
- const total = Math.round((data.depositPercentage + data.balancePercentage) * 100);
20004
- if (total !== 1e4) {
20005
- ctx.addIssue({
20006
- code: "custom",
20007
- message: "Deposit and balance percentages must total 100%",
20008
- path: ["balancePercentage"]
20009
- });
20010
- }
20011
- if (data.revenueBasisAmount <= CUSTOMER_INVOICE_REVENUE_THRESHOLD) {
20012
- if (data.depositPercentage !== 100 || data.balancePercentage !== 0) {
20013
- ctx.addIssue({
20014
- code: "custom",
20015
- message: "Projects with revenue of S$5,000 or below require 100% upfront payment",
20016
- path: ["depositPercentage"]
20017
- });
20018
- }
20019
- return;
20020
- }
20021
- const usesUpfrontPayment = data.depositPercentage === 100 && data.balancePercentage === 0;
20022
- const usesSplitPayment = data.depositPercentage > 0 && data.balancePercentage > 0;
20023
- if (!usesUpfrontPayment && !usesSplitPayment) {
20024
- ctx.addIssue({
20025
- code: "custom",
20026
- message: "Choose 100% upfront, or set both deposit and balance above 0%",
20027
- path: ["depositPercentage"]
20028
- });
20029
- }
20030
- }).transform((data) => ({
20031
- ...data,
20032
- billingEmail: data.deliveryMethod === "EMAIL" ? data.billingEmail?.toLowerCase() ?? null : null,
20033
- uploadUrl: data.deliveryMethod === "PORTAL_UPLOAD" ? data.uploadUrl : null
20034
- }));
20035
- var getProjectInvoiceSettingsInputSchema = exports_external.object({
20036
- projectId: exports_external.string().min(1)
20037
- });
20038
- var updateProjectInvoiceSettingsInputSchema = exports_external.object({
20039
- projectId: exports_external.string().min(1),
20040
- settings: projectInvoiceSettingsFieldsSchema
20041
- });
20042
- var markCustomerInvoiceUploadedInputSchema = exports_external.object({
20043
- invoiceId: exports_external.string().min(1),
20044
- expectedUploadUrl: exports_external.url()
20045
- });
20046
-
20047
- // ../validators/src/budget/budget-status.ts
20048
- var budgetApprovalRejectionReasonSchema = exports_external.string().trim().min(2, { error: "Reason for rejection is required" });
20049
- var budgetApprovalDecisionBaseSchema = exports_external.object({
20050
- budgetApprovalId: exports_external.string()
20051
- });
20052
- var updateBudgetApprovalInputSchema = exports_external.discriminatedUnion("status", [
20053
- budgetApprovalDecisionBaseSchema.extend({
20054
- status: exports_external.literal(ApprovalStatus.APPROVED),
20055
- rejectionReason: exports_external.string().optional()
20056
- }),
20057
- budgetApprovalDecisionBaseSchema.extend({
20058
- status: exports_external.literal(ApprovalStatus.REJECTED),
20059
- rejectionReason: budgetApprovalRejectionReasonSchema
20060
- })
20061
- ]);
20062
- var budgetStatusInputSchema = exports_external.object({
20063
- id: exports_external.string(),
20064
- status: exports_external.enum(BudgetStatus).optional(),
20065
- projectManagerId: exports_external.string().optional(),
20066
- markProjectWon: exports_external.boolean().optional(),
20067
- projectStatusOnCommercialRejection: exports_external.enum([ProjectStatus.PITCH, ProjectStatus.LOST]).optional(),
20068
- projectInvoiceSettings: projectInvoiceSettingsFieldsSchema.optional()
20069
- });
20070
- // ../validators/src/budget/create-budget.ts
20071
- var createBudgetInputSchema = exports_external.object({
20072
- name: exports_external.string().min(1, { error: "Name is required" }),
20073
- budget: exports_external.string({ error: "Budget is required" }).min(1, { error: "Budget is required" }).refine((val) => {
20074
- const numValue = Number.parseFloat(val);
20075
- return !Number.isNaN(numValue);
20076
- }, {
20077
- error: "Budget must be a valid number"
20078
- }).refine((val) => {
20079
- const numValue = Number.parseFloat(val);
20080
- return numValue >= 0;
20081
- }, {
20082
- error: "Budget must be 0 or greater"
20083
- }),
20084
- paymentTerm: exports_external.string().min(1, { error: "Payment term is required" }),
20085
- sourceBudgetId: exports_external.string().optional(),
20086
- unavailableItemReviewAcknowledged: exports_external.boolean().optional().default(false),
20087
- categoryId: exports_external.string().min(1, { error: "Please select a category." }),
20088
- projectId: exports_external.string().min(1, { error: "Please select a project." }),
20089
- pipedriveDealId: exports_external.string().optional()
20090
- });
20091
- // ../validators/src/budget/import-budget.ts
20092
- var importBudgetInputSchema = exports_external.object({
20093
- name: exports_external.string().min(2, { error: "Name is required" }),
20094
- date: exports_external.date({ error: "Please select the actual event date" }),
20095
- startTime: exports_external.date({ error: "Please select the actual event start time" }),
20096
- endTime: exports_external.date({ error: "Please select the actual event end time" }),
20097
- pax: exports_external.string({ error: "Pax is required" }).min(1, { error: "Pax is required" }).refine((val) => {
20098
- const numValue = Number.parseInt(val, 10);
20099
- return !Number.isNaN(numValue) && Number.isInteger(numValue);
20100
- }, {
20101
- error: "Pax must be a valid whole number"
20102
- }).refine((val) => {
20103
- const numValue = Number.parseInt(val, 10);
20104
- return numValue >= 1;
20105
- }, {
20106
- error: "Minimum 1 guest required"
20107
- }),
20108
- venue: exports_external.string().min(1, { error: "Venue is required" }),
20109
- budget: exports_external.string({ error: "Budget is required" }).min(1, { error: "Budget is required" }).refine((val) => {
20110
- const numValue = Number.parseFloat(val);
20111
- return !Number.isNaN(numValue);
20112
- }, {
20113
- error: "Budget must be a valid number"
20114
- }).refine((val) => {
20115
- const numValue = Number.parseFloat(val);
20116
- return numValue >= 0;
20117
- }, {
20118
- error: "Budget must be 0 or greater"
20119
- }),
20120
- categoryId: exports_external.string().min(1, { error: "Please select a category." }),
20121
- items: exports_external.array(exports_external.object({
20122
- id: exports_external.string(),
20123
- categoryName: exports_external.string().min(2, { error: "Category name is required" }),
20124
- name: exports_external.string(),
20125
- description: exports_external.string().optional(),
20126
- descriptionRichText: exports_external.custom().optional().nullable(),
20127
- quantity: exports_external.number(),
20128
- cost: exports_external.number(),
20129
- markup: exports_external.number(),
20130
- unitPrice: exports_external.number(),
20131
- isFreeOfCharge: exports_external.boolean(),
20132
- supplier: exports_external.object({
20133
- name: exports_external.string(),
20134
- gstRegistered: exports_external.boolean(),
20135
- email: exports_external.string().nullable(),
20136
- contactPerson: exports_external.string().nullable(),
20137
- phone: exports_external.string().nullable(),
20138
- address: exports_external.string().nullable()
20139
- })
20140
- })),
20141
- projectId: exports_external.string().min(1, { error: "Please select a project." })
20142
- });
20143
- // ../validators/src/budget/update-budget.ts
20144
- var updateBudgetInputSchema = createBudgetInputSchema.omit({ unavailableItemReviewAcknowledged: true }).extend({
20145
- id: exports_external.string().min(1, { error: "ID is required" })
20146
- });
20147
- // ../validators/src/company/company-discovery.ts
20148
- var searchExistingCompaniesInputSchema = exports_external.object({
20149
- query: exports_external.string().trim().min(2).max(200),
20150
- page: exports_external.number().int().min(1).max(5000).default(1)
20151
- });
20152
- var useExistingCompanyInputSchema = exports_external.discriminatedUnion("source", [
20153
- exports_external.object({
20154
- source: exports_external.literal("bb"),
20155
- companyId: exports_external.string().min(1),
20156
- quickbooksId: exports_external.string().regex(/^\d+$/).optional(),
20157
- confirmDistinct: exports_external.boolean().optional()
20158
- }),
20159
- exports_external.object({
20160
- source: exports_external.literal("qbo"),
20161
- quickbooksId: exports_external.string().regex(/^\d+$/),
20162
- website: exports_external.url().optional(),
20163
- confirmDistinct: exports_external.boolean().optional()
20164
- })
20165
- ]);
20166
- // ../validators/src/quickbooks/qbo-name.ts
20167
- var QUICKBOOKS_NAME_VALIDATION_MESSAGE = "Name can only contain letters, numbers, spaces, and these QuickBooks-supported characters: , . ? @ & ! # ' ~ * _ - ; +";
20168
- var quickBooksNameSchema = exports_external.string().trim().min(1, { error: "Name is required" }).max(100, { error: "Name cannot be longer than 100 characters" }).regex(/^[A-Za-z0-9,.?@&!#'~* _;+-]+$/, {
20169
- error: QUICKBOOKS_NAME_VALIDATION_MESSAGE
20170
- });
20171
-
20172
- // ../validators/src/company/create-company.ts
20173
- var createCompanyInputSchema = exports_external.object({
20174
- name: quickBooksNameSchema.min(2, {
20175
- error: "Name must be at least 2 characters"
20176
- }),
20177
- website: exports_external.url({ error: "Please enter a valid URL" }),
20178
- confirmDistinct: exports_external.boolean().optional()
20179
- });
20180
- // ../validators/src/company/update-company.ts
20181
- var updateCompanyInputSchema = createCompanyInputSchema.extend({
20182
- id: exports_external.string().min(1, { error: "ID is required" })
20183
- });
20184
- // ../validators/src/contact-person/create-contact-person.ts
20185
- var createContactPersonInputSchema = exports_external.object({
20186
- companyId: exports_external.string().min(1, { error: "Company is required" }),
20187
- title: exports_external.enum(Title),
20188
- firstName: exports_external.string().min(1, { error: "First name is required" }),
20189
- lastName: exports_external.string().trim().transform((lastName) => lastName || null).nullable(),
20190
- email: exports_external.email({ error: "Invalid email address" }),
20191
- mobile: exports_external.string().min(8, { error: "Mobile number must be at least 8 characters" }).regex(/^\+?[0-9\s\-()]+$/, {
20192
- error: "Please enter a valid phone number format"
20193
- }),
20194
- address: exports_external.object({
20195
- id: exports_external.string().optional(),
20196
- streetAddress1: exports_external.string().optional(),
20197
- city: exports_external.string().optional(),
20198
- state: exports_external.string().optional(),
20199
- postalCode: exports_external.string().optional(),
20200
- country: exports_external.string().optional()
20201
- }).optional()
20202
- });
20203
- // ../validators/src/contact-person/update-contact-person.ts
20204
- var updateContactPersonInputSchema = createContactPersonInputSchema.extend({
20205
- id: exports_external.string().min(1, { error: "ID is required" })
20206
- });
20207
- // ../validators/src/customer-invoice/customer-invoice.ts
20208
- var customerInvoiceDetailsSchema = exports_external.object({
20209
- billingAddress: exports_external.string().trim().max(2500).refine((value) => {
20210
- const lines = value.split(/\r?\n/).filter(Boolean);
20211
- return lines.length <= 5 && lines.every((line) => line.length <= 500);
20212
- }, {
20213
- error: "Billing address supports up to 5 lines of 500 characters each"
20214
- }),
20215
- customerMemo: exports_external.string().trim().min(1, { error: "Customer memo is required" }).max(1000)
20216
- });
20217
- var customerInvoiceSplitInputSchema = exports_external.object({
20218
- label: exports_external.string().trim().min(1, { error: "Invoice label is required" }),
20219
- percentage: exports_external.number().positive().max(100),
20220
- dueDate: exports_external.coerce.date()
20221
- });
20222
- var createCustomerInvoiceBatchInputSchema = exports_external.object({
20223
- budgetId: exports_external.string().min(1),
20224
- isCustomPercentage: exports_external.boolean().default(false),
20225
- details: customerInvoiceDetailsSchema.optional(),
20226
- splits: exports_external.array(customerInvoiceSplitInputSchema).min(1, { error: "At least one invoice is required" }).max(1, { error: "Create one invoice at a time" })
20227
- });
20228
- var updateCustomerInvoiceDetailsInputSchema = customerInvoiceDetailsSchema.extend({
20229
- invoiceId: exports_external.string().min(1)
20230
- });
20231
- var customerInvoiceBatchIdInputSchema = exports_external.object({
20232
- batchId: exports_external.string().min(1)
20233
- });
20234
- var rejectCustomerInvoiceBatchInputSchema = customerInvoiceBatchIdInputSchema.extend({
20235
- rejectionReason: exports_external.string().trim().min(1, { error: "Rejection reason is required" })
20236
- });
20237
- var sendCustomerInvoiceEmailInputSchema = exports_external.object({
20238
- invoiceId: exports_external.string().min(1),
20239
- to: exports_external.email(),
20240
- cc: exports_external.array(exports_external.email()).max(50),
20241
- replyTo: exports_external.email(),
20242
- subject: exports_external.string().trim().min(1).max(200),
20243
- content: exports_external.string().trim().min(1).max(1e5),
20244
- signature: exports_external.string().trim().min(1).max(50000)
20245
- });
20246
- // ../validators/src/customer-invoice/existing-invoice.ts
20247
- var existingCustomerInvoiceInputSchema = exports_external.object({
20248
- budgetId: exports_external.string().min(1),
20249
- qboInvoiceId: exports_external.string().regex(/^\d+$/, "Enter the numeric QuickBooks invoice transaction ID")
20250
- });
20251
- // ../validators/src/customer-invoice/payment-schedule.ts
20252
- var paymentScheduleAmountSchema = exports_external.discriminatedUnion("type", [
20253
- exports_external.object({
20254
- type: exports_external.literal("PERCENTAGE"),
20255
- value: exports_external.number().positive().max(100).refine((value) => Number(value.toFixed(2)) === value, {
20256
- message: "Percentage supports at most two decimal places"
20257
- })
20258
- }),
20259
- exports_external.object({ type: exports_external.literal("FIXED"), value: exports_external.int().positive() }),
20260
- exports_external.object({ type: exports_external.literal("UNRESOLVED") })
20261
- ]);
20262
- var paymentScheduleDueSchema = exports_external.discriminatedUnion("type", [
20263
- exports_external.object({ type: exports_external.literal("CONFIRMATION") }),
20264
- exports_external.object({ type: exports_external.literal("EVENT_START"), offsetDays: exports_external.int() }),
20265
- exports_external.object({ type: exports_external.literal("EVENT_END"), offsetDays: exports_external.int() }),
20266
- exports_external.object({ type: exports_external.literal("FIXED_DATE"), date: exports_external.iso.date() })
20267
- ]);
20268
- var paymentScheduleIssueSchema = exports_external.discriminatedUnion("type", [
20269
- exports_external.object({ type: exports_external.literal("ACCEPTANCE") }),
20270
- exports_external.object({ type: exports_external.literal("DATE"), date: exports_external.iso.date() }),
20271
- exports_external.object({
20272
- type: exports_external.literal("CONDITION"),
20273
- condition: exports_external.enum([
20274
- "EVENT_COMPLETED",
20275
- "CLIENT_SIGN_OFF",
20276
- "FINAL_QUANTITIES"
20277
- ])
20278
- })
20279
- ]);
20280
- var paymentScheduleRowSchema = exports_external.object({
20281
- key: exports_external.string().trim().min(1).max(100),
20282
- label: exports_external.string().trim().min(1).max(200),
20283
- amount: paymentScheduleAmountSchema,
20284
- due: paymentScheduleDueSchema,
20285
- issue: paymentScheduleIssueSchema,
20286
- ownerId: exports_external.string().trim().min(1).max(100).optional(),
20287
- reason: exports_external.string().trim().min(1).max(1000).optional()
20288
- }).superRefine((row, context) => {
20289
- if ((row.amount.type === "UNRESOLVED" || row.issue.type !== "ACCEPTANCE") && (!row.ownerId || !row.reason)) {
20290
- context.addIssue({
20291
- code: "custom",
20292
- message: "Deferred or unresolved instalments need an owner and reason",
20293
- path: ["ownerId"]
20294
- });
20295
- }
20296
- });
20297
- var paymentScheduleSchema = exports_external.object({
20298
- rows: exports_external.array(paymentScheduleRowSchema).min(1).max(20),
20299
- reminderLeadDays: exports_external.int().min(0).max(365).default(7),
20300
- billingRestriction: exports_external.string().trim().max(1000).default(""),
20301
- exceptionReason: exports_external.string().trim().min(1).max(1000).optional()
20302
- }).superRefine((schedule, context) => {
20303
- const keys = new Set;
20304
- for (const [index, row] of schedule.rows.entries()) {
20305
- if (keys.has(row.key)) {
20306
- context.addIssue({
20307
- code: "custom",
20308
- message: "Each payment schedule row needs a unique key",
20309
- path: ["rows", index, "key"]
20310
- });
20311
- }
20312
- keys.add(row.key);
20313
- }
20314
- if (schedule.billingRestriction && !schedule.exceptionReason) {
20315
- context.addIssue({
20316
- code: "custom",
20317
- message: "An exception reason is required for a billing restriction",
20318
- path: ["exceptionReason"]
20319
- });
20320
- }
20321
- });
20322
- // ../validators/src/feature-flag/feature-flag.ts
20323
- var featureFlagNameSchema = exports_external.string().trim().min(1, { error: "Name is required" }).max(100, { error: "Name must be 100 characters or fewer" }).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u, {
20324
- error: "Use lowercase letters, numbers, and single hyphens only"
20325
- });
20326
- var createFeatureFlagInputSchema = exports_external.object({
20327
- name: featureFlagNameSchema,
20328
- enabled: exports_external.boolean()
20329
- });
20330
- var updateFeatureFlagInputSchema = exports_external.object({
20331
- id: exports_external.string().min(1, { error: "ID is required" }),
20332
- enabled: exports_external.boolean()
20333
- });
20334
- // ../validators/src/inbound/gosh-brief.ts
20335
- var goshBriefJsonValueSchema = exports_external.lazy(() => exports_external.union([
20336
- exports_external.string(),
20337
- exports_external.number().finite(),
20338
- exports_external.boolean(),
20339
- exports_external.null(),
20340
- exports_external.array(goshBriefJsonValueSchema),
20341
- exports_external.record(exports_external.string(), goshBriefJsonValueSchema)
20342
- ]));
20343
- var goshBriefSubmissionSchema = exports_external.object({
20344
- company: exports_external.string().max(500).default(""),
20345
- duration: exports_external.string().max(500).default(""),
20346
- estimate: exports_external.object({ totalLabel: exports_external.string().max(500).default("") }).catchall(goshBriefJsonValueSchema).default({ totalLabel: "" }),
20347
- eventType: exports_external.string().max(500).default(""),
20348
- facilitatorSupport: exports_external.string().max(500).default(""),
20349
- mode: exports_external.literal("enrich"),
20350
- notes: exports_external.string().max(20000).default(""),
20351
- participants: exports_external.string().max(500).default(""),
20352
- planningSupport: exports_external.string().max(500).default(""),
20353
- reporting: exports_external.string().max(500).default(""),
20354
- token: exports_external.string().trim().min(20).max(500),
20355
- userAgent: exports_external.string().max(2000).default("")
20356
- }).catchall(goshBriefJsonValueSchema);
20357
- // ../validators/src/inbound/n8n-legacy-import.ts
20358
- var legacyAnswerValueSchema = exports_external.union([
20359
- exports_external.string(),
20360
- exports_external.number(),
20361
- exports_external.boolean(),
20362
- exports_external.array(exports_external.string()),
20363
- exports_external.null()
20364
- ]);
20365
- var legacyN8nInboundRowSchema = exports_external.object({
20366
- asanaTaskId: exports_external.string().trim().min(1).max(255).nullable().optional(),
20367
- asanaTaskName: exports_external.string().trim().min(1).max(500).nullable().optional(),
20368
- asanaTaskUrl: exports_external.url().max(2000).nullable().optional(),
20369
- bdName: exports_external.string().trim().min(1).max(160).nullable().optional(),
20370
- createdAt: exports_external.iso.datetime().nullable().optional(),
20371
- flatAnswers: exports_external.record(exports_external.string(), legacyAnswerValueSchema),
20372
- googleCalendarEventId: exports_external.string().trim().min(1).max(1024).nullable().optional(),
20373
- googleCalendarEventUrl: exports_external.url().max(2000).nullable().optional(),
20374
- inboundSlackChannelId: exports_external.string().trim().min(1).max(255).nullable().optional(),
20375
- legacyRowId: exports_external.string().trim().min(1).max(255),
20376
- lostReason: exports_external.string().trim().max(2000).nullable().optional(),
20377
- messageTs: exports_external.string().trim().regex(/^\d+(?:\.\d+)?$/).max(255),
20378
- pitchSlackChannelId: exports_external.string().trim().min(1).max(255).nullable().optional(),
20379
- pitchSlackChannelName: exports_external.string().trim().min(1).max(255).nullable().optional(),
20380
- pitchSlackChannelUrl: exports_external.url().max(2000).nullable().optional(),
20381
- projectId: exports_external.string().cuid().nullable().optional(),
20382
- status: exports_external.enum(["PITCH", "WON", "LOST"]).nullable().optional(),
20383
- typeformResponseId: exports_external.string().trim().min(1).max(255).nullable().optional()
20384
- }).strict();
20385
- var legacyN8nInboundImportInputSchema = exports_external.object({
20386
- rows: exports_external.array(legacyN8nInboundRowSchema).min(1).max(1000),
20387
- sourceId: exports_external.string().cuid()
20388
- });
20389
- // ../validators/src/inbound/slack-event.ts
20390
- var slackMessageEventSchema = exports_external.object({
20391
- bot_id: exports_external.string().optional(),
20392
- channel: exports_external.string().trim().min(1).max(255),
20393
- subtype: exports_external.string().optional(),
20394
- text: exports_external.string().max(4000),
20395
- thread_ts: exports_external.string().trim().min(1).max(255).optional(),
20396
- ts: exports_external.string().trim().min(1).max(255),
20397
- type: exports_external.enum(["app_mention", "message"]),
20398
- user: exports_external.string().trim().min(1).max(255).optional()
20399
- }).passthrough();
20400
- var slackUrlVerificationPayloadSchema = exports_external.object({
20401
- challenge: exports_external.string().min(1).max(4000),
20402
- type: exports_external.literal("url_verification")
20403
- }).passthrough();
20404
- var slackEventCallbackPayloadSchema = exports_external.object({
20405
- event: slackMessageEventSchema,
20406
- event_id: exports_external.string().trim().min(1).max(255),
20407
- team_id: exports_external.string().trim().min(1).max(255),
20408
- type: exports_external.literal("event_callback")
20409
- }).passthrough();
20410
- var slackEventCommandSchema = exports_external.discriminatedUnion("type", [
20411
- exports_external.object({
20412
- assigneeName: exports_external.string().trim().min(1).max(100),
20413
- type: exports_external.literal("BD_ASSIGNMENT")
20414
- }),
20415
- exports_external.object({ type: exports_external.literal("DELETE_LEAD") }),
20416
- exports_external.object({
20417
- type: exports_external.literal("GOSH_LINK"),
20418
- variant: exports_external.string().trim().min(1).max(100)
20419
- })
20420
- ]);
20421
- // ../validators/src/inbound/typeform-admin.ts
20422
- var TYPEFORM_CANONICAL_FIELDS = [
20423
- "COMPANY_NAME",
20424
- "CONTACT_FIRST_NAME",
20425
- "CONTACT_LAST_NAME",
20426
- "CONTACT_EMAIL",
20427
- "CONTACT_PHONE",
20428
- "PROJECT_NAME",
20429
- "EVENT_TYPE",
20430
- "EVENT_START_DATE",
20431
- "EVENT_END_DATE",
20432
- "VENUE",
20433
- "EXPECTED_PAX",
20434
- "ESTIMATED_BUDGET",
20435
- "HIGHER_OPTIONS",
20436
- "LEAD_SOURCE",
20437
- "REQUIREMENTS"
20438
- ];
20439
- var typeformCanonicalFieldSchema = exports_external.enum(TYPEFORM_CANONICAL_FIELDS);
20440
- var TYPEFORM_ADMIN_MAPPABLE_FIELDS = TYPEFORM_CANONICAL_FIELDS.filter((field) => field !== "PROJECT_NAME" && field !== "HIGHER_OPTIONS");
20441
- var typeformAdminMappableFieldSchema = exports_external.enum(TYPEFORM_ADMIN_MAPPABLE_FIELDS);
20442
- var listInboundSourcesInputSchema = exports_external.object({
20443
- page: exports_external.number().int().min(1).default(1),
20444
- perPage: exports_external.number().int().min(1).max(100).default(25)
20445
- });
20446
- var inboundSourceIdInputSchema = exports_external.object({
20447
- sourceId: exports_external.string().cuid()
20448
- });
20449
- var getInboundAutomationOptionsInputSchema = inboundSourceIdInputSchema.extend({
20450
- asanaProjectId: exports_external.string().trim().min(1).max(255).optional()
20451
- });
20452
- var inboundSubmissionIdInputSchema = exports_external.object({
20453
- submissionId: exports_external.string().cuid()
20454
- });
20455
- var createInboundSourceInputSchema = exports_external.object({
20456
- externalSourceId: exports_external.string().trim().min(1).max(255),
20457
- name: exports_external.string().trim().min(1).max(160)
20458
- }).strict();
20459
- var updateInboundSourceInputSchema = inboundSourceIdInputSchema.extend({
20460
- name: exports_external.string().trim().min(1).max(160)
20461
- });
20462
- var optionalExternalIdSchema = exports_external.string().trim().min(1).max(255).optional();
20463
- var asanaOptionMapSchema = exports_external.record(exports_external.string().trim().min(1).max(160), exports_external.string().trim().min(1).max(255));
20464
- var inboundAsanaCustomFieldConfigSchema = exports_external.object({
20465
- attendeesFieldId: optionalExternalIdSchema,
20466
- 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);
21288
- };
21289
- var emitCommandResult = (data) => {
21290
- const session = requireSession();
21291
- return emitCliSuccess(session.runtime, session.invocation, toJsonValue(data));
19628
+ // src/commands/index.ts
19629
+ import { randomUUID as randomUUID2 } from "crypto";
19630
+ import { readFile as readFile2, writeFile } from "fs/promises";
19631
+ import { basename } from "path";
19632
+ import { createInterface as createInterface2 } from "readline/promises";
19633
+
19634
+ // ../db/src/generated/prisma/enums.ts
19635
+ var EstimationMode = {
19636
+ MICRO: "MICRO",
19637
+ MANPOWER_SPECIFIED: "MANPOWER_SPECIFIED"
21292
19638
  };
21293
- var emitCommandError = (error61) => {
21294
- const session = requireSession();
21295
- return emitCliError(session.runtime, session.invocation, error61);
19639
+ var ProjectStatus = {
19640
+ PITCH: "PITCH",
19641
+ WON: "WON",
19642
+ COMPLETED: "COMPLETED",
19643
+ RECONCILED: "RECONCILED",
19644
+ LOST: "LOST"
21296
19645
  };
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
- }
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"
21313
19656
  };
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
- };
19657
+ var ApprovalStatus = {
19658
+ PENDING_APPROVAL: "PENDING_APPROVAL",
19659
+ APPROVED: "APPROVED",
19660
+ REJECTED: "REJECTED",
19661
+ SUPERSEDED: "SUPERSEDED"
21335
19662
  };
21336
- var getCustomerInvoiceSplits = async (budgetId) => {
21337
- emitCommandResult(await api2.paymentSchedule.get.query({ budgetId }));
19663
+ var ApprovalType = {
19664
+ BUDGET: "BUDGET",
19665
+ SUPPLIER: "SUPPLIER",
19666
+ BILL: "BILL",
19667
+ CUSTOMER_INVOICE: "CUSTOMER_INVOICE",
19668
+ QUOTATION: "QUOTATION"
21338
19669
  };
21339
- var previewCustomerInvoiceSplits = async (raw) => {
21340
- const input2 = paymentScheduleSaveInput.strict().parse(raw);
21341
- emitCommandResult(await api2.paymentSchedule.preview.query(input2));
19670
+ var ErrorSeverity = {
19671
+ LOW: "LOW",
19672
+ MEDIUM: "MEDIUM",
19673
+ HIGH: "HIGH",
19674
+ CRITICAL: "CRITICAL"
21342
19675
  };
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));
19676
+ var ErrorStatus = {
19677
+ UNRESOLVED: "UNRESOLVED",
19678
+ INVESTIGATING: "INVESTIGATING",
19679
+ RESOLVED: "RESOLVED",
19680
+ IGNORED: "IGNORED"
21352
19681
  };
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 }));
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"
21371
19689
  };
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));
19690
+ var BillStatus = {
19691
+ DRAFT: "DRAFT",
19692
+ PENDING_APPROVAL: "PENDING_APPROVAL",
19693
+ CHECKED: "CHECKED",
19694
+ APPROVED: "APPROVED",
19695
+ PAID: "PAID",
19696
+ REJECTED: "REJECTED"
21400
19697
  };
21401
19698
 
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
-
21408
19699
  // ../utils/src/filter-enums.ts
21409
19700
  var ExtendedApprovalStatus = {
21410
19701
  ...ApprovalStatus,
@@ -21457,6 +19748,13 @@ var ExtendedErrorStatus = {
21457
19748
  ...ErrorStatus
21458
19749
  };
21459
19750
 
19751
+ // src/commands/invoice-splits.ts
19752
+ var invoiceSplitSetupNextAction = (budgetId) => ({
19753
+ prompt: "Configure project invoice settings, then supply a due date for each invoice. Create the setting-derived invoices together. Non-admin creation requests admin approval; admin creation auto-approves. Sending remains separate.",
19754
+ reviewCommand: `bb customer-invoice readiness check ${budgetId}`,
19755
+ createCommand: "bb customer-invoice create --payload '<budgetId-and-splits-with-dueDates-json>'"
19756
+ });
19757
+
21460
19758
  // ../utils/src/customer-invoice-lifecycle.ts
21461
19759
  var customerInvoiceLifecycleStatuses = [
21462
19760
  ...Object.values(CustomerInvoiceBatchStatus),
@@ -21831,10 +20129,142 @@ var normalizePlainTextRichTextField = ({
21831
20129
  if (isRichTextJson(input2[richTextField]) || input2[textField] === undefined) {
21832
20130
  return input2;
21833
20131
  }
21834
- return {
21835
- ...input2,
21836
- [richTextField]: createRichTextFromPlainText(String(input2[textField] ?? ""))
21837
- };
20132
+ return {
20133
+ ...input2,
20134
+ [richTextField]: createRichTextFromPlainText(String(input2[textField] ?? ""))
20135
+ };
20136
+ };
20137
+
20138
+ // src/runtime/error.ts
20139
+ var invocationErrorCodes = new Set([
20140
+ "USAGE",
20141
+ "VALIDATION",
20142
+ "CONFIRMATION_REQUIRED"
20143
+ ]);
20144
+ var trpcCodeMap = {
20145
+ BAD_REQUEST: "VALIDATION",
20146
+ CONFLICT: "CONFLICT",
20147
+ FORBIDDEN: "AUTHORIZATION",
20148
+ INTERNAL_SERVER_ERROR: "API",
20149
+ METHOD_NOT_SUPPORTED: "API",
20150
+ NOT_FOUND: "NOT_FOUND",
20151
+ PARSE_ERROR: "VALIDATION",
20152
+ PAYLOAD_TOO_LARGE: "VALIDATION",
20153
+ PRECONDITION_FAILED: "CONFLICT",
20154
+ TIMEOUT: "NETWORK",
20155
+ TOO_MANY_REQUESTS: "API",
20156
+ UNAUTHORIZED: "AUTHENTICATION",
20157
+ UNPROCESSABLE_CONTENT: "VALIDATION"
20158
+ };
20159
+ var cliErrorCodes = new Set([
20160
+ "USAGE",
20161
+ "VALIDATION",
20162
+ "CONFIRMATION_REQUIRED",
20163
+ "AUTHENTICATION",
20164
+ "AUTHORIZATION",
20165
+ "NOT_FOUND",
20166
+ "CONFLICT",
20167
+ "API",
20168
+ "NETWORK",
20169
+ "ABORTED",
20170
+ "INTERRUPTED",
20171
+ "INTERNAL"
20172
+ ]);
20173
+
20174
+ class CliRuntimeError extends Error {
20175
+ code;
20176
+ details;
20177
+ requestId;
20178
+ constructor(code, message, options = {}) {
20179
+ super(message);
20180
+ this.name = "CliRuntimeError";
20181
+ this.code = code;
20182
+ this.details = options.details;
20183
+ this.requestId = options.requestId;
20184
+ }
20185
+ }
20186
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
20187
+ var asNonEmptyString = (value) => typeof value === "string" && value.trim() !== "" ? value : undefined;
20188
+ var asDetails = (value) => {
20189
+ if (!isRecord(value))
20190
+ return;
20191
+ const details = {};
20192
+ for (const [key, nestedValue] of Object.entries(value)) {
20193
+ if (isJsonValue(nestedValue))
20194
+ details[key] = nestedValue;
20195
+ }
20196
+ return Object.keys(details).length > 0 ? details : undefined;
20197
+ };
20198
+ var isJsonValue = (value) => {
20199
+ if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
20200
+ return true;
20201
+ }
20202
+ if (Array.isArray(value))
20203
+ return value.every(isJsonValue);
20204
+ return isRecord(value) && Object.values(value).every(isJsonValue);
20205
+ };
20206
+ var mapErrorCode = (value) => {
20207
+ if (typeof value !== "string")
20208
+ return;
20209
+ if (cliErrorCodes.has(value))
20210
+ return value;
20211
+ if (value in trpcCodeMap)
20212
+ return trpcCodeMap[value];
20213
+ return;
20214
+ };
20215
+ var networkMessagePattern = /\b(fetch failed|network error|unable to connect|econnrefused|econnreset|enotfound|etimedout|socket hang up)\b/i;
20216
+ var isNetworkError = (error61, depth = 0) => {
20217
+ if (depth > 3 || !isRecord(error61))
20218
+ return false;
20219
+ const message = asNonEmptyString(error61.message);
20220
+ if (message !== undefined && networkMessagePattern.test(message))
20221
+ return true;
20222
+ const code = asNonEmptyString(error61.code);
20223
+ if (code !== undefined && networkMessagePattern.test(code))
20224
+ return true;
20225
+ return isNetworkError(error61.cause, depth + 1);
20226
+ };
20227
+ var normalizeCliError = (error61) => {
20228
+ if (error61 instanceof CliRuntimeError) {
20229
+ return {
20230
+ code: error61.code,
20231
+ message: error61.message,
20232
+ ...error61.details === undefined ? {} : { details: error61.details },
20233
+ ...error61.requestId === undefined ? {} : { requestId: error61.requestId }
20234
+ };
20235
+ }
20236
+ if (isNetworkError(error61)) {
20237
+ return {
20238
+ code: "NETWORK",
20239
+ message: error61 instanceof Error ? error61.message : "The Budget Builder API could not be reached."
20240
+ };
20241
+ }
20242
+ if (isRecord(error61)) {
20243
+ const data = isRecord(error61.data) ? error61.data : undefined;
20244
+ const transportCode = mapErrorCode(data?.code ?? error61.code);
20245
+ if (error61 instanceof Error && data === undefined && transportCode === undefined) {
20246
+ return { code: "INTERNAL", message: error61.message };
20247
+ }
20248
+ const code = transportCode ?? "API";
20249
+ const message = asNonEmptyString(error61.message) ?? "The Budget Builder API request failed.";
20250
+ const requestId = asNonEmptyString(data?.requestId) ?? asNonEmptyString(data?.requestID) ?? asNonEmptyString(error61.requestId);
20251
+ const details = asDetails(data?.details ?? error61.details);
20252
+ return {
20253
+ code,
20254
+ message,
20255
+ ...details === undefined ? {} : { details },
20256
+ ...requestId === undefined ? {} : { requestId }
20257
+ };
20258
+ }
20259
+ if (error61 instanceof Error) {
20260
+ return { code: "INTERNAL", message: error61.message };
20261
+ }
20262
+ return { code: "INTERNAL", message: "An unexpected CLI error occurred." };
20263
+ };
20264
+ var exitCodeForCliError = (error61) => {
20265
+ if (error61.code === "INTERRUPTED")
20266
+ return 130;
20267
+ return invocationErrorCodes.has(error61.code) ? 2 : 1;
21838
20268
  };
21839
20269
 
21840
20270
  // src/parse-mutation-payload.ts
@@ -21893,7 +20323,7 @@ var parseQuotationReferences = (input2, commandName) => {
21893
20323
  if (!Array.isArray(rawQuotationIds)) {
21894
20324
  throw new Error(`${commandName} payload.quotationIds must be a string array.`);
21895
20325
  }
21896
- quotationIds = rawQuotationIds.map((id2, index) => requiredString(id2, `${commandName} payload.quotationIds[${index}]`));
20326
+ quotationIds = rawQuotationIds.map((id, index) => requiredString(id, `${commandName} payload.quotationIds[${index}]`));
21897
20327
  if (new Set(quotationIds).size !== quotationIds.length) {
21898
20328
  throw new Error(`${commandName} payload.quotationIds must not contain duplicates.`);
21899
20329
  }
@@ -22046,9 +20476,9 @@ function parseCreateBudgetPayload(raw) {
22046
20476
  }
22047
20477
  function parseUpdateBudgetPayload(raw) {
22048
20478
  const o = requireObject(raw, "update-budget payload");
22049
- const id2 = requiredString(o.id, "update-budget payload.id");
20479
+ const id = requiredString(o.id, "update-budget payload.id");
22050
20480
  const base = parseCreateBudgetPayload(raw);
22051
- return { ...base, id: id2 };
20481
+ return { ...base, id };
22052
20482
  }
22053
20483
  function parseUpdateBudgetCommissionPayload(raw) {
22054
20484
  const o = requireObject(raw, "update-budget-commission payload");
@@ -22140,7 +20570,7 @@ function parseCreateBillPayload(raw, draftOnlyFlag, quotationExempt = false, req
22140
20570
  throw new Error("create-bill payload.amount must be a number >= 0.");
22141
20571
  }
22142
20572
  const quotationReferences = parseQuotationReferences(o, "create-bill");
22143
- const parsedBudgetItemIds = budgetItemIds.map((id2, i) => requiredString(id2, `create-bill payload.budgetItemIds[${i}]`));
20573
+ const parsedBudgetItemIds = budgetItemIds.map((id, i) => requiredString(id, `create-bill payload.budgetItemIds[${i}]`));
22144
20574
  const lineAmounts = parseBillLineAmounts(o, "create-bill");
22145
20575
  validateExplicitBillAllocations({
22146
20576
  amount,
@@ -22212,7 +20642,7 @@ function parseValidateBillSelectionPayload(raw, quotationFlags = {}) {
22212
20642
  throw new Error("Employee claims cannot include quotation source inputs.");
22213
20643
  }
22214
20644
  const lineAmounts = parseBillLineAmounts(input2, "validate-bill-selection");
22215
- const budgetItemIds = Array.isArray(input2.budgetItemIds) ? input2.budgetItemIds.map((id2, index) => requiredString(id2, `validate-bill-selection payload.budgetItemIds[${index}]`)) : [];
20645
+ const budgetItemIds = Array.isArray(input2.budgetItemIds) ? input2.budgetItemIds.map((id, index) => requiredString(id, `validate-bill-selection payload.budgetItemIds[${index}]`)) : [];
22216
20646
  const amount = input2.amount === undefined || input2.amount === null ? undefined : Number(input2.amount);
22217
20647
  if (lineAmounts || payloadReferences.quotationAllocations) {
22218
20648
  if (amount === undefined || !Number.isFinite(amount) || budgetItemIds.length === 0) {
@@ -22257,7 +20687,7 @@ function parseUpdateBillPaymentEvidencePayload(raw) {
22257
20687
  var validateQuotationAllocations = (raw, command) => {
22258
20688
  const input2 = requireObject(raw, `${command} payload`);
22259
20689
  const ids = input2.budgetItemIds;
22260
- if (!Array.isArray(ids) || ids.length === 0 || ids.some((id2) => typeof id2 !== "string" || id2.length === 0) || new Set(ids).size !== ids.length) {
20690
+ if (!Array.isArray(ids) || ids.length === 0 || ids.some((id) => typeof id !== "string" || id.length === 0) || new Set(ids).size !== ids.length) {
22261
20691
  throw new CliRuntimeError("VALIDATION", `${command} payload.budgetItemIds must contain each selected budget-item ID exactly once.`);
22262
20692
  }
22263
20693
  const isCurrency = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 99999999.99 && Math.abs(value * 100 - Math.round(value * 100)) < 0.00000001;
@@ -22292,7 +20722,7 @@ function parseUpdateQuotationPayload(raw) {
22292
20722
  validateQuotationAllocations(raw, "update-quotation");
22293
20723
  return raw;
22294
20724
  }
22295
- function parseCreateCustomerInvoicePayload(raw) {
20725
+ var parseCreateCustomerInvoicePayload = (raw) => {
22296
20726
  const input2 = requireObject(raw, "create-customer-invoice payload");
22297
20727
  if (!Array.isArray(input2.splits) || input2.splits.length === 0) {
22298
20728
  throw new Error("create-customer-invoice payload.splits must be a non-empty array.");
@@ -22308,7 +20738,7 @@ function parseCreateCustomerInvoicePayload(raw) {
22308
20738
  };
22309
20739
  })
22310
20740
  };
22311
- }
20741
+ };
22312
20742
  function parseUpdateProjectPayload(raw) {
22313
20743
  const o = requireObject(raw, "update-project payload");
22314
20744
  const dr = o.dateRange;
@@ -22400,6 +20830,206 @@ function parseCompanyUpdatePayload(raw) {
22400
20830
  return raw;
22401
20831
  }
22402
20832
 
20833
+ // src/runtime/confirmation.ts
20834
+ var effectLabels = {
20835
+ "state-change": "changes Budget Builder state",
20836
+ email: "sends email",
20837
+ "external-write": "writes to an external system",
20838
+ delete: "deletes data",
20839
+ "financial-write": "creates or changes financial records"
20840
+ };
20841
+ var isEffectAllowed = (effect, permissions) => {
20842
+ switch (effect) {
20843
+ case "state-change":
20844
+ return permissions.allowStateChange === true;
20845
+ case "email":
20846
+ return permissions.allowEmail === true;
20847
+ case "external-write":
20848
+ return permissions.allowExternalWrite === true;
20849
+ case "delete":
20850
+ return permissions.allowDelete === true;
20851
+ case "financial-write":
20852
+ return permissions.allowFinancialWrite === true;
20853
+ }
20854
+ };
20855
+ var uniqueEffects = (effects) => [...new Set(effects)];
20856
+ var getRequiredEffectPermissions = (effects, permissions) => uniqueEffects(effects).filter((effect) => !isEffectAllowed(effect, permissions));
20857
+ var formatConfirmationPreview = (request) => [
20858
+ "Budget Builder workflow preview",
20859
+ `Action: ${request.action}`,
20860
+ `Target: ${request.target?.trim() || "not specified"}`,
20861
+ `Effects: ${uniqueEffects(request.effects).map((effect) => effectLabels[effect]).join("; ") || "none"}`,
20862
+ ...request.details === undefined ? [] : [`Details: ${request.details}`]
20863
+ ].join(`
20864
+ `);
20865
+ var confirmSensitiveCommand = async (runtime2, request) => {
20866
+ const requiredPermissions = getRequiredEffectPermissions(request.effects, request.permissions);
20867
+ if (request.effects.length === 0) {
20868
+ return { confirmed: true, requiredPermissions };
20869
+ }
20870
+ const target = request.target?.trim();
20871
+ if (!runtime2.isTTY) {
20872
+ if (target === undefined) {
20873
+ throw new CliRuntimeError("CONFIRMATION_REQUIRED", "Non-interactive sensitive commands require an explicit target.", {
20874
+ details: {
20875
+ action: request.action,
20876
+ effects: requiredPermissions,
20877
+ missingFlags: requiredPermissions.map((effect) => `--allow-${effect}`),
20878
+ target: "required"
20879
+ }
20880
+ });
20881
+ }
20882
+ if (requiredPermissions.length === 0) {
20883
+ return { confirmed: true, requiredPermissions };
20884
+ }
20885
+ const missing = requiredPermissions.map((effect) => `--allow-${effect}`);
20886
+ throw new CliRuntimeError("CONFIRMATION_REQUIRED", "Non-interactive sensitive commands require an explicit target and all applicable allow flags.", {
20887
+ details: {
20888
+ action: request.action,
20889
+ effects: requiredPermissions,
20890
+ missingFlags: missing
20891
+ }
20892
+ });
20893
+ }
20894
+ if (requiredPermissions.length === 0) {
20895
+ return { confirmed: true, requiredPermissions };
20896
+ }
20897
+ const preview = formatConfirmationPreview(request);
20898
+ runtime2.stderr.write(`${preview}
20899
+ `);
20900
+ if (runtime2.prompt === undefined) {
20901
+ throw new CliRuntimeError("CONFIRMATION_REQUIRED", "This command needs an interactive confirmation prompt.");
20902
+ }
20903
+ const answer = await runtime2.prompt("Type CONFIRM to continue: ");
20904
+ if (answer !== "CONFIRM") {
20905
+ throw new CliRuntimeError("ABORTED", "Command aborted by user.");
20906
+ }
20907
+ return { confirmed: true, preview, requiredPermissions };
20908
+ };
20909
+ // src/runtime/sanitize.ts
20910
+ var sensitiveKeys = new Set([
20911
+ "apikey",
20912
+ "authorization",
20913
+ "bb_api_key",
20914
+ "password",
20915
+ "paymentproof",
20916
+ "paymentproofattachments",
20917
+ "paymentproofpath",
20918
+ "paymentreference",
20919
+ "payload",
20920
+ "proof",
20921
+ "receipt",
20922
+ "secret",
20923
+ "token"
20924
+ ]);
20925
+
20926
+ // src/runtime/output.ts
20927
+ var serialize = (value) => `${JSON.stringify(value, null, 2)}
20928
+ `;
20929
+ var createSuccessEnvelope = (data, invocation) => ({
20930
+ ok: true,
20931
+ data,
20932
+ meta: {
20933
+ command: invocation.command,
20934
+ cliVersion: invocation.cliVersion,
20935
+ apiBaseUrl: invocation.apiBaseUrl
20936
+ }
20937
+ });
20938
+ var emitCliSuccess = (runtime2, invocation, data) => {
20939
+ runtime2.stdout.write(serialize(invocation.legacy ? data : createSuccessEnvelope(data, invocation)));
20940
+ return 0;
20941
+ };
20942
+ var emitCliError = (runtime2, invocation, error61) => {
20943
+ const normalized = normalizeCliError(error61);
20944
+ if (invocation.legacy) {
20945
+ runtime2.stderr.write(serialize({ error: normalized.message }));
20946
+ } else {
20947
+ const envelope = {
20948
+ ok: false,
20949
+ error: normalized,
20950
+ meta: { command: invocation.command }
20951
+ };
20952
+ runtime2.stderr.write(serialize(envelope));
20953
+ }
20954
+ return exitCodeForCliError(normalized);
20955
+ };
20956
+ // src/runtime/process-runtime.ts
20957
+ import { createInterface } from "readline/promises";
20958
+ var createProcessRuntime = () => ({
20959
+ stdout: {
20960
+ write: (value) => {
20961
+ process.stdout.write(value);
20962
+ }
20963
+ },
20964
+ stderr: {
20965
+ write: (value) => {
20966
+ process.stderr.write(value);
20967
+ }
20968
+ },
20969
+ isTTY: Boolean(process.stdin.isTTY && process.stderr.isTTY),
20970
+ env: process.env,
20971
+ prompt: async (message) => {
20972
+ const prompt = createInterface({
20973
+ input: process.stdin,
20974
+ output: process.stderr
20975
+ });
20976
+ try {
20977
+ return await prompt.question(message);
20978
+ } finally {
20979
+ prompt.close();
20980
+ }
20981
+ },
20982
+ now: () => new Date
20983
+ });
20984
+ // src/runtime/session.ts
20985
+ var activeSession;
20986
+ var setActiveCliSession = (session) => {
20987
+ activeSession = {
20988
+ ...session,
20989
+ confirmedEffects: new Set
20990
+ };
20991
+ };
20992
+ var clearActiveCliSession = () => {
20993
+ activeSession = undefined;
20994
+ };
20995
+ var getActiveCliSession = () => activeSession;
20996
+ var requireSession = () => {
20997
+ if (activeSession === undefined) {
20998
+ throw new Error("Budget Builder CLI runtime session is not configured.");
20999
+ }
21000
+ return activeSession;
21001
+ };
21002
+ var toJsonValue = (value) => {
21003
+ const serialized = JSON.stringify(value, (_key, nestedValue) => typeof nestedValue === "bigint" ? nestedValue.toString() : nestedValue);
21004
+ if (serialized === undefined)
21005
+ return null;
21006
+ return JSON.parse(serialized);
21007
+ };
21008
+ var emitCommandResult = (data) => {
21009
+ const session = requireSession();
21010
+ return emitCliSuccess(session.runtime, session.invocation, toJsonValue(data));
21011
+ };
21012
+ var emitCommandError = (error61) => {
21013
+ const session = requireSession();
21014
+ return emitCliError(session.runtime, session.invocation, error61);
21015
+ };
21016
+ var confirmCurrentCommand = async (input2) => {
21017
+ const session = requireSession();
21018
+ const effects = input2.effects ?? session.effects;
21019
+ const unconfirmedEffects = effects.filter((effect) => !session.confirmedEffects.has(effect));
21020
+ if (unconfirmedEffects.length === 0)
21021
+ return;
21022
+ await confirmSensitiveCommand(session.runtime, {
21023
+ action: input2.action,
21024
+ target: input2.target,
21025
+ details: input2.details,
21026
+ effects: unconfirmedEffects,
21027
+ permissions: session.permissions
21028
+ });
21029
+ for (const effect of unconfirmedEffects) {
21030
+ session.confirmedEffects.add(effect);
21031
+ }
21032
+ };
22403
21033
  // src/commands/index.ts
22404
21034
  import { contentType } from "mime-types";
22405
21035
  var BUDGET = "BUDGET";
@@ -22627,7 +21257,7 @@ var combineDateAndTime = (day, time3) => {
22627
21257
  d.setHours(time3.getHours(), time3.getMinutes(), time3.getSeconds(), 0);
22628
21258
  return d;
22629
21259
  };
22630
- var formatOptionalProjectPaxInput2 = (pax) => {
21260
+ var formatOptionalProjectPaxInput = (pax) => {
22631
21261
  if (typeof pax !== "number" || !Number.isFinite(pax) || pax <= 0) {
22632
21262
  return "";
22633
21263
  }
@@ -22659,7 +21289,7 @@ var billAttachmentContentTypeForFileName = (fileName) => {
22659
21289
  return resolvedContentType;
22660
21290
  };
22661
21291
  var PAYMENT_PROOF_MAX_SIZE = 20 * 1024 * 1024;
22662
- var QUOTATION_ATTACHMENT_MAX_SIZE2 = 20 * 1024 * 1024;
21292
+ var QUOTATION_ATTACHMENT_MAX_SIZE = 20 * 1024 * 1024;
22663
21293
  var quotationAttachmentContentTypeForFileName = (fileName) => {
22664
21294
  const resolvedContentType = contentTypeHeaderForFileName(fileName);
22665
21295
  if (resolvedContentType === "application/pdf" || resolvedContentType === "image/jpeg" || resolvedContentType === "image/png") {
@@ -22726,31 +21356,31 @@ Confirm the user explicitly approved this exact action, then type "${expected}"
22726
21356
  }
22727
21357
  };
22728
21358
  var assertLockedBudgetChangeConfirmed = async ({
22729
- budget: budget2,
21359
+ budget,
22730
21360
  action
22731
21361
  }) => {
22732
- if (!LOCKED_BUDGET_STATUSES_REQUIRING_CONFIRMATION.has(budget2.status)) {
21362
+ if (!LOCKED_BUDGET_STATUSES_REQUIRING_CONFIRMATION.has(budget.status)) {
22733
21363
  return;
22734
21364
  }
22735
- const statusLabel = lockedBudgetStatusLabel(budget2.status);
21365
+ const statusLabel = lockedBudgetStatusLabel(budget.status);
22736
21366
  if (getActiveCliSession()?.invocation.legacy !== true) {
22737
21367
  await confirmCurrentCommand({
22738
21368
  action,
22739
- target: `budget ${budget2.id}`,
22740
- details: `Changes locked budget "${budget2.name}" in status ${statusLabel}.`,
21369
+ target: `budget ${budget.id}`,
21370
+ details: `Changes locked budget "${budget.name}" in status ${statusLabel}.`,
22741
21371
  effects: ["state-change"]
22742
21372
  });
22743
21373
  return;
22744
21374
  }
22745
21375
  if (!process.stdin.isTTY || !process.stderr.isTTY) {
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.`);
21376
+ throw new Error(`${action} would change locked budget "${budget.name}" (${budget.id}) with status ${statusLabel}. Run this command in an interactive terminal and type "yes" to confirm.`);
22747
21377
  }
22748
21378
  const rl = createInterface2({
22749
21379
  input: process.stdin,
22750
21380
  output: process.stderr
22751
21381
  });
22752
21382
  try {
22753
- const answer = await rl.question(`${action} will change locked budget "${budget2.name}" (${budget2.id}) with status ${statusLabel}. Type "yes" to continue: `);
21383
+ const answer = await rl.question(`${action} will change locked budget "${budget.name}" (${budget.id}) with status ${statusLabel}. Type "yes" to continue: `);
22754
21384
  if (answer.trim().toLowerCase() !== "yes") {
22755
21385
  throw new Error("Aborted.");
22756
21386
  }
@@ -22759,21 +21389,21 @@ var assertLockedBudgetChangeConfirmed = async ({
22759
21389
  }
22760
21390
  };
22761
21391
  var confirmLockedBudgetChangeByBudgetId = async (budgetId, action) => {
22762
- const budget2 = await api2.budget.getBudget.query({ id: budgetId });
21392
+ const budget = await api2.budget.getBudget.query({ id: budgetId });
22763
21393
  await assertLockedBudgetChangeConfirmed({
22764
21394
  budget: {
22765
- id: budget2.id,
22766
- name: budget2.name,
22767
- status: budget2.status
21395
+ id: budget.id,
21396
+ name: budget.name,
21397
+ status: budget.status
22768
21398
  },
22769
21399
  action
22770
21400
  });
22771
21401
  };
22772
21402
  var confirmLockedBudgetChangeByBudgetItemId = async (budgetItemId, action) => {
22773
- const { budget: budget2 } = await api2.budgetItem.getBudgetItemContext.query({
21403
+ const { budget } = await api2.budgetItem.getBudgetItemContext.query({
22774
21404
  budgetItemId
22775
21405
  });
22776
- await assertLockedBudgetChangeConfirmed({ budget: budget2, action });
21406
+ await assertLockedBudgetChangeConfirmed({ budget, action });
22777
21407
  };
22778
21408
  var updateProjectWithEstimateInvalidationConfirmation = async (input2) => {
22779
21409
  try {
@@ -22810,12 +21440,12 @@ async function listBudgets(opts) {
22810
21440
  const result = await api2.budget.getAllBudgets.query(input2);
22811
21441
  out(result);
22812
21442
  }
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 })
21443
+ async function getBudget(id) {
21444
+ const [budget, items] = await Promise.all([
21445
+ api2.budget.getBudget.query({ id }),
21446
+ api2.budget.getBudgetItems.query({ budgetId: id })
22817
21447
  ]);
22818
- out({ budget: budget2, items });
21448
+ out({ budget, items });
22819
21449
  }
22820
21450
  async function updateBudgetStatus(budgetId, status, opts) {
22821
21451
  await assertSensitiveWorkflowConfirmed({
@@ -22850,21 +21480,21 @@ var assertBudgetActionReadiness = async ({
22850
21480
  requireQuickbooksProject,
22851
21481
  actionLabel
22852
21482
  }) => {
22853
- const budget2 = await api2.budget.getBudget.query({ id: budgetId });
22854
- const hasNoItems = budget2.budgetItems.length === 0;
21483
+ const budget = await api2.budget.getBudget.query({ id: budgetId });
21484
+ const hasNoItems = budget.budgetItems.length === 0;
22855
21485
  if (hasNoItems) {
22856
21486
  throw new Error(`Cannot ${actionLabel}: there are no line items in this budget.`);
22857
21487
  }
22858
- const hasZeroCostItems = budget2.budgetItems.some((item2) => Number(item2.cost ?? 0) === 0);
21488
+ const hasZeroCostItems = budget.budgetItems.some((item) => Number(item.cost ?? 0) === 0);
22859
21489
  if (hasZeroCostItems) {
22860
21490
  throw new Error(`Cannot ${actionLabel}: all line items must have a cost greater than zero.`);
22861
21491
  }
22862
- const inactiveSuppliers = budget2.budgetItems.filter((item2) => !item2.supplier.active).map((item2) => item2.supplier.name);
21492
+ const inactiveSuppliers = budget.budgetItems.filter((item) => !item.supplier.active).map((item) => item.supplier.name);
22863
21493
  const uniqueInactiveSuppliers = [...new Set(inactiveSuppliers)];
22864
21494
  if (uniqueInactiveSuppliers.length > 0) {
22865
21495
  throw new Error(`Cannot ${actionLabel}: inactive suppliers found (${uniqueInactiveSuppliers.join(", ")}).`);
22866
21496
  }
22867
- if (requireQuickbooksProject && !budget2.project.quickbooksProjectId) {
21497
+ if (requireQuickbooksProject && !budget.project.quickbooksProjectId) {
22868
21498
  throw new Error("Cannot create estimate: project is not linked to QuickBooks yet.");
22869
21499
  }
22870
21500
  };
@@ -22878,32 +21508,32 @@ var runBudgetPreflight = async (budgetId, preflight) => {
22878
21508
  await api2.contactPerson.updateContactPerson.mutate(preflight.contactPerson);
22879
21509
  }
22880
21510
  if (preflight.eventDetails) {
22881
- const budget2 = await api2.budget.getBudget.query({ id: budgetId });
22882
- const project2 = await api2.project.getProjectById.query({
22883
- id: budget2.projectId
21511
+ const budget = await api2.budget.getBudget.query({ id: budgetId });
21512
+ const project = await api2.project.getProjectById.query({
21513
+ id: budget.projectId
22884
21514
  });
22885
21515
  const day = new Date(preflight.eventDetails.date);
22886
21516
  const start = new Date(preflight.eventDetails.startTime);
22887
21517
  const end = new Date(preflight.eventDetails.endTime);
22888
21518
  const payload = {
22889
- id: project2.id,
22890
- name: project2.name,
22891
- description: project2.description ?? undefined,
21519
+ id: project.id,
21520
+ name: project.name,
21521
+ description: project.description ?? undefined,
22892
21522
  dateRange: {
22893
21523
  from: combineDateAndTime(day, start),
22894
21524
  to: combineDateAndTime(day, end)
22895
21525
  },
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
21526
+ companyId: project.companyId,
21527
+ contactPersonId: project.contactPersonId,
21528
+ asanaTaskId: project.asanaTaskId ?? "",
21529
+ insideSalesId: project.insideSalesId ?? "",
21530
+ businessDevelopmentId: project.businessDevelopmentId ?? "",
21531
+ venue: project.venue ?? "",
21532
+ pax: formatOptionalProjectPaxInput(project.pax),
21533
+ ...project.slackChannelId && project.slackChannelUrl && project.slackChannelName ? {
21534
+ slackChannelId: project.slackChannelId,
21535
+ slackChannelUrl: project.slackChannelUrl,
21536
+ slackChannelName: project.slackChannelName
22907
21537
  } : {}
22908
21538
  };
22909
21539
  await updateProjectWithEstimateInvalidationConfirmation(payload);
@@ -22964,31 +21594,31 @@ async function listApprovals(opts) {
22964
21594
  const typeFilter = opts.type === undefined || opts.type === "ALL" ? (_n) => true : (n) => n.type === opts.type;
22965
21595
  const filtered = pending.filter(typeFilter).filter((n) => n.type !== BUDGET || !n.budget?.quickbooksEstimateId);
22966
21596
  const items = filtered.map((n) => {
22967
- let id2;
21597
+ let id;
22968
21598
  let name;
22969
21599
  if (n.type === BUDGET && n.budget) {
22970
- id2 = n.budgetId ?? n.id;
21600
+ id = n.budgetId ?? n.id;
22971
21601
  name = n.budget.name;
22972
21602
  } else if (n.type === BILL && n.bill) {
22973
- id2 = n.bill.id;
21603
+ id = n.bill.id;
22974
21604
  name = `Bill ${n.bill.invoiceNumber ?? n.bill.id.slice(0, 8)}`;
22975
21605
  } else if (n.type === SUPPLIER && n.supplier) {
22976
- id2 = n.supplier.id;
21606
+ id = n.supplier.id;
22977
21607
  name = n.supplier.name;
22978
21608
  } else if (n.type === QUOTATION && n.supplierQuotation) {
22979
- id2 = n.supplierQuotation.id;
21609
+ id = n.supplierQuotation.id;
22980
21610
  name = `Quotation ${n.supplierQuotation.supplier.name}`;
22981
21611
  } else if (n.type === CUSTOMER_INVOICE && n.customerInvoiceBatch) {
22982
- id2 = n.customerInvoiceBatch.id;
21612
+ id = n.customerInvoiceBatch.id;
22983
21613
  name = `Customer invoice ${n.customerInvoiceBatch.project.name}`;
22984
21614
  } else {
22985
- id2 = n.id;
21615
+ id = n.id;
22986
21616
  name = "Unknown";
22987
21617
  }
22988
21618
  return {
22989
21619
  approvalId: n.id,
22990
21620
  type: n.type,
22991
- id: id2,
21621
+ id,
22992
21622
  name,
22993
21623
  requestedBy: n.requester?.name ?? n.requester?.email ?? "Unknown",
22994
21624
  requestedAt: typeof n.createdAt === "string" ? n.createdAt : n.createdAt instanceof Date ? n.createdAt.toISOString() : String(n.createdAt)
@@ -23017,7 +21647,7 @@ async function createBillApproval(billId) {
23017
21647
  details: "and send approval request emails"
23018
21648
  });
23019
21649
  const result = await api2.bill.createApproval.mutate({ billId });
23020
- const approvalIds = (result.results ?? []).map((item2) => ({ id: item2.id }));
21650
+ const approvalIds = (result.results ?? []).map((item) => ({ id: item.id }));
23021
21651
  let email3;
23022
21652
  if (approvalIds.length === 0) {
23023
21653
  email3 = createApprovalEmailDeliveryFailure("No approval ids returned");
@@ -23110,8 +21740,8 @@ async function updateBillStatus(opts) {
23110
21740
  entity: `bill ${opts.id}`,
23111
21741
  details: `to ${opts.status}`
23112
21742
  });
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;
21743
+ const bill = opts.paymentProofPath ? await api2.bill.getById.query({ id: opts.id }) : null;
21744
+ const paymentProof = bill && opts.paymentProofPath ? await stageBillPaymentProof(bill.projectId, opts.paymentProofPath) : undefined;
23115
21745
  try {
23116
21746
  const result = await api2.bill.updateStatus.mutate({
23117
21747
  id: opts.id,
@@ -23124,8 +21754,8 @@ async function updateBillStatus(opts) {
23124
21754
  });
23125
21755
  out(result);
23126
21756
  } catch (error61) {
23127
- await rethrowAfterStagedPaymentProofCleanup(error61, bill2 && paymentProof ? async () => {
23128
- await deleteStagedPaymentProof(bill2.projectId, paymentProof.key);
21757
+ await rethrowAfterStagedPaymentProofCleanup(error61, bill && paymentProof ? async () => {
21758
+ await deleteStagedPaymentProof(bill.projectId, paymentProof.key);
23129
21759
  } : undefined);
23130
21760
  }
23131
21761
  }
@@ -23143,15 +21773,15 @@ async function patchBillPayment(opts) {
23143
21773
  const result = await api2.bill.patchPayment.mutate(input2);
23144
21774
  out(result);
23145
21775
  }
23146
- async function patchBillInvoiceNumber(id2, invoiceNumber) {
21776
+ async function patchBillInvoiceNumber(id, invoiceNumber) {
23147
21777
  const result = await api2.bill.patchInvoiceNumber.mutate({
23148
- id: id2,
21778
+ id,
23149
21779
  invoiceNumber
23150
21780
  });
23151
21781
  out(result);
23152
21782
  }
23153
- async function getBillAttachments(id2) {
23154
- const result = await api2.bill.getAttachments.query({ id: id2 });
21783
+ async function getBillAttachments(id) {
21784
+ const result = await api2.bill.getAttachments.query({ id });
23155
21785
  out(result);
23156
21786
  }
23157
21787
  async function stageBillAttachmentsFromPaths(projectId, filePaths) {
@@ -23267,7 +21897,7 @@ async function uploadBillDocumentsFromPaths({
23267
21897
  async function uploadQuotationAttachmentFromPath(projectId, filePath) {
23268
21898
  const buf = await readFile2(filePath);
23269
21899
  const fileName = basename(filePath);
23270
- if (buf.byteLength > QUOTATION_ATTACHMENT_MAX_SIZE2) {
21900
+ if (buf.byteLength > QUOTATION_ATTACHMENT_MAX_SIZE) {
23271
21901
  throw new Error("Quotation attachment must be 20MB or smaller.");
23272
21902
  }
23273
21903
  const attachmentContentType = quotationAttachmentContentTypeForFileName(fileName);
@@ -23380,8 +22010,8 @@ async function uploadBudgetWinProofFromPath(budgetId, filePath, opts) {
23380
22010
  async function markBudgetWonWithProof(budgetId, filePath, opts) {
23381
22011
  await uploadBudgetWinProofFromPath(budgetId, filePath, opts);
23382
22012
  }
23383
- async function getBillDetails(id2) {
23384
- const result = await api2.bill.getById.query({ id: id2 });
22013
+ async function getBillDetails(id) {
22014
+ const result = await api2.bill.getById.query({ id });
23385
22015
  out(result);
23386
22016
  }
23387
22017
  async function getBudgetItemsOnly(budgetId) {
@@ -23447,7 +22077,7 @@ async function createBudgetApproval(budgetId, preflight) {
23447
22077
  actionLabel: "request approval"
23448
22078
  });
23449
22079
  const result = await api2.budget.createBudgetApproval.mutate({ budgetId });
23450
- const approvalIds = (result.results ?? []).map((item2) => ({ id: item2.id }));
22080
+ const approvalIds = (result.results ?? []).map((item) => ({ id: item.id }));
23451
22081
  let email3;
23452
22082
  if (approvalIds.length === 0) {
23453
22083
  email3 = createApprovalEmailDeliveryFailure("No approval ids returned");
@@ -23532,8 +22162,8 @@ async function updateBudgetCategory(opts) {
23532
22162
  const result = await api2.budget.updateBudgetCategory.mutate(opts);
23533
22163
  out(result);
23534
22164
  }
23535
- async function deleteBudgetCategory(id2) {
23536
- const result = await api2.budget.deleteBudgetCategory.mutate({ id: id2 });
22165
+ async function deleteBudgetCategory(id) {
22166
+ const result = await api2.budget.deleteBudgetCategory.mutate({ id });
23537
22167
  out(result);
23538
22168
  }
23539
22169
  async function updateBudgetCommissionFromPayload(raw) {
@@ -23674,8 +22304,8 @@ async function listQuotations(opts) {
23674
22304
  const result = await api2.quotation.list.query(opts);
23675
22305
  out(result);
23676
22306
  }
23677
- async function getQuotationDetails(id2) {
23678
- const result = await api2.quotation.getById.query({ id: id2 });
22307
+ async function getQuotationDetails(id) {
22308
+ const result = await api2.quotation.getById.query({ id });
23679
22309
  out(result);
23680
22310
  }
23681
22311
  async function createQuotationFromPayload(raw) {
@@ -23688,24 +22318,24 @@ var updateQuotationFromPayload = async (raw) => {
23688
22318
  const result = await api2.quotation.updateDraftOrRejected.mutate(input2);
23689
22319
  out(result);
23690
22320
  };
23691
- var deleteQuotationById = async (id2) => {
23692
- const result = await api2.quotation.delete.mutate({ id: id2 });
22321
+ var deleteQuotationById = async (id) => {
22322
+ const result = await api2.quotation.delete.mutate({ id });
23693
22323
  out(result);
23694
22324
  };
23695
- async function submitQuotation(id2, overBudgetOverrideReason) {
22325
+ async function submitQuotation(id, overBudgetOverrideReason) {
23696
22326
  await assertSensitiveWorkflowConfirmed({
23697
22327
  action: "Submit quotation for approval",
23698
- entity: `quotation ${id2}`
22328
+ entity: `quotation ${id}`
23699
22329
  });
23700
22330
  const result = await api2.quotation.submitForApproval.mutate({
23701
- id: id2,
22331
+ id,
23702
22332
  overBudgetOverrideReason
23703
22333
  });
23704
22334
  out(result);
23705
22335
  }
23706
- async function downloadQuotationPdf(id2, fileRole) {
22336
+ async function downloadQuotationPdf(id, fileRole) {
23707
22337
  const result = await api2.quotation.getSignedDownloadUrl.query({
23708
- id: id2,
22338
+ id,
23709
22339
  fileRole
23710
22340
  });
23711
22341
  out(result);
@@ -23754,7 +22384,7 @@ var createCustomerInvoice = async (raw) => {
23754
22384
  await assertSensitiveWorkflowConfirmed({
23755
22385
  action: "Create customer invoice",
23756
22386
  entity: `budget ${input2.budgetId}`,
23757
- details: "creates one invoice in QuickBooks; admin-created invoices are approved immediately, while other invoices request approval; approved cumulative coverage of 100% closes the QuickBooks estimate"
22387
+ details: `creates ${input2.splits.length} invoice(s) in QuickBooks: ${input2.splits.map((split) => `${split.label} (${split.percentage}%), due ${new Date(split.dueDate).toISOString().slice(0, 10)}`).join("; ")}. Admin creation is auto-approved; other creators request approval. No customer email is sent.`
23758
22388
  });
23759
22389
  const result = await api2.customerInvoice.createInvoiceBatch.mutate(input2);
23760
22390
  out(result);
@@ -23867,28 +22497,28 @@ var updateBillPaymentEvidenceFromPayload = async (raw) => {
23867
22497
  const result = await api2.bill.updatePaymentEvidence.mutate(input2);
23868
22498
  out(result);
23869
22499
  };
23870
- async function deleteBillById(id2) {
23871
- const result = await api2.bill.delete.mutate({ id: id2 });
22500
+ async function deleteBillById(id) {
22501
+ const result = await api2.bill.delete.mutate({ id });
23872
22502
  out(result);
23873
22503
  }
23874
22504
  async function createSupplierFromPayload(raw) {
23875
22505
  const input2 = parseSupplierCreatePayload(raw);
23876
22506
  const created = await api2.supplier.createSupplier.mutate(input2);
23877
- const supplier2 = created.result;
23878
- if (supplier2.status !== "PENDING_APPROVAL") {
22507
+ const supplier = created.result;
22508
+ if (supplier.status !== "PENDING_APPROVAL") {
23879
22509
  out(created);
23880
22510
  return;
23881
22511
  }
23882
22512
  await assertSensitiveWorkflowConfirmed({
23883
22513
  action: "Create supplier approval request",
23884
- entity: `supplier ${supplier2.id}`,
22514
+ entity: `supplier ${supplier.id}`,
23885
22515
  details: "and send approval request emails"
23886
22516
  });
23887
22517
  const approval = await api2.supplier.createSupplierApproval.mutate({
23888
- supplierId: supplier2.id
22518
+ supplierId: supplier.id
23889
22519
  });
23890
22520
  let emailResult;
23891
- const approvalIds = (approval.results ?? []).map((item2) => ({ id: item2.id }));
22521
+ const approvalIds = (approval.results ?? []).map((item) => ({ id: item.id }));
23892
22522
  if (approvalIds.length === 0) {
23893
22523
  emailResult = createApprovalEmailDeliveryFailure("No approval ids returned");
23894
22524
  } else {
@@ -24006,7 +22636,7 @@ async function rejectBudget(budgetId, reason) {
24006
22636
  });
24007
22637
  }
24008
22638
  async function getSupplierDetails(supplierId) {
24009
- const supplier2 = await api2.supplier.getSupplierById.query({
22639
+ const supplier = await api2.supplier.getSupplierById.query({
24010
22640
  id: supplierId
24011
22641
  });
24012
22642
  const respondedStatuses = new Set([
@@ -24014,14 +22644,14 @@ async function getSupplierDetails(supplierId) {
24014
22644
  ExtendedApprovalStatus.REJECTED
24015
22645
  ]);
24016
22646
  const latestRespondedApproval = [
24017
- ...supplier2.approvals.filter((approval) => respondedStatuses.has(approval.status))
22647
+ ...supplier.approvals.filter((approval) => respondedStatuses.has(approval.status))
24018
22648
  ].sort((a, b) => new Date(b.respondedAt ?? b.updatedAt).getTime() - new Date(a.respondedAt ?? a.updatedAt).getTime() || new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())[0];
24019
- const pendingApprovals = supplier2.approvals.filter((approval) => approval.status === ExtendedApprovalStatus.PENDING_APPROVAL);
24020
- const supersededApprovals = supplier2.approvals.filter((approval) => approval.status === ExtendedApprovalStatus.SUPERSEDED);
22649
+ const pendingApprovals = supplier.approvals.filter((approval) => approval.status === ExtendedApprovalStatus.PENDING_APPROVAL);
22650
+ const supersededApprovals = supplier.approvals.filter((approval) => approval.status === ExtendedApprovalStatus.SUPERSEDED);
24021
22651
  const respondedByApproval = latestRespondedApproval ?? null;
24022
22652
  const responseUser = respondedByApproval ? respondedByApproval.responder ?? respondedByApproval.approver : null;
24023
22653
  out({
24024
- ...supplier2,
22654
+ ...supplier,
24025
22655
  supplierApprovalSummary: {
24026
22656
  pendingApprovalCount: pendingApprovals.length,
24027
22657
  pendingApprovers: pendingApprovals.map((approval) => ({
@@ -24193,9 +22823,9 @@ async function listCompanies(opts) {
24193
22823
  });
24194
22824
  out(result);
24195
22825
  }
24196
- async function getCompany(id2) {
24197
- const company2 = await api2.company.getCompanyById.query({ id: id2 });
24198
- out(company2);
22826
+ async function getCompany(id) {
22827
+ const company = await api2.company.getCompanyById.query({ id });
22828
+ out(company);
24199
22829
  }
24200
22830
  async function createCompany(opts) {
24201
22831
  const result = await api2.company.createCompany.mutate(opts);
@@ -24226,13 +22856,13 @@ var listProjectHubs = async (opts) => {
24226
22856
  });
24227
22857
  out(result);
24228
22858
  };
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 })
22859
+ async function getProject(id) {
22860
+ const [project, budgetsOverview, billsOverview] = await Promise.all([
22861
+ api2.project.getProjectDetailsById.query({ id }),
22862
+ api2.project.getProjectBudgetsOverview.query({ projectId: id }),
22863
+ api2.project.getProjectBillsOverview.query({ projectId: id })
24234
22864
  ]);
24235
- out({ project: project2, budgetsOverview, billsOverview });
22865
+ out({ project, budgetsOverview, billsOverview });
24236
22866
  }
24237
22867
  var getProjectHubStatus = async (projectId) => {
24238
22868
  const result = await api2.project.getProjectHubShareLink.query({ projectId });
@@ -24288,14 +22918,14 @@ async function createProject(opts) {
24288
22918
  });
24289
22919
  out(result);
24290
22920
  }
24291
- async function updateProjectStatus(id2, status, opts) {
22921
+ async function updateProjectStatus(id, status, opts) {
24292
22922
  await assertSensitiveWorkflowConfirmed({
24293
22923
  action: "Update project status",
24294
- entity: `project ${id2}`,
22924
+ entity: `project ${id}`,
24295
22925
  details: `to ${status}`
24296
22926
  });
24297
22927
  const result = await api2.project.updateProjectStatus.mutate({
24298
- id: id2,
22928
+ id,
24299
22929
  status,
24300
22930
  ...opts?.projectManagerId && { projectManagerId: opts.projectManagerId },
24301
22931
  ...opts?.projectManagerEmail && {
@@ -24404,9 +23034,9 @@ async function listItems(opts) {
24404
23034
  });
24405
23035
  out(result);
24406
23036
  }
24407
- async function getItem(id2) {
24408
- const item2 = await api2.item.getItem.query({ id: id2 });
24409
- out(item2);
23037
+ async function getItem(id) {
23038
+ const item = await api2.item.getItem.query({ id });
23039
+ out(item);
24410
23040
  }
24411
23041
  async function listItemCategories(opts) {
24412
23042
  const result = await api2.itemCategory.getItemCategories.query({
@@ -25097,47 +23727,6 @@ var registry2 = [
25097
23727
  "customer-invoice",
25098
23728
  "import-qbo"
25099
23729
  ]),
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
- },
25141
23730
  legacyCommand("create-customer-invoice", ["customer-invoice", "create"]),
25142
23731
  legacyCommand("discard-customer-invoice", ["customer-invoice", "discard"]),
25143
23732
  legacyCommand("delete-customer-invoice", ["customer-invoice", "delete"]),
@@ -25711,12 +24300,7 @@ Customer invoices
25711
24300
  get-customer-invoice-email-context <batchId> Contact, required CC, budget, and project context for composing an invoice email.
25712
24301
  preview-qbo-customer-invoice <budgetId> <qboInvoiceId> Preview linking an existing QBO transaction (numeric ID, not invoice number); works on accepted/closed estimates.
25713
24302
  import-qbo-customer-invoice <budgetId> <qboInvoiceId> --token <previewToken> Confirm the reviewed BB-only link; requires --allow-state-change --allow-financial-write in automation.
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.
24303
+ create-customer-invoice --payload '<json>' budgetId + setting-derived splits, each with label, percentage, and dueDate; creates invoices and requests approval (admin auto-approves). Never sends.
25720
24304
  discard-customer-invoice <batchId> Discard a reserved CREATING batch with no created QBO invoices.
25721
24305
  delete-customer-invoice <batchId> Delete QBO invoices and the local batch.
25722
24306
  void-customer-invoice <batchId> Void the invoice batch in QuickBooks.
@@ -26198,10 +24782,10 @@ Waiting for approval\u2026
26198
24782
  break;
26199
24783
  }
26200
24784
  case "get-budget": {
26201
- const id2 = positional[0];
26202
- if (!id2)
24785
+ const id = positional[0];
24786
+ if (!id)
26203
24787
  throw new Error("get-budget requires <id>");
26204
- await getBudget(id2);
24788
+ await getBudget(id);
26205
24789
  break;
26206
24790
  }
26207
24791
  case "upload-budget-attachment": {
@@ -26225,17 +24809,17 @@ Waiting for approval\u2026
26225
24809
  break;
26226
24810
  }
26227
24811
  case "get-budget-items": {
26228
- const id2 = positional[0];
26229
- if (!id2)
24812
+ const id = positional[0];
24813
+ if (!id)
26230
24814
  throw new Error("get-budget-items requires <budgetId>");
26231
- await getBudgetItemsOnly(id2);
24815
+ await getBudgetItemsOnly(id);
26232
24816
  break;
26233
24817
  }
26234
24818
  case "get-budget-details": {
26235
- const id2 = positional[0];
26236
- if (!id2)
24819
+ const id = positional[0];
24820
+ if (!id)
26237
24821
  throw new Error("get-budget-details requires <budgetId>");
26238
- await getBudgetDetails(id2);
24822
+ await getBudgetDetails(id);
26239
24823
  break;
26240
24824
  }
26241
24825
  case "get-budget-categories": {
@@ -26243,10 +24827,10 @@ Waiting for approval\u2026
26243
24827
  break;
26244
24828
  }
26245
24829
  case "get-budget-versions": {
26246
- const id2 = positional[0];
26247
- if (!id2)
24830
+ const id = positional[0];
24831
+ if (!id)
26248
24832
  throw new Error("get-budget-versions requires <budgetId>");
26249
- await getBudgetVersions(id2);
24833
+ await getBudgetVersions(id);
26250
24834
  break;
26251
24835
  }
26252
24836
  case "rename-budget-version": {
@@ -26267,14 +24851,14 @@ Waiting for approval\u2026
26267
24851
  break;
26268
24852
  }
26269
24853
  case "update-budget-status": {
26270
- const [id2, status] = positional;
26271
- if (!id2 || !status) {
24854
+ const [id, status] = positional;
24855
+ if (!id || !status) {
26272
24856
  throw new Error("update-budget-status requires <id> <status>");
26273
24857
  }
26274
24858
  if (!isBudgetStatusUpdate(status)) {
26275
24859
  throw new Error(`Invalid status. Use one of: ${budgetStatusesForHelp.join(", ")}`);
26276
24860
  }
26277
- await updateBudgetStatus(id2, status, {
24861
+ await updateBudgetStatus(id, status, {
26278
24862
  projectManagerId: getFlag(flags, "projectManagerId") !== undefined ? String(getFlag(flags, "projectManagerId")) : undefined,
26279
24863
  markProjectWon: parseOptionalBoolFlag(flags, "markProjectWon"),
26280
24864
  projectStatusOnCommercialRejection: parseCommercialRejectionProjectStatus(flags),
@@ -26283,13 +24867,13 @@ Waiting for approval\u2026
26283
24867
  break;
26284
24868
  }
26285
24869
  case "create-budget-approval": {
26286
- const id2 = positional[0];
26287
- if (!id2)
24870
+ const id = positional[0];
24871
+ if (!id)
26288
24872
  throw new Error("create-budget-approval requires <budgetId>");
26289
24873
  const companyPayloadRaw = getFlag(flags, "companyPayload");
26290
24874
  const contactPayloadRaw = getFlag(flags, "contactPayload");
26291
24875
  const eventPayloadRaw = getFlag(flags, "eventPayload");
26292
- await createBudgetApproval(id2, {
24876
+ await createBudgetApproval(id, {
26293
24877
  company: companyPayloadRaw !== undefined ? parseJsonFlag(String(companyPayloadRaw), "--companyPayload") : undefined,
26294
24878
  contactPerson: contactPayloadRaw !== undefined ? parseJsonFlag(String(contactPayloadRaw), "--contactPayload") : undefined,
26295
24879
  eventDetails: eventPayloadRaw !== undefined ? parseJsonFlag(String(eventPayloadRaw), "--eventPayload") : undefined
@@ -26297,13 +24881,13 @@ Waiting for approval\u2026
26297
24881
  break;
26298
24882
  }
26299
24883
  case "create-estimate": {
26300
- const id2 = positional[0];
26301
- if (!id2)
24884
+ const id = positional[0];
24885
+ if (!id)
26302
24886
  throw new Error("create-estimate requires <budgetId>");
26303
24887
  const companyPayloadRaw = getFlag(flags, "companyPayload");
26304
24888
  const contactPayloadRaw = getFlag(flags, "contactPayload");
26305
24889
  const eventPayloadRaw = getFlag(flags, "eventPayload");
26306
- await createEstimate(id2, {
24890
+ await createEstimate(id, {
26307
24891
  company: companyPayloadRaw !== undefined ? parseJsonFlag(String(companyPayloadRaw), "--companyPayload") : undefined,
26308
24892
  contactPerson: contactPayloadRaw !== undefined ? parseJsonFlag(String(contactPayloadRaw), "--contactPayload") : undefined,
26309
24893
  eventDetails: eventPayloadRaw !== undefined ? parseJsonFlag(String(eventPayloadRaw), "--eventPayload") : undefined
@@ -26339,10 +24923,10 @@ Waiting for approval\u2026
26339
24923
  break;
26340
24924
  }
26341
24925
  case "delete-budget": {
26342
- const id2 = positional[0];
26343
- if (!id2)
24926
+ const id = positional[0];
24927
+ if (!id)
26344
24928
  throw new Error("delete-budget requires <budgetId>");
26345
- await deleteBudgetById(id2);
24929
+ await deleteBudgetById(id);
26346
24930
  break;
26347
24931
  }
26348
24932
  case "add-budget-items": {
@@ -26354,11 +24938,11 @@ Waiting for approval\u2026
26354
24938
  break;
26355
24939
  }
26356
24940
  case "update-budget-item": {
26357
- const id2 = getFlag(flags, "id");
26358
- if (!id2)
24941
+ const id = getFlag(flags, "id");
24942
+ if (!id)
26359
24943
  throw new Error("update-budget-item requires --id");
26360
24944
  await updateBudgetItem({
26361
- id: String(id2),
24945
+ id: String(id),
26362
24946
  description: getFlag(flags, "description"),
26363
24947
  note: getFlag(flags, "note"),
26364
24948
  quantity: parseOptionalNumber(getFlag(flags, "quantity"), "--quantity"),
@@ -26372,10 +24956,10 @@ Waiting for approval\u2026
26372
24956
  break;
26373
24957
  }
26374
24958
  case "remove-budget-item": {
26375
- const id2 = positional[0];
26376
- if (!id2)
24959
+ const id = positional[0];
24960
+ if (!id)
26377
24961
  throw new Error("remove-budget-item requires <budgetItemId>");
26378
- await removeBudgetItem(id2);
24962
+ await removeBudgetItem(id);
26379
24963
  break;
26380
24964
  }
26381
24965
  case "replace-budget-item": {
@@ -26436,20 +25020,20 @@ Waiting for approval\u2026
26436
25020
  break;
26437
25021
  }
26438
25022
  case "update-budget-category": {
26439
- const id2 = getFlag(flags, "id");
25023
+ const id = getFlag(flags, "id");
26440
25024
  const name = getFlag(flags, "name");
26441
- if (!id2 || !name) {
25025
+ if (!id || !name) {
26442
25026
  throw new Error("update-budget-category requires --id and --name");
26443
25027
  }
26444
- await updateBudgetCategory({ id: String(id2), name: String(name) });
25028
+ await updateBudgetCategory({ id: String(id), name: String(name) });
26445
25029
  break;
26446
25030
  }
26447
25031
  case "delete-budget-category": {
26448
- const id2 = positional[0];
26449
- if (!id2) {
25032
+ const id = positional[0];
25033
+ if (!id) {
26450
25034
  throw new Error("delete-budget-category requires <id>");
26451
25035
  }
26452
- await deleteBudgetCategory(id2);
25036
+ await deleteBudgetCategory(id);
26453
25037
  break;
26454
25038
  }
26455
25039
  case "update-budget-commission": {
@@ -26522,10 +25106,10 @@ Waiting for approval\u2026
26522
25106
  break;
26523
25107
  }
26524
25108
  case "create-bill-approval": {
26525
- const id2 = positional[0];
26526
- if (!id2)
25109
+ const id = positional[0];
25110
+ if (!id)
26527
25111
  throw new Error("create-bill-approval requires <billId>");
26528
- await createBillApproval(id2);
25112
+ await createBillApproval(id);
26529
25113
  break;
26530
25114
  }
26531
25115
  case "create-bill": {
@@ -26600,15 +25184,15 @@ Waiting for approval\u2026
26600
25184
  break;
26601
25185
  }
26602
25186
  case "delete-bill": {
26603
- const id2 = positional[0];
26604
- if (!id2)
25187
+ const id = positional[0];
25188
+ if (!id)
26605
25189
  throw new Error("delete-bill requires <billId>");
26606
- await deleteBillById(id2);
25190
+ await deleteBillById(id);
26607
25191
  break;
26608
25192
  }
26609
25193
  case "update-bill-status": {
26610
- const [id2, statusRaw] = positional;
26611
- if (!id2 || !statusRaw) {
25194
+ const [id, statusRaw] = positional;
25195
+ if (!id || !statusRaw) {
26612
25196
  throw new Error("update-bill-status requires <billId> <status> [--rejectionReason] [--paymentTrackingUrl] [--paymentReference] [--paymentProof <receipt.pdf>]");
26613
25197
  }
26614
25198
  const paymentProofFlag = flags.paymentProof ?? flags["payment-proof"] ?? flags.receipt;
@@ -26617,7 +25201,7 @@ Waiting for approval\u2026
26617
25201
  }
26618
25202
  const status = parseBillStatusForUpdate(statusRaw);
26619
25203
  await updateBillStatus({
26620
- id: id2,
25204
+ id,
26621
25205
  status,
26622
25206
  rejectionReason: getFlag(flags, "rejectionReason"),
26623
25207
  paymentTrackingUrl: getFlag(flags, "paymentTrackingUrl"),
@@ -26628,8 +25212,8 @@ Waiting for approval\u2026
26628
25212
  break;
26629
25213
  }
26630
25214
  case "patch-bill-payment": {
26631
- const id2 = positional[0];
26632
- if (!id2)
25215
+ const id = positional[0];
25216
+ if (!id)
26633
25217
  throw new Error("patch-bill-payment requires <billId>");
26634
25218
  const paymentTrackingUrl = (() => {
26635
25219
  const clear = parseOptionalBoolFlag(flags, "clearPaymentTrackingUrl");
@@ -26680,7 +25264,7 @@ Waiting for approval\u2026
26680
25264
  return;
26681
25265
  })();
26682
25266
  await patchBillPayment({
26683
- id: id2,
25267
+ id,
26684
25268
  paymentTrackingUrl,
26685
25269
  paymentReference,
26686
25270
  quickbooksBillId,
@@ -26689,18 +25273,18 @@ Waiting for approval\u2026
26689
25273
  break;
26690
25274
  }
26691
25275
  case "patch-bill-invoice-number": {
26692
- const [id2, inv] = positional;
26693
- if (!id2 || !inv) {
25276
+ const [id, inv] = positional;
25277
+ if (!id || !inv) {
26694
25278
  throw new Error("patch-bill-invoice-number requires <billId> <invoiceNumber>");
26695
25279
  }
26696
- await patchBillInvoiceNumber(id2, inv);
25280
+ await patchBillInvoiceNumber(id, inv);
26697
25281
  break;
26698
25282
  }
26699
25283
  case "get-bill-attachments": {
26700
- const id2 = positional[0];
26701
- if (!id2)
25284
+ const id = positional[0];
25285
+ if (!id)
26702
25286
  throw new Error("get-bill-attachments requires <billId>");
26703
- await getBillAttachments(id2);
25287
+ await getBillAttachments(id);
26704
25288
  break;
26705
25289
  }
26706
25290
  case "stage-bill-attachment": {
@@ -26786,10 +25370,10 @@ Waiting for approval\u2026
26786
25370
  break;
26787
25371
  }
26788
25372
  case "get-bill-details": {
26789
- const id2 = positional[0];
26790
- if (!id2)
25373
+ const id = positional[0];
25374
+ if (!id)
26791
25375
  throw new Error("get-bill-details requires <billId>");
26792
- await getBillDetails(id2);
25376
+ await getBillDetails(id);
26793
25377
  break;
26794
25378
  }
26795
25379
  case "list-quotations": {
@@ -26809,10 +25393,10 @@ Waiting for approval\u2026
26809
25393
  break;
26810
25394
  }
26811
25395
  case "get-quotation": {
26812
- const id2 = positional[0];
26813
- if (!id2)
25396
+ const id = positional[0];
25397
+ if (!id)
26814
25398
  throw new Error("get-quotation requires <quotationId>");
26815
- await getQuotationDetails(id2);
25399
+ await getQuotationDetails(id);
26816
25400
  break;
26817
25401
  }
26818
25402
  case "upload-quotation-attachment": {
@@ -26851,38 +25435,38 @@ Waiting for approval\u2026
26851
25435
  break;
26852
25436
  }
26853
25437
  case "delete-quotation": {
26854
- const id2 = positional[0];
26855
- if (!id2)
25438
+ const id = positional[0];
25439
+ if (!id)
26856
25440
  throw new Error("delete-quotation requires <quotationId>");
26857
- await deleteQuotationById(id2);
25441
+ await deleteQuotationById(id);
26858
25442
  break;
26859
25443
  }
26860
25444
  case "submit-quotation": {
26861
- const id2 = positional[0];
26862
- if (!id2)
25445
+ const id = positional[0];
25446
+ if (!id)
26863
25447
  throw new Error("submit-quotation requires <quotationId>");
26864
- await submitQuotation(id2, getFlag(flags, "overBudgetOverrideReason"));
25448
+ await submitQuotation(id, getFlag(flags, "overBudgetOverrideReason"));
26865
25449
  break;
26866
25450
  }
26867
25451
  case "approve-quotation": {
26868
- const id2 = positional[0];
26869
- if (!id2)
25452
+ const id = positional[0];
25453
+ if (!id)
26870
25454
  throw new Error("approve-quotation requires <quotationId>");
26871
- await approveQuotation(id2, getFlag(flags, "overBudgetOverrideReason"));
25455
+ await approveQuotation(id, getFlag(flags, "overBudgetOverrideReason"));
26872
25456
  break;
26873
25457
  }
26874
25458
  case "reject-quotation": {
26875
- const id2 = positional[0];
25459
+ const id = positional[0];
26876
25460
  const reason = getFlag(flags, "reason");
26877
- if (!id2 || reason === undefined) {
25461
+ if (!id || reason === undefined) {
26878
25462
  throw new Error("reject-quotation requires <quotationId> --reason <text>");
26879
25463
  }
26880
- await rejectQuotation(id2, reason);
25464
+ await rejectQuotation(id, reason);
26881
25465
  break;
26882
25466
  }
26883
25467
  case "download-quotation-pdf": {
26884
- const id2 = positional[0];
26885
- if (!id2) {
25468
+ const id = positional[0];
25469
+ if (!id) {
26886
25470
  throw new Error("download-quotation-pdf requires <quotationId> [original|staff|final]");
26887
25471
  }
26888
25472
  const roleRaw = (positional[1] ?? "final").toLowerCase();
@@ -26890,7 +25474,7 @@ Waiting for approval\u2026
26890
25474
  if (!fileRole) {
26891
25475
  throw new Error("download-quotation-pdf role must be original, staff, or final");
26892
25476
  }
26893
- await downloadQuotationPdf(id2, fileRole);
25477
+ await downloadQuotationPdf(id, fileRole);
26894
25478
  break;
26895
25479
  }
26896
25480
  case "check-customer-invoice-readiness": {
@@ -26973,31 +25557,6 @@ Waiting for approval\u2026
26973
25557
  }
26974
25558
  break;
26975
25559
  }
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
- }
27001
25560
  case "create-customer-invoice": {
27002
25561
  const payloadRaw = getFlag(flags, "payload");
27003
25562
  if (!payloadRaw) {
@@ -27080,58 +25639,58 @@ Waiting for approval\u2026
27080
25639
  break;
27081
25640
  }
27082
25641
  case "approve-bill": {
27083
- const id2 = positional[0];
27084
- if (!id2)
25642
+ const id = positional[0];
25643
+ if (!id)
27085
25644
  throw new Error("approve-bill requires <billId>");
27086
- await approveBill(id2, getFlag(flags, "overBudgetOverrideReason"));
25645
+ await approveBill(id, getFlag(flags, "overBudgetOverrideReason"));
27087
25646
  break;
27088
25647
  }
27089
25648
  case "reject-bill": {
27090
- const id2 = positional[0];
25649
+ const id = positional[0];
27091
25650
  const reason = getFlag(flags, "reason");
27092
- if (!id2 || reason === undefined) {
25651
+ if (!id || reason === undefined) {
27093
25652
  throw new Error("reject-bill requires <billId> --reason <text>");
27094
25653
  }
27095
- await rejectBill(id2, reason);
25654
+ await rejectBill(id, reason);
27096
25655
  break;
27097
25656
  }
27098
25657
  case "approve-budget": {
27099
- const id2 = positional[0];
27100
- if (!id2)
25658
+ const id = positional[0];
25659
+ if (!id)
27101
25660
  throw new Error("approve-budget requires <budgetId>");
27102
- await approveBudget(id2);
25661
+ await approveBudget(id);
27103
25662
  break;
27104
25663
  }
27105
25664
  case "reject-budget": {
27106
- const id2 = positional[0];
25665
+ const id = positional[0];
27107
25666
  const reason = getFlag(flags, "reason");
27108
- if (!id2 || reason === undefined) {
25667
+ if (!id || reason === undefined) {
27109
25668
  throw new Error("reject-budget requires <budgetId> --reason <text>");
27110
25669
  }
27111
- await rejectBudget(id2, reason);
25670
+ await rejectBudget(id, reason);
27112
25671
  break;
27113
25672
  }
27114
25673
  case "approve-supplier": {
27115
- const id2 = positional[0];
27116
- if (!id2)
25674
+ const id = positional[0];
25675
+ if (!id)
27117
25676
  throw new Error("approve-supplier requires <supplierId>");
27118
- await approveSupplier(id2);
25677
+ await approveSupplier(id);
27119
25678
  break;
27120
25679
  }
27121
25680
  case "reject-supplier": {
27122
- const id2 = positional[0];
25681
+ const id = positional[0];
27123
25682
  const reason = getFlag(flags, "reason");
27124
- if (!id2 || reason === undefined) {
25683
+ if (!id || reason === undefined) {
27125
25684
  throw new Error("reject-supplier requires <supplierId> --reason <text>");
27126
25685
  }
27127
- await rejectSupplier(id2, reason);
25686
+ await rejectSupplier(id, reason);
27128
25687
  break;
27129
25688
  }
27130
25689
  case "get-supplier-details": {
27131
- const id2 = positional[0];
27132
- if (!id2)
25690
+ const id = positional[0];
25691
+ if (!id)
27133
25692
  throw new Error("get-supplier-details requires <supplierId>");
27134
- await getSupplierDetails(id2);
25693
+ await getSupplierDetails(id);
27135
25694
  break;
27136
25695
  }
27137
25696
  case "list-companies": {
@@ -27145,10 +25704,10 @@ Waiting for approval\u2026
27145
25704
  break;
27146
25705
  }
27147
25706
  case "get-company": {
27148
- const id2 = positional[0];
27149
- if (!id2)
25707
+ const id = positional[0];
25708
+ if (!id)
27150
25709
  throw new Error("get-company requires <id>");
27151
- await getCompany(id2);
25710
+ await getCompany(id);
27152
25711
  break;
27153
25712
  }
27154
25713
  case "create-company": {
@@ -27202,10 +25761,10 @@ Waiting for approval\u2026
27202
25761
  break;
27203
25762
  }
27204
25763
  case "delete-company": {
27205
- const id2 = positional[0];
27206
- if (!id2)
25764
+ const id = positional[0];
25765
+ if (!id)
27207
25766
  throw new Error("delete-company requires <id>");
27208
- await deleteCompanyById(id2);
25767
+ await deleteCompanyById(id);
27209
25768
  break;
27210
25769
  }
27211
25770
  case "list-projects": {
@@ -27232,10 +25791,10 @@ Waiting for approval\u2026
27232
25791
  break;
27233
25792
  }
27234
25793
  case "get-project": {
27235
- const id2 = positional[0];
27236
- if (!id2)
25794
+ const id = positional[0];
25795
+ if (!id)
27237
25796
  throw new Error("get-project requires <id>");
27238
- await getProject(id2);
25797
+ await getProject(id);
27239
25798
  break;
27240
25799
  }
27241
25800
  case "get-project-hub-status": {
@@ -27313,32 +25872,32 @@ Waiting for approval\u2026
27313
25872
  break;
27314
25873
  }
27315
25874
  case "delete-project": {
27316
- const id2 = positional[0];
27317
- if (!id2)
25875
+ const id = positional[0];
25876
+ if (!id)
27318
25877
  throw new Error("delete-project requires <id>");
27319
- await deleteProjectById(id2);
25878
+ await deleteProjectById(id);
27320
25879
  break;
27321
25880
  }
27322
25881
  case "check-project-reconciliation": {
27323
- const id2 = positional[0];
27324
- if (!id2) {
25882
+ const id = positional[0];
25883
+ if (!id) {
27325
25884
  throw new Error("check-project-reconciliation requires <id>");
27326
25885
  }
27327
- await checkProjectReconciliation(id2);
25886
+ await checkProjectReconciliation(id);
27328
25887
  break;
27329
25888
  }
27330
25889
  case "reconcile-project": {
27331
- const id2 = positional[0];
27332
- if (!id2)
25890
+ const id = positional[0];
25891
+ if (!id)
27333
25892
  throw new Error("reconcile-project requires <id>");
27334
- await reconcileProject(id2);
25893
+ await reconcileProject(id);
27335
25894
  break;
27336
25895
  }
27337
25896
  case "complete-project": {
27338
- const id2 = positional[0];
27339
- if (!id2)
25897
+ const id = positional[0];
25898
+ if (!id)
27340
25899
  throw new Error("complete-project requires <id>");
27341
- await completeProject(id2);
25900
+ await completeProject(id);
27342
25901
  break;
27343
25902
  }
27344
25903
  case "import-qbo-project": {
@@ -27351,14 +25910,14 @@ Waiting for approval\u2026
27351
25910
  break;
27352
25911
  }
27353
25912
  case "update-project-status": {
27354
- const [id2, statusRaw] = positional;
27355
- if (!id2 || !statusRaw) {
25913
+ const [id, statusRaw] = positional;
25914
+ if (!id || !statusRaw) {
27356
25915
  throw new Error("update-project-status requires <id> <status>");
27357
25916
  }
27358
25917
  const pmIdFlag = getFlag(flags, "projectManagerId");
27359
25918
  const pmEmailFlag = getFlag(flags, "projectManagerEmail");
27360
25919
  const wonOverrideReasonFlag = getFlag(flags, "wonOverrideReason");
27361
- await updateProjectStatus(id2, parseProjectStatusForUpdate(statusRaw), {
25920
+ await updateProjectStatus(id, parseProjectStatusForUpdate(statusRaw), {
27362
25921
  ...pmIdFlag && { projectManagerId: String(pmIdFlag) },
27363
25922
  ...pmEmailFlag && {
27364
25923
  projectManagerEmail: String(pmEmailFlag)
@@ -27370,10 +25929,10 @@ Waiting for approval\u2026
27370
25929
  break;
27371
25930
  }
27372
25931
  case "list-contacts": {
27373
- const id2 = positional[0];
27374
- if (!id2)
25932
+ const id = positional[0];
25933
+ if (!id)
27375
25934
  throw new Error("list-contacts requires <companyId>");
27376
- await listContacts(id2);
25935
+ await listContacts(id);
27377
25936
  break;
27378
25937
  }
27379
25938
  case "create-contact-person": {
@@ -27488,10 +26047,10 @@ Waiting for approval\u2026
27488
26047
  break;
27489
26048
  }
27490
26049
  case "get-item": {
27491
- const id2 = positional[0];
27492
- if (!id2)
26050
+ const id = positional[0];
26051
+ if (!id)
27493
26052
  throw new Error("get-item requires <id>");
27494
- await getItem(id2);
26053
+ await getItem(id);
27495
26054
  break;
27496
26055
  }
27497
26056
  case "create-item": {
@@ -27513,10 +26072,10 @@ Waiting for approval\u2026
27513
26072
  break;
27514
26073
  }
27515
26074
  case "delete-item": {
27516
- const id2 = positional[0];
27517
- if (!id2)
26075
+ const id = positional[0];
26076
+ if (!id)
27518
26077
  throw new Error("delete-item requires <id>");
27519
- await deleteItemById(id2);
26078
+ await deleteItemById(id);
27520
26079
  break;
27521
26080
  }
27522
26081
  case "archive-items": {
@@ -27546,12 +26105,12 @@ Waiting for approval\u2026
27546
26105
  break;
27547
26106
  }
27548
26107
  case "update-item-category": {
27549
- const id2 = getFlag(flags, "id");
26108
+ const id = getFlag(flags, "id");
27550
26109
  const name = getFlag(flags, "name");
27551
- if (!id2 || !name) {
26110
+ if (!id || !name) {
27552
26111
  throw new Error("update-item-category requires --id and --name");
27553
26112
  }
27554
- await updateItemCategory({ id: String(id2), name: String(name) });
26113
+ await updateItemCategory({ id: String(id), name: String(name) });
27555
26114
  break;
27556
26115
  }
27557
26116
  case "delete-item-categories": {
@@ -27596,12 +26155,12 @@ Waiting for approval\u2026
27596
26155
  break;
27597
26156
  }
27598
26157
  case "revoke-api-key": {
27599
- const id2 = positional[0];
27600
- if (!id2) {
26158
+ const id = positional[0];
26159
+ if (!id) {
27601
26160
  throw new Error("revoke-api-key requires <apiKeyId> [--userId <userId>]");
27602
26161
  }
27603
26162
  await revokeApiKeyForUser({
27604
- id: id2,
26163
+ id,
27605
26164
  userId: getFlag(flags, "userId")
27606
26165
  });
27607
26166
  break;
@@ -27764,12 +26323,12 @@ Waiting for approval\u2026
27764
26323
  break;
27765
26324
  }
27766
26325
  case "get-budget-category-benchmarks": {
27767
- const id2 = positional[0];
27768
- if (!id2) {
26326
+ const id = positional[0];
26327
+ if (!id) {
27769
26328
  throw new Error("get-budget-category-benchmarks requires <categoryId> [--limit]");
27770
26329
  }
27771
26330
  await getBudgetCategoryBenchmarks({
27772
- categoryId: id2,
26331
+ categoryId: id,
27773
26332
  limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit")
27774
26333
  });
27775
26334
  break;
@@ -27784,12 +26343,12 @@ Waiting for approval\u2026
27784
26343
  break;
27785
26344
  }
27786
26345
  case "get-supplier-pricing-history": {
27787
- const id2 = positional[0];
27788
- if (!id2) {
26346
+ const id = positional[0];
26347
+ if (!id) {
27789
26348
  throw new Error("get-supplier-pricing-history requires <supplierId> [--itemCategoryId] [--limit]");
27790
26349
  }
27791
26350
  await getSupplierPricingHistory({
27792
- supplierId: id2,
26351
+ supplierId: id,
27793
26352
  itemCategoryId: getFlag(flags, "itemCategoryId"),
27794
26353
  limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit")
27795
26354
  });