@voyantjs/products-contracts 0.100.0 → 0.101.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.
@@ -0,0 +1,397 @@
1
+ import { z } from "./validation-shared.js";
2
+ export declare const insertProductSchema: z.ZodObject<{
3
+ name: z.ZodString;
4
+ status: z.ZodDefault<z.ZodEnum<{
5
+ draft: "draft";
6
+ active: "active";
7
+ archived: "archived";
8
+ }>>;
9
+ description: z.ZodNullable<z.ZodOptional<z.ZodString>>;
10
+ inclusionsHtml: z.ZodNullable<z.ZodOptional<z.ZodString>>;
11
+ exclusionsHtml: z.ZodNullable<z.ZodOptional<z.ZodString>>;
12
+ termsHtml: z.ZodNullable<z.ZodOptional<z.ZodString>>;
13
+ termsShowOnContract: z.ZodDefault<z.ZodBoolean>;
14
+ bookingMode: z.ZodDefault<z.ZodEnum<{
15
+ date: "date";
16
+ other: "other";
17
+ date_time: "date_time";
18
+ open: "open";
19
+ stay: "stay";
20
+ transfer: "transfer";
21
+ itinerary: "itinerary";
22
+ }>>;
23
+ capacityMode: z.ZodDefault<z.ZodEnum<{
24
+ free_sale: "free_sale";
25
+ limited: "limited";
26
+ on_request: "on_request";
27
+ }>>;
28
+ timezone: z.ZodNullable<z.ZodOptional<z.ZodString>>;
29
+ visibility: z.ZodDefault<z.ZodEnum<{
30
+ public: "public";
31
+ private: "private";
32
+ hidden: "hidden";
33
+ }>>;
34
+ activated: z.ZodDefault<z.ZodBoolean>;
35
+ reservationTimeoutMinutes: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
36
+ sellCurrency: z.ZodString;
37
+ facilityId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
38
+ supplierId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
39
+ productTypeId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
40
+ contractTemplateId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
41
+ taxClassId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
42
+ startDate: z.ZodNullable<z.ZodOptional<z.ZodString>>;
43
+ endDate: z.ZodNullable<z.ZodOptional<z.ZodString>>;
44
+ pax: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
45
+ customerPaymentPolicy: z.ZodNullable<z.ZodOptional<z.ZodObject<{
46
+ deposit: z.ZodObject<{
47
+ kind: z.ZodEnum<{
48
+ none: "none";
49
+ percent: "percent";
50
+ fixed_cents: "fixed_cents";
51
+ }>;
52
+ percent: z.ZodOptional<z.ZodNumber>;
53
+ amountCents: z.ZodOptional<z.ZodNumber>;
54
+ }, z.core.$strip>;
55
+ minDaysBeforeDepartureForDeposit: z.ZodNumber;
56
+ balanceDueDaysBeforeDeparture: z.ZodNumber;
57
+ balanceDueMinDaysFromNow: z.ZodNumber;
58
+ }, z.core.$strip>>>;
59
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
60
+ sellAmountCents: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
61
+ costAmountCents: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
62
+ marginPercent: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
63
+ }, z.core.$strip>;
64
+ export declare const updateProductSchema: z.ZodObject<{
65
+ name: z.ZodOptional<z.ZodString>;
66
+ description: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
67
+ inclusionsHtml: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
68
+ exclusionsHtml: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
69
+ termsHtml: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
70
+ timezone: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
71
+ reservationTimeoutMinutes: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodNumber>>>;
72
+ sellCurrency: z.ZodOptional<z.ZodString>;
73
+ facilityId: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
74
+ supplierId: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
75
+ productTypeId: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
76
+ contractTemplateId: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
77
+ taxClassId: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
78
+ startDate: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
79
+ endDate: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
80
+ pax: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodNumber>>>;
81
+ customerPaymentPolicy: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodObject<{
82
+ deposit: z.ZodObject<{
83
+ kind: z.ZodEnum<{
84
+ none: "none";
85
+ percent: "percent";
86
+ fixed_cents: "fixed_cents";
87
+ }>;
88
+ percent: z.ZodOptional<z.ZodNumber>;
89
+ amountCents: z.ZodOptional<z.ZodNumber>;
90
+ }, z.core.$strip>;
91
+ minDaysBeforeDepartureForDeposit: z.ZodNumber;
92
+ balanceDueDaysBeforeDeparture: z.ZodNumber;
93
+ balanceDueMinDaysFromNow: z.ZodNumber;
94
+ }, z.core.$strip>>>>;
95
+ status: z.ZodOptional<z.ZodEnum<{
96
+ draft: "draft";
97
+ active: "active";
98
+ archived: "archived";
99
+ }>>;
100
+ bookingMode: z.ZodOptional<z.ZodEnum<{
101
+ date: "date";
102
+ other: "other";
103
+ date_time: "date_time";
104
+ open: "open";
105
+ stay: "stay";
106
+ transfer: "transfer";
107
+ itinerary: "itinerary";
108
+ }>>;
109
+ capacityMode: z.ZodOptional<z.ZodEnum<{
110
+ free_sale: "free_sale";
111
+ limited: "limited";
112
+ on_request: "on_request";
113
+ }>>;
114
+ visibility: z.ZodOptional<z.ZodEnum<{
115
+ public: "public";
116
+ private: "private";
117
+ hidden: "hidden";
118
+ }>>;
119
+ activated: z.ZodOptional<z.ZodBoolean>;
120
+ termsShowOnContract: z.ZodOptional<z.ZodBoolean>;
121
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
122
+ sellAmountCents: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
123
+ costAmountCents: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
124
+ marginPercent: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
125
+ }, z.core.$strip>;
126
+ export declare const selectProductSchema: z.ZodObject<{
127
+ name: z.ZodString;
128
+ status: z.ZodDefault<z.ZodEnum<{
129
+ draft: "draft";
130
+ active: "active";
131
+ archived: "archived";
132
+ }>>;
133
+ description: z.ZodNullable<z.ZodOptional<z.ZodString>>;
134
+ inclusionsHtml: z.ZodNullable<z.ZodOptional<z.ZodString>>;
135
+ exclusionsHtml: z.ZodNullable<z.ZodOptional<z.ZodString>>;
136
+ termsHtml: z.ZodNullable<z.ZodOptional<z.ZodString>>;
137
+ termsShowOnContract: z.ZodDefault<z.ZodBoolean>;
138
+ bookingMode: z.ZodDefault<z.ZodEnum<{
139
+ date: "date";
140
+ other: "other";
141
+ date_time: "date_time";
142
+ open: "open";
143
+ stay: "stay";
144
+ transfer: "transfer";
145
+ itinerary: "itinerary";
146
+ }>>;
147
+ capacityMode: z.ZodDefault<z.ZodEnum<{
148
+ free_sale: "free_sale";
149
+ limited: "limited";
150
+ on_request: "on_request";
151
+ }>>;
152
+ timezone: z.ZodNullable<z.ZodOptional<z.ZodString>>;
153
+ visibility: z.ZodDefault<z.ZodEnum<{
154
+ public: "public";
155
+ private: "private";
156
+ hidden: "hidden";
157
+ }>>;
158
+ activated: z.ZodDefault<z.ZodBoolean>;
159
+ reservationTimeoutMinutes: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
160
+ sellCurrency: z.ZodString;
161
+ facilityId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
162
+ supplierId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
163
+ productTypeId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
164
+ contractTemplateId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
165
+ taxClassId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
166
+ startDate: z.ZodNullable<z.ZodOptional<z.ZodString>>;
167
+ endDate: z.ZodNullable<z.ZodOptional<z.ZodString>>;
168
+ pax: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
169
+ customerPaymentPolicy: z.ZodNullable<z.ZodOptional<z.ZodObject<{
170
+ deposit: z.ZodObject<{
171
+ kind: z.ZodEnum<{
172
+ none: "none";
173
+ percent: "percent";
174
+ fixed_cents: "fixed_cents";
175
+ }>;
176
+ percent: z.ZodOptional<z.ZodNumber>;
177
+ amountCents: z.ZodOptional<z.ZodNumber>;
178
+ }, z.core.$strip>;
179
+ minDaysBeforeDepartureForDeposit: z.ZodNumber;
180
+ balanceDueDaysBeforeDeparture: z.ZodNumber;
181
+ balanceDueMinDaysFromNow: z.ZodNumber;
182
+ }, z.core.$strip>>>;
183
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
184
+ id: z.ZodString;
185
+ sellAmountCents: z.ZodNullable<z.ZodNumber>;
186
+ costAmountCents: z.ZodNullable<z.ZodNumber>;
187
+ marginPercent: z.ZodNullable<z.ZodNumber>;
188
+ createdAt: z.ZodCoercedDate<unknown>;
189
+ updatedAt: z.ZodCoercedDate<unknown>;
190
+ }, z.core.$strip>;
191
+ export declare const productListSortFieldSchema: z.ZodEnum<{
192
+ name: "name";
193
+ status: "status";
194
+ createdAt: "createdAt";
195
+ startDate: "startDate";
196
+ endDate: "endDate";
197
+ pax: "pax";
198
+ sellAmount: "sellAmount";
199
+ }>;
200
+ export declare const productListSortDirSchema: z.ZodEnum<{
201
+ asc: "asc";
202
+ desc: "desc";
203
+ }>;
204
+ export declare const productListQuerySchema: z.ZodObject<{
205
+ status: z.ZodOptional<z.ZodEnum<{
206
+ draft: "draft";
207
+ active: "active";
208
+ archived: "archived";
209
+ }>>;
210
+ bookingMode: z.ZodOptional<z.ZodEnum<{
211
+ date: "date";
212
+ other: "other";
213
+ date_time: "date_time";
214
+ open: "open";
215
+ stay: "stay";
216
+ transfer: "transfer";
217
+ itinerary: "itinerary";
218
+ }>>;
219
+ visibility: z.ZodOptional<z.ZodEnum<{
220
+ public: "public";
221
+ private: "private";
222
+ hidden: "hidden";
223
+ }>>;
224
+ activated: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
225
+ 0: "0";
226
+ 1: "1";
227
+ true: "true";
228
+ false: "false";
229
+ }>, z.ZodTransform<boolean, "0" | "1" | "true" | "false">>>;
230
+ facilityId: z.ZodOptional<z.ZodString>;
231
+ supplierId: z.ZodOptional<z.ZodString>;
232
+ productTypeId: z.ZodOptional<z.ZodString>;
233
+ contractTemplateId: z.ZodOptional<z.ZodString>;
234
+ taxClassId: z.ZodOptional<z.ZodString>;
235
+ categoryId: z.ZodOptional<z.ZodString>;
236
+ tag: z.ZodOptional<z.ZodString>;
237
+ search: z.ZodOptional<z.ZodString>;
238
+ dateFrom: z.ZodOptional<z.ZodString>;
239
+ dateTo: z.ZodOptional<z.ZodString>;
240
+ paxMin: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
241
+ paxMax: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
242
+ sellAmountMin: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
243
+ sellAmountMax: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
244
+ sortBy: z.ZodDefault<z.ZodEnum<{
245
+ name: "name";
246
+ status: "status";
247
+ createdAt: "createdAt";
248
+ startDate: "startDate";
249
+ endDate: "endDate";
250
+ pax: "pax";
251
+ sellAmount: "sellAmount";
252
+ }>>;
253
+ sortDir: z.ZodDefault<z.ZodEnum<{
254
+ asc: "asc";
255
+ desc: "desc";
256
+ }>>;
257
+ limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
258
+ offset: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
259
+ }, z.core.$strip>;
260
+ export declare const productAggregatesQuerySchema: z.ZodObject<{
261
+ from: z.ZodOptional<z.ZodString>;
262
+ to: z.ZodOptional<z.ZodString>;
263
+ }, z.core.$strip>;
264
+ export type InsertProduct = z.infer<typeof insertProductSchema>;
265
+ export type UpdateProduct = z.infer<typeof updateProductSchema>;
266
+ export type SelectProduct = z.infer<typeof selectProductSchema>;
267
+ export declare const insertProductOptionSchema: z.ZodObject<{
268
+ name: z.ZodString;
269
+ code: z.ZodNullable<z.ZodOptional<z.ZodString>>;
270
+ description: z.ZodNullable<z.ZodOptional<z.ZodString>>;
271
+ status: z.ZodDefault<z.ZodEnum<{
272
+ draft: "draft";
273
+ active: "active";
274
+ archived: "archived";
275
+ }>>;
276
+ isDefault: z.ZodDefault<z.ZodBoolean>;
277
+ sortOrder: z.ZodDefault<z.ZodNumber>;
278
+ availableFrom: z.ZodNullable<z.ZodOptional<z.ZodString>>;
279
+ availableTo: z.ZodNullable<z.ZodOptional<z.ZodString>>;
280
+ }, z.core.$strip>;
281
+ export declare const updateProductOptionSchema: z.ZodObject<{
282
+ name: z.ZodOptional<z.ZodString>;
283
+ code: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
284
+ description: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
285
+ status: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
286
+ draft: "draft";
287
+ active: "active";
288
+ archived: "archived";
289
+ }>>>;
290
+ isDefault: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
291
+ sortOrder: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
292
+ availableFrom: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
293
+ availableTo: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
294
+ }, z.core.$strip>;
295
+ export declare const productOptionListQuerySchema: z.ZodObject<{
296
+ productId: z.ZodOptional<z.ZodString>;
297
+ status: z.ZodOptional<z.ZodEnum<{
298
+ draft: "draft";
299
+ active: "active";
300
+ archived: "archived";
301
+ }>>;
302
+ limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
303
+ offset: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
304
+ }, z.core.$strip>;
305
+ export type InsertProductOption = z.infer<typeof insertProductOptionSchema>;
306
+ export type UpdateProductOption = z.infer<typeof updateProductOptionSchema>;
307
+ type OccupancyShape = {
308
+ unitType?: string | null | undefined;
309
+ occupancyMin?: number | null | undefined;
310
+ occupancyMax?: number | null | undefined;
311
+ };
312
+ type OccupancyIssue = {
313
+ path: string[];
314
+ message: string;
315
+ };
316
+ export declare const insertOptionUnitSchema: z.ZodObject<{
317
+ name: z.ZodString;
318
+ code: z.ZodNullable<z.ZodOptional<z.ZodString>>;
319
+ description: z.ZodNullable<z.ZodOptional<z.ZodString>>;
320
+ unitType: z.ZodDefault<z.ZodEnum<{
321
+ person: "person";
322
+ group: "group";
323
+ room: "room";
324
+ vehicle: "vehicle";
325
+ service: "service";
326
+ other: "other";
327
+ }>>;
328
+ minQuantity: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
329
+ maxQuantity: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
330
+ minAge: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
331
+ maxAge: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
332
+ occupancyMin: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
333
+ occupancyMax: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
334
+ isRequired: z.ZodDefault<z.ZodBoolean>;
335
+ isHidden: z.ZodDefault<z.ZodBoolean>;
336
+ sortOrder: z.ZodDefault<z.ZodNumber>;
337
+ }, z.core.$strip>;
338
+ export declare const updateOptionUnitSchema: z.ZodObject<{
339
+ name: z.ZodOptional<z.ZodString>;
340
+ code: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
341
+ description: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
342
+ unitType: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
343
+ person: "person";
344
+ group: "group";
345
+ room: "room";
346
+ vehicle: "vehicle";
347
+ service: "service";
348
+ other: "other";
349
+ }>>>;
350
+ minQuantity: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodNumber>>>;
351
+ maxQuantity: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodNumber>>>;
352
+ minAge: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodNumber>>>;
353
+ maxAge: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodNumber>>>;
354
+ occupancyMin: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodNumber>>>;
355
+ occupancyMax: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodNumber>>>;
356
+ isRequired: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
357
+ isHidden: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
358
+ sortOrder: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
359
+ }, z.core.$strip>;
360
+ /**
361
+ * Validate the merged state of an option unit — the persisted record with a
362
+ * patch applied. The partial-update schema only sees the patch payload, so
363
+ * it can't catch e.g. PATCH `{ occupancyMin: null }` clearing occupancy on a
364
+ * unit whose persisted unitType is already "room". The service layer fetches
365
+ * the existing row, applies the patch, and runs this against the merged
366
+ * shape before writing.
367
+ */
368
+ export declare function validateMergedOptionUnit(merged: OccupancyShape): {
369
+ ok: true;
370
+ } | {
371
+ ok: false;
372
+ issues: OccupancyIssue[];
373
+ };
374
+ export declare const optionUnitListQuerySchema: z.ZodObject<{
375
+ optionId: z.ZodOptional<z.ZodString>;
376
+ unitType: z.ZodOptional<z.ZodEnum<{
377
+ person: "person";
378
+ group: "group";
379
+ room: "room";
380
+ vehicle: "vehicle";
381
+ service: "service";
382
+ other: "other";
383
+ }>>;
384
+ limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
385
+ offset: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
386
+ }, z.core.$strip>;
387
+ export type InsertOptionUnit = z.infer<typeof insertOptionUnitSchema>;
388
+ export type UpdateOptionUnit = z.infer<typeof updateOptionUnitSchema>;
389
+ export declare const insertVersionSchema: z.ZodObject<{
390
+ notes: z.ZodNullable<z.ZodOptional<z.ZodString>>;
391
+ }, z.core.$strip>;
392
+ export type InsertVersion = z.infer<typeof insertVersionSchema>;
393
+ export declare const insertProductNoteSchema: z.ZodObject<{
394
+ content: z.ZodString;
395
+ }, z.core.$strip>;
396
+ export {};
397
+ //# sourceMappingURL=validation-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-core.d.ts","sourceRoot":"","sources":["../src/validation-core.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,CAAC,EACF,MAAM,wBAAwB,CAAA;AAgD/B,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAiD,CAAA;AAOjF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAWD,CAAA;AAC/B,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAO9B,CAAA;AACF,eAAO,MAAM,0BAA0B;;;;;;;;EAQrC,CAAA;AAEF,eAAO,MAAM,wBAAwB;;;EAA0B,CAAA;AAE/D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuBjC,CAAA;AAEF,eAAO,MAAM,4BAA4B;;;iBAGvC,CAAA;AACF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAY/D,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;iBAA0B,CAAA;AAChE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;iBAAoC,CAAA;AAC1E,eAAO,MAAM,4BAA4B;;;;;;;;;iBAKvC,CAAA;AACF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAC3E,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAwB3E,KAAK,cAAc,GAAG;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACpC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACxC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CACzC,CAAA;AAED,KAAK,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AA2BzD,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;iBAIjC,CAAA;AAMF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;iBAYjC,CAAA;AAEF;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,cAAc,GACrB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,cAAc,EAAE,CAAA;CAAE,CAGxD;AACD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;iBAKpC,CAAA;AACF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AACrE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAErE,eAAO,MAAM,mBAAmB;;iBAE9B,CAAA;AACF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAE/D,eAAO,MAAM,uBAAuB;;iBAElC,CAAA"}
@@ -0,0 +1,210 @@
1
+ import { booleanQueryParam, optionUnitTypeSchema, productBookingModeSchema, productCapacityModeSchema, productOptionStatusSchema, productStatusSchema, productVisibilitySchema, typeIdSchema, z, } from "./validation-shared.js";
2
+ const productDepositRuleSchema = z.object({
3
+ kind: z.enum(["none", "percent", "fixed_cents"]),
4
+ percent: z.number().min(0).max(100).optional(),
5
+ amountCents: z.number().int().min(0).optional(),
6
+ });
7
+ const productCustomerPaymentPolicySchema = z.object({
8
+ deposit: productDepositRuleSchema,
9
+ minDaysBeforeDepartureForDeposit: z.number().int().min(0),
10
+ balanceDueDaysBeforeDeparture: z.number().int().min(0),
11
+ balanceDueMinDaysFromNow: z.number().int().min(0),
12
+ });
13
+ const productCoreSchema = z.object({
14
+ name: z.string().min(1).max(255),
15
+ status: productStatusSchema.default("draft"),
16
+ description: z.string().optional().nullable(),
17
+ inclusionsHtml: z.string().optional().nullable(),
18
+ exclusionsHtml: z.string().optional().nullable(),
19
+ termsHtml: z.string().optional().nullable(),
20
+ termsShowOnContract: z.boolean().default(false),
21
+ bookingMode: productBookingModeSchema.default("date"),
22
+ capacityMode: productCapacityModeSchema.default("limited"),
23
+ timezone: z.string().max(100).optional().nullable(),
24
+ visibility: productVisibilitySchema.default("private"),
25
+ activated: z.boolean().default(false),
26
+ reservationTimeoutMinutes: z.number().int().min(0).optional().nullable(),
27
+ sellCurrency: z.string().min(3).max(3),
28
+ facilityId: z.string().optional().nullable(),
29
+ supplierId: z.string().optional().nullable(),
30
+ productTypeId: z.string().optional().nullable(),
31
+ contractTemplateId: typeIdSchema("contract_templates").optional().nullable(),
32
+ taxClassId: z.string().optional().nullable(),
33
+ startDate: z.string().optional().nullable(),
34
+ endDate: z.string().optional().nullable(),
35
+ pax: z.number().int().positive().optional().nullable(),
36
+ customerPaymentPolicy: productCustomerPaymentPolicySchema.optional().nullable(),
37
+ tags: z.array(z.string()).default([]),
38
+ });
39
+ const productPricingFields = {
40
+ sellAmountCents: z.number().int().min(0).optional().nullable(),
41
+ costAmountCents: z.number().int().min(0).optional().nullable(),
42
+ marginPercent: z.number().int().optional().nullable(),
43
+ };
44
+ export const insertProductSchema = productCoreSchema.extend(productPricingFields);
45
+ // `productCoreSchema` carries `.default(...)` on enum-ish fields so
46
+ // insert can omit them. zod's `.partial()` does NOT strip those
47
+ // defaults — every PATCH would synthesize `status: "draft"`,
48
+ // `visibility: "private"`, etc., and overwrite the row. Re-declare the
49
+ // defaulted fields without defaults before partial-ising so PATCH only
50
+ // touches the keys the client actually sent.
51
+ export const updateProductSchema = productCoreSchema
52
+ .extend({
53
+ status: productStatusSchema,
54
+ bookingMode: productBookingModeSchema,
55
+ capacityMode: productCapacityModeSchema,
56
+ visibility: productVisibilitySchema,
57
+ activated: z.boolean(),
58
+ termsShowOnContract: z.boolean(),
59
+ tags: z.array(z.string()),
60
+ })
61
+ .partial()
62
+ .extend(productPricingFields);
63
+ export const selectProductSchema = productCoreSchema.extend({
64
+ id: typeIdSchema("products"),
65
+ sellAmountCents: z.number().int().nullable(),
66
+ costAmountCents: z.number().int().nullable(),
67
+ marginPercent: z.number().int().nullable(),
68
+ createdAt: z.coerce.date(),
69
+ updatedAt: z.coerce.date(),
70
+ });
71
+ export const productListSortFieldSchema = z.enum([
72
+ "name",
73
+ "status",
74
+ "sellAmount",
75
+ "pax",
76
+ "startDate",
77
+ "endDate",
78
+ "createdAt",
79
+ ]);
80
+ export const productListSortDirSchema = z.enum(["asc", "desc"]);
81
+ export const productListQuerySchema = z.object({
82
+ status: productStatusSchema.optional(),
83
+ bookingMode: productBookingModeSchema.optional(),
84
+ visibility: productVisibilitySchema.optional(),
85
+ activated: booleanQueryParam.optional(),
86
+ facilityId: z.string().optional(),
87
+ supplierId: z.string().optional(),
88
+ productTypeId: z.string().optional(),
89
+ contractTemplateId: typeIdSchema("contract_templates").optional(),
90
+ taxClassId: z.string().optional(),
91
+ categoryId: z.string().optional(),
92
+ tag: z.string().optional(),
93
+ search: z.string().optional(),
94
+ dateFrom: z.string().optional(),
95
+ dateTo: z.string().optional(),
96
+ paxMin: z.coerce.number().int().min(0).optional(),
97
+ paxMax: z.coerce.number().int().min(0).optional(),
98
+ sellAmountMin: z.coerce.number().int().min(0).optional(),
99
+ sellAmountMax: z.coerce.number().int().min(0).optional(),
100
+ sortBy: productListSortFieldSchema.default("createdAt"),
101
+ sortDir: productListSortDirSchema.default("desc"),
102
+ limit: z.coerce.number().int().min(1).max(100).default(50),
103
+ offset: z.coerce.number().int().min(0).default(0),
104
+ });
105
+ export const productAggregatesQuerySchema = z.object({
106
+ from: z.string().datetime().optional(),
107
+ to: z.string().datetime().optional(),
108
+ });
109
+ const productOptionCoreSchema = z.object({
110
+ name: z.string().min(1).max(255),
111
+ code: z.string().max(100).optional().nullable(),
112
+ description: z.string().optional().nullable(),
113
+ status: productOptionStatusSchema.default("draft"),
114
+ isDefault: z.boolean().default(false),
115
+ sortOrder: z.number().int().default(0),
116
+ availableFrom: z.string().optional().nullable(),
117
+ availableTo: z.string().optional().nullable(),
118
+ });
119
+ export const insertProductOptionSchema = productOptionCoreSchema;
120
+ export const updateProductOptionSchema = productOptionCoreSchema.partial();
121
+ export const productOptionListQuerySchema = z.object({
122
+ productId: z.string().optional(),
123
+ status: productOptionStatusSchema.optional(),
124
+ limit: z.coerce.number().int().min(1).max(100).default(50),
125
+ offset: z.coerce.number().int().min(0).default(0),
126
+ });
127
+ const optionUnitCoreSchema = z.object({
128
+ name: z.string().min(1).max(255),
129
+ code: z.string().max(100).optional().nullable(),
130
+ description: z.string().optional().nullable(),
131
+ unitType: optionUnitTypeSchema.default("person"),
132
+ minQuantity: z.number().int().min(0).optional().nullable(),
133
+ maxQuantity: z.number().int().min(0).optional().nullable(),
134
+ minAge: z.number().int().min(0).optional().nullable(),
135
+ maxAge: z.number().int().min(0).optional().nullable(),
136
+ occupancyMin: z.number().int().min(0).optional().nullable(),
137
+ occupancyMax: z.number().int().min(0).optional().nullable(),
138
+ isRequired: z.boolean().default(false),
139
+ isHidden: z.boolean().default(false),
140
+ sortOrder: z.number().int().default(0),
141
+ });
142
+ // Room and vehicle units carry capacity that the storefront pricing math
143
+ // multiplies by (e.g. `room.occupancy * room.quantity` for per_person
144
+ // totals in service-departures). Without occupancyMin set, the multiplier
145
+ // silently falls back to 1 and the booking under-charges — see #481.
146
+ const OCCUPANCY_REQUIRED_TYPES = new Set(["room", "vehicle"]);
147
+ function collectOccupancyIssues(data) {
148
+ const issues = [];
149
+ if (data.unitType &&
150
+ OCCUPANCY_REQUIRED_TYPES.has(data.unitType) &&
151
+ (data.occupancyMin == null || data.occupancyMin < 1)) {
152
+ issues.push({
153
+ path: ["occupancyMin"],
154
+ message: `${data.unitType} units must declare occupancyMin (≥ 1)`,
155
+ });
156
+ }
157
+ if (data.occupancyMin != null &&
158
+ data.occupancyMax != null &&
159
+ data.occupancyMax < data.occupancyMin) {
160
+ issues.push({
161
+ path: ["occupancyMax"],
162
+ message: "occupancyMax must be ≥ occupancyMin",
163
+ });
164
+ }
165
+ return issues;
166
+ }
167
+ export const insertOptionUnitSchema = optionUnitCoreSchema.superRefine((data, ctx) => {
168
+ for (const issue of collectOccupancyIssues(data)) {
169
+ ctx.addIssue({ code: "custom", ...issue });
170
+ }
171
+ });
172
+ // Partial-update schema only enforces what's statically decidable from the
173
+ // patch alone. The "unitType room/vehicle requires occupancyMin" rule needs
174
+ // the merged record state, so it lives in the service layer below
175
+ // (validateMergedOptionUnit).
176
+ export const updateOptionUnitSchema = optionUnitCoreSchema.partial().superRefine((data, ctx) => {
177
+ if (data.occupancyMin != null &&
178
+ data.occupancyMax != null &&
179
+ data.occupancyMax < data.occupancyMin) {
180
+ ctx.addIssue({
181
+ code: "custom",
182
+ path: ["occupancyMax"],
183
+ message: "occupancyMax must be ≥ occupancyMin",
184
+ });
185
+ }
186
+ });
187
+ /**
188
+ * Validate the merged state of an option unit — the persisted record with a
189
+ * patch applied. The partial-update schema only sees the patch payload, so
190
+ * it can't catch e.g. PATCH `{ occupancyMin: null }` clearing occupancy on a
191
+ * unit whose persisted unitType is already "room". The service layer fetches
192
+ * the existing row, applies the patch, and runs this against the merged
193
+ * shape before writing.
194
+ */
195
+ export function validateMergedOptionUnit(merged) {
196
+ const issues = collectOccupancyIssues(merged);
197
+ return issues.length === 0 ? { ok: true } : { ok: false, issues };
198
+ }
199
+ export const optionUnitListQuerySchema = z.object({
200
+ optionId: z.string().optional(),
201
+ unitType: optionUnitTypeSchema.optional(),
202
+ limit: z.coerce.number().int().min(1).max(100).default(50),
203
+ offset: z.coerce.number().int().min(0).default(0),
204
+ });
205
+ export const insertVersionSchema = z.object({
206
+ notes: z.string().max(10000).optional().nullable(),
207
+ });
208
+ export const insertProductNoteSchema = z.object({
209
+ content: z.string().min(1).max(10000),
210
+ });