@voyant-travel/finance 0.166.0 → 0.167.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.
@@ -23,9 +23,11 @@ export type BookingTaxSettings = {
23
23
  taxPriceMode?: "inclusive" | "exclusive" | null;
24
24
  taxPolicyProfileId?: string | null;
25
25
  /**
26
- * Operator invoicing mode. `direct` bills the fiscal invoice
27
- * straight away; `proforma-first` issues a proforma and converts it
28
- * to a fiscal invoice on full settlement. Absent/null `direct`.
26
+ * Operator invoicing mode for the deferred bank-transfer path.
27
+ * `proforma-first` issues a proforma at order placement and converts it
28
+ * to a fiscal invoice on full settlement; `direct` issues the fiscal
29
+ * invoice at order placement. Card payments always invoice directly and
30
+ * never consult this. Absent/null → `proforma-first`.
29
31
  */
30
32
  invoicingMode?: InvoicingMode | null;
31
33
  /**
@@ -57,12 +59,6 @@ export declare function resolveBookingSellTaxRate(db: PostgresJsDatabase, args:
57
59
  productId?: string | null;
58
60
  facts?: Partial<ProductTaxFacts>;
59
61
  }, options?: ResolveBookingSellTaxRateOptions): Promise<ResolvedBookingSellTaxRate | null>;
60
- /**
61
- * Resolve just the operator invoicing mode, defaulting to `direct`
62
- * when no settings row exists. Shared by the proforma-conversion
63
- * subscriber so it never converts unless the operator opted in.
64
- */
65
- export declare function resolveInvoicingModeOrDefault(db: PostgresJsDatabase, options?: ResolveBookingSellTaxRateOptions): Promise<InvoicingMode>;
66
62
  export declare function computeBookingItemTaxLine(taxRate: ResolvedBookingSellTaxRate | null, amountCents: number, currency: string, sortOrder?: number): {
67
63
  code: string;
68
64
  name: string;
@@ -117,19 +117,10 @@ async function resolveBookingTaxSettingsOrDefault(db, options = {}) {
117
117
  return {
118
118
  taxPriceMode: settings?.taxPriceMode === "exclusive" ? "exclusive" : "inclusive",
119
119
  taxPolicyProfileId: settings?.taxPolicyProfileId ?? null,
120
- invoicingMode: settings?.invoicingMode === "proforma-first" ? "proforma-first" : "direct",
120
+ invoicingMode: settings?.invoicingMode === "direct" ? "direct" : "proforma-first",
121
121
  fxReferenceSource: settings?.fxReferenceSource === "bnr" ? "bnr" : "ecb",
122
122
  };
123
123
  }
124
- /**
125
- * Resolve just the operator invoicing mode, defaulting to `direct`
126
- * when no settings row exists. Shared by the proforma-conversion
127
- * subscriber so it never converts unless the operator opted in.
128
- */
129
- export async function resolveInvoicingModeOrDefault(db, options = {}) {
130
- const settings = await resolveBookingTaxSettingsOrDefault(db, options);
131
- return settings.invoicingMode;
132
- }
133
124
  export function computeBookingItemTaxLine(taxRate, amountCents, currency, sortOrder = 0) {
134
125
  if (!taxRate || taxRate.rate <= 0 || amountCents <= 0)
135
126
  return null;
package/dist/index.d.ts CHANGED
@@ -26,7 +26,7 @@ export declare const createFinanceVoyantRuntime: import("@voyant-travel/core/pro
26
26
  export type { PaymentAdapter, PaymentAdapterCapabilities, PaymentAdapterConformanceHarness, PaymentAdapterConformanceResult, PaymentAdapterDiagnostics, PaymentAdapterRuntimeContext, PaymentCallbackEvent, PaymentCallbackRequest, PaymentCallbackVerificationResult, PaymentInitiationInput, PaymentInitiationResult, PaymentMoney, PaymentOperationInput, PaymentOperationResult, PaymentStatusInput, PaymentStatusResult, } from "@voyant-travel/payments";
27
27
  export { PAYMENT_ADAPTER_CONTRACT_VERSION, PAYMENT_ADAPTER_RUNTIME_PORT_ID, paymentAdapterRuntimePort, runPaymentAdapterConformance, } from "@voyant-travel/payments";
28
28
  export { type BookingCancellationSettlementInput, buildPaidBookingCancellationSettlementNote, closeTerminalBookingPaymentSchedules, financeBookingLifecycle, recordPaidBookingCancellationSettlement, } from "./booking-lifecycle.js";
29
- export { type BookingTaxRouteOptions, type BookingTaxSettings, computeBookingItemTaxLine, createBookingTaxApiExtension, createBookingTaxRoutes, createBookingTaxVoyantRuntime, type InvoicingMode, loadProductTaxFacts, matchesTaxPolicyCondition, mountBookingTaxRoutes, type ProductTaxFacts, type ResolveBookingSellTaxRateOptions, type ResolveBookingTaxSettings, type ResolvedBookingSellTaxRate, resolveBookingSellTaxRate, type TaxPolicyCondition, type UpdateBookingTaxSettings, } from "./booking-tax.js";
29
+ export { type BookingTaxRouteOptions, type BookingTaxSettings, computeBookingItemTaxLine, createBookingTaxApiExtension, createBookingTaxRoutes, createBookingTaxVoyantRuntime, loadProductTaxFacts, matchesTaxPolicyCondition, mountBookingTaxRoutes, type ProductTaxFacts, type ResolveBookingSellTaxRateOptions, type ResolveBookingTaxSettings, type ResolvedBookingSellTaxRate, resolveBookingSellTaxRate, type TaxPolicyCondition, type UpdateBookingTaxSettings, } from "./booking-tax.js";
30
30
  export type { CardPaymentBilling, CardPaymentStartArgs, CardPaymentStarter, CardPaymentStartResult, PaymentAdapterCardPaymentStarterOptions, } from "./card-payment.js";
31
31
  export { createPaymentAdapterCardPaymentStarter } from "./card-payment.js";
32
32
  export { type DocumentDownloadEnvelope, type DocumentDownloadResolution, type DocumentDownloadResolver, resolveStoredDocumentDownload, type StoredDocumentReference, } from "./document-download.js";
@@ -385,11 +385,9 @@ export const createBookingScheduleVoyantRuntime = defineGraphRuntimeFactory(asyn
385
385
  resolveRoutesOptions: () => provider.options,
386
386
  withDb: provider.withDb,
387
387
  });
388
- // Same host/settings wiring powers the proforma-conversion
389
- // subscriber: it reads the operator invoicing mode and, in
390
- // proforma-first mode, mints the fiscal invoice on settlement.
388
+ // Same host wiring powers the proforma-conversion subscriber:
389
+ // when a fully-paid proforma settles it mints the fiscal invoice.
391
390
  container.register(PROFORMA_CONVERSION_SUBSCRIBER_RUNTIME_KEY, {
392
- resolveInvoicingMode: operatorSettings.resolveInvoicingMode,
393
391
  withDb: provider.withDb,
394
392
  eventBus,
395
393
  });
@@ -1,26 +1,29 @@
1
1
  import type { EventBus, SubscriberRuntimeDescriptor } from "@voyant-travel/core";
2
2
  import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
3
- import type { InvoicingMode } from "./booking-tax.js";
4
3
  /**
5
4
  * Standard proforma → fiscal invoice conversion.
6
5
  *
7
- * When the operator runs `invoicing.mode: "proforma-first"`, a proforma
8
- * is issued at checkout and the fiscal invoice is minted once the
9
- * proforma is fully settled. This subscriber watches settlement signals
10
- * (`invoice.settled` from the settlement poller and
11
- * `invoice.payment.recorded` from manual/processed payments) and, in
12
- * proforma-first mode only, converts a fully-paid proforma to its fiscal
13
- * invoice via {@link convertProformaToInvoice} (which copies lines,
14
- * assigns the fiscal number, links both documents, and voids the
15
- * proforma). `direct` mode is a no-op — zero behaviour change for
16
- * existing deployments and the conversion is idempotent (the service
17
- * guards against double-conversion under an advisory lock).
6
+ * A proforma is issued for deferred (bank-transfer) checkouts when the
7
+ * operator runs the `proforma-first` invoicing mode, or by the manual
8
+ * `POST /invoices/{id}/convert-to-invoice` flow. This subscriber watches
9
+ * settlement signals (`invoice.settled` from the settlement poller and
10
+ * `invoice.payment.recorded` from manual/processed payments) and converts
11
+ * a fully-paid proforma to its fiscal invoice via
12
+ * {@link convertProformaToInvoice} (which copies lines, assigns the fiscal
13
+ * number, links both documents, and voids the proforma).
14
+ *
15
+ * The conversion is not gated on the invoicing mode: any fully-paid
16
+ * proforma should mint its fiscal invoice, and the `invoiceType ===
17
+ * "proforma"` guard already scopes it to proformas. Gating on the current
18
+ * mode would strand a proforma that is still outstanding when an operator
19
+ * switches from `proforma-first` to `direct`. When no proforma exists
20
+ * (card checkouts, or bank transfer in `direct` mode) the subscriber
21
+ * simply never fires. The conversion is idempotent (the service guards
22
+ * against double-conversion under an advisory lock).
18
23
  */
19
24
  export declare const FINANCE_PROFORMA_CONVERSION_SUBSCRIBER_ID = "@voyant-travel/finance#subscriber.proforma-conversion";
20
25
  export declare const PROFORMA_CONVERSION_SUBSCRIBER_RUNTIME_KEY = "finance.proformaConversionSubscriberRuntime";
21
26
  export interface ProformaConversionSubscriberRuntime {
22
- /** Operator invoicing mode; defaults to `direct` when unconfigured. */
23
- resolveInvoicingMode(db: PostgresJsDatabase): Promise<InvoicingMode>;
24
27
  /** Resolve the deployment database and retain ownership of its lifecycle. */
25
28
  withDb<T>(bindings: unknown, operation: (db: PostgresJsDatabase) => Promise<T>): Promise<T>;
26
29
  /** Event bus used to re-emit conversion events for downstream plugins. */
@@ -3,17 +3,23 @@ import { convertProformaToInvoice } from "./service-issue.js";
3
3
  /**
4
4
  * Standard proforma → fiscal invoice conversion.
5
5
  *
6
- * When the operator runs `invoicing.mode: "proforma-first"`, a proforma
7
- * is issued at checkout and the fiscal invoice is minted once the
8
- * proforma is fully settled. This subscriber watches settlement signals
9
- * (`invoice.settled` from the settlement poller and
10
- * `invoice.payment.recorded` from manual/processed payments) and, in
11
- * proforma-first mode only, converts a fully-paid proforma to its fiscal
12
- * invoice via {@link convertProformaToInvoice} (which copies lines,
13
- * assigns the fiscal number, links both documents, and voids the
14
- * proforma). `direct` mode is a no-op — zero behaviour change for
15
- * existing deployments and the conversion is idempotent (the service
16
- * guards against double-conversion under an advisory lock).
6
+ * A proforma is issued for deferred (bank-transfer) checkouts when the
7
+ * operator runs the `proforma-first` invoicing mode, or by the manual
8
+ * `POST /invoices/{id}/convert-to-invoice` flow. This subscriber watches
9
+ * settlement signals (`invoice.settled` from the settlement poller and
10
+ * `invoice.payment.recorded` from manual/processed payments) and converts
11
+ * a fully-paid proforma to its fiscal invoice via
12
+ * {@link convertProformaToInvoice} (which copies lines, assigns the fiscal
13
+ * number, links both documents, and voids the proforma).
14
+ *
15
+ * The conversion is not gated on the invoicing mode: any fully-paid
16
+ * proforma should mint its fiscal invoice, and the `invoiceType ===
17
+ * "proforma"` guard already scopes it to proformas. Gating on the current
18
+ * mode would strand a proforma that is still outstanding when an operator
19
+ * switches from `proforma-first` to `direct`. When no proforma exists
20
+ * (card checkouts, or bank transfer in `direct` mode) the subscriber
21
+ * simply never fires. The conversion is idempotent (the service guards
22
+ * against double-conversion under an advisory lock).
17
23
  */
18
24
  export const FINANCE_PROFORMA_CONVERSION_SUBSCRIBER_ID = "@voyant-travel/finance#subscriber.proforma-conversion";
19
25
  export const PROFORMA_CONVERSION_SUBSCRIBER_RUNTIME_KEY = "finance.proformaConversionSubscriberRuntime";
@@ -26,15 +32,13 @@ export function createProformaConversionSubscriberRuntime(dependencies = {}) {
26
32
  eventType: "invoice.settled",
27
33
  register: ({ bindings, container, eventBus }) => {
28
34
  const handler = async ({ data }) => {
29
- // Runtime is only registered when the operator-settings port is
30
- // available. Absent → treat as `direct` mode → no-op.
35
+ // Runtime is registered by the finance booking-schedule host wiring.
36
+ // Absent → the deployment did not mount finance settlement → no-op.
31
37
  if (!container.has(PROFORMA_CONVERSION_SUBSCRIBER_RUNTIME_KEY))
32
38
  return;
33
39
  const runtime = resolveProformaConversionSubscriberRuntime(container);
34
40
  try {
35
41
  await runtime.withDb(bindings, async (db) => {
36
- if ((await runtime.resolveInvoicingMode(db)) !== "proforma-first")
37
- return;
38
42
  const invoice = await financeService.getInvoiceById(db, data.invoiceId);
39
43
  if (!invoice)
40
44
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyant-travel/finance",
3
- "version": "0.166.0",
3
+ "version": "0.167.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -151,17 +151,17 @@
151
151
  "hono": "^4.12.27",
152
152
  "zod": "^4.4.3",
153
153
  "@voyant-travel/action-ledger": "^0.111.1",
154
+ "@voyant-travel/bookings": "^0.167.0",
155
+ "@voyant-travel/core": "^0.125.0",
154
156
  "@voyant-travel/db": "^0.114.10",
155
157
  "@voyant-travel/types": "^0.109.3",
156
- "@voyant-travel/bookings": "^0.166.0",
158
+ "@voyant-travel/finance-contracts": "^0.106.2",
157
159
  "@voyant-travel/hono": "^0.128.1",
158
- "@voyant-travel/core": "^0.125.0",
159
- "@voyant-travel/storage": "^0.111.1",
160
- "@voyant-travel/payments": "^0.2.0",
161
- "@voyant-travel/tools": "^0.3.0",
160
+ "@voyant-travel/payments": "^0.2.1",
162
161
  "@voyant-travel/public-document-delivery": "^0.4.1",
163
- "@voyant-travel/finance-contracts": "^0.106.2",
164
- "@voyant-travel/utils": "^0.107.1"
162
+ "@voyant-travel/storage": "^0.111.1",
163
+ "@voyant-travel/utils": "^0.107.1",
164
+ "@voyant-travel/tools": "^0.3.0"
165
165
  },
166
166
  "devDependencies": {
167
167
  "drizzle-kit": "^0.31.10",