@ticketboothapp/booking 1.2.166 → 1.2.168
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/package.json +1 -1
- package/src/components/booking/AdminChangeBookingContent.tsx +15 -1
- package/src/components/booking/AdminChangeBookingFlow.tsx +274 -94
- package/src/components/booking/AdminChangeCheckoutPanel.tsx +49 -13
- package/src/components/booking/AdminChangePricingPanel.tsx +400 -134
- package/src/components/booking/AdminChangePromoEditor.tsx +97 -0
- package/src/components/booking/AdminChangeReceiptComparison.tsx +172 -147
- package/src/components/booking/ChangeBookingFlow.tsx +6 -3
- package/src/components/booking/ChangeBookingQuoteStatusPlaceholder.tsx +7 -2
- package/src/components/booking/ChangeBookingSelectionControlsPanel.tsx +6 -0
- package/src/components/booking/ChangeBookingTicketsAndAddOnsPanel.tsx +80 -0
- package/src/components/booking/NewBookingFlow.tsx +14 -2
- package/src/components/booking/PickupLocationSelector.module.css +22 -0
- package/src/components/booking/PickupLocationSelector.tsx +3 -3
- package/src/components/booking/PriceBreakdown.tsx +66 -15
- package/src/components/booking/PriceSummary.tsx +16 -0
- package/src/components/booking/PrivateShuttleBookingFlow.tsx +52 -14
- package/src/components/booking/PrivateShuttleCheckoutSection.tsx +95 -54
- package/src/components/booking/admin-adjustment-components.ts +44 -0
- package/src/components/booking/admin-adjustment-tax-behavior.ts +50 -0
- package/src/components/booking/admin-change-flow-state-helpers.ts +10 -0
- package/src/components/booking/admin-change-provider-payload.ts +12 -1
- package/src/components/booking/admin-change-quote-request-key.ts +41 -1
- package/src/components/booking/admin-change-receipt-lines.ts +49 -0
- package/src/components/booking/admin-change-v2-quote-request.ts +109 -0
- package/src/components/booking/admin-refund-decision-context.ts +122 -0
- package/src/components/booking/admin-refund-disposition.ts +71 -2
- package/src/components/booking/availability-date-selection.ts +16 -0
- package/src/components/booking/booking-flow-types.ts +4 -0
- package/src/components/booking/booking-flow.css +5 -0
- package/src/components/booking/change-booking-error-message.ts +137 -0
- package/src/components/booking/change-booking-flow-helpers.ts +77 -2
- package/src/components/booking/change-booking-quote-guards.ts +4 -1
- package/src/components/booking/change-booking-quote-state.ts +44 -1
- package/src/components/booking/incompatible-add-on-selections.ts +19 -0
- package/src/components/booking/private-shuttle-availability.ts +14 -0
- package/src/components/booking/private-shuttle-cancellation-policy.ts +9 -0
- package/src/components/booking/private-shuttle-checkout-controller.ts +5 -0
- package/src/components/booking/private-shuttle-provider-change-runner.ts +21 -0
- package/src/components/booking/private-shuttle-reservation-runner.ts +5 -3
- package/src/components/booking/provider-dashboard-change-booking.ts +18 -2
- package/src/components/booking/use-standard-booking-availability.ts +5 -2
- package/src/components/booking/useAdminChangeCheckoutController.ts +56 -1
- package/src/components/booking/useAdminChangeProductReset.ts +6 -0
- package/src/components/booking/useAdminChangeProtectedPricing.ts +4 -0
- package/src/components/booking/useAdminChangeQuoteDisplayState.tsx +8 -6
- package/src/components/booking/useAdminChangeQuotePreview.ts +146 -61
- package/src/components/booking/useAdminCustomReceiptLines.ts +171 -10
- package/src/components/booking/useAdminProviderPricingAdjustments.ts +50 -19
- package/src/components/booking/useBookingAvailabilityAddOns.ts +26 -4
- package/src/components/booking/useBookingPromoAndQuantityController.ts +16 -2
- package/src/components/booking/useChangeBookingAddOnFloorController.ts +15 -9
- package/src/components/booking/useChangeBookingAutoSelections.ts +60 -17
- package/src/components/booking/useChangeBookingInitialHydration.ts +10 -7
- package/src/components/booking/useChangeBookingQuotePreview.ts +3 -1
- package/src/components/booking/useChangeBookingSelectionDetails.ts +23 -17
- package/src/components/booking/useStandardBookingAutoSelections.ts +55 -7
- package/src/lib/booking/booking-cutoffs.ts +22 -0
- package/src/lib/booking/change-booking-server-preview.ts +92 -5
- package/src/lib/booking/change-flow-pricing.ts +12 -0
- package/src/lib/booking/i18n/messages/en.json +1 -0
- package/src/lib/booking/i18n/messages/fr.json +1 -0
- package/src/lib/booking-api.ts +76 -1
- package/test/change-booking-helpers.test.ts +1319 -7
- package/src/components/booking/useAdminChangePricingDebugPanel.tsx +0 -178
|
@@ -55,6 +55,28 @@ export function filterAvailabilitiesAfterPublicBookingCutoff<T extends { dateTim
|
|
|
55
55
|
);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Admin surfaces intentionally ignore the public advance-booking cutoff, but an
|
|
60
|
+
* availability whose actual start time has passed is no longer bookable by the
|
|
61
|
+
* API. Keep that narrower rule separate so admin and public behavior cannot
|
|
62
|
+
* accidentally inherit one another's cutoff policy.
|
|
63
|
+
*/
|
|
64
|
+
export function isAvailabilityAfterCurrentTime(
|
|
65
|
+
availability: { dateTime: string },
|
|
66
|
+
now: Date = new Date(),
|
|
67
|
+
): boolean {
|
|
68
|
+
return isAvailabilityAfterPublicBookingCutoff(availability, now, 0);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function filterAvailabilitiesAfterCurrentTime<T extends { dateTime: string }>(
|
|
72
|
+
availabilities: T[],
|
|
73
|
+
now: Date = new Date(),
|
|
74
|
+
): T[] {
|
|
75
|
+
return availabilities.filter((availability) =>
|
|
76
|
+
isAvailabilityAfterCurrentTime(availability, now),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
58
80
|
export function isDateWithinPublicPickupUnknownCutoff(date: Date, now: Date = new Date()): boolean {
|
|
59
81
|
return date.getTime() <= now.getTime() + PUBLIC_PICKUP_UNKNOWN_CUTOFF_MS;
|
|
60
82
|
}
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
*
|
|
5
5
|
* See `CHANGE_BOOKING_BE_HANDOFF.md` in this package for fields the API should populate.
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
AdminAmendmentOperationSnapshot,
|
|
9
|
+
ChangeBookingQuoteResponse,
|
|
10
|
+
} from '../booking-api';
|
|
8
11
|
import type { PricingV2QuoteLineSnapshot } from '../booking-api';
|
|
9
12
|
import type { PriceBasisSnapshot } from '../booking-api';
|
|
10
13
|
import type { PriceSummaryLine } from '../../components/booking/PriceSummary';
|
|
@@ -24,6 +27,8 @@ export interface ChangeBookingServerPreview {
|
|
|
24
27
|
completeness: ChangeBookingPreviewCompleteness;
|
|
25
28
|
/** Rows for {@link PriceSummary} / checkout — from quote receipt line items + BE extensions. */
|
|
26
29
|
priceSummaryLines: PriceSummaryLine[];
|
|
30
|
+
/** Full resulting receipt rows, when the amendment quote supplies them. */
|
|
31
|
+
resultingReceiptLines?: PriceSummaryLine[];
|
|
27
32
|
/** From quote — per-category unit prices (major units) for ticket picker when BE sends them. */
|
|
28
33
|
ticketUnitPriceByCategory?: Record<string, number>;
|
|
29
34
|
cancellationPolicyFeeByPolicyId?: Record<string, number>;
|
|
@@ -207,6 +212,7 @@ export function mapQuoteLineItemsToPriceSummaryLines(
|
|
|
207
212
|
: type;
|
|
208
213
|
out.push({
|
|
209
214
|
kind: 'ticket',
|
|
215
|
+
operationId: isPricingV2Line(item) ? item.metadata?.operationId : undefined,
|
|
210
216
|
category,
|
|
211
217
|
qty: qty > 0 ? qty : 1,
|
|
212
218
|
itemTotal: amount,
|
|
@@ -228,6 +234,7 @@ export function mapQuoteLineItemsToPriceSummaryLines(
|
|
|
228
234
|
) {
|
|
229
235
|
out.push({
|
|
230
236
|
kind: 'ticket',
|
|
237
|
+
operationId: isPricingV2Line(item) ? item.metadata?.operationId : undefined,
|
|
231
238
|
category: lettersOnly,
|
|
232
239
|
qty,
|
|
233
240
|
itemTotal: amount,
|
|
@@ -249,6 +256,74 @@ export function mapQuoteLineItemsToPriceSummaryLines(
|
|
|
249
256
|
return out;
|
|
250
257
|
}
|
|
251
258
|
|
|
259
|
+
/** Clarify ticket deltas caused by moving a booking without changing receipt semantics. */
|
|
260
|
+
export function labelAdminAmendmentPriceSummaryLines(
|
|
261
|
+
lines: PriceSummaryLine[],
|
|
262
|
+
operations: readonly AdminAmendmentOperationSnapshot[],
|
|
263
|
+
originalLines: readonly PriceSummaryLine[] = [],
|
|
264
|
+
): PriceSummaryLine[] {
|
|
265
|
+
const movedServiceOperations = new Map(
|
|
266
|
+
operations
|
|
267
|
+
.filter((operation) => operation.type.trim().toUpperCase() === 'MOVE_SERVICE')
|
|
268
|
+
.map((operation) => [operation.operationId, operation] as const),
|
|
269
|
+
);
|
|
270
|
+
if (movedServiceOperations.size === 0) return lines;
|
|
271
|
+
|
|
272
|
+
const normalizedTicketCategory = (category: string) => category
|
|
273
|
+
.replace(/^refund\s*[·:-]\s*/i, '')
|
|
274
|
+
.replace(/\s*[—-]\s*(?:new date|tour) price difference\s*$/i, '')
|
|
275
|
+
.trim()
|
|
276
|
+
.toUpperCase();
|
|
277
|
+
|
|
278
|
+
return lines.map((line) => {
|
|
279
|
+
const moveOperation = line.kind === 'ticket' && line.operationId
|
|
280
|
+
? movedServiceOperations.get(line.operationId)
|
|
281
|
+
: undefined;
|
|
282
|
+
if (
|
|
283
|
+
line.kind !== 'ticket' ||
|
|
284
|
+
!moveOperation ||
|
|
285
|
+
/(?:new date|tour) price difference/i.test(line.category)
|
|
286
|
+
) {
|
|
287
|
+
return line;
|
|
288
|
+
}
|
|
289
|
+
const changesTour = moveOperation.changedFields?.some(
|
|
290
|
+
(field) => field.trim().toLowerCase() === 'productid',
|
|
291
|
+
) ?? false;
|
|
292
|
+
const priceDifferenceLabel = changesTour
|
|
293
|
+
? 'tour price difference'
|
|
294
|
+
: 'new date price difference';
|
|
295
|
+
const originalLine = originalLines.find(
|
|
296
|
+
(candidate) => candidate.kind === 'ticket' &&
|
|
297
|
+
normalizedTicketCategory(candidate.category) === normalizedTicketCategory(line.category),
|
|
298
|
+
);
|
|
299
|
+
const previousUnitAmount = originalLine?.kind === 'ticket' && originalLine.qty > 0
|
|
300
|
+
? roundMoney(originalLine.itemTotal / originalLine.qty)
|
|
301
|
+
: null;
|
|
302
|
+
const differenceUnitAmount = line.qty > 0
|
|
303
|
+
? roundMoney(line.itemTotal / line.qty)
|
|
304
|
+
: null;
|
|
305
|
+
const ticketLabel = normalizedTicketCategory(line.category).toLowerCase();
|
|
306
|
+
return {
|
|
307
|
+
...line,
|
|
308
|
+
category: `${line.category} — ${priceDifferenceLabel}`,
|
|
309
|
+
...(previousUnitAmount != null && differenceUnitAmount != null
|
|
310
|
+
? {
|
|
311
|
+
unitPriceComparison: {
|
|
312
|
+
previousLabel: `Existing ${ticketLabel} price`,
|
|
313
|
+
updatedLabel: changesTour
|
|
314
|
+
? `New-tour ${ticketLabel} price`
|
|
315
|
+
: `New-date ${ticketLabel} price`,
|
|
316
|
+
differenceLabel: `Difference per ${ticketLabel}`,
|
|
317
|
+
previousUnitAmount,
|
|
318
|
+
updatedUnitAmount: roundMoney(previousUnitAmount + differenceUnitAmount),
|
|
319
|
+
differenceUnitAmount,
|
|
320
|
+
},
|
|
321
|
+
}
|
|
322
|
+
: {}),
|
|
323
|
+
};
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
252
327
|
function signedAmountDueFromQuote(quote: ChangeBookingQuoteResponse): number {
|
|
253
328
|
const pricingQuote = quote.pricingQuote ?? quote.quote;
|
|
254
329
|
// Prefer explicit settlement fields over balanceDelta. Receipt-total math and balanceDelta
|
|
@@ -280,10 +355,21 @@ export function buildChangeBookingServerPreview(
|
|
|
280
355
|
const paymentCreditTotal = pricingQuote?.paymentCreditTotal ?? quote.paymentCreditTotal;
|
|
281
356
|
|
|
282
357
|
const rawLines = lineItemsForSummary(quote);
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
358
|
+
const mappedLines = rawLines && rawLines.length > 0
|
|
359
|
+
? mapQuoteLineItemsToPriceSummaryLines(rawLines)
|
|
360
|
+
: [];
|
|
361
|
+
const priceSummaryLines = withPaymentCreditSummaryLine(
|
|
362
|
+
labelAdminAmendmentPriceSummaryLines(
|
|
363
|
+
mappedLines,
|
|
364
|
+
pricingQuote?.amendment?.operations ?? [],
|
|
365
|
+
mapQuoteLineItemsToPriceSummaryLines(quote.originalReceipt?.lineItems),
|
|
366
|
+
),
|
|
367
|
+
paymentCreditTotal,
|
|
368
|
+
);
|
|
369
|
+
const resultingReceiptLines = pricingQuote?.amendment?.updatedReceipt?.lineItems;
|
|
370
|
+
const resultingReceiptPriceSummaryLines = resultingReceiptLines?.length
|
|
371
|
+
? mapQuoteLineItemsToPriceSummaryLines(resultingReceiptLines)
|
|
372
|
+
: undefined;
|
|
287
373
|
|
|
288
374
|
const completeness: ChangeBookingPreviewCompleteness = priceSummaryLines.length > 0 ? 'full' : 'totals_only';
|
|
289
375
|
|
|
@@ -295,6 +381,7 @@ export function buildChangeBookingServerPreview(
|
|
|
295
381
|
totalNewBooking: totals.total,
|
|
296
382
|
completeness,
|
|
297
383
|
priceSummaryLines,
|
|
384
|
+
resultingReceiptLines: resultingReceiptPriceSummaryLines,
|
|
298
385
|
ticketUnitPriceByCategory: quote.ticketUnitPriceByCategory,
|
|
299
386
|
cancellationPolicyFeeByPolicyId: quote.cancellationPolicyFeeByPolicyId,
|
|
300
387
|
returnOptionPriceByReturnAvailabilityId: quote.returnOptionPriceByReturnAvailabilityId,
|
|
@@ -255,6 +255,12 @@ export interface ChangeQuoteUiSlice {
|
|
|
255
255
|
quoteExpiresAt?: string;
|
|
256
256
|
pricingVersion?: string;
|
|
257
257
|
quotedTotal?: number;
|
|
258
|
+
/** Settled cash after completed refunds, calculated by the backend from payment records. */
|
|
259
|
+
amountPreviouslyPaid?: number;
|
|
260
|
+
/** Non-cash value allocated before and after this amendment. */
|
|
261
|
+
currentPaymentCreditTotal?: number;
|
|
262
|
+
projectedPaymentCreditTotal?: number;
|
|
263
|
+
refundCandidate?: number;
|
|
258
264
|
paymentCreditTotal?: number;
|
|
259
265
|
refundDecisionOperations?: ChangeBookingQuoteResponse['refundDecisionOperations'];
|
|
260
266
|
returnPriceTreatmentOperations?: ChangeBookingQuoteResponse['returnPriceTreatmentOperations'];
|
|
@@ -296,6 +302,12 @@ export function sliceChangeQuoteForUi(
|
|
|
296
302
|
quoteExpiresAt: pricingQuote?.expiresAt ?? quote.expiresAt,
|
|
297
303
|
pricingVersion: pricingQuote?.pricingVersion ?? undefined,
|
|
298
304
|
quotedTotal: pricingQuote?.payableTotal ?? pricingQuote?.totalAmount ?? quote.proposed?.total ?? quote.newReceipt?.total,
|
|
305
|
+
amountPreviouslyPaid:
|
|
306
|
+
pricingQuote?.amountPreviouslyPaid ?? quote.amountPreviouslyPaid ?? undefined,
|
|
307
|
+
currentPaymentCreditTotal: quote.originalReceipt?.paymentCreditTotal ?? 0,
|
|
308
|
+
projectedPaymentCreditTotal:
|
|
309
|
+
pricingQuote?.paymentCreditTotal ?? quote.paymentCreditTotal ?? 0,
|
|
310
|
+
refundCandidate: quote.refundCandidate ?? pricingQuote?.refundCandidate ?? 0,
|
|
299
311
|
paymentCreditTotal: pricingQuote?.paymentCreditTotal ?? quote.paymentCreditTotal,
|
|
300
312
|
refundDecisionOperations: quote.refundDecisionOperations,
|
|
301
313
|
returnPriceTreatmentOperations: quote.returnPriceTreatmentOperations,
|
|
@@ -119,6 +119,7 @@
|
|
|
119
119
|
"rounding": "Rounding",
|
|
120
120
|
"deposit": "Deposit",
|
|
121
121
|
"totalOwedForBookingChange": "Total owed for booking difference",
|
|
122
|
+
"changeToBookingTotal": "Change to booking total",
|
|
122
123
|
"subtotal": "Subtotal",
|
|
123
124
|
"tax": "Taxes and fees",
|
|
124
125
|
"returnOption": "Return Option",
|
|
@@ -119,6 +119,7 @@
|
|
|
119
119
|
"rounding": "Arrondi",
|
|
120
120
|
"deposit": "Acompte",
|
|
121
121
|
"totalOwedForBookingChange": "Montant dû pour le changement de réservation",
|
|
122
|
+
"changeToBookingTotal": "Modification du total de la réservation",
|
|
122
123
|
"subtotal": "Sous-total",
|
|
123
124
|
"tax": "Taxes et frais",
|
|
124
125
|
"returnOption": "Option de retour",
|
package/src/lib/booking-api.ts
CHANGED
|
@@ -1102,7 +1102,7 @@ export async function getPromoDiscount(
|
|
|
1102
1102
|
|
|
1103
1103
|
export async function getAddOns(
|
|
1104
1104
|
companyId: string,
|
|
1105
|
-
options?: { productOptionId?: string; preCheckout?: boolean }
|
|
1105
|
+
options?: { productOptionId?: string; preCheckout?: boolean; dateTime?: string }
|
|
1106
1106
|
): Promise<AddOn[]> {
|
|
1107
1107
|
const params = new URLSearchParams({ companyId });
|
|
1108
1108
|
if (options?.productOptionId) {
|
|
@@ -1110,6 +1110,7 @@ export async function getAddOns(
|
|
|
1110
1110
|
if (po) params.set('productOptionId', po);
|
|
1111
1111
|
}
|
|
1112
1112
|
if (options?.preCheckout !== undefined) params.set('preCheckout', String(options.preCheckout));
|
|
1113
|
+
if (options?.dateTime?.trim()) params.set('dateTime', options.dateTime.trim());
|
|
1113
1114
|
const res = await fetchBookingGetWithRetry(`${API_BASE}/1/add-ons?${params}`);
|
|
1114
1115
|
if (!res.ok) {
|
|
1115
1116
|
const err = await res.json();
|
|
@@ -1251,6 +1252,8 @@ export type AdminAmendmentRefundDisposition =
|
|
|
1251
1252
|
| 'PENDING_REFUND'
|
|
1252
1253
|
| 'NO_REFUND';
|
|
1253
1254
|
|
|
1255
|
+
export type AdminPromoApplicationScope = 'CHANGE_ONLY' | 'ENTIRE_BOOKING';
|
|
1256
|
+
|
|
1254
1257
|
export interface AdminAmendmentOperationSnapshot {
|
|
1255
1258
|
operationId: string;
|
|
1256
1259
|
type: string;
|
|
@@ -1262,6 +1265,27 @@ export interface AdminAmendmentOperationSnapshot {
|
|
|
1262
1265
|
metadata?: Record<string, string>;
|
|
1263
1266
|
}
|
|
1264
1267
|
|
|
1268
|
+
export interface AdminAmendmentActiveAdjustmentSnapshot {
|
|
1269
|
+
adjustmentId: string;
|
|
1270
|
+
operationId: string;
|
|
1271
|
+
mode: 'FIXED_AMOUNT' | 'PERCENTAGE_DISCOUNT' | 'PERCENTAGE_SURCHARGE';
|
|
1272
|
+
scope:
|
|
1273
|
+
| 'POSITIVE_AMENDMENT_VALUE'
|
|
1274
|
+
| 'CURRENT_ACTIVE_TICKETS'
|
|
1275
|
+
| 'CURRENT_ACTIVE_BOOKING'
|
|
1276
|
+
| 'SELECTED_COMPONENTS';
|
|
1277
|
+
basisCents: number;
|
|
1278
|
+
percentageBasisPoints?: number | null;
|
|
1279
|
+
amountCents: number;
|
|
1280
|
+
taxDeltaCents: number;
|
|
1281
|
+
affectedComponentIds: string[];
|
|
1282
|
+
taxBehavior: 'NON_TAXABLE' | 'TAXABLE' | 'PRE_TAX_DISCOUNT' | 'POST_TAX_DISCOUNT';
|
|
1283
|
+
reasonCode: string;
|
|
1284
|
+
referenceCode?: string | null;
|
|
1285
|
+
reason?: string | null;
|
|
1286
|
+
createdAt: string;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1265
1289
|
export interface ChangeBookingQuoteRequest {
|
|
1266
1290
|
bookingReference: string;
|
|
1267
1291
|
lastName: string;
|
|
@@ -1276,6 +1300,9 @@ export interface ChangeBookingQuoteRequest {
|
|
|
1276
1300
|
newReturnAvailabilityId?: string | null;
|
|
1277
1301
|
newPassengerCounts?: Array<{ category: string; count: number }>;
|
|
1278
1302
|
newAddOnSelections?: Array<{ addOnId: string; variantId?: string; quantity?: number }>;
|
|
1303
|
+
/** Explicit promo intent for this amendment. Historical booking promos are never implied. */
|
|
1304
|
+
promoCode?: string | null;
|
|
1305
|
+
promoApplicationScope?: AdminPromoApplicationScope | null;
|
|
1279
1306
|
/** Full new-booking total shown in the UI; server verifies within tolerance then uses this for the session so charge matches screen. */
|
|
1280
1307
|
clientProposedTotal?: number;
|
|
1281
1308
|
/**
|
|
@@ -1293,6 +1320,25 @@ export interface ChangeBookingQuoteRequest {
|
|
|
1293
1320
|
} | null;
|
|
1294
1321
|
/** Explicit admin treatment for server-classified removed paid value. Never defaulted by the client. */
|
|
1295
1322
|
refundDispositionsByOperationId?: Record<string, AdminAmendmentRefundDisposition>;
|
|
1323
|
+
/** Server-resolved admin adjustments to append to the immutable amendment ledger. */
|
|
1324
|
+
structuredAdjustments?: Array<{
|
|
1325
|
+
adjustmentId: string;
|
|
1326
|
+
mode: 'FIXED_AMOUNT' | 'PERCENTAGE_DISCOUNT' | 'PERCENTAGE_SURCHARGE';
|
|
1327
|
+
fixedAmount?: number | null;
|
|
1328
|
+
percentageBasisPoints?: number | null;
|
|
1329
|
+
scope:
|
|
1330
|
+
| 'POSITIVE_AMENDMENT_VALUE'
|
|
1331
|
+
| 'CURRENT_ACTIVE_TICKETS'
|
|
1332
|
+
| 'CURRENT_ACTIVE_BOOKING'
|
|
1333
|
+
| 'SELECTED_COMPONENTS';
|
|
1334
|
+
selectedComponentIds?: string[];
|
|
1335
|
+
taxBehavior: 'NON_TAXABLE' | 'TAXABLE' | 'PRE_TAX_DISCOUNT' | 'POST_TAX_DISCOUNT';
|
|
1336
|
+
reasonCode: string;
|
|
1337
|
+
referenceCode?: string | null;
|
|
1338
|
+
reason?: string | null;
|
|
1339
|
+
}>;
|
|
1340
|
+
/** Existing immutable adjustment ids to reverse as part of this amendment. */
|
|
1341
|
+
reverseAdjustmentIds?: string[];
|
|
1296
1342
|
}
|
|
1297
1343
|
|
|
1298
1344
|
/** FE-authored receipt payload for admin quote path (major units, booking currency). */
|
|
@@ -1323,6 +1369,7 @@ export interface AdminChangeBookingQuoteV2Data {
|
|
|
1323
1369
|
oldReceipt: {
|
|
1324
1370
|
currency?: string | null;
|
|
1325
1371
|
grossSubtotal?: number | null;
|
|
1372
|
+
paymentCreditTotal?: number | null;
|
|
1326
1373
|
taxAmount?: number | null;
|
|
1327
1374
|
payableTotal?: number | null;
|
|
1328
1375
|
lines?: PricingV2QuoteLineSnapshot[] | null;
|
|
@@ -1342,6 +1389,7 @@ export interface AdminChangeBookingQuoteV2Data {
|
|
|
1342
1389
|
returnPriceTreatmentOperations?: AdminAmendmentOperationSnapshot[];
|
|
1343
1390
|
allowedRefundDispositionsByOperationId?: Record<string, AdminAmendmentRefundDisposition[]>;
|
|
1344
1391
|
removedValueByOperationId?: Record<string, number>;
|
|
1392
|
+
reversibleAdjustments?: AdminAmendmentActiveAdjustmentSnapshot[];
|
|
1345
1393
|
}
|
|
1346
1394
|
|
|
1347
1395
|
export interface ChangeBookingQuoteReceipt {
|
|
@@ -1349,11 +1397,18 @@ export interface ChangeBookingQuoteReceipt {
|
|
|
1349
1397
|
tax?: number;
|
|
1350
1398
|
total: number;
|
|
1351
1399
|
currency?: string;
|
|
1400
|
+
/** Non-cash value currently allocated to the booking, such as gift-card credit. */
|
|
1401
|
+
paymentCreditTotal?: number;
|
|
1352
1402
|
lineItems?: Array<{
|
|
1353
1403
|
label?: string;
|
|
1354
1404
|
amount?: number;
|
|
1355
1405
|
type?: string;
|
|
1356
1406
|
quantity?: number;
|
|
1407
|
+
reference?: string | null;
|
|
1408
|
+
identity?: {
|
|
1409
|
+
componentId?: string | null;
|
|
1410
|
+
} | null;
|
|
1411
|
+
priceBasis?: PriceBasisSnapshot | null;
|
|
1357
1412
|
}>;
|
|
1358
1413
|
}
|
|
1359
1414
|
|
|
@@ -1446,6 +1501,7 @@ export interface ChangeBookingQuoteResponse {
|
|
|
1446
1501
|
quote?: PricingV2QuoteSnapshot | null;
|
|
1447
1502
|
paymentCreditTotal?: number;
|
|
1448
1503
|
balanceDelta?: number;
|
|
1504
|
+
amountPreviouslyPaid?: number;
|
|
1449
1505
|
amountToCharge?: number;
|
|
1450
1506
|
refundCandidate?: number;
|
|
1451
1507
|
/**
|
|
@@ -1489,6 +1545,8 @@ export interface ChangeBookingQuoteResponse {
|
|
|
1489
1545
|
allowedRefundDispositionsByOperationId?: Record<string, AdminAmendmentRefundDisposition[]>;
|
|
1490
1546
|
/** Original paid basis plus allocated refundable tax for each removal operation. */
|
|
1491
1547
|
removedValueByOperationId?: Record<string, number>;
|
|
1548
|
+
/** Active immutable adjustments that an admin may edit (reverse + replace) or remove. */
|
|
1549
|
+
reversibleAdjustments?: AdminAmendmentActiveAdjustmentSnapshot[];
|
|
1492
1550
|
/** When price check fails / disagrees: optional breakdown for UI line-vs-line comparison. */
|
|
1493
1551
|
pricingDriftDetail?: ChangeBookingQuotePricingDriftDetail;
|
|
1494
1552
|
/** Optional BE debug: receipt-floor vs catalog ticket math (same-parent Rule A/B). */
|
|
@@ -1683,6 +1741,7 @@ export function mapAdminChangeBookingQuoteV2Data(
|
|
|
1683
1741
|
pricingQuote: newQuote,
|
|
1684
1742
|
quote: newQuote,
|
|
1685
1743
|
balanceDelta: data.balanceDelta ?? newQuote?.balanceDelta ?? undefined,
|
|
1744
|
+
amountPreviouslyPaid: newQuote?.amountPreviouslyPaid ?? undefined,
|
|
1686
1745
|
amountToCharge: data.amountToCharge ?? newQuote?.amountToCharge ?? undefined,
|
|
1687
1746
|
refundCandidate: data.refundCandidate ?? newQuote?.refundCandidate ?? undefined,
|
|
1688
1747
|
priceDiff: data.balanceDelta ?? newQuote?.balanceDelta ?? 0,
|
|
@@ -1694,16 +1753,19 @@ export function mapAdminChangeBookingQuoteV2Data(
|
|
|
1694
1753
|
allowedRefundDispositionsByOperationId:
|
|
1695
1754
|
data.allowedRefundDispositionsByOperationId ?? {},
|
|
1696
1755
|
removedValueByOperationId: data.removedValueByOperationId ?? {},
|
|
1756
|
+
reversibleAdjustments: data.reversibleAdjustments ?? [],
|
|
1697
1757
|
originalReceipt: {
|
|
1698
1758
|
subtotal: oldReceipt.grossSubtotal ?? undefined,
|
|
1699
1759
|
tax: oldReceipt.taxAmount ?? undefined,
|
|
1700
1760
|
total: oldReceipt.payableTotal ?? 0,
|
|
1701
1761
|
currency: oldReceipt.currency ?? undefined,
|
|
1762
|
+
paymentCreditTotal: oldReceipt.paymentCreditTotal ?? 0,
|
|
1702
1763
|
lineItems: oldReceipt.lines?.map((line) => ({
|
|
1703
1764
|
label: line.label ?? undefined,
|
|
1704
1765
|
amount: line.amount ?? undefined,
|
|
1705
1766
|
type: line.type ?? undefined,
|
|
1706
1767
|
quantity: line.quantity ?? undefined,
|
|
1768
|
+
priceBasis: line.priceBasis ?? undefined,
|
|
1707
1769
|
})),
|
|
1708
1770
|
},
|
|
1709
1771
|
};
|
|
@@ -2239,6 +2301,19 @@ export interface PricingV2QuoteSnapshot {
|
|
|
2239
2301
|
bookingReference?: string | null;
|
|
2240
2302
|
reservationReference?: string | null;
|
|
2241
2303
|
relatedChangeIntentId?: string | null;
|
|
2304
|
+
amendment?: {
|
|
2305
|
+
operations?: AdminAmendmentOperationSnapshot[] | null;
|
|
2306
|
+
updatedReceipt?: ChangeBookingQuoteReceipt | null;
|
|
2307
|
+
targetState?: {
|
|
2308
|
+
nonPriceState?: {
|
|
2309
|
+
/**
|
|
2310
|
+
* The amendment service stores the canonical itinerary as JSON in non-price state.
|
|
2311
|
+
* Accept an array as well so clients remain compatible if the API stops stringifying it.
|
|
2312
|
+
*/
|
|
2313
|
+
itinerary?: string | ItineraryDisplayStep[] | null;
|
|
2314
|
+
} | null;
|
|
2315
|
+
} | null;
|
|
2316
|
+
} | null;
|
|
2242
2317
|
metadata?: Record<string, string> | null;
|
|
2243
2318
|
[key: string]: unknown;
|
|
2244
2319
|
}
|