@voyant-travel/finance 0.158.0 → 0.161.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.
@@ -20,6 +20,84 @@ const TAX_REGIME_CODES = new Set([
20
20
  const ISSUED_EVENT = "invoice.issued";
21
21
  const PROFORMA_ISSUED_EVENT = "invoice.proforma.issued";
22
22
  const PROFORMA_CONVERTED_EVENT = "invoice.proforma.converted";
23
+ /**
24
+ * Package-owned invoice-from-booking composer shared by HTTP routes and Tools.
25
+ * It owns the cross-module projection read so transport adapters never reach
26
+ * into booking or finance tables themselves.
27
+ */
28
+ export async function issueInvoiceFromBookingCommand(db, input, runtime = {}) {
29
+ const [booking] = await db
30
+ .select()
31
+ .from(bookings)
32
+ .where(eq(bookings.id, input.bookingId))
33
+ .limit(1);
34
+ if (!booking)
35
+ return { status: "booking_not_found" };
36
+ const items = await db
37
+ .select()
38
+ .from(bookingItems)
39
+ .where(eq(bookingItems.bookingId, booking.id))
40
+ .orderBy(asc(bookingItems.createdAt), asc(bookingItems.id));
41
+ const [paymentSchedule] = input.bookingPaymentScheduleId
42
+ ? await db
43
+ .select()
44
+ .from(bookingPaymentSchedules)
45
+ .where(and(eq(bookingPaymentSchedules.id, input.bookingPaymentScheduleId), eq(bookingPaymentSchedules.bookingId, booking.id)))
46
+ .limit(1)
47
+ : [];
48
+ if (input.bookingPaymentScheduleId && !paymentSchedule) {
49
+ return { status: "payment_schedule_not_found" };
50
+ }
51
+ const bookingData = {
52
+ booking: {
53
+ id: booking.id,
54
+ bookingNumber: booking.bookingNumber,
55
+ personId: booking.personId,
56
+ organizationId: booking.organizationId,
57
+ startDate: booking.startDate,
58
+ endDate: booking.endDate,
59
+ sellCurrency: booking.sellCurrency,
60
+ baseCurrency: booking.baseCurrency,
61
+ fxRateSetId: booking.fxRateSetId,
62
+ sellAmountCents: booking.sellAmountCents,
63
+ baseSellAmountCents: booking.baseSellAmountCents,
64
+ },
65
+ paymentSchedule: paymentSchedule
66
+ ? {
67
+ id: paymentSchedule.id,
68
+ bookingId: paymentSchedule.bookingId,
69
+ bookingItemId: paymentSchedule.bookingItemId,
70
+ scheduleType: paymentSchedule.scheduleType,
71
+ dueDate: paymentSchedule.dueDate,
72
+ currency: paymentSchedule.currency,
73
+ amountCents: paymentSchedule.amountCents,
74
+ }
75
+ : null,
76
+ items: items.map((item) => ({
77
+ id: item.id,
78
+ title: item.title,
79
+ productId: item.productId,
80
+ productName: item.productNameSnapshot,
81
+ productNameSnapshot: item.productNameSnapshot,
82
+ optionNameSnapshot: item.optionNameSnapshot,
83
+ unitNameSnapshot: item.unitNameSnapshot,
84
+ departureLabelSnapshot: item.departureLabelSnapshot,
85
+ startDate: item.serviceDate ?? item.startsAt,
86
+ serviceDate: item.serviceDate,
87
+ startsAt: item.startsAt,
88
+ endDate: item.endsAt ?? item.serviceDate,
89
+ endsAt: item.endsAt,
90
+ quantity: item.quantity,
91
+ unitSellAmountCents: item.unitSellAmountCents,
92
+ totalSellAmountCents: item.totalSellAmountCents,
93
+ })),
94
+ };
95
+ const issuer = input.invoiceType === "proforma" ? issueProformaFromBooking : issueInvoiceFromBooking;
96
+ const invoice = await issuer(db, input, bookingData, runtime);
97
+ if (!invoice)
98
+ return { status: "booking_not_found" };
99
+ return { status: "issued", invoice };
100
+ }
23
101
  /**
24
102
  * Create + emit an invoice from a booking. Returns the persisted row
25
103
  * after flipping the status from `draft` to `issued`. Drafts shouldn't
@@ -41,7 +119,7 @@ export async function issueInvoiceFromBooking(db, input, bookingData, runtime =
41
119
  const [row] = await updateIssuedInvoice(tx);
42
120
  if (row) {
43
121
  await touchLinkedBookingUpdatedAt(tx, row.bookingId);
44
- await appendActionLedgerMutation(tx, await buildInvoiceIssuedActionLedgerInput(actionLedgerContext, { invoice: row }, { authorizationSource: runtime.actionLedgerAuthorizationSource }));
122
+ await appendActionLedgerMutation(tx, await buildInvoiceIssuedActionLedgerInput(actionLedgerContext, { invoice: row }, invoiceIssueLedgerOptions(runtime)));
45
123
  }
46
124
  return row;
47
125
  })
@@ -75,7 +153,7 @@ export async function issueProformaFromBooking(db, input, bookingData, runtime =
75
153
  const [row] = await updateIssuedInvoice(tx);
76
154
  if (row) {
77
155
  await touchLinkedBookingUpdatedAt(tx, row.bookingId);
78
- await appendActionLedgerMutation(tx, await buildInvoiceIssuedActionLedgerInput(actionLedgerContext, { invoice: row }, { authorizationSource: runtime.actionLedgerAuthorizationSource }));
156
+ await appendActionLedgerMutation(tx, await buildInvoiceIssuedActionLedgerInput(actionLedgerContext, { invoice: row }, invoiceIssueLedgerOptions(runtime)));
79
157
  }
80
158
  return row;
81
159
  })
@@ -89,6 +167,21 @@ export async function issueProformaFromBooking(db, input, bookingData, runtime =
89
167
  });
90
168
  return row;
91
169
  }
170
+ function invoiceIssueLedgerOptions(runtime) {
171
+ return {
172
+ authorizationSource: runtime.actionLedgerAuthorizationSource,
173
+ actionName: runtime.actionLedgerActionName,
174
+ routeOrToolName: runtime.actionLedgerRouteOrToolName,
175
+ capabilityId: runtime.actionLedgerCapabilityId,
176
+ capabilityVersion: runtime.actionLedgerCapabilityVersion,
177
+ evaluatedRisk: runtime.actionLedgerEvaluatedRisk,
178
+ causationActionId: runtime.actionLedgerCausationActionId,
179
+ approvalId: runtime.actionLedgerApprovalId,
180
+ idempotencyScope: runtime.actionLedgerIdempotencyScope,
181
+ idempotencyKey: runtime.actionLedgerIdempotencyKey,
182
+ idempotencyFingerprint: runtime.actionLedgerIdempotencyFingerprint,
183
+ };
184
+ }
92
185
  async function emitIssued(db, runtime, eventName, invoice, options = {}) {
93
186
  if (!runtime.eventBus)
94
187
  return;
@@ -368,6 +368,18 @@ export interface FinanceServiceRuntime extends InvoiceFxOptions {
368
368
  eventBus?: EventBus;
369
369
  actionLedgerContext?: ActionLedgerRequestContextValues;
370
370
  actionLedgerAuthorizationSource?: string | null;
371
+ actionLedgerActionName?: string | null;
372
+ actionLedgerRouteOrToolName?: string | null;
373
+ actionLedgerTargetType?: string | null;
374
+ actionLedgerTargetId?: string | null;
375
+ actionLedgerCapabilityId?: string | null;
376
+ actionLedgerCapabilityVersion?: string | null;
377
+ actionLedgerEvaluatedRisk?: "low" | "medium" | "high" | "critical" | null;
378
+ actionLedgerCausationActionId?: string | null;
379
+ actionLedgerApprovalId?: string | null;
380
+ actionLedgerIdempotencyScope?: string | null;
381
+ actionLedgerIdempotencyKey?: string | null;
382
+ actionLedgerIdempotencyFingerprint?: string | null;
371
383
  descriptionResolver?: InvoiceLineDescriptionResolver;
372
384
  invoiceDueDateResolver?: InvoiceDueDateResolver;
373
385
  paymentScheduleLineDescriptionFormat?: PaymentScheduleLineDescriptionFormat;
package/dist/service.d.ts CHANGED
@@ -1147,6 +1147,21 @@ export declare const financeService: {
1147
1147
  generated: undefined;
1148
1148
  }, {}, {}>;
1149
1149
  }>, "where" | "orderBy">;
1150
+ getCreditNoteById(db: import("drizzle-orm/postgres-js").PostgresJsDatabase, creditNoteId: string): Promise<{
1151
+ id: string;
1152
+ creditNoteNumber: string;
1153
+ invoiceId: string;
1154
+ status: "draft" | "issued" | "applied";
1155
+ amountCents: number;
1156
+ currency: string;
1157
+ baseCurrency: string | null;
1158
+ baseAmountCents: number | null;
1159
+ fxRateSetId: string | null;
1160
+ reason: string;
1161
+ notes: string | null;
1162
+ createdAt: Date;
1163
+ updatedAt: Date;
1164
+ } | null>;
1150
1165
  createCreditNote(db: import("drizzle-orm/postgres-js").PostgresJsDatabase, invoiceId: string, data: import("./service-shared.js").CreateCreditNoteInput, runtime?: import("./service-shared.js").FinanceServiceRuntime): Promise<{
1151
1166
  id: string;
1152
1167
  status: "draft" | "issued" | "applied";