@ticketboothapp/booking 1.2.161 → 1.2.162
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/AdminChangeBookingFlow.tsx +6 -1
- package/src/components/booking/admin-change-provider-payload.ts +9 -1
- package/src/components/booking/admin-change-quote-request-key.ts +5 -1
- package/src/components/booking/change-booking-checkout-quote-runner.ts +2 -6
- package/src/components/booking/provider-dashboard-change-booking.ts +5 -0
- package/src/components/booking/useAdminChangeCheckoutController.ts +27 -0
- package/src/components/booking/useAdminChangeQuotePreview.ts +22 -9
- package/src/components/booking/useAdminProviderPricingAdjustments.ts +52 -8
- package/src/components/booking/useChangeBookingQuotePreview.ts +2 -2
- package/src/lib/booking-api.ts +1 -1
- package/test/change-booking-helpers.test.ts +35 -0
package/package.json
CHANGED
|
@@ -691,7 +691,9 @@ export function AdminChangeBookingFlow({
|
|
|
691
691
|
returnAvailabilityId: selectedReturnOption?.returnAvailabilityId ?? null,
|
|
692
692
|
quantities,
|
|
693
693
|
addOnSelections,
|
|
694
|
+
cancellationPolicyId,
|
|
694
695
|
adminCustomReceiptLines,
|
|
696
|
+
structuredAdjustments: adminStructuredAdjustments,
|
|
695
697
|
useAdminFeAuthoritativeQuote,
|
|
696
698
|
}), [
|
|
697
699
|
initialValues?.bookingReference,
|
|
@@ -702,7 +704,9 @@ export function AdminChangeBookingFlow({
|
|
|
702
704
|
selectedReturnOption?.returnAvailabilityId,
|
|
703
705
|
quantities,
|
|
704
706
|
addOnSelections,
|
|
707
|
+
cancellationPolicyId,
|
|
705
708
|
adminCustomReceiptLines,
|
|
709
|
+
adminStructuredAdjustments,
|
|
706
710
|
useAdminFeAuthoritativeQuote,
|
|
707
711
|
]);
|
|
708
712
|
const responseRefundDecisionOperations = latestChangeQuote?.refundDecisionOperations ?? [];
|
|
@@ -782,7 +786,9 @@ export function AdminChangeBookingFlow({
|
|
|
782
786
|
pickupLocationId,
|
|
783
787
|
quantities,
|
|
784
788
|
addOnSelections,
|
|
789
|
+
cancellationPolicyId,
|
|
785
790
|
adminCustomReceiptLines,
|
|
791
|
+
adminStructuredAdjustments,
|
|
786
792
|
refundDispositionsByOperationId: activeRefundDispositionsByOperationId,
|
|
787
793
|
useAdminFeAuthoritativeQuote,
|
|
788
794
|
latestChangeQuote,
|
|
@@ -809,7 +815,6 @@ export function AdminChangeBookingFlow({
|
|
|
809
815
|
changeFlowNewBookingTotal,
|
|
810
816
|
effectiveSubtotalForCheckout,
|
|
811
817
|
effectiveTax,
|
|
812
|
-
adminCustomLinesAsAdditionalAdjustments,
|
|
813
818
|
adminFeAuthoritativeReceipt,
|
|
814
819
|
isTaxIncludedInPrice,
|
|
815
820
|
t,
|
|
@@ -36,6 +36,8 @@ export interface BuildAdminChangeProviderPayloadParams {
|
|
|
36
36
|
refundDispositionsByOperationId: NonNullable<
|
|
37
37
|
ProviderDashboardChangeBookingPayload['refundDispositionsByOperationId']
|
|
38
38
|
>;
|
|
39
|
+
pricingV2QuoteId?: string | null;
|
|
40
|
+
pricingV2QuotedTotal?: number | null;
|
|
39
41
|
providerApplyAuthoritativeReceipt?: ProviderDashboardChangeBookingPayload['authoritativeReceipt'];
|
|
40
42
|
previousPassengerCount: number;
|
|
41
43
|
previousAvailabilityId?: string | null;
|
|
@@ -59,6 +61,8 @@ export function buildAdminChangeProviderPayload({
|
|
|
59
61
|
mergedProviderAdditionalAdjustments,
|
|
60
62
|
adminStructuredAdjustments,
|
|
61
63
|
refundDispositionsByOperationId,
|
|
64
|
+
pricingV2QuoteId,
|
|
65
|
+
pricingV2QuotedTotal,
|
|
62
66
|
providerApplyAuthoritativeReceipt,
|
|
63
67
|
previousPassengerCount,
|
|
64
68
|
previousAvailabilityId,
|
|
@@ -73,6 +77,7 @@ export function buildAdminChangeProviderPayload({
|
|
|
73
77
|
return {
|
|
74
78
|
productId: availabilityProductOptionId,
|
|
75
79
|
parentProductId: product.productId,
|
|
80
|
+
availabilityId: selectedAvailability.availabilityId ?? null,
|
|
76
81
|
dateTime: selectedAvailability.dateTime,
|
|
77
82
|
bookingItems,
|
|
78
83
|
returnAvailabilityId: selectedReturnOption?.returnAvailabilityId ?? null,
|
|
@@ -83,7 +88,8 @@ export function buildAdminChangeProviderPayload({
|
|
|
83
88
|
passengerCount: null,
|
|
84
89
|
childSafetySeatsCount: null,
|
|
85
90
|
foodRestrictions: null,
|
|
86
|
-
|
|
91
|
+
// The admin editor owns the complete target selection. [] intentionally means clear all.
|
|
92
|
+
addOnSelections,
|
|
87
93
|
cancellationPolicyId: cancellationPolicyId ?? initialCancellationPolicyId ?? null,
|
|
88
94
|
promoCode: appliedPromoCode ?? null,
|
|
89
95
|
newTotalAmount,
|
|
@@ -101,6 +107,8 @@ export function buildAdminChangeProviderPayload({
|
|
|
101
107
|
authoritativeReceipt: providerApplyAuthoritativeReceipt,
|
|
102
108
|
structuredAdjustments: adminStructuredAdjustments,
|
|
103
109
|
refundDispositionsByOperationId,
|
|
110
|
+
pricingV2QuoteId: pricingV2QuoteId ?? null,
|
|
111
|
+
pricingV2QuotedTotal: pricingV2QuotedTotal ?? null,
|
|
104
112
|
capacitySeatCredit: {
|
|
105
113
|
enabled: true,
|
|
106
114
|
previousPassengerCount,
|
|
@@ -10,7 +10,9 @@ interface AdminChangeQuoteRequestKeyInput {
|
|
|
10
10
|
returnAvailabilityId: string | null;
|
|
11
11
|
quantities: Record<string, number>;
|
|
12
12
|
addOnSelections: Array<{ addOnId: string; variantId?: string; quantity?: number }>;
|
|
13
|
-
adminCustomReceiptLines:
|
|
13
|
+
adminCustomReceiptLines: unknown[];
|
|
14
|
+
cancellationPolicyId?: string | null;
|
|
15
|
+
structuredAdjustments?: unknown[];
|
|
14
16
|
refundDispositionsByOperationId?: Record<string, string>;
|
|
15
17
|
useAdminFeAuthoritativeQuote: boolean;
|
|
16
18
|
}
|
|
@@ -33,6 +35,8 @@ export function buildAdminChangeQuoteRequestKey(input: AdminChangeQuoteRequestKe
|
|
|
33
35
|
quantities: input.quantities,
|
|
34
36
|
addOnSelections: input.addOnSelections,
|
|
35
37
|
adminCustomReceiptLines: input.adminCustomReceiptLines,
|
|
38
|
+
cancellationPolicyId: input.cancellationPolicyId ?? null,
|
|
39
|
+
structuredAdjustments: input.structuredAdjustments ?? [],
|
|
36
40
|
refundDispositionsByOperationId: Object.fromEntries(
|
|
37
41
|
Object.entries(input.refundDispositionsByOperationId ?? {}).sort(([a], [b]) => a.localeCompare(b)),
|
|
38
42
|
),
|
|
@@ -74,9 +74,7 @@ export async function quoteCustomerChangeForCheckout({
|
|
|
74
74
|
newPickupLocationId: pickupLocationId || null,
|
|
75
75
|
newReturnAvailabilityId: returnAvailabilityId ?? null,
|
|
76
76
|
newPassengerCounts: bookingItems,
|
|
77
|
-
...(addOnSelections
|
|
78
|
-
? { newAddOnSelections: addOnSelections }
|
|
79
|
-
: {}),
|
|
77
|
+
...(addOnSelections ? { newAddOnSelections: addOnSelections } : {}),
|
|
80
78
|
clientProposedTotal,
|
|
81
79
|
});
|
|
82
80
|
const { quoteSlice, mergedQuote } = sliceAndMergeChangeQuoteForUi(
|
|
@@ -163,9 +161,7 @@ export async function quoteAdminChangeForCheckout({
|
|
|
163
161
|
newPickupLocationId: pickupLocationId || null,
|
|
164
162
|
newReturnAvailabilityId: returnAvailabilityId ?? null,
|
|
165
163
|
newPassengerCounts: bookingItems,
|
|
166
|
-
...(addOnSelections
|
|
167
|
-
? { newAddOnSelections: addOnSelections }
|
|
168
|
-
: {}),
|
|
164
|
+
...(addOnSelections ? { newAddOnSelections: addOnSelections } : {}),
|
|
169
165
|
...(manualLineAdjustments && manualLineAdjustments.length > 0
|
|
170
166
|
? { manualLineAdjustments }
|
|
171
167
|
: {}),
|
|
@@ -37,6 +37,8 @@ export type ProviderDashboardChangeBookingPayload = {
|
|
|
37
37
|
parentProductId?: string;
|
|
38
38
|
/** Product option id selected for the new booking. */
|
|
39
39
|
productId: string;
|
|
40
|
+
/** Exact outbound availability selected and priced by the server. */
|
|
41
|
+
availabilityId?: string | null;
|
|
40
42
|
dateTime: string;
|
|
41
43
|
bookingItems: Array<{ category: string; count: number }>;
|
|
42
44
|
returnAvailabilityId?: string | null;
|
|
@@ -67,6 +69,9 @@ export type ProviderDashboardChangeBookingPayload = {
|
|
|
67
69
|
string,
|
|
68
70
|
'REFUND_TO_ORIGINAL_PAYMENT' | 'PENDING_REFUND' | 'NO_REFUND'
|
|
69
71
|
>;
|
|
72
|
+
/** Exact server quote shown to the operator. Apply reuses this quote when it is still valid. */
|
|
73
|
+
pricingV2QuoteId?: string | null;
|
|
74
|
+
pricingV2QuotedTotal?: number | null;
|
|
70
75
|
/**
|
|
71
76
|
* Admin/provider change flow: when FE-authoritative quoting is used, apply must persist
|
|
72
77
|
* the exact receipt that was quoted instead of rebuilding from catalog pricing.
|
|
@@ -338,6 +338,8 @@ export function useAdminChangeCheckoutController({
|
|
|
338
338
|
mergedProviderAdditionalAdjustments,
|
|
339
339
|
adminStructuredAdjustments,
|
|
340
340
|
refundDispositionsByOperationId,
|
|
341
|
+
pricingV2QuoteId: latestChangeQuote?.quoteId ?? null,
|
|
342
|
+
pricingV2QuotedTotal: latestChangeQuote?.quotedTotal ?? latestChangeQuote?.serverDisplay?.total ?? null,
|
|
341
343
|
providerApplyAuthoritativeReceipt,
|
|
342
344
|
previousPassengerCount: changeFlowInitialTicketCount,
|
|
343
345
|
previousAvailabilityId: initialValues?.availabilityId ?? null,
|
|
@@ -417,6 +419,31 @@ export function useAdminChangeCheckoutController({
|
|
|
417
419
|
return;
|
|
418
420
|
}
|
|
419
421
|
|
|
422
|
+
// Provider changes are applied by the dashboard through the exact Pricing V2 quote.
|
|
423
|
+
// Do not create the legacy FE-authoritative change intent or collect a card payment first;
|
|
424
|
+
// apply-v2 records any positive delta as a quote-linked pending balance.
|
|
425
|
+
if (isProviderDashboardChange && isChangeBookingContext) {
|
|
426
|
+
if (!onChangeBooking) {
|
|
427
|
+
throw new Error('Provider change handler is unavailable.');
|
|
428
|
+
}
|
|
429
|
+
if (!latestChangeQuote?.quoteId || latestChangeQuote.canProceed === false) {
|
|
430
|
+
throw new Error(latestChangeQuote?.reasonIfBlocked || 'The authoritative change quote is not ready.');
|
|
431
|
+
}
|
|
432
|
+
const providerPayload = buildProviderChangePayload(availabilityProductOptionId, bookingItems);
|
|
433
|
+
if (!providerPayload) {
|
|
434
|
+
throw new Error('No availability selected');
|
|
435
|
+
}
|
|
436
|
+
await onChangeBooking(providerPayload);
|
|
437
|
+
const bookingReference = initialValues?.bookingReference?.trim();
|
|
438
|
+
if (!bookingReference) {
|
|
439
|
+
throw new Error('Missing booking reference.');
|
|
440
|
+
}
|
|
441
|
+
onSuccess?.({ reservationReference: bookingReference });
|
|
442
|
+
finishManageFlow(bookingReference);
|
|
443
|
+
setLoading(false);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
|
|
420
447
|
const bookingSourceContext = buildBookingSourceContext(bookingSourceAttribution, {
|
|
421
448
|
clientChannelSource: inferClientBookingSourceFromProductIds(
|
|
422
449
|
product.productId,
|
|
@@ -28,10 +28,18 @@ import {
|
|
|
28
28
|
import type { Currency } from './CurrencySwitcher';
|
|
29
29
|
import type { AdminChangeLatestQuote } from './useAdminChangeCheckoutController';
|
|
30
30
|
import { buildAdminChangeQuoteRequestKey } from './admin-change-quote-request-key';
|
|
31
|
+
import type { ProviderDashboardChangeBookingPayload } from './provider-dashboard-change-booking';
|
|
31
32
|
|
|
32
33
|
type AddOnSelection = { addOnId: string; variantId?: string; quantity?: number };
|
|
33
|
-
type AdminCustomReceiptLine = {
|
|
34
|
-
|
|
34
|
+
type AdminCustomReceiptLine = {
|
|
35
|
+
label: string;
|
|
36
|
+
amountInput: string;
|
|
37
|
+
amountSign?: number;
|
|
38
|
+
calculation: 'FIXED_AMOUNT' | 'PERCENTAGE';
|
|
39
|
+
scope: 'POSITIVE_AMENDMENT_VALUE' | 'CURRENT_ACTIVE_TICKETS' | 'CURRENT_ACTIVE_BOOKING';
|
|
40
|
+
taxBehavior: 'NON_TAXABLE' | 'TAXABLE' | 'PRE_TAX_DISCOUNT' | 'POST_TAX_DISCOUNT';
|
|
41
|
+
reasonCode: string;
|
|
42
|
+
};
|
|
35
43
|
|
|
36
44
|
interface ChangeSelectionDetailsForPreview {
|
|
37
45
|
dateChanged: boolean;
|
|
@@ -51,7 +59,9 @@ export interface UseAdminChangeQuotePreviewParams {
|
|
|
51
59
|
pickupLocationId: string | null;
|
|
52
60
|
quantities: Record<string, number>;
|
|
53
61
|
addOnSelections: AddOnSelection[];
|
|
62
|
+
cancellationPolicyId: string | null;
|
|
54
63
|
adminCustomReceiptLines: AdminCustomReceiptLine[];
|
|
64
|
+
adminStructuredAdjustments: NonNullable<ProviderDashboardChangeBookingPayload['structuredAdjustments']>;
|
|
55
65
|
refundDispositionsByOperationId: Record<string, AdminAmendmentRefundDisposition>;
|
|
56
66
|
useAdminFeAuthoritativeQuote: boolean;
|
|
57
67
|
latestChangeQuote: AdminChangeLatestQuote | null;
|
|
@@ -78,7 +88,6 @@ export interface UseAdminChangeQuotePreviewParams {
|
|
|
78
88
|
changeFlowNewBookingTotal: number;
|
|
79
89
|
effectiveSubtotalForCheckout: number;
|
|
80
90
|
effectiveTax: number;
|
|
81
|
-
adminCustomLinesAsAdditionalAdjustments: AdditionalAdjustment[];
|
|
82
91
|
adminFeAuthoritativeReceipt: AdminFeAuthoritativeReceipt;
|
|
83
92
|
isTaxIncludedInPrice: boolean;
|
|
84
93
|
t: (key: string) => string;
|
|
@@ -109,7 +118,9 @@ export function useAdminChangeQuotePreview({
|
|
|
109
118
|
pickupLocationId,
|
|
110
119
|
quantities,
|
|
111
120
|
addOnSelections,
|
|
121
|
+
cancellationPolicyId,
|
|
112
122
|
adminCustomReceiptLines,
|
|
123
|
+
adminStructuredAdjustments,
|
|
113
124
|
refundDispositionsByOperationId,
|
|
114
125
|
useAdminFeAuthoritativeQuote,
|
|
115
126
|
latestChangeQuote,
|
|
@@ -136,7 +147,6 @@ export function useAdminChangeQuotePreview({
|
|
|
136
147
|
changeFlowNewBookingTotal,
|
|
137
148
|
effectiveSubtotalForCheckout,
|
|
138
149
|
effectiveTax,
|
|
139
|
-
adminCustomLinesAsAdditionalAdjustments,
|
|
140
150
|
adminFeAuthoritativeReceipt,
|
|
141
151
|
isTaxIncludedInPrice,
|
|
142
152
|
t,
|
|
@@ -167,6 +177,8 @@ export function useAdminChangeQuotePreview({
|
|
|
167
177
|
quantities,
|
|
168
178
|
addOnSelections,
|
|
169
179
|
adminCustomReceiptLines,
|
|
180
|
+
cancellationPolicyId,
|
|
181
|
+
structuredAdjustments: adminStructuredAdjustments,
|
|
170
182
|
refundDispositionsByOperationId,
|
|
171
183
|
useAdminFeAuthoritativeQuote,
|
|
172
184
|
}), [
|
|
@@ -179,6 +191,8 @@ export function useAdminChangeQuotePreview({
|
|
|
179
191
|
quantities,
|
|
180
192
|
addOnSelections,
|
|
181
193
|
adminCustomReceiptLines,
|
|
194
|
+
cancellationPolicyId,
|
|
195
|
+
adminStructuredAdjustments,
|
|
182
196
|
refundDispositionsByOperationId,
|
|
183
197
|
useAdminFeAuthoritativeQuote,
|
|
184
198
|
]);
|
|
@@ -420,11 +434,10 @@ export function useAdminChangeQuotePreview({
|
|
|
420
434
|
newPickupLocationId: pickupLocationId || null,
|
|
421
435
|
newReturnAvailabilityId: selectedReturnOption?.returnAvailabilityId ?? null,
|
|
422
436
|
newPassengerCounts: bookingItems,
|
|
423
|
-
//
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
: {}),
|
|
437
|
+
// Admin owns the complete target selection. [] intentionally clears all add-ons.
|
|
438
|
+
newAddOnSelections: addOnSelections,
|
|
439
|
+
cancellationPolicyId,
|
|
440
|
+
structuredAdjustments: adminStructuredAdjustments,
|
|
428
441
|
...(Object.keys(refundDispositionsByOperationId).length > 0
|
|
429
442
|
? { refundDispositionsByOperationId }
|
|
430
443
|
: {}),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useMemo } from 'react';
|
|
1
|
+
import { useMemo, useState } from 'react';
|
|
2
2
|
import { roundMoney } from '../../lib/booking/change-flow-pricing';
|
|
3
3
|
import type {
|
|
4
4
|
ProviderDashboardChangePricingUi,
|
|
@@ -55,6 +55,9 @@ export function useAdminProviderPricingAdjustments({
|
|
|
55
55
|
adminCustomReceiptLines,
|
|
56
56
|
isManualOverrideEligibleLine,
|
|
57
57
|
}: UseAdminProviderPricingAdjustmentsParams) {
|
|
58
|
+
const [adjustmentIdNamespace] = useState(() =>
|
|
59
|
+
`${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`,
|
|
60
|
+
);
|
|
58
61
|
const providerTotalsPreview =
|
|
59
62
|
showProviderPricingInlineEditor && providerPricingUi?.totalsPreview
|
|
60
63
|
? providerPricingUi.totalsPreview
|
|
@@ -137,17 +140,18 @@ export function useAdminProviderPricingAdjustments({
|
|
|
137
140
|
);
|
|
138
141
|
|
|
139
142
|
const adminStructuredAdjustments = useMemo(
|
|
140
|
-
(): AdminStructuredAdjustment[] =>
|
|
141
|
-
adminCustomReceiptLines.flatMap((line) => {
|
|
143
|
+
(): AdminStructuredAdjustment[] => {
|
|
144
|
+
const customAdjustments = adminCustomReceiptLines.flatMap((line) => {
|
|
142
145
|
const value = Number(line.amountInput.trim());
|
|
143
146
|
if (!Number.isFinite(value) || value <= 0) return [];
|
|
144
147
|
const discount = (line.amountSign ?? 1) < 0;
|
|
145
148
|
const percentage = line.calculation === 'PERCENTAGE';
|
|
149
|
+
const mode: AdminStructuredAdjustment['mode'] = percentage
|
|
150
|
+
? discount ? 'PERCENTAGE_DISCOUNT' : 'PERCENTAGE_SURCHARGE'
|
|
151
|
+
: 'FIXED_AMOUNT';
|
|
146
152
|
return [{
|
|
147
153
|
adjustmentId: line.id,
|
|
148
|
-
mode
|
|
149
|
-
? discount ? 'PERCENTAGE_DISCOUNT' : 'PERCENTAGE_SURCHARGE'
|
|
150
|
-
: 'FIXED_AMOUNT',
|
|
154
|
+
mode,
|
|
151
155
|
...(percentage
|
|
152
156
|
? { percentageBasisPoints: Math.round(value * 100) }
|
|
153
157
|
: { fixedAmount: roundMoney((discount ? -1 : 1) * value) }),
|
|
@@ -156,8 +160,48 @@ export function useAdminProviderPricingAdjustments({
|
|
|
156
160
|
reasonCode: line.reasonCode.trim() || 'STAFF_ADJUSTMENT',
|
|
157
161
|
reason: line.label.trim() || null,
|
|
158
162
|
}];
|
|
159
|
-
})
|
|
160
|
-
|
|
163
|
+
});
|
|
164
|
+
const inlineAdjustments = isProviderDashboardChange
|
|
165
|
+
? (providerPricingUi?.additionalAdjustments ?? []).flatMap((line) => {
|
|
166
|
+
const value = Number((line.amountInput ?? '').trim());
|
|
167
|
+
if (!Number.isFinite(value) || value <= 0) return [];
|
|
168
|
+
const discount = line.mode === 'DISCOUNT';
|
|
169
|
+
return [{
|
|
170
|
+
adjustmentId: line.id,
|
|
171
|
+
mode: 'FIXED_AMOUNT' as const,
|
|
172
|
+
fixedAmount: roundMoney((discount ? -1 : 1) * value),
|
|
173
|
+
scope: 'CURRENT_ACTIVE_BOOKING' as const,
|
|
174
|
+
taxBehavior: 'TAXABLE' as const,
|
|
175
|
+
reasonCode: 'PROVIDER_ADJUSTMENT',
|
|
176
|
+
reason: line.label?.trim() || (discount ? 'Provider discount' : 'Provider charge'),
|
|
177
|
+
}];
|
|
178
|
+
})
|
|
179
|
+
: [];
|
|
180
|
+
const lineOverrideAdjustments = providerPricingOverrides.flatMap((override, index) => {
|
|
181
|
+
const original = providerQuotedLines.find((line) => line.lineKey === override.lineKey);
|
|
182
|
+
if (!original) return [];
|
|
183
|
+
const delta = roundMoney(override.amount - original.amount);
|
|
184
|
+
if (Math.abs(delta) < 0.005) return [];
|
|
185
|
+
return [{
|
|
186
|
+
adjustmentId: `provider-line-${adjustmentIdNamespace}-${index}`,
|
|
187
|
+
mode: 'FIXED_AMOUNT' as const,
|
|
188
|
+
fixedAmount: delta,
|
|
189
|
+
scope: 'CURRENT_ACTIVE_BOOKING' as const,
|
|
190
|
+
taxBehavior: 'TAXABLE' as const,
|
|
191
|
+
reasonCode: 'PROVIDER_LINE_OVERRIDE',
|
|
192
|
+
reason: `Override ${original.label}`,
|
|
193
|
+
}];
|
|
194
|
+
});
|
|
195
|
+
return [...customAdjustments, ...inlineAdjustments, ...lineOverrideAdjustments];
|
|
196
|
+
},
|
|
197
|
+
[
|
|
198
|
+
adminCustomReceiptLines,
|
|
199
|
+
isProviderDashboardChange,
|
|
200
|
+
providerPricingUi?.additionalAdjustments,
|
|
201
|
+
providerPricingOverrides,
|
|
202
|
+
providerQuotedLines,
|
|
203
|
+
adjustmentIdNamespace,
|
|
204
|
+
],
|
|
161
205
|
);
|
|
162
206
|
|
|
163
207
|
const mergedProviderAdditionalAdjustments = useMemo(
|
|
@@ -372,8 +372,8 @@ export function useChangeBookingQuotePreview({
|
|
|
372
372
|
newPickupLocationId: pickupLocationId || null,
|
|
373
373
|
newReturnAvailabilityId: selectedReturnOption?.returnAvailabilityId ?? null,
|
|
374
374
|
newPassengerCounts: bookingItems,
|
|
375
|
-
//
|
|
376
|
-
|
|
375
|
+
// The editor owns the complete target selection. [] intentionally clears all add-ons.
|
|
376
|
+
newAddOnSelections: addOnSelections,
|
|
377
377
|
clientProposedTotal: changeFlowNewBookingTotal,
|
|
378
378
|
capacitySeatCredit: {
|
|
379
379
|
enabled: true,
|
package/src/lib/booking-api.ts
CHANGED
|
@@ -1515,7 +1515,7 @@ export interface ApplyPaidChangeResponse {
|
|
|
1515
1515
|
}
|
|
1516
1516
|
|
|
1517
1517
|
export interface BookingChangeIntentStatusResponse {
|
|
1518
|
-
status: 'CREATED' | 'PAYMENT_REQUIRED' | 'PAYMENT_SUCCEEDED' | 'APPLYING' | 'APPLIED' | 'FAILED' | 'EXPIRED';
|
|
1518
|
+
status: 'CREATED' | 'PAYMENT_REQUIRED' | 'PAYMENT_AUTHORIZED' | 'PAYMENT_SUCCEEDED' | 'APPLYING' | 'APPLIED' | 'FAILED' | 'EXPIRED';
|
|
1519
1519
|
bookingReference?: string;
|
|
1520
1520
|
booking?: unknown;
|
|
1521
1521
|
failureCode?: string;
|
|
@@ -176,6 +176,8 @@ test('admin no-charge provider payload carries the selected pickup itinerary', (
|
|
|
176
176
|
mergedProviderAdditionalAdjustments: [],
|
|
177
177
|
adminStructuredAdjustments: [],
|
|
178
178
|
refundDispositionsByOperationId: {},
|
|
179
|
+
pricingV2QuoteId: 'quote_reviewed',
|
|
180
|
+
pricingV2QuotedTotal: 202.87,
|
|
179
181
|
previousPassengerCount: 1,
|
|
180
182
|
previousAvailabilityId: 'availability_1',
|
|
181
183
|
previousReturnAvailabilityId: 'return_1',
|
|
@@ -185,6 +187,10 @@ test('admin no-charge provider payload carries the selected pickup itinerary', (
|
|
|
185
187
|
assert.deepEqual(payload?.itineraryDisplay, itineraryDisplay);
|
|
186
188
|
assert.equal(payload?.pickupLocationId, 'pickup_new');
|
|
187
189
|
assert.equal(payload?.travelerHotel, 'A Bear and Bison Inn');
|
|
190
|
+
assert.equal(payload?.availabilityId, 'availability_1');
|
|
191
|
+
assert.deepEqual(payload?.addOnSelections, []);
|
|
192
|
+
assert.equal(payload?.pricingV2QuoteId, 'quote_reviewed');
|
|
193
|
+
assert.equal(payload?.pricingV2QuotedTotal, 202.87);
|
|
188
194
|
});
|
|
189
195
|
|
|
190
196
|
test('public paid change preview shows amendment-only charges before combined tax', () => {
|
|
@@ -349,6 +355,35 @@ test('changing admin refund treatment creates a distinct authoritative quote req
|
|
|
349
355
|
assert.notEqual(pending, noRefund);
|
|
350
356
|
});
|
|
351
357
|
|
|
358
|
+
test('admin cancellation policy and structured adjustments are authoritative quote inputs', () => {
|
|
359
|
+
const base = {
|
|
360
|
+
bookingReference: 'DE8H04DX',
|
|
361
|
+
lastName: 'Tauro',
|
|
362
|
+
productId: 'p_1',
|
|
363
|
+
selectedAvailability: {
|
|
364
|
+
availabilityId: 'a_1',
|
|
365
|
+
productId: 'po_1',
|
|
366
|
+
dateTime: '2026-07-21T03:00:00-06:00',
|
|
367
|
+
vacancies: 10,
|
|
368
|
+
} as Parameters<typeof buildAdminChangeQuoteRequestKey>[0]['selectedAvailability'],
|
|
369
|
+
pickupLocationId: 'pickup_1',
|
|
370
|
+
returnAvailabilityId: 'return_1',
|
|
371
|
+
quantities: { ADULT: 1 },
|
|
372
|
+
addOnSelections: [],
|
|
373
|
+
adminCustomReceiptLines: [],
|
|
374
|
+
useAdminFeAuthoritativeQuote: true,
|
|
375
|
+
};
|
|
376
|
+
const original = buildAdminChangeQuoteRequestKey(base);
|
|
377
|
+
const policyChanged = buildAdminChangeQuoteRequestKey({ ...base, cancellationPolicyId: 'flexible' });
|
|
378
|
+
const adjustmentChanged = buildAdminChangeQuoteRequestKey({
|
|
379
|
+
...base,
|
|
380
|
+
structuredAdjustments: [{ adjustmentId: 'adj_1', mode: 'FIXED_AMOUNT', fixedAmount: 10 }],
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
assert.notEqual(original, policyChanged);
|
|
384
|
+
assert.notEqual(original, adjustmentChanged);
|
|
385
|
+
});
|
|
386
|
+
|
|
352
387
|
test('private shuttle pricing replaces the selected calendar summary with hydrated rate details', () => {
|
|
353
388
|
const selectedSummary = {
|
|
354
389
|
availabilityId: 'a_private',
|