@ticketboothapp/booking 1.2.130 → 1.2.132
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 +2 -0
- package/src/components/booking/ChangeBookingFlow.tsx +2 -0
- package/src/components/booking/useAdminChangePriceSummary.ts +12 -0
- package/src/components/booking/useAdminChangePromoDiscount.ts +66 -2
- package/src/components/booking/useAdminChangeProtectedPricing.ts +31 -16
- package/src/components/booking/useChangeBookingPriceSummary.ts +12 -0
- package/src/components/booking/useChangeBookingPromoDiscount.ts +67 -2
- package/src/components/booking/useChangeBookingProtectedPricing.ts +31 -16
- package/src/components/booking/useStandardBookingPriceSummary.ts +18 -5
- package/src/components/booking/useStandardBookingPromoDiscount.ts +13 -3
- package/src/lib/booking-api.ts +43 -0
package/package.json
CHANGED
|
@@ -517,6 +517,8 @@ export function AdminChangeBookingFlow({
|
|
|
517
517
|
feeLineItems,
|
|
518
518
|
changeFlowBookedFeeUnitByNormalizedLabel,
|
|
519
519
|
addOnSelections,
|
|
520
|
+
originalAddOnSelections: initialValues?.addOnSelections ?? null,
|
|
521
|
+
originalProductOptionId: initialValues?.productOptionId ?? null,
|
|
520
522
|
addOns,
|
|
521
523
|
adminCustomReceiptLines,
|
|
522
524
|
quantities,
|
|
@@ -528,6 +528,8 @@ export function ChangeBookingFlow({
|
|
|
528
528
|
feeLineItems,
|
|
529
529
|
changeFlowBookedFeeUnitByNormalizedLabel,
|
|
530
530
|
addOnSelections,
|
|
531
|
+
originalAddOnSelections: initialValues?.addOnSelections ?? null,
|
|
532
|
+
originalProductOptionId: initialValues?.productOptionId ?? null,
|
|
531
533
|
addOns,
|
|
532
534
|
quantities,
|
|
533
535
|
pricing,
|
|
@@ -59,6 +59,8 @@ export interface UseAdminChangePriceSummaryParams {
|
|
|
59
59
|
feeLineItems: OrderSummaryFeeLine[];
|
|
60
60
|
changeFlowBookedFeeUnitByNormalizedLabel: Map<string, number>;
|
|
61
61
|
addOnSelections: AddOnSelection[];
|
|
62
|
+
originalAddOnSelections?: AddOnSelection[] | null;
|
|
63
|
+
originalProductOptionId?: string | null;
|
|
62
64
|
addOns: AddOn[];
|
|
63
65
|
adminCustomReceiptLines: AdminCustomReceiptLine[];
|
|
64
66
|
quantities: Record<string, number>;
|
|
@@ -106,6 +108,8 @@ export function useAdminChangePriceSummary({
|
|
|
106
108
|
feeLineItems,
|
|
107
109
|
changeFlowBookedFeeUnitByNormalizedLabel,
|
|
108
110
|
addOnSelections,
|
|
111
|
+
originalAddOnSelections,
|
|
112
|
+
originalProductOptionId,
|
|
109
113
|
addOns,
|
|
110
114
|
adminCustomReceiptLines,
|
|
111
115
|
quantities,
|
|
@@ -138,6 +142,9 @@ export function useAdminChangePriceSummary({
|
|
|
138
142
|
effectiveSubtotal,
|
|
139
143
|
adminCustomAdjustmentTotal,
|
|
140
144
|
effectiveSubtotalForCheckout,
|
|
145
|
+
promoEligibleSubtotalForCheckout,
|
|
146
|
+
addOnTotal,
|
|
147
|
+
originalAddOnSubtotal,
|
|
141
148
|
feeLineItemsForChangeFlowDisplay,
|
|
142
149
|
feeLineItemsWithAddOns,
|
|
143
150
|
} = useAdminChangeProtectedPricing({
|
|
@@ -160,6 +167,7 @@ export function useAdminChangePriceSummary({
|
|
|
160
167
|
feeLineItems,
|
|
161
168
|
changeFlowBookedFeeUnitByNormalizedLabel,
|
|
162
169
|
addOnSelections,
|
|
170
|
+
originalAddOnSelections,
|
|
163
171
|
addOns,
|
|
164
172
|
adminCustomReceiptLines,
|
|
165
173
|
});
|
|
@@ -307,6 +315,10 @@ export function useAdminChangePriceSummary({
|
|
|
307
315
|
ticketLineItemsForChangeFlowDisplay,
|
|
308
316
|
feeLineItemsWithAddOns,
|
|
309
317
|
effectiveSubtotalForCheckout,
|
|
318
|
+
promoEligibleSubtotalForCheckout,
|
|
319
|
+
addOnTotal,
|
|
320
|
+
originalAddOnSubtotal,
|
|
321
|
+
originalProductOptionId,
|
|
310
322
|
lockedPromoCode,
|
|
311
323
|
originalReceipt,
|
|
312
324
|
isTaxIncludedInPrice,
|
|
@@ -24,12 +24,29 @@ interface UseAdminChangePromoDiscountParams {
|
|
|
24
24
|
ticketLineItemsForChangeFlowDisplay: OrderSummaryTicketLine[];
|
|
25
25
|
feeLineItemsWithAddOns: OrderSummaryFeeLine[];
|
|
26
26
|
effectiveSubtotalForCheckout: number;
|
|
27
|
+
promoEligibleSubtotalForCheckout?: number;
|
|
28
|
+
addOnTotal?: number;
|
|
29
|
+
originalAddOnSubtotal?: number;
|
|
30
|
+
originalProductOptionId?: string | null;
|
|
27
31
|
lockedPromoCode: string | null;
|
|
28
32
|
originalReceipt: ChangeBookingFlowProps['originalReceipt'];
|
|
29
33
|
isTaxIncludedInPrice: boolean;
|
|
30
34
|
pricingConfig: PricingConfig | null;
|
|
31
35
|
}
|
|
32
36
|
|
|
37
|
+
function originalReceiptGrossSubtotalBeforePromo(
|
|
38
|
+
originalReceipt: ChangeBookingFlowProps['originalReceipt'],
|
|
39
|
+
): number {
|
|
40
|
+
const chargeTypes = new Set(['TICKET', 'FEE', 'RETURN_OPTION', 'CANCELLATION_UPGRADE']);
|
|
41
|
+
return (originalReceipt?.lineItems ?? []).reduce((sum, line) => {
|
|
42
|
+
const amount = Number(line.amount) || 0;
|
|
43
|
+
if (amount <= 0) return sum;
|
|
44
|
+
const type = String(line.type ?? '').trim().toUpperCase();
|
|
45
|
+
if (!chargeTypes.has(type)) return sum;
|
|
46
|
+
return sum + amount;
|
|
47
|
+
}, 0);
|
|
48
|
+
}
|
|
49
|
+
|
|
33
50
|
export function useAdminChangePromoDiscount({
|
|
34
51
|
appliedPromoCode,
|
|
35
52
|
selectedAvailability,
|
|
@@ -42,6 +59,10 @@ export function useAdminChangePromoDiscount({
|
|
|
42
59
|
ticketLineItemsForChangeFlowDisplay,
|
|
43
60
|
feeLineItemsWithAddOns,
|
|
44
61
|
effectiveSubtotalForCheckout,
|
|
62
|
+
promoEligibleSubtotalForCheckout = effectiveSubtotalForCheckout,
|
|
63
|
+
addOnTotal = Math.max(0, effectiveSubtotalForCheckout - promoEligibleSubtotalForCheckout),
|
|
64
|
+
originalAddOnSubtotal = 0,
|
|
65
|
+
originalProductOptionId,
|
|
45
66
|
lockedPromoCode,
|
|
46
67
|
originalReceipt,
|
|
47
68
|
isTaxIncludedInPrice,
|
|
@@ -65,20 +86,40 @@ export function useAdminChangePromoDiscount({
|
|
|
65
86
|
selectedAvailability: null as Availability | null,
|
|
66
87
|
ticketLineItems: [] as Array<{ category: string; qty: number; itemTotal: number }>,
|
|
67
88
|
effectiveSubtotal: 0,
|
|
89
|
+
promoEligibleSubtotal: 0,
|
|
68
90
|
displayedProductFeeTotal: 0,
|
|
69
91
|
appliedPromoCode: null as string | null,
|
|
70
|
-
changeBookingPromo: null as {
|
|
92
|
+
changeBookingPromo: null as {
|
|
93
|
+
priorAmount?: number;
|
|
94
|
+
originalFullSubtotal?: number;
|
|
95
|
+
originalAddOnSubtotal?: number;
|
|
96
|
+
newFullSubtotal?: number;
|
|
97
|
+
newAddOnSubtotal?: number;
|
|
98
|
+
} | null,
|
|
71
99
|
});
|
|
72
100
|
|
|
73
101
|
const originalReceiptPromoAdjustment = useMemo(
|
|
74
102
|
() => deriveOriginalReceiptPromoAdjustment(originalReceipt),
|
|
75
103
|
[originalReceipt],
|
|
76
104
|
);
|
|
105
|
+
const originalFullSubtotalBeforePromo = useMemo(
|
|
106
|
+
() => originalReceiptGrossSubtotalBeforePromo(originalReceipt),
|
|
107
|
+
[originalReceipt],
|
|
108
|
+
);
|
|
77
109
|
|
|
78
110
|
const selectedAvailabilityOptionId = useMemo(
|
|
79
111
|
() => (selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : ''),
|
|
80
112
|
[selectedAvailability],
|
|
81
113
|
);
|
|
114
|
+
const includeLegacyPromoRetentionContext = useMemo(() => {
|
|
115
|
+
const originalOptionId = originalProductOptionId?.trim();
|
|
116
|
+
return Boolean(
|
|
117
|
+
lockedPromoCode &&
|
|
118
|
+
originalOptionId &&
|
|
119
|
+
selectedAvailabilityOptionId &&
|
|
120
|
+
selectedAvailabilityOptionId === originalOptionId,
|
|
121
|
+
);
|
|
122
|
+
}, [lockedPromoCode, originalProductOptionId, selectedAvailabilityOptionId]);
|
|
82
123
|
|
|
83
124
|
const promoDiscountFetchKey = useMemo(() => {
|
|
84
125
|
if (!appliedPromoCode || !selectedAvailability || totalQuantity === 0) return '';
|
|
@@ -100,6 +141,10 @@ export function useAdminChangePromoDiscount({
|
|
|
100
141
|
.join('|'),
|
|
101
142
|
String(Math.round(feeLineItemsWithAddOns.reduce((sum, fee) => sum + fee.totalAmount, 0) * 100)),
|
|
102
143
|
String(Math.round(effectiveSubtotalForCheckout * 100)),
|
|
144
|
+
String(Math.round(promoEligibleSubtotalForCheckout * 100)),
|
|
145
|
+
String(Math.round(addOnTotal * 100)),
|
|
146
|
+
includeLegacyPromoRetentionContext ? String(Math.round(originalFullSubtotalBeforePromo * 100)) : '',
|
|
147
|
+
includeLegacyPromoRetentionContext ? String(Math.round(originalAddOnSubtotal * 100)) : '',
|
|
103
148
|
lockedPromoCode ? String(Math.round(originalReceiptPromoAdjustment.amount * 100)) : '',
|
|
104
149
|
].join('::');
|
|
105
150
|
}, [
|
|
@@ -115,6 +160,11 @@ export function useAdminChangePromoDiscount({
|
|
|
115
160
|
ticketLineItemsForChangeFlowDisplay,
|
|
116
161
|
feeLineItemsWithAddOns,
|
|
117
162
|
effectiveSubtotalForCheckout,
|
|
163
|
+
promoEligibleSubtotalForCheckout,
|
|
164
|
+
addOnTotal,
|
|
165
|
+
includeLegacyPromoRetentionContext,
|
|
166
|
+
originalFullSubtotalBeforePromo,
|
|
167
|
+
originalAddOnSubtotal,
|
|
118
168
|
lockedPromoCode,
|
|
119
169
|
originalReceiptPromoAdjustment.amount,
|
|
120
170
|
]);
|
|
@@ -127,10 +177,17 @@ export function useAdminChangePromoDiscount({
|
|
|
127
177
|
itemTotal: line.itemTotal,
|
|
128
178
|
})),
|
|
129
179
|
effectiveSubtotal: effectiveSubtotalForCheckout,
|
|
180
|
+
promoEligibleSubtotal: promoEligibleSubtotalForCheckout,
|
|
130
181
|
displayedProductFeeTotal: feeLineItemsWithAddOns.reduce((sum, fee) => sum + fee.totalAmount, 0),
|
|
131
182
|
appliedPromoCode,
|
|
132
183
|
changeBookingPromo: lockedPromoCode
|
|
133
|
-
? {
|
|
184
|
+
? {
|
|
185
|
+
priorAmount: originalReceiptPromoAdjustment.amount,
|
|
186
|
+
originalFullSubtotal: includeLegacyPromoRetentionContext ? originalFullSubtotalBeforePromo : undefined,
|
|
187
|
+
originalAddOnSubtotal: includeLegacyPromoRetentionContext ? originalAddOnSubtotal : undefined,
|
|
188
|
+
newFullSubtotal: includeLegacyPromoRetentionContext ? effectiveSubtotalForCheckout : undefined,
|
|
189
|
+
newAddOnSubtotal: includeLegacyPromoRetentionContext ? addOnTotal : undefined,
|
|
190
|
+
}
|
|
134
191
|
: null,
|
|
135
192
|
};
|
|
136
193
|
|
|
@@ -151,6 +208,7 @@ export function useAdminChangePromoDiscount({
|
|
|
151
208
|
ticketLineItems: lines,
|
|
152
209
|
effectiveSubtotal: sub,
|
|
153
210
|
displayedProductFeeTotal,
|
|
211
|
+
promoEligibleSubtotal: promoSub,
|
|
154
212
|
appliedPromoCode: code,
|
|
155
213
|
changeBookingPromo,
|
|
156
214
|
} = promoDiscountParamsRef.current;
|
|
@@ -177,10 +235,16 @@ export function useAdminChangePromoDiscount({
|
|
|
177
235
|
priorPromoDiscountAmount: changeBookingPromo.priorAmount,
|
|
178
236
|
displayedTicketLines: lines,
|
|
179
237
|
displayedProductFeeTotal,
|
|
238
|
+
promoEligibleSubtotal: promoSub,
|
|
239
|
+
legacyPromoOriginalFullSubtotal: changeBookingPromo.originalFullSubtotal,
|
|
240
|
+
legacyPromoOriginalAddOnSubtotal: changeBookingPromo.originalAddOnSubtotal,
|
|
241
|
+
legacyPromoNewFullSubtotal: changeBookingPromo.newFullSubtotal,
|
|
242
|
+
legacyPromoNewAddOnSubtotal: changeBookingPromo.newAddOnSubtotal,
|
|
180
243
|
}
|
|
181
244
|
: {
|
|
182
245
|
displayedTicketLines: lines,
|
|
183
246
|
displayedProductFeeTotal,
|
|
247
|
+
promoEligibleSubtotal: promoSub,
|
|
184
248
|
},
|
|
185
249
|
)
|
|
186
250
|
.then((res) => {
|
|
@@ -17,6 +17,23 @@ import { normalizeLineLabelForCompare } from './change-booking-flow-helpers';
|
|
|
17
17
|
type AddOnSelection = { addOnId: string; variantId?: string; quantity?: number };
|
|
18
18
|
type AdminCustomReceiptLine = { label: string; amountInput: string; amountSign?: number };
|
|
19
19
|
|
|
20
|
+
function addOnSelectionTotal(selections: AddOnSelection[] | null | undefined, addOns: AddOn[]): number {
|
|
21
|
+
let sum = 0;
|
|
22
|
+
for (const sel of selections ?? []) {
|
|
23
|
+
const addOn = addOns.find((a) => a.addOnId === sel.addOnId);
|
|
24
|
+
if (!addOn) continue;
|
|
25
|
+
const basePrice = addOn.price ?? 0;
|
|
26
|
+
const hasVariant =
|
|
27
|
+
(addOn.variantType === 'single_choice' || addOn.variantType === 'multi_quantity') &&
|
|
28
|
+
sel.variantId;
|
|
29
|
+
const variantAdjustment = hasVariant
|
|
30
|
+
? (addOn.variants?.find((v) => v.id === sel.variantId)?.priceAdjustment ?? 0)
|
|
31
|
+
: 0;
|
|
32
|
+
sum += (basePrice + variantAdjustment) * (sel.quantity ?? 1);
|
|
33
|
+
}
|
|
34
|
+
return sum;
|
|
35
|
+
}
|
|
36
|
+
|
|
20
37
|
interface UseAdminChangeProtectedPricingParams {
|
|
21
38
|
selectedAvailability: Availability | null;
|
|
22
39
|
selectedReturnOption: ReturnOption | null;
|
|
@@ -37,6 +54,7 @@ interface UseAdminChangeProtectedPricingParams {
|
|
|
37
54
|
feeLineItems: OrderSummaryFeeLine[];
|
|
38
55
|
changeFlowBookedFeeUnitByNormalizedLabel: Map<string, number>;
|
|
39
56
|
addOnSelections: AddOnSelection[];
|
|
57
|
+
originalAddOnSelections?: AddOnSelection[] | null;
|
|
40
58
|
addOns: AddOn[];
|
|
41
59
|
adminCustomReceiptLines: AdminCustomReceiptLine[];
|
|
42
60
|
}
|
|
@@ -61,25 +79,18 @@ export function useAdminChangeProtectedPricing({
|
|
|
61
79
|
feeLineItems,
|
|
62
80
|
changeFlowBookedFeeUnitByNormalizedLabel,
|
|
63
81
|
addOnSelections,
|
|
82
|
+
originalAddOnSelections,
|
|
64
83
|
addOns,
|
|
65
84
|
adminCustomReceiptLines,
|
|
66
85
|
}: UseAdminChangeProtectedPricingParams) {
|
|
67
|
-
const addOnTotal = useMemo(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
sel.variantId;
|
|
76
|
-
const variantAdjustment = hasVariant
|
|
77
|
-
? (addOn.variants?.find((v) => v.id === sel.variantId)?.priceAdjustment ?? 0)
|
|
78
|
-
: 0;
|
|
79
|
-
sum += (basePrice + variantAdjustment) * (sel.quantity ?? 1);
|
|
80
|
-
}
|
|
81
|
-
return sum;
|
|
82
|
-
}, [addOnSelections, addOns]);
|
|
86
|
+
const addOnTotal = useMemo(
|
|
87
|
+
() => addOnSelectionTotal(addOnSelections, addOns),
|
|
88
|
+
[addOnSelections, addOns],
|
|
89
|
+
);
|
|
90
|
+
const originalAddOnSubtotal = useMemo(
|
|
91
|
+
() => addOnSelectionTotal(originalAddOnSelections, addOns),
|
|
92
|
+
[originalAddOnSelections, addOns],
|
|
93
|
+
);
|
|
83
94
|
|
|
84
95
|
const currentTicketSubtotal = useMemo(
|
|
85
96
|
() => ticketLineItems.reduce((sum, line) => sum + Math.max(0, Number(line.itemTotal) || 0), 0),
|
|
@@ -215,6 +226,7 @@ export function useAdminChangeProtectedPricing({
|
|
|
215
226
|
[adminCustomReceiptLines],
|
|
216
227
|
);
|
|
217
228
|
const effectiveSubtotalForCheckout = roundMoney(effectiveSubtotal + adminCustomAdjustmentTotal);
|
|
229
|
+
const promoEligibleSubtotalForCheckout = roundMoney(effectiveSubtotalBeforeAddOns + adminCustomAdjustmentTotal);
|
|
218
230
|
|
|
219
231
|
const feeLineItemsForChangeFlowDisplay = useMemo(() => {
|
|
220
232
|
if (!changeFlowApplyReceiptPaidFloors || totalQuantity <= 0) return feeLineItems;
|
|
@@ -274,6 +286,9 @@ export function useAdminChangeProtectedPricing({
|
|
|
274
286
|
effectiveSubtotal,
|
|
275
287
|
adminCustomAdjustmentTotal,
|
|
276
288
|
effectiveSubtotalForCheckout,
|
|
289
|
+
promoEligibleSubtotalForCheckout,
|
|
290
|
+
addOnTotal,
|
|
291
|
+
originalAddOnSubtotal,
|
|
277
292
|
feeLineItemsForChangeFlowDisplay,
|
|
278
293
|
feeLineItemsWithAddOns,
|
|
279
294
|
};
|
|
@@ -48,6 +48,8 @@ interface UseChangeBookingPriceSummaryParams {
|
|
|
48
48
|
feeLineItems: OrderSummaryFeeLine[];
|
|
49
49
|
changeFlowBookedFeeUnitByNormalizedLabel: Map<string, number>;
|
|
50
50
|
addOnSelections: AddOnSelection[];
|
|
51
|
+
originalAddOnSelections?: AddOnSelection[] | null;
|
|
52
|
+
originalProductOptionId?: string | null;
|
|
51
53
|
addOns: AddOn[];
|
|
52
54
|
quantities: Record<string, number>;
|
|
53
55
|
pricing: PricingRate[];
|
|
@@ -88,6 +90,8 @@ export function useChangeBookingPriceSummary({
|
|
|
88
90
|
feeLineItems,
|
|
89
91
|
changeFlowBookedFeeUnitByNormalizedLabel,
|
|
90
92
|
addOnSelections,
|
|
93
|
+
originalAddOnSelections,
|
|
94
|
+
originalProductOptionId,
|
|
91
95
|
addOns,
|
|
92
96
|
quantities,
|
|
93
97
|
pricing,
|
|
@@ -113,6 +117,9 @@ export function useChangeBookingPriceSummary({
|
|
|
113
117
|
ticketLineItemsForChangeFlowDisplay,
|
|
114
118
|
effectiveSubtotal,
|
|
115
119
|
feeLineItemsWithAddOns,
|
|
120
|
+
promoEligibleSubtotal,
|
|
121
|
+
addOnTotal,
|
|
122
|
+
originalAddOnSubtotal,
|
|
116
123
|
} = useChangeBookingProtectedPricing({
|
|
117
124
|
selectedAvailability,
|
|
118
125
|
selectedReturnOption,
|
|
@@ -132,6 +139,7 @@ export function useChangeBookingPriceSummary({
|
|
|
132
139
|
feeLineItems,
|
|
133
140
|
changeFlowBookedFeeUnitByNormalizedLabel,
|
|
134
141
|
addOnSelections,
|
|
142
|
+
originalAddOnSelections,
|
|
135
143
|
addOns,
|
|
136
144
|
});
|
|
137
145
|
|
|
@@ -151,6 +159,10 @@ export function useChangeBookingPriceSummary({
|
|
|
151
159
|
ticketLineItemsForChangeFlowDisplay,
|
|
152
160
|
feeLineItems,
|
|
153
161
|
effectiveSubtotal,
|
|
162
|
+
promoEligibleSubtotal,
|
|
163
|
+
addOnTotal,
|
|
164
|
+
originalAddOnSubtotal,
|
|
165
|
+
originalProductOptionId,
|
|
154
166
|
lockedPromoCode,
|
|
155
167
|
originalReceipt,
|
|
156
168
|
isTaxIncludedInPrice,
|
|
@@ -21,12 +21,29 @@ interface UseChangeBookingPromoDiscountParams {
|
|
|
21
21
|
ticketLineItemsForChangeFlowDisplay: OrderSummaryTicketLine[];
|
|
22
22
|
feeLineItems: OrderSummaryFeeLine[];
|
|
23
23
|
effectiveSubtotal: number;
|
|
24
|
+
promoEligibleSubtotal?: number;
|
|
25
|
+
addOnTotal?: number;
|
|
26
|
+
originalAddOnSubtotal?: number;
|
|
27
|
+
originalProductOptionId?: string | null;
|
|
24
28
|
lockedPromoCode: string | null;
|
|
25
29
|
originalReceipt: ChangeBookingFlowProps['originalReceipt'];
|
|
26
30
|
isTaxIncludedInPrice: boolean;
|
|
27
31
|
pricingConfig: PricingConfig | null;
|
|
28
32
|
}
|
|
29
33
|
|
|
34
|
+
function originalReceiptGrossSubtotalBeforePromo(
|
|
35
|
+
originalReceipt: ChangeBookingFlowProps['originalReceipt'],
|
|
36
|
+
): number {
|
|
37
|
+
const chargeTypes = new Set(['TICKET', 'FEE', 'RETURN_OPTION', 'CANCELLATION_UPGRADE']);
|
|
38
|
+
return (originalReceipt?.lineItems ?? []).reduce((sum, line) => {
|
|
39
|
+
const amount = Number(line.amount) || 0;
|
|
40
|
+
if (amount <= 0) return sum;
|
|
41
|
+
const type = String(line.type ?? '').trim().toUpperCase();
|
|
42
|
+
if (!chargeTypes.has(type)) return sum;
|
|
43
|
+
return sum + amount;
|
|
44
|
+
}, 0);
|
|
45
|
+
}
|
|
46
|
+
|
|
30
47
|
export function useChangeBookingPromoDiscount({
|
|
31
48
|
appliedPromoCode,
|
|
32
49
|
selectedAvailability,
|
|
@@ -39,6 +56,10 @@ export function useChangeBookingPromoDiscount({
|
|
|
39
56
|
ticketLineItemsForChangeFlowDisplay,
|
|
40
57
|
feeLineItems,
|
|
41
58
|
effectiveSubtotal,
|
|
59
|
+
promoEligibleSubtotal = effectiveSubtotal,
|
|
60
|
+
addOnTotal = Math.max(0, effectiveSubtotal - promoEligibleSubtotal),
|
|
61
|
+
originalAddOnSubtotal = 0,
|
|
62
|
+
originalProductOptionId,
|
|
42
63
|
lockedPromoCode,
|
|
43
64
|
originalReceipt,
|
|
44
65
|
isTaxIncludedInPrice,
|
|
@@ -62,15 +83,36 @@ export function useChangeBookingPromoDiscount({
|
|
|
62
83
|
selectedAvailability: null as Availability | null,
|
|
63
84
|
ticketLineItems: [] as Array<{ category: string; qty: number; itemTotal: number }>,
|
|
64
85
|
effectiveSubtotal: 0,
|
|
86
|
+
promoEligibleSubtotal: 0,
|
|
65
87
|
displayedProductFeeTotal: 0,
|
|
66
88
|
appliedPromoCode: null as string | null,
|
|
67
|
-
changeBookingPromo: null as {
|
|
89
|
+
changeBookingPromo: null as {
|
|
90
|
+
priorAmount?: number;
|
|
91
|
+
originalFullSubtotal?: number;
|
|
92
|
+
originalAddOnSubtotal?: number;
|
|
93
|
+
newFullSubtotal?: number;
|
|
94
|
+
newAddOnSubtotal?: number;
|
|
95
|
+
} | null,
|
|
68
96
|
});
|
|
69
97
|
|
|
98
|
+
const originalFullSubtotalBeforePromo = useMemo(
|
|
99
|
+
() => originalReceiptGrossSubtotalBeforePromo(originalReceipt),
|
|
100
|
+
[originalReceipt],
|
|
101
|
+
);
|
|
102
|
+
|
|
70
103
|
const selectedAvailabilityOptionId = useMemo(
|
|
71
104
|
() => (selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : ''),
|
|
72
105
|
[selectedAvailability],
|
|
73
106
|
);
|
|
107
|
+
const includeLegacyPromoRetentionContext = useMemo(() => {
|
|
108
|
+
const originalOptionId = originalProductOptionId?.trim();
|
|
109
|
+
return Boolean(
|
|
110
|
+
lockedPromoCode &&
|
|
111
|
+
originalOptionId &&
|
|
112
|
+
selectedAvailabilityOptionId &&
|
|
113
|
+
selectedAvailabilityOptionId === originalOptionId,
|
|
114
|
+
);
|
|
115
|
+
}, [lockedPromoCode, originalProductOptionId, selectedAvailabilityOptionId]);
|
|
74
116
|
|
|
75
117
|
const promoDiscountFetchKey = useMemo(() => {
|
|
76
118
|
if (!appliedPromoCode || !selectedAvailability || totalQuantity === 0) return '';
|
|
@@ -92,6 +134,10 @@ export function useChangeBookingPromoDiscount({
|
|
|
92
134
|
.join('|'),
|
|
93
135
|
String(Math.round(feeLineItems.reduce((sum, fee) => sum + fee.totalAmount, 0) * 100)),
|
|
94
136
|
String(Math.round(effectiveSubtotal * 100)),
|
|
137
|
+
String(Math.round(promoEligibleSubtotal * 100)),
|
|
138
|
+
String(Math.round(addOnTotal * 100)),
|
|
139
|
+
includeLegacyPromoRetentionContext ? String(Math.round(originalFullSubtotalBeforePromo * 100)) : '',
|
|
140
|
+
includeLegacyPromoRetentionContext ? String(Math.round(originalAddOnSubtotal * 100)) : '',
|
|
95
141
|
lockedPromoCode ? String(Math.round((originalReceipt?.promoAmount ?? 0) * 100)) : '',
|
|
96
142
|
].join('::');
|
|
97
143
|
}, [
|
|
@@ -107,6 +153,11 @@ export function useChangeBookingPromoDiscount({
|
|
|
107
153
|
ticketLineItemsForChangeFlowDisplay,
|
|
108
154
|
feeLineItems,
|
|
109
155
|
effectiveSubtotal,
|
|
156
|
+
promoEligibleSubtotal,
|
|
157
|
+
addOnTotal,
|
|
158
|
+
includeLegacyPromoRetentionContext,
|
|
159
|
+
originalFullSubtotalBeforePromo,
|
|
160
|
+
originalAddOnSubtotal,
|
|
110
161
|
lockedPromoCode,
|
|
111
162
|
originalReceipt?.promoAmount,
|
|
112
163
|
]);
|
|
@@ -119,10 +170,17 @@ export function useChangeBookingPromoDiscount({
|
|
|
119
170
|
itemTotal: line.itemTotal,
|
|
120
171
|
})),
|
|
121
172
|
effectiveSubtotal,
|
|
173
|
+
promoEligibleSubtotal,
|
|
122
174
|
displayedProductFeeTotal: feeLineItems.reduce((sum, fee) => sum + fee.totalAmount, 0),
|
|
123
175
|
appliedPromoCode,
|
|
124
176
|
changeBookingPromo: lockedPromoCode
|
|
125
|
-
? {
|
|
177
|
+
? {
|
|
178
|
+
priorAmount: originalReceipt?.promoAmount,
|
|
179
|
+
originalFullSubtotal: includeLegacyPromoRetentionContext ? originalFullSubtotalBeforePromo : undefined,
|
|
180
|
+
originalAddOnSubtotal: includeLegacyPromoRetentionContext ? originalAddOnSubtotal : undefined,
|
|
181
|
+
newFullSubtotal: includeLegacyPromoRetentionContext ? effectiveSubtotal : undefined,
|
|
182
|
+
newAddOnSubtotal: includeLegacyPromoRetentionContext ? addOnTotal : undefined,
|
|
183
|
+
}
|
|
126
184
|
: null,
|
|
127
185
|
};
|
|
128
186
|
|
|
@@ -143,6 +201,7 @@ export function useChangeBookingPromoDiscount({
|
|
|
143
201
|
ticketLineItems: lines,
|
|
144
202
|
effectiveSubtotal: sub,
|
|
145
203
|
displayedProductFeeTotal,
|
|
204
|
+
promoEligibleSubtotal: promoSub,
|
|
146
205
|
appliedPromoCode: code,
|
|
147
206
|
changeBookingPromo,
|
|
148
207
|
} = promoDiscountParamsRef.current;
|
|
@@ -169,10 +228,16 @@ export function useChangeBookingPromoDiscount({
|
|
|
169
228
|
priorPromoDiscountAmount: changeBookingPromo.priorAmount,
|
|
170
229
|
displayedTicketLines: lines,
|
|
171
230
|
displayedProductFeeTotal,
|
|
231
|
+
promoEligibleSubtotal: promoSub,
|
|
232
|
+
legacyPromoOriginalFullSubtotal: changeBookingPromo.originalFullSubtotal,
|
|
233
|
+
legacyPromoOriginalAddOnSubtotal: changeBookingPromo.originalAddOnSubtotal,
|
|
234
|
+
legacyPromoNewFullSubtotal: changeBookingPromo.newFullSubtotal,
|
|
235
|
+
legacyPromoNewAddOnSubtotal: changeBookingPromo.newAddOnSubtotal,
|
|
172
236
|
}
|
|
173
237
|
: {
|
|
174
238
|
displayedTicketLines: lines,
|
|
175
239
|
displayedProductFeeTotal,
|
|
240
|
+
promoEligibleSubtotal: promoSub,
|
|
176
241
|
},
|
|
177
242
|
)
|
|
178
243
|
.then((res) => {
|
|
@@ -15,6 +15,23 @@ import { normalizeLineLabelForCompare } from './change-booking-flow-helpers';
|
|
|
15
15
|
|
|
16
16
|
type AddOnSelection = { addOnId: string; variantId?: string; quantity?: number };
|
|
17
17
|
|
|
18
|
+
function addOnSelectionTotal(selections: AddOnSelection[] | null | undefined, addOns: AddOn[]): number {
|
|
19
|
+
let sum = 0;
|
|
20
|
+
for (const sel of selections ?? []) {
|
|
21
|
+
const addOn = addOns.find((a) => a.addOnId === sel.addOnId);
|
|
22
|
+
if (!addOn) continue;
|
|
23
|
+
const basePrice = addOn.price ?? 0;
|
|
24
|
+
const hasVariant =
|
|
25
|
+
(addOn.variantType === 'single_choice' || addOn.variantType === 'multi_quantity') &&
|
|
26
|
+
sel.variantId;
|
|
27
|
+
const variantAdjustment = hasVariant
|
|
28
|
+
? (addOn.variants?.find((v) => v.id === sel.variantId)?.priceAdjustment ?? 0)
|
|
29
|
+
: 0;
|
|
30
|
+
sum += (basePrice + variantAdjustment) * (sel.quantity ?? 1);
|
|
31
|
+
}
|
|
32
|
+
return sum;
|
|
33
|
+
}
|
|
34
|
+
|
|
18
35
|
interface UseChangeBookingProtectedPricingParams {
|
|
19
36
|
selectedAvailability: Availability | null;
|
|
20
37
|
selectedReturnOption: ReturnOption | null;
|
|
@@ -34,6 +51,7 @@ interface UseChangeBookingProtectedPricingParams {
|
|
|
34
51
|
feeLineItems: OrderSummaryFeeLine[];
|
|
35
52
|
changeFlowBookedFeeUnitByNormalizedLabel: Map<string, number>;
|
|
36
53
|
addOnSelections: AddOnSelection[];
|
|
54
|
+
originalAddOnSelections?: AddOnSelection[] | null;
|
|
37
55
|
addOns: AddOn[];
|
|
38
56
|
}
|
|
39
57
|
|
|
@@ -56,6 +74,7 @@ export function useChangeBookingProtectedPricing({
|
|
|
56
74
|
feeLineItems,
|
|
57
75
|
changeFlowBookedFeeUnitByNormalizedLabel,
|
|
58
76
|
addOnSelections,
|
|
77
|
+
originalAddOnSelections,
|
|
59
78
|
addOns,
|
|
60
79
|
}: UseChangeBookingProtectedPricingParams) {
|
|
61
80
|
const changeFlowProtectedTicketSubtotal = useMemo(() => {
|
|
@@ -87,22 +106,14 @@ export function useChangeBookingProtectedPricing({
|
|
|
87
106
|
changeFlowProtectedReceiptPricing,
|
|
88
107
|
]);
|
|
89
108
|
|
|
90
|
-
const addOnTotal = useMemo(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
sel.variantId;
|
|
99
|
-
const variantAdjustment = hasVariant
|
|
100
|
-
? (addOn.variants?.find((v) => v.id === sel.variantId)?.priceAdjustment ?? 0)
|
|
101
|
-
: 0;
|
|
102
|
-
sum += (basePrice + variantAdjustment) * (sel.quantity ?? 1);
|
|
103
|
-
}
|
|
104
|
-
return sum;
|
|
105
|
-
}, [addOnSelections, addOns]);
|
|
109
|
+
const addOnTotal = useMemo(
|
|
110
|
+
() => addOnSelectionTotal(addOnSelections, addOns),
|
|
111
|
+
[addOnSelections, addOns],
|
|
112
|
+
);
|
|
113
|
+
const originalAddOnSubtotal = useMemo(
|
|
114
|
+
() => addOnSelectionTotal(originalAddOnSelections, addOns),
|
|
115
|
+
[originalAddOnSelections, addOns],
|
|
116
|
+
);
|
|
106
117
|
|
|
107
118
|
const currentTicketSubtotal = useMemo(
|
|
108
119
|
() => ticketLineItems.reduce((sum, line) => sum + Math.max(0, Number(line.itemTotal) || 0), 0),
|
|
@@ -220,6 +231,7 @@ export function useChangeBookingProtectedPricing({
|
|
|
220
231
|
changeFlowProtectedReturnAdjustment
|
|
221
232
|
: subtotal;
|
|
222
233
|
const effectiveSubtotal = effectiveSubtotalBeforeAddOns + addOnTotal;
|
|
234
|
+
const promoEligibleSubtotal = effectiveSubtotalBeforeAddOns;
|
|
223
235
|
|
|
224
236
|
const feeLineItemsWithAddOns = useMemo(() => {
|
|
225
237
|
const addOnLines = addOnSelections
|
|
@@ -251,6 +263,9 @@ export function useChangeBookingProtectedPricing({
|
|
|
251
263
|
checkoutReturnLineAmount,
|
|
252
264
|
ticketLineItemsForChangeFlowDisplay,
|
|
253
265
|
effectiveSubtotal,
|
|
266
|
+
promoEligibleSubtotal,
|
|
267
|
+
addOnTotal,
|
|
268
|
+
originalAddOnSubtotal,
|
|
254
269
|
feeLineItemsWithAddOns,
|
|
255
270
|
};
|
|
256
271
|
}
|
|
@@ -112,10 +112,21 @@ export function useStandardBookingPriceSummary({
|
|
|
112
112
|
return Math.max(0, selectedReturnOption.vacancies ?? 0);
|
|
113
113
|
}, [selectedReturnOption]);
|
|
114
114
|
|
|
115
|
+
const pricingAvailability = useMemo(() => {
|
|
116
|
+
if (!selectedAvailability || !selectedReturnOption?.rates?.length) return selectedAvailability;
|
|
117
|
+
const outboundVacancies = Math.max(0, selectedAvailability.vacancies ?? 0);
|
|
118
|
+
const returnVacancies = Math.max(0, selectedReturnOption.vacancies ?? 0);
|
|
119
|
+
return {
|
|
120
|
+
...selectedAvailability,
|
|
121
|
+
vacancies: Math.min(outboundVacancies, returnVacancies),
|
|
122
|
+
rates: selectedReturnOption.rates,
|
|
123
|
+
};
|
|
124
|
+
}, [selectedAvailability, selectedReturnOption?.rates, selectedReturnOption?.vacancies]);
|
|
125
|
+
|
|
115
126
|
const pricing = useMemo(
|
|
116
127
|
() =>
|
|
117
128
|
buildPricingFromAvailability(
|
|
118
|
-
|
|
129
|
+
pricingAvailability,
|
|
119
130
|
activeOptions,
|
|
120
131
|
precomputedPricesByOption,
|
|
121
132
|
currency,
|
|
@@ -124,7 +135,7 @@ export function useStandardBookingPriceSummary({
|
|
|
124
135
|
isSimplifiedPricingView,
|
|
125
136
|
),
|
|
126
137
|
[
|
|
127
|
-
|
|
138
|
+
pricingAvailability,
|
|
128
139
|
activeOptions,
|
|
129
140
|
precomputedPricesByOption,
|
|
130
141
|
currency,
|
|
@@ -145,7 +156,7 @@ export function useStandardBookingPriceSummary({
|
|
|
145
156
|
const selectedOption = activeOptions.find(
|
|
146
157
|
(option) =>
|
|
147
158
|
option.optionId ===
|
|
148
|
-
(
|
|
159
|
+
(pricingAvailability ? getAvailabilityOptionId(pricingAvailability) : ''),
|
|
149
160
|
);
|
|
150
161
|
const basePriceCAD = selectedOption?.pricing?.[category.toUpperCase()] ?? 0;
|
|
151
162
|
return computePriceBreakdown(
|
|
@@ -160,7 +171,7 @@ export function useStandardBookingPriceSummary({
|
|
|
160
171
|
isSimplifiedPricingView,
|
|
161
172
|
);
|
|
162
173
|
},
|
|
163
|
-
[pricingConfig, activeOptions,
|
|
174
|
+
[pricingConfig, activeOptions, pricingAvailability, currency, hasFees, isSimplifiedPricingView],
|
|
164
175
|
);
|
|
165
176
|
|
|
166
177
|
const orderSummary = useMemo(
|
|
@@ -235,6 +246,7 @@ export function useStandardBookingPriceSummary({
|
|
|
235
246
|
const checkoutReturnLineAmount = returnPriceAdjustment;
|
|
236
247
|
const effectiveSubtotalBeforeAddOns = subtotal;
|
|
237
248
|
const effectiveSubtotal = effectiveSubtotalBeforeAddOns + addOnTotal;
|
|
249
|
+
const promoEligibleSubtotal = effectiveSubtotalBeforeAddOns;
|
|
238
250
|
|
|
239
251
|
const feeLineItemsWithAddOns = useMemo(() => {
|
|
240
252
|
const addOnLines = addOnSelections
|
|
@@ -352,7 +364,7 @@ export function useStandardBookingPriceSummary({
|
|
|
352
364
|
totalPrice,
|
|
353
365
|
} = useStandardBookingPromoDiscount({
|
|
354
366
|
appliedPromoCode,
|
|
355
|
-
selectedAvailability,
|
|
367
|
+
selectedAvailability: pricingAvailability,
|
|
356
368
|
totalQuantity,
|
|
357
369
|
productCompanyId,
|
|
358
370
|
envCompanyId,
|
|
@@ -362,6 +374,7 @@ export function useStandardBookingPriceSummary({
|
|
|
362
374
|
ticketLineItems,
|
|
363
375
|
feeLineItems,
|
|
364
376
|
effectiveSubtotal,
|
|
377
|
+
promoEligibleSubtotal,
|
|
365
378
|
isTaxIncludedInPrice,
|
|
366
379
|
pricingConfig,
|
|
367
380
|
});
|
|
@@ -11,6 +11,8 @@ import type {
|
|
|
11
11
|
import type { Currency } from './CurrencySwitcher';
|
|
12
12
|
import { getAvailabilityOptionId } from './standard-booking-availability';
|
|
13
13
|
|
|
14
|
+
const normalizeMoneyZero = (value: number) => (Math.abs(value) < 0.005 ? 0 : value);
|
|
15
|
+
|
|
14
16
|
interface UseStandardBookingPromoDiscountParams {
|
|
15
17
|
appliedPromoCode: string | null;
|
|
16
18
|
selectedAvailability: Availability | null;
|
|
@@ -23,6 +25,7 @@ interface UseStandardBookingPromoDiscountParams {
|
|
|
23
25
|
ticketLineItems: OrderSummaryTicketLine[];
|
|
24
26
|
feeLineItems: OrderSummaryFeeLine[];
|
|
25
27
|
effectiveSubtotal: number;
|
|
28
|
+
promoEligibleSubtotal?: number;
|
|
26
29
|
isTaxIncludedInPrice: boolean;
|
|
27
30
|
pricingConfig: PricingConfig | null;
|
|
28
31
|
}
|
|
@@ -39,6 +42,7 @@ export function useStandardBookingPromoDiscount({
|
|
|
39
42
|
ticketLineItems,
|
|
40
43
|
feeLineItems,
|
|
41
44
|
effectiveSubtotal,
|
|
45
|
+
promoEligibleSubtotal = effectiveSubtotal,
|
|
42
46
|
isTaxIncludedInPrice,
|
|
43
47
|
pricingConfig,
|
|
44
48
|
}: UseStandardBookingPromoDiscountParams) {
|
|
@@ -60,6 +64,7 @@ export function useStandardBookingPromoDiscount({
|
|
|
60
64
|
selectedAvailability: null as Availability | null,
|
|
61
65
|
ticketLineItems: [] as Array<{ category: string; qty: number; itemTotal: number }>,
|
|
62
66
|
effectiveSubtotal: 0,
|
|
67
|
+
promoEligibleSubtotal: 0,
|
|
63
68
|
displayedProductFeeTotal: 0,
|
|
64
69
|
appliedPromoCode: null as string | null,
|
|
65
70
|
});
|
|
@@ -89,6 +94,7 @@ export function useStandardBookingPromoDiscount({
|
|
|
89
94
|
.join('|'),
|
|
90
95
|
String(Math.round(feeLineItems.reduce((sum, fee) => sum + fee.totalAmount, 0) * 100)),
|
|
91
96
|
String(Math.round(effectiveSubtotal * 100)),
|
|
97
|
+
String(Math.round(promoEligibleSubtotal * 100)),
|
|
92
98
|
].join('::');
|
|
93
99
|
}, [
|
|
94
100
|
appliedPromoCode,
|
|
@@ -103,6 +109,7 @@ export function useStandardBookingPromoDiscount({
|
|
|
103
109
|
ticketLineItems,
|
|
104
110
|
feeLineItems,
|
|
105
111
|
effectiveSubtotal,
|
|
112
|
+
promoEligibleSubtotal,
|
|
106
113
|
]);
|
|
107
114
|
|
|
108
115
|
promoDiscountParamsRef.current = {
|
|
@@ -113,6 +120,7 @@ export function useStandardBookingPromoDiscount({
|
|
|
113
120
|
itemTotal: line.itemTotal,
|
|
114
121
|
})),
|
|
115
122
|
effectiveSubtotal,
|
|
123
|
+
promoEligibleSubtotal,
|
|
116
124
|
displayedProductFeeTotal: feeLineItems.reduce((sum, fee) => sum + fee.totalAmount, 0),
|
|
117
125
|
appliedPromoCode,
|
|
118
126
|
};
|
|
@@ -134,6 +142,7 @@ export function useStandardBookingPromoDiscount({
|
|
|
134
142
|
ticketLineItems: lines,
|
|
135
143
|
effectiveSubtotal: sub,
|
|
136
144
|
displayedProductFeeTotal,
|
|
145
|
+
promoEligibleSubtotal: promoSub,
|
|
137
146
|
appliedPromoCode: code,
|
|
138
147
|
} = promoDiscountParamsRef.current;
|
|
139
148
|
if (!code || !sel) return;
|
|
@@ -158,6 +167,7 @@ export function useStandardBookingPromoDiscount({
|
|
|
158
167
|
totalCapacity: sel.totalCapacity,
|
|
159
168
|
displayedTicketLines: lines,
|
|
160
169
|
displayedProductFeeTotal,
|
|
170
|
+
promoEligibleSubtotal: promoSub,
|
|
161
171
|
},
|
|
162
172
|
)
|
|
163
173
|
.then((res) => {
|
|
@@ -188,9 +198,9 @@ export function useStandardBookingPromoDiscount({
|
|
|
188
198
|
const taxOnSubtotal = isTaxIncludedInPrice ? 0 : effectiveSubtotal * (pricingConfig?.taxRate ?? 0);
|
|
189
199
|
const effectiveTax =
|
|
190
200
|
effectivePromoDiscountAmount > 0 && !isGiftCard && !isVoucher
|
|
191
|
-
? (effectiveSubtotal - effectivePromoDiscountAmount) * (pricingConfig?.taxRate ?? 0)
|
|
192
|
-
: taxOnSubtotal;
|
|
193
|
-
const totalPrice = effectiveSubtotal + effectiveTax - effectivePromoDiscountAmount;
|
|
201
|
+
? normalizeMoneyZero((effectiveSubtotal - effectivePromoDiscountAmount) * (pricingConfig?.taxRate ?? 0))
|
|
202
|
+
: normalizeMoneyZero(taxOnSubtotal);
|
|
203
|
+
const totalPrice = normalizeMoneyZero(effectiveSubtotal + effectiveTax - effectivePromoDiscountAmount);
|
|
194
204
|
|
|
195
205
|
return {
|
|
196
206
|
promoDiscountAmount,
|
package/src/lib/booking-api.ts
CHANGED
|
@@ -980,6 +980,12 @@ export interface GetPromoDiscountBookingChangeOptions {
|
|
|
980
980
|
displayedTicketLines?: Array<{ category: string; qty: number; itemTotal: number }>;
|
|
981
981
|
/** Displayed product fee total from the cart, excluding add-ons, return, and cancellation lines. */
|
|
982
982
|
displayedProductFeeTotal?: number;
|
|
983
|
+
/** Promo-eligible subtotal excluding add-ons; normal promos use this, gift cards/vouchers use subtotal. */
|
|
984
|
+
promoEligibleSubtotal?: number;
|
|
985
|
+
legacyPromoOriginalFullSubtotal?: number;
|
|
986
|
+
legacyPromoOriginalAddOnSubtotal?: number;
|
|
987
|
+
legacyPromoNewFullSubtotal?: number;
|
|
988
|
+
legacyPromoNewAddOnSubtotal?: number;
|
|
983
989
|
}
|
|
984
990
|
|
|
985
991
|
export async function getPromoDiscount(
|
|
@@ -1004,6 +1010,13 @@ export async function getPromoDiscount(
|
|
|
1004
1010
|
});
|
|
1005
1011
|
if (dateTime) params.set('dateTime', dateTime);
|
|
1006
1012
|
if (subtotal != null && subtotal > 0) params.set('subtotal', String(subtotal));
|
|
1013
|
+
if (
|
|
1014
|
+
bookingChange?.promoEligibleSubtotal != null &&
|
|
1015
|
+
Number.isFinite(bookingChange.promoEligibleSubtotal) &&
|
|
1016
|
+
bookingChange.promoEligibleSubtotal >= 0
|
|
1017
|
+
) {
|
|
1018
|
+
params.set('promoEligibleSubtotal', String(bookingChange.promoEligibleSubtotal));
|
|
1019
|
+
}
|
|
1007
1020
|
if (
|
|
1008
1021
|
bookingChange?.availabilityCount != null &&
|
|
1009
1022
|
Number.isFinite(bookingChange.availabilityCount)
|
|
@@ -1039,6 +1052,34 @@ export async function getPromoDiscount(
|
|
|
1039
1052
|
) {
|
|
1040
1053
|
params.set('priorPromoDiscountAmount', String(bookingChange.priorPromoDiscountAmount));
|
|
1041
1054
|
}
|
|
1055
|
+
if (
|
|
1056
|
+
bookingChange?.legacyPromoOriginalFullSubtotal != null &&
|
|
1057
|
+
Number.isFinite(bookingChange.legacyPromoOriginalFullSubtotal) &&
|
|
1058
|
+
bookingChange.legacyPromoOriginalFullSubtotal >= 0
|
|
1059
|
+
) {
|
|
1060
|
+
params.set('legacyPromoOriginalFullSubtotal', String(bookingChange.legacyPromoOriginalFullSubtotal));
|
|
1061
|
+
}
|
|
1062
|
+
if (
|
|
1063
|
+
bookingChange?.legacyPromoOriginalAddOnSubtotal != null &&
|
|
1064
|
+
Number.isFinite(bookingChange.legacyPromoOriginalAddOnSubtotal) &&
|
|
1065
|
+
bookingChange.legacyPromoOriginalAddOnSubtotal >= 0
|
|
1066
|
+
) {
|
|
1067
|
+
params.set('legacyPromoOriginalAddOnSubtotal', String(bookingChange.legacyPromoOriginalAddOnSubtotal));
|
|
1068
|
+
}
|
|
1069
|
+
if (
|
|
1070
|
+
bookingChange?.legacyPromoNewFullSubtotal != null &&
|
|
1071
|
+
Number.isFinite(bookingChange.legacyPromoNewFullSubtotal) &&
|
|
1072
|
+
bookingChange.legacyPromoNewFullSubtotal >= 0
|
|
1073
|
+
) {
|
|
1074
|
+
params.set('legacyPromoNewFullSubtotal', String(bookingChange.legacyPromoNewFullSubtotal));
|
|
1075
|
+
}
|
|
1076
|
+
if (
|
|
1077
|
+
bookingChange?.legacyPromoNewAddOnSubtotal != null &&
|
|
1078
|
+
Number.isFinite(bookingChange.legacyPromoNewAddOnSubtotal) &&
|
|
1079
|
+
bookingChange.legacyPromoNewAddOnSubtotal >= 0
|
|
1080
|
+
) {
|
|
1081
|
+
params.set('legacyPromoNewAddOnSubtotal', String(bookingChange.legacyPromoNewAddOnSubtotal));
|
|
1082
|
+
}
|
|
1042
1083
|
const res = await fetchBookingGetWithRetry(`${API_BASE}/1/get-promo-discount?${params}`);
|
|
1043
1084
|
if (!res.ok) {
|
|
1044
1085
|
const err = await res.json();
|
|
@@ -1669,6 +1710,8 @@ export interface ReturnOption {
|
|
|
1669
1710
|
bookedCapacity?: number;
|
|
1670
1711
|
pricesByCategory?: { retailPrices: Array<{ category: string; price: number }> };
|
|
1671
1712
|
priceAdjustmentByCurrency?: Record<string, number>;
|
|
1713
|
+
/** Ticket rates priced using this specific return option's capacity floor. */
|
|
1714
|
+
rates?: AvailabilityRate[];
|
|
1672
1715
|
returnLocation: string;
|
|
1673
1716
|
mostPopular?: boolean;
|
|
1674
1717
|
}
|