@ticketboothapp/booking 1.2.159 → 1.2.160
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
|
@@ -60,6 +60,7 @@ import { useSeedPromoCodeInput } from './useSeedPromoCodeInput';
|
|
|
60
60
|
import { useBookingQuantityCapTrim } from './useBookingQuantityCapTrim';
|
|
61
61
|
import { useBookingViewItemAnalytics } from './useBookingViewItemAnalytics';
|
|
62
62
|
import type { ChangeBookingMergedQuoteState } from './change-booking-quote-state';
|
|
63
|
+
import { AdminChangeReceiptComparison } from './AdminChangeReceiptComparison';
|
|
63
64
|
|
|
64
65
|
export type { ChangeBookingFlowProps } from './booking-flow-types';
|
|
65
66
|
|
|
@@ -504,7 +505,6 @@ export function ChangeBookingFlow({
|
|
|
504
505
|
effectiveSubtotal,
|
|
505
506
|
feeLineItemsWithAddOns,
|
|
506
507
|
checkoutPriceSummaryLinesForCheckout,
|
|
507
|
-
priceSummaryLinesIncludeTaxRow,
|
|
508
508
|
effectivePromoDiscountAmount,
|
|
509
509
|
effectiveTax,
|
|
510
510
|
totalPrice,
|
|
@@ -581,7 +581,7 @@ export function ChangeBookingFlow({
|
|
|
581
581
|
displayChangeFlowProposedTotal,
|
|
582
582
|
displayChangeFlowSubtotal,
|
|
583
583
|
displayChangeFlowTax,
|
|
584
|
-
|
|
584
|
+
publicChangeAmountDueSummary,
|
|
585
585
|
checkoutModalTicketLinesDisplay,
|
|
586
586
|
changeFlowClientEstimateDue,
|
|
587
587
|
changeFlowAmountDue,
|
|
@@ -600,6 +600,22 @@ export function ChangeBookingFlow({
|
|
|
600
600
|
totalPrice,
|
|
601
601
|
});
|
|
602
602
|
|
|
603
|
+
const publicReceiptComparison = originalReceipt ? (
|
|
604
|
+
<AdminChangeReceiptComparison
|
|
605
|
+
originalReceipt={originalReceipt}
|
|
606
|
+
amendmentLines={latestChangeQuote?.serverPreview?.priceSummaryLines ?? null}
|
|
607
|
+
amountDue={latestChangeQuote?.serverPreview?.amountDue ?? null}
|
|
608
|
+
updatedTotal={latestChangeQuote?.serverPreview?.totalNewBooking ?? null}
|
|
609
|
+
selectionChanged={hasEffectiveChangeSelection}
|
|
610
|
+
quoteError={changeQuoteFetchError}
|
|
611
|
+
amountDueLabel="Amount due now"
|
|
612
|
+
currency={currency}
|
|
613
|
+
locale={locale}
|
|
614
|
+
t={t}
|
|
615
|
+
taxRate={pricingConfig?.taxRate}
|
|
616
|
+
/>
|
|
617
|
+
) : null;
|
|
618
|
+
|
|
603
619
|
const {
|
|
604
620
|
missingRequiredReturnSelection,
|
|
605
621
|
isChangeQuoteBlocked,
|
|
@@ -926,11 +942,11 @@ export function ChangeBookingFlow({
|
|
|
926
942
|
{/* Total and Checkout — shared PriceSummary component */}
|
|
927
943
|
{selectedAvailability && (
|
|
928
944
|
<ChangeBookingCheckoutPanel
|
|
929
|
-
priceSummaryLines={
|
|
930
|
-
replacePriceSummary={selfServeCheckoutPlaceholder}
|
|
945
|
+
priceSummaryLines={publicChangeAmountDueSummary.lines}
|
|
946
|
+
replacePriceSummary={selfServeCheckoutPlaceholder ?? publicReceiptComparison}
|
|
931
947
|
totalPrice={changeFlowAmountDue}
|
|
932
|
-
subtotal={
|
|
933
|
-
taxAmount={
|
|
948
|
+
subtotal={publicChangeAmountDueSummary.subtotal}
|
|
949
|
+
taxAmount={publicChangeAmountDueSummary.tax}
|
|
934
950
|
taxRate={pricingConfig?.taxRate}
|
|
935
951
|
currency={currency}
|
|
936
952
|
locale={locale}
|
|
@@ -968,9 +984,9 @@ export function ChangeBookingFlow({
|
|
|
968
984
|
attributionConfirmLabel={flowUi?.partnerAttributionConfirmLabel}
|
|
969
985
|
attributionConfirmed={partnerAttributionConfirmed}
|
|
970
986
|
onAttributionConfirmedChange={setPartnerAttributionConfirmed}
|
|
971
|
-
priceSummaryLinesIncludeTaxRow={
|
|
987
|
+
priceSummaryLinesIncludeTaxRow={false}
|
|
972
988
|
isTaxIncludedInPrice={isTaxIncludedInPrice}
|
|
973
|
-
displayChangeFlowTax={
|
|
989
|
+
displayChangeFlowTax={publicChangeAmountDueSummary.tax}
|
|
974
990
|
/>
|
|
975
991
|
)}
|
|
976
992
|
</div>
|
|
@@ -112,6 +112,41 @@ export function mergePickerTicketQtyIntoPriceSummaryLines(
|
|
|
112
112
|
});
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Build the customer-facing amount-due summary from authoritative amendment
|
|
117
|
+
* lines. Change quotes contain only the incremental lines, so they must not be
|
|
118
|
+
* scaled to the booking's final picker quantities or mixed with the full new
|
|
119
|
+
* booking subtotal. Taxes are combined into the shared summary tax row to keep
|
|
120
|
+
* the preview auditable: charges, amendment subtotal, tax, amount due.
|
|
121
|
+
*/
|
|
122
|
+
export function buildPublicChangeAmountDueSummary(lines: PriceSummaryLine[]): {
|
|
123
|
+
lines: PriceSummaryLine[];
|
|
124
|
+
subtotal: number;
|
|
125
|
+
tax: number;
|
|
126
|
+
} {
|
|
127
|
+
const isTaxLine = (line: PriceSummaryLine): boolean =>
|
|
128
|
+
line.kind === 'line' && String(line.type ?? '').trim().toUpperCase() === 'TAX';
|
|
129
|
+
const nonTaxLines = lines.filter((line) => !isTaxLine(line));
|
|
130
|
+
const ticketLines = nonTaxLines.filter((line) => line.kind === 'ticket');
|
|
131
|
+
const otherLines = nonTaxLines.filter((line) => line.kind !== 'ticket');
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
lines: [...ticketLines, ...otherLines],
|
|
135
|
+
subtotal: roundMoney(
|
|
136
|
+
nonTaxLines.reduce(
|
|
137
|
+
(sum, line) => sum + (line.kind === 'ticket' ? line.itemTotal : line.amount),
|
|
138
|
+
0,
|
|
139
|
+
),
|
|
140
|
+
),
|
|
141
|
+
tax: roundMoney(
|
|
142
|
+
lines.reduce(
|
|
143
|
+
(sum, line) => sum + (isTaxLine(line) && line.kind === 'line' ? line.amount : 0),
|
|
144
|
+
0,
|
|
145
|
+
),
|
|
146
|
+
),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
115
150
|
export function normalizeFeReceiptLineItemType(type: string | null | undefined): string | undefined {
|
|
116
151
|
const normalized = String(type ?? '').trim().toUpperCase();
|
|
117
152
|
if (!normalized) return undefined;
|
|
@@ -11,7 +11,10 @@ import type { ChangeBookingFlowProps } from './booking-flow-types';
|
|
|
11
11
|
import type { ChangeBookingMergedQuoteState } from './change-booking-quote-state';
|
|
12
12
|
import type { CheckoutModalLineItem } from './CheckoutModal';
|
|
13
13
|
import type { PriceSummaryLine } from './PriceSummary';
|
|
14
|
-
import
|
|
14
|
+
import {
|
|
15
|
+
buildPublicChangeAmountDueSummary,
|
|
16
|
+
type PricingRate,
|
|
17
|
+
} from './change-booking-flow-helpers';
|
|
15
18
|
|
|
16
19
|
type GetPriceBreakdown = (
|
|
17
20
|
category: string,
|
|
@@ -103,6 +106,13 @@ export function useChangeBookingReceiptDerivation({
|
|
|
103
106
|
});
|
|
104
107
|
}, [taxIncludedSelfServeReconciledPriceSummaryLines, pricing, getPriceBreakdown]);
|
|
105
108
|
|
|
109
|
+
const publicChangeAmountDueSummary = useMemo(() => {
|
|
110
|
+
const authoritativeAmendmentLines = latestChangeQuote?.serverPreview?.priceSummaryLines;
|
|
111
|
+
return buildPublicChangeAmountDueSummary(
|
|
112
|
+
authoritativeAmendmentLines ?? taxIncludedSelfServeReconciledPriceSummaryLines,
|
|
113
|
+
);
|
|
114
|
+
}, [latestChangeQuote, taxIncludedSelfServeReconciledPriceSummaryLines]);
|
|
115
|
+
|
|
106
116
|
const changeFlowClientEstimateDue = (() => {
|
|
107
117
|
if (!originalReceipt) return totalPrice;
|
|
108
118
|
if (latestChangeQuote != null && !changeQuoteFetchError) {
|
|
@@ -121,6 +131,7 @@ export function useChangeBookingReceiptDerivation({
|
|
|
121
131
|
displayChangeFlowSubtotal,
|
|
122
132
|
displayChangeFlowTax,
|
|
123
133
|
taxIncludedSelfServeReconciledPriceSummaryLines,
|
|
134
|
+
publicChangeAmountDueSummary,
|
|
124
135
|
checkoutModalTicketLinesDisplay,
|
|
125
136
|
changeFlowClientEstimateDue,
|
|
126
137
|
changeFlowAmountDue,
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
reservationHoldSecondsRemaining,
|
|
34
34
|
} from '../src/components/booking/reservation-hold';
|
|
35
35
|
import { buildAdminChangeProviderPayload } from '../src/components/booking/admin-change-provider-payload';
|
|
36
|
+
import { buildPublicChangeAmountDueSummary } from '../src/components/booking/change-booking-flow-helpers';
|
|
36
37
|
|
|
37
38
|
function quote(overrides: Partial<ChangeBookingQuoteResponse>): ChangeBookingQuoteResponse {
|
|
38
39
|
return {
|
|
@@ -186,6 +187,50 @@ test('admin no-charge provider payload carries the selected pickup itinerary', (
|
|
|
186
187
|
assert.equal(payload?.travelerHotel, 'A Bear and Bison Inn');
|
|
187
188
|
});
|
|
188
189
|
|
|
190
|
+
test('public paid change preview shows amendment-only charges before combined tax', () => {
|
|
191
|
+
const summary = buildPublicChangeAmountDueSummary([
|
|
192
|
+
{
|
|
193
|
+
kind: 'line',
|
|
194
|
+
label: 'Moraine Lake Road Access Fee',
|
|
195
|
+
amount: 15.99,
|
|
196
|
+
type: 'FEE',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
kind: 'line',
|
|
200
|
+
label: 'Tax · Moraine Lake Road Access Fee',
|
|
201
|
+
amount: 1.36,
|
|
202
|
+
type: 'TAX',
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
kind: 'ticket',
|
|
206
|
+
category: 'ADULT',
|
|
207
|
+
qty: 1,
|
|
208
|
+
itemTotal: 181.49,
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
kind: 'line',
|
|
212
|
+
label: 'Tax · ADULT',
|
|
213
|
+
amount: 15.43,
|
|
214
|
+
type: 'TAX',
|
|
215
|
+
},
|
|
216
|
+
]);
|
|
217
|
+
|
|
218
|
+
assert.deepEqual(
|
|
219
|
+
summary.lines.map((line) =>
|
|
220
|
+
line.kind === 'ticket'
|
|
221
|
+
? { label: line.category, quantity: line.qty, amount: line.itemTotal }
|
|
222
|
+
: { label: line.label, amount: line.amount },
|
|
223
|
+
),
|
|
224
|
+
[
|
|
225
|
+
{ label: 'ADULT', quantity: 1, amount: 181.49 },
|
|
226
|
+
{ label: 'Moraine Lake Road Access Fee', amount: 15.99 },
|
|
227
|
+
],
|
|
228
|
+
);
|
|
229
|
+
assert.equal(summary.subtotal, 197.48);
|
|
230
|
+
assert.equal(summary.tax, 16.79);
|
|
231
|
+
assert.equal(Number((summary.subtotal + summary.tax).toFixed(2)), 214.27);
|
|
232
|
+
});
|
|
233
|
+
|
|
189
234
|
test('admin removal quote preserves explicit release refund decisions for the preview', () => {
|
|
190
235
|
const operation = {
|
|
191
236
|
operationId: 'op_remove_adult',
|