@voyantjs/bookings 0.6.9 → 0.8.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/service.js CHANGED
@@ -1,6 +1,6 @@
1
- import { and, asc, desc, eq, ilike, inArray, lte, ne, or, sql } from "drizzle-orm";
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 { bookingItemProductDetailsRef, bookingProductDetailsRef, optionUnitsRef, productDayServicesRef, productDaysRef, productOptionsRef, productsRef, productTicketSettingsRef, } from "./products-ref.js";
3
+ import { bookingItemProductDetailsRef, bookingProductDetailsRef, optionUnitsRef, productDayServicesRef, productDaysRef, productItinerariesRef, productOptionsRef, productsRef, productTicketSettingsRef, } from "./products-ref.js";
4
4
  import { bookingActivityLog, bookingAllocations, bookingDocuments, bookingFulfillments, bookingItems, bookingItemTravelers, bookingNotes, bookingRedemptionEvents, bookingStaffAssignments, bookingSupplierStatuses, bookings, bookingTravelers, } from "./schema.js";
5
5
  import { cleanupGroupOnBookingCancelled } from "./service-groups.js";
6
6
  import { bookingTransactionDetailsRef, offerItemParticipantsRef, offerItemsRef, offerParticipantsRef, offerStaffAssignmentsRef, offersRef, orderItemParticipantsRef, orderItemsRef, orderParticipantsRef, orderStaffAssignmentsRef, ordersRef, } from "./transactions-ref.js";
@@ -167,10 +167,15 @@ async function getConvertProductData(db, data) {
167
167
  .limit(1);
168
168
  option = defaultOption ?? null;
169
169
  }
170
+ // product_days is keyed by itinerary_id (products re-parented days onto
171
+ // product_itineraries); getConvertProductData joins through the itinerary
172
+ // ref so the per-product day lookup still works for converts that want to
173
+ // seed booking supplier statuses from the product's day services.
170
174
  const days = await db
171
- .select()
175
+ .select({ id: productDaysRef.id, dayNumber: productDaysRef.dayNumber })
172
176
  .from(productDaysRef)
173
- .where(eq(productDaysRef.productId, product.id))
177
+ .innerJoin(productItinerariesRef, eq(productDaysRef.itineraryId, productItinerariesRef.id))
178
+ .where(eq(productItinerariesRef.productId, product.id))
174
179
  .orderBy(asc(productDaysRef.dayNumber));
175
180
  const dayServices = days.length
176
181
  ? await db
@@ -184,7 +189,9 @@ async function getConvertProductData(db, data) {
184
189
  .where(sql `${productDayServicesRef.dayId} IN (
185
190
  SELECT ${productDaysRef.id}
186
191
  FROM ${productDaysRef}
187
- WHERE ${productDaysRef.productId} = ${product.id}
192
+ INNER JOIN ${productItinerariesRef}
193
+ ON ${productDaysRef.itineraryId} = ${productItinerariesRef.id}
194
+ WHERE ${productItinerariesRef.productId} = ${product.id}
188
195
  )`)
189
196
  .orderBy(asc(productDayServicesRef.sortOrder), asc(productDayServicesRef.id))
190
197
  : [];
@@ -195,6 +202,27 @@ async function getConvertProductData(db, data) {
195
202
  .from(optionUnitsRef)
196
203
  .where(eq(optionUnitsRef.optionId, option.id))
197
204
  .orderBy(asc(optionUnitsRef.sortOrder), asc(optionUnitsRef.createdAt));
205
+ let slot = null;
206
+ if (data.slotId) {
207
+ const [selectedSlot] = await db
208
+ .select()
209
+ .from(availabilitySlotsRef)
210
+ .where(and(eq(availabilitySlotsRef.id, data.slotId), eq(availabilitySlotsRef.productId, product.id)))
211
+ .limit(1);
212
+ if (!selectedSlot) {
213
+ return null;
214
+ }
215
+ if (option && selectedSlot.optionId && selectedSlot.optionId !== option.id) {
216
+ return null;
217
+ }
218
+ slot = {
219
+ id: selectedSlot.id,
220
+ dateLocal: selectedSlot.dateLocal,
221
+ startsAt: selectedSlot.startsAt,
222
+ endsAt: selectedSlot.endsAt,
223
+ timezone: selectedSlot.timezone,
224
+ };
225
+ }
198
226
  return {
199
227
  product: {
200
228
  id: product.id,
@@ -209,6 +237,7 @@ async function getConvertProductData(db, data) {
209
237
  pax: product.pax,
210
238
  },
211
239
  option: option ? { id: option.id, name: option.name } : null,
240
+ slot,
212
241
  dayServices,
213
242
  units: units.map((unit) => ({
214
243
  id: unit.id,
@@ -799,7 +828,92 @@ async function autoIssueFulfillmentsForBooking(db, bookingId, userId) {
799
828
  metadata: { count: fulfillmentsToInsert.length },
800
829
  });
801
830
  }
831
+ /**
832
+ * Booking statuses that count as "active" for aggregate purposes (matches the
833
+ * slot-unit-availability counting rules — cancelled and expired drop out).
834
+ */
835
+ const AGGREGATE_ACTIVE_STATUSES = [
836
+ "draft",
837
+ "on_hold",
838
+ "confirmed",
839
+ "in_progress",
840
+ "completed",
841
+ ];
802
842
  export const bookingsService = {
843
+ /**
844
+ * Pre-aggregated dashboard numbers for the admin bookings surface. Replaces
845
+ * the pattern of fetching a large `listBookings` page and deriving KPIs
846
+ * client-side — which broke past the page limit and disagreed across apps
847
+ * on which statuses count.
848
+ *
849
+ * All ranges are UTC-based.
850
+ */
851
+ async getBookingAggregates(db, options = {}) {
852
+ const fromDate = options.from ? new Date(options.from) : undefined;
853
+ const toDate = options.to ? new Date(options.to) : undefined;
854
+ const rangeConditions = [];
855
+ if (fromDate)
856
+ rangeConditions.push(sql `${bookings.createdAt} >= ${fromDate}`);
857
+ if (toDate)
858
+ rangeConditions.push(sql `${bookings.createdAt} < ${toDate}`);
859
+ const rangeWhere = rangeConditions.length ? and(...rangeConditions) : undefined;
860
+ const [totalRow] = await db
861
+ .select({ count: sql `count(*)::int` })
862
+ .from(bookings)
863
+ .where(rangeWhere);
864
+ const statusRows = await db
865
+ .select({
866
+ status: bookings.status,
867
+ count: sql `count(*)::int`,
868
+ })
869
+ .from(bookings)
870
+ .where(rangeWhere)
871
+ .groupBy(bookings.status);
872
+ const countsByStatusMap = new Map(statusRows.map((row) => [row.status, row.count]));
873
+ const monthlyCountsRows = await db
874
+ .select({
875
+ yearMonth: sql `to_char(${bookings.createdAt} at time zone 'UTC', 'YYYY-MM')`,
876
+ count: sql `count(*)::int`,
877
+ })
878
+ .from(bookings)
879
+ .where(rangeWhere)
880
+ .groupBy(sql `to_char(${bookings.createdAt} at time zone 'UTC', 'YYYY-MM')`)
881
+ .orderBy(sql `to_char(${bookings.createdAt} at time zone 'UTC', 'YYYY-MM')`);
882
+ const monthlyRevenueRows = await db
883
+ .select({
884
+ yearMonth: sql `to_char(${bookings.createdAt} at time zone 'UTC', 'YYYY-MM')`,
885
+ currency: bookings.sellCurrency,
886
+ sellAmountCents: sql `coalesce(sum(${bookings.sellAmountCents}), 0)::bigint`,
887
+ })
888
+ .from(bookings)
889
+ .where(and(...(rangeConditions.length ? rangeConditions : []), sql `${bookings.sellAmountCents} IS NOT NULL`, inArray(bookings.status, [...AGGREGATE_ACTIVE_STATUSES])))
890
+ .groupBy(sql `to_char(${bookings.createdAt} at time zone 'UTC', 'YYYY-MM')`, bookings.sellCurrency)
891
+ .orderBy(sql `to_char(${bookings.createdAt} at time zone 'UTC', 'YYYY-MM')`, bookings.sellCurrency);
892
+ const todayUtc = new Date();
893
+ todayUtc.setUTCHours(0, 0, 0, 0);
894
+ const todayDateString = todayUtc.toISOString().slice(0, 10);
895
+ const [upcomingRow] = await db
896
+ .select({ count: sql `count(*)::int` })
897
+ .from(bookings)
898
+ .where(and(inArray(bookings.status, [...AGGREGATE_ACTIVE_STATUSES]), sql `${bookings.startDate} >= ${todayDateString}`));
899
+ return {
900
+ total: totalRow?.count ?? 0,
901
+ countsByStatus: AGGREGATE_ACTIVE_STATUSES.concat(["expired", "cancelled"]).map((status) => ({
902
+ status,
903
+ count: countsByStatusMap.get(status) ?? 0,
904
+ })),
905
+ monthlyCounts: monthlyCountsRows.map((row) => ({
906
+ yearMonth: row.yearMonth,
907
+ count: row.count,
908
+ })),
909
+ monthlyRevenue: monthlyRevenueRows.map((row) => ({
910
+ yearMonth: row.yearMonth,
911
+ currency: row.currency,
912
+ sellAmountCents: Number(row.sellAmountCents),
913
+ })),
914
+ upcomingDepartures: upcomingRow?.count ?? 0,
915
+ };
916
+ },
803
917
  async listBookings(db, query) {
804
918
  const conditions = [];
805
919
  if (query.status) {
@@ -809,6 +923,25 @@ export const bookingsService = {
809
923
  const term = `%${query.search}%`;
810
924
  conditions.push(or(ilike(bookings.bookingNumber, term), ilike(bookings.internalNotes, term)));
811
925
  }
926
+ if (query.personId) {
927
+ conditions.push(eq(bookings.personId, query.personId));
928
+ }
929
+ if (query.organizationId) {
930
+ conditions.push(eq(bookings.organizationId, query.organizationId));
931
+ }
932
+ if (query.productId || query.optionId) {
933
+ const itemConditions = [eq(bookingItems.bookingId, bookings.id)];
934
+ if (query.productId) {
935
+ itemConditions.push(eq(bookingItems.productId, query.productId));
936
+ }
937
+ if (query.optionId) {
938
+ itemConditions.push(eq(bookingItems.optionId, query.optionId));
939
+ }
940
+ conditions.push(exists(db
941
+ .select({ one: sql `1` })
942
+ .from(bookingItems)
943
+ .where(and(...itemConditions))));
944
+ }
812
945
  const where = conditions.length > 0 ? and(...conditions) : undefined;
813
946
  const [rows, countResult] = await Promise.all([
814
947
  db
@@ -828,7 +961,16 @@ export const bookingsService = {
828
961
  };
829
962
  },
830
963
  async convertProductToBooking(db, data, productData, userId) {
831
- const { product, option, dayServices, units } = productData;
964
+ const { product, option, slot, dayServices, units } = productData;
965
+ // Slot dates win over product dates so scheduled/recurring products don't
966
+ // land with null dates. endsAt is a timestamp; fall back to the slot's
967
+ // dateLocal when the slot has no explicit end timestamp.
968
+ const startDate = slot?.dateLocal ?? product.startDate;
969
+ const endDate = slot
970
+ ? slot.endsAt
971
+ ? slot.endsAt.toISOString().slice(0, 10)
972
+ : slot.dateLocal
973
+ : product.endDate;
832
974
  const [booking] = await db
833
975
  .insert(bookings)
834
976
  .values({
@@ -840,8 +982,8 @@ export const bookingsService = {
840
982
  sellAmountCents: product.sellAmountCents,
841
983
  costAmountCents: product.costAmountCents,
842
984
  marginPercent: product.marginPercent,
843
- startDate: product.startDate,
844
- endDate: product.endDate,
985
+ startDate,
986
+ endDate,
845
987
  pax: product.pax,
846
988
  internalNotes: data.internalNotes ?? null,
847
989
  })
@@ -865,6 +1007,14 @@ export const bookingsService = {
865
1007
  : selectedUnits.length === 1
866
1008
  ? selectedUnits
867
1009
  : [];
1010
+ const slotFields = slot
1011
+ ? {
1012
+ serviceDate: slot.dateLocal,
1013
+ startsAt: slot.startsAt,
1014
+ endsAt: slot.endsAt,
1015
+ metadata: { availabilitySlotId: slot.id },
1016
+ }
1017
+ : { metadata: null };
868
1018
  const itemRows = unitsToSeed.length > 0
869
1019
  ? unitsToSeed.map((unit, index) => {
870
1020
  const quantity = unit.unitType === "person" && product.pax
@@ -897,6 +1047,7 @@ export const bookingsService = {
897
1047
  productId: product.id,
898
1048
  optionId: option?.id ?? null,
899
1049
  optionUnitId: unit.id,
1050
+ ...slotFields,
900
1051
  };
901
1052
  })
902
1053
  : [
@@ -916,6 +1067,7 @@ export const bookingsService = {
916
1067
  productId: product.id,
917
1068
  optionId: option?.id ?? null,
918
1069
  optionUnitId: null,
1070
+ ...slotFields,
919
1071
  },
920
1072
  ];
921
1073
  const insertedItems = await db.insert(bookingItems).values(itemRows).returning();
@@ -948,7 +1100,12 @@ export const bookingsService = {
948
1100
  actorId: userId ?? "system",
949
1101
  activityType: "booking_converted",
950
1102
  description: `Booking converted from product "${product.name}"`,
951
- metadata: { productId: product.id, productName: product.name, optionId: option?.id ?? null },
1103
+ metadata: {
1104
+ productId: product.id,
1105
+ productName: product.name,
1106
+ optionId: option?.id ?? null,
1107
+ slotId: slot?.id ?? null,
1108
+ },
952
1109
  });
953
1110
  return booking;
954
1111
  },
@@ -1324,7 +1481,7 @@ export const bookingsService = {
1324
1481
  .returning({ id: bookings.id });
1325
1482
  return row ?? null;
1326
1483
  },
1327
- async updateBookingStatus(db, id, data, userId) {
1484
+ async updateBookingStatus(db, id, data, userId, runtime = {}) {
1328
1485
  const [current] = await db
1329
1486
  .select({ id: bookings.id, status: bookings.status })
1330
1487
  .from(bookings)
@@ -1334,13 +1491,13 @@ export const bookingsService = {
1334
1491
  return { status: "not_found" };
1335
1492
  }
1336
1493
  if (current.status === "on_hold" && data.status === "confirmed") {
1337
- return bookingsService.confirmBooking(db, id, { note: data.note }, userId);
1494
+ return bookingsService.confirmBooking(db, id, { note: data.note }, userId, runtime);
1338
1495
  }
1339
1496
  if (current.status === "on_hold" && data.status === "expired") {
1340
- return bookingsService.expireBooking(db, id, { note: data.note }, userId);
1497
+ return bookingsService.expireBooking(db, id, { note: data.note }, userId, runtime);
1341
1498
  }
1342
1499
  if (data.status === "cancelled") {
1343
- return bookingsService.cancelBooking(db, id, { note: data.note }, userId);
1500
+ return bookingsService.cancelBooking(db, id, { note: data.note }, userId, runtime);
1344
1501
  }
1345
1502
  if (data.status === "on_hold") {
1346
1503
  return { status: "invalid_transition" };
@@ -1376,9 +1533,9 @@ export const bookingsService = {
1376
1533
  }
1377
1534
  return { status: "ok", booking: row ?? null };
1378
1535
  },
1379
- async confirmBooking(db, id, data, userId) {
1536
+ async confirmBooking(db, id, data, userId, runtime = {}) {
1380
1537
  try {
1381
- return await db.transaction(async (tx) => {
1538
+ const result = await db.transaction(async (tx) => {
1382
1539
  const rows = await tx.execute(sql `SELECT id, booking_number, status, hold_expires_at
1383
1540
  FROM ${bookings}
1384
1541
  WHERE ${bookings.id} = ${id}
@@ -1432,6 +1589,17 @@ export const bookingsService = {
1432
1589
  }
1433
1590
  return { status: "ok", booking: row ?? null };
1434
1591
  });
1592
+ // Emit AFTER the transaction commits so subscribers can't observe a
1593
+ // confirmed state that might still roll back. `emit` is fire-and-forget
1594
+ // per the EventBus contract — subscriber errors are logged, not rethrown.
1595
+ if (result.status === "ok" && result.booking) {
1596
+ await runtime.eventBus?.emit("booking.confirmed", {
1597
+ bookingId: result.booking.id,
1598
+ bookingNumber: result.booking.bookingNumber,
1599
+ actorId: userId ?? null,
1600
+ }, { category: "domain", source: "service" });
1601
+ }
1602
+ return result;
1435
1603
  }
1436
1604
  catch (error) {
1437
1605
  if (error instanceof BookingServiceError) {
@@ -1490,9 +1658,9 @@ export const bookingsService = {
1490
1658
  throw error;
1491
1659
  }
1492
1660
  },
1493
- async expireBooking(db, id, data, userId) {
1661
+ async expireBooking(db, id, data, userId, runtime = {}) {
1494
1662
  try {
1495
- return await db.transaction(async (tx) => {
1663
+ const result = await db.transaction(async (tx) => {
1496
1664
  const rows = await tx.execute(sql `SELECT id, status, hold_expires_at
1497
1665
  FROM ${bookings}
1498
1666
  WHERE ${bookings.id} = ${id}
@@ -1549,6 +1717,15 @@ export const bookingsService = {
1549
1717
  }
1550
1718
  return { status: "ok", booking: row ?? null };
1551
1719
  });
1720
+ if (result.status === "ok" && result.booking) {
1721
+ await runtime.eventBus?.emit("booking.expired", {
1722
+ bookingId: result.booking.id,
1723
+ bookingNumber: result.booking.bookingNumber,
1724
+ cause: runtime.cause ?? "route",
1725
+ actorId: userId ?? null,
1726
+ }, { category: "domain", source: "service" });
1727
+ }
1728
+ return result;
1552
1729
  }
1553
1730
  catch (error) {
1554
1731
  if (error instanceof BookingServiceError) {
@@ -1557,7 +1734,7 @@ export const bookingsService = {
1557
1734
  throw error;
1558
1735
  }
1559
1736
  },
1560
- async expireStaleBookings(db, data, userId) {
1737
+ async expireStaleBookings(db, data, userId, runtime = {}) {
1561
1738
  const cutoff = data.before ? new Date(data.before) : new Date();
1562
1739
  const staleBookings = await db
1563
1740
  .select({ id: bookings.id })
@@ -1566,7 +1743,7 @@ export const bookingsService = {
1566
1743
  .orderBy(asc(bookings.holdExpiresAt), asc(bookings.createdAt));
1567
1744
  const expiredIds = [];
1568
1745
  for (const booking of staleBookings) {
1569
- const result = await this.expireBooking(db, booking.id, { note: data.note ?? "Hold expired by sweep" }, userId);
1746
+ const result = await this.expireBooking(db, booking.id, { note: data.note ?? "Hold expired by sweep" }, userId, { ...runtime, cause: "sweep" });
1570
1747
  if ("booking" in result && result.booking) {
1571
1748
  expiredIds.push(result.booking.id);
1572
1749
  }
@@ -1577,9 +1754,9 @@ export const bookingsService = {
1577
1754
  cutoff,
1578
1755
  };
1579
1756
  },
1580
- async cancelBooking(db, id, data, userId) {
1757
+ async cancelBooking(db, id, data, userId, runtime = {}) {
1581
1758
  try {
1582
- return await db.transaction(async (tx) => {
1759
+ const result = await db.transaction(async (tx) => {
1583
1760
  const rows = await tx.execute(sql `SELECT id, status
1584
1761
  FROM ${bookings}
1585
1762
  WHERE ${bookings.id} = ${id}
@@ -1591,6 +1768,7 @@ export const bookingsService = {
1591
1768
  if (!["draft", "on_hold", "confirmed", "in_progress"].includes(booking.status)) {
1592
1769
  throw new BookingServiceError("invalid_transition");
1593
1770
  }
1771
+ const previousStatus = booking.status;
1594
1772
  const allocations = await tx
1595
1773
  .select()
1596
1774
  .from(bookingAllocations)
@@ -1640,8 +1818,17 @@ export const bookingsService = {
1640
1818
  }
1641
1819
  // Clean up any booking-group membership (dissolve if ≤1 active members remain).
1642
1820
  await cleanupGroupOnBookingCancelled(tx, id);
1643
- return { status: "ok", booking: row ?? null };
1821
+ return { status: "ok", booking: row ?? null, previousStatus };
1644
1822
  });
1823
+ if (result.status === "ok" && result.booking) {
1824
+ await runtime.eventBus?.emit("booking.cancelled", {
1825
+ bookingId: result.booking.id,
1826
+ bookingNumber: result.booking.bookingNumber,
1827
+ previousStatus: result.previousStatus,
1828
+ actorId: userId ?? null,
1829
+ }, { category: "domain", source: "service" });
1830
+ }
1831
+ return { status: result.status, booking: result.booking };
1645
1832
  }
1646
1833
  catch (error) {
1647
1834
  if (error instanceof BookingServiceError) {
@@ -27,8 +27,8 @@ export declare const publicCreateBookingSessionItemSchema: z.ZodObject<{
27
27
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
28
28
  itemType: z.ZodDefault<z.ZodEnum<{
29
29
  service: "service";
30
- other: "other";
31
30
  unit: "unit";
31
+ other: "other";
32
32
  extra: "extra";
33
33
  fee: "fee";
34
34
  tax: "tax";
@@ -53,8 +53,8 @@ export declare const publicCreateBookingSessionItemSchema: z.ZodObject<{
53
53
  metadata: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
54
54
  availabilitySlotId: z.ZodString;
55
55
  allocationType: z.ZodDefault<z.ZodEnum<{
56
- resource: "resource";
57
56
  unit: "unit";
57
+ resource: "resource";
58
58
  pickup: "pickup";
59
59
  }>>;
60
60
  }, z.core.$strip>;
@@ -78,8 +78,8 @@ export declare const publicCreateBookingSessionSchema: z.ZodObject<{
78
78
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
79
79
  itemType: z.ZodDefault<z.ZodEnum<{
80
80
  service: "service";
81
- other: "other";
82
81
  unit: "unit";
82
+ other: "other";
83
83
  extra: "extra";
84
84
  fee: "fee";
85
85
  tax: "tax";
@@ -104,8 +104,8 @@ export declare const publicCreateBookingSessionSchema: z.ZodObject<{
104
104
  metadata: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
105
105
  availabilitySlotId: z.ZodString;
106
106
  allocationType: z.ZodDefault<z.ZodEnum<{
107
- resource: "resource";
108
107
  unit: "unit";
108
+ resource: "resource";
109
109
  pickup: "pickup";
110
110
  }>>;
111
111
  }, z.core.$strip>>;
@@ -252,8 +252,8 @@ export declare const publicBookingSessionItemSchema: z.ZodObject<{
252
252
  description: z.ZodNullable<z.ZodString>;
253
253
  itemType: z.ZodEnum<{
254
254
  service: "service";
255
- other: "other";
256
255
  unit: "unit";
256
+ other: "other";
257
257
  extra: "extra";
258
258
  fee: "fee";
259
259
  tax: "tax";
@@ -307,8 +307,8 @@ export declare const publicBookingSessionAllocationSchema: z.ZodObject<{
307
307
  availabilitySlotId: z.ZodNullable<z.ZodString>;
308
308
  quantity: z.ZodNumber;
309
309
  allocationType: z.ZodEnum<{
310
- resource: "resource";
311
310
  unit: "unit";
311
+ resource: "resource";
312
312
  pickup: "pickup";
313
313
  }>;
314
314
  status: z.ZodEnum<{
@@ -384,8 +384,8 @@ export declare const publicBookingSessionSchema: z.ZodObject<{
384
384
  description: z.ZodNullable<z.ZodString>;
385
385
  itemType: z.ZodEnum<{
386
386
  service: "service";
387
- other: "other";
388
387
  unit: "unit";
388
+ other: "other";
389
389
  extra: "extra";
390
390
  fee: "fee";
391
391
  tax: "tax";
@@ -439,8 +439,8 @@ export declare const publicBookingSessionSchema: z.ZodObject<{
439
439
  availabilitySlotId: z.ZodNullable<z.ZodString>;
440
440
  quantity: z.ZodNumber;
441
441
  allocationType: z.ZodEnum<{
442
- resource: "resource";
443
442
  unit: "unit";
443
+ resource: "resource";
444
444
  pickup: "pickup";
445
445
  }>;
446
446
  status: z.ZodEnum<{
@@ -589,8 +589,8 @@ export declare const publicBookingSessionRepriceResultSchema: z.ZodObject<{
589
589
  description: z.ZodNullable<z.ZodString>;
590
590
  itemType: z.ZodEnum<{
591
591
  service: "service";
592
- other: "other";
593
592
  unit: "unit";
593
+ other: "other";
594
594
  extra: "extra";
595
595
  fee: "fee";
596
596
  tax: "tax";
@@ -644,8 +644,8 @@ export declare const publicBookingSessionRepriceResultSchema: z.ZodObject<{
644
644
  availabilitySlotId: z.ZodNullable<z.ZodString>;
645
645
  quantity: z.ZodNumber;
646
646
  allocationType: z.ZodEnum<{
647
- resource: "resource";
648
647
  unit: "unit";
648
+ resource: "resource";
649
649
  pickup: "pickup";
650
650
  }>;
651
651
  status: z.ZodEnum<{
@@ -769,8 +769,8 @@ export declare const publicBookingOverviewSchema: z.ZodObject<{
769
769
  description: z.ZodNullable<z.ZodString>;
770
770
  itemType: z.ZodEnum<{
771
771
  service: "service";
772
- other: "other";
773
772
  unit: "unit";
773
+ other: "other";
774
774
  extra: "extra";
775
775
  fee: "fee";
776
776
  tax: "tax";
@@ -37,8 +37,8 @@ export declare const bookingTravelerCategorySchema: z.ZodEnum<{
37
37
  }>;
38
38
  export declare const bookingItemTypeSchema: z.ZodEnum<{
39
39
  service: "service";
40
- other: "other";
41
40
  unit: "unit";
41
+ other: "other";
42
42
  extra: "extra";
43
43
  fee: "fee";
44
44
  tax: "tax";
@@ -66,8 +66,8 @@ export declare const bookingStaffAssignmentRoleSchema: z.ZodEnum<{
66
66
  service_assignee: "service_assignee";
67
67
  }>;
68
68
  export declare const bookingAllocationTypeSchema: z.ZodEnum<{
69
- resource: "resource";
70
69
  unit: "unit";
70
+ resource: "resource";
71
71
  pickup: "pickup";
72
72
  }>;
73
73
  export declare const bookingAllocationStatusSchema: z.ZodEnum<{
@@ -161,17 +161,36 @@ export declare const bookingListQuerySchema: z.ZodObject<{
161
161
  expired: "expired";
162
162
  }>>;
163
163
  search: z.ZodOptional<z.ZodString>;
164
+ productId: z.ZodOptional<z.ZodString>;
165
+ optionId: z.ZodOptional<z.ZodString>;
166
+ personId: z.ZodOptional<z.ZodString>;
167
+ organizationId: z.ZodOptional<z.ZodString>;
164
168
  limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
165
169
  offset: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
166
170
  }, z.core.$strip>;
171
+ export declare const bookingAggregatesQuerySchema: z.ZodObject<{
172
+ from: z.ZodOptional<z.ZodString>;
173
+ to: z.ZodOptional<z.ZodString>;
174
+ }, z.core.$strip>;
167
175
  export declare const convertProductSchema: z.ZodObject<{
168
176
  productId: z.ZodString;
169
177
  optionId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
178
+ slotId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
170
179
  bookingNumber: z.ZodString;
171
180
  personId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
172
181
  organizationId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
173
182
  internalNotes: z.ZodNullable<z.ZodOptional<z.ZodString>>;
174
183
  }, z.core.$strip>;
184
+ /**
185
+ * Admin pricing-preview request. Mirrors the storefront pricing session
186
+ * resolver input so the operator dialog sees the same numbers the customer
187
+ * would see for the same product + option + catalog.
188
+ */
189
+ export declare const pricingPreviewSchema: z.ZodObject<{
190
+ productId: z.ZodString;
191
+ optionId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
192
+ catalogId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
193
+ }, z.core.$strip>;
175
194
  export declare const updateBookingStatusSchema: z.ZodObject<{
176
195
  status: z.ZodEnum<{
177
196
  cancelled: "cancelled";
@@ -189,8 +208,8 @@ export declare const reserveBookingItemSchema: z.ZodObject<{
189
208
  description: z.ZodNullable<z.ZodOptional<z.ZodString>>;
190
209
  itemType: z.ZodDefault<z.ZodEnum<{
191
210
  service: "service";
192
- other: "other";
193
211
  unit: "unit";
212
+ other: "other";
194
213
  extra: "extra";
195
214
  fee: "fee";
196
215
  tax: "tax";
@@ -215,8 +234,8 @@ export declare const reserveBookingItemSchema: z.ZodObject<{
215
234
  sourceOfferId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
216
235
  availabilitySlotId: z.ZodString;
217
236
  allocationType: z.ZodDefault<z.ZodEnum<{
218
- resource: "resource";
219
237
  unit: "unit";
238
+ resource: "resource";
220
239
  pickup: "pickup";
221
240
  }>>;
222
241
  metadata: z.ZodNullable<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
@@ -264,8 +283,8 @@ export declare const reserveBookingSchema: z.ZodObject<{
264
283
  description: z.ZodNullable<z.ZodOptional<z.ZodString>>;
265
284
  itemType: z.ZodDefault<z.ZodEnum<{
266
285
  service: "service";
267
- other: "other";
268
286
  unit: "unit";
287
+ other: "other";
269
288
  extra: "extra";
270
289
  fee: "fee";
271
290
  tax: "tax";
@@ -290,8 +309,8 @@ export declare const reserveBookingSchema: z.ZodObject<{
290
309
  sourceOfferId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
291
310
  availabilitySlotId: z.ZodString;
292
311
  allocationType: z.ZodDefault<z.ZodEnum<{
293
- resource: "resource";
294
312
  unit: "unit";
313
+ resource: "resource";
295
314
  pickup: "pickup";
296
315
  }>>;
297
316
  metadata: z.ZodNullable<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
@@ -438,8 +457,8 @@ export declare const insertBookingItemSchema: z.ZodObject<{
438
457
  description: z.ZodNullable<z.ZodOptional<z.ZodString>>;
439
458
  itemType: z.ZodDefault<z.ZodEnum<{
440
459
  service: "service";
441
- other: "other";
442
460
  unit: "unit";
461
+ other: "other";
443
462
  extra: "extra";
444
463
  fee: "fee";
445
464
  tax: "tax";
@@ -480,8 +499,8 @@ export declare const updateBookingItemSchema: z.ZodObject<{
480
499
  description: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
481
500
  itemType: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
482
501
  service: "service";
483
- other: "other";
484
502
  unit: "unit";
503
+ other: "other";
485
504
  extra: "extra";
486
505
  fee: "fee";
487
506
  tax: "tax";
@@ -526,8 +545,8 @@ export declare const insertBookingAllocationSchema: z.ZodObject<{
526
545
  availabilitySlotId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
527
546
  quantity: z.ZodDefault<z.ZodNumber>;
528
547
  allocationType: z.ZodDefault<z.ZodEnum<{
529
- resource: "resource";
530
548
  unit: "unit";
549
+ resource: "resource";
531
550
  pickup: "pickup";
532
551
  }>>;
533
552
  status: z.ZodDefault<z.ZodEnum<{
@@ -552,8 +571,8 @@ export declare const updateBookingAllocationSchema: z.ZodObject<{
552
571
  availabilitySlotId: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
553
572
  quantity: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
554
573
  allocationType: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
555
- resource: "resource";
556
574
  unit: "unit";
575
+ resource: "resource";
557
576
  pickup: "pickup";
558
577
  }>>>;
559
578
  status: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}