@voyantjs/finance 0.46.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -59,6 +59,20 @@ The event does not include rendered document bodies or signed download URLs.
59
59
  Subscriber failures do not roll back the rendition write; use a durable job or
60
60
  workflow when a downstream reaction needs retries.
61
61
 
62
+ ## Customer-Safe Document Lookup
63
+
64
+ Public finance routes include booking-scoped document lookup for customer
65
+ portal and checkout surfaces:
66
+
67
+ - `GET /v1/public/finance/bookings/:bookingId/documents`
68
+ - `GET /v1/public/finance/bookings/:bookingId/documents/by-reference?reference=...`
69
+ - `GET /v1/public/finance/documents/by-reference?reference=...`
70
+
71
+ Booking-scoped routes require a checkout capability for the requested booking.
72
+ The by-reference variant resolves invoice numbers and payment reference numbers
73
+ only inside that booking, so a valid capability for one booking cannot retrieve
74
+ documents from another booking.
75
+
62
76
  ## Exports
63
77
 
64
78
  | Entry | Description |
@@ -194,6 +194,52 @@ export declare function createPublicFinanceRoutes(options?: PublicFinanceRouteOp
194
194
  status: import("hono/utils/http-status").ContentfulStatusCode;
195
195
  };
196
196
  };
197
+ } & {
198
+ "/bookings/:bookingId/documents/by-reference": {
199
+ $get: {
200
+ input: {
201
+ param: {
202
+ bookingId: string;
203
+ };
204
+ };
205
+ output: {
206
+ error: string;
207
+ };
208
+ outputFormat: "json";
209
+ status: 404;
210
+ } | {
211
+ input: {
212
+ param: {
213
+ bookingId: string;
214
+ };
215
+ };
216
+ output: {
217
+ data: {
218
+ invoiceId: string;
219
+ invoiceNumber: string;
220
+ invoiceType: "invoice" | "proforma" | "credit_note";
221
+ invoiceStatus: "void" | "draft" | "sent" | "partially_paid" | "paid" | "overdue";
222
+ currency: string;
223
+ totalCents: number;
224
+ paidCents: number;
225
+ balanceDueCents: number;
226
+ issueDate: string;
227
+ dueDate: string;
228
+ renditionId: string | null;
229
+ documentStatus: "pending" | "failed" | "ready" | "stale" | "missing";
230
+ format: "json" | "pdf" | "html" | "xml" | null;
231
+ language: string | null;
232
+ generatedAt: string | null;
233
+ fileSize: number | null;
234
+ checksum: string | null;
235
+ downloadUrl: string | null;
236
+ bookingId: string;
237
+ };
238
+ };
239
+ outputFormat: "json";
240
+ status: import("hono/utils/http-status").ContentfulStatusCode;
241
+ };
242
+ };
197
243
  } & {
198
244
  "/bookings/:bookingId/payments": {
199
245
  $get: {
@@ -750,6 +796,52 @@ export declare const publicFinanceRoutes: import("hono/hono-base").HonoBase<Env,
750
796
  status: import("hono/utils/http-status").ContentfulStatusCode;
751
797
  };
752
798
  };
799
+ } & {
800
+ "/bookings/:bookingId/documents/by-reference": {
801
+ $get: {
802
+ input: {
803
+ param: {
804
+ bookingId: string;
805
+ };
806
+ };
807
+ output: {
808
+ error: string;
809
+ };
810
+ outputFormat: "json";
811
+ status: 404;
812
+ } | {
813
+ input: {
814
+ param: {
815
+ bookingId: string;
816
+ };
817
+ };
818
+ output: {
819
+ data: {
820
+ invoiceId: string;
821
+ invoiceNumber: string;
822
+ invoiceType: "invoice" | "proforma" | "credit_note";
823
+ invoiceStatus: "void" | "draft" | "sent" | "partially_paid" | "paid" | "overdue";
824
+ currency: string;
825
+ totalCents: number;
826
+ paidCents: number;
827
+ balanceDueCents: number;
828
+ issueDate: string;
829
+ dueDate: string;
830
+ renditionId: string | null;
831
+ documentStatus: "pending" | "failed" | "ready" | "stale" | "missing";
832
+ format: "json" | "pdf" | "html" | "xml" | null;
833
+ language: string | null;
834
+ generatedAt: string | null;
835
+ fileSize: number | null;
836
+ checksum: string | null;
837
+ downloadUrl: string | null;
838
+ bookingId: string;
839
+ };
840
+ };
841
+ outputFormat: "json";
842
+ status: import("hono/utils/http-status").ContentfulStatusCode;
843
+ };
844
+ };
753
845
  } & {
754
846
  "/bookings/:bookingId/payments": {
755
847
  $get: {
@@ -1 +1 @@
1
- {"version":3,"file":"routes-public.d.ts","sourceRoot":"","sources":["../src/routes-public.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,GAAG,EAA2B,MAAM,oBAAoB,CAAA;AAStE,MAAM,WAAW,yBAAyB;IACxC,0BAA0B,CAAC,EAAE,CAC3B,QAAQ,EAAE,OAAO,EACjB,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAA;CAC5C;AA+CD,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,yBAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gDA0IhF;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+CAA8B,CAAA;AAE9D,MAAM,MAAM,mBAAmB,GAAG,OAAO,mBAAmB,CAAA"}
1
+ {"version":3,"file":"routes-public.d.ts","sourceRoot":"","sources":["../src/routes-public.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,GAAG,EAA2B,MAAM,oBAAoB,CAAA;AAStE,MAAM,WAAW,yBAAyB;IACxC,0BAA0B,CAAC,EAAE,CAC3B,QAAQ,EAAE,OAAO,EACjB,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAA;CAC5C;AA+CD,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,yBAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gDAwJhF;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+CAA8B,CAAA;AAE9D,MAAM,MAAM,mBAAmB,GAAG,OAAO,mBAAmB,CAAA"}
@@ -63,6 +63,13 @@ export function createPublicFinanceRoutes(options = {}) {
63
63
  resolveDocumentDownloadUrl: (storageKey) => resolveDocumentDownloadUrl(c.env, storageKey),
64
64
  });
65
65
  return documents ? c.json({ data: documents }) : notFound(c, "Booking documents not found");
66
+ })
67
+ .get("/bookings/:bookingId/documents/by-reference", async (c) => {
68
+ await requireBookingCheckoutCapability(c, c.req.param("bookingId"), "payment:read");
69
+ const document = await publicFinanceService.getBookingDocumentByReference(c.get("db"), c.req.param("bookingId"), parseQuery(c, publicFinanceDocumentLookupQuerySchema).reference, {
70
+ resolveDocumentDownloadUrl: (storageKey) => resolveDocumentDownloadUrl(c.env, storageKey),
71
+ });
72
+ return document ? c.json({ data: document }) : notFound(c, "Finance document not found");
66
73
  })
67
74
  .get("/bookings/:bookingId/payments", async (c) => {
68
75
  await requireBookingCheckoutCapability(c, c.req.param("bookingId"), "payment:read");
@@ -6,6 +6,7 @@ export interface PublicFinanceRuntimeOptions {
6
6
  export declare const publicFinanceService: {
7
7
  getBookingDocuments(db: PostgresJsDatabase, bookingId: string, runtime?: PublicFinanceRuntimeOptions): Promise<PublicBookingFinanceDocuments | null>;
8
8
  getDocumentByReference(db: PostgresJsDatabase, reference: string, runtime?: PublicFinanceRuntimeOptions): Promise<PublicFinanceDocumentLookup | null>;
9
+ getBookingDocumentByReference(db: PostgresJsDatabase, bookingId: string, reference: string, runtime?: PublicFinanceRuntimeOptions): Promise<PublicFinanceDocumentLookup | null>;
9
10
  getBookingPaymentOptions(db: PostgresJsDatabase, bookingId: string, query: PublicPaymentOptionsQuery): Promise<{
10
11
  bookingId: string;
11
12
  accounts: {
@@ -1 +1 @@
1
- {"version":3,"file":"service-public.d.ts","sourceRoot":"","sources":["../src/service-public.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAYjE,OAAO,KAAK,EACV,6BAA6B,EAC7B,4BAA4B,EAE5B,2BAA2B,EAC3B,yBAAyB,EACzB,8BAA8B,EAC9B,0BAA0B,EAC3B,MAAM,wBAAwB,CAAA;AAE/B,MAAM,WAAW,2BAA2B;IAC1C,0BAA0B,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAA;CAC5F;AA+HD,eAAO,MAAM,oBAAoB;4BAEzB,kBAAkB,aACX,MAAM,YACR,2BAA2B,GACnC,OAAO,CAAC,6BAA6B,GAAG,IAAI,CAAC;+BA6C1C,kBAAkB,aACX,MAAM,YACR,2BAA2B,GACnC,OAAO,CAAC,2BAA2B,GAAG,IAAI,CAAC;iCA0CxC,kBAAkB,aACX,MAAM,SACV,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAoH5B,kBAAkB,aACX,MAAM,GAChB,OAAO,CAAC,4BAA4B,GAAG,IAAI,CAAC;0BA2DnB,kBAAkB,aAAa,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAKnC,kBAAkB,aAAa,MAAM;2CAW7D,kBAAkB,aACX,MAAM,cACL,MAAM,SACX,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;4CA4BjC,kBAAkB,aACX,MAAM,eACJ,MAAM,SACZ,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCAuBjC,kBAAkB,aACX,MAAM,SACV,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAMb,kBAAkB,SAAS,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsEhF,CAAA"}
1
+ {"version":3,"file":"service-public.d.ts","sourceRoot":"","sources":["../src/service-public.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAYjE,OAAO,KAAK,EACV,6BAA6B,EAC7B,4BAA4B,EAE5B,2BAA2B,EAC3B,yBAAyB,EACzB,8BAA8B,EAC9B,0BAA0B,EAC3B,MAAM,wBAAwB,CAAA;AAE/B,MAAM,WAAW,2BAA2B;IAC1C,0BAA0B,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAA;CAC5F;AA+HD,eAAO,MAAM,oBAAoB;4BAEzB,kBAAkB,aACX,MAAM,YACR,2BAA2B,GACnC,OAAO,CAAC,6BAA6B,GAAG,IAAI,CAAC;+BA6C1C,kBAAkB,aACX,MAAM,YACR,2BAA2B,GACnC,OAAO,CAAC,2BAA2B,GAAG,IAAI,CAAC;sCA0CxC,kBAAkB,aACX,MAAM,aACN,MAAM,YACR,2BAA2B,GACnC,OAAO,CAAC,2BAA2B,GAAG,IAAI,CAAC;iCA+CxC,kBAAkB,aACX,MAAM,SACV,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAoH5B,kBAAkB,aACX,MAAM,GAChB,OAAO,CAAC,4BAA4B,GAAG,IAAI,CAAC;0BA2DnB,kBAAkB,aAAa,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAKnC,kBAAkB,aAAa,MAAM;2CAW7D,kBAAkB,aACX,MAAM,cACL,MAAM,SACX,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;4CA4BjC,kBAAkB,aACX,MAAM,eACJ,MAAM,SACZ,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCAuBjC,kBAAkB,aACX,MAAM,SACV,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAMb,kBAAkB,SAAS,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsEhF,CAAA"}
@@ -172,6 +172,46 @@ export const publicFinanceService = {
172
172
  ...(await mapInvoiceDocument(invoice, renditions, runtime)),
173
173
  };
174
174
  },
175
+ async getBookingDocumentByReference(db, bookingId, reference, runtime = {}) {
176
+ const [invoiceMatch, paymentMatch] = await Promise.all([
177
+ db
178
+ .select()
179
+ .from(invoices)
180
+ .where(and(eq(invoices.bookingId, bookingId), eq(invoices.invoiceNumber, reference)))
181
+ .orderBy(desc(invoices.createdAt))
182
+ .limit(1),
183
+ db
184
+ .select({
185
+ invoiceId: payments.invoiceId,
186
+ })
187
+ .from(payments)
188
+ .innerJoin(invoices, eq(payments.invoiceId, invoices.id))
189
+ .where(and(eq(invoices.bookingId, bookingId), eq(payments.referenceNumber, reference)))
190
+ .orderBy(desc(payments.createdAt))
191
+ .limit(1),
192
+ ]);
193
+ const invoiceId = invoiceMatch[0]?.id ?? paymentMatch[0]?.invoiceId ?? null;
194
+ if (!invoiceId) {
195
+ return null;
196
+ }
197
+ const [invoice] = await db
198
+ .select()
199
+ .from(invoices)
200
+ .where(and(eq(invoices.id, invoiceId), eq(invoices.bookingId, bookingId)))
201
+ .limit(1);
202
+ if (!invoice?.bookingId) {
203
+ return null;
204
+ }
205
+ const renditions = await db
206
+ .select()
207
+ .from(invoiceRenditions)
208
+ .where(eq(invoiceRenditions.invoiceId, invoice.id))
209
+ .orderBy(desc(invoiceRenditions.createdAt));
210
+ return {
211
+ bookingId: invoice.bookingId,
212
+ ...(await mapInvoiceDocument(invoice, renditions, runtime)),
213
+ };
214
+ },
175
215
  async getBookingPaymentOptions(db, bookingId, query) {
176
216
  const [booking] = await db
177
217
  .select({ id: bookings.id })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyantjs/finance",
3
- "version": "0.46.0",
3
+ "version": "0.47.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -39,12 +39,12 @@
39
39
  "drizzle-orm": "^0.45.2",
40
40
  "hono": "^4.12.10",
41
41
  "zod": "^4.3.6",
42
- "@voyantjs/bookings": "0.46.0",
43
- "@voyantjs/core": "0.46.0",
44
- "@voyantjs/db": "0.46.0",
45
- "@voyantjs/hono": "0.46.0",
46
- "@voyantjs/utils": "0.46.0",
47
- "@voyantjs/storage": "0.46.0"
42
+ "@voyantjs/bookings": "0.47.0",
43
+ "@voyantjs/core": "0.47.0",
44
+ "@voyantjs/db": "0.47.0",
45
+ "@voyantjs/hono": "0.47.0",
46
+ "@voyantjs/utils": "0.47.0",
47
+ "@voyantjs/storage": "0.47.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "typescript": "^6.0.2",