> ## Documentation Index
> Fetch the complete documentation index at: https://voyant.travel/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Finance

> The finance module is the authoritative record of money in and money out: invoices, payments, credit notes, supplier payments, schedules, guarantees, and checkout collection.

The finance module owns money. Every payment session, capture, refund, and bank-transfer instruction flows through it for persistence, and it stays authoritative on what was actually paid and reconciled. It is the durable record behind the universal payments stack: the payments runtime holds sessions and links, finance holds the payments, invoices, credit notes, and supplier payments that document them.

It ships as `@voyant-travel/finance`, a headless module with Drizzle schema, validation, services, and Hono routes, plus a checkout collection runtime under the `./checkout` subpaths.

```bash theme={null}
pnpm add @voyant-travel/finance
```

## Key concepts

These terms come from the [glossary](/docs/concepts/glossary). Finance is where they live.

* **Invoice.** A billing document issued to a payer; lifecycle `draft → sent → partially_paid / paid / overdue / void`.
* **Invoice Number Series.** A configured numbering sequence (per legal entity, year, and type) that invoices draw from.
* **Credit Note.** A reversal or adjustment document referencing an Invoice.
* **Payment.** A recorded inbound transfer of money (bank transfer, card, cash, voucher, direct bill).
* **Supplier Payment.** A recorded outbound transfer to a Supplier.
* **Payment Schedule.** An installment plan attached to a Booking (deposit, installment, balance, hold) with due dates.
* **Guarantee.** A security hold (deposit, pre-auth, card-on-file, agency letter, voucher) ensuring eventual payment.
* **Payment Session.** An active payment attempt against a target: a Booking, Invoice, Schedule line, Guarantee, Program, or explicit provider reference.
* **Collection Plan.** A preview of what will be collected from the customer and when.

<Note>
  Cancel, Void, and Close are different verbs. **Cancel** is an operational reversal of a Booking. **Void** is a financial reversal of an Invoice or Payment. If a customer paid and then cancels, you do not void a paid invoice; you issue a **Credit Note**.
</Note>

## What it owns

Finance owns the documents and the runtime that produce and reconcile them.

### Documents and ledger

The core entities (with TypeID prefixes) are invoices and invoice lines (`inv`, `inli`), payments (`pay`), credit notes and credit-note lines (`crn`, `cnli`), supplier payments (`spay`), finance notes (`fnot`), invoice number series (`invs`), invoice templates (`invt`), invoice renditions (`invr`), tax regimes (`txrg`), and invoice external refs (`iner`).

### Invoice numbering

Routes that issue invoice-like documents share one precedence rule for document numbers:

1. A caller-supplied `invoiceNumber` always wins. The server stores it as-is and does not advance a sequence.
2. If `invoiceNumber` is omitted but `seriesId` is supplied, the server uses that series. It must be active and must match the requested scope (`invoice` for invoices, `proforma` for proformas).
3. If both are omitted, the server resolves the active default series for the scope, falling back to the most recently updated active series.
4. If no active series exists for the scope, the route returns `409` with `error: "no_active_series_for_scope"`.

Local allocation uses `financeService.allocateInvoiceNumber(...)`, which row-locks the series while advancing `currentSequence`. Only one active default series is allowed per scope. Series with an `externalProvider` set do not advance local sequences: they create the local invoice with a `PENDING-…` placeholder and `status: "pending_external_allocation"`, emit the issued event with `externalAllocationRequired: true`, and a provider adapter later patches in the provider-owned number via `financeService.applyExternalInvoiceAllocation(...)`.

### Checkout collection

Finance owns the checkout collection runtime under `./checkout`, `./checkout-routes`, and `./checkout-validation`. Provider startup is injected through payment initiations, bank-transfer details resolve through host wiring, and notification delivery stays behind a dispatcher rather than a direct package dependency. Mounted routes include the collection plan, the initiate-collection flow, a collections bootstrap, and reminder-run listing. When a session linked to a booking payment schedule completes, finance emits `booking_payment_schedule.paid` after the transaction commits, so booking lifecycles can promote a deposit-first booking from `on_hold` to `confirmed` without provider-specific webhook glue.

### Supplier-invoice profitability

Voyant historically modeled money owed to the operator (AR) but nothing owed by the operator. The supplier-invoice design closes that gap inside finance as a sibling of the customer-facing invoices model, not by overloading them with a direction column.

The conceptual model keeps three layers distinct:

* **Revenue (AR)** already exists as customer invoices.
* **Planned cost** already exists as the `costAmountCents` snapshots captured on booking items at quote or booking time.
* **Actual cost** is new: supplier invoices the operator records, whose lines are **allocated** to a departure, product, booking, or (derived) traveler.

From those, the read model computes **profit** (revenue minus actual cost) and **variance** (planned minus actual). The unit of profitability is the departure (an availability slot, the `departureLinkable`); a product's P\&L is the sum of its departures. Per-traveler cost is derived by splitting a departure-level allocation, not stored as a first-class allocation. New services such as `getDepartureProfitability` and `getProductProfitability` sit beside `getFinanceAggregates`, built on explicit service SQL over allocation ids rather than graph traversal. Supplier-invoice currency may differ from the operator base currency, so allocations store a base-currency amount resolved through the existing `baseCurrency` and `fxRateSetId` plumbing, with reverse-charge tax handled by the shared `tax_regimes`.

### Invoice FX and renditions

Invoice issuing can enrich `invoice.issued` events with the operator accounting currency, FX rate, FX commission, and effective provider rate when you configure `invoiceFxSettings` and an exchange-rate resolver. The default resolver calls the Voyant Data FX pair route through `@voyant-travel/data-sdk`. Rendered invoice artifacts are bound to an invoice with `financeService.bindInvoiceRendition(...)`, which writes a `ready` rendition inside a transaction and emits `invoice.rendered` (metadata only, never document bodies or signed URLs) after commit.

## Working with it

Standard modules are not registered by hand. `@voyant-travel/finance` is part of the standard product graph, so its routes, services, subscribers, and jobs are resolved into the application at build time. See [Configuration](/docs/platform/fundamentals/configuration).

Configure invoice FX so cross-currency invoices carry provenance:

```ts theme={null}
import {
  createFinanceVoyantRuntime,
  createVoyantDataFxExchangeRateResolver,
} from "@voyant-travel/finance"

createFinanceVoyantRuntime({
  invoiceFxSettings: {
    baseCurrency: "RON",
    fxCommissionBps: 200,
    fxCommissionInvoiceMention: "2% comision curs risc valutar",
  },
  resolveInvoiceExchangeRate: createVoyantDataFxExchangeRateResolver({
    apiKey: process.env.VOYANT_DATA_API_KEY!,
  }),
})
```

Show the same booking tax line in the create dialog that finalization will persist. The booking-tax preview extension is contributed by the package; you supply how its settings resolve:

```ts theme={null}
import { createBookingTaxPreviewApiExtension } from "@voyant-travel/finance/booking-tax"

createBookingTaxPreviewApiExtension({
  resolveBookingTaxSettings: async (db) => {
    const settings = await getTaxSettings(db)
    return {
      taxPriceMode: settings?.taxPriceMode ?? "inclusive",
      taxPolicyProfileId: settings?.taxPolicyProfileId ?? null,
    }
  },
})
```

<Warning>
  If the booking-tax preview is not configured, the client treats a missing preview as "no tax to show" and silently drops tax rows from the dialog summary. Configure it, or compute tax server-side with `computeBookingItemTaxLine` from `@voyant-travel/finance/booking-tax`.
</Warning>

Bind an already-stored invoice artifact as the ready rendition:

```ts theme={null}
await financeService.bindInvoiceRendition(db, invoiceId, artifact, {
  eventBus,
  replaceExisting: true,
})
// Emits invoice.rendered (metadata only) after the transaction commits.
```

## Links to other modules

* **Bookings.** An invoice targets a Booking (among other targets), a payment schedule belongs to a Booking, and `booking_payment_schedule.paid` lets booking lifecycles react to collected installments.
* **Operations.** Supplier cost allocations reference a departure through the operations `departureLinkable`. See [Operations](/docs/platform/modules/operations).
* **Distribution.** Supplier identity for AP comes from distribution suppliers; finance references them as indexed text ids with service-layer validation, not cross-module foreign keys.
* **Notifications.** Payment-session and invoice sends, plus booking document bundles, are orchestration wrappers in [Notifications](/docs/platform/modules/notifications) over the shared notification service, dispatched through finance's notification seam.
* **Legal.** A fully-paid booking can trigger contract and invoice document generation through the notifications document-bundle lifecycle.

## React package

`@voyant-travel/finance-react` provides the data hooks and admin UI, and `@voyant-travel/finance-react/checkout-ui` ships the universal checkout components.

* Hooks include `usePublicPaymentSession`, `usePublicBookingPaymentOptions`, `useInvoices`, `useInvoiceNumberSeries`, `useBookingPaymentSchedules`, `useBookingGuarantees`, `useDepartureProfitability`, and `useProductProfitability`.
* `@voyant-travel/finance-react/checkout-ui` exports `<PaymentStep>` (a single capability-gated payment selector that every vertical reuses) and `<PaymentLinkLandingPage>` (the one universal `/pay/:sessionId` page).

```tsx theme={null}
import { PaymentStep } from "@voyant-travel/finance-react/checkout-ui"

<PaymentStep
  request={paymentRequest}
  capabilities={{ chargeSavedCard: false, sendLink: true, bankTransfer: true }}
/>
```

<Note>
  The payment contract itself lives in `@voyant-travel/payments`: the payment adapter contract (`voyant.payment-adapter.v1`), the provider catalog, and the remote transport. Processors such as Netopia and Voyant Pay are entries in that catalog, and they stay invisible to the vertical — verticals talk to `<PaymentStep>` and the finance-react hooks. See [Adapters and providers](/docs/platform/extending/adapters-and-providers).
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="Notifications" icon="paper-plane" href="/docs/platform/modules/notifications">
    Send payment links, invoices, and booking documents over email and SMS.
  </Card>

  <Card title="Operations" icon="bus" href="/docs/platform/modules/operations">
    The departure model that supplier-cost profitability reports against.
  </Card>

  <Card title="Legal" icon="file-signature" href="/docs/platform/modules/legal">
    Contracts and policies that travel alongside invoices at checkout.
  </Card>

  <Card title="Glossary" icon="book-open" href="/docs/concepts/glossary">
    The Cost, Rate, and Price distinction and the money vocabulary.
  </Card>
</CardGroup>
