@voyantjs/bookings 0.96.0 → 0.98.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/schema/travel-details.d.ts +2 -7
- package/dist/schema/travel-details.d.ts.map +1 -1
- package/dist/schema/travel-details.js +4 -7
- package/dist/validation-public.d.ts +1 -1063
- package/dist/validation-public.d.ts.map +1 -1
- package/dist/validation-public.js +1 -319
- package/dist/validation-shared.d.ts +1 -117
- package/dist/validation-shared.d.ts.map +1 -1
- package/dist/validation-shared.js +1 -95
- package/dist/validation.d.ts +1 -1287
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +1 -632
- package/package.json +8 -7
package/dist/validation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAA"}
|
package/dist/validation.js
CHANGED
|
@@ -1,632 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { bookingTravelerBedPreferenceSchema, travelerAllocationMapSchema, } from "./schema/travel-details.js";
|
|
4
|
-
import { bookingAllocationStatusSchema, bookingAllocationTypeSchema, bookingDocumentTypeSchema, bookingFulfillmentDeliveryChannelSchema, bookingFulfillmentStatusSchema, bookingFulfillmentTypeSchema, bookingItemParticipantRoleSchema, bookingItemStatusSchema, bookingItemTypeSchema, bookingParticipantTypeSchema, bookingRedemptionMethodSchema, bookingSourceTypeSchema, bookingStatusSchema, bookingTravelerCategorySchema, supplierConfirmationStatusSchema, } from "./validation-shared.js";
|
|
5
|
-
// ---------- bookings ----------
|
|
6
|
-
const bookingDepositRuleSchema = z.object({
|
|
7
|
-
kind: z.enum(["none", "percent", "fixed_cents"]),
|
|
8
|
-
percent: z.number().min(0).max(100).optional(),
|
|
9
|
-
amountCents: z.number().int().min(0).optional(),
|
|
10
|
-
});
|
|
11
|
-
const bookingCustomerPaymentPolicySchema = z.object({
|
|
12
|
-
deposit: bookingDepositRuleSchema,
|
|
13
|
-
minDaysBeforeDepartureForDeposit: z.number().int().min(0),
|
|
14
|
-
balanceDueDaysBeforeDeparture: z.number().int().min(0),
|
|
15
|
-
balanceDueMinDaysFromNow: z.number().int().min(0),
|
|
16
|
-
});
|
|
17
|
-
export const bookingPriceOverrideSchema = z.object({
|
|
18
|
-
isManual: z.literal(true),
|
|
19
|
-
originalAmountCents: z.number().int().min(0).nullable(),
|
|
20
|
-
overriddenAmountCents: z.number().int().min(0),
|
|
21
|
-
currency: z.string().min(3).max(3),
|
|
22
|
-
reason: z.string().trim().min(1).max(1000),
|
|
23
|
-
overriddenBy: z.string().min(1),
|
|
24
|
-
overriddenAt: z.string().datetime(),
|
|
25
|
-
});
|
|
26
|
-
const bookingBillingPersonIdSchema = typeIdSchemas.person.optional().nullable();
|
|
27
|
-
const bookingBillingOrganizationIdSchema = typeIdSchemas.organization.optional().nullable();
|
|
28
|
-
function validateExclusiveBillingParty(value, ctx) {
|
|
29
|
-
if (!value.personId || !value.organizationId)
|
|
30
|
-
return;
|
|
31
|
-
ctx.addIssue({
|
|
32
|
-
code: z.ZodIssueCode.custom,
|
|
33
|
-
path: ["organizationId"],
|
|
34
|
-
message: "Billing party must be either personId or organizationId, not both",
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
const bookingCoreSchema = z.object({
|
|
38
|
-
bookingNumber: z.string().min(1).max(50),
|
|
39
|
-
status: bookingStatusSchema.default("draft"),
|
|
40
|
-
personId: bookingBillingPersonIdSchema,
|
|
41
|
-
organizationId: bookingBillingOrganizationIdSchema,
|
|
42
|
-
sourceType: bookingSourceTypeSchema.default("manual"),
|
|
43
|
-
externalBookingRef: z.string().optional().nullable(),
|
|
44
|
-
communicationLanguage: z.string().max(35).optional().nullable(),
|
|
45
|
-
contactFirstName: z.string().max(255).optional().nullable(),
|
|
46
|
-
contactLastName: z.string().max(255).optional().nullable(),
|
|
47
|
-
contactPartyType: z.enum(["individual", "company"]).optional().nullable(),
|
|
48
|
-
contactTaxId: z.string().max(100).optional().nullable(),
|
|
49
|
-
contactEmail: z.string().email().optional().nullable(),
|
|
50
|
-
contactPhone: z.string().max(50).optional().nullable(),
|
|
51
|
-
contactPreferredLanguage: z.string().max(35).optional().nullable(),
|
|
52
|
-
contactCountry: z.string().max(100).optional().nullable(),
|
|
53
|
-
contactRegion: z.string().max(255).optional().nullable(),
|
|
54
|
-
contactCity: z.string().max(255).optional().nullable(),
|
|
55
|
-
contactAddressLine1: z.string().max(255).optional().nullable(),
|
|
56
|
-
contactAddressLine2: z.string().max(255).optional().nullable(),
|
|
57
|
-
contactPostalCode: z.string().max(50).optional().nullable(),
|
|
58
|
-
sellCurrency: z.string().min(3).max(3),
|
|
59
|
-
baseCurrency: z.string().min(3).max(3).optional().nullable(),
|
|
60
|
-
sellAmountCents: z.number().int().min(0).optional().nullable(),
|
|
61
|
-
baseSellAmountCents: z.number().int().min(0).optional().nullable(),
|
|
62
|
-
costAmountCents: z.number().int().min(0).optional().nullable(),
|
|
63
|
-
baseCostAmountCents: z.number().int().min(0).optional().nullable(),
|
|
64
|
-
marginPercent: z.number().int().optional().nullable(),
|
|
65
|
-
startDate: z.string().optional().nullable(),
|
|
66
|
-
endDate: z.string().optional().nullable(),
|
|
67
|
-
pax: z.number().int().positive().optional().nullable(),
|
|
68
|
-
internalNotes: z.string().optional().nullable(),
|
|
69
|
-
customerPaymentPolicy: bookingCustomerPaymentPolicySchema.optional().nullable(),
|
|
70
|
-
priceOverride: bookingPriceOverrideSchema.optional().nullable(),
|
|
71
|
-
holdExpiresAt: z.string().datetime().optional().nullable(),
|
|
72
|
-
confirmedAt: z.string().datetime().optional().nullable(),
|
|
73
|
-
expiredAt: z.string().datetime().optional().nullable(),
|
|
74
|
-
cancelledAt: z.string().datetime().optional().nullable(),
|
|
75
|
-
completedAt: z.string().datetime().optional().nullable(),
|
|
76
|
-
redeemedAt: z.string().datetime().optional().nullable(),
|
|
77
|
-
});
|
|
78
|
-
export const insertBookingSchema = bookingCoreSchema.superRefine(validateExclusiveBillingParty);
|
|
79
|
-
export const updateBookingSchema = bookingCoreSchema
|
|
80
|
-
.partial()
|
|
81
|
-
.superRefine(validateExclusiveBillingParty);
|
|
82
|
-
export const createBookingSchema = bookingCoreSchema
|
|
83
|
-
.extend({
|
|
84
|
-
sourceType: z.enum(["manual", "internal"]).default("manual"),
|
|
85
|
-
})
|
|
86
|
-
.refine((value) => value.status !== "on_hold", {
|
|
87
|
-
message: "Use the reservation flow to create on-hold bookings",
|
|
88
|
-
path: ["status"],
|
|
89
|
-
})
|
|
90
|
-
.refine((value) => value.holdExpiresAt == null, {
|
|
91
|
-
message: "Use the reservation flow to manage booking hold expiry",
|
|
92
|
-
path: ["holdExpiresAt"],
|
|
93
|
-
})
|
|
94
|
-
.superRefine(validateExclusiveBillingParty);
|
|
95
|
-
export const bookingListSortFieldSchema = z.enum([
|
|
96
|
-
"bookingNumber",
|
|
97
|
-
"status",
|
|
98
|
-
"sellAmount",
|
|
99
|
-
"pax",
|
|
100
|
-
"startDate",
|
|
101
|
-
"endDate",
|
|
102
|
-
"createdAt",
|
|
103
|
-
]);
|
|
104
|
-
export const bookingListSortDirSchema = z.enum(["asc", "desc"]);
|
|
105
|
-
export const bookingListQuerySchema = z.object({
|
|
106
|
-
status: bookingStatusSchema.optional(),
|
|
107
|
-
/**
|
|
108
|
-
* Statuses to omit from the result. Lets the operator list page hide
|
|
109
|
-
* noise (draft + expired by default) without forcing a separate
|
|
110
|
-
* endpoint. The wire format is a comma-separated string (e.g.
|
|
111
|
-
* `?excludeStatuses=draft,expired`) — query parsing collapses
|
|
112
|
-
* repeated keys, so a list has to ride on a single param. The
|
|
113
|
-
* preprocess hook splits + trims; the union then validates each
|
|
114
|
-
* entry against the enum.
|
|
115
|
-
*/
|
|
116
|
-
excludeStatuses: z.preprocess((value) => {
|
|
117
|
-
if (typeof value !== "string" || !value.includes(","))
|
|
118
|
-
return value;
|
|
119
|
-
return value
|
|
120
|
-
.split(",")
|
|
121
|
-
.map((part) => part.trim())
|
|
122
|
-
.filter(Boolean);
|
|
123
|
-
}, z.union([bookingStatusSchema, z.array(bookingStatusSchema)]).optional()),
|
|
124
|
-
search: z.string().optional(),
|
|
125
|
-
productId: z.string().optional(),
|
|
126
|
-
optionId: z.string().optional(),
|
|
127
|
-
/**
|
|
128
|
-
* Filter to bookings whose items reference this availability slot
|
|
129
|
-
* (post-0026, items carry `availability_slot_id` directly). Scoped
|
|
130
|
-
* to a specific departure so the operator can answer "who's on this
|
|
131
|
-
* 28-May 09:00 sailing?" from the list page.
|
|
132
|
-
*/
|
|
133
|
-
availabilitySlotId: z.string().optional(),
|
|
134
|
-
supplierId: z.string().optional(),
|
|
135
|
-
productCategoryId: z.string().optional(),
|
|
136
|
-
personId: z.string().optional(),
|
|
137
|
-
organizationId: z.string().optional(),
|
|
138
|
-
dateFrom: z.string().optional(),
|
|
139
|
-
dateTo: z.string().optional(),
|
|
140
|
-
paxMin: z.coerce.number().int().min(0).optional(),
|
|
141
|
-
paxMax: z.coerce.number().int().min(0).optional(),
|
|
142
|
-
sortBy: bookingListSortFieldSchema.default("createdAt"),
|
|
143
|
-
sortDir: bookingListSortDirSchema.default("desc"),
|
|
144
|
-
limit: z.coerce.number().int().min(1).max(100).default(50),
|
|
145
|
-
offset: z.coerce.number().int().min(0).default(0),
|
|
146
|
-
});
|
|
147
|
-
export const bookingAggregatesQuerySchema = z.object({
|
|
148
|
-
from: z.string().datetime().optional(),
|
|
149
|
-
to: z.string().datetime().optional(),
|
|
150
|
-
/**
|
|
151
|
-
* Cap on the number of upcoming-departure rows returned alongside
|
|
152
|
-
* the count. The dashboard uses 8; we allow up to 20 so adjacent
|
|
153
|
-
* dashboards / digests can share the endpoint.
|
|
154
|
-
*/
|
|
155
|
-
upcomingLimit: z.coerce.number().int().min(0).max(20).default(8),
|
|
156
|
-
});
|
|
157
|
-
export const sharingGroupsForSlotQuerySchema = z.object({
|
|
158
|
-
slotId: z.string().min(1),
|
|
159
|
-
});
|
|
160
|
-
export const convertProductSchema = z
|
|
161
|
-
.object({
|
|
162
|
-
productId: z.string().min(1),
|
|
163
|
-
optionId: z.string().optional().nullable(),
|
|
164
|
-
slotId: z.string().optional().nullable(),
|
|
165
|
-
bookingNumber: z.string().min(1).max(50),
|
|
166
|
-
personId: bookingBillingPersonIdSchema,
|
|
167
|
-
organizationId: bookingBillingOrganizationIdSchema,
|
|
168
|
-
pax: z.number().int().positive().optional().nullable(),
|
|
169
|
-
internalNotes: z.string().optional().nullable(),
|
|
170
|
-
/**
|
|
171
|
-
* Override the seed `sellAmountCents` on the new booking + line item.
|
|
172
|
-
* When unset, the converter uses `product.sellAmountCents` as before.
|
|
173
|
-
* Used by the catalog booking-engine when promotional offers are
|
|
174
|
-
* applied to the quote — the discounted base flows through here so
|
|
175
|
-
* the booking row's payable amount reflects the customer-shown total
|
|
176
|
-
* (per docs/architecture/promotions-architecture.md §7.1).
|
|
177
|
-
*/
|
|
178
|
-
sellAmountCentsOverride: z.number().int().min(0).optional().nullable(),
|
|
179
|
-
/**
|
|
180
|
-
* Catalog-resolved preview total shown to the operator. Unlike
|
|
181
|
-
* `sellAmountCentsOverride`, this is not a promotion adjustment; it lets
|
|
182
|
-
* the create flow seed the booking total from the pricing preview even
|
|
183
|
-
* when the legacy product row has no static price.
|
|
184
|
-
*/
|
|
185
|
-
catalogSellAmountCents: z.number().int().min(0).optional().nullable(),
|
|
186
|
-
/**
|
|
187
|
-
* Operator-confirmed booking total. If it differs from the catalog preview
|
|
188
|
-
* (or there was no catalog preview), `priceOverrideReason` is required and
|
|
189
|
-
* the service stamps an audit payload onto `bookings.price_override`.
|
|
190
|
-
*/
|
|
191
|
-
confirmedSellAmountCents: z.number().int().min(0).optional().nullable(),
|
|
192
|
-
priceOverrideReason: z.string().trim().min(1).max(1000).optional().nullable(),
|
|
193
|
-
/**
|
|
194
|
-
* Initial status to insert with — defaults to `draft`. Lets the booking-
|
|
195
|
-
* create flow commit straight to `confirmed` or `awaiting_payment` in
|
|
196
|
-
* one transaction instead of doing a `create-then-flip` dance that's
|
|
197
|
-
* vulnerable to a window where the second request can't see the just-
|
|
198
|
-
* committed booking row. When set to `confirmed`, the caller is
|
|
199
|
-
* responsible for emitting the matching `booking.confirmed` event.
|
|
200
|
-
*/
|
|
201
|
-
initialStatus: bookingStatusSchema.optional(),
|
|
202
|
-
/**
|
|
203
|
-
* Billing-contact snapshot. Captures who the operator was billing
|
|
204
|
-
* at create time so the booking detail page renders the right
|
|
205
|
-
* payer even if the linked CRM person/organization changes later
|
|
206
|
-
* (or is hard-deleted). All fields optional — the create flow can
|
|
207
|
-
* pass a partial snapshot when only some details are known.
|
|
208
|
-
*/
|
|
209
|
-
contactFirstName: z.string().max(255).optional().nullable(),
|
|
210
|
-
contactLastName: z.string().max(255).optional().nullable(),
|
|
211
|
-
contactPartyType: z.enum(["individual", "company"]).optional().nullable(),
|
|
212
|
-
contactTaxId: z.string().max(100).optional().nullable(),
|
|
213
|
-
contactEmail: z.string().max(255).optional().nullable(),
|
|
214
|
-
contactPhone: z.string().max(50).optional().nullable(),
|
|
215
|
-
contactPreferredLanguage: z.string().max(35).optional().nullable(),
|
|
216
|
-
contactCountry: z.string().max(2).optional().nullable(),
|
|
217
|
-
contactRegion: z.string().max(100).optional().nullable(),
|
|
218
|
-
contactCity: z.string().max(100).optional().nullable(),
|
|
219
|
-
contactAddressLine1: z.string().max(500).optional().nullable(),
|
|
220
|
-
contactAddressLine2: z.string().max(500).optional().nullable(),
|
|
221
|
-
contactPostalCode: z.string().max(20).optional().nullable(),
|
|
222
|
-
itemLines: z
|
|
223
|
-
.array(z.object({
|
|
224
|
-
/**
|
|
225
|
-
* Stable client-side key (e.g. `unit:optu_adult`). Stamped
|
|
226
|
-
* into the inserted booking_item's
|
|
227
|
-
* `metadata.bookingCreateLineKey` so the orchestrator can
|
|
228
|
-
* link items to travelers via `booking_item_travelers`.
|
|
229
|
-
* See voyantjs/voyant#1267.
|
|
230
|
-
*/
|
|
231
|
-
clientLineKey: z.string().min(1).max(255).optional().nullable(),
|
|
232
|
-
optionId: z.string().min(1).optional().nullable(),
|
|
233
|
-
optionUnitId: z.string().min(1),
|
|
234
|
-
quantity: z.number().int().min(1),
|
|
235
|
-
title: z.string().min(1).max(255).optional().nullable(),
|
|
236
|
-
description: z.string().max(5000).optional().nullable(),
|
|
237
|
-
unitSellAmountCents: z.number().int().min(0).optional().nullable(),
|
|
238
|
-
totalSellAmountCents: z.number().int().min(0).optional().nullable(),
|
|
239
|
-
travelerKeys: z.array(z.string().min(1).max(255)).optional().nullable(),
|
|
240
|
-
travelerIndexes: z.array(z.number().int().min(0)).optional().nullable(),
|
|
241
|
-
}))
|
|
242
|
-
.optional(),
|
|
243
|
-
})
|
|
244
|
-
.superRefine((value, ctx) => {
|
|
245
|
-
validateExclusiveBillingParty(value, ctx);
|
|
246
|
-
if (value.confirmedSellAmountCents == null)
|
|
247
|
-
return;
|
|
248
|
-
if (value.catalogSellAmountCents === value.confirmedSellAmountCents)
|
|
249
|
-
return;
|
|
250
|
-
if (value.priceOverrideReason)
|
|
251
|
-
return;
|
|
252
|
-
ctx.addIssue({
|
|
253
|
-
code: z.ZodIssueCode.custom,
|
|
254
|
-
path: ["priceOverrideReason"],
|
|
255
|
-
message: "A price override reason is required when the confirmed total differs from catalog pricing",
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
/**
|
|
259
|
-
* Admin pricing-preview request. Mirrors the storefront pricing session
|
|
260
|
-
* resolver input so the operator dialog sees the same numbers the customer
|
|
261
|
-
* would see for the same product + option + catalog.
|
|
262
|
-
*/
|
|
263
|
-
export const pricingPreviewSchema = z.object({
|
|
264
|
-
productId: z.string().min(1),
|
|
265
|
-
optionId: z.string().optional().nullable(),
|
|
266
|
-
catalogId: z.string().optional().nullable(),
|
|
267
|
-
});
|
|
268
|
-
export const reserveBookingItemSchema = z.object({
|
|
269
|
-
title: z.string().min(1).max(255),
|
|
270
|
-
description: z.string().optional().nullable(),
|
|
271
|
-
itemType: bookingItemTypeSchema.default("unit"),
|
|
272
|
-
quantity: z.number().int().positive().default(1),
|
|
273
|
-
sellCurrency: z.string().min(3).max(3).optional().nullable(),
|
|
274
|
-
unitSellAmountCents: z.number().int().min(0).optional().nullable(),
|
|
275
|
-
totalSellAmountCents: z.number().int().min(0).optional().nullable(),
|
|
276
|
-
costCurrency: z.string().min(3).max(3).optional().nullable(),
|
|
277
|
-
unitCostAmountCents: z.number().int().min(0).optional().nullable(),
|
|
278
|
-
totalCostAmountCents: z.number().int().min(0).optional().nullable(),
|
|
279
|
-
notes: z.string().optional().nullable(),
|
|
280
|
-
productId: z.string().optional().nullable(),
|
|
281
|
-
optionId: z.string().optional().nullable(),
|
|
282
|
-
optionUnitId: z.string().optional().nullable(),
|
|
283
|
-
pricingCategoryId: z.string().optional().nullable(),
|
|
284
|
-
productNameSnapshot: z.string().optional().nullable(),
|
|
285
|
-
optionNameSnapshot: z.string().optional().nullable(),
|
|
286
|
-
unitNameSnapshot: z.string().optional().nullable(),
|
|
287
|
-
departureLabelSnapshot: z.string().optional().nullable(),
|
|
288
|
-
sourceSnapshotId: z.string().optional().nullable(),
|
|
289
|
-
sourceOfferId: z.string().optional().nullable(),
|
|
290
|
-
availabilitySlotId: z.string().min(1),
|
|
291
|
-
allocationType: bookingAllocationTypeSchema.default("unit"),
|
|
292
|
-
metadata: z.record(z.string(), z.unknown()).optional().nullable(),
|
|
293
|
-
});
|
|
294
|
-
export const reserveBookingSchema = bookingCoreSchema
|
|
295
|
-
.omit({
|
|
296
|
-
status: true,
|
|
297
|
-
holdExpiresAt: true,
|
|
298
|
-
confirmedAt: true,
|
|
299
|
-
expiredAt: true,
|
|
300
|
-
cancelledAt: true,
|
|
301
|
-
completedAt: true,
|
|
302
|
-
redeemedAt: true,
|
|
303
|
-
})
|
|
304
|
-
.extend({
|
|
305
|
-
holdMinutes: z
|
|
306
|
-
.number()
|
|
307
|
-
.int()
|
|
308
|
-
.positive()
|
|
309
|
-
.max(24 * 60)
|
|
310
|
-
.optional(),
|
|
311
|
-
holdExpiresAt: z.string().datetime().optional().nullable(),
|
|
312
|
-
items: z.array(reserveBookingItemSchema).min(1),
|
|
313
|
-
})
|
|
314
|
-
.superRefine(validateExclusiveBillingParty);
|
|
315
|
-
export const extendBookingHoldSchema = z
|
|
316
|
-
.object({
|
|
317
|
-
holdMinutes: z
|
|
318
|
-
.number()
|
|
319
|
-
.int()
|
|
320
|
-
.positive()
|
|
321
|
-
.max(24 * 60)
|
|
322
|
-
.optional(),
|
|
323
|
-
holdExpiresAt: z.string().datetime().optional().nullable(),
|
|
324
|
-
})
|
|
325
|
-
.refine((value) => value.holdMinutes !== undefined || value.holdExpiresAt !== undefined, {
|
|
326
|
-
message: "holdMinutes or holdExpiresAt is required",
|
|
327
|
-
});
|
|
328
|
-
export const confirmBookingSchema = z.object({
|
|
329
|
-
note: z.string().optional().nullable(),
|
|
330
|
-
/**
|
|
331
|
-
* When true, downstream subscribers that send customer-facing
|
|
332
|
-
* notifications (e.g. notifications module's `autoConfirmAndDispatch`)
|
|
333
|
-
* skip dispatching for this confirmation. Lets the operator confirm a
|
|
334
|
-
* booking internally without auto-sending a confirmation email.
|
|
335
|
-
*/
|
|
336
|
-
suppressNotifications: z.boolean().optional(),
|
|
337
|
-
});
|
|
338
|
-
export const cancelBookingSchema = z.object({
|
|
339
|
-
note: z.string().optional().nullable(),
|
|
340
|
-
});
|
|
341
|
-
export const expireBookingSchema = z.object({
|
|
342
|
-
note: z.string().optional().nullable(),
|
|
343
|
-
});
|
|
344
|
-
export const expireStaleBookingsSchema = z.object({
|
|
345
|
-
before: z.string().datetime().optional().nullable(),
|
|
346
|
-
note: z.string().optional().nullable(),
|
|
347
|
-
});
|
|
348
|
-
export const startBookingSchema = z.object({
|
|
349
|
-
note: z.string().optional().nullable(),
|
|
350
|
-
});
|
|
351
|
-
export const completeBookingSchema = z.object({
|
|
352
|
-
note: z.string().optional().nullable(),
|
|
353
|
-
});
|
|
354
|
-
/**
|
|
355
|
-
* Admin-only override: skips the transition graph. `reason` is required —
|
|
356
|
-
* the operator has to explain why they're bypassing lifecycle laws. Use the
|
|
357
|
-
* verb-specific endpoints (/confirm, /cancel, /start, /complete, /expire) for
|
|
358
|
-
* normal state changes; this is for data-correction and exceptional cases.
|
|
359
|
-
* Confirmed overrides emit `booking.confirmed` by default for create-dialog
|
|
360
|
-
* compatibility; pass `suppressLifecycleEvents` for pure data correction.
|
|
361
|
-
*/
|
|
362
|
-
export const overrideBookingStatusSchema = z.object({
|
|
363
|
-
status: bookingStatusSchema,
|
|
364
|
-
reason: z.string().min(1).max(2000),
|
|
365
|
-
note: z.string().optional().nullable(),
|
|
366
|
-
/**
|
|
367
|
-
* Same notification opt-out as `confirmBookingSchema.suppressNotifications`.
|
|
368
|
-
* Only applies when the override path emits `booking.confirmed`.
|
|
369
|
-
*/
|
|
370
|
-
suppressNotifications: z.boolean().optional(),
|
|
371
|
-
/**
|
|
372
|
-
* When true, skip verb-specific lifecycle events such as
|
|
373
|
-
* `booking.confirmed`. The audit event `booking.status_overridden` still
|
|
374
|
-
* emits either way.
|
|
375
|
-
*/
|
|
376
|
-
suppressLifecycleEvents: z.boolean().optional(),
|
|
377
|
-
});
|
|
378
|
-
export const reserveBookingFromTransactionSchema = bookingCoreSchema
|
|
379
|
-
.pick({
|
|
380
|
-
bookingNumber: true,
|
|
381
|
-
sourceType: true,
|
|
382
|
-
contactFirstName: true,
|
|
383
|
-
contactLastName: true,
|
|
384
|
-
contactPartyType: true,
|
|
385
|
-
contactTaxId: true,
|
|
386
|
-
contactEmail: true,
|
|
387
|
-
contactPhone: true,
|
|
388
|
-
contactPreferredLanguage: true,
|
|
389
|
-
contactCountry: true,
|
|
390
|
-
contactRegion: true,
|
|
391
|
-
contactCity: true,
|
|
392
|
-
contactAddressLine1: true,
|
|
393
|
-
contactAddressLine2: true,
|
|
394
|
-
contactPostalCode: true,
|
|
395
|
-
internalNotes: true,
|
|
396
|
-
})
|
|
397
|
-
.extend({
|
|
398
|
-
sourceType: bookingSourceTypeSchema.default("internal"),
|
|
399
|
-
holdMinutes: z
|
|
400
|
-
.number()
|
|
401
|
-
.int()
|
|
402
|
-
.positive()
|
|
403
|
-
.max(24 * 60)
|
|
404
|
-
.optional(),
|
|
405
|
-
holdExpiresAt: z.string().datetime().optional().nullable(),
|
|
406
|
-
note: z.string().optional().nullable(),
|
|
407
|
-
includeParticipants: z.boolean().default(true),
|
|
408
|
-
});
|
|
409
|
-
// ---------- traveler records ----------
|
|
410
|
-
const travelerRecordCoreSchema = z.object({
|
|
411
|
-
personId: z.string().optional().nullable(),
|
|
412
|
-
participantType: bookingParticipantTypeSchema.default("traveler"),
|
|
413
|
-
travelerCategory: bookingTravelerCategorySchema.optional().nullable(),
|
|
414
|
-
firstName: z.string().min(1).max(255),
|
|
415
|
-
lastName: z.string().min(1).max(255),
|
|
416
|
-
email: z.string().email().optional().nullable(),
|
|
417
|
-
phone: z.string().max(50).optional().nullable(),
|
|
418
|
-
preferredLanguage: z.string().max(35).optional().nullable(),
|
|
419
|
-
specialRequests: z.string().optional().nullable(),
|
|
420
|
-
isPrimary: z.boolean().default(false),
|
|
421
|
-
notes: z.string().optional().nullable(),
|
|
422
|
-
});
|
|
423
|
-
// ---------- travelers ----------
|
|
424
|
-
const travelerCoreSchema = z.object({
|
|
425
|
-
firstName: z.string().min(1).max(255),
|
|
426
|
-
lastName: z.string().min(1).max(255),
|
|
427
|
-
email: z.string().email().optional().nullable(),
|
|
428
|
-
phone: z.string().max(50).optional().nullable(),
|
|
429
|
-
preferredLanguage: z.string().max(35).optional().nullable(),
|
|
430
|
-
specialRequests: z.string().optional().nullable(),
|
|
431
|
-
travelerCategory: bookingTravelerCategorySchema.optional().nullable(),
|
|
432
|
-
isPrimary: z.boolean().optional().nullable(),
|
|
433
|
-
notes: z.string().optional().nullable(),
|
|
434
|
-
});
|
|
435
|
-
export const insertTravelerSchema = travelerCoreSchema;
|
|
436
|
-
export const updateTravelerSchema = travelerCoreSchema.partial();
|
|
437
|
-
export const insertTravelerRecordSchema = travelerRecordCoreSchema;
|
|
438
|
-
export const updateTravelerRecordSchema = travelerRecordCoreSchema.partial();
|
|
439
|
-
// ---------- traveler travel details ----------
|
|
440
|
-
export const upsertTravelerTravelDetailsSchema = z.object({
|
|
441
|
-
nationality: z.string().max(100).optional().nullable(),
|
|
442
|
-
documentType: z
|
|
443
|
-
.enum(["passport", "id_card", "driver_license", "visa", "other"])
|
|
444
|
-
.optional()
|
|
445
|
-
.nullable(),
|
|
446
|
-
documentNumber: z.string().max(255).optional().nullable(),
|
|
447
|
-
documentExpiry: z.string().optional().nullable(),
|
|
448
|
-
documentIssuingCountry: z.string().max(255).optional().nullable(),
|
|
449
|
-
documentIssuingAuthority: z.string().max(255).optional().nullable(),
|
|
450
|
-
/** Provenance pointer to the seeding `crm.person_documents` row. */
|
|
451
|
-
documentPersonDocumentId: z.string().optional().nullable(),
|
|
452
|
-
dateOfBirth: z.string().optional().nullable(),
|
|
453
|
-
dietaryRequirements: z.string().optional().nullable(),
|
|
454
|
-
accessibilityNeeds: z.string().optional().nullable(),
|
|
455
|
-
isLeadTraveler: z.boolean().optional().nullable(),
|
|
456
|
-
sharingGroupId: z.string().max(255).optional().nullable(),
|
|
457
|
-
roomTypeId: z.string().max(255).optional().nullable(),
|
|
458
|
-
bedPreference: bookingTravelerBedPreferenceSchema.optional().nullable(),
|
|
459
|
-
allocations: travelerAllocationMapSchema.optional(),
|
|
460
|
-
});
|
|
461
|
-
// Flat shape combining plaintext traveler columns + encrypted travel-details
|
|
462
|
-
// fields, matching the pre-0.10 `createTravelerRecord` ergonomics. Migration
|
|
463
|
-
// boundary helper — see `bookingsService.createTravelerWithTravelDetails`.
|
|
464
|
-
export const createTravelerWithTravelDetailsSchema = travelerRecordCoreSchema.extend(upsertTravelerTravelDetailsSchema.shape);
|
|
465
|
-
export const updateTravelerWithTravelDetailsSchema = createTravelerWithTravelDetailsSchema.partial();
|
|
466
|
-
// ---------- booking items ----------
|
|
467
|
-
const bookingItemCoreSchema = z.object({
|
|
468
|
-
title: z.string().min(1).max(255),
|
|
469
|
-
description: z.string().optional().nullable(),
|
|
470
|
-
itemType: bookingItemTypeSchema.default("unit"),
|
|
471
|
-
status: bookingItemStatusSchema.default("draft"),
|
|
472
|
-
serviceDate: z.string().optional().nullable(),
|
|
473
|
-
startsAt: z.string().optional().nullable(),
|
|
474
|
-
endsAt: z.string().optional().nullable(),
|
|
475
|
-
quantity: z.number().int().positive().default(1),
|
|
476
|
-
sellCurrency: z.string().min(3).max(3),
|
|
477
|
-
unitSellAmountCents: z.number().int().optional().nullable(),
|
|
478
|
-
totalSellAmountCents: z.number().int().optional().nullable(),
|
|
479
|
-
costCurrency: z.string().min(3).max(3).optional().nullable(),
|
|
480
|
-
unitCostAmountCents: z.number().int().optional().nullable(),
|
|
481
|
-
totalCostAmountCents: z.number().int().optional().nullable(),
|
|
482
|
-
notes: z.string().optional().nullable(),
|
|
483
|
-
productId: z.string().optional().nullable(),
|
|
484
|
-
optionId: z.string().optional().nullable(),
|
|
485
|
-
optionUnitId: z.string().optional().nullable(),
|
|
486
|
-
pricingCategoryId: z.string().optional().nullable(),
|
|
487
|
-
availabilitySlotId: z.string().optional().nullable(),
|
|
488
|
-
// Catalog-snapshot overrides for catalog-less deployments (OTA case)
|
|
489
|
-
// or when the caller wants to write a specific historical label. If
|
|
490
|
-
// omitted, the service looks the values up from the catalog refs
|
|
491
|
-
// using the foreign IDs.
|
|
492
|
-
productNameSnapshot: z.string().optional().nullable(),
|
|
493
|
-
optionNameSnapshot: z.string().optional().nullable(),
|
|
494
|
-
unitNameSnapshot: z.string().optional().nullable(),
|
|
495
|
-
departureLabelSnapshot: z.string().optional().nullable(),
|
|
496
|
-
sourceSnapshotId: z.string().optional().nullable(),
|
|
497
|
-
sourceOfferId: z.string().optional().nullable(),
|
|
498
|
-
metadata: z.record(z.string(), z.unknown()).optional().nullable(),
|
|
499
|
-
});
|
|
500
|
-
export const insertBookingItemSchema = bookingItemCoreSchema;
|
|
501
|
-
export const updateBookingItemSchema = bookingItemCoreSchema.partial();
|
|
502
|
-
export const insertBookingAllocationSchema = z.object({
|
|
503
|
-
bookingItemId: z.string().min(1),
|
|
504
|
-
productId: z.string().optional().nullable(),
|
|
505
|
-
optionId: z.string().optional().nullable(),
|
|
506
|
-
optionUnitId: z.string().optional().nullable(),
|
|
507
|
-
pricingCategoryId: z.string().optional().nullable(),
|
|
508
|
-
availabilitySlotId: z.string().optional().nullable(),
|
|
509
|
-
quantity: z.number().int().positive().default(1),
|
|
510
|
-
allocationType: bookingAllocationTypeSchema.default("unit"),
|
|
511
|
-
status: bookingAllocationStatusSchema.default("held"),
|
|
512
|
-
holdExpiresAt: z.string().datetime().optional().nullable(),
|
|
513
|
-
confirmedAt: z.string().datetime().optional().nullable(),
|
|
514
|
-
releasedAt: z.string().datetime().optional().nullable(),
|
|
515
|
-
metadata: z.record(z.string(), z.unknown()).optional().nullable(),
|
|
516
|
-
});
|
|
517
|
-
export const updateBookingAllocationSchema = insertBookingAllocationSchema.partial();
|
|
518
|
-
// ---------- booking fulfillments ----------
|
|
519
|
-
const bookingFulfillmentInputSchema = z.object({
|
|
520
|
-
bookingItemId: z.string().optional().nullable(),
|
|
521
|
-
travelerId: z.string().optional().nullable(),
|
|
522
|
-
fulfillmentType: bookingFulfillmentTypeSchema,
|
|
523
|
-
deliveryChannel: bookingFulfillmentDeliveryChannelSchema,
|
|
524
|
-
status: bookingFulfillmentStatusSchema.default("issued"),
|
|
525
|
-
artifactUrl: z.string().url().optional().nullable(),
|
|
526
|
-
payload: z.record(z.string(), z.unknown()).optional().nullable(),
|
|
527
|
-
issuedAt: z.string().datetime().optional().nullable(),
|
|
528
|
-
revokedAt: z.string().datetime().optional().nullable(),
|
|
529
|
-
});
|
|
530
|
-
export const insertBookingFulfillmentSchema = bookingFulfillmentInputSchema.transform(({ travelerId, ...rest }) => ({
|
|
531
|
-
...rest,
|
|
532
|
-
travelerId: travelerId ?? null,
|
|
533
|
-
}));
|
|
534
|
-
export const updateBookingFulfillmentSchema = bookingFulfillmentInputSchema
|
|
535
|
-
.partial()
|
|
536
|
-
.transform(({ travelerId, ...rest }) => ({
|
|
537
|
-
...rest,
|
|
538
|
-
travelerId: travelerId !== undefined ? (travelerId ?? null) : undefined,
|
|
539
|
-
}));
|
|
540
|
-
// ---------- booking redemption events ----------
|
|
541
|
-
export const recordBookingRedemptionSchema = z
|
|
542
|
-
.object({
|
|
543
|
-
bookingItemId: z.string().optional().nullable(),
|
|
544
|
-
travelerId: z.string().optional().nullable(),
|
|
545
|
-
redeemedAt: z.string().datetime().optional().nullable(),
|
|
546
|
-
redeemedBy: z.string().max(255).optional().nullable(),
|
|
547
|
-
location: z.string().max(500).optional().nullable(),
|
|
548
|
-
method: bookingRedemptionMethodSchema.default("manual"),
|
|
549
|
-
metadata: z.record(z.string(), z.unknown()).optional().nullable(),
|
|
550
|
-
})
|
|
551
|
-
.transform(({ travelerId, ...rest }) => ({
|
|
552
|
-
...rest,
|
|
553
|
-
travelerId: travelerId ?? null,
|
|
554
|
-
}));
|
|
555
|
-
// ---------- booking item participants ----------
|
|
556
|
-
export const insertBookingItemTravelerSchema = z
|
|
557
|
-
.object({
|
|
558
|
-
travelerId: z.string().min(1).optional(),
|
|
559
|
-
role: bookingItemParticipantRoleSchema.default("traveler"),
|
|
560
|
-
isPrimary: z.boolean().default(false),
|
|
561
|
-
})
|
|
562
|
-
.refine((value) => Boolean(value.travelerId), {
|
|
563
|
-
message: "travelerId is required",
|
|
564
|
-
path: ["travelerId"],
|
|
565
|
-
})
|
|
566
|
-
.transform(({ travelerId, ...rest }) => ({
|
|
567
|
-
...rest,
|
|
568
|
-
travelerId: travelerId,
|
|
569
|
-
}));
|
|
570
|
-
export const insertBookingItemParticipantSchema = insertBookingItemTravelerSchema;
|
|
571
|
-
// ---------- supplier statuses ----------
|
|
572
|
-
const supplierStatusCoreSchema = z.object({
|
|
573
|
-
supplierServiceId: z.string().optional().nullable(),
|
|
574
|
-
serviceName: z.string().min(1).max(255),
|
|
575
|
-
status: supplierConfirmationStatusSchema.default("pending"),
|
|
576
|
-
supplierReference: z.string().max(255).optional().nullable(),
|
|
577
|
-
costCurrency: z.string().min(3).max(3),
|
|
578
|
-
costAmountCents: z.number().int().min(0),
|
|
579
|
-
notes: z.string().optional().nullable(),
|
|
580
|
-
});
|
|
581
|
-
export const insertSupplierStatusSchema = supplierStatusCoreSchema;
|
|
582
|
-
export const updateSupplierStatusSchema = supplierStatusCoreSchema.partial().extend({
|
|
583
|
-
confirmedAt: z.string().optional().nullable(),
|
|
584
|
-
});
|
|
585
|
-
// ---------- notes ----------
|
|
586
|
-
export const insertBookingNoteSchema = z.object({
|
|
587
|
-
content: z.string().min(1).max(10000),
|
|
588
|
-
});
|
|
589
|
-
export const updateBookingNoteSchema = z.object({
|
|
590
|
-
content: z.string().min(1).max(10000),
|
|
591
|
-
});
|
|
592
|
-
// ---------- documents ----------
|
|
593
|
-
export const insertBookingDocumentSchema = z
|
|
594
|
-
.object({
|
|
595
|
-
travelerId: z.string().optional().nullable(),
|
|
596
|
-
type: bookingDocumentTypeSchema,
|
|
597
|
-
fileName: z.string().min(1).max(500),
|
|
598
|
-
fileUrl: z.string().url(),
|
|
599
|
-
expiresAt: z.string().optional().nullable(),
|
|
600
|
-
notes: z.string().optional().nullable(),
|
|
601
|
-
})
|
|
602
|
-
.transform(({ travelerId, ...rest }) => ({
|
|
603
|
-
...rest,
|
|
604
|
-
travelerId: travelerId ?? null,
|
|
605
|
-
}));
|
|
606
|
-
export const insertBookingTravelerDocumentSchema = insertBookingDocumentSchema;
|
|
607
|
-
// ---------- booking groups ----------
|
|
608
|
-
export const bookingGroupKindSchema = z.enum(["shared_room", "cruise_party", "other"]);
|
|
609
|
-
export const bookingGroupMemberRoleSchema = z.enum(["primary", "shared"]);
|
|
610
|
-
const bookingGroupCoreSchema = z.object({
|
|
611
|
-
kind: bookingGroupKindSchema.default("shared_room"),
|
|
612
|
-
label: z.string().min(1).max(500),
|
|
613
|
-
primaryBookingId: z.string().optional().nullable(),
|
|
614
|
-
productId: z.string().optional().nullable(),
|
|
615
|
-
optionUnitId: z.string().optional().nullable(),
|
|
616
|
-
metadata: z.record(z.string(), z.unknown()).optional().nullable(),
|
|
617
|
-
});
|
|
618
|
-
export const insertBookingGroupSchema = bookingGroupCoreSchema;
|
|
619
|
-
export const updateBookingGroupSchema = bookingGroupCoreSchema.partial();
|
|
620
|
-
export const addBookingGroupMemberSchema = z.object({
|
|
621
|
-
bookingId: z.string().min(1),
|
|
622
|
-
role: bookingGroupMemberRoleSchema.default("shared"),
|
|
623
|
-
});
|
|
624
|
-
export const bookingGroupListQuerySchema = z.object({
|
|
625
|
-
kind: bookingGroupKindSchema.optional(),
|
|
626
|
-
productId: z.string().optional(),
|
|
627
|
-
optionUnitId: z.string().optional(),
|
|
628
|
-
limit: z.coerce.number().int().min(1).max(200).default(50),
|
|
629
|
-
offset: z.coerce.number().int().min(0).default(0),
|
|
630
|
-
});
|
|
631
|
-
export * from "./validation-public.js";
|
|
632
|
-
export * from "./validation-shared.js";
|
|
1
|
+
export * from "@voyantjs/bookings-contracts";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voyantjs/bookings",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.98.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -84,15 +84,16 @@
|
|
|
84
84
|
"drizzle-orm": "^0.45.2",
|
|
85
85
|
"hono": "^4.12.10",
|
|
86
86
|
"zod": "^4.3.6",
|
|
87
|
-
"@voyantjs/action-ledger": "0.
|
|
88
|
-
"@voyantjs/
|
|
89
|
-
"@voyantjs/
|
|
90
|
-
"@voyantjs/
|
|
91
|
-
"@voyantjs/
|
|
87
|
+
"@voyantjs/action-ledger": "0.98.0",
|
|
88
|
+
"@voyantjs/bookings-contracts": "0.98.0",
|
|
89
|
+
"@voyantjs/core": "0.98.0",
|
|
90
|
+
"@voyantjs/db": "0.98.0",
|
|
91
|
+
"@voyantjs/hono": "0.98.0",
|
|
92
|
+
"@voyantjs/utils": "0.98.0"
|
|
92
93
|
},
|
|
93
94
|
"devDependencies": {
|
|
94
95
|
"typescript": "^6.0.2",
|
|
95
|
-
"@voyantjs/products": "0.
|
|
96
|
+
"@voyantjs/products": "0.98.0",
|
|
96
97
|
"@voyantjs/voyant-typescript-config": "0.1.0"
|
|
97
98
|
},
|
|
98
99
|
"files": [
|