@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.
@@ -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;AAgP7C,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAEG,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"}
@@ -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
- const runtime = getFinanceRouteRuntime(c);
212
- const row = await financeService.createSupplierPayment(c.get("db"), c.req.valid("json"), {
213
- ...(runtime ?? {}),
214
- actionLedgerContext: getActionLedgerRequestContext(c),
215
- actionLedgerAuthorizationSource: "finance.supplier_payment.route",
216
- });
217
- if (!row) {
218
- throw new Error("Failed to create supplier payment");
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
- const runtime = getFinanceRouteRuntime(c);
224
- const row = await financeService.updateSupplierPayment(c.get("db"), c.req.valid("param").id, c.req.valid("json"), {
225
- ...(runtime ?? {}),
226
- actionLedgerContext: getActionLedgerRequestContext(c),
227
- actionLedgerAuthorizationSource: "finance.supplier_payment.route",
228
- });
229
- return row ? c.json({ data: row }, 200) : c.json({ error: "Supplier payment not found" }, 404);
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;AAm7BvD,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAQL,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) => c.json({ data: created(await financeService.createTaxPolicyRule(c.get("db"), c.req.valid("json"))) }, 201))
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
- const row = await financeService.updateTaxPolicyRule(c.get("db"), c.req.valid("param").id, c.req.valid("json"));
814
- return row ? c.json({ data: row }, 200) : notFound(c, "Tax policy rule not found");
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;AAksB7C,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAGY,CAAA;AAE9C,MAAM,MAAM,qBAAqB,GAAG,OAAO,qBAAqB,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: "Edit would over-allocate a surviving whole-invoice allocation (§6.1)",
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
- return handleSupplierInvoiceError(c, error);
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
- const row = await supplierInvoicesService.update(c.get("db"), c.req.valid("param").id, c.req.valid("json"), apRuntime(c));
426
- if (!row)
427
- return c.json({ error: "Supplier invoice not found" }, 404);
428
- return c.json({ data: row }, 200);
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
- return handleSupplierInvoiceError(c, error);
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
- const row = await financeService.createSupplierPayment(c.get("db"), { ...c.req.valid("json"), supplierInvoiceId: c.req.valid("param").id }, {
520
- ...getFinanceRouteRuntime(c),
521
- actionLedgerContext: getActionLedgerRequestContext(c),
522
- actionLedgerAuthorizationSource: "finance.supplier_payment.route",
523
- });
524
- return c.json({ data: row }, 201);
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: {
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAwBa,CAAA;AAEvC,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAA;AAChD,MAAM,MAAM,mBAAmB,GAAG,OAAO,mBAAmB,CAAA"}
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;AA+BD,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;;;;;;;;;;;;;4BAiBlD,kBAAkB,MAAM,MAAM,QAAQ,wBAAwB;;;;;;;;;;;;;4BAS9D,kBAAkB,MAAM,MAAM;;;gCAY1B,kBAAkB,aAAa,MAAM;;;;;;;;;;;;;;IAQvE;;;;OAIG;mCAEG,kBAAkB,aACX,MAAM,QACX,6BAA6B;;;;;;;;;;;;;;iCAkDF,kBAAkB,MAAM,MAAM;;;CAOlE,CAAA"}
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":"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"}
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;AASpB,OAAO,KAAK,EACV,qCAAqC,EACrC,2BAA2B,EAC3B,gCAAgC,EAChC,6BAA6B,EAE7B,8BAA8B,EAC9B,8BAA8B,EAC9B,2BAA2B,EAC5B,MAAM,iBAAiB,CAAA;AAExB,KAAK,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAA;AAC7E,KAAK,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAA;AAC7E,KAAK,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAA;AAC9E,KAAK,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAA;AAE9E,KAAK,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAA;AAClE,KAAK,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAA;AAC3E,KAAK,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qCAAqC,CAAC,CAAA;AAElF,MAAM,MAAM,wBAAwB,GAChC,4BAA4B,GAC5B,uBAAuB,GACvB,wBAAwB,GACxB,gBAAgB,GAChB,yBAAyB,GACzB,6BAA6B,CAAA;AAEjC;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IAElD,QAAQ,CAAC,IAAI,EAAE,wBAAwB;gBAA9B,IAAI,EAAE,wBAAwB,EACvC,OAAO,CAAC,EAAE,MAAM;CAKnB;AAED,MAAM,WAAW,6BAA8B,SAAQ,gBAAgB;IACrE,mBAAmB,CAAC,EAAE,gCAAgC,CAAA;IACtD,+BAA+B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAChD;AAsFD,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,SAAS,wBAAwB,EAAE,GACzC,aAAa,CAQf;AAWD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM,CAEvF;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,SAAS,wBAAwB,EAAE,QAqBtF;AAwED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,wBAAwB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAElE;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,iBAAiB,EAAE,MAAM,CAAA;IACzB,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAA;IACrC,WAAW,EAAE,SAAS,oBAAoB,EAAE,CAAA;CAC7C,GAAG,qBAAqB,CA4DxB;AAED,KAAK,qBAAqB,GAAG,CAAC,OAAO,gBAAgB,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,CAAA;AAE7E;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,qBAAqB,EAC9B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,qBAAqB,CAMvB;AAED;;;;;GAKG;AACH,wBAAsB,+BAA+B,CACnD,EAAE,EAAE,kBAAkB,EACtB,iBAAiB,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAuC1B;AA2JD,eAAO,MAAM,uBAAuB;aACnB,kBAAkB,SAAS,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBA6DhD,kBAAkB,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAK1C,kBAAkB,SACf,0BAA0B,YACxB,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eA+GlC,kBAAkB,MAClB,MAAM,SACH,0BAA0B,YACxB,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAgHxC;;;;OAIG;iBAEG,kBAAkB,MAClB,MAAM,SACH,aAAa,YACX,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA2FxC;;;OAGG;uBAEG,kBAAkB,MAClB,MAAM,SACH,mBAAmB,YACjB,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAkDxC,2EAA2E;mBAErE,kBAAkB,MAClB,MAAM,YACD,6BAA6B;;;wBAgCpB,kBAAkB,qBAAqB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAQrC,kBAAkB,gBAAgB,MAAM;;;;;;;;;;;;yBAU9D,kBAAkB,qBACH,MAAM,SAClB,qBAAqB;;;;;;;;;;;;yBAyBH,kBAAkB,qBAAqB,MAAM,gBAAgB,MAAM;;;CAiB/F,CAAA"}
@@ -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;AAoB5B,eAAO,MAAM,6BAA6B;6BACT,kBAAkB,SAAS,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;8BAuE5E,kBAAkB,QAChB,0BAA0B,YACvB,qBAAqB;;;;;;;;;;;;;;;;;;;;8BA2D1B,kBAAkB,MAClB,MAAM,QACJ,0BAA0B,YACvB,qBAAqB;;;;;;;;;;;;;;;;;;;;CA6CjC,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.5",
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.9",
106
- "@voyant-travel/bookings": "^0.138.3",
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.8",
111
- "@voyant-travel/hono": "^0.118.4",
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"