@ticketboothapp/booking 1.2.144 → 1.2.145
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
CHANGED
|
@@ -687,8 +687,6 @@ export function AdminChangeBookingFlow({
|
|
|
687
687
|
quantities,
|
|
688
688
|
addOnSelections,
|
|
689
689
|
adminCustomReceiptLines,
|
|
690
|
-
editableSummaryLineAmountInputs,
|
|
691
|
-
editableSummaryLineLabelInputs,
|
|
692
690
|
useAdminFeAuthoritativeQuote,
|
|
693
691
|
latestChangeQuote,
|
|
694
692
|
setLatestChangeQuote,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Availability } from '../../lib/booking-api';
|
|
2
|
+
import { getAvailabilityOptionId } from './change-booking-flow-helpers';
|
|
3
|
+
|
|
4
|
+
interface AdminChangeQuoteRequestKeyInput {
|
|
5
|
+
bookingReference?: string | null;
|
|
6
|
+
lastName: string;
|
|
7
|
+
productId: string;
|
|
8
|
+
selectedAvailability: Availability | null;
|
|
9
|
+
pickupLocationId: string | null;
|
|
10
|
+
returnAvailabilityId: string | null;
|
|
11
|
+
quantities: Record<string, number>;
|
|
12
|
+
addOnSelections: Array<{ addOnId: string; variantId?: string; quantity?: number }>;
|
|
13
|
+
adminCustomReceiptLines: Array<{ label: string; amountInput: string; amountSign?: number }>;
|
|
14
|
+
useAdminFeAuthoritativeQuote: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Identity of a price request. This must contain user-controlled quote inputs only.
|
|
19
|
+
* Server/display-derived receipt state must never invalidate the response that produced it.
|
|
20
|
+
*/
|
|
21
|
+
export function buildAdminChangeQuoteRequestKey(input: AdminChangeQuoteRequestKeyInput): string {
|
|
22
|
+
const availability = input.selectedAvailability;
|
|
23
|
+
return JSON.stringify({
|
|
24
|
+
bookingReference: input.bookingReference?.trim() ?? '',
|
|
25
|
+
lastName: input.lastName.trim().toLowerCase(),
|
|
26
|
+
parentProductId: input.productId,
|
|
27
|
+
optionId: availability ? getAvailabilityOptionId(availability) : '',
|
|
28
|
+
dateTime: availability?.dateTime ?? '',
|
|
29
|
+
availabilityId: availability?.availabilityId ?? null,
|
|
30
|
+
pickupLocationId: input.pickupLocationId,
|
|
31
|
+
returnAvailabilityId: input.returnAvailabilityId,
|
|
32
|
+
quantities: input.quantities,
|
|
33
|
+
addOnSelections: input.addOnSelections,
|
|
34
|
+
adminCustomReceiptLines: input.adminCustomReceiptLines,
|
|
35
|
+
useAdminFeAuthoritativeQuote: input.useAdminFeAuthoritativeQuote,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
} from './change-booking-flow-helpers';
|
|
27
27
|
import type { Currency } from './CurrencySwitcher';
|
|
28
28
|
import type { AdminChangeLatestQuote } from './useAdminChangeCheckoutController';
|
|
29
|
+
import { buildAdminChangeQuoteRequestKey } from './admin-change-quote-request-key';
|
|
29
30
|
|
|
30
31
|
type AddOnSelection = { addOnId: string; variantId?: string; quantity?: number };
|
|
31
32
|
type AdminCustomReceiptLine = { label: string; amountInput: string; amountSign?: number };
|
|
@@ -50,8 +51,6 @@ export interface UseAdminChangeQuotePreviewParams {
|
|
|
50
51
|
quantities: Record<string, number>;
|
|
51
52
|
addOnSelections: AddOnSelection[];
|
|
52
53
|
adminCustomReceiptLines: AdminCustomReceiptLine[];
|
|
53
|
-
editableSummaryLineAmountInputs: Record<string, string>;
|
|
54
|
-
editableSummaryLineLabelInputs: Record<string, string>;
|
|
55
54
|
useAdminFeAuthoritativeQuote: boolean;
|
|
56
55
|
latestChangeQuote: AdminChangeLatestQuote | null;
|
|
57
56
|
setLatestChangeQuote: Dispatch<SetStateAction<AdminChangeLatestQuote | null>>;
|
|
@@ -109,8 +108,6 @@ export function useAdminChangeQuotePreview({
|
|
|
109
108
|
quantities,
|
|
110
109
|
addOnSelections,
|
|
111
110
|
adminCustomReceiptLines,
|
|
112
|
-
editableSummaryLineAmountInputs,
|
|
113
|
-
editableSummaryLineLabelInputs,
|
|
114
111
|
useAdminFeAuthoritativeQuote,
|
|
115
112
|
latestChangeQuote,
|
|
116
113
|
setLatestChangeQuote,
|
|
@@ -154,20 +151,16 @@ export function useAdminChangeQuotePreview({
|
|
|
154
151
|
!!lastName.trim();
|
|
155
152
|
|
|
156
153
|
const isChangeQuoteBlocked = isCustomerSelfServeChange && latestChangeQuote?.canProceed === false;
|
|
157
|
-
const changeQuoteInputsKey = useMemo(() =>
|
|
158
|
-
bookingReference: initialValues?.bookingReference
|
|
159
|
-
lastName
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
availabilityId: selectedAvailability?.availabilityId ?? null,
|
|
164
|
-
pickupLocationId: pickupLocationId ?? null,
|
|
154
|
+
const changeQuoteInputsKey = useMemo(() => buildAdminChangeQuoteRequestKey({
|
|
155
|
+
bookingReference: initialValues?.bookingReference,
|
|
156
|
+
lastName,
|
|
157
|
+
productId: product.productId,
|
|
158
|
+
selectedAvailability,
|
|
159
|
+
pickupLocationId,
|
|
165
160
|
returnAvailabilityId: selectedReturnOption?.returnAvailabilityId ?? null,
|
|
166
161
|
quantities,
|
|
167
162
|
addOnSelections,
|
|
168
163
|
adminCustomReceiptLines,
|
|
169
|
-
editableSummaryLineAmountInputs,
|
|
170
|
-
editableSummaryLineLabelInputs,
|
|
171
164
|
useAdminFeAuthoritativeQuote,
|
|
172
165
|
}), [
|
|
173
166
|
initialValues?.bookingReference,
|
|
@@ -179,8 +172,6 @@ export function useAdminChangeQuotePreview({
|
|
|
179
172
|
quantities,
|
|
180
173
|
addOnSelections,
|
|
181
174
|
adminCustomReceiptLines,
|
|
182
|
-
editableSummaryLineAmountInputs,
|
|
183
|
-
editableSummaryLineLabelInputs,
|
|
184
175
|
useAdminFeAuthoritativeQuote,
|
|
185
176
|
]);
|
|
186
177
|
const destinationRequiresReturnSelection = Boolean(selectedAvailability?.returnOptions?.length);
|
|
@@ -7,6 +7,7 @@ import { evaluateChangeBookingQuoteForCheckout } from '../src/components/booking
|
|
|
7
7
|
import { buildCheckoutModalSummaryFromPricingV2Quote } from '../src/components/booking/pricing-v2-checkout-summary';
|
|
8
8
|
import type { ChangeQuoteUiSlice } from '../src/lib/booking/change-flow-pricing';
|
|
9
9
|
import type { ChangeBookingQuoteResponse } from '../src/lib/booking-api';
|
|
10
|
+
import { buildAdminChangeQuoteRequestKey } from '../src/components/booking/admin-change-quote-request-key';
|
|
10
11
|
|
|
11
12
|
function quote(overrides: Partial<ChangeBookingQuoteResponse>): ChangeBookingQuoteResponse {
|
|
12
13
|
return {
|
|
@@ -35,6 +36,39 @@ function test(name: string, fn: () => void): void {
|
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
test('admin quote response display state cannot invalidate its request identity', () => {
|
|
40
|
+
const selectedAvailability = {
|
|
41
|
+
availabilityId: 'a_1',
|
|
42
|
+
productId: 'po_1',
|
|
43
|
+
dateTime: '2026-07-22T09:35:00-06:00',
|
|
44
|
+
vacancies: 10,
|
|
45
|
+
} as Parameters<typeof buildAdminChangeQuoteRequestKey>[0]['selectedAvailability'];
|
|
46
|
+
const userInputs = {
|
|
47
|
+
bookingReference: 'BIHAYXQS',
|
|
48
|
+
lastName: 'Tauro',
|
|
49
|
+
productId: 'p_1',
|
|
50
|
+
selectedAvailability,
|
|
51
|
+
pickupLocationId: 'pickup_1',
|
|
52
|
+
returnAvailabilityId: 'return_1',
|
|
53
|
+
quantities: { ADULT: 3 },
|
|
54
|
+
addOnSelections: [],
|
|
55
|
+
adminCustomReceiptLines: [],
|
|
56
|
+
useAdminFeAuthoritativeQuote: true,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const keyBeforeResponse = buildAdminChangeQuoteRequestKey(userInputs);
|
|
60
|
+
// These are populated from the successful server quote. They are deliberately
|
|
61
|
+
// not accepted by the request-key builder and therefore cannot trigger a re-quote.
|
|
62
|
+
const serverDisplayState = {
|
|
63
|
+
editableSummaryLineAmountInputs: { adult: '173.24', tax: '16.09' },
|
|
64
|
+
editableSummaryLineLabelInputs: { adult: 'ADULT' },
|
|
65
|
+
};
|
|
66
|
+
assert.ok(serverDisplayState.editableSummaryLineAmountInputs.adult);
|
|
67
|
+
const keyAfterResponse = buildAdminChangeQuoteRequestKey(userInputs);
|
|
68
|
+
|
|
69
|
+
assert.equal(keyAfterResponse, keyBeforeResponse);
|
|
70
|
+
});
|
|
71
|
+
|
|
38
72
|
test('routes customer change quote to paid checkout when amount is due', () => {
|
|
39
73
|
const decision = evaluateChangeBookingQuoteForCheckout({
|
|
40
74
|
quote: quote({
|