@voyantjs/finance 0.66.0 → 0.68.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
@@ -125,11 +125,41 @@ import {
125
125
  } from "@voyantjs/finance/booking-tax"
126
126
  ```
127
127
 
128
+ ## Invoice FX Settings
129
+
130
+ Invoice issuing can enrich `invoice.issued` events with operator accounting
131
+ currency, FX rate, FX commission, and effective provider rate. Configure the
132
+ finance module with `invoiceFxSettings` or `resolveInvoiceFxSettings`, plus an
133
+ exchange-rate resolver:
134
+
135
+ ```typescript
136
+ import {
137
+ createFinanceHonoModule,
138
+ createVoyantDataFxExchangeRateResolver,
139
+ } from "@voyantjs/finance"
140
+
141
+ createFinanceHonoModule({
142
+ invoiceFxSettings: {
143
+ baseCurrency: "RON",
144
+ fxCommissionBps: 200,
145
+ fxCommissionInvoiceMention: "2% comision curs risc valutar",
146
+ },
147
+ resolveInvoiceExchangeRate: createVoyantDataFxExchangeRateResolver({
148
+ apiKey: process.env.VOYANT_DATA_API_KEY!,
149
+ }),
150
+ })
151
+ ```
152
+
153
+ The default data resolver calls the Voyant Data FX pair route
154
+ `/data/fx/v1/fx/pair/{invoiceCurrency}/{baseCurrency}`. If the invoice currency
155
+ matches the operator base currency, no FX fields are emitted.
156
+
128
157
  ## Exports
129
158
 
130
159
  | Entry | Description |
131
160
  | --- | --- |
132
161
  | `.` | Module export |
162
+ | `./invoice-fx` | Invoice FX settings, route helpers, and data FX resolver |
133
163
  | `./schema` | Drizzle tables |
134
164
  | `./validation` | Zod schemas |
135
165
  | `./booking-tax` | Booking sell-side tax policy helpers and route mounting |
package/dist/index.d.ts CHANGED
@@ -21,6 +21,7 @@ export interface FinanceHonoModuleOptions extends FinanceRuntimeOptions, PublicF
21
21
  export declare function createFinanceHonoModule(options?: FinanceHonoModuleOptions): HonoModule;
22
22
  export declare const financeHonoModule: HonoModule;
23
23
  export { type BookingTaxRouteOptions, type BookingTaxSettings, computeBookingItemTaxLine, createBookingTaxHonoExtension, createBookingTaxRoutes, loadProductTaxFacts, matchesTaxPolicyCondition, mountBookingTaxRoutes, type ProductTaxFacts, type ResolveBookingSellTaxRateOptions, type ResolveBookingTaxSettings, type ResolvedBookingSellTaxRate, resolveBookingSellTaxRate, type TaxPolicyCondition, type UpdateBookingTaxSettings, } from "./booking-tax.js";
24
+ export { createInvoiceFxHonoExtension, createInvoiceFxRoutes, createVoyantDataFxExchangeRateResolver, type InvoiceFxContext, type InvoiceFxOptions, type InvoiceFxRouteOptions, type InvoiceFxSettings, mountInvoiceFxRoutes, type ResolvedInvoiceFxSettings, type ResolveInvoiceExchangeRate, type ResolveInvoiceExchangeRateInput, type ResolveInvoiceFxSettings, resolveInvoiceFxContext, resolveInvoiceFxSettingsOrDefault, type UpdateInvoiceFxSettings, type VoyantDataFxResolverOptions, } from "./invoice-fx.js";
24
25
  export type { ComputedScheduleEntry, ComputeScheduleInput, DepositKind, DepositRule, PaymentPolicy, PaymentPolicyCascadeLayers, PaymentPolicySource, PaymentScheduleEntryType, ResolvedPaymentPolicy, } from "./payment-policy.js";
25
26
  export { computePaymentSchedule, isPaymentPolicyEmpty, noDepositPolicy, policyShouldRequireFullPayment, resolveEffectivePaymentPolicy, } from "./payment-policy.js";
26
27
  export { buildFinanceRouteRuntime, FINANCE_ROUTE_RUNTIME_CONTAINER_KEY, type FinanceRouteRuntime, type FinanceRuntimeOptions, } from "./route-runtime.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Hono } from "hono";
2
+ import { createInvoiceFxRoutes } from "./invoice-fx.js";
2
3
  import { buildFinanceRouteRuntime, FINANCE_ROUTE_RUNTIME_CONTAINER_KEY, } from "./route-runtime.js";
3
4
  import { financeRoutes } from "./routes.js";
4
5
  import { financeActionLedgerRoutes } from "./routes-action-ledger.js";
@@ -40,6 +41,7 @@ export function createFinanceHonoModule(options = {}) {
40
41
  const adminRoutes = new Hono()
41
42
  .route("/", financeRoutes)
42
43
  .route("/", financeActionLedgerRoutes)
44
+ .route("/", createInvoiceFxRoutes(options))
43
45
  .route("/", createFinanceAdminDocumentRoutes(options))
44
46
  .route("/", createFinanceAdminSettlementRoutes(options));
45
47
  const module = {
@@ -65,6 +67,7 @@ export function createFinanceHonoModule(options = {}) {
65
67
  }
66
68
  export const financeHonoModule = createFinanceHonoModule();
67
69
  export { computeBookingItemTaxLine, createBookingTaxHonoExtension, createBookingTaxRoutes, loadProductTaxFacts, matchesTaxPolicyCondition, mountBookingTaxRoutes, resolveBookingSellTaxRate, } from "./booking-tax.js";
70
+ export { createInvoiceFxHonoExtension, createInvoiceFxRoutes, createVoyantDataFxExchangeRateResolver, mountInvoiceFxRoutes, resolveInvoiceFxContext, resolveInvoiceFxSettingsOrDefault, } from "./invoice-fx.js";
68
71
  export { computePaymentSchedule, isPaymentPolicyEmpty, noDepositPolicy, policyShouldRequireFullPayment, resolveEffectivePaymentPolicy, } from "./payment-policy.js";
69
72
  export { buildFinanceRouteRuntime, FINANCE_ROUTE_RUNTIME_CONTAINER_KEY, } from "./route-runtime.js";
70
73
  export { bookingsCreateExtension } from "./routes-booking-create.js";
@@ -0,0 +1,90 @@
1
+ import type { HonoExtension } from "@voyantjs/hono/module";
2
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
3
+ import type { Hono } from "hono";
4
+ import type { Env } from "./routes-shared.js";
5
+ export type InvoiceFxSettings = {
6
+ baseCurrency?: string | null;
7
+ fxCommissionBps?: number | null;
8
+ fxCommissionInvoiceMention?: string | null;
9
+ };
10
+ export type ResolveInvoiceFxSettings = (db: PostgresJsDatabase) => InvoiceFxSettings | null | undefined | Promise<InvoiceFxSettings | null | undefined>;
11
+ export type UpdateInvoiceFxSettings = (db: PostgresJsDatabase, settings: InvoiceFxSettings) => InvoiceFxSettings | null | undefined | Promise<InvoiceFxSettings | null | undefined>;
12
+ export type ResolveInvoiceExchangeRateInput = {
13
+ /** Currency being invoiced, e.g. EUR. */
14
+ baseCurrency: string;
15
+ /** Operator accounting/reporting currency, e.g. RON. */
16
+ quoteCurrency: string;
17
+ date?: string;
18
+ };
19
+ export type ResolveInvoiceExchangeRate = (input: ResolveInvoiceExchangeRateInput) => number | null | undefined | Promise<number | null | undefined>;
20
+ export type HandleInvoiceFxResolutionError = (error: unknown, input: ResolveInvoiceExchangeRateInput) => void | Promise<void>;
21
+ export interface InvoiceFxOptions {
22
+ invoiceFxSettings?: InvoiceFxSettings | null;
23
+ resolveInvoiceFxSettings?: ResolveInvoiceFxSettings;
24
+ updateInvoiceFxSettings?: UpdateInvoiceFxSettings;
25
+ resolveInvoiceExchangeRate?: ResolveInvoiceExchangeRate;
26
+ onInvoiceFxResolutionError?: HandleInvoiceFxResolutionError;
27
+ }
28
+ export interface InvoiceFxRouteOptions extends InvoiceFxOptions {
29
+ }
30
+ export type InvoiceFxInvoice = {
31
+ currency: string;
32
+ baseCurrency?: string | null;
33
+ issueDate?: string | Date | null;
34
+ };
35
+ export type InvoiceFxContext = {
36
+ baseCurrency: string;
37
+ fxRate: number;
38
+ fxCommissionBps: number;
39
+ effectiveRate: number;
40
+ fxCommissionInvoiceMention?: string;
41
+ };
42
+ export type ResolvedInvoiceFxSettings = {
43
+ baseCurrency: string;
44
+ fxCommissionBps: number;
45
+ fxCommissionInvoiceMention?: string;
46
+ };
47
+ export type VoyantDataFxResolverOptions = {
48
+ baseUrl?: string;
49
+ apiKey: string;
50
+ authHeader?: string;
51
+ authScheme?: string | null;
52
+ fetch?: typeof fetch;
53
+ };
54
+ export declare function resolveInvoiceFxSettingsOrDefault(db: PostgresJsDatabase, options?: InvoiceFxOptions): Promise<ResolvedInvoiceFxSettings>;
55
+ export declare function resolveInvoiceFxContext(db: PostgresJsDatabase, invoice: InvoiceFxInvoice, options?: InvoiceFxOptions): Promise<InvoiceFxContext | null>;
56
+ export declare function createVoyantDataFxExchangeRateResolver(options: VoyantDataFxResolverOptions): ResolveInvoiceExchangeRate;
57
+ export declare function createInvoiceFxRoutes(options?: InvoiceFxRouteOptions): import("hono/hono-base").HonoBase<Env, {
58
+ "/invoice-fx-settings": {
59
+ $get: {
60
+ input: {};
61
+ output: {
62
+ data: {
63
+ baseCurrency: string;
64
+ fxCommissionBps: number;
65
+ fxCommissionInvoiceMention?: string | undefined;
66
+ };
67
+ };
68
+ outputFormat: "json";
69
+ status: import("hono/utils/http-status").ContentfulStatusCode;
70
+ };
71
+ };
72
+ } & {
73
+ "/invoice-fx-settings": {
74
+ $patch: {
75
+ input: {};
76
+ output: {
77
+ data: {
78
+ baseCurrency: string;
79
+ fxCommissionBps: number;
80
+ fxCommissionInvoiceMention?: string | undefined;
81
+ };
82
+ };
83
+ outputFormat: "json";
84
+ status: import("hono/utils/http-status").ContentfulStatusCode;
85
+ };
86
+ };
87
+ }, "/", "/invoice-fx-settings">;
88
+ export declare function mountInvoiceFxRoutes(hono: Hono, options?: InvoiceFxRouteOptions): Hono;
89
+ export declare function createInvoiceFxHonoExtension(options?: InvoiceFxRouteOptions): HonoExtension;
90
+ //# sourceMappingURL=invoice-fx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoice-fx.d.ts","sourceRoot":"","sources":["../src/invoice-fx.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,142 @@
1
+ import { ApiHttpError, parseJsonBody } from "@voyantjs/hono";
2
+ import { Hono as HonoApp } from "hono";
3
+ import { z } from "zod";
4
+ const DEFAULT_DATA_BASE_URL = "https://api.voyantjs.com";
5
+ const invoiceFxSettingsPatchSchema = z.object({
6
+ baseCurrency: z.string().min(3).max(8).nullable().optional(),
7
+ fxCommissionBps: z.number().int().min(0).max(100_000).nullable().optional(),
8
+ fxCommissionInvoiceMention: z.string().nullable().optional(),
9
+ });
10
+ export async function resolveInvoiceFxSettingsOrDefault(db, options = {}) {
11
+ const settings = await resolveConfiguredInvoiceFxSettings(db, options);
12
+ return {
13
+ baseCurrency: normalizeCurrency(settings?.baseCurrency) ?? "RON",
14
+ fxCommissionBps: normalizeBasisPoints(settings?.fxCommissionBps),
15
+ fxCommissionInvoiceMention: normalizeOptionalText(settings?.fxCommissionInvoiceMention),
16
+ };
17
+ }
18
+ export async function resolveInvoiceFxContext(db, invoice, options = {}) {
19
+ const settings = await resolveConfiguredInvoiceFxSettings(db, options);
20
+ const baseCurrency = normalizeCurrency(settings?.baseCurrency) ?? normalizeCurrency(invoice.baseCurrency) ?? "RON";
21
+ const invoiceCurrency = normalizeCurrency(invoice.currency);
22
+ if (!baseCurrency || !invoiceCurrency || invoiceCurrency === baseCurrency)
23
+ return null;
24
+ if (!options.resolveInvoiceExchangeRate)
25
+ return null;
26
+ const rateInput = {
27
+ baseCurrency: invoiceCurrency,
28
+ quoteCurrency: baseCurrency,
29
+ date: toDateString(invoice.issueDate),
30
+ };
31
+ let fxRate;
32
+ try {
33
+ fxRate = await options.resolveInvoiceExchangeRate(rateInput);
34
+ }
35
+ catch (error) {
36
+ await notifyInvoiceFxResolutionError(options, error, rateInput);
37
+ return null;
38
+ }
39
+ if (typeof fxRate !== "number" || !Number.isFinite(fxRate) || fxRate <= 0)
40
+ return null;
41
+ const fxCommissionBps = normalizeBasisPoints(settings?.fxCommissionBps);
42
+ const effectiveRate = roundRate(fxRate * (1 + fxCommissionBps / 10_000));
43
+ return {
44
+ baseCurrency,
45
+ fxRate: roundRate(fxRate),
46
+ fxCommissionBps,
47
+ effectiveRate,
48
+ ...(fxCommissionBps > 0 && normalizeOptionalText(settings?.fxCommissionInvoiceMention)
49
+ ? { fxCommissionInvoiceMention: normalizeOptionalText(settings?.fxCommissionInvoiceMention) }
50
+ : {}),
51
+ };
52
+ }
53
+ export function createVoyantDataFxExchangeRateResolver(options) {
54
+ const fetchImpl = options.fetch ?? fetch;
55
+ const baseUrl = options.baseUrl ?? DEFAULT_DATA_BASE_URL;
56
+ const authHeader = options.authHeader ?? "authorization";
57
+ const authScheme = options.authScheme === undefined ? "Bearer" : options.authScheme;
58
+ return async ({ baseCurrency, quoteCurrency }) => {
59
+ const url = new URL(`/data/fx/v1/fx/pair/${encodeURIComponent(baseCurrency)}/${encodeURIComponent(quoteCurrency)}`, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
60
+ const headers = new Headers();
61
+ headers.set(authHeader, authScheme ? `${authScheme} ${options.apiKey}` : options.apiKey);
62
+ headers.set("x-voyant-sdk", "voyant-finance");
63
+ const response = await fetchImpl(url, { headers });
64
+ const body = (await response.json());
65
+ if (!response.ok) {
66
+ throw new Error(`Voyant Data FX request failed with status ${response.status}`);
67
+ }
68
+ const rate = body.conversionRate ?? body.conversion_rate;
69
+ return typeof rate === "number" && Number.isFinite(rate) ? rate : null;
70
+ };
71
+ }
72
+ export function createInvoiceFxRoutes(options = {}) {
73
+ return new HonoApp()
74
+ .get("/invoice-fx-settings", async (c) => {
75
+ return c.json({
76
+ data: await resolveInvoiceFxSettingsOrDefault(c.get("db"), options),
77
+ });
78
+ })
79
+ .patch("/invoice-fx-settings", async (c) => {
80
+ if (!options.updateInvoiceFxSettings) {
81
+ throw new ApiHttpError("Invoice FX settings updates are not configured", {
82
+ status: 501,
83
+ code: "invoice_fx_settings_update_not_configured",
84
+ });
85
+ }
86
+ const current = await resolveInvoiceFxSettingsOrDefault(c.get("db"), options);
87
+ const patch = await parseJsonBody(c, invoiceFxSettingsPatchSchema);
88
+ const next = await options.updateInvoiceFxSettings(c.get("db"), {
89
+ ...current,
90
+ ...patch,
91
+ });
92
+ return c.json({
93
+ data: await resolveInvoiceFxSettingsOrDefault(c.get("db"), { invoiceFxSettings: next }),
94
+ });
95
+ });
96
+ }
97
+ export function mountInvoiceFxRoutes(hono, options = {}) {
98
+ hono.route("/v1/admin/finance", createInvoiceFxRoutes(options));
99
+ return hono;
100
+ }
101
+ export function createInvoiceFxHonoExtension(options = {}) {
102
+ const extension = {
103
+ name: "finance.invoice-fx",
104
+ module: "finance",
105
+ };
106
+ return {
107
+ extension,
108
+ adminRoutes: createInvoiceFxRoutes(options),
109
+ routes: createInvoiceFxRoutes(options),
110
+ };
111
+ }
112
+ async function resolveConfiguredInvoiceFxSettings(db, options) {
113
+ return options.invoiceFxSettings !== undefined
114
+ ? options.invoiceFxSettings
115
+ : ((await options.resolveInvoiceFxSettings?.(db)) ?? null);
116
+ }
117
+ function normalizeCurrency(value) {
118
+ const normalized = value?.trim().toUpperCase();
119
+ return normalized ? normalized : null;
120
+ }
121
+ async function notifyInvoiceFxResolutionError(options, error, input) {
122
+ try {
123
+ await options.onInvoiceFxResolutionError?.(error, input);
124
+ }
125
+ catch {
126
+ // FX enrichment is optional; a reporting hook must not fail invoice issuance.
127
+ }
128
+ }
129
+ function normalizeBasisPoints(value) {
130
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.round(value) : 0;
131
+ }
132
+ function normalizeOptionalText(value) {
133
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
134
+ }
135
+ function toDateString(value) {
136
+ if (value instanceof Date)
137
+ return value.toISOString().slice(0, 10);
138
+ return typeof value === "string" && value.length > 0 ? value : undefined;
139
+ }
140
+ function roundRate(value) {
141
+ return Number(value.toFixed(8));
142
+ }
@@ -1,4 +1,5 @@
1
1
  import type { EventBus } from "@voyantjs/core";
2
+ import type { InvoiceFxOptions } from "./invoice-fx.js";
2
3
  import type { FinanceDocumentRouteOptions, InvoiceDocumentGenerator } from "./routes-documents.js";
3
4
  import type { FinanceSettlementRouteOptions, InvoiceSettlementPoller } from "./routes-settlement.js";
4
5
  export type FinanceRouteRuntime = {
@@ -6,9 +7,9 @@ export type FinanceRouteRuntime = {
6
7
  resolveDocumentDownloadUrl?: FinanceDocumentRouteOptions["resolveDocumentDownloadUrl"];
7
8
  invoiceSettlementPollers: Record<string, InvoiceSettlementPoller>;
8
9
  eventBus?: EventBus;
9
- };
10
+ } & InvoiceFxOptions;
10
11
  export declare const FINANCE_ROUTE_RUNTIME_CONTAINER_KEY = "providers.finance.runtime";
11
- export interface FinanceRuntimeOptions extends FinanceDocumentRouteOptions, FinanceSettlementRouteOptions {
12
+ export interface FinanceRuntimeOptions extends FinanceDocumentRouteOptions, FinanceSettlementRouteOptions, InvoiceFxOptions {
12
13
  }
13
14
  export declare function buildFinanceRouteRuntime(bindings: Record<string, unknown>, options?: FinanceRuntimeOptions): FinanceRouteRuntime;
14
15
  //# sourceMappingURL=route-runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"route-runtime.d.ts","sourceRoot":"","sources":["../src/route-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAE9C,OAAO,KAAK,EAAE,2BAA2B,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAClG,OAAO,KAAK,EAAE,6BAA6B,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAA;AAEpG,MAAM,MAAM,mBAAmB,GAAG;IAChC,wBAAwB,CAAC,EAAE,wBAAwB,CAAA;IACnD,0BAA0B,CAAC,EAAE,2BAA2B,CAAC,4BAA4B,CAAC,CAAA;IACtF,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAA;IACjE,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACpB,CAAA;AAED,eAAO,MAAM,mCAAmC,8BAA8B,CAAA;AAE9E,MAAM,WAAW,qBACf,SAAQ,2BAA2B,EACjC,6BAA6B;CAAG;AAEpC,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,OAAO,GAAE,qBAA0B,GAClC,mBAAmB,CASrB"}
1
+ {"version":3,"file":"route-runtime.d.ts","sourceRoot":"","sources":["../src/route-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAE9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACvD,OAAO,KAAK,EAAE,2BAA2B,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAClG,OAAO,KAAK,EAAE,6BAA6B,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAA;AAEpG,MAAM,MAAM,mBAAmB,GAAG;IAChC,wBAAwB,CAAC,EAAE,wBAAwB,CAAA;IACnD,0BAA0B,CAAC,EAAE,2BAA2B,CAAC,4BAA4B,CAAC,CAAA;IACtF,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAA;IACjE,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACpB,GAAG,gBAAgB,CAAA;AAEpB,eAAO,MAAM,mCAAmC,8BAA8B,CAAA;AAE9E,MAAM,WAAW,qBACf,SAAQ,2BAA2B,EACjC,6BAA6B,EAC7B,gBAAgB;CAAG;AAEvB,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,OAAO,GAAE,qBAA0B,GAClC,mBAAmB,CAarB"}
@@ -5,5 +5,9 @@ export function buildFinanceRouteRuntime(bindings, options = {}) {
5
5
  resolveDocumentDownloadUrl: options.resolveDocumentDownloadUrl,
6
6
  invoiceSettlementPollers: options.resolveInvoiceSettlementPollers?.(bindings) ?? options.invoiceSettlementPollers ?? {},
7
7
  eventBus: options.resolveEventBus?.(bindings) ?? options.eventBus,
8
+ invoiceFxSettings: options.invoiceFxSettings,
9
+ resolveInvoiceFxSettings: options.resolveInvoiceFxSettings,
10
+ updateInvoiceFxSettings: options.updateInvoiceFxSettings,
11
+ resolveInvoiceExchangeRate: options.resolveInvoiceExchangeRate,
8
12
  };
9
13
  }
@@ -1,6 +1,7 @@
1
1
  import { type ActionLedgerRequestContextValues } from "@voyantjs/action-ledger";
2
2
  import type { EventBus } from "@voyantjs/core";
3
3
  import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
4
+ import { type InvoiceFxOptions } from "./invoice-fx.js";
4
5
  import { invoices } from "./schema.js";
5
6
  import { type CreateInvoiceFromBookingInput, type InvoiceFromBookingData } from "./service.js";
6
7
  /**
@@ -12,7 +13,7 @@ import { type CreateInvoiceFromBookingInput, type InvoiceFromBookingData } from
12
13
  * the workflow / route layer chooses when to mark them as "issued"
13
14
  * (a status change that's also a system signal).
14
15
  */
15
- export interface InvoiceIssueRuntime {
16
+ export interface InvoiceIssueRuntime extends InvoiceFxOptions {
16
17
  eventBus?: EventBus;
17
18
  actionLedgerContext?: ActionLedgerRequestContextValues;
18
19
  actionLedgerAuthorizationSource?: string | null;
@@ -24,6 +25,16 @@ export interface InvoiceIssuedEvent {
24
25
  bookingId: string | null;
25
26
  totalCents: number;
26
27
  currency: string;
28
+ /** Operator accounting/reporting currency when different from `currency`. */
29
+ baseCurrency?: string;
30
+ /** Spot rate for `currency` → `baseCurrency`. */
31
+ fxRate?: number;
32
+ /** Operator FX commission added on top of the spot rate. */
33
+ fxCommissionBps?: number;
34
+ /** `fxRate` after commission. Invoice providers should prefer this rate. */
35
+ effectiveRate?: number;
36
+ /** Optional invoice mention appended by providers when commission is non-zero. */
37
+ fxCommissionInvoiceMention?: string;
27
38
  /** Linkage when this invoice replaced a proforma. */
28
39
  convertedFromInvoiceId?: string | null;
29
40
  clientName?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"service-issue.d.ts","sourceRoot":"","sources":["../src/service-issue.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service-issue.d.ts","sourceRoot":"","sources":["../src/service-issue.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { appendActionLedgerMutation, } from "@voyantjs/action-ledger";
2
2
  import { bookings } from "@voyantjs/bookings/schema";
3
3
  import { asc, eq } from "drizzle-orm";
4
+ import { resolveInvoiceFxContext } from "./invoice-fx.js";
4
5
  import { invoiceLineItems, invoices, payments } from "./schema.js";
5
6
  import { buildInvoiceIssuedActionLedgerInput, financeService, } from "./service.js";
6
7
  const ISSUED_EVENT = "invoice.issued";
@@ -103,6 +104,9 @@ async function emitIssued(db, runtime, eventName, invoice) {
103
104
  issueDate: toDateString(invoice.issueDate),
104
105
  dueDate: toDateString(invoice.dueDate),
105
106
  };
107
+ const fx = await resolveInvoiceFxContext(db, invoice, runtime);
108
+ if (fx)
109
+ Object.assign(payload, fx);
106
110
  await runtime.eventBus.emit(eventName, payload);
107
111
  }
108
112
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyantjs/finance",
3
- "version": "0.66.0",
3
+ "version": "0.68.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -14,6 +14,11 @@
14
14
  "import": "./dist/action-ledger-drift.js",
15
15
  "default": "./dist/action-ledger-drift.js"
16
16
  },
17
+ "./invoice-fx": {
18
+ "types": "./dist/invoice-fx.d.ts",
19
+ "import": "./dist/invoice-fx.js",
20
+ "default": "./dist/invoice-fx.js"
21
+ },
17
22
  "./payment-link": {
18
23
  "types": "./dist/payment-link.d.ts",
19
24
  "import": "./dist/payment-link.js",
@@ -54,14 +59,14 @@
54
59
  "drizzle-orm": "^0.45.2",
55
60
  "hono": "^4.12.10",
56
61
  "zod": "^4.3.6",
57
- "@voyantjs/action-ledger": "0.66.0",
58
- "@voyantjs/bookings": "0.66.0",
59
- "@voyantjs/core": "0.66.0",
60
- "@voyantjs/db": "0.66.0",
61
- "@voyantjs/hono": "0.66.0",
62
- "@voyantjs/products": "0.66.0",
63
- "@voyantjs/utils": "0.66.0",
64
- "@voyantjs/storage": "0.66.0"
62
+ "@voyantjs/action-ledger": "0.68.0",
63
+ "@voyantjs/bookings": "0.68.0",
64
+ "@voyantjs/core": "0.68.0",
65
+ "@voyantjs/db": "0.68.0",
66
+ "@voyantjs/hono": "0.68.0",
67
+ "@voyantjs/products": "0.68.0",
68
+ "@voyantjs/utils": "0.68.0",
69
+ "@voyantjs/storage": "0.68.0"
65
70
  },
66
71
  "devDependencies": {
67
72
  "typescript": "^6.0.2",