@voyant-travel/finance 0.138.5 → 0.138.7
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/routes-payments.d.ts +56 -0
- package/dist/routes-payments.d.ts.map +1 -1
- package/dist/routes-payments.js +44 -16
- package/dist/routes-reference-data.d.ts.map +1 -1
- package/dist/routes-reference-data.js +23 -3
- package/dist/routes-supplier-invoices.d.ts +72 -0
- package/dist/routes-supplier-invoices.d.ts.map +1 -1
- package/dist/routes-supplier-invoices.js +47 -13
- package/dist/routes.d.ts +56 -0
- package/dist/routes.d.ts.map +1 -1
- package/dist/service-reference-data.d.ts.map +1 -1
- package/dist/service-reference-data.js +26 -0
- package/dist/service-supplier-invoices.d.ts +3 -1
- package/dist/service-supplier-invoices.d.ts.map +1 -1
- package/dist/service-supplier-invoices.js +89 -0
- package/dist/service-supplier-payments.d.ts.map +1 -1
- package/dist/service-supplier-payments.js +62 -1
- package/package.json +7 -7
|
@@ -134,6 +134,32 @@ export declare const financePaymentRoutes: OpenAPIHono<Env, import("hono/types")
|
|
|
134
134
|
};
|
|
135
135
|
outputFormat: "json";
|
|
136
136
|
status: 201;
|
|
137
|
+
} | {
|
|
138
|
+
input: {
|
|
139
|
+
json: {
|
|
140
|
+
amountCents: number;
|
|
141
|
+
currency: string;
|
|
142
|
+
paymentMethod: "bank_transfer" | "credit_card" | "debit_card" | "cash" | "cheque" | "wallet" | "direct_bill" | "voucher" | "other";
|
|
143
|
+
paymentDate: string;
|
|
144
|
+
bookingId?: string | null | undefined;
|
|
145
|
+
supplierInvoiceId?: string | null | undefined;
|
|
146
|
+
supplierId?: string | null | undefined;
|
|
147
|
+
bookingSupplierStatusId?: string | null | undefined;
|
|
148
|
+
baseCurrency?: string | null | undefined;
|
|
149
|
+
baseAmountCents?: number | null | undefined;
|
|
150
|
+
fxRateSetId?: string | null | undefined;
|
|
151
|
+
paymentInstrumentId?: string | null | undefined;
|
|
152
|
+
status?: "pending" | "completed" | "failed" | "refunded" | undefined;
|
|
153
|
+
referenceNumber?: string | null | undefined;
|
|
154
|
+
notes?: string | null | undefined;
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
output: {
|
|
158
|
+
error: string;
|
|
159
|
+
code: string;
|
|
160
|
+
};
|
|
161
|
+
outputFormat: "json";
|
|
162
|
+
status: 422;
|
|
137
163
|
};
|
|
138
164
|
};
|
|
139
165
|
} & {
|
|
@@ -196,6 +222,36 @@ export declare const financePaymentRoutes: OpenAPIHono<Env, import("hono/types")
|
|
|
196
222
|
};
|
|
197
223
|
outputFormat: "json";
|
|
198
224
|
status: 400;
|
|
225
|
+
} | {
|
|
226
|
+
input: {
|
|
227
|
+
param: {
|
|
228
|
+
id: string;
|
|
229
|
+
};
|
|
230
|
+
} & {
|
|
231
|
+
json: {
|
|
232
|
+
bookingId?: string | null | undefined;
|
|
233
|
+
supplierInvoiceId?: string | null | undefined;
|
|
234
|
+
supplierId?: string | null | undefined;
|
|
235
|
+
bookingSupplierStatusId?: string | null | undefined;
|
|
236
|
+
amountCents?: number | undefined;
|
|
237
|
+
currency?: string | undefined;
|
|
238
|
+
baseCurrency?: string | null | undefined;
|
|
239
|
+
baseAmountCents?: number | null | undefined;
|
|
240
|
+
fxRateSetId?: string | null | undefined;
|
|
241
|
+
paymentMethod?: "bank_transfer" | "credit_card" | "debit_card" | "cash" | "cheque" | "wallet" | "direct_bill" | "voucher" | "other" | undefined;
|
|
242
|
+
paymentInstrumentId?: string | null | undefined;
|
|
243
|
+
status?: "pending" | "completed" | "failed" | "refunded" | undefined;
|
|
244
|
+
referenceNumber?: string | null | undefined;
|
|
245
|
+
paymentDate?: string | undefined;
|
|
246
|
+
notes?: string | null | undefined;
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
output: {
|
|
250
|
+
error: string;
|
|
251
|
+
code: string;
|
|
252
|
+
};
|
|
253
|
+
outputFormat: "json";
|
|
254
|
+
status: 422;
|
|
199
255
|
} | {
|
|
200
256
|
input: {
|
|
201
257
|
param: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes-payments.d.ts","sourceRoot":"","sources":["../src/routes-payments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAA;AAM/D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"routes-payments.d.ts","sourceRoot":"","sources":["../src/routes-payments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAA;AAM/D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AA2Q7C,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAEG,CAAA"}
|
package/dist/routes-payments.js
CHANGED
|
@@ -20,8 +20,16 @@ import { errorResponseSchema, paymentSchema } from "./routes-invoice-schemas.js"
|
|
|
20
20
|
import { supplierPaymentSchema, unifiedPaymentSchema } from "./routes-payment-schemas.js";
|
|
21
21
|
import { getActionLedgerRequestContext, getFinanceRouteRuntime } from "./routes-runtime.js";
|
|
22
22
|
import { financeService, PaymentValidationError } from "./service.js";
|
|
23
|
+
import { SupplierInvoiceServiceError } from "./service-supplier-invoices.js";
|
|
23
24
|
import { insertSupplierPaymentSchema, paymentListQuerySchema, supplierPaymentListQuerySchema, updatePaymentSchema, updateSupplierPaymentSchema, } from "./validation.js";
|
|
24
25
|
const idParamSchema = z.object({ id: z.string() });
|
|
26
|
+
const supplierPaymentInvariantErrorSchema = z.object({ error: z.string(), code: z.string() });
|
|
27
|
+
function handleSupplierPaymentError(error) {
|
|
28
|
+
if (error instanceof SupplierInvoiceServiceError) {
|
|
29
|
+
return { error: error.message, code: error.code, status: 422 };
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
25
33
|
// --- unified payments (customer + supplier) -------------------------------
|
|
26
34
|
const listAllPaymentsRoute = createRoute({
|
|
27
35
|
method: "get",
|
|
@@ -178,6 +186,10 @@ const createSupplierPaymentRoute = createRoute({
|
|
|
178
186
|
description: "invalid_request: request body failed validation",
|
|
179
187
|
content: { "application/json": { schema: errorResponseSchema } },
|
|
180
188
|
},
|
|
189
|
+
422: {
|
|
190
|
+
description: "supplier payment payable invariant failed",
|
|
191
|
+
content: { "application/json": { schema: supplierPaymentInvariantErrorSchema } },
|
|
192
|
+
},
|
|
181
193
|
},
|
|
182
194
|
});
|
|
183
195
|
const updateSupplierPaymentRoute = createRoute({
|
|
@@ -203,30 +215,46 @@ const updateSupplierPaymentRoute = createRoute({
|
|
|
203
215
|
description: "Supplier payment not found",
|
|
204
216
|
content: { "application/json": { schema: errorResponseSchema } },
|
|
205
217
|
},
|
|
218
|
+
422: {
|
|
219
|
+
description: "supplier payment payable invariant failed",
|
|
220
|
+
content: { "application/json": { schema: supplierPaymentInvariantErrorSchema } },
|
|
221
|
+
},
|
|
206
222
|
},
|
|
207
223
|
});
|
|
208
224
|
const supplierPaymentRoutes = new OpenAPIHono({ defaultHook: openApiValidationHook })
|
|
209
225
|
.openapi(listSupplierPaymentsRoute, async (c) => c.json(await financeService.listSupplierPayments(c.get("db"), c.req.valid("query")), 200))
|
|
210
226
|
.openapi(createSupplierPaymentRoute, async (c) => {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
227
|
+
try {
|
|
228
|
+
const runtime = getFinanceRouteRuntime(c);
|
|
229
|
+
const row = await financeService.createSupplierPayment(c.get("db"), c.req.valid("json"), {
|
|
230
|
+
...(runtime ?? {}),
|
|
231
|
+
actionLedgerContext: getActionLedgerRequestContext(c),
|
|
232
|
+
actionLedgerAuthorizationSource: "finance.supplier_payment.route",
|
|
233
|
+
});
|
|
234
|
+
if (!row) {
|
|
235
|
+
throw new Error("Failed to create supplier payment");
|
|
236
|
+
}
|
|
237
|
+
return c.json({ data: row }, 201);
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
const body = handleSupplierPaymentError(error);
|
|
241
|
+
return c.json({ error: body.error, code: body.code }, body.status);
|
|
219
242
|
}
|
|
220
|
-
return c.json({ data: row }, 201);
|
|
221
243
|
})
|
|
222
244
|
.openapi(updateSupplierPaymentRoute, async (c) => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
245
|
+
try {
|
|
246
|
+
const runtime = getFinanceRouteRuntime(c);
|
|
247
|
+
const row = await financeService.updateSupplierPayment(c.get("db"), c.req.valid("param").id, c.req.valid("json"), {
|
|
248
|
+
...(runtime ?? {}),
|
|
249
|
+
actionLedgerContext: getActionLedgerRequestContext(c),
|
|
250
|
+
actionLedgerAuthorizationSource: "finance.supplier_payment.route",
|
|
251
|
+
});
|
|
252
|
+
return row ? c.json({ data: row }, 200) : c.json({ error: "Supplier payment not found" }, 404);
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
const body = handleSupplierPaymentError(error);
|
|
256
|
+
return c.json({ error: body.error, code: body.code }, body.status);
|
|
257
|
+
}
|
|
230
258
|
});
|
|
231
259
|
export const financePaymentRoutes = new OpenAPIHono({ defaultHook: openApiValidationHook })
|
|
232
260
|
.route("/", unifiedPaymentRoutes)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes-reference-data.d.ts","sourceRoot":"","sources":["../src/routes-reference-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAA;AAI/D,OAAO,EAAE,KAAK,GAAG,EAAY,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"routes-reference-data.d.ts","sourceRoot":"","sources":["../src/routes-reference-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAA;AAI/D,OAAO,EAAE,KAAK,GAAG,EAAY,MAAM,oBAAoB,CAAA;AAy8BvD,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAQL,CAAA"}
|
|
@@ -804,14 +804,34 @@ const deleteTaxPolicyRuleRoute = createRoute({
|
|
|
804
804
|
});
|
|
805
805
|
const taxPolicyRuleRoutes = new OpenAPIHono({ defaultHook: openApiValidationHook })
|
|
806
806
|
.openapi(listTaxPolicyRulesRoute, async (c) => c.json(await financeService.listTaxPolicyRules(c.get("db"), c.req.valid("query")), 200))
|
|
807
|
-
.openapi(createTaxPolicyRuleRoute, async (c) =>
|
|
807
|
+
.openapi(createTaxPolicyRuleRoute, async (c) => {
|
|
808
|
+
try {
|
|
809
|
+
return c.json({
|
|
810
|
+
data: created(await financeService.createTaxPolicyRule(c.get("db"), c.req.valid("json"))),
|
|
811
|
+
}, 201);
|
|
812
|
+
}
|
|
813
|
+
catch (error) {
|
|
814
|
+
if (error instanceof ReferenceDataValidationError) {
|
|
815
|
+
return c.json({ error: error.message, code: error.code, details: error.details }, error.status);
|
|
816
|
+
}
|
|
817
|
+
throw error;
|
|
818
|
+
}
|
|
819
|
+
})
|
|
808
820
|
.openapi(getTaxPolicyRuleRoute, async (c) => {
|
|
809
821
|
const row = await financeService.getTaxPolicyRuleById(c.get("db"), c.req.valid("param").id);
|
|
810
822
|
return row ? c.json({ data: row }, 200) : notFound(c, "Tax policy rule not found");
|
|
811
823
|
})
|
|
812
824
|
.openapi(updateTaxPolicyRuleRoute, async (c) => {
|
|
813
|
-
|
|
814
|
-
|
|
825
|
+
try {
|
|
826
|
+
const row = await financeService.updateTaxPolicyRule(c.get("db"), c.req.valid("param").id, c.req.valid("json"));
|
|
827
|
+
return row ? c.json({ data: row }, 200) : notFound(c, "Tax policy rule not found");
|
|
828
|
+
}
|
|
829
|
+
catch (error) {
|
|
830
|
+
if (error instanceof ReferenceDataValidationError) {
|
|
831
|
+
return c.json({ error: error.message, code: error.code, details: error.details }, error.status);
|
|
832
|
+
}
|
|
833
|
+
throw error;
|
|
834
|
+
}
|
|
815
835
|
})
|
|
816
836
|
.openapi(deleteTaxPolicyRuleRoute, async (c) => {
|
|
817
837
|
const row = await financeService.deleteTaxPolicyRule(c.get("db"), c.req.valid("param").id);
|
|
@@ -277,6 +277,35 @@ export declare const supplierInvoiceRoutes: OpenAPIHono<Env, import("hono/types"
|
|
|
277
277
|
};
|
|
278
278
|
outputFormat: "json";
|
|
279
279
|
status: 400;
|
|
280
|
+
} | {
|
|
281
|
+
input: {
|
|
282
|
+
param: {
|
|
283
|
+
id: string;
|
|
284
|
+
};
|
|
285
|
+
} & {
|
|
286
|
+
json: {
|
|
287
|
+
currency: string;
|
|
288
|
+
amountCents: number;
|
|
289
|
+
paymentMethod: "bank_transfer" | "credit_card" | "debit_card" | "cash" | "cheque" | "wallet" | "direct_bill" | "voucher" | "other";
|
|
290
|
+
paymentDate: string;
|
|
291
|
+
status?: "pending" | "completed" | "failed" | "refunded" | undefined;
|
|
292
|
+
notes?: string | null | undefined;
|
|
293
|
+
bookingId?: string | null | undefined;
|
|
294
|
+
baseCurrency?: string | null | undefined;
|
|
295
|
+
fxRateSetId?: string | null | undefined;
|
|
296
|
+
baseAmountCents?: number | null | undefined;
|
|
297
|
+
referenceNumber?: string | null | undefined;
|
|
298
|
+
supplierId?: string | null | undefined;
|
|
299
|
+
paymentInstrumentId?: string | null | undefined;
|
|
300
|
+
bookingSupplierStatusId?: string | null | undefined;
|
|
301
|
+
};
|
|
302
|
+
};
|
|
303
|
+
output: {
|
|
304
|
+
error: string;
|
|
305
|
+
code?: string | undefined;
|
|
306
|
+
};
|
|
307
|
+
outputFormat: "json";
|
|
308
|
+
status: 422;
|
|
280
309
|
} | {
|
|
281
310
|
input: {
|
|
282
311
|
param: {
|
|
@@ -660,6 +689,18 @@ export declare const supplierInvoiceRoutes: OpenAPIHono<Env, import("hono/types"
|
|
|
660
689
|
} & {
|
|
661
690
|
"/supplier-invoices/:id": {
|
|
662
691
|
$get: {
|
|
692
|
+
input: {
|
|
693
|
+
param: {
|
|
694
|
+
id: string;
|
|
695
|
+
};
|
|
696
|
+
};
|
|
697
|
+
output: {
|
|
698
|
+
error: string;
|
|
699
|
+
code?: string | undefined;
|
|
700
|
+
};
|
|
701
|
+
outputFormat: "json";
|
|
702
|
+
status: 422;
|
|
703
|
+
} | {
|
|
663
704
|
input: {
|
|
664
705
|
param: {
|
|
665
706
|
id: string;
|
|
@@ -779,6 +820,37 @@ export declare const supplierInvoiceRoutes: OpenAPIHono<Env, import("hono/types"
|
|
|
779
820
|
};
|
|
780
821
|
outputFormat: "json";
|
|
781
822
|
status: 400;
|
|
823
|
+
} | {
|
|
824
|
+
input: {
|
|
825
|
+
param: {
|
|
826
|
+
id: string;
|
|
827
|
+
};
|
|
828
|
+
} & {
|
|
829
|
+
json: {
|
|
830
|
+
supplierId?: string | undefined;
|
|
831
|
+
supplierInvoiceNo?: string | undefined;
|
|
832
|
+
internalRef?: string | null | undefined;
|
|
833
|
+
status?: "draft" | "partially_paid" | "paid" | "void" | "received" | "approved" | "disputed" | undefined;
|
|
834
|
+
currency?: string | undefined;
|
|
835
|
+
baseCurrency?: string | null | undefined;
|
|
836
|
+
fxRateSetId?: string | null | undefined;
|
|
837
|
+
subtotalCents?: number | undefined;
|
|
838
|
+
taxCents?: number | undefined;
|
|
839
|
+
totalCents?: number | undefined;
|
|
840
|
+
taxRegimeId?: string | null | undefined;
|
|
841
|
+
issueDate?: string | undefined;
|
|
842
|
+
dueDate?: string | null | undefined;
|
|
843
|
+
storageKey?: string | null | undefined;
|
|
844
|
+
extractionId?: string | null | undefined;
|
|
845
|
+
notes?: string | null | undefined;
|
|
846
|
+
};
|
|
847
|
+
};
|
|
848
|
+
output: {
|
|
849
|
+
error: string;
|
|
850
|
+
code?: string | undefined;
|
|
851
|
+
};
|
|
852
|
+
outputFormat: "json";
|
|
853
|
+
status: 422;
|
|
782
854
|
} | {
|
|
783
855
|
input: {
|
|
784
856
|
param: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes-supplier-invoices.d.ts","sourceRoot":"","sources":["../src/routes-supplier-invoices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAA;AAM/D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"routes-supplier-invoices.d.ts","sourceRoot":"","sources":["../src/routes-supplier-invoices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAA;AAM/D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AAkuB7C,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAGY,CAAA;AAE9C,MAAM,MAAM,qBAAqB,GAAG,OAAO,qBAAqB,CAAA"}
|
|
@@ -284,6 +284,10 @@ const getSupplierInvoiceRoute = createRoute({
|
|
|
284
284
|
description: "Supplier invoice not found",
|
|
285
285
|
content: { "application/json": { schema: errorResponseSchema } },
|
|
286
286
|
},
|
|
287
|
+
422: {
|
|
288
|
+
description: "Supplier invoice payable invariant failed",
|
|
289
|
+
content: { "application/json": { schema: supplierInvoiceErrorSchema } },
|
|
290
|
+
},
|
|
287
291
|
},
|
|
288
292
|
});
|
|
289
293
|
const updateSupplierInvoiceRoute = createRoute({
|
|
@@ -309,6 +313,10 @@ const updateSupplierInvoiceRoute = createRoute({
|
|
|
309
313
|
description: "Supplier invoice not found",
|
|
310
314
|
content: { "application/json": { schema: errorResponseSchema } },
|
|
311
315
|
},
|
|
316
|
+
422: {
|
|
317
|
+
description: "Supplier invoice payable invariant failed",
|
|
318
|
+
content: { "application/json": { schema: supplierInvoiceErrorSchema } },
|
|
319
|
+
},
|
|
312
320
|
},
|
|
313
321
|
});
|
|
314
322
|
const deleteSupplierInvoiceRoute = createRoute({
|
|
@@ -352,7 +360,7 @@ const setSupplierInvoiceLinesRoute = createRoute({
|
|
|
352
360
|
content: { "application/json": { schema: supplierInvoiceErrorSchema } },
|
|
353
361
|
},
|
|
354
362
|
422: {
|
|
355
|
-
description: "
|
|
363
|
+
description: "Supplier invoice payable or allocation invariant failed",
|
|
356
364
|
content: { "application/json": { schema: supplierInvoiceErrorSchema } },
|
|
357
365
|
},
|
|
358
366
|
},
|
|
@@ -412,7 +420,10 @@ const supplierInvoiceCrudRoutes = new OpenAPIHono({ defaultHook: openApiValidati
|
|
|
412
420
|
return c.json({ data: row }, 201);
|
|
413
421
|
}
|
|
414
422
|
catch (error) {
|
|
415
|
-
|
|
423
|
+
if (error instanceof SupplierInvoiceServiceError) {
|
|
424
|
+
return c.json({ error: error.message, code: error.code }, 422);
|
|
425
|
+
}
|
|
426
|
+
throw error;
|
|
416
427
|
}
|
|
417
428
|
})
|
|
418
429
|
.openapi(getSupplierInvoiceRoute, async (c) => {
|
|
@@ -422,10 +433,18 @@ const supplierInvoiceCrudRoutes = new OpenAPIHono({ defaultHook: openApiValidati
|
|
|
422
433
|
return c.json({ data: row }, 200);
|
|
423
434
|
})
|
|
424
435
|
.openapi(updateSupplierInvoiceRoute, async (c) => {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
436
|
+
try {
|
|
437
|
+
const row = await supplierInvoicesService.update(c.get("db"), c.req.valid("param").id, c.req.valid("json"), apRuntime(c));
|
|
438
|
+
if (!row)
|
|
439
|
+
return c.json({ error: "Supplier invoice not found" }, 404);
|
|
440
|
+
return c.json({ data: row }, 200);
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
if (error instanceof SupplierInvoiceServiceError) {
|
|
444
|
+
return c.json({ error: error.message, code: error.code }, 422);
|
|
445
|
+
}
|
|
446
|
+
throw error;
|
|
447
|
+
}
|
|
429
448
|
})
|
|
430
449
|
.openapi(deleteSupplierInvoiceRoute, async (c) => {
|
|
431
450
|
const result = await supplierInvoicesService.softDelete(c.get("db"), c.req.valid("param").id, apRuntime(c));
|
|
@@ -454,7 +473,10 @@ const supplierInvoiceCrudRoutes = new OpenAPIHono({ defaultHook: openApiValidati
|
|
|
454
473
|
return c.json({ data: row }, 200);
|
|
455
474
|
}
|
|
456
475
|
catch (error) {
|
|
457
|
-
|
|
476
|
+
if (error instanceof SupplierInvoiceServiceError) {
|
|
477
|
+
return c.json({ error: error.message, code: error.code }, 422);
|
|
478
|
+
}
|
|
479
|
+
throw error;
|
|
458
480
|
}
|
|
459
481
|
})
|
|
460
482
|
.openapi(downloadSupplierInvoiceDocumentRoute, async (c) => {
|
|
@@ -508,6 +530,10 @@ const createSupplierInvoicePaymentRoute = createRoute({
|
|
|
508
530
|
description: "invalid_request: request body failed validation",
|
|
509
531
|
content: { "application/json": { schema: errorResponseSchema } },
|
|
510
532
|
},
|
|
533
|
+
422: {
|
|
534
|
+
description: "supplier invoice payable invariant failed",
|
|
535
|
+
content: { "application/json": { schema: supplierInvoiceErrorSchema } },
|
|
536
|
+
},
|
|
511
537
|
},
|
|
512
538
|
});
|
|
513
539
|
const supplierPaymentRoutes = new OpenAPIHono({ defaultHook: openApiValidationHook })
|
|
@@ -516,12 +542,20 @@ const supplierPaymentRoutes = new OpenAPIHono({ defaultHook: openApiValidationHo
|
|
|
516
542
|
supplierInvoiceId: c.req.valid("param").id,
|
|
517
543
|
}), 200))
|
|
518
544
|
.openapi(createSupplierInvoicePaymentRoute, async (c) => {
|
|
519
|
-
|
|
520
|
-
...
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
545
|
+
try {
|
|
546
|
+
const row = await financeService.createSupplierPayment(c.get("db"), { ...c.req.valid("json"), supplierInvoiceId: c.req.valid("param").id }, {
|
|
547
|
+
...getFinanceRouteRuntime(c),
|
|
548
|
+
actionLedgerContext: getActionLedgerRequestContext(c),
|
|
549
|
+
actionLedgerAuthorizationSource: "finance.supplier_payment.route",
|
|
550
|
+
});
|
|
551
|
+
return c.json({ data: row }, 201);
|
|
552
|
+
}
|
|
553
|
+
catch (error) {
|
|
554
|
+
if (error instanceof SupplierInvoiceServiceError) {
|
|
555
|
+
return c.json({ error: error.message, code: error.code }, 422);
|
|
556
|
+
}
|
|
557
|
+
throw error;
|
|
558
|
+
}
|
|
525
559
|
});
|
|
526
560
|
// ===========================================================================
|
|
527
561
|
// Supplier-invoice attachments (metadata + signed file download)
|
package/dist/routes.d.ts
CHANGED
|
@@ -4710,6 +4710,32 @@ export declare const financeRoutes: import("hono/hono-base").HonoBase<Env, {
|
|
|
4710
4710
|
};
|
|
4711
4711
|
outputFormat: "json";
|
|
4712
4712
|
status: 201;
|
|
4713
|
+
} | {
|
|
4714
|
+
input: {
|
|
4715
|
+
json: {
|
|
4716
|
+
amountCents: number;
|
|
4717
|
+
currency: string;
|
|
4718
|
+
paymentMethod: "bank_transfer" | "credit_card" | "debit_card" | "cash" | "cheque" | "wallet" | "direct_bill" | "voucher" | "other";
|
|
4719
|
+
paymentDate: string;
|
|
4720
|
+
bookingId?: string | null | undefined;
|
|
4721
|
+
supplierInvoiceId?: string | null | undefined;
|
|
4722
|
+
supplierId?: string | null | undefined;
|
|
4723
|
+
bookingSupplierStatusId?: string | null | undefined;
|
|
4724
|
+
baseCurrency?: string | null | undefined;
|
|
4725
|
+
baseAmountCents?: number | null | undefined;
|
|
4726
|
+
fxRateSetId?: string | null | undefined;
|
|
4727
|
+
paymentInstrumentId?: string | null | undefined;
|
|
4728
|
+
status?: "pending" | "completed" | "failed" | "refunded" | undefined;
|
|
4729
|
+
referenceNumber?: string | null | undefined;
|
|
4730
|
+
notes?: string | null | undefined;
|
|
4731
|
+
};
|
|
4732
|
+
};
|
|
4733
|
+
output: {
|
|
4734
|
+
error: string;
|
|
4735
|
+
code: string;
|
|
4736
|
+
};
|
|
4737
|
+
outputFormat: "json";
|
|
4738
|
+
status: 422;
|
|
4713
4739
|
};
|
|
4714
4740
|
};
|
|
4715
4741
|
} & {
|
|
@@ -4772,6 +4798,36 @@ export declare const financeRoutes: import("hono/hono-base").HonoBase<Env, {
|
|
|
4772
4798
|
};
|
|
4773
4799
|
outputFormat: "json";
|
|
4774
4800
|
status: 400;
|
|
4801
|
+
} | {
|
|
4802
|
+
input: {
|
|
4803
|
+
param: {
|
|
4804
|
+
id: string;
|
|
4805
|
+
};
|
|
4806
|
+
} & {
|
|
4807
|
+
json: {
|
|
4808
|
+
bookingId?: string | null | undefined;
|
|
4809
|
+
supplierInvoiceId?: string | null | undefined;
|
|
4810
|
+
supplierId?: string | null | undefined;
|
|
4811
|
+
bookingSupplierStatusId?: string | null | undefined;
|
|
4812
|
+
amountCents?: number | undefined;
|
|
4813
|
+
currency?: string | undefined;
|
|
4814
|
+
baseCurrency?: string | null | undefined;
|
|
4815
|
+
baseAmountCents?: number | null | undefined;
|
|
4816
|
+
fxRateSetId?: string | null | undefined;
|
|
4817
|
+
paymentMethod?: "bank_transfer" | "credit_card" | "debit_card" | "cash" | "cheque" | "wallet" | "direct_bill" | "voucher" | "other" | undefined;
|
|
4818
|
+
paymentInstrumentId?: string | null | undefined;
|
|
4819
|
+
status?: "pending" | "completed" | "failed" | "refunded" | undefined;
|
|
4820
|
+
referenceNumber?: string | null | undefined;
|
|
4821
|
+
paymentDate?: string | undefined;
|
|
4822
|
+
notes?: string | null | undefined;
|
|
4823
|
+
};
|
|
4824
|
+
};
|
|
4825
|
+
output: {
|
|
4826
|
+
error: string;
|
|
4827
|
+
code: string;
|
|
4828
|
+
};
|
|
4829
|
+
outputFormat: "json";
|
|
4830
|
+
status: 422;
|
|
4775
4831
|
} | {
|
|
4776
4832
|
input: {
|
|
4777
4833
|
param: {
|
package/dist/routes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAI7D,OAAO,EAAE,6BAA6B,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAE3F,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AAU7C,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAI7D,OAAO,EAAE,6BAA6B,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAE3F,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AAU7C,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAwBa,CAAA;AAEvC,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAA;AAChD,MAAM,MAAM,mBAAmB,GAAG,OAAO,mBAAmB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-reference-data.d.ts","sourceRoot":"","sources":["../src/service-reference-data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,yBAAyB,EACzB,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,2BAA2B,EAC3B,wBAAwB,EACxB,oBAAoB,EACrB,MAAM,qBAAqB,CAAA;AAkB5B,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,QAAQ,CAAC,MAAM,OAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;gBAE9B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,SAAoB;CAMzF;
|
|
1
|
+
{"version":3,"file":"service-reference-data.d.ts","sourceRoot":"","sources":["../src/service-reference-data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,yBAAyB,EACzB,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,2BAA2B,EAC3B,wBAAwB,EACxB,oBAAoB,EACrB,MAAM,qBAAqB,CAAA;AAkB5B,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,QAAQ,CAAC,MAAM,OAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;gBAE9B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,SAAoB;CAMzF;AAkED,eAAO,MAAM,2BAA2B;uBACb,kBAAkB,SAAS,kBAAkB;;;;;;;;;;;;;yBAoB3C,kBAAkB,MAAM,MAAM;;;;;;;;;;;;;wBAK/B,kBAAkB,QAAQ,oBAAoB;;;;;;;;;;;;;wBAiB9C,kBAAkB,MAAM,MAAM,QAAQ,oBAAoB;;;;;;;;;;;;;wBAS1D,kBAAkB,MAAM,MAAM;;;uBAY/B,kBAAkB,SAAS,iBAAiB;;;;;;;;;;;;;;wBAmB3C,kBAAkB,MAAM,MAAM;;;;;;;;;;;;;;uBAK/B,kBAAkB,QAAQ,mBAAmB;;;;;;;;;;;;;;uBAiB7C,kBAAkB,MAAM,MAAM,QAAQ,mBAAmB;;;;;;;;;;;;;;uBAWzD,kBAAkB,MAAM,MAAM;;;8BAYvB,kBAAkB,SAAS,yBAAyB;;;;;;;;;;gCAsBlD,kBAAkB,MAAM,MAAM;;;;;;;;;;+BAS/B,kBAAkB,QAAQ,2BAA2B;;;;;;;;;;+BAehF,kBAAkB,MAClB,MAAM,QACJ,2BAA2B;;;;;;;;;;+BAUF,kBAAkB,MAAM,MAAM;;;2BAYlC,kBAAkB,SAAS,sBAAsB;;;;;;;;;;;;;6BAoB/C,kBAAkB,MAAM,MAAM;;;;;;;;;;;;;4BAK/B,kBAAkB,QAAQ,wBAAwB;;;;;;;;;;;;;4BAmBlD,kBAAkB,MAAM,MAAM,QAAQ,wBAAwB;;;;;;;;;;;;;4BAW9D,kBAAkB,MAAM,MAAM;;;gCAY1B,kBAAkB,aAAa,MAAM;;;;;;;;;;;;;;IAQvE;;;;OAIG;mCAEG,kBAAkB,aACX,MAAM,QACX,6BAA6B;;;;;;;;;;;;;;iCAkDF,kBAAkB,MAAM,MAAM;;;CAOlE,CAAA"}
|
|
@@ -29,6 +29,30 @@ async function assertTaxClassRegimesExist(db, data) {
|
|
|
29
29
|
throw new ReferenceDataValidationError("Tax class references unknown tax regimes", { missingRegimeIds }, "invalid_reference");
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
+
async function assertTaxPolicyRuleReferencesExist(db, data) {
|
|
33
|
+
const missing = {};
|
|
34
|
+
if (data.profileId) {
|
|
35
|
+
const [profile] = await db
|
|
36
|
+
.select({ id: taxPolicyProfiles.id })
|
|
37
|
+
.from(taxPolicyProfiles)
|
|
38
|
+
.where(eq(taxPolicyProfiles.id, data.profileId))
|
|
39
|
+
.limit(1);
|
|
40
|
+
if (!profile)
|
|
41
|
+
missing.missingProfileId = data.profileId;
|
|
42
|
+
}
|
|
43
|
+
if (data.taxRegimeId) {
|
|
44
|
+
const [regime] = await db
|
|
45
|
+
.select({ id: taxRegimes.id })
|
|
46
|
+
.from(taxRegimes)
|
|
47
|
+
.where(eq(taxRegimes.id, data.taxRegimeId))
|
|
48
|
+
.limit(1);
|
|
49
|
+
if (!regime)
|
|
50
|
+
missing.missingTaxRegimeId = data.taxRegimeId;
|
|
51
|
+
}
|
|
52
|
+
if (missing.missingProfileId || missing.missingTaxRegimeId) {
|
|
53
|
+
throw new ReferenceDataValidationError("Tax policy rule references unknown records", missing, "invalid_reference");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
32
56
|
export const financeReferenceDataService = {
|
|
33
57
|
async listTaxRegimes(db, query) {
|
|
34
58
|
const conditions = [];
|
|
@@ -217,6 +241,7 @@ export const financeReferenceDataService = {
|
|
|
217
241
|
return row ?? null;
|
|
218
242
|
},
|
|
219
243
|
async createTaxPolicyRule(db, data) {
|
|
244
|
+
await assertTaxPolicyRuleReferencesExist(db, data);
|
|
220
245
|
const [row] = await db
|
|
221
246
|
.insert(taxPolicyRules)
|
|
222
247
|
.values({
|
|
@@ -233,6 +258,7 @@ export const financeReferenceDataService = {
|
|
|
233
258
|
return row ?? null;
|
|
234
259
|
},
|
|
235
260
|
async updateTaxPolicyRule(db, id, data) {
|
|
261
|
+
await assertTaxPolicyRuleReferencesExist(db, data);
|
|
236
262
|
const [row] = await db
|
|
237
263
|
.update(taxPolicyRules)
|
|
238
264
|
.set({ ...data, updatedAt: new Date() })
|
|
@@ -11,7 +11,7 @@ type SupplierInvoiceLineInput = z.infer<typeof supplierInvoiceLineInputSchema>;
|
|
|
11
11
|
type SetLinesInput = z.infer<typeof setSupplierInvoiceLinesSchema>;
|
|
12
12
|
type SetAllocationsInput = z.infer<typeof setSupplierCostAllocationsSchema>;
|
|
13
13
|
type CreateAttachmentInput = z.infer<typeof insertSupplierInvoiceAttachmentSchema>;
|
|
14
|
-
export type SupplierInvoiceErrorCode = "supplier_invoice_not_found" | "mixed_allocation_modes" | "over_allocated" | "unknown_allocation_line" | "allocate_lines_after_create";
|
|
14
|
+
export type SupplierInvoiceErrorCode = "supplier_invoice_not_found" | "invalid_payable_state" | "mixed_allocation_modes" | "over_allocated" | "unknown_allocation_line" | "allocate_lines_after_create";
|
|
15
15
|
/**
|
|
16
16
|
* Raised by the supplier-invoice (AP) service. Route handlers map `code` to HTTP.
|
|
17
17
|
*/
|
|
@@ -34,6 +34,8 @@ export interface InvoiceTotals {
|
|
|
34
34
|
* internally consistent regardless of per-line unit×qty rounding.
|
|
35
35
|
*/
|
|
36
36
|
export declare function recomputeTotalsFromLines(lines: readonly SupplierInvoiceLineInput[]): InvoiceTotals;
|
|
37
|
+
export declare function expectedSupplierInvoiceLineTotal(line: SupplierInvoiceLineInput): number;
|
|
38
|
+
export declare function validateSupplierInvoiceLines(lines: readonly SupplierInvoiceLineInput[]): void;
|
|
37
39
|
export interface AllocationCheckLine {
|
|
38
40
|
id: string;
|
|
39
41
|
totalAmountCents: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-supplier-invoices.d.ts","sourceRoot":"","sources":["../src/service-supplier-invoices.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,gCAAgC,EAEtC,MAAM,8BAA8B,CAAA;AAGrC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AACjE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAE5B,OAAO,EAAE,KAAK,gBAAgB,EAAqC,MAAM,iBAAiB,CAAA;AAC1F,OAAO,EAIL,gBAAgB,EAEjB,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"service-supplier-invoices.d.ts","sourceRoot":"","sources":["../src/service-supplier-invoices.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@ import { resolveInvoiceFxSettingsOrDefault } from "./invoice-fx.js";
|
|
|
7
7
|
import { supplierCostAllocations, supplierInvoiceAttachments, supplierInvoiceLines, supplierInvoices, supplierPayments, } from "./schema.js";
|
|
8
8
|
import { buildSupplierInvoiceAllocationsActionLedgerInput, buildSupplierInvoiceCreateActionLedgerInput, buildSupplierInvoiceDeleteActionLedgerInput, buildSupplierInvoiceUpdateActionLedgerInput, } from "./service-action-ledger-supplier-invoices.js";
|
|
9
9
|
import { executeBoundaryRows, normalizeDateOnly, sqlList } from "./service-boundary-sql.js";
|
|
10
|
+
import { toRows } from "./service-shared.js";
|
|
10
11
|
/**
|
|
11
12
|
* Raised by the supplier-invoice (AP) service. Route handlers map `code` to HTTP.
|
|
12
13
|
*/
|
|
@@ -89,6 +90,71 @@ export function recomputeTotalsFromLines(lines) {
|
|
|
89
90
|
}
|
|
90
91
|
return { subtotalCents: total - tax, taxCents: tax, totalCents: total };
|
|
91
92
|
}
|
|
93
|
+
function assertNonNegativeCents(label, value) {
|
|
94
|
+
if (value != null && value < 0) {
|
|
95
|
+
throw new SupplierInvoiceServiceError("invalid_payable_state", `${label} must be greater than or equal to 0`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export function expectedSupplierInvoiceLineTotal(line) {
|
|
99
|
+
return (line.quantity ?? 1) * line.unitAmountCents + (line.taxAmountCents ?? 0);
|
|
100
|
+
}
|
|
101
|
+
export function validateSupplierInvoiceLines(lines) {
|
|
102
|
+
for (const [index, line] of lines.entries()) {
|
|
103
|
+
const label = `line ${index + 1}`;
|
|
104
|
+
if ((line.quantity ?? 1) < 1) {
|
|
105
|
+
throw new SupplierInvoiceServiceError("invalid_payable_state", `${label} quantity must be at least 1`);
|
|
106
|
+
}
|
|
107
|
+
assertNonNegativeCents(`${label} unit amount`, line.unitAmountCents);
|
|
108
|
+
assertNonNegativeCents(`${label} tax amount`, line.taxAmountCents);
|
|
109
|
+
assertNonNegativeCents(`${label} total amount`, line.totalAmountCents);
|
|
110
|
+
const expectedTotal = expectedSupplierInvoiceLineTotal(line);
|
|
111
|
+
if (line.totalAmountCents !== expectedTotal) {
|
|
112
|
+
throw new SupplierInvoiceServiceError("invalid_payable_state", `${label} total amount must equal quantity × unit amount plus tax (${expectedTotal})`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function validateHeaderTotals(input) {
|
|
117
|
+
if (input.supplierId != null && input.supplierId.trim().length === 0) {
|
|
118
|
+
throw new SupplierInvoiceServiceError("invalid_payable_state", "supplierId is required for supplier invoices");
|
|
119
|
+
}
|
|
120
|
+
assertNonNegativeCents("subtotalCents", input.subtotalCents);
|
|
121
|
+
assertNonNegativeCents("taxCents", input.taxCents);
|
|
122
|
+
assertNonNegativeCents("totalCents", input.totalCents);
|
|
123
|
+
assertNonNegativeCents("baseSubtotalCents", input.baseSubtotalCents);
|
|
124
|
+
assertNonNegativeCents("baseTaxCents", input.baseTaxCents);
|
|
125
|
+
assertNonNegativeCents("baseTotalCents", input.baseTotalCents);
|
|
126
|
+
}
|
|
127
|
+
async function assertSupplierReferenceExists(db, supplierId) {
|
|
128
|
+
if (!supplierId)
|
|
129
|
+
return;
|
|
130
|
+
const tableResult = await db.execute(sql `
|
|
131
|
+
SELECT EXISTS (
|
|
132
|
+
SELECT 1
|
|
133
|
+
FROM information_schema.tables
|
|
134
|
+
WHERE table_schema = 'public'
|
|
135
|
+
AND table_name = 'suppliers'
|
|
136
|
+
) AS table_exists
|
|
137
|
+
`);
|
|
138
|
+
const tableExists = Boolean(toRows(tableResult)[0]?.table_exists);
|
|
139
|
+
if (!tableExists)
|
|
140
|
+
return;
|
|
141
|
+
const supplierResult = await db.execute(sql `SELECT id FROM suppliers WHERE id = ${supplierId} LIMIT 1`);
|
|
142
|
+
if (toRows(supplierResult).length === 0) {
|
|
143
|
+
throw new SupplierInvoiceServiceError("invalid_payable_state", "supplierId does not reference an existing supplier");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function validateProvidedTotalsAgainstLines(input, totals) {
|
|
147
|
+
const mismatches = [
|
|
148
|
+
["subtotalCents", input.subtotalCents, totals.subtotalCents],
|
|
149
|
+
["taxCents", input.taxCents, totals.taxCents],
|
|
150
|
+
["totalCents", input.totalCents, totals.totalCents],
|
|
151
|
+
];
|
|
152
|
+
for (const [field, provided, expected] of mismatches) {
|
|
153
|
+
if (provided != null && provided !== expected) {
|
|
154
|
+
throw new SupplierInvoiceServiceError("invalid_payable_state", `${field} must match the totals derived from supplier invoice lines (${expected})`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
92
158
|
/**
|
|
93
159
|
* Allocation invariants (§6.1):
|
|
94
160
|
* 1. One mode per invoice — either every allocation is whole-invoice
|
|
@@ -103,6 +169,15 @@ export function validateAllocations(params) {
|
|
|
103
169
|
const { invoiceTotalCents, lines, allocations } = params;
|
|
104
170
|
if (allocations.length === 0)
|
|
105
171
|
return { ok: true };
|
|
172
|
+
for (const allocation of allocations) {
|
|
173
|
+
if (allocation.amountCents < 0) {
|
|
174
|
+
return {
|
|
175
|
+
ok: false,
|
|
176
|
+
code: "invalid_payable_state",
|
|
177
|
+
message: "supplier invoice allocations must be greater than or equal to 0",
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
106
181
|
const hasLineLess = allocations.some((a) => a.supplierInvoiceLineId == null);
|
|
107
182
|
const hasPerLine = allocations.some((a) => a.supplierInvoiceLineId != null);
|
|
108
183
|
if (hasLineLess && hasPerLine) {
|
|
@@ -383,8 +458,10 @@ export const supplierInvoicesService = {
|
|
|
383
458
|
return loadSupplierInvoice(db, id);
|
|
384
459
|
},
|
|
385
460
|
async create(db, input, runtime = {}) {
|
|
461
|
+
validateHeaderTotals(input);
|
|
386
462
|
const lines = input.lines ?? [];
|
|
387
463
|
const allocations = input.allocations ?? [];
|
|
464
|
+
validateSupplierInvoiceLines(lines);
|
|
388
465
|
// Create-time allocations must be whole-invoice: new lines have no ids yet,
|
|
389
466
|
// so per-line allocation has to happen via setAllocations after create.
|
|
390
467
|
if (allocations.some((a) => a.supplierInvoiceLineId)) {
|
|
@@ -397,6 +474,8 @@ export const supplierInvoicesService = {
|
|
|
397
474
|
taxCents: input.taxCents ?? 0,
|
|
398
475
|
totalCents: input.totalCents ?? 0,
|
|
399
476
|
};
|
|
477
|
+
if (lines.length)
|
|
478
|
+
validateProvidedTotalsAgainstLines(input, totals);
|
|
400
479
|
const check = validateAllocations({
|
|
401
480
|
invoiceTotalCents: totals.totalCents,
|
|
402
481
|
lines: [],
|
|
@@ -404,6 +483,7 @@ export const supplierInvoicesService = {
|
|
|
404
483
|
});
|
|
405
484
|
if (!check.ok)
|
|
406
485
|
throw new SupplierInvoiceServiceError(check.code, check.message);
|
|
486
|
+
await assertSupplierReferenceExists(db, input.supplierId);
|
|
407
487
|
const fx = await snapshotSupplierInvoiceFx(db, {
|
|
408
488
|
currency: input.currency,
|
|
409
489
|
subtotalCents: totals.subtotalCents,
|
|
@@ -464,6 +544,8 @@ export const supplierInvoicesService = {
|
|
|
464
544
|
return created ? loadSupplierInvoice(db, created.id) : null;
|
|
465
545
|
},
|
|
466
546
|
async update(db, id, input, runtime = {}) {
|
|
547
|
+
validateHeaderTotals(input);
|
|
548
|
+
await assertSupplierReferenceExists(db, input.supplierId);
|
|
467
549
|
const set = { updatedAt: new Date() };
|
|
468
550
|
for (const key of [
|
|
469
551
|
"supplierId",
|
|
@@ -509,6 +591,9 @@ export const supplierInvoicesService = {
|
|
|
509
591
|
.limit(1);
|
|
510
592
|
if (current) {
|
|
511
593
|
const totalCents = input.totalCents ?? current.totalCents;
|
|
594
|
+
if (totalCents < current.paidCents) {
|
|
595
|
+
throw new SupplierInvoiceServiceError("invalid_payable_state", `supplier invoice total cannot be less than completed payments (${current.paidCents})`);
|
|
596
|
+
}
|
|
512
597
|
const fx = await snapshotSupplierInvoiceFx(db, {
|
|
513
598
|
currency: input.currency ?? current.currency,
|
|
514
599
|
subtotalCents: input.subtotalCents ?? current.subtotalCents,
|
|
@@ -557,6 +642,7 @@ export const supplierInvoicesService = {
|
|
|
557
642
|
* — re-set allocations after editing lines.
|
|
558
643
|
*/
|
|
559
644
|
async setLines(db, id, input, runtime = {}) {
|
|
645
|
+
validateSupplierInvoiceLines(input.lines);
|
|
560
646
|
const totals = recomputeTotalsFromLines(input.lines);
|
|
561
647
|
const updated = await db.transaction(async (tx) => {
|
|
562
648
|
const [invoice] = await tx
|
|
@@ -566,6 +652,9 @@ export const supplierInvoicesService = {
|
|
|
566
652
|
.limit(1);
|
|
567
653
|
if (!invoice)
|
|
568
654
|
return null;
|
|
655
|
+
if (totals.totalCents < invoice.paidCents) {
|
|
656
|
+
throw new SupplierInvoiceServiceError("invalid_payable_state", `supplier invoice total cannot be less than completed payments (${invoice.paidCents})`);
|
|
657
|
+
}
|
|
569
658
|
await tx.delete(supplierInvoiceLines).where(eq(supplierInvoiceLines.supplierInvoiceId, id));
|
|
570
659
|
if (input.lines.length) {
|
|
571
660
|
await tx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-supplier-payments.d.ts","sourceRoot":"","sources":["../src/service-supplier-payments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,qBAAqB,EACrB,kBAAkB,EAClB,wBAAwB,EACxB,0BAA0B,EAC3B,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"service-supplier-payments.d.ts","sourceRoot":"","sources":["../src/service-supplier-payments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,qBAAqB,EACrB,kBAAkB,EAClB,wBAAwB,EACxB,0BAA0B,EAC3B,MAAM,qBAAqB,CAAA;AAwF5B,eAAO,MAAM,6BAA6B;6BACT,kBAAkB,SAAS,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;8BAuE5E,kBAAkB,QAChB,0BAA0B,YACvB,qBAAqB;;;;;;;;;;;;;;;;;;;;8BA4D1B,kBAAkB,MAClB,MAAM,QACJ,0BAA0B,YACvB,qBAAqB;;;;;;;;;;;;;;;;;;;;CA8DjC,CAAA"}
|
|
@@ -1,4 +1,51 @@
|
|
|
1
|
-
import { and, appendActionLedgerMutation, asc, bookings, buildSupplierPaymentCreateActionLedgerInput, buildSupplierPaymentUpdateActionLedgerInput, desc, eq, gte, lte, recomputeSupplierInvoiceBalance, resolveFxMoneyBaseAmount, resolveSupplierPaymentUpdateData, sql, supplierInvoices, supplierPayments, } from "./service-shared.js";
|
|
1
|
+
import { and, appendActionLedgerMutation, asc, bookings, buildSupplierPaymentCreateActionLedgerInput, buildSupplierPaymentUpdateActionLedgerInput, desc, eq, gte, lte, recomputeSupplierInvoiceBalance, resolveFxMoneyBaseAmount, resolveSupplierPaymentUpdateData, sql, supplierInvoices, supplierPayments, toRows, } from "./service-shared.js";
|
|
2
|
+
import { SupplierInvoiceServiceError } from "./service-supplier-invoices.js";
|
|
3
|
+
function settlementAmountInInvoiceCurrency(payment, invoice) {
|
|
4
|
+
if (payment.status !== "completed")
|
|
5
|
+
return 0;
|
|
6
|
+
if (payment.currency === invoice.currency)
|
|
7
|
+
return payment.amountCents;
|
|
8
|
+
if (payment.baseCurrency === invoice.currency)
|
|
9
|
+
return payment.baseAmountCents ?? 0;
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
async function assertSupplierPaymentDoesNotOverpay(db, data, options = {}) {
|
|
13
|
+
if (!data.supplierInvoiceId || data.status !== "completed")
|
|
14
|
+
return;
|
|
15
|
+
const invoiceResult = await db.execute(sql `
|
|
16
|
+
SELECT id, currency, total_cents AS "totalCents"
|
|
17
|
+
FROM supplier_invoices
|
|
18
|
+
WHERE id = ${data.supplierInvoiceId}
|
|
19
|
+
FOR UPDATE
|
|
20
|
+
`);
|
|
21
|
+
const invoice = toRows(invoiceResult)[0] ?? null;
|
|
22
|
+
if (!invoice)
|
|
23
|
+
return;
|
|
24
|
+
const conditions = [
|
|
25
|
+
eq(supplierPayments.supplierInvoiceId, data.supplierInvoiceId),
|
|
26
|
+
eq(supplierPayments.status, "completed"),
|
|
27
|
+
];
|
|
28
|
+
if (options.excludePaymentId) {
|
|
29
|
+
conditions.push(sql `${supplierPayments.id} <> ${options.excludePaymentId}`);
|
|
30
|
+
}
|
|
31
|
+
const [agg] = await db
|
|
32
|
+
.select({
|
|
33
|
+
paid: sql `coalesce(sum(
|
|
34
|
+
case
|
|
35
|
+
when ${supplierPayments.currency} = ${invoice.currency} then ${supplierPayments.amountCents}
|
|
36
|
+
when ${supplierPayments.baseCurrency} = ${invoice.currency} then coalesce(${supplierPayments.baseAmountCents}, 0)
|
|
37
|
+
else 0
|
|
38
|
+
end
|
|
39
|
+
), 0)::int`,
|
|
40
|
+
})
|
|
41
|
+
.from(supplierPayments)
|
|
42
|
+
.where(and(...conditions));
|
|
43
|
+
const existingPaid = agg?.paid ?? 0;
|
|
44
|
+
const paymentAmount = settlementAmountInInvoiceCurrency(data, invoice);
|
|
45
|
+
if (existingPaid + paymentAmount > invoice.totalCents) {
|
|
46
|
+
throw new SupplierInvoiceServiceError("invalid_payable_state", `supplier invoice payment exceeds payable balance (${invoice.totalCents - existingPaid})`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
2
49
|
export const financeSupplierPaymentService = {
|
|
3
50
|
async listSupplierPayments(db, query) {
|
|
4
51
|
const conditions = [];
|
|
@@ -88,6 +135,7 @@ export const financeSupplierPaymentService = {
|
|
|
88
135
|
date: data.paymentDate,
|
|
89
136
|
});
|
|
90
137
|
const row = await db.transaction(async (tx) => {
|
|
138
|
+
await assertSupplierPaymentDoesNotOverpay(tx, paymentData);
|
|
91
139
|
const [created] = await tx
|
|
92
140
|
.insert(supplierPayments)
|
|
93
141
|
.values({ ...paymentData, paymentInstrumentId: paymentData.paymentInstrumentId ?? null })
|
|
@@ -114,7 +162,20 @@ export const financeSupplierPaymentService = {
|
|
|
114
162
|
const updateData = await resolveSupplierPaymentUpdateData(db, id, data, runtime);
|
|
115
163
|
if (!updateData)
|
|
116
164
|
return null;
|
|
165
|
+
const paymentAfterUpdate = {
|
|
166
|
+
amountCents: updateData.amountCents ?? existing.amountCents,
|
|
167
|
+
currency: updateData.currency ?? existing.currency,
|
|
168
|
+
baseCurrency: updateData.baseCurrency !== undefined ? updateData.baseCurrency : existing.baseCurrency,
|
|
169
|
+
baseAmountCents: updateData.baseAmountCents !== undefined
|
|
170
|
+
? updateData.baseAmountCents
|
|
171
|
+
: existing.baseAmountCents,
|
|
172
|
+
status: updateData.status ?? existing.status,
|
|
173
|
+
supplierInvoiceId: updateData.supplierInvoiceId !== undefined
|
|
174
|
+
? updateData.supplierInvoiceId
|
|
175
|
+
: existing.supplierInvoiceId,
|
|
176
|
+
};
|
|
117
177
|
const row = await db.transaction(async (tx) => {
|
|
178
|
+
await assertSupplierPaymentDoesNotOverpay(tx, paymentAfterUpdate, { excludePaymentId: id });
|
|
118
179
|
const [updated] = await tx
|
|
119
180
|
.update(supplierPayments)
|
|
120
181
|
.set({ ...updateData, updatedAt: new Date() })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voyant-travel/finance",
|
|
3
|
-
"version": "0.138.
|
|
3
|
+
"version": "0.138.7",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -102,13 +102,13 @@
|
|
|
102
102
|
"fflate": "^0.8.2",
|
|
103
103
|
"hono": "^4.12.25",
|
|
104
104
|
"zod": "^4.4.3",
|
|
105
|
-
"@voyant-travel/action-ledger": "^0.105.
|
|
106
|
-
"@voyant-travel/bookings": "^0.138.
|
|
105
|
+
"@voyant-travel/action-ledger": "^0.105.10",
|
|
106
|
+
"@voyant-travel/bookings": "^0.138.5",
|
|
107
107
|
"@voyant-travel/core": "^0.111.1",
|
|
108
108
|
"@voyant-travel/db": "^0.109.4",
|
|
109
109
|
"@voyant-travel/types": "^0.106.1",
|
|
110
|
-
"@voyant-travel/finance-contracts": "^0.105.
|
|
111
|
-
"@voyant-travel/hono": "^0.
|
|
110
|
+
"@voyant-travel/finance-contracts": "^0.105.9",
|
|
111
|
+
"@voyant-travel/hono": "^0.119.0",
|
|
112
112
|
"@voyant-travel/storage": "^0.106.0",
|
|
113
113
|
"@voyant-travel/utils": "^0.105.5"
|
|
114
114
|
},
|
|
@@ -137,10 +137,10 @@
|
|
|
137
137
|
]
|
|
138
138
|
},
|
|
139
139
|
"scripts": {
|
|
140
|
-
"typecheck": "tsc -p tsconfig.typecheck.json",
|
|
140
|
+
"typecheck": "NODE_OPTIONS=--max-old-space-size=4096 tsc -p tsconfig.typecheck.json",
|
|
141
141
|
"lint": "biome check src/",
|
|
142
142
|
"test": "vitest run",
|
|
143
|
-
"build": "tsc -p tsconfig.build.json",
|
|
143
|
+
"build": "NODE_OPTIONS=--max-old-space-size=4096 tsc -p tsconfig.build.json",
|
|
144
144
|
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
145
145
|
"migrate:vouchers": "node --experimental-strip-types --experimental-transform-types scripts/migrate-vouchers.ts",
|
|
146
146
|
"db:generate": "drizzle-kit generate --config=drizzle.migrations.config.ts --name=finance_baseline && node ../../scripts/migrations/guard-create-type.mjs ./migrations && node ../../scripts/migrations/ensure-extensions.mjs ./migrations"
|