@voyant-travel/trips 0.110.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 +38 -0
- package/dist/catalog-component-adapter.d.ts +16 -0
- package/dist/catalog-component-adapter.d.ts.map +1 -0
- package/dist/catalog-component-adapter.js +34 -0
- package/dist/cruise-extension.d.ts +48 -0
- package/dist/cruise-extension.d.ts.map +1 -0
- package/dist/cruise-extension.js +66 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/mcp-contract.d.ts +79 -0
- package/dist/mcp-contract.d.ts.map +1 -0
- package/dist/mcp-contract.js +21 -0
- package/dist/mcp-registry.d.ts +48 -0
- package/dist/mcp-registry.d.ts.map +1 -0
- package/dist/mcp-registry.js +88 -0
- package/dist/mcp-tools.d.ts +157 -0
- package/dist/mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools.js +109 -0
- package/dist/routes.d.ts +2068 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +280 -0
- package/dist/schema.d.ts +1897 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +255 -0
- package/dist/service-cancellation.d.ts +6 -0
- package/dist/service-cancellation.d.ts.map +1 -0
- package/dist/service-cancellation.js +251 -0
- package/dist/service-checkout.d.ts +6 -0
- package/dist/service-checkout.d.ts.map +1 -0
- package/dist/service-checkout.js +328 -0
- package/dist/service-helpers.d.ts +17 -0
- package/dist/service-helpers.d.ts.map +1 -0
- package/dist/service-helpers.js +161 -0
- package/dist/service-internals.d.ts +11 -0
- package/dist/service-internals.d.ts.map +1 -0
- package/dist/service-internals.js +72 -0
- package/dist/service-pricing.d.ts +8 -0
- package/dist/service-pricing.d.ts.map +1 -0
- package/dist/service-pricing.js +142 -0
- package/dist/service-reservation.d.ts +5 -0
- package/dist/service-reservation.d.ts.map +1 -0
- package/dist/service-reservation.js +493 -0
- package/dist/service-snapshots.d.ts +8 -0
- package/dist/service-snapshots.d.ts.map +1 -0
- package/dist/service-snapshots.js +115 -0
- package/dist/service-trips.d.ts +14 -0
- package/dist/service-trips.d.ts.map +1 -0
- package/dist/service-trips.js +378 -0
- package/dist/service-types.d.ts +285 -0
- package/dist/service-types.d.ts.map +1 -0
- package/dist/service-types.js +6 -0
- package/dist/service.d.ts +38 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +40 -0
- package/dist/traveler-party-validation.d.ts +3 -0
- package/dist/traveler-party-validation.d.ts.map +1 -0
- package/dist/traveler-party-validation.js +68 -0
- package/dist/validation.d.ts +449 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +261 -0
- package/package.json +83 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { typeId, typeIdRef } from "@voyant-travel/db/lib/typeid-column";
|
|
2
|
+
import { relations } from "drizzle-orm";
|
|
3
|
+
import { index, integer, jsonb, pgEnum, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
|
4
|
+
export const tripEnvelopeStatusEnum = pgEnum("trip_envelope_status", [
|
|
5
|
+
"draft",
|
|
6
|
+
"priced",
|
|
7
|
+
"reserve_in_progress",
|
|
8
|
+
"reserved",
|
|
9
|
+
"checkout_started",
|
|
10
|
+
"booked",
|
|
11
|
+
"failed",
|
|
12
|
+
"cancelled",
|
|
13
|
+
]);
|
|
14
|
+
export const tripComponentKindEnum = pgEnum("trip_component_kind", [
|
|
15
|
+
"catalog_booking",
|
|
16
|
+
"manual_placeholder",
|
|
17
|
+
"flight_placeholder",
|
|
18
|
+
"flight_order",
|
|
19
|
+
"external_order",
|
|
20
|
+
]);
|
|
21
|
+
export const tripComponentStatusEnum = pgEnum("trip_component_status", [
|
|
22
|
+
"draft",
|
|
23
|
+
"priced",
|
|
24
|
+
"unavailable",
|
|
25
|
+
"held",
|
|
26
|
+
"booked",
|
|
27
|
+
"checkout_started",
|
|
28
|
+
"failed",
|
|
29
|
+
"cancelled",
|
|
30
|
+
"removed",
|
|
31
|
+
]);
|
|
32
|
+
export const tripComponentEventTypeEnum = pgEnum("trip_component_event_type", [
|
|
33
|
+
"created",
|
|
34
|
+
"updated",
|
|
35
|
+
"priced",
|
|
36
|
+
"hold_placed",
|
|
37
|
+
"booked",
|
|
38
|
+
"checkout_started",
|
|
39
|
+
"failed",
|
|
40
|
+
"cancelled",
|
|
41
|
+
"removed",
|
|
42
|
+
"staff_remediation_required",
|
|
43
|
+
]);
|
|
44
|
+
export const tripReservationPlanStatusEnum = pgEnum("trip_reservation_plan_status", [
|
|
45
|
+
"pending",
|
|
46
|
+
"submitted",
|
|
47
|
+
"reserved",
|
|
48
|
+
"failed",
|
|
49
|
+
"cancelled",
|
|
50
|
+
]);
|
|
51
|
+
export const tripEnvelopes = pgTable("trip_envelopes", {
|
|
52
|
+
id: typeId("trip_envelopes"),
|
|
53
|
+
status: tripEnvelopeStatusEnum("status").notNull().default("draft"),
|
|
54
|
+
title: text("title"),
|
|
55
|
+
description: text("description"),
|
|
56
|
+
travelerParty: jsonb("traveler_party").$type().notNull().default({}),
|
|
57
|
+
constraints: jsonb("constraints").$type().notNull().default({}),
|
|
58
|
+
aggregateCurrency: text("aggregate_currency"),
|
|
59
|
+
aggregateSubtotalAmountCents: integer("aggregate_subtotal_amount_cents"),
|
|
60
|
+
aggregateTaxAmountCents: integer("aggregate_tax_amount_cents"),
|
|
61
|
+
aggregateTotalAmountCents: integer("aggregate_total_amount_cents"),
|
|
62
|
+
aggregatePricingSnapshot: jsonb("aggregate_pricing_snapshot").$type(),
|
|
63
|
+
currentPriceExpiresAt: timestamp("current_price_expires_at", { withTimezone: true }),
|
|
64
|
+
bookingGroupId: text("booking_group_id"),
|
|
65
|
+
orderId: text("order_id"),
|
|
66
|
+
paymentSessionId: text("payment_session_id"),
|
|
67
|
+
reserveIdempotencyKey: text("reserve_idempotency_key"),
|
|
68
|
+
reserveStartedAt: timestamp("reserve_started_at", { withTimezone: true }),
|
|
69
|
+
reservedAt: timestamp("reserved_at", { withTimezone: true }),
|
|
70
|
+
checkoutIdempotencyKey: text("checkout_idempotency_key"),
|
|
71
|
+
checkoutStartedAt: timestamp("checkout_started_at", { withTimezone: true }),
|
|
72
|
+
createdBy: text("created_by"),
|
|
73
|
+
updatedBy: text("updated_by"),
|
|
74
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
75
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
76
|
+
}, (table) => [
|
|
77
|
+
index("idx_trip_envelopes_status_updated").on(table.status, table.updatedAt),
|
|
78
|
+
index("idx_trip_envelopes_created_by_updated").on(table.createdBy, table.updatedAt),
|
|
79
|
+
index("idx_trip_envelopes_booking_group").on(table.bookingGroupId),
|
|
80
|
+
index("idx_trip_envelopes_payment_session").on(table.paymentSessionId),
|
|
81
|
+
index("idx_trip_envelopes_reserve_idempotency").on(table.reserveIdempotencyKey),
|
|
82
|
+
index("idx_trip_envelopes_checkout_idempotency").on(table.checkoutIdempotencyKey),
|
|
83
|
+
]);
|
|
84
|
+
export const tripComponents = pgTable("trip_components", {
|
|
85
|
+
id: typeId("trip_components"),
|
|
86
|
+
envelopeId: typeIdRef("envelope_id")
|
|
87
|
+
.notNull()
|
|
88
|
+
.references(() => tripEnvelopes.id, { onDelete: "cascade" }),
|
|
89
|
+
sequence: integer("sequence").notNull().default(0),
|
|
90
|
+
kind: tripComponentKindEnum("kind").notNull(),
|
|
91
|
+
status: tripComponentStatusEnum("status").notNull().default("draft"),
|
|
92
|
+
title: text("title"),
|
|
93
|
+
description: text("description"),
|
|
94
|
+
entityModule: text("entity_module"),
|
|
95
|
+
entityId: text("entity_id"),
|
|
96
|
+
sourceKind: text("source_kind"),
|
|
97
|
+
sourceConnectionId: text("source_connection_id"),
|
|
98
|
+
sourceRef: text("source_ref"),
|
|
99
|
+
bookingDraftId: text("booking_draft_id"),
|
|
100
|
+
catalogQuoteId: text("catalog_quote_id"),
|
|
101
|
+
bookingId: text("booking_id"),
|
|
102
|
+
bookingGroupId: text("booking_group_id"),
|
|
103
|
+
orderId: text("order_id"),
|
|
104
|
+
paymentSessionId: text("payment_session_id"),
|
|
105
|
+
providerRef: text("provider_ref"),
|
|
106
|
+
supplierRef: text("supplier_ref"),
|
|
107
|
+
componentCurrency: text("component_currency"),
|
|
108
|
+
componentSubtotalAmountCents: integer("component_subtotal_amount_cents"),
|
|
109
|
+
componentTaxAmountCents: integer("component_tax_amount_cents"),
|
|
110
|
+
componentTotalAmountCents: integer("component_total_amount_cents"),
|
|
111
|
+
pricingSnapshot: jsonb("pricing_snapshot").$type(),
|
|
112
|
+
taxLines: jsonb("tax_lines").$type().default([]),
|
|
113
|
+
cancellationSnapshot: jsonb("cancellation_snapshot").$type(),
|
|
114
|
+
holdToken: text("hold_token"),
|
|
115
|
+
holdExpiresAt: timestamp("hold_expires_at", { withTimezone: true }),
|
|
116
|
+
priceExpiresAt: timestamp("price_expires_at", { withTimezone: true }),
|
|
117
|
+
warningCodes: jsonb("warning_codes").$type().notNull().default([]),
|
|
118
|
+
metadata: jsonb("metadata").$type().notNull().default({}),
|
|
119
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
120
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
121
|
+
}, (table) => [
|
|
122
|
+
index("idx_trip_components_envelope_sequence").on(table.envelopeId, table.sequence),
|
|
123
|
+
index("idx_trip_components_envelope_status").on(table.envelopeId, table.status),
|
|
124
|
+
index("idx_trip_components_catalog_entity").on(table.entityModule, table.entityId),
|
|
125
|
+
index("idx_trip_components_booking_draft").on(table.bookingDraftId),
|
|
126
|
+
index("idx_trip_components_catalog_quote").on(table.catalogQuoteId),
|
|
127
|
+
index("idx_trip_components_booking").on(table.bookingId),
|
|
128
|
+
index("idx_trip_components_order").on(table.orderId),
|
|
129
|
+
index("idx_trip_components_payment_session").on(table.paymentSessionId),
|
|
130
|
+
]);
|
|
131
|
+
export const tripComponentEvents = pgTable("trip_component_events", {
|
|
132
|
+
id: typeId("trip_component_events"),
|
|
133
|
+
envelopeId: typeIdRef("envelope_id")
|
|
134
|
+
.notNull()
|
|
135
|
+
.references(() => tripEnvelopes.id, { onDelete: "cascade" }),
|
|
136
|
+
componentId: typeIdRef("component_id").references(() => tripComponents.id, {
|
|
137
|
+
onDelete: "set null",
|
|
138
|
+
}),
|
|
139
|
+
eventType: tripComponentEventTypeEnum("event_type").notNull(),
|
|
140
|
+
fromStatus: tripComponentStatusEnum("from_status"),
|
|
141
|
+
toStatus: tripComponentStatusEnum("to_status"),
|
|
142
|
+
payload: jsonb("payload").$type().notNull().default({}),
|
|
143
|
+
actorId: text("actor_id"),
|
|
144
|
+
occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull().defaultNow(),
|
|
145
|
+
}, (table) => [
|
|
146
|
+
index("idx_trip_component_events_envelope_time").on(table.envelopeId, table.occurredAt),
|
|
147
|
+
index("idx_trip_component_events_component_time").on(table.componentId, table.occurredAt),
|
|
148
|
+
index("idx_trip_component_events_type_time").on(table.eventType, table.occurredAt),
|
|
149
|
+
]);
|
|
150
|
+
export const tripSnapshots = pgTable("trip_snapshots", {
|
|
151
|
+
id: typeId("trip_snapshots"),
|
|
152
|
+
envelopeId: typeIdRef("envelope_id")
|
|
153
|
+
.notNull()
|
|
154
|
+
.references(() => tripEnvelopes.id, { onDelete: "restrict" }),
|
|
155
|
+
sourceEnvelopeUpdatedAt: timestamp("source_envelope_updated_at", {
|
|
156
|
+
withTimezone: true,
|
|
157
|
+
}).notNull(),
|
|
158
|
+
titleSnapshot: text("title_snapshot"),
|
|
159
|
+
descriptionSnapshot: text("description_snapshot"),
|
|
160
|
+
travelerPartySnapshot: jsonb("traveler_party_snapshot")
|
|
161
|
+
.$type()
|
|
162
|
+
.notNull()
|
|
163
|
+
.default({}),
|
|
164
|
+
constraintsSnapshot: jsonb("constraints_snapshot")
|
|
165
|
+
.$type()
|
|
166
|
+
.notNull()
|
|
167
|
+
.default({}),
|
|
168
|
+
currency: text("currency").notNull(),
|
|
169
|
+
subtotalAmountCents: integer("subtotal_amount_cents").notNull().default(0),
|
|
170
|
+
taxAmountCents: integer("tax_amount_cents").notNull().default(0),
|
|
171
|
+
totalAmountCents: integer("total_amount_cents").notNull().default(0),
|
|
172
|
+
componentCount: integer("component_count").notNull().default(0),
|
|
173
|
+
pricedComponentCount: integer("priced_component_count").notNull().default(0),
|
|
174
|
+
frozenEnvelope: jsonb("frozen_envelope").$type().notNull(),
|
|
175
|
+
frozenComponents: jsonb("frozen_components")
|
|
176
|
+
.$type()
|
|
177
|
+
.notNull()
|
|
178
|
+
.default([]),
|
|
179
|
+
proposal: jsonb("proposal").$type().notNull(),
|
|
180
|
+
createdBy: text("created_by"),
|
|
181
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
182
|
+
}, (table) => [
|
|
183
|
+
index("idx_trip_snapshots_envelope_created").on(table.envelopeId, table.createdAt),
|
|
184
|
+
index("idx_trip_snapshots_created").on(table.createdAt),
|
|
185
|
+
]);
|
|
186
|
+
export const tripReservationPlans = pgTable("trip_reservation_plans", {
|
|
187
|
+
id: typeId("trip_reservation_plans"),
|
|
188
|
+
envelopeId: typeIdRef("envelope_id")
|
|
189
|
+
.notNull()
|
|
190
|
+
.references(() => tripEnvelopes.id, { onDelete: "restrict" }),
|
|
191
|
+
snapshotId: typeIdRef("snapshot_id").references(() => tripSnapshots.id, {
|
|
192
|
+
onDelete: "set null",
|
|
193
|
+
}),
|
|
194
|
+
status: tripReservationPlanStatusEnum("status").notNull().default("pending"),
|
|
195
|
+
idempotencyKey: text("idempotency_key"),
|
|
196
|
+
refreshScope: jsonb("refresh_scope").$type(),
|
|
197
|
+
componentCount: integer("component_count").notNull().default(0),
|
|
198
|
+
components: jsonb("components")
|
|
199
|
+
.$type()
|
|
200
|
+
.notNull()
|
|
201
|
+
.default([]),
|
|
202
|
+
failures: jsonb("failures").$type().default([]),
|
|
203
|
+
compensations: jsonb("compensations")
|
|
204
|
+
.$type()
|
|
205
|
+
.default([]),
|
|
206
|
+
warnings: jsonb("warnings").$type().notNull().default([]),
|
|
207
|
+
submittedAt: timestamp("submitted_at", { withTimezone: true }),
|
|
208
|
+
completedAt: timestamp("completed_at", { withTimezone: true }),
|
|
209
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
210
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
211
|
+
}, (table) => [
|
|
212
|
+
index("idx_trip_reservation_plans_envelope_created").on(table.envelopeId, table.createdAt),
|
|
213
|
+
index("idx_trip_reservation_plans_snapshot").on(table.snapshotId),
|
|
214
|
+
index("idx_trip_reservation_plans_status_updated").on(table.status, table.updatedAt),
|
|
215
|
+
index("idx_trip_reservation_plans_idempotency").on(table.idempotencyKey),
|
|
216
|
+
]);
|
|
217
|
+
export const tripEnvelopeRelations = relations(tripEnvelopes, ({ many }) => ({
|
|
218
|
+
components: many(tripComponents),
|
|
219
|
+
events: many(tripComponentEvents),
|
|
220
|
+
snapshots: many(tripSnapshots),
|
|
221
|
+
reservationPlans: many(tripReservationPlans),
|
|
222
|
+
}));
|
|
223
|
+
export const tripComponentRelations = relations(tripComponents, ({ one, many }) => ({
|
|
224
|
+
envelope: one(tripEnvelopes, {
|
|
225
|
+
fields: [tripComponents.envelopeId],
|
|
226
|
+
references: [tripEnvelopes.id],
|
|
227
|
+
}),
|
|
228
|
+
events: many(tripComponentEvents),
|
|
229
|
+
}));
|
|
230
|
+
export const tripComponentEventRelations = relations(tripComponentEvents, ({ one }) => ({
|
|
231
|
+
envelope: one(tripEnvelopes, {
|
|
232
|
+
fields: [tripComponentEvents.envelopeId],
|
|
233
|
+
references: [tripEnvelopes.id],
|
|
234
|
+
}),
|
|
235
|
+
component: one(tripComponents, {
|
|
236
|
+
fields: [tripComponentEvents.componentId],
|
|
237
|
+
references: [tripComponents.id],
|
|
238
|
+
}),
|
|
239
|
+
}));
|
|
240
|
+
export const tripSnapshotRelations = relations(tripSnapshots, ({ one }) => ({
|
|
241
|
+
envelope: one(tripEnvelopes, {
|
|
242
|
+
fields: [tripSnapshots.envelopeId],
|
|
243
|
+
references: [tripEnvelopes.id],
|
|
244
|
+
}),
|
|
245
|
+
}));
|
|
246
|
+
export const tripReservationPlanRelations = relations(tripReservationPlans, ({ one }) => ({
|
|
247
|
+
envelope: one(tripEnvelopes, {
|
|
248
|
+
fields: [tripReservationPlans.envelopeId],
|
|
249
|
+
references: [tripEnvelopes.id],
|
|
250
|
+
}),
|
|
251
|
+
snapshot: one(tripSnapshots, {
|
|
252
|
+
fields: [tripReservationPlans.snapshotId],
|
|
253
|
+
references: [tripSnapshots.id],
|
|
254
|
+
}),
|
|
255
|
+
}));
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AnyDrizzleDb } from "@voyant-travel/db";
|
|
2
|
+
import type { CancelTripComponentsDeps, CancelTripComponentsResult, PreviewTripCancellationDeps, TripCancellationPreviewResult } from "./service-types.js";
|
|
3
|
+
import { type CancelTripComponentsInput, type PreviewTripCancellationInput } from "./validation.js";
|
|
4
|
+
export declare function previewCancellation(db: AnyDrizzleDb, input: PreviewTripCancellationInput, deps?: PreviewTripCancellationDeps): Promise<TripCancellationPreviewResult>;
|
|
5
|
+
export declare function cancelComponents(db: AnyDrizzleDb, input: CancelTripComponentsInput, deps?: CancelTripComponentsDeps): Promise<CancelTripComponentsResult>;
|
|
6
|
+
//# sourceMappingURL=service-cancellation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-cancellation.d.ts","sourceRoot":"","sources":["../src/service-cancellation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAWrD,OAAO,KAAK,EAEV,wBAAwB,EACxB,0BAA0B,EAE1B,2BAA2B,EAE3B,6BAA6B,EAC9B,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,KAAK,yBAAyB,EAE9B,KAAK,4BAA4B,EAClC,MAAM,iBAAiB,CAAA;AAExB,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,YAAY,EAChB,KAAK,EAAE,4BAA4B,EACnC,IAAI,GAAE,2BAAgC,GACrC,OAAO,CAAC,6BAA6B,CAAC,CAqBxC;AAED,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,YAAY,EAChB,KAAK,EAAE,yBAAyB,EAChC,IAAI,GAAE,wBAA6B,GAClC,OAAO,CAAC,0BAA0B,CAAC,CAsFrC"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
import { tripComponents, tripEnvelopes } from "./schema.js";
|
|
3
|
+
import { assertTripComponentCanBeUpdated, hasCommittedComponentReference, } from "./service-helpers.js";
|
|
4
|
+
import { createComponentEvent, markComponentForStaffRemediation } from "./service-internals.js";
|
|
5
|
+
import { getTrip } from "./service-trips.js";
|
|
6
|
+
import { TripsInvariantError } from "./service-types.js";
|
|
7
|
+
import { isTerminalTripComponentStatus, } from "./validation.js";
|
|
8
|
+
export async function previewCancellation(db, input, deps = {}) {
|
|
9
|
+
const trip = await getTrip(db, input.envelopeId);
|
|
10
|
+
if (!trip) {
|
|
11
|
+
throw new TripsInvariantError(`Trip envelope ${input.envelopeId} was not found`);
|
|
12
|
+
}
|
|
13
|
+
const requestedAt = input.requestedAt ? new Date(input.requestedAt) : new Date();
|
|
14
|
+
const selected = selectComponentsForCancellation(trip, input.componentIds);
|
|
15
|
+
const componentPreviews = [];
|
|
16
|
+
for (const component of selected) {
|
|
17
|
+
componentPreviews.push(await previewComponentCancellation(trip.envelope, component, input, requestedAt, deps));
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
envelope: trip.envelope,
|
|
21
|
+
components: trip.components,
|
|
22
|
+
preview: buildCancellationPreviewAggregate(trip.envelope, selected, componentPreviews),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export async function cancelComponents(db, input, deps = {}) {
|
|
26
|
+
const preview = await previewCancellation(db, input, deps);
|
|
27
|
+
const requestedAt = input.requestedAt ? new Date(input.requestedAt) : new Date();
|
|
28
|
+
const componentsById = new Map(preview.components.map((component) => [component.id, component]));
|
|
29
|
+
const cancelled = [];
|
|
30
|
+
const remediation = [];
|
|
31
|
+
const skipped = [];
|
|
32
|
+
for (const item of preview.preview.components) {
|
|
33
|
+
const component = componentsById.get(item.componentId);
|
|
34
|
+
if (!component)
|
|
35
|
+
continue;
|
|
36
|
+
if (item.action === "no_op") {
|
|
37
|
+
skipped.push({ componentId: item.componentId, reason: item.reason ?? "no_action" });
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (item.action === "staff_remediation") {
|
|
41
|
+
const reason = item.reason ?? "staff_remediation_required";
|
|
42
|
+
const updated = await markComponentForStaffRemediation(db, component, reason);
|
|
43
|
+
componentsById.set(updated.id, updated);
|
|
44
|
+
remediation.push({ componentId: item.componentId, reason });
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (!deps.cancelComponent && !canCancelComponentLocally(component)) {
|
|
48
|
+
const reason = "cancel_adapter_not_configured";
|
|
49
|
+
const updated = await markComponentForStaffRemediation(db, component, reason);
|
|
50
|
+
componentsById.set(updated.id, updated);
|
|
51
|
+
remediation.push({ componentId: item.componentId, reason });
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const result = canCancelComponentLocally(component)
|
|
56
|
+
? localCancellationResult(item)
|
|
57
|
+
: await deps.cancelComponent?.({
|
|
58
|
+
envelope: preview.envelope,
|
|
59
|
+
component,
|
|
60
|
+
preview: item,
|
|
61
|
+
reason: input.reason,
|
|
62
|
+
requestedAt,
|
|
63
|
+
request: input.request,
|
|
64
|
+
});
|
|
65
|
+
if (!result || result.status !== "cancelled") {
|
|
66
|
+
const reason = result?.reason ?? `cancel_${result?.status ?? "not_configured"}`;
|
|
67
|
+
const updated = await markComponentForStaffRemediation(db, component, reason);
|
|
68
|
+
componentsById.set(updated.id, updated);
|
|
69
|
+
remediation.push({ componentId: item.componentId, reason });
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const updated = await markComponentCancelled(db, component, {
|
|
73
|
+
...item,
|
|
74
|
+
refundAmountCents: result.refundAmountCents ?? item.refundAmountCents,
|
|
75
|
+
refundCurrency: result.refundCurrency ?? item.refundCurrency,
|
|
76
|
+
snapshot: result.snapshot ?? item.snapshot,
|
|
77
|
+
});
|
|
78
|
+
componentsById.set(updated.id, updated);
|
|
79
|
+
cancelled.push({ componentId: item.componentId, status: "cancelled" });
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
const reason = error instanceof Error ? error.message : "cancel_failed";
|
|
83
|
+
const updated = await markComponentForStaffRemediation(db, component, reason);
|
|
84
|
+
componentsById.set(updated.id, updated);
|
|
85
|
+
remediation.push({ componentId: item.componentId, reason });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const refreshed = await getTrip(db, input.envelopeId);
|
|
89
|
+
const components = refreshed?.components ?? [...componentsById.values()];
|
|
90
|
+
const envelope = await maybeCancelEnvelope(db, preview.envelope, components);
|
|
91
|
+
const finalTrip = envelope.status === preview.envelope.status ? refreshed : await getTrip(db, input.envelopeId);
|
|
92
|
+
return {
|
|
93
|
+
envelope,
|
|
94
|
+
components: finalTrip?.components ?? components,
|
|
95
|
+
preview: {
|
|
96
|
+
...preview.preview,
|
|
97
|
+
staffActionRequired: preview.preview.staffActionRequired || remediation.length > 0,
|
|
98
|
+
},
|
|
99
|
+
cancelled,
|
|
100
|
+
remediation,
|
|
101
|
+
skipped,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function selectComponentsForCancellation(trip, componentIds) {
|
|
105
|
+
if (!componentIds || componentIds.length === 0) {
|
|
106
|
+
return trip.components.filter((component) => !isTerminalTripComponentStatus(component.status));
|
|
107
|
+
}
|
|
108
|
+
const componentsById = new Map(trip.components.map((component) => [component.id, component]));
|
|
109
|
+
const selected = [];
|
|
110
|
+
for (const componentId of componentIds) {
|
|
111
|
+
const component = componentsById.get(componentId);
|
|
112
|
+
if (!component) {
|
|
113
|
+
throw new TripsInvariantError(`Trip component ${componentId} was not found on envelope ${trip.envelope.id}`);
|
|
114
|
+
}
|
|
115
|
+
selected.push(component);
|
|
116
|
+
}
|
|
117
|
+
return selected;
|
|
118
|
+
}
|
|
119
|
+
async function previewComponentCancellation(envelope, component, input, requestedAt, deps) {
|
|
120
|
+
if (isTerminalTripComponentStatus(component.status)) {
|
|
121
|
+
return componentCancellationPreview(component, {
|
|
122
|
+
action: "no_op",
|
|
123
|
+
staffActionRequired: false,
|
|
124
|
+
reason: `already_${component.status}`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
if (canCancelComponentLocally(component)) {
|
|
128
|
+
return componentCancellationPreview(component, {
|
|
129
|
+
action: "cancel",
|
|
130
|
+
staffActionRequired: false,
|
|
131
|
+
reason: "local_component_cancel",
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (deps.previewComponentCancellation) {
|
|
135
|
+
return deps.previewComponentCancellation({
|
|
136
|
+
envelope,
|
|
137
|
+
component,
|
|
138
|
+
reason: input.reason,
|
|
139
|
+
requestedAt,
|
|
140
|
+
request: input.request,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return componentCancellationPreview(component, {
|
|
144
|
+
action: "staff_remediation",
|
|
145
|
+
staffActionRequired: true,
|
|
146
|
+
reason: "cancel_preview_not_configured",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function canCancelComponentLocally(component) {
|
|
150
|
+
if (component.kind === "manual_placeholder" || component.kind === "flight_placeholder") {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
return (component.kind === "catalog_booking" &&
|
|
154
|
+
!hasCommittedComponentReference(component) &&
|
|
155
|
+
(component.status === "draft" ||
|
|
156
|
+
component.status === "priced" ||
|
|
157
|
+
component.status === "unavailable" ||
|
|
158
|
+
component.status === "failed"));
|
|
159
|
+
}
|
|
160
|
+
function componentCancellationPreview(component, overrides) {
|
|
161
|
+
return {
|
|
162
|
+
componentId: component.id,
|
|
163
|
+
currentStatus: component.status,
|
|
164
|
+
refundAmountCents: 0,
|
|
165
|
+
refundCurrency: component.componentCurrency ?? undefined,
|
|
166
|
+
penaltyAmountCents: 0,
|
|
167
|
+
...overrides,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function buildCancellationPreviewAggregate(envelope, selected, componentPreviews) {
|
|
171
|
+
const warnings = new Set();
|
|
172
|
+
let currency = envelope.aggregateCurrency ?? null;
|
|
173
|
+
let estimatedRefundAmountCents = 0;
|
|
174
|
+
let estimatedPenaltyAmountCents = 0;
|
|
175
|
+
for (const item of componentPreviews) {
|
|
176
|
+
if (item.refundCurrency) {
|
|
177
|
+
currency ??= item.refundCurrency;
|
|
178
|
+
if (currency !== item.refundCurrency) {
|
|
179
|
+
warnings.add(`refund_currency_mismatch:${item.refundCurrency}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (!item.refundCurrency || !currency || item.refundCurrency === currency) {
|
|
183
|
+
estimatedRefundAmountCents += item.refundAmountCents ?? 0;
|
|
184
|
+
}
|
|
185
|
+
estimatedPenaltyAmountCents += item.penaltyAmountCents ?? 0;
|
|
186
|
+
if (item.reason && item.action !== "cancel")
|
|
187
|
+
warnings.add(item.reason);
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
envelopeId: envelope.id,
|
|
191
|
+
selectedComponentIds: selected.map((component) => component.id),
|
|
192
|
+
currency,
|
|
193
|
+
estimatedRefundAmountCents,
|
|
194
|
+
estimatedPenaltyAmountCents,
|
|
195
|
+
staffActionRequired: componentPreviews.some((item) => item.staffActionRequired),
|
|
196
|
+
components: componentPreviews,
|
|
197
|
+
warnings: [...warnings],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function localCancellationResult(preview) {
|
|
201
|
+
return {
|
|
202
|
+
status: "cancelled",
|
|
203
|
+
refundAmountCents: preview.refundAmountCents,
|
|
204
|
+
refundCurrency: preview.refundCurrency,
|
|
205
|
+
snapshot: preview.snapshot,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
async function markComponentCancelled(db, component, preview) {
|
|
209
|
+
assertTripComponentCanBeUpdated(component, { status: "cancelled" });
|
|
210
|
+
const cancellationSnapshot = {
|
|
211
|
+
action: preview.action,
|
|
212
|
+
refundAmountCents: preview.refundAmountCents ?? 0,
|
|
213
|
+
refundCurrency: preview.refundCurrency ?? component.componentCurrency ?? null,
|
|
214
|
+
penaltyAmountCents: preview.penaltyAmountCents ?? 0,
|
|
215
|
+
supplierCancellationDeadline: preview.supplierCancellationDeadline ?? null,
|
|
216
|
+
policySummary: preview.policySummary ?? null,
|
|
217
|
+
snapshot: preview.snapshot ?? null,
|
|
218
|
+
};
|
|
219
|
+
const [updated] = (await db
|
|
220
|
+
.update(tripComponents)
|
|
221
|
+
.set({
|
|
222
|
+
status: "cancelled",
|
|
223
|
+
cancellationSnapshot,
|
|
224
|
+
updatedAt: new Date(),
|
|
225
|
+
})
|
|
226
|
+
.where(eq(tripComponents.id, component.id))
|
|
227
|
+
.returning());
|
|
228
|
+
if (!updated) {
|
|
229
|
+
throw new Error(`markComponentCancelled: update returned no row for ${component.id}`);
|
|
230
|
+
}
|
|
231
|
+
await createComponentEvent(db, {
|
|
232
|
+
envelopeId: updated.envelopeId,
|
|
233
|
+
componentId: updated.id,
|
|
234
|
+
eventType: "cancelled",
|
|
235
|
+
fromStatus: component.status,
|
|
236
|
+
toStatus: updated.status,
|
|
237
|
+
payload: cancellationSnapshot,
|
|
238
|
+
});
|
|
239
|
+
return updated;
|
|
240
|
+
}
|
|
241
|
+
async function maybeCancelEnvelope(db, envelope, components) {
|
|
242
|
+
const hasActiveComponent = components.some((component) => component.status !== "cancelled" && component.status !== "removed");
|
|
243
|
+
if (hasActiveComponent || envelope.status === "cancelled")
|
|
244
|
+
return envelope;
|
|
245
|
+
const [updated] = (await db
|
|
246
|
+
.update(tripEnvelopes)
|
|
247
|
+
.set({ status: "cancelled", updatedAt: new Date() })
|
|
248
|
+
.where(eq(tripEnvelopes.id, envelope.id))
|
|
249
|
+
.returning());
|
|
250
|
+
return updated ?? envelope;
|
|
251
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AnyDrizzleDb } from "@voyant-travel/db";
|
|
2
|
+
import type { CompleteTripCheckoutInput, CompleteTripCheckoutResult, StartCheckoutDeps, StartCheckoutResult } from "./service-types.js";
|
|
3
|
+
import type { StartTripCheckoutInput } from "./validation.js";
|
|
4
|
+
export declare function startCheckout(db: AnyDrizzleDb, input: StartTripCheckoutInput, deps: StartCheckoutDeps): Promise<StartCheckoutResult>;
|
|
5
|
+
export declare function completeTripCheckout(db: AnyDrizzleDb, input: CompleteTripCheckoutInput): Promise<CompleteTripCheckoutResult | null>;
|
|
6
|
+
//# sourceMappingURL=service-checkout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-checkout.d.ts","sourceRoot":"","sources":["../src/service-checkout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAqBrD,OAAO,KAAK,EACV,yBAAyB,EACzB,0BAA0B,EAE1B,iBAAiB,EACjB,mBAAmB,EAKpB,MAAM,oBAAoB,CAAA;AAG3B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAK7D,wBAAsB,aAAa,CACjC,EAAE,EAAE,YAAY,EAChB,KAAK,EAAE,sBAAsB,EAC7B,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAyI9B;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,YAAY,EAChB,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC,CAkG5C"}
|