@voyant-travel/finance 0.138.3 → 0.138.5

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.
@@ -37,6 +37,7 @@ export type CheckoutRouteRuntime = {
37
37
  listBookingReminderRuns?: CheckoutRoutesOptions["listBookingReminderRuns"];
38
38
  };
39
39
  export declare const CHECKOUT_ROUTE_RUNTIME_CONTAINER_KEY = "providers.finance.checkout.runtime";
40
+ export declare const CHECKOUT_ROUTE_RUNTIME_NOT_CONFIGURED_MESSAGE = "Checkout payment links require a configured checkout provider. Configure a Finance checkout runtime with a card payment starter before generating payment links.";
40
41
  export declare function createCheckoutRoutes(options?: CheckoutRoutesOptions): import("hono/hono-base").HonoBase<Env, {
41
42
  "/bookings/:bookingId/collection-plan": {
42
43
  $post: {
@@ -353,6 +354,17 @@ export declare function createCheckoutRoutes(options?: CheckoutRoutesOptions): i
353
354
  };
354
355
  outputFormat: "json";
355
356
  status: 201;
357
+ } | {
358
+ input: {
359
+ param: {
360
+ bookingId: string;
361
+ };
362
+ };
363
+ output: {
364
+ error: string;
365
+ };
366
+ outputFormat: "json";
367
+ status: 501;
356
368
  } | {
357
369
  input: {
358
370
  param: {
@@ -581,6 +593,13 @@ export declare function createCheckoutRoutes(options?: CheckoutRoutesOptions): i
581
593
  };
582
594
  outputFormat: "json";
583
595
  status: 201;
596
+ } | {
597
+ input: {};
598
+ output: {
599
+ error: string;
600
+ };
601
+ outputFormat: "json";
602
+ status: 501;
584
603
  } | {
585
604
  input: {};
586
605
  output: {
@@ -907,6 +926,17 @@ export declare function createCheckoutAdminRoutes(options?: CheckoutRoutesOption
907
926
  };
908
927
  outputFormat: "json";
909
928
  status: 201;
929
+ } | {
930
+ input: {
931
+ param: {
932
+ bookingId: string;
933
+ };
934
+ };
935
+ output: {
936
+ error: string;
937
+ };
938
+ outputFormat: "json";
939
+ status: 501;
910
940
  } | {
911
941
  input: {
912
942
  param: {
@@ -1135,6 +1165,13 @@ export declare function createCheckoutAdminRoutes(options?: CheckoutRoutesOption
1135
1165
  };
1136
1166
  outputFormat: "json";
1137
1167
  status: 201;
1168
+ } | {
1169
+ input: {};
1170
+ output: {
1171
+ error: string;
1172
+ };
1173
+ outputFormat: "json";
1174
+ status: 501;
1138
1175
  } | {
1139
1176
  input: {};
1140
1177
  output: {
@@ -1 +1 @@
1
- {"version":3,"file":"checkout-routes.d.ts","sourceRoot":"","sources":["../src/checkout-routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"checkout-routes.d.ts","sourceRoot":"","sources":["../src/checkout-routes.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,12 @@ import { Hono } from "hono";
4
4
  import { bootstrapCheckoutCollection, initiateCheckoutCollection, previewCheckoutCollection, } from "./checkout-service.js";
5
5
  import { bootstrapCheckoutCollectionSchema, checkoutReminderRunListQuerySchema, initiateCheckoutCollectionSchema, previewCheckoutCollectionSchema, } from "./checkout-validation.js";
6
6
  export const CHECKOUT_ROUTE_RUNTIME_CONTAINER_KEY = "providers.finance.checkout.runtime";
7
+ export const CHECKOUT_ROUTE_RUNTIME_NOT_CONFIGURED_MESSAGE = "Checkout payment links require a configured checkout provider. Configure a Finance checkout runtime with a card payment starter before generating payment links.";
8
+ class CheckoutRouteRuntimeNotConfiguredError extends Error {
9
+ constructor() {
10
+ super(CHECKOUT_ROUTE_RUNTIME_NOT_CONFIGURED_MESSAGE);
11
+ }
12
+ }
7
13
  function runtimeEnv(c) {
8
14
  return c.env;
9
15
  }
@@ -26,9 +32,8 @@ function attachCollectionRoutes(app, options) {
26
32
  // Pin the middleware to this module's Env so Hono doesn't intersect the
27
33
  // middleware's default VoyantBindings into the handlers' `c.env` type.
28
34
  const collectionIdempotency = () => idempotencyKey();
29
- function getRuntime(bindings, resolveFromContainer) {
30
- return (resolveFromContainer?.(CHECKOUT_ROUTE_RUNTIME_CONTAINER_KEY) ??
31
- buildCheckoutRouteRuntime(bindings, options));
35
+ function getRuntime(bindings, container) {
36
+ return resolveCheckoutRouteRuntime(bindings, options, container);
32
37
  }
33
38
  return (app
34
39
  // Mostly a read, but `ensureDefaultPaymentPlan` can materialize a
@@ -49,8 +54,10 @@ function attachCollectionRoutes(app, options) {
49
54
  })
50
55
  .post("/bookings/:bookingId/initiate-collection", collectionIdempotency(), async (c) => {
51
56
  try {
52
- const runtime = getRuntime(c.env, (key) => c.var.container?.resolve(key));
53
- const result = await initiateCheckoutCollection(c.get("db"), c.req.param("bookingId"), await parseJsonBody(c, initiateCheckoutCollectionSchema), options.policy, runtime);
57
+ const input = await parseJsonBody(c, initiateCheckoutCollectionSchema);
58
+ const runtime = getRuntime(c.env, c.var.container);
59
+ assertCheckoutRuntimeSupportsCollection(runtime, input);
60
+ const result = await initiateCheckoutCollection(c.get("db"), c.req.param("bookingId"), input, options.policy, runtime);
54
61
  if (!result) {
55
62
  return c.json({ error: "Booking not found" }, 404);
56
63
  }
@@ -61,13 +68,18 @@ function attachCollectionRoutes(app, options) {
61
68
  if (message.includes("Booking not found")) {
62
69
  return c.json({ error: message }, 404);
63
70
  }
71
+ if (error instanceof CheckoutRouteRuntimeNotConfiguredError) {
72
+ return c.json({ error: message }, 501);
73
+ }
64
74
  return c.json({ error: message }, 409);
65
75
  }
66
76
  })
67
77
  .post("/collections/bootstrap", collectionIdempotency(), async (c) => {
68
78
  try {
69
- const runtime = getRuntime(c.env, (key) => c.var.container?.resolve(key));
70
- const result = await bootstrapCheckoutCollection(c.get("db"), await parseJsonBody(c, bootstrapCheckoutCollectionSchema), options.policy, runtime);
79
+ const input = await parseJsonBody(c, bootstrapCheckoutCollectionSchema);
80
+ const runtime = getRuntime(c.env, c.var.container);
81
+ assertCheckoutRuntimeSupportsCollection(runtime, input);
82
+ const result = await bootstrapCheckoutCollection(c.get("db"), input, options.policy, runtime);
71
83
  if (!result) {
72
84
  return c.json({ error: "Booking session not found" }, 404);
73
85
  }
@@ -78,6 +90,9 @@ function attachCollectionRoutes(app, options) {
78
90
  if (message.includes("Booking not found")) {
79
91
  return c.json({ error: message }, 404);
80
92
  }
93
+ if (error instanceof CheckoutRouteRuntimeNotConfiguredError) {
94
+ return c.json({ error: message }, 501);
95
+ }
81
96
  return c.json({ error: message }, 409);
82
97
  }
83
98
  }));
@@ -91,8 +106,7 @@ export function createCheckoutRoutes(options = {}) {
91
106
  export function createCheckoutAdminRoutes(options = {}) {
92
107
  const app = new Hono().get("/bookings/:bookingId/reminder-runs", async (c) => {
93
108
  const query = parseQuery(c, checkoutReminderRunListQuerySchema);
94
- const runtime = c.var.container?.resolve(CHECKOUT_ROUTE_RUNTIME_CONTAINER_KEY) ??
95
- buildCheckoutRouteRuntime(c.env, options);
109
+ const runtime = resolveCheckoutRouteRuntime(c.env, options, c.var.container);
96
110
  if (!runtime.listBookingReminderRuns) {
97
111
  return c.json({ data: [], total: 0, limit: query.limit, offset: query.offset });
98
112
  }
@@ -100,6 +114,17 @@ export function createCheckoutAdminRoutes(options = {}) {
100
114
  });
101
115
  return attachCollectionRoutes(app, options);
102
116
  }
117
+ function resolveCheckoutRouteRuntime(bindings, options, container) {
118
+ if (container?.has(CHECKOUT_ROUTE_RUNTIME_CONTAINER_KEY)) {
119
+ return container.resolve(CHECKOUT_ROUTE_RUNTIME_CONTAINER_KEY);
120
+ }
121
+ return buildCheckoutRouteRuntime(bindings, options);
122
+ }
123
+ function assertCheckoutRuntimeSupportsCollection(runtime, input) {
124
+ if (input.method === "card" && Object.keys(runtime.paymentStarters).length === 0) {
125
+ throw new CheckoutRouteRuntimeNotConfiguredError();
126
+ }
127
+ }
103
128
  export function buildCheckoutRouteRuntime(bindings, options = {}) {
104
129
  return {
105
130
  bindings,
@@ -274,6 +274,29 @@ export declare const financeInvoiceCoreRoutes: OpenAPIHono<Env, import("hono/typ
274
274
  };
275
275
  outputFormat: "json";
276
276
  status: 400;
277
+ } | {
278
+ input: {
279
+ param: {
280
+ id: string;
281
+ };
282
+ } & {
283
+ json: {
284
+ creditNoteNumber: string;
285
+ amountCents: number;
286
+ currency: string;
287
+ reason: string;
288
+ status?: "draft" | "issued" | "applied" | undefined;
289
+ baseCurrency?: string | null | undefined;
290
+ baseAmountCents?: number | null | undefined;
291
+ fxRateSetId?: string | null | undefined;
292
+ notes?: string | null | undefined;
293
+ };
294
+ };
295
+ output: {
296
+ error: string;
297
+ };
298
+ outputFormat: "json";
299
+ status: 409;
277
300
  } | {
278
301
  input: {
279
302
  param: {
@@ -363,6 +386,30 @@ export declare const financeInvoiceCoreRoutes: OpenAPIHono<Env, import("hono/typ
363
386
  };
364
387
  outputFormat: "json";
365
388
  status: 400;
389
+ } | {
390
+ input: {
391
+ param: {
392
+ id: string;
393
+ creditNoteId: string;
394
+ };
395
+ } & {
396
+ json: {
397
+ creditNoteNumber?: string | undefined;
398
+ status?: "draft" | "issued" | "applied" | undefined;
399
+ amountCents?: number | undefined;
400
+ currency?: string | undefined;
401
+ baseCurrency?: string | null | undefined;
402
+ baseAmountCents?: number | null | undefined;
403
+ fxRateSetId?: string | null | undefined;
404
+ reason?: string | undefined;
405
+ notes?: string | null | undefined;
406
+ };
407
+ };
408
+ output: {
409
+ error: string;
410
+ };
411
+ outputFormat: "json";
412
+ status: 409;
366
413
  } | {
367
414
  input: {
368
415
  param: {
@@ -1005,6 +1052,43 @@ export declare const financeInvoiceCoreRoutes: OpenAPIHono<Env, import("hono/typ
1005
1052
  };
1006
1053
  outputFormat: "json";
1007
1054
  status: 400;
1055
+ } | {
1056
+ input: {
1057
+ param: {
1058
+ id: string;
1059
+ };
1060
+ } & {
1061
+ json: {
1062
+ invoiceNumber?: string | undefined;
1063
+ bookingId?: string | undefined;
1064
+ personId?: string | null | undefined;
1065
+ organizationId?: string | null | undefined;
1066
+ status?: "draft" | "pending_external_allocation" | "issued" | "partially_paid" | "paid" | "overdue" | "void" | undefined;
1067
+ currency?: string | undefined;
1068
+ baseCurrency?: string | null | undefined;
1069
+ fxRateSetId?: string | null | undefined;
1070
+ subtotalCents?: number | undefined;
1071
+ baseSubtotalCents?: number | null | undefined;
1072
+ taxCents?: number | undefined;
1073
+ baseTaxCents?: number | null | undefined;
1074
+ totalCents?: number | undefined;
1075
+ baseTotalCents?: number | null | undefined;
1076
+ paidCents?: number | undefined;
1077
+ basePaidCents?: number | null | undefined;
1078
+ balanceDueCents?: number | undefined;
1079
+ baseBalanceDueCents?: number | null | undefined;
1080
+ commissionPercent?: number | null | undefined;
1081
+ commissionAmountCents?: number | null | undefined;
1082
+ issueDate?: string | undefined;
1083
+ dueDate?: string | undefined;
1084
+ notes?: string | null | undefined;
1085
+ };
1086
+ };
1087
+ output: {
1088
+ error: string;
1089
+ };
1090
+ outputFormat: "json";
1091
+ status: 409;
1008
1092
  };
1009
1093
  };
1010
1094
  } & {
@@ -1068,6 +1152,17 @@ export declare const financeInvoiceCoreRoutes: OpenAPIHono<Env, import("hono/typ
1068
1152
  };
1069
1153
  outputFormat: "json";
1070
1154
  status: 400;
1155
+ } | {
1156
+ input: {
1157
+ param: {
1158
+ id: string;
1159
+ };
1160
+ };
1161
+ output: {
1162
+ error: string;
1163
+ };
1164
+ outputFormat: "json";
1165
+ status: 409;
1071
1166
  } | {
1072
1167
  input: {
1073
1168
  param: {
@@ -1115,17 +1210,6 @@ export declare const financeInvoiceCoreRoutes: OpenAPIHono<Env, import("hono/typ
1115
1210
  };
1116
1211
  outputFormat: "json";
1117
1212
  status: 200;
1118
- } | {
1119
- input: {
1120
- param: {
1121
- id: string;
1122
- };
1123
- };
1124
- output: {
1125
- error: string;
1126
- };
1127
- outputFormat: "json";
1128
- status: 409;
1129
1213
  };
1130
1214
  };
1131
1215
  } & {
@@ -1 +1 @@
1
- {"version":3,"file":"routes-invoice-core.d.ts","sourceRoot":"","sources":["../src/routes-invoice-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAA;AAe/D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AAgqB7C,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAQZ,CAAA"}
1
+ {"version":3,"file":"routes-invoice-core.d.ts","sourceRoot":"","sources":["../src/routes-invoice-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAA;AAe/D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AAgwB7C,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAQZ,CAAA"}
@@ -25,7 +25,7 @@ import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
25
25
  import { openApiValidationHook, parseOptionalJsonBody, requireUserId } from "@voyant-travel/hono";
26
26
  import { creditNoteLineItemSchema, creditNoteSchema, errorResponseSchema, financeNoteSchema, invoiceDetailSchema, invoiceLineItemSchema, invoiceSchema, paymentSchema, paymentSessionSchema, successResponseSchema, } from "./routes-invoice-schemas.js";
27
27
  import { getActionLedgerRequestContext, getFinanceRouteRuntime } from "./routes-runtime.js";
28
- import { financeService, PaymentValidationError } from "./service.js";
28
+ import { financeService, InvoiceNumberConflictError, InvoiceValidationError, PaymentValidationError, } from "./service.js";
29
29
  import { createPaymentSessionFromInvoiceSchema, insertCreditNoteLineItemSchema, insertCreditNoteSchema, insertFinanceNoteSchema, insertInvoiceLineItemSchema, insertPaymentSchema, updateCreditNoteSchema, updateInvoiceLineItemSchema, updateInvoiceSchema, voidInvoiceSchema, } from "./validation.js";
30
30
  const idParamSchema = z.object({ id: z.string() });
31
31
  const lineParamSchema = z.object({ id: z.string(), lineId: z.string() });
@@ -69,6 +69,10 @@ const updateInvoiceRoute = createRoute({
69
69
  description: "Invoice not found",
70
70
  content: { "application/json": { schema: errorResponseSchema } },
71
71
  },
72
+ 409: {
73
+ description: "Invoice number already exists",
74
+ content: { "application/json": { schema: errorResponseSchema } },
75
+ },
72
76
  },
73
77
  });
74
78
  const deleteInvoiceRoute = createRoute({
@@ -154,13 +158,28 @@ const invoiceActionRoutes = new OpenAPIHono({ defaultHook: openApiValidationHook
154
158
  return row ? c.json({ data: row }, 200) : c.json({ error: "Invoice not found" }, 404);
155
159
  })
156
160
  .openapi(updateInvoiceRoute, async (c) => {
157
- const runtime = getFinanceRouteRuntime(c);
158
- const row = await financeService.updateInvoice(c.get("db"), c.req.valid("param").id, c.req.valid("json"), {
159
- eventBus: runtime?.eventBus,
160
- actionLedgerContext: getActionLedgerRequestContext(c),
161
- actionLedgerAuthorizationSource: "finance.invoice.route",
162
- });
163
- return row ? c.json({ data: row }, 200) : c.json({ error: "Invoice not found" }, 404);
161
+ try {
162
+ const runtime = getFinanceRouteRuntime(c);
163
+ const row = await financeService.updateInvoice(c.get("db"), c.req.valid("param").id, c.req.valid("json"), {
164
+ eventBus: runtime?.eventBus,
165
+ actionLedgerContext: getActionLedgerRequestContext(c),
166
+ actionLedgerAuthorizationSource: "finance.invoice.route",
167
+ });
168
+ return row ? c.json({ data: row }, 200) : c.json({ error: "Invoice not found" }, 404);
169
+ }
170
+ catch (error) {
171
+ if (error instanceof InvoiceNumberConflictError) {
172
+ return c.json({
173
+ error: "Invoice number already exists",
174
+ code: error.code,
175
+ invoiceNumber: error.invoiceNumber,
176
+ }, 409);
177
+ }
178
+ if (error instanceof InvoiceValidationError) {
179
+ return c.json({ error: error.message, code: error.code, details: error.details }, error.status);
180
+ }
181
+ throw error;
182
+ }
164
183
  })
165
184
  .openapi(deleteInvoiceRoute, async (c) => {
166
185
  const runtime = getFinanceRouteRuntime(c);
@@ -304,22 +323,48 @@ const deleteInvoiceLineItemRoute = createRoute({
304
323
  const invoiceLineItemRoutes = new OpenAPIHono({ defaultHook: openApiValidationHook })
305
324
  .openapi(listInvoiceLineItemsRoute, async (c) => c.json({ data: await financeService.listInvoiceLineItems(c.get("db"), c.req.valid("param").id) }, 200))
306
325
  .openapi(createInvoiceLineItemRoute, async (c) => {
307
- const runtime = getFinanceRouteRuntime(c);
308
- const row = await financeService.createInvoiceLineItem(c.get("db"), c.req.valid("param").id, c.req.valid("json"), {
309
- eventBus: runtime?.eventBus,
310
- actionLedgerContext: getActionLedgerRequestContext(c),
311
- actionLedgerAuthorizationSource: "finance.invoice_line_item.route",
312
- });
313
- return row ? c.json({ data: row }, 201) : c.json({ error: "Invoice not found" }, 404);
326
+ try {
327
+ const runtime = getFinanceRouteRuntime(c);
328
+ const row = await financeService.createInvoiceLineItem(c.get("db"), c.req.valid("param").id, c.req.valid("json"), {
329
+ eventBus: runtime?.eventBus,
330
+ actionLedgerContext: getActionLedgerRequestContext(c),
331
+ actionLedgerAuthorizationSource: "finance.invoice_line_item.route",
332
+ });
333
+ return row ? c.json({ data: row }, 201) : c.json({ error: "Invoice not found" }, 404);
334
+ }
335
+ catch (error) {
336
+ if (error instanceof InvoiceValidationError) {
337
+ if (error.status === 404) {
338
+ return c.json({ error: error.message, code: error.code, details: error.details }, 404);
339
+ }
340
+ if (error.status === 409)
341
+ throw error;
342
+ return c.json({ error: error.message, code: error.code, details: error.details }, 400);
343
+ }
344
+ throw error;
345
+ }
314
346
  })
315
347
  .openapi(updateInvoiceLineItemRoute, async (c) => {
316
- const runtime = getFinanceRouteRuntime(c);
317
- const row = await financeService.updateInvoiceLineItem(c.get("db"), c.req.valid("param").lineId, c.req.valid("json"), {
318
- eventBus: runtime?.eventBus,
319
- actionLedgerContext: getActionLedgerRequestContext(c),
320
- actionLedgerAuthorizationSource: "finance.invoice_line_item.route",
321
- });
322
- return row ? c.json({ data: row }, 200) : c.json({ error: "Line item not found" }, 404);
348
+ try {
349
+ const runtime = getFinanceRouteRuntime(c);
350
+ const row = await financeService.updateInvoiceLineItem(c.get("db"), c.req.valid("param").lineId, c.req.valid("json"), {
351
+ eventBus: runtime?.eventBus,
352
+ actionLedgerContext: getActionLedgerRequestContext(c),
353
+ actionLedgerAuthorizationSource: "finance.invoice_line_item.route",
354
+ });
355
+ return row ? c.json({ data: row }, 200) : c.json({ error: "Line item not found" }, 404);
356
+ }
357
+ catch (error) {
358
+ if (error instanceof InvoiceValidationError) {
359
+ if (error.status === 404) {
360
+ return c.json({ error: error.message, code: error.code, details: error.details }, 404);
361
+ }
362
+ if (error.status === 409)
363
+ throw error;
364
+ return c.json({ error: error.message, code: error.code, details: error.details }, 400);
365
+ }
366
+ throw error;
367
+ }
323
368
  })
324
369
  .openapi(deleteInvoiceLineItemRoute, async (c) => {
325
370
  const runtime = getFinanceRouteRuntime(c);
@@ -428,6 +473,10 @@ const createCreditNoteRoute = createRoute({
428
473
  description: "Invoice not found",
429
474
  content: { "application/json": { schema: errorResponseSchema } },
430
475
  },
476
+ 409: {
477
+ description: "Credit note would exceed the invoice balance due",
478
+ content: { "application/json": { schema: errorResponseSchema } },
479
+ },
431
480
  },
432
481
  });
433
482
  const updateCreditNoteRoute = createRoute({
@@ -453,27 +502,59 @@ const updateCreditNoteRoute = createRoute({
453
502
  description: "Credit note not found",
454
503
  content: { "application/json": { schema: errorResponseSchema } },
455
504
  },
505
+ 409: {
506
+ description: "Credit note would exceed the invoice balance due",
507
+ content: { "application/json": { schema: errorResponseSchema } },
508
+ },
456
509
  },
457
510
  });
458
511
  const creditNoteRoutes = new OpenAPIHono({ defaultHook: openApiValidationHook })
459
512
  .openapi(listCreditNotesRoute, async (c) => c.json({ data: await financeService.listCreditNotes(c.get("db"), c.req.valid("param").id) }, 200))
460
513
  .openapi(createCreditNoteRoute, async (c) => {
461
- const runtime = getFinanceRouteRuntime(c);
462
- const row = await financeService.createCreditNote(c.get("db"), c.req.valid("param").id, c.req.valid("json"), {
463
- ...(runtime ?? {}),
464
- actionLedgerContext: getActionLedgerRequestContext(c),
465
- actionLedgerAuthorizationSource: "finance.credit_note.route",
466
- });
467
- return row ? c.json({ data: row }, 201) : c.json({ error: "Invoice not found" }, 404);
514
+ try {
515
+ const runtime = getFinanceRouteRuntime(c);
516
+ const row = await financeService.createCreditNote(c.get("db"), c.req.valid("param").id, c.req.valid("json"), {
517
+ ...(runtime ?? {}),
518
+ actionLedgerContext: getActionLedgerRequestContext(c),
519
+ actionLedgerAuthorizationSource: "finance.credit_note.route",
520
+ });
521
+ return row ? c.json({ data: row }, 201) : c.json({ error: "Invoice not found" }, 404);
522
+ }
523
+ catch (error) {
524
+ if (error instanceof InvoiceValidationError) {
525
+ if (error.status === 404) {
526
+ return c.json({ error: error.message, code: error.code, details: error.details }, 404);
527
+ }
528
+ if (error.status === 409) {
529
+ return c.json({ error: error.message, code: error.code, details: error.details }, 409);
530
+ }
531
+ return c.json({ error: error.message, code: error.code, details: error.details }, 400);
532
+ }
533
+ throw error;
534
+ }
468
535
  })
469
536
  .openapi(updateCreditNoteRoute, async (c) => {
470
- const runtime = getFinanceRouteRuntime(c);
471
- const row = await financeService.updateCreditNote(c.get("db"), c.req.valid("param").creditNoteId, c.req.valid("json"), {
472
- ...(runtime ?? {}),
473
- actionLedgerContext: getActionLedgerRequestContext(c),
474
- actionLedgerAuthorizationSource: "finance.credit_note.route",
475
- });
476
- return row ? c.json({ data: row }, 200) : c.json({ error: "Credit note not found" }, 404);
537
+ try {
538
+ const runtime = getFinanceRouteRuntime(c);
539
+ const row = await financeService.updateCreditNote(c.get("db"), c.req.valid("param").creditNoteId, c.req.valid("json"), {
540
+ ...(runtime ?? {}),
541
+ actionLedgerContext: getActionLedgerRequestContext(c),
542
+ actionLedgerAuthorizationSource: "finance.credit_note.route",
543
+ });
544
+ return row ? c.json({ data: row }, 200) : c.json({ error: "Credit note not found" }, 404);
545
+ }
546
+ catch (error) {
547
+ if (error instanceof InvoiceValidationError) {
548
+ if (error.status === 404) {
549
+ return c.json({ error: error.message, code: error.code, details: error.details }, 404);
550
+ }
551
+ if (error.status === 409) {
552
+ return c.json({ error: error.message, code: error.code, details: error.details }, 409);
553
+ }
554
+ return c.json({ error: error.message, code: error.code, details: error.details }, 400);
555
+ }
556
+ throw error;
557
+ }
477
558
  });
478
559
  // --- credit note line items ------------------------------------------------
479
560
  const listCreditNoteLineItemsRoute = createRoute({
@@ -519,13 +600,26 @@ const creditNoteLineItemRoutes = new OpenAPIHono({ defaultHook: openApiValidatio
519
600
  data: await financeService.listCreditNoteLineItems(c.get("db"), c.req.valid("param").creditNoteId),
520
601
  }, 200))
521
602
  .openapi(createCreditNoteLineItemRoute, async (c) => {
522
- const runtime = getFinanceRouteRuntime(c);
523
- const row = await financeService.createCreditNoteLineItem(c.get("db"), c.req.valid("param").creditNoteId, c.req.valid("json"), {
524
- eventBus: runtime?.eventBus,
525
- actionLedgerContext: getActionLedgerRequestContext(c),
526
- actionLedgerAuthorizationSource: "finance.credit_note_line_item.route",
527
- });
528
- return row ? c.json({ data: row }, 201) : c.json({ error: "Credit note not found" }, 404);
603
+ try {
604
+ const runtime = getFinanceRouteRuntime(c);
605
+ const row = await financeService.createCreditNoteLineItem(c.get("db"), c.req.valid("param").creditNoteId, c.req.valid("json"), {
606
+ eventBus: runtime?.eventBus,
607
+ actionLedgerContext: getActionLedgerRequestContext(c),
608
+ actionLedgerAuthorizationSource: "finance.credit_note_line_item.route",
609
+ });
610
+ return row ? c.json({ data: row }, 201) : c.json({ error: "Credit note not found" }, 404);
611
+ }
612
+ catch (error) {
613
+ if (error instanceof InvoiceValidationError) {
614
+ if (error.status === 404) {
615
+ return c.json({ error: error.message, code: error.code, details: error.details }, 404);
616
+ }
617
+ if (error.status === 409)
618
+ throw error;
619
+ return c.json({ error: error.message, code: error.code, details: error.details }, 400);
620
+ }
621
+ throw error;
622
+ }
529
623
  });
530
624
  // --- finance notes ---------------------------------------------------------
531
625
  const listNotesRoute = createRoute({
@@ -1 +1 @@
1
- {"version":3,"file":"routes-invoice-issue.d.ts","sourceRoot":"","sources":["../src/routes-invoice-issue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAA;AAc/D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AA6G7C,eAAO,MAAM,yBAAyB,2BAEpC,CAAA"}
1
+ {"version":3,"file":"routes-invoice-issue.d.ts","sourceRoot":"","sources":["../src/routes-invoice-issue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAA;AAc/D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AAsH7C,eAAO,MAAM,yBAAyB,2BAEpC,CAAA"}
@@ -18,7 +18,7 @@ import { openApiValidationHook, parseJsonBody } from "@voyant-travel/hono";
18
18
  import { listResponseSchema } from "@voyant-travel/types";
19
19
  import { errorResponseSchema, invoiceListItemSchema, invoiceSchema, } from "./routes-invoice-schemas.js";
20
20
  import { getActionLedgerRequestContext, getFinanceRouteRuntime, routeIdempotencyKey, } from "./routes-runtime.js";
21
- import { financeService, InvoiceFromBookingValidationError, InvoiceNumberAllocationError, InvoiceNumberConflictError, } from "./service.js";
21
+ import { financeService, InvoiceFromBookingValidationError, InvoiceNumberAllocationError, InvoiceNumberConflictError, InvoiceValidationError, } from "./service.js";
22
22
  import { insertInvoiceSchema, invoiceFromBookingSchema, invoiceListQuerySchema, } from "./validation.js";
23
23
  const idParamSchema = z.object({ id: z.string() });
24
24
  const listInvoicesRoute = createRoute({
@@ -50,6 +50,14 @@ const createInvoiceRoute = createRoute({
50
50
  description: "invalid_request: request body failed validation",
51
51
  content: { "application/json": { schema: errorResponseSchema } },
52
52
  },
53
+ 404: {
54
+ description: "Booking, person, or organization reference not found",
55
+ content: { "application/json": { schema: errorResponseSchema } },
56
+ },
57
+ 409: {
58
+ description: "Invoice number already exists",
59
+ content: { "application/json": { schema: errorResponseSchema } },
60
+ },
53
61
  },
54
62
  });
55
63
  const issueInvoiceFromBookingRoute = createRoute({
@@ -126,7 +134,24 @@ financeInvoiceIssueRoutes.use("/invoices/from-booking", postOnly(routeIdempotenc
126
134
  })));
127
135
  financeInvoiceIssueRoutes
128
136
  .openapi(listInvoicesRoute, async (c) => c.json(await financeService.listInvoices(c.get("db"), c.req.valid("query")), 200))
129
- .openapi(createInvoiceRoute, async (c) => c.json({ data: (await financeService.createInvoice(c.get("db"), c.req.valid("json"))) ?? null }, 201))
137
+ .openapi(createInvoiceRoute, async (c) => {
138
+ try {
139
+ return c.json({ data: (await financeService.createInvoice(c.get("db"), c.req.valid("json"))) ?? null }, 201);
140
+ }
141
+ catch (error) {
142
+ if (error instanceof InvoiceNumberConflictError) {
143
+ return c.json({
144
+ error: "Invoice number already exists",
145
+ code: error.code,
146
+ invoiceNumber: error.invoiceNumber,
147
+ }, 409);
148
+ }
149
+ if (error instanceof InvoiceValidationError) {
150
+ return c.json({ error: error.message, code: error.code, details: error.details }, error.status);
151
+ }
152
+ throw error;
153
+ }
154
+ })
130
155
  .openapi(issueInvoiceFromBookingRoute, async (c) => {
131
156
  const input = c.req.valid("json");
132
157
  const db = c.get("db");