@voyant-travel/cruises 0.118.2
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/LICENSE +201 -0
- package/README.md +50 -0
- package/dist/adapters/connect-compat.d.ts +20 -0
- package/dist/adapters/connect-compat.d.ts.map +1 -0
- package/dist/adapters/connect-compat.js +71 -0
- package/dist/adapters/contract-fixture.d.ts +32 -0
- package/dist/adapters/contract-fixture.d.ts.map +1 -0
- package/dist/adapters/contract-fixture.js +152 -0
- package/dist/adapters/index.d.ts +331 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +16 -0
- package/dist/adapters/memoize.d.ts +28 -0
- package/dist/adapters/memoize.d.ts.map +1 -0
- package/dist/adapters/memoize.js +131 -0
- package/dist/adapters/mock.d.ts +44 -0
- package/dist/adapters/mock.d.ts.map +1 -0
- package/dist/adapters/mock.js +192 -0
- package/dist/adapters/registry.d.ts +26 -0
- package/dist/adapters/registry.d.ts.map +1 -0
- package/dist/adapters/registry.js +42 -0
- package/dist/adapters/source-adapter-shim.d.ts +80 -0
- package/dist/adapters/source-adapter-shim.d.ts.map +1 -0
- package/dist/adapters/source-adapter-shim.js +390 -0
- package/dist/booking-engine/handler.d.ts +108 -0
- package/dist/booking-engine/handler.d.ts.map +1 -0
- package/dist/booking-engine/handler.js +225 -0
- package/dist/booking-engine/index.d.ts +9 -0
- package/dist/booking-engine/index.d.ts.map +1 -0
- package/dist/booking-engine/index.js +8 -0
- package/dist/booking-extension.d.ts +1179 -0
- package/dist/booking-extension.d.ts.map +1 -0
- package/dist/booking-extension.js +342 -0
- package/dist/cabin-features.d.ts +8 -0
- package/dist/cabin-features.d.ts.map +1 -0
- package/dist/cabin-features.js +7 -0
- package/dist/catalog-policy-cabins.d.ts +18 -0
- package/dist/catalog-policy-cabins.d.ts.map +1 -0
- package/dist/catalog-policy-cabins.js +96 -0
- package/dist/catalog-policy-core.d.ts +3 -0
- package/dist/catalog-policy-core.d.ts.map +1 -0
- package/dist/catalog-policy-core.js +247 -0
- package/dist/catalog-policy-structure.d.ts +3 -0
- package/dist/catalog-policy-structure.d.ts.map +1 -0
- package/dist/catalog-policy-structure.js +387 -0
- package/dist/catalog-policy.d.ts +15 -0
- package/dist/catalog-policy.d.ts.map +1 -0
- package/dist/catalog-policy.js +19 -0
- package/dist/content-shape.d.ts +5 -0
- package/dist/content-shape.d.ts.map +1 -0
- package/dist/content-shape.js +13 -0
- package/dist/draft-shape.d.ts +59 -0
- package/dist/draft-shape.d.ts.map +1 -0
- package/dist/draft-shape.js +98 -0
- package/dist/events.d.ts +21 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +21 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/lib/key.d.ts +41 -0
- package/dist/lib/key.d.ts.map +1 -0
- package/dist/lib/key.js +100 -0
- package/dist/routes-booking-payloads.d.ts +133 -0
- package/dist/routes-booking-payloads.d.ts.map +1 -0
- package/dist/routes-booking-payloads.js +142 -0
- package/dist/routes-content.d.ts +53 -0
- package/dist/routes-content.d.ts.map +1 -0
- package/dist/routes-content.js +158 -0
- package/dist/routes-core.d.ts +4 -0
- package/dist/routes-core.d.ts.map +1 -0
- package/dist/routes-core.js +68 -0
- package/dist/routes-detail.d.ts +4 -0
- package/dist/routes-detail.d.ts.map +1 -0
- package/dist/routes-detail.js +261 -0
- package/dist/routes-env.d.ts +13 -0
- package/dist/routes-env.d.ts.map +1 -0
- package/dist/routes-env.js +1 -0
- package/dist/routes-keying.d.ts +28 -0
- package/dist/routes-keying.d.ts.map +1 -0
- package/dist/routes-keying.js +70 -0
- package/dist/routes-public.d.ts +911 -0
- package/dist/routes-public.d.ts.map +1 -0
- package/dist/routes-public.js +252 -0
- package/dist/routes-sailings-prices.d.ts +4 -0
- package/dist/routes-sailings-prices.d.ts.map +1 -0
- package/dist/routes-sailings-prices.js +278 -0
- package/dist/routes-search-index.d.ts +4 -0
- package/dist/routes-search-index.d.ts.map +1 -0
- package/dist/routes-search-index.js +25 -0
- package/dist/routes-ships.d.ts +4 -0
- package/dist/routes-ships.d.ts.map +1 -0
- package/dist/routes-ships.js +147 -0
- package/dist/routes-voyage-groups.d.ts +4 -0
- package/dist/routes-voyage-groups.d.ts.map +1 -0
- package/dist/routes-voyage-groups.js +85 -0
- package/dist/routes.d.ts +5 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +14 -0
- package/dist/schema-cabins.d.ts +1098 -0
- package/dist/schema-cabins.d.ts.map +1 -0
- package/dist/schema-cabins.js +105 -0
- package/dist/schema-content.d.ts +577 -0
- package/dist/schema-content.d.ts.map +1 -0
- package/dist/schema-content.js +63 -0
- package/dist/schema-core.d.ts +1790 -0
- package/dist/schema-core.d.ts.map +1 -0
- package/dist/schema-core.js +171 -0
- package/dist/schema-itinerary.d.ts +556 -0
- package/dist/schema-itinerary.d.ts.map +1 -0
- package/dist/schema-itinerary.js +50 -0
- package/dist/schema-pricing.d.ts +633 -0
- package/dist/schema-pricing.d.ts.map +1 -0
- package/dist/schema-pricing.js +73 -0
- package/dist/schema-search.d.ts +611 -0
- package/dist/schema-search.d.ts.map +1 -0
- package/dist/schema-search.js +64 -0
- package/dist/schema-shared.d.ts +23 -0
- package/dist/schema-shared.d.ts.map +1 -0
- package/dist/schema-shared.js +107 -0
- package/dist/schema-sourced-content.d.ts +247 -0
- package/dist/schema-sourced-content.d.ts.map +1 -0
- package/dist/schema-sourced-content.js +38 -0
- package/dist/schema.d.ts +10 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +9 -0
- package/dist/service-booking-helpers.d.ts +12 -0
- package/dist/service-booking-helpers.d.ts.map +1 -0
- package/dist/service-booking-helpers.js +94 -0
- package/dist/service-booking-types.d.ts +101 -0
- package/dist/service-booking-types.d.ts.map +1 -0
- package/dist/service-booking-types.js +1 -0
- package/dist/service-bookings.d.ts +46 -0
- package/dist/service-bookings.d.ts.map +1 -0
- package/dist/service-bookings.js +420 -0
- package/dist/service-catalog-plane-cabins.d.ts +24 -0
- package/dist/service-catalog-plane-cabins.d.ts.map +1 -0
- package/dist/service-catalog-plane-cabins.js +90 -0
- package/dist/service-catalog-plane.d.ts +74 -0
- package/dist/service-catalog-plane.d.ts.map +1 -0
- package/dist/service-catalog-plane.js +194 -0
- package/dist/service-content-synthesizer.d.ts +42 -0
- package/dist/service-content-synthesizer.d.ts.map +1 -0
- package/dist/service-content-synthesizer.js +144 -0
- package/dist/service-content.d.ts +74 -0
- package/dist/service-content.d.ts.map +1 -0
- package/dist/service-content.js +315 -0
- package/dist/service-core.d.ts +134 -0
- package/dist/service-core.d.ts.map +1 -0
- package/dist/service-core.js +257 -0
- package/dist/service-detach.d.ts +18 -0
- package/dist/service-detach.d.ts.map +1 -0
- package/dist/service-detach.js +199 -0
- package/dist/service-enrichment.d.ts +11 -0
- package/dist/service-enrichment.d.ts.map +1 -0
- package/dist/service-enrichment.js +47 -0
- package/dist/service-external-refresh.d.ts +39 -0
- package/dist/service-external-refresh.d.ts.map +1 -0
- package/dist/service-external-refresh.js +47 -0
- package/dist/service-itinerary.d.ts +22 -0
- package/dist/service-itinerary.d.ts.map +1 -0
- package/dist/service-itinerary.js +34 -0
- package/dist/service-prices.d.ts +46 -0
- package/dist/service-prices.d.ts.map +1 -0
- package/dist/service-prices.js +89 -0
- package/dist/service-pricing.d.ts +97 -0
- package/dist/service-pricing.d.ts.map +1 -0
- package/dist/service-pricing.js +198 -0
- package/dist/service-sailings.d.ts +48 -0
- package/dist/service-sailings.d.ts.map +1 -0
- package/dist/service-sailings.js +145 -0
- package/dist/service-search-types.d.ts +54 -0
- package/dist/service-search-types.d.ts.map +1 -0
- package/dist/service-search-types.js +1 -0
- package/dist/service-search.d.ts +65 -0
- package/dist/service-search.d.ts.map +1 -0
- package/dist/service-search.js +467 -0
- package/dist/service-shared.d.ts +22 -0
- package/dist/service-shared.d.ts.map +1 -0
- package/dist/service-shared.js +22 -0
- package/dist/service-ships.d.ts +47 -0
- package/dist/service-ships.d.ts.map +1 -0
- package/dist/service-ships.js +156 -0
- package/dist/service.d.ts +255 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +12 -0
- package/dist/validation-cabins.d.ts +267 -0
- package/dist/validation-cabins.d.ts.map +1 -0
- package/dist/validation-cabins.js +77 -0
- package/dist/validation-content.d.ts +123 -0
- package/dist/validation-content.d.ts.map +1 -0
- package/dist/validation-content.js +40 -0
- package/dist/validation-core.d.ts +393 -0
- package/dist/validation-core.d.ts.map +1 -0
- package/dist/validation-core.js +162 -0
- package/dist/validation-itinerary.d.ts +123 -0
- package/dist/validation-itinerary.d.ts.map +1 -0
- package/dist/validation-itinerary.js +47 -0
- package/dist/validation-pricing.d.ts +137 -0
- package/dist/validation-pricing.d.ts.map +1 -0
- package/dist/validation-pricing.js +49 -0
- package/dist/validation-search.d.ts +118 -0
- package/dist/validation-search.d.ts.map +1 -0
- package/dist/validation-search.js +60 -0
- package/dist/validation-shared.d.ts +123 -0
- package/dist/validation-shared.d.ts.map +1 -0
- package/dist/validation-shared.js +103 -0
- package/dist/validation.d.ts +8 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +7 -0
- package/package.json +146 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { CruiseAdapter, ExternalBookingTerms, ExternalFareVariant, ExternalPassengerComposition, SourceRef } from "./adapters/index.js";
|
|
2
|
+
import type { BookingCruiseDetail, BookingGroupCruiseDetail } from "./booking-extension.js";
|
|
3
|
+
import type { Quote } from "./service-pricing.js";
|
|
4
|
+
export type CruiseBookingPassenger = {
|
|
5
|
+
firstName: string;
|
|
6
|
+
lastName: string;
|
|
7
|
+
email?: string | null;
|
|
8
|
+
phone?: string | null;
|
|
9
|
+
travelerCategory?: "adult" | "child" | "infant" | "senior" | "other" | null;
|
|
10
|
+
preferredLanguage?: string | null;
|
|
11
|
+
specialRequests?: string | null;
|
|
12
|
+
personId?: string | null;
|
|
13
|
+
isPrimary?: boolean;
|
|
14
|
+
notes?: string | null;
|
|
15
|
+
};
|
|
16
|
+
export type CruiseBookingContact = {
|
|
17
|
+
firstName: string;
|
|
18
|
+
lastName: string;
|
|
19
|
+
email?: string | null;
|
|
20
|
+
phone?: string | null;
|
|
21
|
+
language?: string | null;
|
|
22
|
+
country?: string | null;
|
|
23
|
+
region?: string | null;
|
|
24
|
+
city?: string | null;
|
|
25
|
+
address?: string | null;
|
|
26
|
+
postalCode?: string | null;
|
|
27
|
+
};
|
|
28
|
+
export type CruiseBookingMode = "inquiry" | "reserve";
|
|
29
|
+
export type CreateCruiseBookingInput = {
|
|
30
|
+
sailingId: string;
|
|
31
|
+
cabinCategoryId: string;
|
|
32
|
+
cabinId?: string | null;
|
|
33
|
+
occupancy: number;
|
|
34
|
+
fareCode?: string | null;
|
|
35
|
+
fareVariant?: ExternalFareVariant | null;
|
|
36
|
+
mode?: CruiseBookingMode;
|
|
37
|
+
personId?: string | null;
|
|
38
|
+
organizationId?: string | null;
|
|
39
|
+
contact: CruiseBookingContact;
|
|
40
|
+
passengers: CruiseBookingPassenger[];
|
|
41
|
+
notes?: string | null;
|
|
42
|
+
/**
|
|
43
|
+
* Air-arrangement intent for this cruise. The cruise booking
|
|
44
|
+
* itself only carries the cabin line; the actual flight booking
|
|
45
|
+
* lives in the flights vertical (or with the customer when
|
|
46
|
+
* "independent"). Per booking-journey-architecture §7.
|
|
47
|
+
*/
|
|
48
|
+
airArrangement?: "cruise_line" | "independent" | "none" | null;
|
|
49
|
+
/** Optional pointer to a linked flight booking when the composer
|
|
50
|
+
* ties cabin + flight lines together. */
|
|
51
|
+
linkedFlightBookingId?: string | null;
|
|
52
|
+
};
|
|
53
|
+
export type CreateCruiseBookingResult = {
|
|
54
|
+
bookingId: string;
|
|
55
|
+
bookingNumber: string;
|
|
56
|
+
cruiseDetails: BookingCruiseDetail;
|
|
57
|
+
quote: Quote;
|
|
58
|
+
};
|
|
59
|
+
export type CruisePartyCabinEntry = {
|
|
60
|
+
cabinCategoryId: string;
|
|
61
|
+
cabinId?: string | null;
|
|
62
|
+
occupancy: number;
|
|
63
|
+
fareCode?: string | null;
|
|
64
|
+
fareVariant?: ExternalFareVariant | null;
|
|
65
|
+
passengers: CruiseBookingPassenger[];
|
|
66
|
+
notes?: string | null;
|
|
67
|
+
};
|
|
68
|
+
export type CreateCruisePartyBookingInput = {
|
|
69
|
+
sailingId: string;
|
|
70
|
+
cabins: CruisePartyCabinEntry[];
|
|
71
|
+
leadPersonId?: string | null;
|
|
72
|
+
organizationId?: string | null;
|
|
73
|
+
contact: CruiseBookingContact;
|
|
74
|
+
mode?: CruiseBookingMode;
|
|
75
|
+
label?: string;
|
|
76
|
+
notes?: string | null;
|
|
77
|
+
};
|
|
78
|
+
export type CreateCruisePartyBookingResult = {
|
|
79
|
+
groupId: string;
|
|
80
|
+
primaryBookingId: string | null;
|
|
81
|
+
groupDetails: BookingGroupCruiseDetail;
|
|
82
|
+
cabins: CreateCruiseBookingResult[];
|
|
83
|
+
};
|
|
84
|
+
export type CreateExternalCruiseBookingInput = {
|
|
85
|
+
adapter: CruiseAdapter;
|
|
86
|
+
sailingRef: SourceRef;
|
|
87
|
+
cabinCategoryRef: SourceRef;
|
|
88
|
+
cabinId?: string | null;
|
|
89
|
+
occupancy: number;
|
|
90
|
+
passengerComposition?: ExternalPassengerComposition | null;
|
|
91
|
+
fareCode?: string | null;
|
|
92
|
+
fareVariant?: ExternalFareVariant | null;
|
|
93
|
+
mode?: CruiseBookingMode;
|
|
94
|
+
personId?: string | null;
|
|
95
|
+
organizationId?: string | null;
|
|
96
|
+
contact: CruiseBookingContact;
|
|
97
|
+
passengers: CruiseBookingPassenger[];
|
|
98
|
+
bookingTerms?: ExternalBookingTerms | null;
|
|
99
|
+
notes?: string | null;
|
|
100
|
+
};
|
|
101
|
+
//# sourceMappingURL=service-booking-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-booking-types.d.ts","sourceRoot":"","sources":["../src/service-booking-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
2
|
+
import type { SourceRef } from "./adapters/index.js";
|
|
3
|
+
import type { CreateCruiseBookingInput, CreateCruiseBookingResult, CreateCruisePartyBookingInput, CreateCruisePartyBookingResult, CreateExternalCruiseBookingInput } from "./service-booking-types.js";
|
|
4
|
+
export type { CreateCruiseBookingInput, CreateCruiseBookingResult, CreateCruisePartyBookingInput, CreateCruisePartyBookingResult, CreateExternalCruiseBookingInput, CruiseBookingContact, CruiseBookingMode, CruiseBookingPassenger, CruisePartyCabinEntry, } from "./service-booking-types.js";
|
|
5
|
+
export declare const cruisesBookingService: {
|
|
6
|
+
/**
|
|
7
|
+
* Create a single-cabin cruise booking.
|
|
8
|
+
*
|
|
9
|
+
* Atomic: assembles the quote, creates the booking via bookingsService,
|
|
10
|
+
* inserts travelers, snapshots the quote into booking_cruise_details — all
|
|
11
|
+
* in one transaction. If any step fails the transaction is rolled back.
|
|
12
|
+
*
|
|
13
|
+
* Self-managed (local) sailings only in v1. External sailings flow through
|
|
14
|
+
* the adapter contract in §10 of the design doc; that branch lands in phase 3.
|
|
15
|
+
*/
|
|
16
|
+
createCruiseBooking(db: PostgresJsDatabase, input: CreateCruiseBookingInput, userId?: string): Promise<CreateCruiseBookingResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Create a multi-cabin cruise booking — one booking_group of kind 'cruise_party'
|
|
19
|
+
* containing N child cabin bookings on the same sailing.
|
|
20
|
+
*
|
|
21
|
+
* Same atomicity guarantees as createCruiseBooking. The group is the unit of
|
|
22
|
+
* shared confirmation / atomic cancellation / single deposit.
|
|
23
|
+
*/
|
|
24
|
+
createCruisePartyBooking(db: PostgresJsDatabase, input: CreateCruisePartyBookingInput, userId?: string): Promise<CreateCruisePartyBookingResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Create a single-cabin booking against an external (adapter-sourced) sailing.
|
|
27
|
+
*
|
|
28
|
+
* 1. Fetch sailing pricing from the adapter and pick the matching row.
|
|
29
|
+
* 2. composeQuote locally — pricing math is identical to local sailings.
|
|
30
|
+
* 3. Call adapter.createBooking to commit upstream — receives the carrier's
|
|
31
|
+
* confirmation reference.
|
|
32
|
+
* 4. Create the local booking row + travelers + booking_cruise_details with
|
|
33
|
+
* source='external' and the upstream connectorBookingRef snapshotted.
|
|
34
|
+
*
|
|
35
|
+
* If the upstream commit succeeds but the local insert fails, the upstream
|
|
36
|
+
* booking exists with no local trace — operators see it through the adapter's
|
|
37
|
+
* own UI but can't manage it via Voyant. v2 will introduce a reconciliation
|
|
38
|
+
* sweep; v1 surfaces the upstream ref in the error so the operator can manually
|
|
39
|
+
* cancel if needed.
|
|
40
|
+
*/
|
|
41
|
+
createExternalCruiseBooking(db: PostgresJsDatabase, input: CreateExternalCruiseBookingInput, userId?: string): Promise<CreateCruiseBookingResult & {
|
|
42
|
+
sourceProvider: string;
|
|
43
|
+
sourceRef: SourceRef;
|
|
44
|
+
}>;
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=service-bookings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-bookings.d.ts","sourceRoot":"","sources":["../src/service-bookings.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAapD,OAAO,KAAK,EACV,wBAAwB,EACxB,yBAAyB,EACzB,6BAA6B,EAC7B,8BAA8B,EAC9B,gCAAgC,EAEjC,MAAM,4BAA4B,CAAA;AAGnC,YAAY,EACV,wBAAwB,EACxB,yBAAyB,EACzB,6BAA6B,EAC7B,8BAA8B,EAC9B,gCAAgC,EAChC,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,4BAA4B,CAAA;AAEnC,eAAO,MAAM,qBAAqB;IAChC;;;;;;;;;OASG;4BAEG,kBAAkB,SACf,wBAAwB,WACtB,MAAM,GACd,OAAO,CAAC,yBAAyB,CAAC;IAwGrC;;;;;;OAMG;iCAEG,kBAAkB,SACf,6BAA6B,WAC3B,MAAM,GACd,OAAO,CAAC,8BAA8B,CAAC;IA+K1C;;;;;;;;;;;;;;;OAeG;oCAEG,kBAAkB,SACf,gCAAgC,WAC9B,MAAM,GACd,OAAO,CAAC,yBAAyB,GAAG;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,SAAS,CAAA;KAAE,CAAC;CA2KzF,CAAA"}
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import { bookingGroupsService, bookingsService } from "@voyant-travel/bookings";
|
|
2
|
+
import { bookingCruiseDetailsService, bookingGroupCruiseDetailsService, } from "./booking-extension.js";
|
|
3
|
+
import { assertPassengerCompositionMatchesPassengers, generateCruiseBookingNumber, passengerCompositionCount, passengerCompositionMatches, priceCentsFromString, sourceRefMatches, } from "./service-booking-helpers.js";
|
|
4
|
+
import { composeQuote, pricingService } from "./service-pricing.js";
|
|
5
|
+
export const cruisesBookingService = {
|
|
6
|
+
/**
|
|
7
|
+
* Create a single-cabin cruise booking.
|
|
8
|
+
*
|
|
9
|
+
* Atomic: assembles the quote, creates the booking via bookingsService,
|
|
10
|
+
* inserts travelers, snapshots the quote into booking_cruise_details — all
|
|
11
|
+
* in one transaction. If any step fails the transaction is rolled back.
|
|
12
|
+
*
|
|
13
|
+
* Self-managed (local) sailings only in v1. External sailings flow through
|
|
14
|
+
* the adapter contract in §10 of the design doc; that branch lands in phase 3.
|
|
15
|
+
*/
|
|
16
|
+
async createCruiseBooking(db, input, userId) {
|
|
17
|
+
const guestCount = input.passengers.length;
|
|
18
|
+
if (guestCount < 1)
|
|
19
|
+
throw new Error("At least one passenger is required");
|
|
20
|
+
if (guestCount > input.occupancy) {
|
|
21
|
+
throw new Error(`passengers.length (${guestCount}) cannot exceed occupancy (${input.occupancy})`);
|
|
22
|
+
}
|
|
23
|
+
return db.transaction(async (tx) => {
|
|
24
|
+
// 1. Resolve quote (reads pricing for self-managed sailings).
|
|
25
|
+
const quote = await pricingService.assembleQuote(tx, {
|
|
26
|
+
sailingId: input.sailingId,
|
|
27
|
+
cabinCategoryId: input.cabinCategoryId,
|
|
28
|
+
occupancy: input.occupancy,
|
|
29
|
+
guestCount,
|
|
30
|
+
fareCode: input.fareCode ?? null,
|
|
31
|
+
fareVariant: input.fareVariant ?? null,
|
|
32
|
+
});
|
|
33
|
+
// 2. Create the booking row.
|
|
34
|
+
const bookingNumber = generateCruiseBookingNumber();
|
|
35
|
+
const totalCents = priceCentsFromString(quote.totalForCabin);
|
|
36
|
+
const booking = await bookingsService.createBooking(tx, {
|
|
37
|
+
bookingNumber,
|
|
38
|
+
sellCurrency: quote.currency,
|
|
39
|
+
status: "draft",
|
|
40
|
+
sourceType: "manual",
|
|
41
|
+
personId: input.personId ?? null,
|
|
42
|
+
organizationId: input.organizationId ?? null,
|
|
43
|
+
contactFirstName: input.contact.firstName,
|
|
44
|
+
contactLastName: input.contact.lastName,
|
|
45
|
+
contactEmail: input.contact.email ?? null,
|
|
46
|
+
contactPhone: input.contact.phone ?? null,
|
|
47
|
+
contactPreferredLanguage: input.contact.language ?? null,
|
|
48
|
+
contactCountry: input.contact.country ?? null,
|
|
49
|
+
contactRegion: input.contact.region ?? null,
|
|
50
|
+
contactCity: input.contact.city ?? null,
|
|
51
|
+
contactAddressLine1: input.contact.address ?? null,
|
|
52
|
+
contactPostalCode: input.contact.postalCode ?? null,
|
|
53
|
+
sellAmountCents: totalCents,
|
|
54
|
+
pax: guestCount,
|
|
55
|
+
internalNotes: input.notes ?? null,
|
|
56
|
+
}, userId);
|
|
57
|
+
if (!booking)
|
|
58
|
+
throw new Error("bookingsService.createBooking returned null");
|
|
59
|
+
// 3. Insert travelers.
|
|
60
|
+
for (const passenger of input.passengers) {
|
|
61
|
+
await bookingsService.createTraveler(tx, booking.id, {
|
|
62
|
+
firstName: passenger.firstName,
|
|
63
|
+
lastName: passenger.lastName,
|
|
64
|
+
email: passenger.email ?? null,
|
|
65
|
+
phone: passenger.phone ?? null,
|
|
66
|
+
travelerCategory: passenger.travelerCategory ?? null,
|
|
67
|
+
preferredLanguage: passenger.preferredLanguage ?? null,
|
|
68
|
+
specialRequests: passenger.specialRequests ?? null,
|
|
69
|
+
isPrimary: passenger.isPrimary ?? false,
|
|
70
|
+
notes: passenger.notes ?? null,
|
|
71
|
+
}, userId);
|
|
72
|
+
}
|
|
73
|
+
// 4. Snapshot the quote into booking_cruise_details.
|
|
74
|
+
const cruiseDetails = await bookingCruiseDetailsService.upsert(tx, booking.id, {
|
|
75
|
+
source: "local",
|
|
76
|
+
sourceProvider: null,
|
|
77
|
+
sourceRef: null,
|
|
78
|
+
sailingId: input.sailingId,
|
|
79
|
+
cabinCategoryId: input.cabinCategoryId,
|
|
80
|
+
cabinId: input.cabinId ?? null,
|
|
81
|
+
sailingDisplayName: null,
|
|
82
|
+
cabinDisplayName: null,
|
|
83
|
+
occupancy: input.occupancy,
|
|
84
|
+
fareCode: input.fareCode ?? null,
|
|
85
|
+
fareVariant: quote.fareVariant,
|
|
86
|
+
mode: input.mode ?? "inquiry",
|
|
87
|
+
quotedPricePerPerson: quote.totalPerPerson,
|
|
88
|
+
quotedTotalForCabin: quote.totalForCabin,
|
|
89
|
+
quotedCurrency: quote.currency,
|
|
90
|
+
quotedComponentsJson: quote.components,
|
|
91
|
+
connectorBookingRef: null,
|
|
92
|
+
connectorStatus: null,
|
|
93
|
+
airArrangement: input.airArrangement ?? null,
|
|
94
|
+
linkedFlightBookingId: input.linkedFlightBookingId ?? null,
|
|
95
|
+
notes: input.notes ?? null,
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
bookingId: booking.id,
|
|
99
|
+
bookingNumber: booking.bookingNumber,
|
|
100
|
+
cruiseDetails,
|
|
101
|
+
quote,
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
/**
|
|
106
|
+
* Create a multi-cabin cruise booking — one booking_group of kind 'cruise_party'
|
|
107
|
+
* containing N child cabin bookings on the same sailing.
|
|
108
|
+
*
|
|
109
|
+
* Same atomicity guarantees as createCruiseBooking. The group is the unit of
|
|
110
|
+
* shared confirmation / atomic cancellation / single deposit.
|
|
111
|
+
*/
|
|
112
|
+
async createCruisePartyBooking(db, input, userId) {
|
|
113
|
+
if (input.cabins.length < 2) {
|
|
114
|
+
throw new Error("createCruisePartyBooking requires at least 2 cabins; use createCruiseBooking for a single cabin");
|
|
115
|
+
}
|
|
116
|
+
if (input.cabins.length > 20) {
|
|
117
|
+
throw new Error("createCruisePartyBooking supports at most 20 cabins per group");
|
|
118
|
+
}
|
|
119
|
+
return db.transaction(async (tx) => {
|
|
120
|
+
// 1. Quote each cabin independently and validate currency consistency.
|
|
121
|
+
const quotes = [];
|
|
122
|
+
for (const cabin of input.cabins) {
|
|
123
|
+
const guestCount = cabin.passengers.length;
|
|
124
|
+
if (guestCount < 1)
|
|
125
|
+
throw new Error("Each cabin in a party booking must have at least one passenger");
|
|
126
|
+
if (guestCount > cabin.occupancy)
|
|
127
|
+
throw new Error(`Cabin passengers.length (${guestCount}) cannot exceed occupancy (${cabin.occupancy})`);
|
|
128
|
+
const quote = await pricingService.assembleQuote(tx, {
|
|
129
|
+
sailingId: input.sailingId,
|
|
130
|
+
cabinCategoryId: cabin.cabinCategoryId,
|
|
131
|
+
occupancy: cabin.occupancy,
|
|
132
|
+
guestCount,
|
|
133
|
+
fareCode: cabin.fareCode ?? null,
|
|
134
|
+
fareVariant: cabin.fareVariant ?? null,
|
|
135
|
+
});
|
|
136
|
+
quotes.push({ quote, cabin });
|
|
137
|
+
}
|
|
138
|
+
const firstCurrency = quotes[0]?.quote.currency;
|
|
139
|
+
if (!firstCurrency)
|
|
140
|
+
throw new Error("No quotes assembled");
|
|
141
|
+
for (const q of quotes) {
|
|
142
|
+
if (q.quote.currency !== firstCurrency) {
|
|
143
|
+
throw new Error(`All cabins in a party booking must share a currency; got ${q.quote.currency} after ${firstCurrency}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// 2. Create the booking group up front so we have a primaryBookingId target.
|
|
147
|
+
const group = await bookingGroupsService.createBookingGroup(tx, {
|
|
148
|
+
kind: "cruise_party",
|
|
149
|
+
label: input.label ?? `Party booking on sailing ${input.sailingId}`,
|
|
150
|
+
primaryBookingId: null,
|
|
151
|
+
productId: null,
|
|
152
|
+
optionUnitId: null,
|
|
153
|
+
metadata: { sailingId: input.sailingId, cabinCount: input.cabins.length },
|
|
154
|
+
});
|
|
155
|
+
// 3. Create each cabin booking and add it to the group.
|
|
156
|
+
const cabinResults = [];
|
|
157
|
+
let primaryBookingId = null;
|
|
158
|
+
for (let i = 0; i < quotes.length; i++) {
|
|
159
|
+
const { cabin, quote } = quotes[i];
|
|
160
|
+
const isPrimary = i === 0;
|
|
161
|
+
const bookingNumber = generateCruiseBookingNumber(i + 1);
|
|
162
|
+
const totalCents = priceCentsFromString(quote.totalForCabin);
|
|
163
|
+
const booking = await bookingsService.createBooking(tx, {
|
|
164
|
+
bookingNumber,
|
|
165
|
+
sellCurrency: quote.currency,
|
|
166
|
+
status: "draft",
|
|
167
|
+
sourceType: "manual",
|
|
168
|
+
personId: input.leadPersonId ?? null,
|
|
169
|
+
organizationId: input.organizationId ?? null,
|
|
170
|
+
contactFirstName: input.contact.firstName,
|
|
171
|
+
contactLastName: input.contact.lastName,
|
|
172
|
+
contactEmail: input.contact.email ?? null,
|
|
173
|
+
contactPhone: input.contact.phone ?? null,
|
|
174
|
+
contactPreferredLanguage: input.contact.language ?? null,
|
|
175
|
+
contactCountry: input.contact.country ?? null,
|
|
176
|
+
contactRegion: input.contact.region ?? null,
|
|
177
|
+
contactCity: input.contact.city ?? null,
|
|
178
|
+
contactAddressLine1: input.contact.address ?? null,
|
|
179
|
+
contactPostalCode: input.contact.postalCode ?? null,
|
|
180
|
+
sellAmountCents: totalCents,
|
|
181
|
+
pax: cabin.passengers.length,
|
|
182
|
+
internalNotes: cabin.notes ?? null,
|
|
183
|
+
}, userId);
|
|
184
|
+
if (!booking)
|
|
185
|
+
throw new Error("bookingsService.createBooking returned null");
|
|
186
|
+
for (const passenger of cabin.passengers) {
|
|
187
|
+
await bookingsService.createTraveler(tx, booking.id, {
|
|
188
|
+
firstName: passenger.firstName,
|
|
189
|
+
lastName: passenger.lastName,
|
|
190
|
+
email: passenger.email ?? null,
|
|
191
|
+
phone: passenger.phone ?? null,
|
|
192
|
+
travelerCategory: passenger.travelerCategory ?? null,
|
|
193
|
+
preferredLanguage: passenger.preferredLanguage ?? null,
|
|
194
|
+
specialRequests: passenger.specialRequests ?? null,
|
|
195
|
+
isPrimary: passenger.isPrimary ?? false,
|
|
196
|
+
notes: passenger.notes ?? null,
|
|
197
|
+
}, userId);
|
|
198
|
+
}
|
|
199
|
+
const cruiseDetails = await bookingCruiseDetailsService.upsert(tx, booking.id, {
|
|
200
|
+
source: "local",
|
|
201
|
+
sourceProvider: null,
|
|
202
|
+
sourceRef: null,
|
|
203
|
+
sailingId: input.sailingId,
|
|
204
|
+
cabinCategoryId: cabin.cabinCategoryId,
|
|
205
|
+
cabinId: cabin.cabinId ?? null,
|
|
206
|
+
sailingDisplayName: null,
|
|
207
|
+
cabinDisplayName: null,
|
|
208
|
+
occupancy: cabin.occupancy,
|
|
209
|
+
fareCode: cabin.fareCode ?? null,
|
|
210
|
+
fareVariant: quote.fareVariant,
|
|
211
|
+
mode: input.mode ?? "inquiry",
|
|
212
|
+
quotedPricePerPerson: quote.totalPerPerson,
|
|
213
|
+
quotedTotalForCabin: quote.totalForCabin,
|
|
214
|
+
quotedCurrency: quote.currency,
|
|
215
|
+
quotedComponentsJson: quote.components,
|
|
216
|
+
connectorBookingRef: null,
|
|
217
|
+
connectorStatus: null,
|
|
218
|
+
notes: cabin.notes ?? null,
|
|
219
|
+
});
|
|
220
|
+
const memberResult = await bookingGroupsService.addGroupMember(tx, group.id, {
|
|
221
|
+
bookingId: booking.id,
|
|
222
|
+
role: isPrimary ? "primary" : "shared",
|
|
223
|
+
});
|
|
224
|
+
if (memberResult.status !== "ok") {
|
|
225
|
+
throw new Error(`Failed to add booking ${booking.id} to group ${group.id}: ${memberResult.status}`);
|
|
226
|
+
}
|
|
227
|
+
if (isPrimary)
|
|
228
|
+
primaryBookingId = booking.id;
|
|
229
|
+
cabinResults.push({
|
|
230
|
+
bookingId: booking.id,
|
|
231
|
+
bookingNumber: booking.bookingNumber,
|
|
232
|
+
cruiseDetails,
|
|
233
|
+
quote,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
// 4. Sum the per-cabin totals for the group snapshot.
|
|
237
|
+
let totalCents = 0;
|
|
238
|
+
for (const r of cabinResults) {
|
|
239
|
+
totalCents += priceCentsFromString(r.quote.totalForCabin);
|
|
240
|
+
}
|
|
241
|
+
const totalString = `${Math.trunc(totalCents / 100)}.${(Math.abs(totalCents) % 100).toString().padStart(2, "0")}`;
|
|
242
|
+
const groupDetails = await bookingGroupCruiseDetailsService.upsert(tx, group.id, {
|
|
243
|
+
source: "local",
|
|
244
|
+
sourceProvider: null,
|
|
245
|
+
sourceRef: null,
|
|
246
|
+
sailingId: input.sailingId,
|
|
247
|
+
sailingDisplayName: null,
|
|
248
|
+
cabinCount: input.cabins.length,
|
|
249
|
+
totalQuotedAmount: totalString,
|
|
250
|
+
quotedCurrency: firstCurrency,
|
|
251
|
+
connectorBookingRef: null,
|
|
252
|
+
notes: input.notes ?? null,
|
|
253
|
+
});
|
|
254
|
+
return {
|
|
255
|
+
groupId: group.id,
|
|
256
|
+
primaryBookingId,
|
|
257
|
+
groupDetails,
|
|
258
|
+
cabins: cabinResults,
|
|
259
|
+
};
|
|
260
|
+
});
|
|
261
|
+
},
|
|
262
|
+
/**
|
|
263
|
+
* Create a single-cabin booking against an external (adapter-sourced) sailing.
|
|
264
|
+
*
|
|
265
|
+
* 1. Fetch sailing pricing from the adapter and pick the matching row.
|
|
266
|
+
* 2. composeQuote locally — pricing math is identical to local sailings.
|
|
267
|
+
* 3. Call adapter.createBooking to commit upstream — receives the carrier's
|
|
268
|
+
* confirmation reference.
|
|
269
|
+
* 4. Create the local booking row + travelers + booking_cruise_details with
|
|
270
|
+
* source='external' and the upstream connectorBookingRef snapshotted.
|
|
271
|
+
*
|
|
272
|
+
* If the upstream commit succeeds but the local insert fails, the upstream
|
|
273
|
+
* booking exists with no local trace — operators see it through the adapter's
|
|
274
|
+
* own UI but can't manage it via Voyant. v2 will introduce a reconciliation
|
|
275
|
+
* sweep; v1 surfaces the upstream ref in the error so the operator can manually
|
|
276
|
+
* cancel if needed.
|
|
277
|
+
*/
|
|
278
|
+
async createExternalCruiseBooking(db, input, userId) {
|
|
279
|
+
const passengerComposition = assertPassengerCompositionMatchesPassengers(input.passengerComposition, input.passengers);
|
|
280
|
+
const guestCount = passengerCompositionCount(passengerComposition);
|
|
281
|
+
if (guestCount < 1)
|
|
282
|
+
throw new Error("At least one passenger is required");
|
|
283
|
+
if (guestCount > input.occupancy) {
|
|
284
|
+
throw new Error(`passengers.length (${guestCount}) cannot exceed occupancy (${input.occupancy})`);
|
|
285
|
+
}
|
|
286
|
+
// 1. Fetch upstream pricing + locate the matching row.
|
|
287
|
+
const prices = await input.adapter.fetchSailingPricing(input.sailingRef);
|
|
288
|
+
const matching = prices.find((p) => sourceRefMatches(p.cabinCategoryRef, input.cabinCategoryRef) &&
|
|
289
|
+
p.occupancy === input.occupancy &&
|
|
290
|
+
passengerCompositionMatches(p.passengerComposition, passengerComposition) &&
|
|
291
|
+
(!input.fareCode || p.fareCode === input.fareCode) &&
|
|
292
|
+
(!input.fareVariant || p.fareVariant === input.fareVariant));
|
|
293
|
+
if (!matching) {
|
|
294
|
+
throw new Error(`Adapter '${input.adapter.name}' has no matching price for sailing=${input.sailingRef.externalId} category=${input.cabinCategoryRef.externalId} occupancy=${input.occupancy}`);
|
|
295
|
+
}
|
|
296
|
+
// 2. Compose the quote locally for snapshot purposes.
|
|
297
|
+
const quote = composeQuote({
|
|
298
|
+
price: {
|
|
299
|
+
pricePerPerson: matching.pricePerPerson,
|
|
300
|
+
originalPricePerPerson: matching.originalPricePerPerson ?? null,
|
|
301
|
+
secondGuestPricePerPerson: matching.secondGuestPricePerPerson ?? null,
|
|
302
|
+
singlePricePerPerson: matching.singlePricePerPerson ?? null,
|
|
303
|
+
singleSupplementPercent: matching.singleSupplementPercent ?? null,
|
|
304
|
+
currency: matching.currency,
|
|
305
|
+
fareCode: matching.fareCode ?? null,
|
|
306
|
+
fareCodeName: matching.fareCodeName ?? null,
|
|
307
|
+
fareVariant: matching.fareVariant ?? "cruise_only",
|
|
308
|
+
earlyBookingDeadline: matching.earlyBookingDeadline ?? null,
|
|
309
|
+
earlyBookingBonusDescription: matching.earlyBookingBonusDescription ?? null,
|
|
310
|
+
},
|
|
311
|
+
components: (matching.components ?? []).map((c) => ({
|
|
312
|
+
kind: c.kind,
|
|
313
|
+
label: c.label ?? null,
|
|
314
|
+
amount: c.amount,
|
|
315
|
+
currency: c.currency,
|
|
316
|
+
direction: c.direction,
|
|
317
|
+
perPerson: c.perPerson,
|
|
318
|
+
})),
|
|
319
|
+
occupancy: input.occupancy,
|
|
320
|
+
guestCount,
|
|
321
|
+
bookingTerms: input.bookingTerms ?? matching.bookingTerms ?? null,
|
|
322
|
+
});
|
|
323
|
+
// 3. Commit upstream BEFORE writing local rows so we can fail loudly if the
|
|
324
|
+
// cruise line rejects the booking.
|
|
325
|
+
const upstream = await input.adapter.createBooking({
|
|
326
|
+
sailingRef: input.sailingRef,
|
|
327
|
+
cabinCategoryRef: input.cabinCategoryRef,
|
|
328
|
+
occupancy: input.occupancy,
|
|
329
|
+
passengerComposition,
|
|
330
|
+
fareCode: input.fareCode ?? null,
|
|
331
|
+
fareVariant: input.fareVariant ?? null,
|
|
332
|
+
passengers: input.passengers,
|
|
333
|
+
contact: input.contact,
|
|
334
|
+
bookingTerms: input.bookingTerms ?? matching.bookingTerms ?? null,
|
|
335
|
+
notes: input.notes ?? null,
|
|
336
|
+
});
|
|
337
|
+
// The adapter may return a refined quote (e.g. last-minute promo expired).
|
|
338
|
+
// Prefer the upstream-resolved quote when present.
|
|
339
|
+
const finalQuote = upstream.finalQuote ?? quote;
|
|
340
|
+
const finalComponents = upstream.finalComponents ?? quote.components;
|
|
341
|
+
const finalBookingTerms = upstream.finalBookingTerms ??
|
|
342
|
+
finalQuote.bookingTerms ??
|
|
343
|
+
input.bookingTerms ??
|
|
344
|
+
matching.bookingTerms ??
|
|
345
|
+
null;
|
|
346
|
+
// 4. Local persistence (now that upstream confirmation is in hand).
|
|
347
|
+
return db.transaction(async (tx) => {
|
|
348
|
+
const bookingNumber = generateCruiseBookingNumber();
|
|
349
|
+
const totalCents = priceCentsFromString(finalQuote.totalForCabin);
|
|
350
|
+
const booking = await bookingsService.createBooking(tx, {
|
|
351
|
+
bookingNumber,
|
|
352
|
+
sellCurrency: finalQuote.currency,
|
|
353
|
+
status: "draft",
|
|
354
|
+
sourceType: "manual",
|
|
355
|
+
personId: input.personId ?? null,
|
|
356
|
+
organizationId: input.organizationId ?? null,
|
|
357
|
+
contactFirstName: input.contact.firstName,
|
|
358
|
+
contactLastName: input.contact.lastName,
|
|
359
|
+
contactEmail: input.contact.email ?? null,
|
|
360
|
+
contactPhone: input.contact.phone ?? null,
|
|
361
|
+
contactPreferredLanguage: input.contact.language ?? null,
|
|
362
|
+
contactCountry: input.contact.country ?? null,
|
|
363
|
+
contactRegion: input.contact.region ?? null,
|
|
364
|
+
contactCity: input.contact.city ?? null,
|
|
365
|
+
contactAddressLine1: input.contact.address ?? null,
|
|
366
|
+
contactPostalCode: input.contact.postalCode ?? null,
|
|
367
|
+
sellAmountCents: totalCents,
|
|
368
|
+
pax: guestCount,
|
|
369
|
+
internalNotes: input.notes ?? null,
|
|
370
|
+
}, userId);
|
|
371
|
+
if (!booking) {
|
|
372
|
+
throw new Error(`Upstream booking ${upstream.connectorBookingRef} succeeded but local createBooking returned null. Operator must reconcile manually.`);
|
|
373
|
+
}
|
|
374
|
+
for (const passenger of input.passengers) {
|
|
375
|
+
await bookingsService.createTraveler(tx, booking.id, {
|
|
376
|
+
firstName: passenger.firstName,
|
|
377
|
+
lastName: passenger.lastName,
|
|
378
|
+
email: passenger.email ?? null,
|
|
379
|
+
phone: passenger.phone ?? null,
|
|
380
|
+
travelerCategory: passenger.travelerCategory ?? null,
|
|
381
|
+
preferredLanguage: passenger.preferredLanguage ?? null,
|
|
382
|
+
specialRequests: passenger.specialRequests ?? null,
|
|
383
|
+
isPrimary: passenger.isPrimary ?? false,
|
|
384
|
+
notes: null,
|
|
385
|
+
}, userId);
|
|
386
|
+
}
|
|
387
|
+
const cruiseDetails = await bookingCruiseDetailsService.upsert(tx, booking.id, {
|
|
388
|
+
source: "external",
|
|
389
|
+
sourceProvider: input.adapter.name,
|
|
390
|
+
sourceRef: input.sailingRef,
|
|
391
|
+
sailingId: null,
|
|
392
|
+
cabinCategoryId: null,
|
|
393
|
+
cabinId: null,
|
|
394
|
+
sailingDisplayName: null,
|
|
395
|
+
cabinDisplayName: null,
|
|
396
|
+
occupancy: input.occupancy,
|
|
397
|
+
fareCode: input.fareCode ?? null,
|
|
398
|
+
fareVariant: finalQuote.fareVariant,
|
|
399
|
+
mode: input.mode ?? "inquiry",
|
|
400
|
+
quotedPricePerPerson: finalQuote.totalPerPerson,
|
|
401
|
+
quotedTotalForCabin: finalQuote.totalForCabin,
|
|
402
|
+
quotedCurrency: finalQuote.currency,
|
|
403
|
+
quotedComponentsJson: finalComponents,
|
|
404
|
+
bookingTermsSnapshotJson: finalBookingTerms,
|
|
405
|
+
passengerCompositionSnapshotJson: passengerComposition,
|
|
406
|
+
connectorBookingRef: upstream.connectorBookingRef,
|
|
407
|
+
connectorStatus: upstream.connectorStatus ?? null,
|
|
408
|
+
notes: input.notes ?? null,
|
|
409
|
+
});
|
|
410
|
+
return {
|
|
411
|
+
bookingId: booking.id,
|
|
412
|
+
bookingNumber: booking.bookingNumber,
|
|
413
|
+
cruiseDetails,
|
|
414
|
+
quote: finalQuote,
|
|
415
|
+
sourceProvider: input.adapter.name,
|
|
416
|
+
sourceRef: input.sailingRef,
|
|
417
|
+
};
|
|
418
|
+
});
|
|
419
|
+
},
|
|
420
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cruise cabin/deck projection extension.
|
|
3
|
+
*
|
|
4
|
+
* Joins a cruise's default ship to cabin categories, specific cabins, and
|
|
5
|
+
* decks, then denormalizes machine-filterable cabin/deck facets onto the
|
|
6
|
+
* cruise search document.
|
|
7
|
+
*/
|
|
8
|
+
import type { CruiseProjectionExtension } from "./service-catalog-plane.js";
|
|
9
|
+
export interface CruiseCabinFacetJoinRow {
|
|
10
|
+
cabinCategoryId: string;
|
|
11
|
+
cabinCategoryCode: string;
|
|
12
|
+
roomType: string;
|
|
13
|
+
featureCodes: string[] | null;
|
|
14
|
+
bedConfigurations: string[] | null;
|
|
15
|
+
accessibilityFeatures: string[] | null;
|
|
16
|
+
viewType: string | null;
|
|
17
|
+
gradeCodes: string[] | null;
|
|
18
|
+
deckId: string | null;
|
|
19
|
+
deckName: string | null;
|
|
20
|
+
deckLevel: number | null;
|
|
21
|
+
}
|
|
22
|
+
export declare function projectCruiseCabinFacetRows(rows: ReadonlyArray<CruiseCabinFacetJoinRow>): ReadonlyMap<string, unknown>;
|
|
23
|
+
export declare function createCruiseCabinFacetProjectionExtension(): CruiseProjectionExtension;
|
|
24
|
+
//# sourceMappingURL=service-catalog-plane-cabins.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-catalog-plane-cabins.d.ts","sourceRoot":"","sources":["../src/service-catalog-plane-cabins.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAA;AAE3E,MAAM,WAAW,uBAAuB;IACtC,eAAe,EAAE,MAAM,CAAA;IACvB,iBAAiB,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IAC7B,iBAAiB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IAClC,qBAAqB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACtC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,aAAa,CAAC,uBAAuB,CAAC,GAC3C,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CA0C9B;AAED,wBAAgB,yCAAyC,IAAI,yBAAyB,CA4BrF"}
|