@ticketboothapp/booking 1.2.97 → 1.2.98
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
|
@@ -153,6 +153,49 @@ function omitZeroAmountPromoDiscountSummaryLines(lines: PriceSummaryLine[]): Pri
|
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
function isPromoDiscountGiftCardOrVoucherLine(line: {
|
|
157
|
+
type?: string | null;
|
|
158
|
+
label?: string | null;
|
|
159
|
+
amount?: number | null;
|
|
160
|
+
}): boolean {
|
|
161
|
+
const type = String(line.type ?? '').trim().toUpperCase();
|
|
162
|
+
if (type === 'PROMO_CODE' || type === 'DISCOUNT' || type === 'GIFT_CARD' || type === 'VOUCHER') {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
const label = String(line.label ?? '').trim().toLowerCase();
|
|
166
|
+
return /promo|discount|voucher|gift\s*card/.test(label);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function deriveOriginalReceiptPromoAdjustment(originalReceipt: ChangeBookingFlowProps['originalReceipt']): {
|
|
170
|
+
amount: number;
|
|
171
|
+
label: string | null;
|
|
172
|
+
type: 'PROMO_CODE' | 'GIFT_CARD' | 'VOUCHER' | 'DISCOUNT';
|
|
173
|
+
} {
|
|
174
|
+
let amount = Math.max(0, Number(originalReceipt?.promoAmount) || 0);
|
|
175
|
+
let label = originalReceipt?.promoLabel?.trim() || null;
|
|
176
|
+
let type: 'PROMO_CODE' | 'GIFT_CARD' | 'VOUCHER' | 'DISCOUNT' = 'PROMO_CODE';
|
|
177
|
+
|
|
178
|
+
for (const line of originalReceipt?.lineItems ?? []) {
|
|
179
|
+
if (!isPromoDiscountGiftCardOrVoucherLine(line)) continue;
|
|
180
|
+
const lineAmount = Number(line.amount) || 0;
|
|
181
|
+
if (lineAmount < -0.005) {
|
|
182
|
+
amount = Math.max(amount, Math.abs(lineAmount));
|
|
183
|
+
if (!label) label = line.label?.trim() || null;
|
|
184
|
+
}
|
|
185
|
+
const lineType = String(line.type ?? '').trim().toUpperCase();
|
|
186
|
+
const lineLabel = String(line.label ?? '').trim().toLowerCase();
|
|
187
|
+
if (lineType === 'GIFT_CARD' || /gift\s*card/.test(lineLabel)) {
|
|
188
|
+
type = 'GIFT_CARD';
|
|
189
|
+
} else if (lineType === 'VOUCHER' || /voucher/.test(lineLabel)) {
|
|
190
|
+
type = 'VOUCHER';
|
|
191
|
+
} else if (lineType === 'DISCOUNT' || /discount/.test(lineLabel)) {
|
|
192
|
+
type = 'DISCOUNT';
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return { amount: roundMoney(amount), label, type };
|
|
197
|
+
}
|
|
198
|
+
|
|
156
199
|
function resolveTicketQtyFromQuantities(
|
|
157
200
|
category: string,
|
|
158
201
|
quantities: Record<string, number>,
|
|
@@ -3265,49 +3308,6 @@ export function AdminChangeBookingFlow({
|
|
|
3265
3308
|
providerEditableLineByNormalizedLabel,
|
|
3266
3309
|
]);
|
|
3267
3310
|
|
|
3268
|
-
const checkoutPriceSummaryLinesForCheckout = useMemo(() => {
|
|
3269
|
-
let raw: PriceSummaryLine[];
|
|
3270
|
-
if (suppressSelfServeCurrencyUi && selfServePricingConfirmed) {
|
|
3271
|
-
const serverLines = latestChangeQuote?.serverPreview?.priceSummaryLines;
|
|
3272
|
-
raw = serverLines && serverLines.length > 0 ? serverLines : checkoutPriceSummaryLines;
|
|
3273
|
-
} else {
|
|
3274
|
-
raw = checkoutPriceSummaryLines;
|
|
3275
|
-
}
|
|
3276
|
-
return omitZeroAmountPromoDiscountSummaryLines(raw);
|
|
3277
|
-
}, [
|
|
3278
|
-
suppressSelfServeCurrencyUi,
|
|
3279
|
-
selfServePricingConfirmed,
|
|
3280
|
-
checkoutPriceSummaryLines,
|
|
3281
|
-
latestChangeQuote?.serverPreview?.priceSummaryLines,
|
|
3282
|
-
]);
|
|
3283
|
-
|
|
3284
|
-
/** Receipt/server lines already include {@link PriceSummary}'s TAX row — do not also pass `taxAmount` or it duplicates. */
|
|
3285
|
-
const priceSummaryLinesIncludeTaxRow = useMemo(
|
|
3286
|
-
() =>
|
|
3287
|
-
checkoutPriceSummaryLinesForCheckout.some(
|
|
3288
|
-
(line) => line.kind === 'line' && String(line.type ?? '').toUpperCase() === 'TAX',
|
|
3289
|
-
),
|
|
3290
|
-
[checkoutPriceSummaryLinesForCheckout],
|
|
3291
|
-
);
|
|
3292
|
-
const {
|
|
3293
|
-
editableCheckoutPriceSummaryLines,
|
|
3294
|
-
editableSummaryLineAmountInputs,
|
|
3295
|
-
editableSummaryLineLabelInputs,
|
|
3296
|
-
editableSummaryPreSubtotalDelta,
|
|
3297
|
-
editableSummaryPreSubtotalDebugRows,
|
|
3298
|
-
onLineAmountInputChange: handleEditableLineAmountInputChange,
|
|
3299
|
-
onLineAmountInputBlur: handleEditableLineAmountInputBlur,
|
|
3300
|
-
onLineAmountReset: handleEditableLineAmountReset,
|
|
3301
|
-
} = useEditableSummaryLines(
|
|
3302
|
-
checkoutPriceSummaryLinesForCheckout,
|
|
3303
|
-
{
|
|
3304
|
-
enabled: showProviderPricingInlineEditor,
|
|
3305
|
-
onChange: providerPricingUi?.onLineAmountInputChange,
|
|
3306
|
-
onBlur: providerPricingUi?.onLineAmountInputBlur,
|
|
3307
|
-
onReset: providerPricingUi?.onLineAmountReset,
|
|
3308
|
-
},
|
|
3309
|
-
);
|
|
3310
|
-
|
|
3311
3311
|
// Promo discount from backend (order-level only; rates are pre-promo)
|
|
3312
3312
|
const [promoDiscountAmount, setPromoDiscountAmount] = useState(0);
|
|
3313
3313
|
const [isGiftCard, setIsGiftCard] = useState(false);
|
|
@@ -3323,6 +3323,11 @@ export function AdminChangeBookingFlow({
|
|
|
3323
3323
|
changeBookingPromo: null as { priorAmount?: number } | null,
|
|
3324
3324
|
});
|
|
3325
3325
|
|
|
3326
|
+
const originalReceiptPromoAdjustment = useMemo(
|
|
3327
|
+
() => deriveOriginalReceiptPromoAdjustment(originalReceipt),
|
|
3328
|
+
[originalReceipt],
|
|
3329
|
+
);
|
|
3330
|
+
|
|
3326
3331
|
const promoDiscountFetchKey = useMemo(() => {
|
|
3327
3332
|
if (!appliedPromoCode || !selectedAvailability || totalQuantity === 0) return '';
|
|
3328
3333
|
const companyId = product.companyId ?? env.COMPANY_ID;
|
|
@@ -3339,7 +3344,7 @@ export function AdminChangeBookingFlow({
|
|
|
3339
3344
|
currency,
|
|
3340
3345
|
quantitiesSignature,
|
|
3341
3346
|
String(Math.round(effectiveSubtotalForCheckout * 100)),
|
|
3342
|
-
lockedPromoCode ? String(Math.round(
|
|
3347
|
+
lockedPromoCode ? String(Math.round(originalReceiptPromoAdjustment.amount * 100)) : '',
|
|
3343
3348
|
].join('::');
|
|
3344
3349
|
}, [
|
|
3345
3350
|
appliedPromoCode,
|
|
@@ -3353,7 +3358,7 @@ export function AdminChangeBookingFlow({
|
|
|
3353
3358
|
quantitiesSignature,
|
|
3354
3359
|
effectiveSubtotalForCheckout,
|
|
3355
3360
|
lockedPromoCode,
|
|
3356
|
-
|
|
3361
|
+
originalReceiptPromoAdjustment.amount,
|
|
3357
3362
|
]);
|
|
3358
3363
|
|
|
3359
3364
|
promoDiscountParamsRef.current = {
|
|
@@ -3362,7 +3367,7 @@ export function AdminChangeBookingFlow({
|
|
|
3362
3367
|
effectiveSubtotal: effectiveSubtotalForCheckout,
|
|
3363
3368
|
appliedPromoCode,
|
|
3364
3369
|
changeBookingPromo: lockedPromoCode
|
|
3365
|
-
? { priorAmount:
|
|
3370
|
+
? { priorAmount: originalReceiptPromoAdjustment.amount }
|
|
3366
3371
|
: null,
|
|
3367
3372
|
};
|
|
3368
3373
|
|
|
@@ -3436,16 +3441,88 @@ export function AdminChangeBookingFlow({
|
|
|
3436
3441
|
// Change booking: get-promo-discount uses forBookingChange + prior promo from receipt so expired codes stay locked (BE).
|
|
3437
3442
|
const lockedPromoFallbackAmount =
|
|
3438
3443
|
lockedPromoCode
|
|
3439
|
-
? Math.max(0,
|
|
3444
|
+
? Math.max(0, originalReceiptPromoAdjustment.amount)
|
|
3440
3445
|
: 0;
|
|
3441
3446
|
const effectivePromoDiscountAmount =
|
|
3442
3447
|
promoDiscountAmount > 0 ? promoDiscountAmount : lockedPromoFallbackAmount;
|
|
3448
|
+
const effectivePromoLineType =
|
|
3449
|
+
isGiftCard || originalReceiptPromoAdjustment.type === 'GIFT_CARD'
|
|
3450
|
+
? 'GIFT_CARD'
|
|
3451
|
+
: isVoucher || originalReceiptPromoAdjustment.type === 'VOUCHER'
|
|
3452
|
+
? 'VOUCHER'
|
|
3453
|
+
: originalReceiptPromoAdjustment.type;
|
|
3443
3454
|
const taxOnSubtotal = isTaxIncludedInPrice ? 0 : effectiveSubtotalForCheckout * (pricingConfig?.taxRate ?? 0);
|
|
3444
3455
|
const effectiveTax =
|
|
3445
|
-
effectivePromoDiscountAmount > 0 &&
|
|
3456
|
+
effectivePromoDiscountAmount > 0 && effectivePromoLineType !== 'GIFT_CARD' && effectivePromoLineType !== 'VOUCHER'
|
|
3446
3457
|
? (effectiveSubtotalForCheckout - effectivePromoDiscountAmount) * (pricingConfig?.taxRate ?? 0)
|
|
3447
3458
|
: taxOnSubtotal;
|
|
3448
3459
|
const totalPrice = effectiveSubtotalForCheckout + effectiveTax - effectivePromoDiscountAmount;
|
|
3460
|
+
|
|
3461
|
+
const checkoutPriceSummaryLinesForCheckout = useMemo(() => {
|
|
3462
|
+
let raw: PriceSummaryLine[];
|
|
3463
|
+
if (suppressSelfServeCurrencyUi && selfServePricingConfirmed) {
|
|
3464
|
+
const serverLines = latestChangeQuote?.serverPreview?.priceSummaryLines;
|
|
3465
|
+
raw = serverLines && serverLines.length > 0 ? serverLines : checkoutPriceSummaryLines;
|
|
3466
|
+
} else {
|
|
3467
|
+
raw = checkoutPriceSummaryLines;
|
|
3468
|
+
}
|
|
3469
|
+
const lines = omitZeroAmountPromoDiscountSummaryLines(raw);
|
|
3470
|
+
const hasPromoSummaryLine = lines.some((line) =>
|
|
3471
|
+
line.kind === 'line' && isPromoDiscountGiftCardOrVoucherLine(line)
|
|
3472
|
+
);
|
|
3473
|
+
if (hasPromoSummaryLine || effectivePromoDiscountAmount < 0.005) return lines;
|
|
3474
|
+
const trimmedPromoCode = appliedPromoCode?.trim() ?? '';
|
|
3475
|
+
const promoLabel =
|
|
3476
|
+
trimmedPromoCode.length > 0
|
|
3477
|
+
? `Promo: ${trimmedPromoCode}`
|
|
3478
|
+
: originalReceiptPromoAdjustment.label || (t('booking.discount') || 'Discount');
|
|
3479
|
+
return [
|
|
3480
|
+
...lines,
|
|
3481
|
+
{
|
|
3482
|
+
kind: 'line' as const,
|
|
3483
|
+
label: promoLabel,
|
|
3484
|
+
amount: -effectivePromoDiscountAmount,
|
|
3485
|
+
type: effectivePromoLineType,
|
|
3486
|
+
},
|
|
3487
|
+
];
|
|
3488
|
+
}, [
|
|
3489
|
+
suppressSelfServeCurrencyUi,
|
|
3490
|
+
selfServePricingConfirmed,
|
|
3491
|
+
checkoutPriceSummaryLines,
|
|
3492
|
+
latestChangeQuote?.serverPreview?.priceSummaryLines,
|
|
3493
|
+
effectivePromoDiscountAmount,
|
|
3494
|
+
effectivePromoLineType,
|
|
3495
|
+
appliedPromoCode,
|
|
3496
|
+
originalReceiptPromoAdjustment.label,
|
|
3497
|
+
t,
|
|
3498
|
+
]);
|
|
3499
|
+
|
|
3500
|
+
/** Receipt/server lines already include {@link PriceSummary}'s TAX row — do not also pass `taxAmount` or it duplicates. */
|
|
3501
|
+
const priceSummaryLinesIncludeTaxRow = useMemo(
|
|
3502
|
+
() =>
|
|
3503
|
+
checkoutPriceSummaryLinesForCheckout.some(
|
|
3504
|
+
(line) => line.kind === 'line' && String(line.type ?? '').toUpperCase() === 'TAX',
|
|
3505
|
+
),
|
|
3506
|
+
[checkoutPriceSummaryLinesForCheckout],
|
|
3507
|
+
);
|
|
3508
|
+
const {
|
|
3509
|
+
editableCheckoutPriceSummaryLines,
|
|
3510
|
+
editableSummaryLineAmountInputs,
|
|
3511
|
+
editableSummaryLineLabelInputs,
|
|
3512
|
+
editableSummaryPreSubtotalDelta,
|
|
3513
|
+
editableSummaryPreSubtotalDebugRows,
|
|
3514
|
+
onLineAmountInputChange: handleEditableLineAmountInputChange,
|
|
3515
|
+
onLineAmountInputBlur: handleEditableLineAmountInputBlur,
|
|
3516
|
+
onLineAmountReset: handleEditableLineAmountReset,
|
|
3517
|
+
} = useEditableSummaryLines(
|
|
3518
|
+
checkoutPriceSummaryLinesForCheckout,
|
|
3519
|
+
{
|
|
3520
|
+
enabled: showProviderPricingInlineEditor,
|
|
3521
|
+
onChange: providerPricingUi?.onLineAmountInputChange,
|
|
3522
|
+
onBlur: providerPricingUi?.onLineAmountInputBlur,
|
|
3523
|
+
onReset: providerPricingUi?.onLineAmountReset,
|
|
3524
|
+
},
|
|
3525
|
+
);
|
|
3449
3526
|
/**
|
|
3450
3527
|
* FE cart rollup for line math, breakdowns, and `clientProposedTotal` hint to the API. Self-serve **footer** totals
|
|
3451
3528
|
* prefer `latestChangeQuote` once quote succeeds — this value is not an alternate source of truth for checkout.
|
|
@@ -4036,9 +4113,14 @@ export function AdminChangeBookingFlow({
|
|
|
4036
4113
|
let subtotal = roundMoney(displayChangeFlowSubtotal + editableSummaryPreSubtotalDelta);
|
|
4037
4114
|
const tax = roundMoney(displayChangeFlowTax);
|
|
4038
4115
|
const expectedTotal = roundMoney(displayChangeFlowProposedTotalWithEditableLines);
|
|
4039
|
-
const
|
|
4116
|
+
const promoAdjustmentTotal = roundMoney(
|
|
4117
|
+
lineItems.reduce((sum, line) => (
|
|
4118
|
+
isPromoDiscountGiftCardOrVoucherLine(line) ? sum + (Number(line.amount) || 0) : sum
|
|
4119
|
+
), 0)
|
|
4120
|
+
);
|
|
4121
|
+
const recomputed = roundMoney(subtotal + tax + promoAdjustmentTotal);
|
|
4040
4122
|
if (Math.abs(recomputed - expectedTotal) >= 0.005) {
|
|
4041
|
-
subtotal = roundMoney(expectedTotal - tax);
|
|
4123
|
+
subtotal = roundMoney(expectedTotal - tax - promoAdjustmentTotal);
|
|
4042
4124
|
}
|
|
4043
4125
|
return {
|
|
4044
4126
|
subtotal,
|
|
@@ -5364,7 +5446,7 @@ export function AdminChangeBookingFlow({
|
|
|
5364
5446
|
{
|
|
5365
5447
|
label: appliedPromoCode ? `Promo: ${appliedPromoCode}` : (originalReceipt?.promoLabel || (t('booking.discount') || 'Discount')),
|
|
5366
5448
|
amount: -effectivePromoDiscountAmount,
|
|
5367
|
-
type:
|
|
5449
|
+
type: effectivePromoLineType,
|
|
5368
5450
|
},
|
|
5369
5451
|
]
|
|
5370
5452
|
: []),
|