@voyantjs/bookings 0.6.8 → 0.6.9

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.
Files changed (63) hide show
  1. package/README.md +2 -2
  2. package/dist/index.d.ts +7 -7
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +4 -4
  5. package/dist/pii.d.ts +10 -9
  6. package/dist/pii.d.ts.map +1 -1
  7. package/dist/pii.js +33 -33
  8. package/dist/routes-groups.d.ts +25 -5
  9. package/dist/routes-groups.d.ts.map +1 -1
  10. package/dist/routes-groups.js +3 -3
  11. package/dist/routes-public.d.ts +19 -21
  12. package/dist/routes-public.d.ts.map +1 -1
  13. package/dist/routes-public.js +1 -1
  14. package/dist/routes-shared.d.ts +1 -1
  15. package/dist/routes-shared.d.ts.map +1 -1
  16. package/dist/routes.d.ts +200 -187
  17. package/dist/routes.d.ts.map +1 -1
  18. package/dist/routes.js +65 -95
  19. package/dist/schema/travel-details.d.ts +27 -27
  20. package/dist/schema/travel-details.d.ts.map +1 -1
  21. package/dist/schema/travel-details.js +19 -14
  22. package/dist/schema-core.d.ts +194 -305
  23. package/dist/schema-core.d.ts.map +1 -1
  24. package/dist/schema-core.js +19 -10
  25. package/dist/schema-items.d.ts +15 -15
  26. package/dist/schema-items.d.ts.map +1 -1
  27. package/dist/schema-items.js +12 -12
  28. package/dist/schema-operations.d.ts +1 -1
  29. package/dist/schema-operations.js +3 -3
  30. package/dist/schema-relations.d.ts +26 -9
  31. package/dist/schema-relations.d.ts.map +1 -1
  32. package/dist/schema-relations.js +36 -21
  33. package/dist/schema-shared.d.ts +3 -2
  34. package/dist/schema-shared.d.ts.map +1 -1
  35. package/dist/schema-shared.js +4 -5
  36. package/dist/schema-staff.d.ts +267 -0
  37. package/dist/schema-staff.d.ts.map +1 -0
  38. package/dist/schema-staff.js +31 -0
  39. package/dist/schema.d.ts +1 -0
  40. package/dist/schema.d.ts.map +1 -1
  41. package/dist/schema.js +1 -0
  42. package/dist/service-groups.d.ts +3 -7
  43. package/dist/service-groups.d.ts.map +1 -1
  44. package/dist/service-groups.js +6 -10
  45. package/dist/service-public.d.ts +49 -55
  46. package/dist/service-public.d.ts.map +1 -1
  47. package/dist/service-public.js +106 -53
  48. package/dist/service.d.ts +227 -99
  49. package/dist/service.d.ts.map +1 -1
  50. package/dist/service.js +321 -108
  51. package/dist/transactions-ref.d.ts +930 -66
  52. package/dist/transactions-ref.d.ts.map +1 -1
  53. package/dist/transactions-ref.js +56 -2
  54. package/dist/validation-public.d.ts +29 -69
  55. package/dist/validation-public.d.ts.map +1 -1
  56. package/dist/validation-public.js +21 -20
  57. package/dist/validation-shared.d.ts +4 -5
  58. package/dist/validation-shared.d.ts.map +1 -1
  59. package/dist/validation-shared.js +2 -10
  60. package/dist/validation.d.ts +229 -44
  61. package/dist/validation.d.ts.map +1 -1
  62. package/dist/validation.js +84 -28
  63. package/package.json +6 -6
package/dist/service.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { and, asc, desc, eq, ilike, inArray, lte, ne, or, sql } from "drizzle-orm";
2
2
  import { availabilitySlotsRef } from "./availability-ref.js";
3
3
  import { bookingItemProductDetailsRef, bookingProductDetailsRef, optionUnitsRef, productDayServicesRef, productDaysRef, productOptionsRef, productsRef, productTicketSettingsRef, } from "./products-ref.js";
4
- import { bookingActivityLog, bookingAllocations, bookingDocuments, bookingFulfillments, bookingItemParticipants, bookingItems, bookingNotes, bookingParticipants, bookingRedemptionEvents, bookingSupplierStatuses, bookings, } from "./schema.js";
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
- import { bookingTransactionDetailsRef, offerItemParticipantsRef, offerItemsRef, offerParticipantsRef, offersRef, orderItemParticipantsRef, orderItemsRef, orderParticipantsRef, ordersRef, } from "./transactions-ref.js";
6
+ import { bookingTransactionDetailsRef, offerItemParticipantsRef, offerItemsRef, offerParticipantsRef, offerStaffAssignmentsRef, offersRef, orderItemParticipantsRef, orderItemsRef, orderParticipantsRef, orderStaffAssignmentsRef, ordersRef, } from "./transactions-ref.js";
7
7
  const travelerParticipantTypes = ["traveler", "occupant"];
8
8
  class BookingServiceError extends Error {
9
9
  code;
@@ -24,47 +24,31 @@ function toDateValueOrNull(value) {
24
24
  return null;
25
25
  return value instanceof Date ? value : new Date(value);
26
26
  }
27
- function toPassengerResponse(participant) {
27
+ function toTravelerResponse(participant) {
28
28
  return {
29
29
  id: participant.id,
30
30
  bookingId: participant.bookingId,
31
+ participantType: participant.participantType,
32
+ travelerCategory: participant.travelerCategory,
31
33
  firstName: participant.firstName,
32
34
  lastName: participant.lastName,
33
35
  email: participant.email,
34
36
  phone: participant.phone,
37
+ preferredLanguage: participant.preferredLanguage,
38
+ accessibilityNeeds: participant.accessibilityNeeds,
35
39
  specialRequests: participant.specialRequests,
36
- isLeadPassenger: participant.isPrimary,
40
+ isPrimary: participant.isPrimary,
41
+ notes: participant.notes,
37
42
  createdAt: participant.createdAt,
38
43
  updatedAt: participant.updatedAt,
39
44
  };
40
45
  }
41
- function toCreateParticipantFromPassenger(data) {
42
- return {
43
- participantType: "traveler",
44
- firstName: data.firstName,
45
- lastName: data.lastName,
46
- email: data.email ?? null,
47
- phone: data.phone ?? null,
48
- specialRequests: data.specialRequests ?? null,
49
- isPrimary: data.isLeadPassenger ?? false,
50
- };
51
- }
52
- function toUpdateParticipantFromPassenger(data) {
53
- return {
54
- firstName: data.firstName,
55
- lastName: data.lastName,
56
- email: data.email ?? null,
57
- phone: data.phone ?? null,
58
- specialRequests: data.specialRequests ?? null,
59
- isPrimary: data.isLeadPassenger ?? undefined,
60
- };
61
- }
62
- async function ensureParticipantFlags(db, bookingId, participantId, data) {
46
+ async function ensureParticipantFlags(db, bookingId, travelerId, data) {
63
47
  if (data.isPrimary) {
64
48
  await db
65
- .update(bookingParticipants)
49
+ .update(bookingTravelers)
66
50
  .set({ isPrimary: false, updatedAt: new Date() })
67
- .where(and(eq(bookingParticipants.bookingId, bookingId), ne(bookingParticipants.id, participantId)));
51
+ .where(and(eq(bookingTravelers.bookingId, bookingId), ne(bookingTravelers.id, travelerId)));
68
52
  }
69
53
  }
70
54
  async function ensureBookingScopedLinks(db, bookingId, data) {
@@ -78,18 +62,24 @@ async function ensureBookingScopedLinks(db, bookingId, data) {
78
62
  return { ok: false, reason: "booking_item_not_found" };
79
63
  }
80
64
  }
81
- if (data.participantId) {
82
- const [participant] = await db
83
- .select({ id: bookingParticipants.id })
84
- .from(bookingParticipants)
85
- .where(and(eq(bookingParticipants.id, data.participantId), eq(bookingParticipants.bookingId, bookingId)))
65
+ if (data.travelerId) {
66
+ const [traveler] = await db
67
+ .select({ id: bookingTravelers.id })
68
+ .from(bookingTravelers)
69
+ .where(and(eq(bookingTravelers.id, data.travelerId), eq(bookingTravelers.bookingId, bookingId)))
86
70
  .limit(1);
87
- if (!participant) {
88
- return { ok: false, reason: "participant_not_found" };
71
+ if (!traveler) {
72
+ return { ok: false, reason: "traveler_not_found" };
89
73
  }
90
74
  }
91
75
  return { ok: true };
92
76
  }
77
+ function isStaffParticipantType(participantType) {
78
+ return participantType === "staff";
79
+ }
80
+ function toStaffAssignmentRole(role) {
81
+ return role === "service_assignee" ? "service_assignee" : "other";
82
+ }
93
83
  function deriveBookingDateRange(items) {
94
84
  const dates = items
95
85
  .flatMap((item) => [item.serviceDate, item.startsAt?.toISOString().slice(0, 10) ?? null])
@@ -112,6 +102,23 @@ function deriveBookingPax(participants, items) {
112
102
  function getTransactionItemParticipantItemId(link) {
113
103
  return "offerItemId" in link ? link.offerItemId : link.orderItemId;
114
104
  }
105
+ function toStaffReservationParticipant(assignment, suffix) {
106
+ return {
107
+ id: `staff:${suffix}:${assignment.id}`,
108
+ personId: assignment.personId,
109
+ participantType: "staff",
110
+ travelerCategory: null,
111
+ firstName: assignment.firstName,
112
+ lastName: assignment.lastName,
113
+ email: assignment.email,
114
+ phone: assignment.phone,
115
+ preferredLanguage: assignment.preferredLanguage,
116
+ isPrimary: assignment.isPrimary,
117
+ notes: assignment.notes,
118
+ createdAt: new Date(),
119
+ updatedAt: new Date(),
120
+ };
121
+ }
115
122
  function mapDeliveryFormatToFulfillment(format) {
116
123
  switch (format) {
117
124
  case "pdf":
@@ -318,6 +325,16 @@ async function reserveBookingFromTransactionSource(db, source, data, userId) {
318
325
  personId: source.personId,
319
326
  organizationId: source.organizationId,
320
327
  sourceType: data.sourceType,
328
+ contactFirstName: data.contactFirstName ?? source.contactFirstName,
329
+ contactLastName: data.contactLastName ?? source.contactLastName,
330
+ contactEmail: data.contactEmail ?? source.contactEmail,
331
+ contactPhone: data.contactPhone ?? source.contactPhone,
332
+ contactPreferredLanguage: data.contactPreferredLanguage ?? source.contactPreferredLanguage,
333
+ contactCountry: data.contactCountry ?? source.contactCountry,
334
+ contactRegion: data.contactRegion ?? source.contactRegion,
335
+ contactCity: data.contactCity ?? source.contactCity,
336
+ contactAddressLine1: data.contactAddressLine1 ?? source.contactAddressLine1,
337
+ contactPostalCode: data.contactPostalCode ?? source.contactPostalCode,
321
338
  sellCurrency: source.currency,
322
339
  baseCurrency: source.baseCurrency,
323
340
  sellAmountCents: source.totalAmountCents,
@@ -333,10 +350,15 @@ async function reserveBookingFromTransactionSource(db, source, data, userId) {
333
350
  throw new BookingServiceError("booking_create_failed");
334
351
  }
335
352
  const participantMap = new Map();
353
+ const staffParticipantMap = new Map();
336
354
  if (data.includeParticipants) {
337
355
  for (const participant of source.participants) {
356
+ if (isStaffParticipantType(participant.participantType)) {
357
+ staffParticipantMap.set(participant.id, participant);
358
+ continue;
359
+ }
338
360
  const [createdParticipant] = await tx
339
- .insert(bookingParticipants)
361
+ .insert(bookingTravelers)
340
362
  .values({
341
363
  bookingId: booking.id,
342
364
  personId: participant.personId ?? null,
@@ -437,17 +459,78 @@ async function reserveBookingFromTransactionSource(db, source, data, userId) {
437
459
  continue;
438
460
  }
439
461
  const bookingItemId = bookingItemMap.get(sourceItemId);
440
- const participantId = participantMap.get(link.participantId);
441
- if (!bookingItemId || !participantId) {
462
+ const travelerId = participantMap.get(link.travelerId);
463
+ if (!bookingItemId || !travelerId) {
442
464
  continue;
443
465
  }
444
- await tx.insert(bookingItemParticipants).values({
466
+ await tx.insert(bookingItemTravelers).values({
445
467
  bookingItemId,
446
- participantId,
468
+ travelerId,
447
469
  role: link.role,
448
470
  isPrimary: link.isPrimary,
449
471
  });
450
472
  }
473
+ if (staffParticipantMap.size > 0) {
474
+ const linkedStaffAssignments = [];
475
+ const linkedStaffParticipantIds = new Set();
476
+ for (const link of source.itemParticipants) {
477
+ const staffParticipant = staffParticipantMap.get(link.travelerId);
478
+ if (!staffParticipant) {
479
+ continue;
480
+ }
481
+ const sourceItemId = getTransactionItemParticipantItemId(link);
482
+ if (!sourceItemId) {
483
+ continue;
484
+ }
485
+ const bookingItemId = bookingItemMap.get(sourceItemId);
486
+ if (!bookingItemId) {
487
+ continue;
488
+ }
489
+ linkedStaffParticipantIds.add(staffParticipant.id);
490
+ linkedStaffAssignments.push({
491
+ bookingId: booking.id,
492
+ bookingItemId,
493
+ personId: staffParticipant.personId ?? null,
494
+ role: toStaffAssignmentRole(link.role),
495
+ firstName: staffParticipant.firstName,
496
+ lastName: staffParticipant.lastName,
497
+ email: staffParticipant.email ?? null,
498
+ phone: staffParticipant.phone ?? null,
499
+ preferredLanguage: staffParticipant.preferredLanguage ?? null,
500
+ isPrimary: link.isPrimary || staffParticipant.isPrimary,
501
+ notes: staffParticipant.notes ?? null,
502
+ metadata: {
503
+ sourceParticipantId: staffParticipant.id,
504
+ sourceItemId,
505
+ sourceRole: link.role,
506
+ },
507
+ });
508
+ }
509
+ for (const staffParticipant of staffParticipantMap.values()) {
510
+ if (linkedStaffParticipantIds.has(staffParticipant.id)) {
511
+ continue;
512
+ }
513
+ linkedStaffAssignments.push({
514
+ bookingId: booking.id,
515
+ bookingItemId: null,
516
+ personId: staffParticipant.personId ?? null,
517
+ role: "service_assignee",
518
+ firstName: staffParticipant.firstName,
519
+ lastName: staffParticipant.lastName,
520
+ email: staffParticipant.email ?? null,
521
+ phone: staffParticipant.phone ?? null,
522
+ preferredLanguage: staffParticipant.preferredLanguage ?? null,
523
+ isPrimary: staffParticipant.isPrimary,
524
+ notes: staffParticipant.notes ?? null,
525
+ metadata: {
526
+ sourceParticipantId: staffParticipant.id,
527
+ },
528
+ });
529
+ }
530
+ if (linkedStaffAssignments.length > 0) {
531
+ await tx.insert(bookingStaffAssignments).values(linkedStaffAssignments);
532
+ }
533
+ }
451
534
  await tx
452
535
  .insert(bookingTransactionDetailsRef)
453
536
  .values({
@@ -616,13 +699,13 @@ async function autoIssueFulfillmentsForBooking(db, bookingId, userId) {
616
699
  const settingsByProductId = new Map(settings.map((setting) => [setting.productId, setting]));
617
700
  const travelerParticipants = await db
618
701
  .select()
619
- .from(bookingParticipants)
620
- .where(and(eq(bookingParticipants.bookingId, bookingId), or(eq(bookingParticipants.participantType, "traveler"), eq(bookingParticipants.participantType, "occupant"))))
621
- .orderBy(desc(bookingParticipants.isPrimary), asc(bookingParticipants.createdAt));
702
+ .from(bookingTravelers)
703
+ .where(and(eq(bookingTravelers.bookingId, bookingId), or(eq(bookingTravelers.participantType, "traveler"), eq(bookingTravelers.participantType, "occupant"))))
704
+ .orderBy(desc(bookingTravelers.isPrimary), asc(bookingTravelers.createdAt));
622
705
  const participantLinks = await db
623
706
  .select()
624
- .from(bookingItemParticipants)
625
- .where(sql `${bookingItemParticipants.bookingItemId} IN (
707
+ .from(bookingItemTravelers)
708
+ .where(sql `${bookingItemTravelers.bookingItemId} IN (
626
709
  SELECT ${bookingItems.id}
627
710
  FROM ${bookingItems}
628
711
  WHERE ${bookingItems.bookingId} = ${bookingId}
@@ -660,7 +743,7 @@ async function autoIssueFulfillmentsForBooking(db, bookingId, userId) {
660
743
  fulfillmentsToInsert.push({
661
744
  bookingId,
662
745
  bookingItemId: item.id,
663
- participantId: null,
746
+ travelerId: null,
664
747
  fulfillmentType: delivery.fulfillmentType,
665
748
  deliveryChannel: delivery.deliveryChannel,
666
749
  status: "issued",
@@ -673,7 +756,7 @@ async function autoIssueFulfillmentsForBooking(db, bookingId, userId) {
673
756
  fulfillmentsToInsert.push({
674
757
  bookingId,
675
758
  bookingItemId: item.id,
676
- participantId: null,
759
+ travelerId: null,
677
760
  fulfillmentType: delivery.fulfillmentType,
678
761
  deliveryChannel: delivery.deliveryChannel,
679
762
  status: "issued",
@@ -684,20 +767,20 @@ async function autoIssueFulfillmentsForBooking(db, bookingId, userId) {
684
767
  }
685
768
  const linkedParticipants = participantLinksByItemId
686
769
  .get(item.id)
687
- ?.map((link) => travelerParticipants.find((participant) => participant.id === link.participantId))
770
+ ?.map((link) => travelerParticipants.find((participant) => participant.id === link.travelerId))
688
771
  .filter((participant) => Boolean(participant)) ?? [];
689
772
  const participantsForItem = linkedParticipants.length > 0 ? linkedParticipants : travelerParticipants;
690
773
  for (const participant of participantsForItem) {
691
774
  fulfillmentsToInsert.push({
692
775
  bookingId,
693
776
  bookingItemId: item.id,
694
- participantId: participant.id,
777
+ travelerId: participant.id,
695
778
  fulfillmentType: delivery.fulfillmentType,
696
779
  deliveryChannel: delivery.deliveryChannel,
697
780
  status: "issued",
698
781
  payload: {
699
782
  ...payloadBase,
700
- participantId: participant.id,
783
+ travelerId: participant.id,
701
784
  scope: "participant",
702
785
  },
703
786
  issuedAt: now,
@@ -892,7 +975,7 @@ export const bookingsService = {
892
975
  if (!offer) {
893
976
  return { status: "not_found" };
894
977
  }
895
- const [participants, items, itemParticipants] = await Promise.all([
978
+ const [participants, items, itemParticipants, staffAssignments] = await Promise.all([
896
979
  db
897
980
  .select()
898
981
  .from(offerParticipantsRef)
@@ -912,7 +995,31 @@ export const bookingsService = {
912
995
  WHERE ${offerItemsRef.offerId} = ${offerId}
913
996
  )`)
914
997
  .orderBy(asc(offerItemParticipantsRef.createdAt)),
998
+ db
999
+ .select()
1000
+ .from(offerStaffAssignmentsRef)
1001
+ .where(eq(offerStaffAssignmentsRef.offerId, offerId))
1002
+ .orderBy(asc(offerStaffAssignmentsRef.createdAt)),
915
1003
  ]);
1004
+ const reservationParticipants = [...participants];
1005
+ const reservationItemParticipants = itemParticipants.map((link) => ({
1006
+ travelerId: link.travelerId,
1007
+ role: link.role,
1008
+ isPrimary: link.isPrimary,
1009
+ offerItemId: link.offerItemId,
1010
+ }));
1011
+ for (const assignment of staffAssignments) {
1012
+ const participant = toStaffReservationParticipant(assignment, "offer");
1013
+ reservationParticipants.push(participant);
1014
+ if (assignment.offerItemId) {
1015
+ reservationItemParticipants.push({
1016
+ travelerId: participant.id,
1017
+ role: assignment.role,
1018
+ isPrimary: assignment.isPrimary,
1019
+ offerItemId: assignment.offerItemId,
1020
+ });
1021
+ }
1022
+ }
916
1023
  return reserveBookingFromTransactionSource(db, {
917
1024
  kind: "offer",
918
1025
  sourceId: offerId,
@@ -920,14 +1027,24 @@ export const bookingsService = {
920
1027
  orderId: null,
921
1028
  personId: offer.personId ?? null,
922
1029
  organizationId: offer.organizationId ?? null,
1030
+ contactFirstName: offer.contactFirstName ?? null,
1031
+ contactLastName: offer.contactLastName ?? null,
1032
+ contactEmail: offer.contactEmail ?? null,
1033
+ contactPhone: offer.contactPhone ?? null,
1034
+ contactPreferredLanguage: offer.contactPreferredLanguage ?? null,
1035
+ contactCountry: offer.contactCountry ?? null,
1036
+ contactRegion: offer.contactRegion ?? null,
1037
+ contactCity: offer.contactCity ?? null,
1038
+ contactAddressLine1: offer.contactAddressLine1 ?? null,
1039
+ contactPostalCode: offer.contactPostalCode ?? null,
923
1040
  currency: offer.currency,
924
1041
  baseCurrency: offer.baseCurrency ?? null,
925
1042
  totalAmountCents: offer.totalAmountCents ?? null,
926
1043
  costAmountCents: offer.costAmountCents ?? null,
927
1044
  notes: offer.notes ?? null,
928
- participants,
1045
+ participants: reservationParticipants,
929
1046
  items,
930
- itemParticipants,
1047
+ itemParticipants: reservationItemParticipants,
931
1048
  }, data, userId);
932
1049
  },
933
1050
  async reserveBookingFromOrder(db, orderId, data, userId) {
@@ -935,7 +1052,7 @@ export const bookingsService = {
935
1052
  if (!order) {
936
1053
  return { status: "not_found" };
937
1054
  }
938
- const [participants, items, itemParticipants] = await Promise.all([
1055
+ const [participants, items, itemParticipants, staffAssignments] = await Promise.all([
939
1056
  db
940
1057
  .select()
941
1058
  .from(orderParticipantsRef)
@@ -955,7 +1072,31 @@ export const bookingsService = {
955
1072
  WHERE ${orderItemsRef.orderId} = ${orderId}
956
1073
  )`)
957
1074
  .orderBy(asc(orderItemParticipantsRef.createdAt)),
1075
+ db
1076
+ .select()
1077
+ .from(orderStaffAssignmentsRef)
1078
+ .where(eq(orderStaffAssignmentsRef.orderId, orderId))
1079
+ .orderBy(asc(orderStaffAssignmentsRef.createdAt)),
958
1080
  ]);
1081
+ const reservationParticipants = [...participants];
1082
+ const reservationItemParticipants = itemParticipants.map((link) => ({
1083
+ travelerId: link.travelerId,
1084
+ role: link.role,
1085
+ isPrimary: link.isPrimary,
1086
+ orderItemId: link.orderItemId,
1087
+ }));
1088
+ for (const assignment of staffAssignments) {
1089
+ const participant = toStaffReservationParticipant(assignment, "order");
1090
+ reservationParticipants.push(participant);
1091
+ if (assignment.orderItemId) {
1092
+ reservationItemParticipants.push({
1093
+ travelerId: participant.id,
1094
+ role: assignment.role,
1095
+ isPrimary: assignment.isPrimary,
1096
+ orderItemId: assignment.orderItemId,
1097
+ });
1098
+ }
1099
+ }
959
1100
  return reserveBookingFromTransactionSource(db, {
960
1101
  kind: "order",
961
1102
  sourceId: orderId,
@@ -963,14 +1104,24 @@ export const bookingsService = {
963
1104
  orderId: order.id,
964
1105
  personId: order.personId ?? null,
965
1106
  organizationId: order.organizationId ?? null,
1107
+ contactFirstName: order.contactFirstName ?? null,
1108
+ contactLastName: order.contactLastName ?? null,
1109
+ contactEmail: order.contactEmail ?? null,
1110
+ contactPhone: order.contactPhone ?? null,
1111
+ contactPreferredLanguage: order.contactPreferredLanguage ?? null,
1112
+ contactCountry: order.contactCountry ?? null,
1113
+ contactRegion: order.contactRegion ?? null,
1114
+ contactCity: order.contactCity ?? null,
1115
+ contactAddressLine1: order.contactAddressLine1 ?? null,
1116
+ contactPostalCode: order.contactPostalCode ?? null,
966
1117
  currency: order.currency,
967
1118
  baseCurrency: order.baseCurrency ?? null,
968
1119
  totalAmountCents: order.totalAmountCents ?? null,
969
1120
  costAmountCents: order.costAmountCents ?? null,
970
1121
  notes: order.notes ?? null,
971
- participants,
1122
+ participants: reservationParticipants,
972
1123
  items,
973
- itemParticipants,
1124
+ itemParticipants: reservationItemParticipants,
974
1125
  }, data, userId);
975
1126
  },
976
1127
  async reserveBooking(db, data, userId) {
@@ -987,6 +1138,16 @@ export const bookingsService = {
987
1138
  sourceType: data.sourceType,
988
1139
  externalBookingRef: data.externalBookingRef ?? null,
989
1140
  communicationLanguage: data.communicationLanguage ?? null,
1141
+ contactFirstName: data.contactFirstName ?? null,
1142
+ contactLastName: data.contactLastName ?? null,
1143
+ contactEmail: data.contactEmail ?? null,
1144
+ contactPhone: data.contactPhone ?? null,
1145
+ contactPreferredLanguage: data.contactPreferredLanguage ?? null,
1146
+ contactCountry: data.contactCountry ?? null,
1147
+ contactRegion: data.contactRegion ?? null,
1148
+ contactCity: data.contactCity ?? null,
1149
+ contactAddressLine1: data.contactAddressLine1 ?? null,
1150
+ contactPostalCode: data.contactPostalCode ?? null,
990
1151
  sellCurrency: data.sellCurrency,
991
1152
  baseCurrency: data.baseCurrency ?? null,
992
1153
  sellAmountCents: data.sellAmountCents ?? null,
@@ -1097,6 +1258,16 @@ export const bookingsService = {
1097
1258
  .insert(bookings)
1098
1259
  .values({
1099
1260
  ...data,
1261
+ contactFirstName: data.contactFirstName ?? null,
1262
+ contactLastName: data.contactLastName ?? null,
1263
+ contactEmail: data.contactEmail ?? null,
1264
+ contactPhone: data.contactPhone ?? null,
1265
+ contactPreferredLanguage: data.contactPreferredLanguage ?? null,
1266
+ contactCountry: data.contactCountry ?? null,
1267
+ contactRegion: data.contactRegion ?? null,
1268
+ contactCity: data.contactCity ?? null,
1269
+ contactAddressLine1: data.contactAddressLine1 ?? null,
1270
+ contactPostalCode: data.contactPostalCode ?? null,
1100
1271
  holdExpiresAt: toTimestamp(data.holdExpiresAt),
1101
1272
  confirmedAt: toTimestamp(data.confirmedAt),
1102
1273
  expiredAt: toTimestamp(data.expiredAt),
@@ -1122,6 +1293,18 @@ export const bookingsService = {
1122
1293
  .update(bookings)
1123
1294
  .set({
1124
1295
  ...data,
1296
+ contactFirstName: data.contactFirstName === undefined ? undefined : (data.contactFirstName ?? null),
1297
+ contactLastName: data.contactLastName === undefined ? undefined : (data.contactLastName ?? null),
1298
+ contactEmail: data.contactEmail === undefined ? undefined : (data.contactEmail ?? null),
1299
+ contactPhone: data.contactPhone === undefined ? undefined : (data.contactPhone ?? null),
1300
+ contactPreferredLanguage: data.contactPreferredLanguage === undefined
1301
+ ? undefined
1302
+ : (data.contactPreferredLanguage ?? null),
1303
+ contactCountry: data.contactCountry === undefined ? undefined : (data.contactCountry ?? null),
1304
+ contactRegion: data.contactRegion === undefined ? undefined : (data.contactRegion ?? null),
1305
+ contactCity: data.contactCity === undefined ? undefined : (data.contactCity ?? null),
1306
+ contactAddressLine1: data.contactAddressLine1 === undefined ? undefined : (data.contactAddressLine1 ?? null),
1307
+ contactPostalCode: data.contactPostalCode === undefined ? undefined : (data.contactPostalCode ?? null),
1125
1308
  holdExpiresAt: data.holdExpiresAt === undefined ? undefined : toTimestamp(data.holdExpiresAt),
1126
1309
  confirmedAt: data.confirmedAt === undefined ? undefined : toTimestamp(data.confirmedAt),
1127
1310
  expiredAt: data.expiredAt === undefined ? undefined : toTimestamp(data.expiredAt),
@@ -1467,22 +1650,22 @@ export const bookingsService = {
1467
1650
  throw error;
1468
1651
  }
1469
1652
  },
1470
- listParticipants(db, bookingId) {
1653
+ listTravelerRecords(db, bookingId) {
1471
1654
  return db
1472
1655
  .select()
1473
- .from(bookingParticipants)
1474
- .where(eq(bookingParticipants.bookingId, bookingId))
1475
- .orderBy(desc(bookingParticipants.isPrimary), asc(bookingParticipants.createdAt));
1656
+ .from(bookingTravelers)
1657
+ .where(eq(bookingTravelers.bookingId, bookingId))
1658
+ .orderBy(desc(bookingTravelers.isPrimary), asc(bookingTravelers.createdAt));
1476
1659
  },
1477
- async getParticipantById(db, bookingId, participantId) {
1660
+ async getTravelerRecordById(db, bookingId, travelerId) {
1478
1661
  const [row] = await db
1479
1662
  .select()
1480
- .from(bookingParticipants)
1481
- .where(and(eq(bookingParticipants.id, participantId), eq(bookingParticipants.bookingId, bookingId)))
1663
+ .from(bookingTravelers)
1664
+ .where(and(eq(bookingTravelers.id, travelerId), eq(bookingTravelers.bookingId, bookingId)))
1482
1665
  .limit(1);
1483
1666
  return row ?? null;
1484
1667
  },
1485
- async createParticipant(db, bookingId, data, userId) {
1668
+ async createTravelerRecord(db, bookingId, data, userId) {
1486
1669
  const [booking] = await db
1487
1670
  .select({ id: bookings.id })
1488
1671
  .from(bookings)
@@ -1492,7 +1675,7 @@ export const bookingsService = {
1492
1675
  return null;
1493
1676
  }
1494
1677
  const [row] = await db
1495
- .insert(bookingParticipants)
1678
+ .insert(bookingTravelers)
1496
1679
  .values({
1497
1680
  bookingId,
1498
1681
  personId: data.personId ?? null,
@@ -1518,15 +1701,15 @@ export const bookingsService = {
1518
1701
  actorId: userId ?? "system",
1519
1702
  activityType: "passenger_update",
1520
1703
  description: `Participant ${data.firstName} ${data.lastName} added`,
1521
- metadata: { participantId: row.id, participantType: data.participantType },
1704
+ metadata: { travelerId: row.id, participantType: data.participantType },
1522
1705
  });
1523
1706
  return row;
1524
1707
  },
1525
- async updateParticipant(db, participantId, data) {
1708
+ async updateTravelerRecord(db, travelerId, data) {
1526
1709
  const [row] = await db
1527
- .update(bookingParticipants)
1710
+ .update(bookingTravelers)
1528
1711
  .set({ ...data, updatedAt: new Date() })
1529
- .where(eq(bookingParticipants.id, participantId))
1712
+ .where(eq(bookingTravelers.id, travelerId))
1530
1713
  .returning();
1531
1714
  if (!row) {
1532
1715
  return null;
@@ -1534,31 +1717,54 @@ export const bookingsService = {
1534
1717
  await ensureParticipantFlags(db, row.bookingId, row.id, data);
1535
1718
  return row;
1536
1719
  },
1537
- async deleteParticipant(db, participantId) {
1720
+ async deleteTravelerRecord(db, travelerId) {
1538
1721
  const [row] = await db
1539
- .delete(bookingParticipants)
1540
- .where(eq(bookingParticipants.id, participantId))
1541
- .returning({ id: bookingParticipants.id });
1722
+ .delete(bookingTravelers)
1723
+ .where(eq(bookingTravelers.id, travelerId))
1724
+ .returning({ id: bookingTravelers.id });
1542
1725
  return row ?? null;
1543
1726
  },
1544
- listPassengers(db, bookingId) {
1727
+ listTravelers(db, bookingId) {
1545
1728
  return db
1546
1729
  .select()
1547
- .from(bookingParticipants)
1548
- .where(and(eq(bookingParticipants.bookingId, bookingId), or(...travelerParticipantTypes.map((type) => eq(bookingParticipants.participantType, type)))))
1549
- .orderBy(asc(bookingParticipants.createdAt))
1550
- .then((rows) => rows.map(toPassengerResponse));
1730
+ .from(bookingTravelers)
1731
+ .where(and(eq(bookingTravelers.bookingId, bookingId), or(...travelerParticipantTypes.map((type) => eq(bookingTravelers.participantType, type)))))
1732
+ .orderBy(asc(bookingTravelers.createdAt))
1733
+ .then((rows) => rows.map(toTravelerResponse));
1551
1734
  },
1552
- async createPassenger(db, bookingId, data, userId) {
1553
- const row = await this.createParticipant(db, bookingId, toCreateParticipantFromPassenger(data), userId);
1554
- return row ? toPassengerResponse(row) : null;
1735
+ async createTraveler(db, bookingId, data, userId) {
1736
+ const row = await this.createTravelerRecord(db, bookingId, {
1737
+ participantType: "traveler",
1738
+ travelerCategory: data.travelerCategory ?? null,
1739
+ firstName: data.firstName,
1740
+ lastName: data.lastName,
1741
+ email: data.email ?? null,
1742
+ phone: data.phone ?? null,
1743
+ preferredLanguage: data.preferredLanguage ?? null,
1744
+ accessibilityNeeds: data.accessibilityNeeds ?? null,
1745
+ specialRequests: data.specialRequests ?? null,
1746
+ isPrimary: data.isPrimary ?? false,
1747
+ notes: data.notes ?? null,
1748
+ }, userId);
1749
+ return row ? toTravelerResponse(row) : null;
1555
1750
  },
1556
- async updatePassenger(db, passengerId, data) {
1557
- const row = await this.updateParticipant(db, passengerId, toUpdateParticipantFromPassenger(data));
1558
- return row ? toPassengerResponse(row) : null;
1751
+ async updateTraveler(db, travelerId, data) {
1752
+ const row = await this.updateTravelerRecord(db, travelerId, {
1753
+ firstName: data.firstName,
1754
+ lastName: data.lastName,
1755
+ email: data.email ?? null,
1756
+ phone: data.phone ?? null,
1757
+ preferredLanguage: data.preferredLanguage ?? null,
1758
+ accessibilityNeeds: data.accessibilityNeeds ?? null,
1759
+ specialRequests: data.specialRequests ?? null,
1760
+ travelerCategory: data.travelerCategory ?? null,
1761
+ isPrimary: data.isPrimary ?? undefined,
1762
+ notes: data.notes ?? null,
1763
+ });
1764
+ return row ? toTravelerResponse(row) : null;
1559
1765
  },
1560
- async deletePassenger(db, passengerId) {
1561
- return this.deleteParticipant(db, passengerId);
1766
+ async deleteTraveler(db, travelerId) {
1767
+ return this.deleteTravelerRecord(db, travelerId);
1562
1768
  },
1563
1769
  listItems(db, bookingId) {
1564
1770
  return db
@@ -1639,9 +1845,9 @@ export const bookingsService = {
1639
1845
  listItemParticipants(db, itemId) {
1640
1846
  return db
1641
1847
  .select()
1642
- .from(bookingItemParticipants)
1643
- .where(eq(bookingItemParticipants.bookingItemId, itemId))
1644
- .orderBy(desc(bookingItemParticipants.isPrimary), asc(bookingItemParticipants.createdAt));
1848
+ .from(bookingItemTravelers)
1849
+ .where(eq(bookingItemTravelers.bookingItemId, itemId))
1850
+ .orderBy(desc(bookingItemTravelers.isPrimary), asc(bookingItemTravelers.createdAt));
1645
1851
  },
1646
1852
  async addItemParticipant(db, itemId, data) {
1647
1853
  const [item] = await db
@@ -1652,25 +1858,25 @@ export const bookingsService = {
1652
1858
  if (!item) {
1653
1859
  return null;
1654
1860
  }
1655
- const [participant] = await db
1656
- .select({ id: bookingParticipants.id })
1657
- .from(bookingParticipants)
1658
- .where(eq(bookingParticipants.id, data.participantId))
1861
+ const [traveler] = await db
1862
+ .select({ id: bookingTravelers.id })
1863
+ .from(bookingTravelers)
1864
+ .where(eq(bookingTravelers.id, data.travelerId))
1659
1865
  .limit(1);
1660
- if (!participant) {
1866
+ if (!traveler) {
1661
1867
  return null;
1662
1868
  }
1663
1869
  if (data.isPrimary) {
1664
1870
  await db
1665
- .update(bookingItemParticipants)
1871
+ .update(bookingItemTravelers)
1666
1872
  .set({ isPrimary: false })
1667
- .where(eq(bookingItemParticipants.bookingItemId, itemId));
1873
+ .where(eq(bookingItemTravelers.bookingItemId, itemId));
1668
1874
  }
1669
1875
  const [row] = await db
1670
- .insert(bookingItemParticipants)
1876
+ .insert(bookingItemTravelers)
1671
1877
  .values({
1672
1878
  bookingItemId: itemId,
1673
- participantId: data.participantId,
1879
+ travelerId: data.travelerId,
1674
1880
  role: data.role,
1675
1881
  isPrimary: data.isPrimary ?? false,
1676
1882
  })
@@ -1679,9 +1885,9 @@ export const bookingsService = {
1679
1885
  },
1680
1886
  async removeItemParticipant(db, linkId) {
1681
1887
  const [row] = await db
1682
- .delete(bookingItemParticipants)
1683
- .where(eq(bookingItemParticipants.id, linkId))
1684
- .returning({ id: bookingItemParticipants.id });
1888
+ .delete(bookingItemTravelers)
1889
+ .where(eq(bookingItemTravelers.id, linkId))
1890
+ .returning({ id: bookingItemTravelers.id });
1685
1891
  return row ?? null;
1686
1892
  },
1687
1893
  listSupplierStatuses(db, bookingId) {
@@ -1789,7 +1995,7 @@ export const bookingsService = {
1789
1995
  .values({
1790
1996
  bookingId,
1791
1997
  bookingItemId: data.bookingItemId ?? null,
1792
- participantId: data.participantId ?? null,
1998
+ travelerId: data.travelerId ?? null,
1793
1999
  fulfillmentType: data.fulfillmentType,
1794
2000
  deliveryChannel: data.deliveryChannel,
1795
2001
  status,
@@ -1807,7 +2013,7 @@ export const bookingsService = {
1807
2013
  metadata: {
1808
2014
  fulfillmentId: row?.id ?? null,
1809
2015
  bookingItemId: data.bookingItemId ?? null,
1810
- participantId: data.participantId ?? null,
2016
+ travelerId: data.travelerId ?? null,
1811
2017
  status,
1812
2018
  },
1813
2019
  });
@@ -1831,7 +2037,7 @@ export const bookingsService = {
1831
2037
  .update(bookingFulfillments)
1832
2038
  .set({
1833
2039
  bookingItemId: data.bookingItemId === undefined ? undefined : (data.bookingItemId ?? null),
1834
- participantId: data.participantId === undefined ? undefined : (data.participantId ?? null),
2040
+ travelerId: data.travelerId === undefined ? undefined : (data.travelerId ?? null),
1835
2041
  fulfillmentType: data.fulfillmentType,
1836
2042
  deliveryChannel: data.deliveryChannel,
1837
2043
  status: nextStatus,
@@ -1860,7 +2066,7 @@ export const bookingsService = {
1860
2066
  metadata: {
1861
2067
  fulfillmentId,
1862
2068
  bookingItemId: row.bookingItemId,
1863
- participantId: row.participantId,
2069
+ travelerId: row.travelerId,
1864
2070
  status: row.status,
1865
2071
  },
1866
2072
  });
@@ -1897,7 +2103,7 @@ export const bookingsService = {
1897
2103
  .values({
1898
2104
  bookingId,
1899
2105
  bookingItemId: data.bookingItemId ?? null,
1900
- participantId: data.participantId ?? null,
2106
+ travelerId: data.travelerId ?? null,
1901
2107
  redeemedAt,
1902
2108
  redeemedBy: data.redeemedBy ?? userId ?? null,
1903
2109
  location: data.location ?? null,
@@ -1938,7 +2144,7 @@ export const bookingsService = {
1938
2144
  metadata: {
1939
2145
  redemptionEventId: event?.id ?? null,
1940
2146
  bookingItemId: data.bookingItemId ?? null,
1941
- participantId: data.participantId ?? null,
2147
+ travelerId: data.travelerId ?? null,
1942
2148
  redeemedAt: redeemedAt.toISOString(),
1943
2149
  method: data.method,
1944
2150
  },
@@ -1986,6 +2192,13 @@ export const bookingsService = {
1986
2192
  });
1987
2193
  return row;
1988
2194
  },
2195
+ async deleteNote(db, noteId) {
2196
+ const [row] = await db
2197
+ .delete(bookingNotes)
2198
+ .where(eq(bookingNotes.id, noteId))
2199
+ .returning({ id: bookingNotes.id });
2200
+ return row ?? null;
2201
+ },
1989
2202
  listDocuments(db, bookingId) {
1990
2203
  return db
1991
2204
  .select()
@@ -2006,7 +2219,7 @@ export const bookingsService = {
2006
2219
  .insert(bookingDocuments)
2007
2220
  .values({
2008
2221
  bookingId,
2009
- participantId: data.participantId ?? data.passengerId ?? null,
2222
+ travelerId: data.travelerId ?? null,
2010
2223
  type: data.type,
2011
2224
  fileName: data.fileName,
2012
2225
  fileUrl: data.fileUrl,