@voyantjs/bookings 0.9.0 → 0.10.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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/markets-ref.d.ts +151 -0
- package/dist/markets-ref.d.ts.map +1 -0
- package/dist/markets-ref.js +19 -0
- package/dist/pii-redaction.d.ts +89 -0
- package/dist/pii-redaction.d.ts.map +1 -0
- package/dist/pii-redaction.js +120 -0
- package/dist/pii.d.ts +1 -0
- package/dist/pii.d.ts.map +1 -1
- package/dist/pii.js +20 -1
- package/dist/routes-groups.d.ts +3 -2
- package/dist/routes-groups.d.ts.map +1 -1
- package/dist/routes-public.d.ts +11 -13
- package/dist/routes-public.d.ts.map +1 -1
- package/dist/routes-public.js +3 -3
- package/dist/routes.d.ts +16 -8
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +57 -9
- package/dist/schema/travel-details.d.ts +37 -0
- package/dist/schema/travel-details.d.ts.map +1 -1
- package/dist/schema/travel-details.js +6 -0
- package/dist/schema-core.d.ts +17 -17
- package/dist/schema-core.d.ts.map +1 -1
- package/dist/schema-core.js +8 -2
- package/dist/schema-items.d.ts.map +1 -1
- package/dist/schema-items.js +6 -1
- package/dist/service-public.d.ts +0 -6
- package/dist/service-public.d.ts.map +1 -1
- package/dist/service-public.js +0 -4
- package/dist/service.d.ts +55 -46
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +288 -89
- package/dist/state-machine.d.ts +29 -0
- package/dist/state-machine.d.ts.map +1 -0
- package/dist/state-machine.js +39 -0
- package/dist/validation-public.d.ts +0 -6
- package/dist/validation-public.d.ts.map +1 -1
- package/dist/validation-public.js +0 -2
- package/dist/validation.d.ts +0 -4
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +0 -2
- package/dist/workflows/refund-booking.d.ts +87 -0
- package/dist/workflows/refund-booking.d.ts.map +1 -0
- package/dist/workflows/refund-booking.js +210 -0
- package/package.json +7 -6
package/dist/service.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { and, asc, desc, eq, exists, ilike, inArray, lte, ne, or, sql } from "drizzle-orm";
|
|
2
2
|
import { availabilitySlotsRef } from "./availability-ref.js";
|
|
3
|
+
import { exchangeRatesRef } from "./markets-ref.js";
|
|
3
4
|
import { bookingItemProductDetailsRef, bookingProductDetailsRef, optionUnitsRef, productDayServicesRef, productDaysRef, productItinerariesRef, productOptionsRef, productsRef, productTicketSettingsRef, } from "./products-ref.js";
|
|
4
5
|
import { bookingActivityLog, bookingAllocations, bookingDocuments, bookingFulfillments, bookingItems, bookingItemTravelers, bookingNotes, bookingRedemptionEvents, bookingStaffAssignments, bookingSupplierStatuses, bookings, bookingTravelers, } from "./schema.js";
|
|
5
6
|
import { cleanupGroupOnBookingCancelled } from "./service-groups.js";
|
|
7
|
+
import { BookingTransitionError, canTransitionBooking, transitionBooking, } from "./state-machine.js";
|
|
6
8
|
import { bookingTransactionDetailsRef, offerItemParticipantsRef, offerItemsRef, offerParticipantsRef, offerStaffAssignmentsRef, offersRef, orderItemParticipantsRef, orderItemsRef, orderParticipantsRef, orderStaffAssignmentsRef, ordersRef, } from "./transactions-ref.js";
|
|
7
9
|
const travelerParticipantTypes = ["traveler", "occupant"];
|
|
8
10
|
class BookingServiceError extends Error {
|
|
@@ -35,7 +37,6 @@ function toTravelerResponse(participant) {
|
|
|
35
37
|
email: participant.email,
|
|
36
38
|
phone: participant.phone,
|
|
37
39
|
preferredLanguage: participant.preferredLanguage,
|
|
38
|
-
accessibilityNeeds: participant.accessibilityNeeds,
|
|
39
40
|
specialRequests: participant.specialRequests,
|
|
40
41
|
isPrimary: participant.isPrimary,
|
|
41
42
|
notes: participant.notes,
|
|
@@ -250,18 +251,6 @@ async function getConvertProductData(db, data) {
|
|
|
250
251
|
})),
|
|
251
252
|
};
|
|
252
253
|
}
|
|
253
|
-
const VALID_BOOKING_TRANSITIONS = {
|
|
254
|
-
draft: ["on_hold", "confirmed", "cancelled"],
|
|
255
|
-
on_hold: ["confirmed", "expired", "cancelled"],
|
|
256
|
-
confirmed: ["in_progress", "cancelled"],
|
|
257
|
-
in_progress: ["completed", "cancelled"],
|
|
258
|
-
completed: [],
|
|
259
|
-
expired: [],
|
|
260
|
-
cancelled: [],
|
|
261
|
-
};
|
|
262
|
-
function isValidBookingTransition(from, to) {
|
|
263
|
-
return VALID_BOOKING_TRANSITIONS[from].includes(to);
|
|
264
|
-
}
|
|
265
254
|
function computeHoldExpiresAt(input) {
|
|
266
255
|
if (input.holdExpiresAt) {
|
|
267
256
|
return new Date(input.holdExpiresAt);
|
|
@@ -270,14 +259,94 @@ function computeHoldExpiresAt(input) {
|
|
|
270
259
|
const minutes = input.holdMinutes ?? 30;
|
|
271
260
|
return new Date(now + minutes * 60 * 1000);
|
|
272
261
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
262
|
+
/**
|
|
263
|
+
* Walk a booking's items, convert each line into the booking's
|
|
264
|
+
* `baseCurrency` via the booking's `fxRateSetId`, sum.
|
|
265
|
+
*
|
|
266
|
+
* Returns:
|
|
267
|
+
* - `{ status: "ok", baseSellAmountCents, baseCostAmountCents }` when
|
|
268
|
+
* every item's currency was either already in `baseCurrency` or had
|
|
269
|
+
* a rate row in the rate set
|
|
270
|
+
* - `{ status: "missing_rate", currency }` when an item's
|
|
271
|
+
* `sellCurrency` had no rate in the rate set; caller treats as
|
|
272
|
+
* "leave base totals untouched, surface to ops"
|
|
273
|
+
* - `{ status: "skipped" }` when the booking has no `fxRateSetId`
|
|
274
|
+
* (multi-currency conversion isn't possible without one)
|
|
275
|
+
*
|
|
276
|
+
* Pure conversion math. Caller controls persistence.
|
|
277
|
+
*/
|
|
278
|
+
async function rollupBaseTotals(db, bookingId, baseCurrency) {
|
|
279
|
+
const [booking] = await db
|
|
280
|
+
.select({ fxRateSetId: bookings.fxRateSetId })
|
|
281
|
+
.from(bookings)
|
|
282
|
+
.where(eq(bookings.id, bookingId))
|
|
283
|
+
.limit(1);
|
|
284
|
+
if (!booking?.fxRateSetId) {
|
|
285
|
+
return { status: "skipped" };
|
|
286
|
+
}
|
|
287
|
+
// Cache for the closure — TypeScript can't narrow `booking` after
|
|
288
|
+
// the closure boundary, so capture the id in a local.
|
|
289
|
+
const fxRateSetId = booking.fxRateSetId;
|
|
290
|
+
const items = await db
|
|
291
|
+
.select({
|
|
292
|
+
sellCurrency: bookingItems.sellCurrency,
|
|
293
|
+
totalSellAmountCents: bookingItems.totalSellAmountCents,
|
|
294
|
+
costCurrency: bookingItems.costCurrency,
|
|
295
|
+
totalCostAmountCents: bookingItems.totalCostAmountCents,
|
|
296
|
+
})
|
|
297
|
+
.from(bookingItems)
|
|
298
|
+
.where(eq(bookingItems.bookingId, bookingId));
|
|
299
|
+
// Cache rates we look up to avoid N+1 within one booking.
|
|
300
|
+
const rateCache = new Map(); // key: `${from}->${to}`, value: decimal rate or null
|
|
301
|
+
async function rate(from, to) {
|
|
302
|
+
if (from === to)
|
|
303
|
+
return 1;
|
|
304
|
+
const key = `${from}->${to}`;
|
|
305
|
+
if (rateCache.has(key))
|
|
306
|
+
return rateCache.get(key) ?? null;
|
|
307
|
+
const [direct] = await db
|
|
308
|
+
.select({ rate: exchangeRatesRef.rateDecimal })
|
|
309
|
+
.from(exchangeRatesRef)
|
|
310
|
+
.where(and(eq(exchangeRatesRef.fxRateSetId, fxRateSetId), eq(exchangeRatesRef.baseCurrency, from), eq(exchangeRatesRef.quoteCurrency, to)))
|
|
311
|
+
.limit(1);
|
|
312
|
+
if (direct) {
|
|
313
|
+
const value = Number.parseFloat(direct.rate);
|
|
314
|
+
rateCache.set(key, value);
|
|
315
|
+
return value;
|
|
316
|
+
}
|
|
317
|
+
// Try the inverse
|
|
318
|
+
const [inverse] = await db
|
|
319
|
+
.select({ rate: exchangeRatesRef.inverseRateDecimal })
|
|
320
|
+
.from(exchangeRatesRef)
|
|
321
|
+
.where(and(eq(exchangeRatesRef.fxRateSetId, fxRateSetId), eq(exchangeRatesRef.baseCurrency, to), eq(exchangeRatesRef.quoteCurrency, from)))
|
|
322
|
+
.limit(1);
|
|
323
|
+
if (inverse?.rate) {
|
|
324
|
+
const value = Number.parseFloat(inverse.rate);
|
|
325
|
+
rateCache.set(key, value);
|
|
326
|
+
return value;
|
|
327
|
+
}
|
|
328
|
+
rateCache.set(key, null);
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
let baseSellAmountCents = 0;
|
|
332
|
+
let baseCostAmountCents = 0;
|
|
333
|
+
for (const item of items) {
|
|
334
|
+
if (item.totalSellAmountCents !== null) {
|
|
335
|
+
const r = await rate(item.sellCurrency, baseCurrency);
|
|
336
|
+
if (r === null) {
|
|
337
|
+
return { status: "missing_rate", currency: item.sellCurrency };
|
|
338
|
+
}
|
|
339
|
+
baseSellAmountCents += Math.round(item.totalSellAmountCents * r);
|
|
340
|
+
}
|
|
341
|
+
if (item.totalCostAmountCents !== null && item.costCurrency) {
|
|
342
|
+
const r = await rate(item.costCurrency, baseCurrency);
|
|
343
|
+
if (r === null) {
|
|
344
|
+
return { status: "missing_rate", currency: item.costCurrency };
|
|
345
|
+
}
|
|
346
|
+
baseCostAmountCents += Math.round(item.totalCostAmountCents * r);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return { status: "ok", baseSellAmountCents, baseCostAmountCents };
|
|
281
350
|
}
|
|
282
351
|
async function lockAvailabilitySlot(db, slotId) {
|
|
283
352
|
const rows = await db.execute(sql `SELECT id, product_id, option_id, date_local, starts_at, ends_at, timezone, status, unlimited, remaining_pax
|
|
@@ -1499,17 +1568,24 @@ export const bookingsService = {
|
|
|
1499
1568
|
if (data.status === "cancelled") {
|
|
1500
1569
|
return bookingsService.cancelBooking(db, id, { note: data.note }, userId, runtime);
|
|
1501
1570
|
}
|
|
1571
|
+
// `on_hold` is only reachable through `reserveBooking`, never via direct status PATCH.
|
|
1502
1572
|
if (data.status === "on_hold") {
|
|
1503
1573
|
return { status: "invalid_transition" };
|
|
1504
1574
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1575
|
+
let patch;
|
|
1576
|
+
try {
|
|
1577
|
+
patch = transitionBooking(current.status, data.status);
|
|
1578
|
+
}
|
|
1579
|
+
catch (error) {
|
|
1580
|
+
if (error instanceof BookingTransitionError) {
|
|
1581
|
+
return { status: "invalid_transition" };
|
|
1582
|
+
}
|
|
1583
|
+
throw error;
|
|
1507
1584
|
}
|
|
1508
1585
|
const [row] = await db
|
|
1509
1586
|
.update(bookings)
|
|
1510
1587
|
.set({
|
|
1511
|
-
|
|
1512
|
-
...toBookingStatusTimestamps(data.status),
|
|
1588
|
+
...patch,
|
|
1513
1589
|
updatedAt: new Date(),
|
|
1514
1590
|
})
|
|
1515
1591
|
.where(eq(bookings.id, id))
|
|
@@ -1544,12 +1620,16 @@ export const bookingsService = {
|
|
|
1544
1620
|
if (!booking) {
|
|
1545
1621
|
throw new BookingServiceError("not_found");
|
|
1546
1622
|
}
|
|
1623
|
+
if (!canTransitionBooking(booking.status, "confirmed")) {
|
|
1624
|
+
throw new BookingServiceError("invalid_transition");
|
|
1625
|
+
}
|
|
1547
1626
|
if (booking.status !== "on_hold") {
|
|
1548
1627
|
throw new BookingServiceError("invalid_transition");
|
|
1549
1628
|
}
|
|
1550
1629
|
if (booking.hold_expires_at && booking.hold_expires_at < new Date()) {
|
|
1551
1630
|
throw new BookingServiceError("hold_expired");
|
|
1552
1631
|
}
|
|
1632
|
+
const patch = transitionBooking(booking.status, "confirmed");
|
|
1553
1633
|
await tx
|
|
1554
1634
|
.update(bookingAllocations)
|
|
1555
1635
|
.set({
|
|
@@ -1565,9 +1645,8 @@ export const bookingsService = {
|
|
|
1565
1645
|
const [row] = await tx
|
|
1566
1646
|
.update(bookings)
|
|
1567
1647
|
.set({
|
|
1568
|
-
|
|
1648
|
+
...patch,
|
|
1569
1649
|
holdExpiresAt: null,
|
|
1570
|
-
confirmedAt: new Date(),
|
|
1571
1650
|
updatedAt: new Date(),
|
|
1572
1651
|
})
|
|
1573
1652
|
.where(eq(bookings.id, id))
|
|
@@ -1669,9 +1748,13 @@ export const bookingsService = {
|
|
|
1669
1748
|
if (!booking) {
|
|
1670
1749
|
throw new BookingServiceError("not_found");
|
|
1671
1750
|
}
|
|
1751
|
+
if (!canTransitionBooking(booking.status, "expired")) {
|
|
1752
|
+
throw new BookingServiceError("invalid_transition");
|
|
1753
|
+
}
|
|
1672
1754
|
if (booking.status !== "on_hold") {
|
|
1673
1755
|
throw new BookingServiceError("invalid_transition");
|
|
1674
1756
|
}
|
|
1757
|
+
const patch = transitionBooking(booking.status, "expired");
|
|
1675
1758
|
const allocations = await tx
|
|
1676
1759
|
.select()
|
|
1677
1760
|
.from(bookingAllocations)
|
|
@@ -1694,9 +1777,8 @@ export const bookingsService = {
|
|
|
1694
1777
|
const [row] = await tx
|
|
1695
1778
|
.update(bookings)
|
|
1696
1779
|
.set({
|
|
1697
|
-
|
|
1780
|
+
...patch,
|
|
1698
1781
|
holdExpiresAt: null,
|
|
1699
|
-
expiredAt: new Date(),
|
|
1700
1782
|
updatedAt: new Date(),
|
|
1701
1783
|
})
|
|
1702
1784
|
.where(eq(bookings.id, id))
|
|
@@ -1765,9 +1847,10 @@ export const bookingsService = {
|
|
|
1765
1847
|
if (!booking) {
|
|
1766
1848
|
throw new BookingServiceError("not_found");
|
|
1767
1849
|
}
|
|
1768
|
-
if (!
|
|
1850
|
+
if (!canTransitionBooking(booking.status, "cancelled")) {
|
|
1769
1851
|
throw new BookingServiceError("invalid_transition");
|
|
1770
1852
|
}
|
|
1853
|
+
const patch = transitionBooking(booking.status, "cancelled");
|
|
1771
1854
|
const previousStatus = booking.status;
|
|
1772
1855
|
const allocations = await tx
|
|
1773
1856
|
.select()
|
|
@@ -1794,9 +1877,8 @@ export const bookingsService = {
|
|
|
1794
1877
|
const [row] = await tx
|
|
1795
1878
|
.update(bookings)
|
|
1796
1879
|
.set({
|
|
1797
|
-
|
|
1880
|
+
...patch,
|
|
1798
1881
|
holdExpiresAt: null,
|
|
1799
|
-
cancelledAt: new Date(),
|
|
1800
1882
|
updatedAt: new Date(),
|
|
1801
1883
|
})
|
|
1802
1884
|
.where(eq(bookings.id, id))
|
|
@@ -1873,7 +1955,6 @@ export const bookingsService = {
|
|
|
1873
1955
|
email: data.email ?? null,
|
|
1874
1956
|
phone: data.phone ?? null,
|
|
1875
1957
|
preferredLanguage: data.preferredLanguage ?? null,
|
|
1876
|
-
accessibilityNeeds: data.accessibilityNeeds ?? null,
|
|
1877
1958
|
specialRequests: data.specialRequests ?? null,
|
|
1878
1959
|
isPrimary: data.isPrimary ?? false,
|
|
1879
1960
|
notes: data.notes ?? null,
|
|
@@ -1928,7 +2009,6 @@ export const bookingsService = {
|
|
|
1928
2009
|
email: data.email ?? null,
|
|
1929
2010
|
phone: data.phone ?? null,
|
|
1930
2011
|
preferredLanguage: data.preferredLanguage ?? null,
|
|
1931
|
-
accessibilityNeeds: data.accessibilityNeeds ?? null,
|
|
1932
2012
|
specialRequests: data.specialRequests ?? null,
|
|
1933
2013
|
isPrimary: data.isPrimary ?? false,
|
|
1934
2014
|
notes: data.notes ?? null,
|
|
@@ -1942,7 +2022,6 @@ export const bookingsService = {
|
|
|
1942
2022
|
email: data.email ?? null,
|
|
1943
2023
|
phone: data.phone ?? null,
|
|
1944
2024
|
preferredLanguage: data.preferredLanguage ?? null,
|
|
1945
|
-
accessibilityNeeds: data.accessibilityNeeds ?? null,
|
|
1946
2025
|
specialRequests: data.specialRequests ?? null,
|
|
1947
2026
|
travelerCategory: data.travelerCategory ?? null,
|
|
1948
2027
|
isPrimary: data.isPrimary ?? undefined,
|
|
@@ -1960,74 +2039,194 @@ export const bookingsService = {
|
|
|
1960
2039
|
.where(eq(bookingItems.bookingId, bookingId))
|
|
1961
2040
|
.orderBy(asc(bookingItems.createdAt));
|
|
1962
2041
|
},
|
|
1963
|
-
|
|
2042
|
+
/**
|
|
2043
|
+
* Re-derive `bookings.sellAmountCents` / `costAmountCents` from
|
|
2044
|
+
* `Σ(booking_items.total*AmountCents)`, plus — when the booking
|
|
2045
|
+
* declares a `baseCurrency` and `fxRateSetId` — re-derive
|
|
2046
|
+
* `baseSellAmountCents` / `baseCostAmountCents` by converting each
|
|
2047
|
+
* item's total via the FX rate set.
|
|
2048
|
+
*
|
|
2049
|
+
* Called automatically inside the item-mutation methods so callers
|
|
2050
|
+
* that go through `createItem` / `updateItem` / `deleteItem` never
|
|
2051
|
+
* have to remember to roll the parent. Public so external flows
|
|
2052
|
+
* (saga compensations, ad-hoc fix-ups) can also invoke it.
|
|
2053
|
+
*
|
|
2054
|
+
* Pass a tx-bound `db` to compose with an existing transaction; this
|
|
2055
|
+
* method does NOT wrap its own transaction.
|
|
2056
|
+
*
|
|
2057
|
+
* **FX rollup behaviour**:
|
|
2058
|
+
*
|
|
2059
|
+
* - Single-currency booking (every item's `sellCurrency === baseCurrency`,
|
|
2060
|
+
* or `baseCurrency === sellCurrency` on the parent): `base*Cents`
|
|
2061
|
+
* equal `sell*Cents` / `cost*Cents` directly. No FX lookup needed.
|
|
2062
|
+
* - Multi-currency booking with `fxRateSetId`: every item is
|
|
2063
|
+
* converted to `baseCurrency` via `exchange_rates`. If any item's
|
|
2064
|
+
* currency is missing from the rate set, the FX rollup short-circuits
|
|
2065
|
+
* with `fxStatus: "missing_rate"` and `base*Cents` are LEFT
|
|
2066
|
+
* UNCHANGED on the parent (caller chooses whether to abort).
|
|
2067
|
+
* - No `baseCurrency` configured: FX rollup is skipped entirely
|
|
2068
|
+
* (`fxStatus: "skipped"`), and `base*Cents` stay null.
|
|
2069
|
+
*
|
|
2070
|
+
* Returns `{ sellAmountCents, costAmountCents, baseSellAmountCents,
|
|
2071
|
+
* baseCostAmountCents, fxStatus, missingCurrency? }` or `null` for a
|
|
2072
|
+
* missing booking.
|
|
2073
|
+
*/
|
|
2074
|
+
async recomputeBookingTotal(db, bookingId) {
|
|
1964
2075
|
const [booking] = await db
|
|
1965
|
-
.select({
|
|
2076
|
+
.select({
|
|
2077
|
+
id: bookings.id,
|
|
2078
|
+
sellCurrency: bookings.sellCurrency,
|
|
2079
|
+
baseCurrency: bookings.baseCurrency,
|
|
2080
|
+
})
|
|
1966
2081
|
.from(bookings)
|
|
1967
2082
|
.where(eq(bookings.id, bookingId))
|
|
1968
2083
|
.limit(1);
|
|
1969
2084
|
if (!booking) {
|
|
1970
2085
|
return null;
|
|
1971
2086
|
}
|
|
1972
|
-
const [
|
|
1973
|
-
.
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
title: data.title,
|
|
1977
|
-
description: data.description ?? null,
|
|
1978
|
-
itemType: data.itemType,
|
|
1979
|
-
status: data.status,
|
|
1980
|
-
serviceDate: data.serviceDate ?? null,
|
|
1981
|
-
startsAt: toTimestamp(data.startsAt),
|
|
1982
|
-
endsAt: toTimestamp(data.endsAt),
|
|
1983
|
-
quantity: data.quantity,
|
|
1984
|
-
sellCurrency: data.sellCurrency ?? booking.sellCurrency,
|
|
1985
|
-
unitSellAmountCents: data.unitSellAmountCents ?? null,
|
|
1986
|
-
totalSellAmountCents: data.totalSellAmountCents ?? null,
|
|
1987
|
-
costCurrency: data.costCurrency ?? null,
|
|
1988
|
-
unitCostAmountCents: data.unitCostAmountCents ?? null,
|
|
1989
|
-
totalCostAmountCents: data.totalCostAmountCents ?? null,
|
|
1990
|
-
notes: data.notes ?? null,
|
|
1991
|
-
productId: data.productId ?? null,
|
|
1992
|
-
optionId: data.optionId ?? null,
|
|
1993
|
-
optionUnitId: data.optionUnitId ?? null,
|
|
1994
|
-
pricingCategoryId: data.pricingCategoryId ?? null,
|
|
1995
|
-
sourceSnapshotId: data.sourceSnapshotId ?? null,
|
|
1996
|
-
sourceOfferId: data.sourceOfferId ?? null,
|
|
1997
|
-
metadata: data.metadata ?? null,
|
|
2087
|
+
const [totals] = await db
|
|
2088
|
+
.select({
|
|
2089
|
+
sellAmountCents: sql `coalesce(sum(${bookingItems.totalSellAmountCents}), 0)::int`,
|
|
2090
|
+
costAmountCents: sql `coalesce(sum(${bookingItems.totalCostAmountCents}), 0)::int`,
|
|
1998
2091
|
})
|
|
1999
|
-
.
|
|
2000
|
-
|
|
2001
|
-
|
|
2092
|
+
.from(bookingItems)
|
|
2093
|
+
.where(eq(bookingItems.bookingId, bookingId));
|
|
2094
|
+
const sellAmountCents = totals?.sellAmountCents ?? 0;
|
|
2095
|
+
const costAmountCents = totals?.costAmountCents ?? 0;
|
|
2096
|
+
// We need fxRateSetId from the bookings row plus per-item currency
|
|
2097
|
+
// for the FX rollup. Refetch with those columns.
|
|
2098
|
+
const [bookingForFx] = await db
|
|
2099
|
+
.select({
|
|
2100
|
+
baseCurrency: bookings.baseCurrency,
|
|
2101
|
+
sellCurrency: bookings.sellCurrency,
|
|
2102
|
+
})
|
|
2103
|
+
.from(bookings)
|
|
2104
|
+
.where(eq(bookings.id, bookingId))
|
|
2105
|
+
.limit(1);
|
|
2106
|
+
let fxStatus = "skipped";
|
|
2107
|
+
let baseSellAmountCents = null;
|
|
2108
|
+
let baseCostAmountCents = null;
|
|
2109
|
+
let missingCurrency = null;
|
|
2110
|
+
const baseCurrency = bookingForFx?.baseCurrency ?? null;
|
|
2111
|
+
if (baseCurrency) {
|
|
2112
|
+
const fxResult = await rollupBaseTotals(db, bookingId, baseCurrency);
|
|
2113
|
+
if (fxResult.status === "ok") {
|
|
2114
|
+
fxStatus = "ok";
|
|
2115
|
+
baseSellAmountCents = fxResult.baseSellAmountCents;
|
|
2116
|
+
baseCostAmountCents = fxResult.baseCostAmountCents;
|
|
2117
|
+
}
|
|
2118
|
+
else if (fxResult.status === "missing_rate") {
|
|
2119
|
+
fxStatus = "missing_rate";
|
|
2120
|
+
missingCurrency = fxResult.currency;
|
|
2121
|
+
}
|
|
2002
2122
|
}
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2123
|
+
const patch = {
|
|
2124
|
+
sellAmountCents,
|
|
2125
|
+
costAmountCents,
|
|
2126
|
+
updatedAt: new Date(),
|
|
2127
|
+
};
|
|
2128
|
+
if (fxStatus === "ok") {
|
|
2129
|
+
patch.baseSellAmountCents = baseSellAmountCents;
|
|
2130
|
+
patch.baseCostAmountCents = baseCostAmountCents;
|
|
2131
|
+
}
|
|
2132
|
+
await db.update(bookings).set(patch).where(eq(bookings.id, bookingId));
|
|
2133
|
+
return {
|
|
2134
|
+
sellAmountCents,
|
|
2135
|
+
costAmountCents,
|
|
2136
|
+
baseSellAmountCents,
|
|
2137
|
+
baseCostAmountCents,
|
|
2138
|
+
fxStatus,
|
|
2139
|
+
...(missingCurrency ? { missingCurrency } : {}),
|
|
2140
|
+
};
|
|
2141
|
+
},
|
|
2142
|
+
async createItem(db, bookingId, data, userId) {
|
|
2143
|
+
return db.transaction(async (tx) => {
|
|
2144
|
+
const [booking] = await tx
|
|
2145
|
+
.select({ id: bookings.id, sellCurrency: bookings.sellCurrency })
|
|
2146
|
+
.from(bookings)
|
|
2147
|
+
.where(eq(bookings.id, bookingId))
|
|
2148
|
+
.limit(1);
|
|
2149
|
+
if (!booking) {
|
|
2150
|
+
return null;
|
|
2151
|
+
}
|
|
2152
|
+
const [row] = await tx
|
|
2153
|
+
.insert(bookingItems)
|
|
2154
|
+
.values({
|
|
2155
|
+
bookingId,
|
|
2156
|
+
title: data.title,
|
|
2157
|
+
description: data.description ?? null,
|
|
2158
|
+
itemType: data.itemType,
|
|
2159
|
+
status: data.status,
|
|
2160
|
+
serviceDate: data.serviceDate ?? null,
|
|
2161
|
+
startsAt: toTimestamp(data.startsAt),
|
|
2162
|
+
endsAt: toTimestamp(data.endsAt),
|
|
2163
|
+
quantity: data.quantity,
|
|
2164
|
+
sellCurrency: data.sellCurrency ?? booking.sellCurrency,
|
|
2165
|
+
unitSellAmountCents: data.unitSellAmountCents ?? null,
|
|
2166
|
+
totalSellAmountCents: data.totalSellAmountCents ?? null,
|
|
2167
|
+
costCurrency: data.costCurrency ?? null,
|
|
2168
|
+
unitCostAmountCents: data.unitCostAmountCents ?? null,
|
|
2169
|
+
totalCostAmountCents: data.totalCostAmountCents ?? null,
|
|
2170
|
+
notes: data.notes ?? null,
|
|
2171
|
+
productId: data.productId ?? null,
|
|
2172
|
+
optionId: data.optionId ?? null,
|
|
2173
|
+
optionUnitId: data.optionUnitId ?? null,
|
|
2174
|
+
pricingCategoryId: data.pricingCategoryId ?? null,
|
|
2175
|
+
sourceSnapshotId: data.sourceSnapshotId ?? null,
|
|
2176
|
+
sourceOfferId: data.sourceOfferId ?? null,
|
|
2177
|
+
metadata: data.metadata ?? null,
|
|
2178
|
+
})
|
|
2179
|
+
.returning();
|
|
2180
|
+
if (!row) {
|
|
2181
|
+
return null;
|
|
2182
|
+
}
|
|
2183
|
+
await tx.insert(bookingActivityLog).values({
|
|
2184
|
+
bookingId,
|
|
2185
|
+
actorId: userId ?? "system",
|
|
2186
|
+
activityType: "item_update",
|
|
2187
|
+
description: `Booking item "${data.title}" added`,
|
|
2188
|
+
metadata: { bookingItemId: row.id, itemType: data.itemType },
|
|
2189
|
+
});
|
|
2190
|
+
await bookingsService.recomputeBookingTotal(tx, bookingId);
|
|
2191
|
+
return row;
|
|
2009
2192
|
});
|
|
2010
|
-
return row;
|
|
2011
2193
|
},
|
|
2012
2194
|
async updateItem(db, itemId, data) {
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2195
|
+
return db.transaction(async (tx) => {
|
|
2196
|
+
const [row] = await tx
|
|
2197
|
+
.update(bookingItems)
|
|
2198
|
+
.set({
|
|
2199
|
+
...data,
|
|
2200
|
+
startsAt: data.startsAt === undefined ? undefined : toTimestamp(data.startsAt),
|
|
2201
|
+
endsAt: data.endsAt === undefined ? undefined : toTimestamp(data.endsAt),
|
|
2202
|
+
updatedAt: new Date(),
|
|
2203
|
+
})
|
|
2204
|
+
.where(eq(bookingItems.id, itemId))
|
|
2205
|
+
.returning();
|
|
2206
|
+
if (!row)
|
|
2207
|
+
return null;
|
|
2208
|
+
await bookingsService.recomputeBookingTotal(tx, row.bookingId);
|
|
2209
|
+
return row;
|
|
2210
|
+
});
|
|
2024
2211
|
},
|
|
2025
2212
|
async deleteItem(db, itemId) {
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
.
|
|
2029
|
-
|
|
2030
|
-
|
|
2213
|
+
return db.transaction(async (tx) => {
|
|
2214
|
+
// Look up the parent booking BEFORE the delete so we can roll up
|
|
2215
|
+
// afterwards.
|
|
2216
|
+
const [item] = await tx
|
|
2217
|
+
.select({ bookingId: bookingItems.bookingId })
|
|
2218
|
+
.from(bookingItems)
|
|
2219
|
+
.where(eq(bookingItems.id, itemId))
|
|
2220
|
+
.limit(1);
|
|
2221
|
+
const [row] = await tx
|
|
2222
|
+
.delete(bookingItems)
|
|
2223
|
+
.where(eq(bookingItems.id, itemId))
|
|
2224
|
+
.returning({ id: bookingItems.id });
|
|
2225
|
+
if (item) {
|
|
2226
|
+
await bookingsService.recomputeBookingTotal(tx, item.bookingId);
|
|
2227
|
+
}
|
|
2228
|
+
return row ?? null;
|
|
2229
|
+
});
|
|
2031
2230
|
},
|
|
2032
2231
|
listItemParticipants(db, itemId) {
|
|
2033
2232
|
return db
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { bookings } from "./schema-core.js";
|
|
2
|
+
export type BookingStatus = (typeof bookings.$inferSelect)["status"];
|
|
3
|
+
export declare const BOOKING_TRANSITIONS: {
|
|
4
|
+
readonly draft: readonly ["on_hold", "confirmed", "cancelled"];
|
|
5
|
+
readonly on_hold: readonly ["confirmed", "expired", "cancelled"];
|
|
6
|
+
readonly confirmed: readonly ["in_progress", "cancelled"];
|
|
7
|
+
readonly in_progress: readonly ["completed", "cancelled"];
|
|
8
|
+
readonly completed: readonly [];
|
|
9
|
+
readonly expired: readonly [];
|
|
10
|
+
readonly cancelled: readonly [];
|
|
11
|
+
};
|
|
12
|
+
export declare class BookingTransitionError extends Error {
|
|
13
|
+
readonly from: BookingStatus;
|
|
14
|
+
readonly to: BookingStatus;
|
|
15
|
+
readonly code = "INVALID_BOOKING_TRANSITION";
|
|
16
|
+
constructor(from: BookingStatus, to: BookingStatus);
|
|
17
|
+
}
|
|
18
|
+
export declare function canTransitionBooking(from: BookingStatus, to: BookingStatus): boolean;
|
|
19
|
+
export interface BookingStatusPatch {
|
|
20
|
+
status: BookingStatus;
|
|
21
|
+
confirmedAt?: Date;
|
|
22
|
+
expiredAt?: Date;
|
|
23
|
+
cancelledAt?: Date;
|
|
24
|
+
completedAt?: Date;
|
|
25
|
+
}
|
|
26
|
+
export declare function transitionBooking(from: BookingStatus, to: BookingStatus, opts?: {
|
|
27
|
+
now?: Date;
|
|
28
|
+
}): BookingStatusPatch;
|
|
29
|
+
//# sourceMappingURL=state-machine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-machine.d.ts","sourceRoot":"","sources":["../src/state-machine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAEhD,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,QAAQ,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,CAAA;AAEpE,eAAO,MAAM,mBAAmB;;;;;;;;CAQoC,CAAA;AAEpE,qBAAa,sBAAuB,SAAQ,KAAK;IAI7C,QAAQ,CAAC,IAAI,EAAE,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,aAAa;IAJ5B,QAAQ,CAAC,IAAI,gCAA+B;gBAGjC,IAAI,EAAE,aAAa,EACnB,EAAE,EAAE,aAAa;CAK7B;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,GAAG,OAAO,CAEpF;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,aAAa,CAAA;IACrB,WAAW,CAAC,EAAE,IAAI,CAAA;IAClB,SAAS,CAAC,EAAE,IAAI,CAAA;IAChB,WAAW,CAAC,EAAE,IAAI,CAAA;IAClB,WAAW,CAAC,EAAE,IAAI,CAAA;CACnB;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,aAAa,EACnB,EAAE,EAAE,aAAa,EACjB,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,IAAI,CAAA;CAAO,GACxB,kBAAkB,CAcpB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const BOOKING_TRANSITIONS = {
|
|
2
|
+
draft: ["on_hold", "confirmed", "cancelled"],
|
|
3
|
+
on_hold: ["confirmed", "expired", "cancelled"],
|
|
4
|
+
confirmed: ["in_progress", "cancelled"],
|
|
5
|
+
in_progress: ["completed", "cancelled"],
|
|
6
|
+
completed: [],
|
|
7
|
+
expired: [],
|
|
8
|
+
cancelled: [],
|
|
9
|
+
};
|
|
10
|
+
export class BookingTransitionError extends Error {
|
|
11
|
+
from;
|
|
12
|
+
to;
|
|
13
|
+
code = "INVALID_BOOKING_TRANSITION";
|
|
14
|
+
constructor(from, to) {
|
|
15
|
+
super(`Illegal booking status transition: ${from} → ${to}`);
|
|
16
|
+
this.from = from;
|
|
17
|
+
this.to = to;
|
|
18
|
+
this.name = "BookingTransitionError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function canTransitionBooking(from, to) {
|
|
22
|
+
return BOOKING_TRANSITIONS[from].includes(to);
|
|
23
|
+
}
|
|
24
|
+
export function transitionBooking(from, to, opts = {}) {
|
|
25
|
+
if (!canTransitionBooking(from, to)) {
|
|
26
|
+
throw new BookingTransitionError(from, to);
|
|
27
|
+
}
|
|
28
|
+
const now = opts.now ?? new Date();
|
|
29
|
+
const patch = { status: to };
|
|
30
|
+
if (to === "confirmed")
|
|
31
|
+
patch.confirmedAt = now;
|
|
32
|
+
if (to === "expired")
|
|
33
|
+
patch.expiredAt = now;
|
|
34
|
+
if (to === "cancelled")
|
|
35
|
+
patch.cancelledAt = now;
|
|
36
|
+
if (to === "completed")
|
|
37
|
+
patch.completedAt = now;
|
|
38
|
+
return patch;
|
|
39
|
+
}
|
|
@@ -17,7 +17,6 @@ export declare const publicBookingSessionTravelerInputSchema: z.ZodObject<{
|
|
|
17
17
|
email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
18
18
|
phone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
19
19
|
preferredLanguage: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
20
|
-
accessibilityNeeds: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
21
20
|
specialRequests: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
22
21
|
isPrimary: z.ZodDefault<z.ZodBoolean>;
|
|
23
22
|
notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -127,7 +126,6 @@ export declare const publicCreateBookingSessionSchema: z.ZodObject<{
|
|
|
127
126
|
email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
128
127
|
phone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
129
128
|
preferredLanguage: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
130
|
-
accessibilityNeeds: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
131
129
|
specialRequests: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
132
130
|
isPrimary: z.ZodDefault<z.ZodBoolean>;
|
|
133
131
|
notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -157,7 +155,6 @@ export declare const publicUpdateBookingSessionSchema: z.ZodObject<{
|
|
|
157
155
|
email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
158
156
|
phone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
159
157
|
preferredLanguage: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
160
|
-
accessibilityNeeds: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
161
158
|
specialRequests: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
162
159
|
isPrimary: z.ZodDefault<z.ZodBoolean>;
|
|
163
160
|
notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -230,7 +227,6 @@ export declare const publicBookingSessionTravelerSchema: z.ZodObject<{
|
|
|
230
227
|
email: z.ZodNullable<z.ZodString>;
|
|
231
228
|
phone: z.ZodNullable<z.ZodString>;
|
|
232
229
|
preferredLanguage: z.ZodNullable<z.ZodString>;
|
|
233
|
-
accessibilityNeeds: z.ZodNullable<z.ZodString>;
|
|
234
230
|
specialRequests: z.ZodNullable<z.ZodString>;
|
|
235
231
|
isPrimary: z.ZodBoolean;
|
|
236
232
|
notes: z.ZodNullable<z.ZodString>;
|
|
@@ -373,7 +369,6 @@ export declare const publicBookingSessionSchema: z.ZodObject<{
|
|
|
373
369
|
email: z.ZodNullable<z.ZodString>;
|
|
374
370
|
phone: z.ZodNullable<z.ZodString>;
|
|
375
371
|
preferredLanguage: z.ZodNullable<z.ZodString>;
|
|
376
|
-
accessibilityNeeds: z.ZodNullable<z.ZodString>;
|
|
377
372
|
specialRequests: z.ZodNullable<z.ZodString>;
|
|
378
373
|
isPrimary: z.ZodBoolean;
|
|
379
374
|
notes: z.ZodNullable<z.ZodString>;
|
|
@@ -578,7 +573,6 @@ export declare const publicBookingSessionRepriceResultSchema: z.ZodObject<{
|
|
|
578
573
|
email: z.ZodNullable<z.ZodString>;
|
|
579
574
|
phone: z.ZodNullable<z.ZodString>;
|
|
580
575
|
preferredLanguage: z.ZodNullable<z.ZodString>;
|
|
581
|
-
accessibilityNeeds: z.ZodNullable<z.ZodString>;
|
|
582
576
|
specialRequests: z.ZodNullable<z.ZodString>;
|
|
583
577
|
isPrimary: z.ZodBoolean;
|
|
584
578
|
notes: z.ZodNullable<z.ZodString>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation-public.d.ts","sourceRoot":"","sources":["../src/validation-public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAqBvB,eAAO,MAAM,uCAAuC
|
|
1
|
+
{"version":3,"file":"validation-public.d.ts","sourceRoot":"","sources":["../src/validation-public.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,7 +13,6 @@ export const publicBookingSessionTravelerInputSchema = z.object({
|
|
|
13
13
|
email: z.string().email().nullable().optional(),
|
|
14
14
|
phone: z.string().max(50).nullable().optional(),
|
|
15
15
|
preferredLanguage: z.string().max(35).nullable().optional(),
|
|
16
|
-
accessibilityNeeds: z.string().nullable().optional(),
|
|
17
16
|
specialRequests: z.string().nullable().optional(),
|
|
18
17
|
isPrimary: z.boolean().default(false),
|
|
19
18
|
notes: z.string().nullable().optional(),
|
|
@@ -130,7 +129,6 @@ export const publicBookingSessionTravelerSchema = z.object({
|
|
|
130
129
|
email: z.string().nullable(),
|
|
131
130
|
phone: z.string().nullable(),
|
|
132
131
|
preferredLanguage: z.string().nullable(),
|
|
133
|
-
accessibilityNeeds: z.string().nullable(),
|
|
134
132
|
specialRequests: z.string().nullable(),
|
|
135
133
|
isPrimary: z.boolean(),
|
|
136
134
|
notes: z.string().nullable(),
|