@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/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/products-ref.d.ts +93 -1
- package/dist/products-ref.d.ts.map +1 -1
- package/dist/products-ref.js +12 -1
- package/dist/routes-public.d.ts +5 -5
- package/dist/routes-shared.d.ts +2 -1
- package/dist/routes-shared.d.ts.map +1 -1
- package/dist/routes.d.ts +88 -6
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +25 -8
- package/dist/schema-items.d.ts +2 -2
- package/dist/service-public.d.ts +67 -14
- package/dist/service-public.d.ts.map +1 -1
- package/dist/service-public.js +13 -1
- package/dist/service.d.ts +108 -13
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +209 -22
- package/dist/validation-public.d.ts +11 -11
- package/dist/validation-shared.d.ts +2 -2
- package/dist/validation.d.ts +27 -8
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +19 -0
- package/package.json +6 -6
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
|
-
.
|
|
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
|
-
|
|
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
|
|
844
|
-
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: {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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<{
|
package/dist/validation.d.ts
CHANGED
|
@@ -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<{
|
package/dist/validation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA2DvB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAoB,CAAA;AACpD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA8B,CAAA;AAE9D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAW5B,CAAA;AAEJ,eAAO,MAAM,sBAAsB
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}
|