@voyant-travel/commerce 0.2.3 → 0.3.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/dist/checkout/acceptance-signature.d.ts +4 -0
- package/dist/checkout/acceptance-signature.d.ts.map +1 -0
- package/dist/checkout/acceptance-signature.js +95 -0
- package/dist/checkout/finalize.d.ts +42 -0
- package/dist/checkout/finalize.d.ts.map +1 -0
- package/dist/checkout/finalize.js +208 -0
- package/dist/checkout/index.d.ts +26 -0
- package/dist/checkout/index.d.ts.map +1 -0
- package/dist/checkout/index.js +24 -0
- package/dist/checkout/materialization-support.d.ts +105 -0
- package/dist/checkout/materialization-support.d.ts.map +1 -0
- package/dist/checkout/materialization-support.js +451 -0
- package/dist/checkout/materialization-support.test.d.ts +2 -0
- package/dist/checkout/materialization-support.test.d.ts.map +1 -0
- package/dist/checkout/materialization-support.test.js +196 -0
- package/dist/checkout/materialization-tax.d.ts +10 -0
- package/dist/checkout/materialization-tax.d.ts.map +1 -0
- package/dist/checkout/materialization-tax.js +113 -0
- package/dist/checkout/materialization-tax.test.d.ts +2 -0
- package/dist/checkout/materialization-tax.test.d.ts.map +1 -0
- package/dist/checkout/materialization-tax.test.js +69 -0
- package/dist/checkout/materialization.d.ts +99 -0
- package/dist/checkout/materialization.d.ts.map +1 -0
- package/dist/checkout/materialization.js +269 -0
- package/dist/checkout/options.d.ts +89 -0
- package/dist/checkout/options.d.ts.map +1 -0
- package/dist/checkout/options.js +21 -0
- package/dist/checkout/routes.d.ts +21 -0
- package/dist/checkout/routes.d.ts.map +1 -0
- package/dist/checkout/routes.js +59 -0
- package/dist/checkout/start-service.d.ts +75 -0
- package/dist/checkout/start-service.d.ts.map +1 -0
- package/dist/checkout/start-service.js +415 -0
- package/dist/checkout/start-service.test.d.ts +2 -0
- package/dist/checkout/start-service.test.d.ts.map +1 -0
- package/dist/checkout/start-service.test.js +57 -0
- package/dist/markets/routes.d.ts +1 -1
- package/dist/markets/service-core.d.ts +1 -1
- package/dist/sellability/routes.d.ts +10 -10
- package/dist/sellability/service-records.d.ts +4 -4
- package/dist/sellability/service-snapshots.d.ts +2 -2
- package/dist/sellability/service.d.ts +10 -10
- package/package.json +27 -5
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { bookingItemTaxLines, computeBookingItemTaxLine, resolveBookingSellTaxRate, } from "@voyant-travel/finance";
|
|
2
|
+
import { eq } from "drizzle-orm";
|
|
3
|
+
import { inferSnapshotTaxFacts } from "./materialization-support.js";
|
|
4
|
+
export async function rebuildBookingItemTaxLines(db, bookingId, options) {
|
|
5
|
+
const { bookingItems: bookingItemsTable, bookings: bookingsTable } = await import("@voyant-travel/bookings/schema");
|
|
6
|
+
const { bookingCatalogSnapshotTable } = await import("@voyant-travel/catalog");
|
|
7
|
+
const [booking] = await db
|
|
8
|
+
.select()
|
|
9
|
+
.from(bookingsTable)
|
|
10
|
+
.where(eq(bookingsTable.id, bookingId))
|
|
11
|
+
.limit(1);
|
|
12
|
+
if (!booking)
|
|
13
|
+
return { rebuilt: 0, itemsWithoutSnapshot: 0 };
|
|
14
|
+
const items = await db
|
|
15
|
+
.select()
|
|
16
|
+
.from(bookingItemsTable)
|
|
17
|
+
.where(eq(bookingItemsTable.bookingId, bookingId));
|
|
18
|
+
let rebuilt = 0;
|
|
19
|
+
let itemsWithoutSnapshot = 0;
|
|
20
|
+
for (const item of items) {
|
|
21
|
+
const snapshot = await loadSnapshotForItem(db, bookingCatalogSnapshotTable, item);
|
|
22
|
+
if (!snapshot) {
|
|
23
|
+
itemsWithoutSnapshot += 1;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
await db.delete(bookingItemTaxLines).where(eq(bookingItemTaxLines.bookingItemId, item.id));
|
|
27
|
+
await materializeBookingItemTaxLine(db, booking, item.id, item.totalSellAmountCents ?? 0, snapshot, options);
|
|
28
|
+
rebuilt += 1;
|
|
29
|
+
}
|
|
30
|
+
return { rebuilt, itemsWithoutSnapshot };
|
|
31
|
+
}
|
|
32
|
+
async function loadSnapshotForItem(db, snapshotTable, item) {
|
|
33
|
+
const snapshotId = item.sourceSnapshotId;
|
|
34
|
+
if (!snapshotId) {
|
|
35
|
+
// Item wasn't materialized from a catalog snapshot; fall back to the
|
|
36
|
+
// booking-level snapshot if there is exactly one for this booking.
|
|
37
|
+
const rows = await db
|
|
38
|
+
.select()
|
|
39
|
+
.from(snapshotTable)
|
|
40
|
+
.where(eq(snapshotTable.booking_id, item.bookingId))
|
|
41
|
+
.limit(2);
|
|
42
|
+
return rows.length === 1 && rows[0] ? toMaterializationSnapshot(rows[0]) : null;
|
|
43
|
+
}
|
|
44
|
+
const [row] = await db
|
|
45
|
+
.select()
|
|
46
|
+
.from(snapshotTable)
|
|
47
|
+
.where(eq(snapshotTable.id, snapshotId))
|
|
48
|
+
.limit(1);
|
|
49
|
+
return row ? toMaterializationSnapshot(row) : null;
|
|
50
|
+
}
|
|
51
|
+
function toMaterializationSnapshot(row) {
|
|
52
|
+
return {
|
|
53
|
+
id: row.id,
|
|
54
|
+
entity_module: row.entity_module,
|
|
55
|
+
entity_id: row.entity_id,
|
|
56
|
+
source_kind: row.source_kind,
|
|
57
|
+
source_provider: row.source_provider,
|
|
58
|
+
source_ref: row.source_ref,
|
|
59
|
+
frozen_payload: row.frozen_payload,
|
|
60
|
+
pricing_base_amount: row.pricing_base_amount != null ? String(row.pricing_base_amount) : null,
|
|
61
|
+
pricing_taxes: row.pricing_taxes != null ? String(row.pricing_taxes) : null,
|
|
62
|
+
pricing_fees: row.pricing_fees != null ? String(row.pricing_fees) : null,
|
|
63
|
+
pricing_surcharges: row.pricing_surcharges != null ? String(row.pricing_surcharges) : null,
|
|
64
|
+
pricing_currency: row.pricing_currency,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export async function materializeBookingItemTaxLine(db, booking, bookingItemId, amountCents, snapshot, options) {
|
|
68
|
+
const currency = booking.sellCurrency ?? snapshot.pricing_currency ?? "EUR";
|
|
69
|
+
const taxRate = await resolveBookingSellTaxRate(db, {
|
|
70
|
+
productId: snapshot.entity_module === "products" ? snapshot.entity_id : null,
|
|
71
|
+
facts: inferSnapshotTaxFacts(snapshot),
|
|
72
|
+
}, {
|
|
73
|
+
resolveBookingTaxSettings: options.resolveBookingTaxSettings,
|
|
74
|
+
});
|
|
75
|
+
const policyLine = computeBookingItemTaxLine(taxRate, amountCents, currency);
|
|
76
|
+
// Fall back to the snapshot's `pricing_taxes` when the operator has no
|
|
77
|
+
// tax policy configured. Without this the booking page (which reads the
|
|
78
|
+
// snapshot directly) shows tax but the invoice (which reads
|
|
79
|
+
// `booking_item_tax_lines`) shows zero — operators see a mismatch.
|
|
80
|
+
// The booking total already includes this tax (sellAmountCents = base +
|
|
81
|
+
// taxes + fees + surcharges), so the row is `includedInPrice: true`.
|
|
82
|
+
const fallbackLine = policyLine ? null : buildSnapshotFallbackTaxLine(snapshot, currency);
|
|
83
|
+
const taxLine = policyLine ?? fallbackLine;
|
|
84
|
+
if (!taxLine)
|
|
85
|
+
return;
|
|
86
|
+
await db
|
|
87
|
+
.insert(bookingItemTaxLines)
|
|
88
|
+
.values({
|
|
89
|
+
bookingItemId,
|
|
90
|
+
...taxLine,
|
|
91
|
+
})
|
|
92
|
+
.onConflictDoNothing();
|
|
93
|
+
}
|
|
94
|
+
function buildSnapshotFallbackTaxLine(snapshot, currency) {
|
|
95
|
+
if (!snapshot.pricing_taxes)
|
|
96
|
+
return null;
|
|
97
|
+
const taxAmount = Number.parseFloat(snapshot.pricing_taxes);
|
|
98
|
+
if (!Number.isFinite(taxAmount) || taxAmount <= 0)
|
|
99
|
+
return null;
|
|
100
|
+
const taxCents = Math.round(taxAmount);
|
|
101
|
+
if (taxCents <= 0)
|
|
102
|
+
return null;
|
|
103
|
+
return {
|
|
104
|
+
code: "snapshot/tax",
|
|
105
|
+
name: "Tax",
|
|
106
|
+
scope: "included",
|
|
107
|
+
currency,
|
|
108
|
+
amountCents: taxCents,
|
|
109
|
+
rateBasisPoints: null,
|
|
110
|
+
includedInPrice: true,
|
|
111
|
+
sortOrder: 0,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"materialization-tax.test.d.ts","sourceRoot":"","sources":["../../src/checkout/materialization-tax.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { materializeBookingItemTaxLine } from "./materialization-tax.js";
|
|
3
|
+
vi.mock("@voyant-travel/finance", async (importOriginal) => {
|
|
4
|
+
const actual = await importOriginal();
|
|
5
|
+
return {
|
|
6
|
+
...actual,
|
|
7
|
+
// Force "no tax policy" so the snapshot-fallback path is exercised.
|
|
8
|
+
resolveBookingSellTaxRate: vi.fn().mockResolvedValue(null),
|
|
9
|
+
computeBookingItemTaxLine: vi.fn().mockReturnValue(null),
|
|
10
|
+
};
|
|
11
|
+
});
|
|
12
|
+
function snapshot(overrides = {}) {
|
|
13
|
+
return {
|
|
14
|
+
id: "snap_1",
|
|
15
|
+
entity_module: "products",
|
|
16
|
+
entity_id: "prod_1",
|
|
17
|
+
source_kind: "demo",
|
|
18
|
+
source_provider: null,
|
|
19
|
+
source_ref: null,
|
|
20
|
+
frozen_payload: null,
|
|
21
|
+
pricing_base_amount: null,
|
|
22
|
+
pricing_taxes: null,
|
|
23
|
+
pricing_fees: null,
|
|
24
|
+
pricing_surcharges: null,
|
|
25
|
+
pricing_currency: "EUR",
|
|
26
|
+
...overrides,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const booking = { sellCurrency: "EUR" };
|
|
30
|
+
describe("materializeBookingItemTaxLine", () => {
|
|
31
|
+
it("writes a snapshot-fallback tax line when no policy line resolves", async () => {
|
|
32
|
+
const inserted = [];
|
|
33
|
+
const db = {
|
|
34
|
+
insert: () => ({
|
|
35
|
+
values: (v) => ({
|
|
36
|
+
onConflictDoNothing: async () => {
|
|
37
|
+
inserted.push(v);
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
await materializeBookingItemTaxLine(db, booking, "item_1", 11900, snapshot({ pricing_taxes: "1900" }), {
|
|
43
|
+
resolveBookingTaxSettings: vi.fn(),
|
|
44
|
+
});
|
|
45
|
+
expect(inserted).toHaveLength(1);
|
|
46
|
+
expect(inserted[0]).toMatchObject({
|
|
47
|
+
bookingItemId: "item_1",
|
|
48
|
+
code: "snapshot/tax",
|
|
49
|
+
amountCents: 1900,
|
|
50
|
+
includedInPrice: true,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
it("writes nothing when there is no policy line and no snapshot tax", async () => {
|
|
54
|
+
const inserted = [];
|
|
55
|
+
const db = {
|
|
56
|
+
insert: () => ({
|
|
57
|
+
values: (v) => ({
|
|
58
|
+
onConflictDoNothing: async () => {
|
|
59
|
+
inserted.push(v);
|
|
60
|
+
},
|
|
61
|
+
}),
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
await materializeBookingItemTaxLine(db, booking, "item_1", 10000, snapshot(), {
|
|
65
|
+
resolveBookingTaxSettings: vi.fn(),
|
|
66
|
+
});
|
|
67
|
+
expect(inserted).toHaveLength(0);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { bookings } from "@voyant-travel/bookings/schema";
|
|
2
|
+
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
3
|
+
import type { CheckoutModuleOptions } from "./options.js";
|
|
4
|
+
export { rebuildBookingItemTaxLines } from "./materialization-tax.js";
|
|
5
|
+
/**
|
|
6
|
+
* Look up the catalog snapshot for a `bookingId` (the catalog plane
|
|
7
|
+
* always writes one) and materialize a real bookings row plus the
|
|
8
|
+
* traveler / item children. Used when /book went through the sourced
|
|
9
|
+
* arm — sourced adapters don't write to the bookings table directly,
|
|
10
|
+
* so the checkout flow has to bridge the snapshot into normal
|
|
11
|
+
* booking shape before it can place payment sessions, transition
|
|
12
|
+
* status, etc.
|
|
13
|
+
*
|
|
14
|
+
* The booking_drafts table carries the customer-entered detail
|
|
15
|
+
* (passengers, billing contact, configure pax / dates) — we look
|
|
16
|
+
* up the draft via `consumed_booking_id` and pull contact + traveler
|
|
17
|
+
* rows out of it. Snapshot supplies pricing + entity refs.
|
|
18
|
+
*/
|
|
19
|
+
export declare function materializeBookingFromSnapshot(db: PostgresJsDatabase, bookingId: string, env: Record<string, unknown>, options: CheckoutModuleOptions): Promise<typeof bookings.$inferSelect | null>;
|
|
20
|
+
export interface DraftPayload {
|
|
21
|
+
billing?: {
|
|
22
|
+
contact?: {
|
|
23
|
+
firstName?: string;
|
|
24
|
+
lastName?: string;
|
|
25
|
+
email?: string;
|
|
26
|
+
phone?: string;
|
|
27
|
+
};
|
|
28
|
+
address?: {
|
|
29
|
+
country?: string;
|
|
30
|
+
city?: string;
|
|
31
|
+
line1?: string;
|
|
32
|
+
line2?: string;
|
|
33
|
+
postal?: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
configure?: {
|
|
37
|
+
pax?: {
|
|
38
|
+
adult?: number;
|
|
39
|
+
child?: number;
|
|
40
|
+
infant?: number;
|
|
41
|
+
};
|
|
42
|
+
departureSlotId?: string;
|
|
43
|
+
departureDate?: string;
|
|
44
|
+
dateRange?: {
|
|
45
|
+
checkIn?: string;
|
|
46
|
+
checkOut?: string;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
travelers?: Array<{
|
|
50
|
+
rowId?: string;
|
|
51
|
+
firstName?: string;
|
|
52
|
+
lastName?: string;
|
|
53
|
+
email?: string;
|
|
54
|
+
phone?: string;
|
|
55
|
+
band?: string;
|
|
56
|
+
dateOfBirth?: string;
|
|
57
|
+
nationality?: string;
|
|
58
|
+
documentType?: "passport" | "id_card" | "driver_license" | "visa" | "other";
|
|
59
|
+
documentNumber?: string;
|
|
60
|
+
documentExpiry?: string;
|
|
61
|
+
passportNumber?: string;
|
|
62
|
+
passportExpiry?: string;
|
|
63
|
+
passportExpiresAt?: string;
|
|
64
|
+
dietaryRequirements?: string;
|
|
65
|
+
accessibilityNeeds?: string;
|
|
66
|
+
preferredLanguage?: string;
|
|
67
|
+
specialRequests?: string;
|
|
68
|
+
isPrimary?: boolean;
|
|
69
|
+
isLeadTraveler?: boolean;
|
|
70
|
+
documents?: Record<string, unknown>;
|
|
71
|
+
}>;
|
|
72
|
+
entity?: {
|
|
73
|
+
module?: string;
|
|
74
|
+
id?: string;
|
|
75
|
+
};
|
|
76
|
+
internalNotes?: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Snapshot subset `materializeChildren` reads from. The catalog table
|
|
80
|
+
* has more columns (idempotency_key, captured_at, etc.), but children
|
|
81
|
+
* materialization only needs the parts that drive line items + supplier
|
|
82
|
+
* statuses.
|
|
83
|
+
*/
|
|
84
|
+
export type MaterializationSnapshot = {
|
|
85
|
+
/** Snapshot id — stamped on each `booking_items.source_snapshot_id`. */
|
|
86
|
+
id?: string;
|
|
87
|
+
entity_module: string;
|
|
88
|
+
entity_id: string;
|
|
89
|
+
source_kind: string;
|
|
90
|
+
source_provider: string | null;
|
|
91
|
+
source_ref: string | null;
|
|
92
|
+
frozen_payload: Record<string, unknown> | null;
|
|
93
|
+
pricing_base_amount: string | null;
|
|
94
|
+
pricing_taxes: string | null;
|
|
95
|
+
pricing_fees: string | null;
|
|
96
|
+
pricing_surcharges: string | null;
|
|
97
|
+
pricing_currency: string | null;
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=materialization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"materialization.d.ts","sourceRoot":"","sources":["../../src/checkout/materialization.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
// agent-quality: file-size exception -- owner: commerce; the snapshot→booking
|
|
2
|
+
// materialization (parent row + traveler/item/allocation/supplier children) is
|
|
3
|
+
// one cohesive bridge; splitting it would scatter a single checkout step.
|
|
4
|
+
import { bookings } from "@voyant-travel/bookings/schema";
|
|
5
|
+
import { OWNED_SOURCE_KIND } from "@voyant-travel/catalog/booking-engine";
|
|
6
|
+
import { eq } from "drizzle-orm";
|
|
7
|
+
import { extractBookingDates, extractItemDates, extractItemDescription, materializeBookingAllocations, materializeTravelerTravelDetails, resolveLineItemTitle, resolveSupplierFromSnapshot, resolveUpstreamCostCents, travelerBandToCategory, } from "./materialization-support.js";
|
|
8
|
+
import { materializeBookingItemTaxLine } from "./materialization-tax.js";
|
|
9
|
+
export { rebuildBookingItemTaxLines } from "./materialization-tax.js";
|
|
10
|
+
/**
|
|
11
|
+
* Look up the catalog snapshot for a `bookingId` (the catalog plane
|
|
12
|
+
* always writes one) and materialize a real bookings row plus the
|
|
13
|
+
* traveler / item children. Used when /book went through the sourced
|
|
14
|
+
* arm — sourced adapters don't write to the bookings table directly,
|
|
15
|
+
* so the checkout flow has to bridge the snapshot into normal
|
|
16
|
+
* booking shape before it can place payment sessions, transition
|
|
17
|
+
* status, etc.
|
|
18
|
+
*
|
|
19
|
+
* The booking_drafts table carries the customer-entered detail
|
|
20
|
+
* (passengers, billing contact, configure pax / dates) — we look
|
|
21
|
+
* up the draft via `consumed_booking_id` and pull contact + traveler
|
|
22
|
+
* rows out of it. Snapshot supplies pricing + entity refs.
|
|
23
|
+
*/
|
|
24
|
+
export async function materializeBookingFromSnapshot(db, bookingId, env, options) {
|
|
25
|
+
const { bookingCatalogSnapshotTable } = await import("@voyant-travel/catalog");
|
|
26
|
+
const { bookingDraftsTable } = await import("@voyant-travel/catalog/booking-engine");
|
|
27
|
+
const [snapshot] = await db
|
|
28
|
+
.select()
|
|
29
|
+
.from(bookingCatalogSnapshotTable)
|
|
30
|
+
.where(eq(bookingCatalogSnapshotTable.booking_id, bookingId))
|
|
31
|
+
.limit(1);
|
|
32
|
+
if (!snapshot)
|
|
33
|
+
return null;
|
|
34
|
+
const baseAmount = snapshot.pricing_base_amount
|
|
35
|
+
? Number.parseFloat(String(snapshot.pricing_base_amount))
|
|
36
|
+
: 0;
|
|
37
|
+
const taxes = snapshot.pricing_taxes ? Number.parseFloat(String(snapshot.pricing_taxes)) : 0;
|
|
38
|
+
const fees = snapshot.pricing_fees ? Number.parseFloat(String(snapshot.pricing_fees)) : 0;
|
|
39
|
+
const surcharges = snapshot.pricing_surcharges
|
|
40
|
+
? Number.parseFloat(String(snapshot.pricing_surcharges))
|
|
41
|
+
: 0;
|
|
42
|
+
const sellAmountCents = Math.round(baseAmount + taxes + fees + surcharges);
|
|
43
|
+
const sellCurrency = snapshot.pricing_currency ?? "EUR";
|
|
44
|
+
// Pull the consuming draft so we can copy the customer-entered
|
|
45
|
+
// billing contact + travelers + dates into the booking row.
|
|
46
|
+
const [draftRow] = await db
|
|
47
|
+
.select()
|
|
48
|
+
.from(bookingDraftsTable)
|
|
49
|
+
.where(eq(bookingDraftsTable.consumed_booking_id, bookingId))
|
|
50
|
+
.limit(1);
|
|
51
|
+
const draftPayload = (draftRow?.draft_payload ?? {});
|
|
52
|
+
const frozenPayload = (snapshot.frozen_payload ?? {});
|
|
53
|
+
const bookingDates = extractBookingDates({
|
|
54
|
+
frozen_payload: frozenPayload,
|
|
55
|
+
}, draftPayload);
|
|
56
|
+
const billingContact = draftPayload.billing?.contact;
|
|
57
|
+
const billingAddress = draftPayload.billing?.address;
|
|
58
|
+
const config = draftPayload.configure;
|
|
59
|
+
const pax = config?.pax;
|
|
60
|
+
const totalPax = pax ? (pax.adult ?? 0) + (pax.child ?? 0) + (pax.infant ?? 0) : null;
|
|
61
|
+
const startDate = bookingDates.startDate;
|
|
62
|
+
const endDate = bookingDates.endDate;
|
|
63
|
+
const bookingNumber = `BK-${bookingId.slice(-12).toUpperCase()}`;
|
|
64
|
+
const [row] = await db
|
|
65
|
+
.insert(bookings)
|
|
66
|
+
.values({
|
|
67
|
+
id: bookingId,
|
|
68
|
+
bookingNumber,
|
|
69
|
+
status: "on_hold",
|
|
70
|
+
sourceType: "direct",
|
|
71
|
+
sellCurrency,
|
|
72
|
+
sellAmountCents,
|
|
73
|
+
contactFirstName: billingContact?.firstName ?? null,
|
|
74
|
+
contactLastName: billingContact?.lastName ?? null,
|
|
75
|
+
contactEmail: billingContact?.email ?? null,
|
|
76
|
+
contactPhone: billingContact?.phone ?? null,
|
|
77
|
+
contactCountry: billingAddress?.country ?? null,
|
|
78
|
+
contactCity: billingAddress?.city ?? null,
|
|
79
|
+
contactAddressLine1: billingAddress?.line1 ?? null,
|
|
80
|
+
contactAddressLine2: billingAddress?.line2 ?? null,
|
|
81
|
+
contactPostalCode: billingAddress?.postal ?? null,
|
|
82
|
+
startDate,
|
|
83
|
+
endDate,
|
|
84
|
+
pax: totalPax && totalPax > 0 ? totalPax : null,
|
|
85
|
+
internalNotes: typeof draftPayload.internalNotes === "string" ? draftPayload.internalNotes : null,
|
|
86
|
+
})
|
|
87
|
+
.onConflictDoNothing({ target: bookings.id })
|
|
88
|
+
.returning();
|
|
89
|
+
const inserted = row ?? null;
|
|
90
|
+
// Materialize travelers + a single line item per booked entity so
|
|
91
|
+
// the operator detail page has something to render. No-ops on
|
|
92
|
+
// re-entry (race or retry) because we only run this when the
|
|
93
|
+
// bookings row was just inserted.
|
|
94
|
+
if (inserted) {
|
|
95
|
+
await materializeChildren(db, inserted, {
|
|
96
|
+
id: snapshot.id,
|
|
97
|
+
entity_module: snapshot.entity_module,
|
|
98
|
+
entity_id: snapshot.entity_id,
|
|
99
|
+
source_kind: snapshot.source_kind,
|
|
100
|
+
source_provider: snapshot.source_provider,
|
|
101
|
+
source_ref: snapshot.source_ref,
|
|
102
|
+
frozen_payload: (snapshot.frozen_payload ?? {}),
|
|
103
|
+
pricing_base_amount: snapshot.pricing_base_amount != null ? String(snapshot.pricing_base_amount) : null,
|
|
104
|
+
pricing_taxes: snapshot.pricing_taxes != null ? String(snapshot.pricing_taxes) : null,
|
|
105
|
+
pricing_fees: snapshot.pricing_fees != null ? String(snapshot.pricing_fees) : null,
|
|
106
|
+
pricing_surcharges: snapshot.pricing_surcharges != null ? String(snapshot.pricing_surcharges) : null,
|
|
107
|
+
pricing_currency: snapshot.pricing_currency,
|
|
108
|
+
}, draftPayload, env, options);
|
|
109
|
+
}
|
|
110
|
+
if (inserted)
|
|
111
|
+
return inserted;
|
|
112
|
+
// Race: another request already inserted; re-fetch.
|
|
113
|
+
const [existing] = await db.select().from(bookings).where(eq(bookings.id, bookingId)).limit(1);
|
|
114
|
+
return existing ?? null;
|
|
115
|
+
}
|
|
116
|
+
async function materializeChildren(db, booking, snapshot, draftPayload, env, options) {
|
|
117
|
+
const { bookingTravelers, bookingItems, bookingSupplierStatuses } = await import("@voyant-travel/bookings/schema");
|
|
118
|
+
const travelers = draftPayload.travelers ?? [];
|
|
119
|
+
if (travelers.length > 0) {
|
|
120
|
+
const travelerRows = travelers
|
|
121
|
+
.map((t, idx) => ({
|
|
122
|
+
draftTraveler: t,
|
|
123
|
+
row: {
|
|
124
|
+
bookingId: booking.id,
|
|
125
|
+
firstName: t.firstName ?? "Traveler",
|
|
126
|
+
lastName: t.lastName ?? `${idx + 1}`,
|
|
127
|
+
email: t.email ?? null,
|
|
128
|
+
phone: t.phone ?? null,
|
|
129
|
+
travelerCategory: travelerBandToCategory(t.band),
|
|
130
|
+
preferredLanguage: t.preferredLanguage ?? null,
|
|
131
|
+
specialRequests: t.specialRequests ?? null,
|
|
132
|
+
isPrimary: t.isPrimary ?? t.isLeadTraveler ?? idx === 0,
|
|
133
|
+
},
|
|
134
|
+
}))
|
|
135
|
+
.filter(({ draftTraveler }) => {
|
|
136
|
+
return ((draftTraveler.firstName?.length ?? 0) > 0 || (draftTraveler.lastName?.length ?? 0) > 0);
|
|
137
|
+
});
|
|
138
|
+
const rows = travelerRows.map(({ row }) => row);
|
|
139
|
+
if (rows.length > 0) {
|
|
140
|
+
const insertedTravelers = await db.insert(bookingTravelers).values(rows).returning();
|
|
141
|
+
try {
|
|
142
|
+
await materializeTravelerTravelDetails(db, insertedTravelers, travelerRows.map(({ draftTraveler }) => draftTraveler), env);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
console.warn("[catalog-checkout] traveler travel-details materialization failed", err);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// One item summarizing the booked entity, so the items tab isn't
|
|
150
|
+
// empty. Real verticals (cruises with cabin lines, accommodations with
|
|
151
|
+
// room lines) fan this out per their own conventions; this is the
|
|
152
|
+
// generic fallback so sourced products show up in the UI.
|
|
153
|
+
//
|
|
154
|
+
// Resolve a real product title rather than the dumb fallback
|
|
155
|
+
// "Tour booking". For sourced products, the projection captured
|
|
156
|
+
// by `catalog_sourced_entries` carries the upstream name; for
|
|
157
|
+
// owned products, the local `products.title` is canonical.
|
|
158
|
+
const resolvedTitle = await resolveLineItemTitle(db, snapshot, options);
|
|
159
|
+
const itemDates = extractItemDates(snapshot, draftPayload, booking);
|
|
160
|
+
const itemDescription = extractItemDescription(snapshot);
|
|
161
|
+
// Item-level cost mirrors the booking-level cost we set later: when
|
|
162
|
+
// the upstream provides a net rate (Bokun-style net/gross split),
|
|
163
|
+
// use it; otherwise fall back to sell. Owned bookings skip cost on
|
|
164
|
+
// the item — the operator IS the supplier, no "cost" makes sense.
|
|
165
|
+
const sellAmountCents = booking.sellAmountCents ?? 0;
|
|
166
|
+
const upstreamCostCents = snapshot.source_kind !== OWNED_SOURCE_KIND ? await resolveUpstreamCostCents(db, snapshot) : null;
|
|
167
|
+
const itemCostAmountCents = snapshot.source_kind !== OWNED_SOURCE_KIND ? (upstreamCostCents ?? sellAmountCents) : null;
|
|
168
|
+
const itemQuantity = booking.pax ?? 1;
|
|
169
|
+
const insertedItems = await db
|
|
170
|
+
.insert(bookingItems)
|
|
171
|
+
.values({
|
|
172
|
+
bookingId: booking.id,
|
|
173
|
+
title: resolvedTitle,
|
|
174
|
+
description: itemDescription,
|
|
175
|
+
productId: snapshot.entity_module === "products" ? snapshot.entity_id : null,
|
|
176
|
+
quantity: itemQuantity,
|
|
177
|
+
itemType: "service",
|
|
178
|
+
status: "on_hold",
|
|
179
|
+
serviceDate: itemDates.serviceDate ?? null,
|
|
180
|
+
startsAt: itemDates.startsAt ?? null,
|
|
181
|
+
endsAt: itemDates.endsAt ?? null,
|
|
182
|
+
unitSellAmountCents: booking.pax && booking.pax > 0 && booking.sellAmountCents
|
|
183
|
+
? Math.round(booking.sellAmountCents / booking.pax)
|
|
184
|
+
: (booking.sellAmountCents ?? 0),
|
|
185
|
+
totalSellAmountCents: booking.sellAmountCents ?? 0,
|
|
186
|
+
sellCurrency: booking.sellCurrency,
|
|
187
|
+
...(itemCostAmountCents != null
|
|
188
|
+
? {
|
|
189
|
+
costCurrency: booking.sellCurrency,
|
|
190
|
+
unitCostAmountCents: itemQuantity > 0
|
|
191
|
+
? Math.round(itemCostAmountCents / itemQuantity)
|
|
192
|
+
: itemCostAmountCents,
|
|
193
|
+
totalCostAmountCents: itemCostAmountCents,
|
|
194
|
+
}
|
|
195
|
+
: {}),
|
|
196
|
+
sourceSnapshotId: snapshot.id ?? null,
|
|
197
|
+
})
|
|
198
|
+
.onConflictDoNothing()
|
|
199
|
+
.returning();
|
|
200
|
+
for (const item of insertedItems) {
|
|
201
|
+
await materializeBookingItemTaxLine(db, booking, item.id, item.totalSellAmountCents ?? 0, snapshot, options);
|
|
202
|
+
}
|
|
203
|
+
await materializeBookingAllocations(db, booking, insertedItems, draftPayload, snapshot);
|
|
204
|
+
// Sourced bookings: auto-populate the supplier-status row + booking
|
|
205
|
+
// cost columns from the catalog snapshot. Without this the operator
|
|
206
|
+
// sees an empty "Furnizori" tab and "Cost / Marja —" on a deal
|
|
207
|
+
// we already know the supplier for. Owned bookings skip — the
|
|
208
|
+
// operator IS the supplier.
|
|
209
|
+
if (snapshot.source_kind !== OWNED_SOURCE_KIND) {
|
|
210
|
+
const supplierInfo = await resolveSupplierFromSnapshot(db, snapshot);
|
|
211
|
+
if (supplierInfo) {
|
|
212
|
+
// Cost = sell as a working assumption (zero-markup default).
|
|
213
|
+
// Operators with a configured net/gross split should override
|
|
214
|
+
// via the supplier-status edit form. We mark the row's notes so
|
|
215
|
+
// it's clear this came from auto-fill, not from a real
|
|
216
|
+
// supplier confirmation.
|
|
217
|
+
const costAmountCents = supplierInfo.upstreamCostCents ?? sellAmountCents;
|
|
218
|
+
const costCurrency = booking.sellCurrency;
|
|
219
|
+
try {
|
|
220
|
+
await db
|
|
221
|
+
.insert(bookingSupplierStatuses)
|
|
222
|
+
.values({
|
|
223
|
+
bookingId: booking.id,
|
|
224
|
+
supplierServiceId: supplierInfo.supplierServiceId ?? null,
|
|
225
|
+
serviceName: supplierInfo.serviceName,
|
|
226
|
+
supplierReference: supplierInfo.supplierReference ?? null,
|
|
227
|
+
costCurrency,
|
|
228
|
+
costAmountCents,
|
|
229
|
+
status: "pending",
|
|
230
|
+
notes: `Auto-populated from ${snapshot.source_kind} catalog snapshot. ` +
|
|
231
|
+
"Verify against supplier confirmation when it lands.",
|
|
232
|
+
})
|
|
233
|
+
.onConflictDoNothing();
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
console.warn("[catalog-checkout] auto supplier-status insert failed", err);
|
|
237
|
+
}
|
|
238
|
+
// Stamp booking-level cost so the header's Cost/Marja card
|
|
239
|
+
// shows something meaningful. Margin computed against the
|
|
240
|
+
// current cost+sell — when sell == cost, margin is 0; that's
|
|
241
|
+
// accurate for our zero-markup default until the operator
|
|
242
|
+
// updates the cost.
|
|
243
|
+
try {
|
|
244
|
+
const margin = sellAmountCents > 0
|
|
245
|
+
? Math.round(((sellAmountCents - costAmountCents) / sellAmountCents) * 100)
|
|
246
|
+
: 0;
|
|
247
|
+
const baseCurrencyMatches = booking.baseCurrency != null && booking.baseCurrency === booking.sellCurrency;
|
|
248
|
+
await db
|
|
249
|
+
.update(bookings)
|
|
250
|
+
.set({
|
|
251
|
+
costAmountCents,
|
|
252
|
+
// Only set base_cost_amount_cents when base_currency is
|
|
253
|
+
// already set on the booking. The check constraint
|
|
254
|
+
// `ck_bookings_base_currency_amounts` rejects setting
|
|
255
|
+
// base_*_amount with no base_currency.
|
|
256
|
+
baseCostAmountCents: baseCurrencyMatches
|
|
257
|
+
? costAmountCents
|
|
258
|
+
: booking.baseCostAmountCents,
|
|
259
|
+
marginPercent: margin,
|
|
260
|
+
updatedAt: new Date(),
|
|
261
|
+
})
|
|
262
|
+
.where(eq(bookings.id, booking.id));
|
|
263
|
+
}
|
|
264
|
+
catch (err) {
|
|
265
|
+
console.warn("[catalog-checkout] booking cost update failed", err);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment-supplied options for the catalog-checkout cluster.
|
|
3
|
+
*
|
|
4
|
+
* The checkout business logic (materialization, tax, start-service,
|
|
5
|
+
* acceptance signature) lives in `@voyant-travel/commerce`. The two
|
|
6
|
+
* genuinely deployment-specific dependencies are injected here as
|
|
7
|
+
* structural functions so the package never statically imports the
|
|
8
|
+
* deployment, and — crucially — never imports `@voyant-travel/inventory`
|
|
9
|
+
* (which already depends on `@voyant-travel/commerce`; a static import
|
|
10
|
+
* would cycle).
|
|
11
|
+
*
|
|
12
|
+
* - `resolveBookingTaxSettings` — reads the operator's tax-mode /
|
|
13
|
+
* tax-policy-profile row. The deployment owns the settings table.
|
|
14
|
+
* - `getOwnedProductName` — resolves an owned product's title for the
|
|
15
|
+
* line-item fallback. The package can't import inventory's
|
|
16
|
+
* `productsService.getProductById` without cycling, so the
|
|
17
|
+
* deployment hands it in.
|
|
18
|
+
* - `resolveBankTransferInstructions` — reads the operator profile +
|
|
19
|
+
* payment-instruction rows for the bank-transfer checkout path.
|
|
20
|
+
*/
|
|
21
|
+
import type { BookingTaxSettings } from "@voyant-travel/finance";
|
|
22
|
+
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
23
|
+
/**
|
|
24
|
+
* Bank-transfer instructions surfaced on the bank_transfer_instructions
|
|
25
|
+
* checkout result. The deployment composes these from its operator
|
|
26
|
+
* profile / payment-instruction rows + env fallbacks.
|
|
27
|
+
*/
|
|
28
|
+
export interface CheckoutBankTransferInstructions {
|
|
29
|
+
beneficiary: string;
|
|
30
|
+
iban: string;
|
|
31
|
+
bankName: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Options shared by the checkout materialization + start-service. All
|
|
35
|
+
* structural — no deployment imports, no platform bindings.
|
|
36
|
+
*/
|
|
37
|
+
export interface CheckoutModuleOptions {
|
|
38
|
+
/**
|
|
39
|
+
* Read the operator's booking tax settings (tax-price mode + policy
|
|
40
|
+
* profile). Modelled on the operator's `resolveBookingTaxSettings`;
|
|
41
|
+
* structural so commerce doesn't import the deployment settings table.
|
|
42
|
+
*/
|
|
43
|
+
resolveBookingTaxSettings(db: PostgresJsDatabase): Promise<BookingTaxSettings>;
|
|
44
|
+
/**
|
|
45
|
+
* Resolve an owned product's display name for the line-item title
|
|
46
|
+
* fallback. INJECTED because `@voyant-travel/inventory` depends on
|
|
47
|
+
* `@voyant-travel/commerce` — a static import would cycle. Returns the
|
|
48
|
+
* product name, or null when not an owned product / not found.
|
|
49
|
+
*/
|
|
50
|
+
getOwnedProductName(db: PostgresJsDatabase, entityModule: string, entityId: string): Promise<string | null>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Options for the checkout-start service (`startCatalogCheckout`). Extends
|
|
54
|
+
* the shared module options with the bank-transfer instruction reader the
|
|
55
|
+
* bank_transfer path needs.
|
|
56
|
+
*/
|
|
57
|
+
export interface CheckoutStartOptions extends CheckoutModuleOptions {
|
|
58
|
+
/**
|
|
59
|
+
* Resolve the bank-transfer instructions for the bank_transfer payment
|
|
60
|
+
* intent. The deployment reads its operator profile / payment
|
|
61
|
+
* instructions and applies env fallbacks.
|
|
62
|
+
*/
|
|
63
|
+
resolveBankTransferInstructions(db: PostgresJsDatabase, env: Record<string, string | undefined>): Promise<CheckoutBankTransferInstructions>;
|
|
64
|
+
/**
|
|
65
|
+
* Start the card-payment provider session for the `card` checkout intent.
|
|
66
|
+
* INJECTED so commerce never imports a specific payment provider (which
|
|
67
|
+
* would pull a provider package into the retail-spine closure). The
|
|
68
|
+
* deployment owns the provider choice (e.g. Netopia) and the
|
|
69
|
+
* provider-specific placeholder billing.
|
|
70
|
+
*
|
|
71
|
+
* Returns `{ redirectUrl }` to redirect the customer to the provider, or
|
|
72
|
+
* `null`/`undefined` when no card provider is configured — in which case
|
|
73
|
+
* the checkout falls back to the `card_pending` confirmation-page poll.
|
|
74
|
+
*/
|
|
75
|
+
startCardPayment?(params: {
|
|
76
|
+
db: PostgresJsDatabase;
|
|
77
|
+
sessionId: string;
|
|
78
|
+
billing: {
|
|
79
|
+
email: string;
|
|
80
|
+
firstName: string;
|
|
81
|
+
lastName: string;
|
|
82
|
+
};
|
|
83
|
+
description: string;
|
|
84
|
+
returnUrl?: string;
|
|
85
|
+
}): Promise<{
|
|
86
|
+
redirectUrl: string | null;
|
|
87
|
+
} | null>;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/checkout/options.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE;;;;GAIG;AACH,MAAM,WAAW,gCAAgC;IAC/C,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAC9E;;;;;OAKG;IACH,mBAAmB,CACjB,EAAE,EAAE,kBAAkB,EACtB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IACjE;;;;OAIG;IACH,+BAA+B,CAC7B,EAAE,EAAE,kBAAkB,EACtB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC,OAAO,CAAC,gCAAgC,CAAC,CAAA;IAC5C;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,CAAC,MAAM,EAAE;QACxB,EAAE,EAAE,kBAAkB,CAAA;QACtB,SAAS,EAAE,MAAM,CAAA;QACjB,OAAO,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAA;QAC/D,WAAW,EAAE,MAAM,CAAA;QACnB,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,GAAG,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;CACnD"}
|