@ticketboothapp/booking 1.2.140 → 1.2.142
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/AdminChangeBookingContent.tsx +3 -0
- package/src/components/booking/AdminChangeBookingFlow.tsx +12 -0
- package/src/components/booking/AdminChangeCheckoutPanel.tsx +10 -14
- package/src/components/booking/AdminChangeReceiptComparison.tsx +50 -207
- package/src/components/booking/useAdminChangePriceSummary.ts +1 -2
- package/src/components/booking/useAdminChangeQuotePreview.ts +6 -4
- package/src/components/booking/useAdminChangeReceiptDerivation.ts +6 -1
package/package.json
CHANGED
|
@@ -971,6 +971,18 @@ export function AdminChangeBookingFlow({
|
|
|
971
971
|
receiptTax: adminFeAuthoritativeReceipt.tax,
|
|
972
972
|
receiptTotal: adminFeAuthoritativeReceipt.total,
|
|
973
973
|
originalReceipt,
|
|
974
|
+
serverAmendmentLines:
|
|
975
|
+
latestChangeQuote?.pricingVersion === 'booking-amendment-v1'
|
|
976
|
+
? latestChangeQuote.serverPreview?.priceSummaryLines ?? []
|
|
977
|
+
: null,
|
|
978
|
+
serverAmountDue:
|
|
979
|
+
latestChangeQuote?.pricingVersion === 'booking-amendment-v1'
|
|
980
|
+
? latestChangeQuote.serverPreview?.amountDue ?? null
|
|
981
|
+
: null,
|
|
982
|
+
serverUpdatedTotal:
|
|
983
|
+
latestChangeQuote?.pricingVersion === 'booking-amendment-v1'
|
|
984
|
+
? latestChangeQuote.serverPreview?.totalNewBooking ?? null
|
|
985
|
+
: null,
|
|
974
986
|
...{ priceSummaryLinesIncludeTaxRow, isTaxIncludedInPrice, showProviderPricingInlineEditor },
|
|
975
987
|
taxRate: pricingConfig?.taxRate,
|
|
976
988
|
...{ providerPricingUi, providerQuotedLines },
|
|
@@ -30,6 +30,9 @@ export interface AdminChangeCheckoutPanelProps {
|
|
|
30
30
|
receiptTotal: number;
|
|
31
31
|
originalReceipt: ChangeBookingFlowProps['originalReceipt'];
|
|
32
32
|
priceSummaryLinesIncludeTaxRow: boolean;
|
|
33
|
+
serverAmendmentLines: PriceSummaryLine[] | null;
|
|
34
|
+
serverAmountDue: number | null;
|
|
35
|
+
serverUpdatedTotal: number | null;
|
|
33
36
|
isTaxIncludedInPrice: boolean;
|
|
34
37
|
taxRate?: number;
|
|
35
38
|
currency: Currency;
|
|
@@ -99,6 +102,9 @@ export function AdminChangeCheckoutPanel({
|
|
|
99
102
|
receiptTotal,
|
|
100
103
|
originalReceipt,
|
|
101
104
|
priceSummaryLinesIncludeTaxRow,
|
|
105
|
+
serverAmendmentLines,
|
|
106
|
+
serverAmountDue,
|
|
107
|
+
serverUpdatedTotal,
|
|
102
108
|
isTaxIncludedInPrice,
|
|
103
109
|
taxRate,
|
|
104
110
|
currency,
|
|
@@ -181,25 +187,15 @@ export function AdminChangeCheckoutPanel({
|
|
|
181
187
|
isAdmin && originalReceipt ? (
|
|
182
188
|
<AdminChangeReceiptComparison
|
|
183
189
|
originalReceipt={originalReceipt}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
total: receiptTotal,
|
|
188
|
-
}}
|
|
189
|
-
newPriceSummaryLines={priceSummaryLines}
|
|
190
|
-
newPriceSummaryLinesIncludeTaxRow={priceSummaryLinesIncludeTaxRow}
|
|
191
|
-
amountDue={changeFlowAmountDue}
|
|
190
|
+
amendmentLines={serverAmendmentLines}
|
|
191
|
+
amountDue={serverAmountDue}
|
|
192
|
+
updatedTotal={serverUpdatedTotal}
|
|
192
193
|
amountDueLabel={totalSummaryLabel}
|
|
193
194
|
currency={currency}
|
|
194
195
|
locale={locale}
|
|
195
196
|
t={t}
|
|
196
197
|
taxRate={taxRate}
|
|
197
|
-
|
|
198
|
-
lineAmountInputs={lineAmountInputs}
|
|
199
|
-
onLineAmountInputChange={onLineAmountInputChange}
|
|
200
|
-
onLineAmountInputBlur={onLineAmountInputBlur}
|
|
201
|
-
onLineAmountReset={onLineAmountReset}
|
|
202
|
-
lineLabelInputs={lineLabelInputs}
|
|
198
|
+
adjustments={pricingAdjustments}
|
|
203
199
|
/>
|
|
204
200
|
) : null;
|
|
205
201
|
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { AdminFeAuthoritativeReceipt } from '../../lib/booking-api';
|
|
3
|
-
import { roundMoney } from '../../lib/booking/change-flow-pricing';
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
4
2
|
import { mapQuoteLineItemsToPriceSummaryLines } from '../../lib/booking/change-booking-server-preview';
|
|
5
3
|
import { formatCurrencyAmount } from '../../lib/currency';
|
|
6
4
|
import type { Locale } from '../../lib/booking/i18n/config';
|
|
@@ -13,206 +11,61 @@ type OriginalReceipt = NonNullable<ChangeBookingFlowProps['originalReceipt']>;
|
|
|
13
11
|
|
|
14
12
|
export interface AdminChangeReceiptComparisonProps {
|
|
15
13
|
originalReceipt: OriginalReceipt;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
amountDue: number;
|
|
14
|
+
/** Amendment-only rows returned by the Pricing V2 server quote. */
|
|
15
|
+
amendmentLines: PriceSummaryLine[] | null;
|
|
16
|
+
/** Signed server balance delta. Never calculated by this component. */
|
|
17
|
+
amountDue: number | null;
|
|
18
|
+
/** Full resulting booking total returned by the same server quote. */
|
|
19
|
+
updatedTotal: number | null;
|
|
20
20
|
amountDueLabel: string;
|
|
21
21
|
currency: Currency;
|
|
22
22
|
locale: string;
|
|
23
23
|
t: TranslationFn;
|
|
24
24
|
taxRate?: number;
|
|
25
|
-
|
|
26
|
-
lineAmountInputs?: Record<string, string>;
|
|
27
|
-
onLineAmountInputChange?: (lineKey: string, value: string) => void;
|
|
28
|
-
onLineAmountInputBlur?: (lineKey: string) => void;
|
|
29
|
-
onLineAmountReset?: (lineKey: string) => void;
|
|
30
|
-
lineLabelInputs?: Record<string, string>;
|
|
25
|
+
adjustments?: ReactNode;
|
|
31
26
|
}
|
|
32
27
|
|
|
33
28
|
function normalizeLocale(locale: string): Locale {
|
|
34
29
|
return locale === 'fr' ? 'fr' : 'en';
|
|
35
30
|
}
|
|
36
31
|
|
|
32
|
+
function originalLines(receipt: OriginalReceipt): PriceSummaryLine[] {
|
|
33
|
+
return receipt.lineItems?.length
|
|
34
|
+
? mapQuoteLineItemsToPriceSummaryLines(receipt.lineItems)
|
|
35
|
+
: [];
|
|
36
|
+
}
|
|
37
|
+
|
|
37
38
|
function hasTaxLine(lines: PriceSummaryLine[]): boolean {
|
|
38
39
|
return lines.some(
|
|
39
40
|
(line) => line.kind === 'line' && String(line.type ?? '').toUpperCase() === 'TAX',
|
|
40
41
|
);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
function mapOriginalReceiptLines(originalReceipt: OriginalReceipt): PriceSummaryLine[] {
|
|
44
|
-
if (!originalReceipt.lineItems?.length) return [];
|
|
45
|
-
return mapQuoteLineItemsToPriceSummaryLines(originalReceipt.lineItems);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function lineIdentity(line: PriceSummaryLine): string {
|
|
49
|
-
const normalizedLabel = (value: string) =>
|
|
50
|
-
value
|
|
51
|
-
.replace(/\s*\([^)]*(?:people|person|x\s*\d+)[^)]*\)\s*$/i, '')
|
|
52
|
-
.replace(/\s+/g, ' ')
|
|
53
|
-
.trim()
|
|
54
|
-
.toUpperCase();
|
|
55
|
-
return line.kind === 'ticket'
|
|
56
|
-
? `ticket:${line.category.trim().toUpperCase()}`
|
|
57
|
-
: `line:${String(line.type ?? '').trim().toUpperCase()}:${normalizedLabel(line.label)}`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function lineAmount(line: PriceSummaryLine): number {
|
|
61
|
-
return line.kind === 'ticket' ? line.itemTotal : line.amount;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function lineQuantity(line: PriceSummaryLine): number | null {
|
|
65
|
-
return line.kind === 'ticket' ? line.qty : line.quantity ?? null;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function amendmentDeltaLines(
|
|
69
|
-
originalLines: PriceSummaryLine[],
|
|
70
|
-
proposedLines: PriceSummaryLine[],
|
|
71
|
-
): PriceSummaryLine[] {
|
|
72
|
-
const remainingOriginal = new Map<string, { amount: number; quantity: number | null }>();
|
|
73
|
-
for (const line of originalLines) {
|
|
74
|
-
const key = lineIdentity(line);
|
|
75
|
-
const previous = remainingOriginal.get(key);
|
|
76
|
-
remainingOriginal.set(key, {
|
|
77
|
-
amount: roundMoney((previous?.amount ?? 0) + lineAmount(line)),
|
|
78
|
-
quantity:
|
|
79
|
-
previous?.quantity == null && lineQuantity(line) == null
|
|
80
|
-
? null
|
|
81
|
-
: (previous?.quantity ?? 0) + (lineQuantity(line) ?? 0),
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return proposedLines.flatMap((line): PriceSummaryLine[] => {
|
|
86
|
-
const original = remainingOriginal.get(lineIdentity(line));
|
|
87
|
-
const proposedQuantity = lineQuantity(line);
|
|
88
|
-
const quantity =
|
|
89
|
-
proposedQuantity == null && original?.quantity == null
|
|
90
|
-
? null
|
|
91
|
-
: (proposedQuantity ?? 0) - (original?.quantity ?? 0);
|
|
92
|
-
const proposedAmount = lineAmount(line);
|
|
93
|
-
const amount = roundMoney(
|
|
94
|
-
quantity != null && quantity > 0 && proposedQuantity != null && proposedQuantity > 0
|
|
95
|
-
? (proposedAmount / proposedQuantity) * quantity
|
|
96
|
-
: proposedAmount - (original?.amount ?? 0),
|
|
97
|
-
);
|
|
98
|
-
if (Math.abs(amount) < 0.005 && (quantity == null || quantity === 0)) return [];
|
|
99
|
-
|
|
100
|
-
if (line.kind === 'ticket') {
|
|
101
|
-
return [{
|
|
102
|
-
...line,
|
|
103
|
-
qty: Math.abs(quantity ?? 0) || line.qty,
|
|
104
|
-
itemTotal: amount,
|
|
105
|
-
editable: false,
|
|
106
|
-
lineKey: undefined,
|
|
107
|
-
}];
|
|
108
|
-
}
|
|
109
|
-
const isTax = String(line.type ?? '').toUpperCase() === 'TAX';
|
|
110
|
-
return [{
|
|
111
|
-
...line,
|
|
112
|
-
label: isTax
|
|
113
|
-
? (amount >= 0 ? 'Additional tax' : 'Tax reduction')
|
|
114
|
-
: line.label.replace(/\s*\([^)]*(?:people|person|x\s*\d+)[^)]*\)\s*$/i, '').trim(),
|
|
115
|
-
amount,
|
|
116
|
-
quantity: quantity == null ? null : Math.abs(quantity),
|
|
117
|
-
editable: false,
|
|
118
|
-
lineKey: undefined,
|
|
119
|
-
}];
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
44
|
export function AdminChangeReceiptComparison({
|
|
124
45
|
originalReceipt,
|
|
125
|
-
|
|
126
|
-
|
|
46
|
+
amendmentLines,
|
|
47
|
+
amountDue,
|
|
48
|
+
updatedTotal,
|
|
127
49
|
amountDueLabel,
|
|
128
50
|
currency,
|
|
129
51
|
locale,
|
|
130
52
|
t,
|
|
131
53
|
taxRate,
|
|
132
|
-
|
|
54
|
+
adjustments,
|
|
133
55
|
}: AdminChangeReceiptComparisonProps) {
|
|
134
56
|
const displayLocale = normalizeLocale(locale);
|
|
135
|
-
const
|
|
136
|
-
() => mapOriginalReceiptLines(originalReceipt),
|
|
137
|
-
[originalReceipt],
|
|
138
|
-
);
|
|
139
|
-
const originalLinesIncludeTaxRow = hasTaxLine(originalReceiptLines);
|
|
140
|
-
const calculatedChangeLines = useMemo(
|
|
141
|
-
() => amendmentDeltaLines(originalReceiptLines, newPriceSummaryLines),
|
|
142
|
-
[newPriceSummaryLines, originalReceiptLines],
|
|
143
|
-
);
|
|
144
|
-
const nonTaxChangeSubtotal = roundMoney(
|
|
145
|
-
calculatedChangeLines.reduce(
|
|
146
|
-
(sum, line) =>
|
|
147
|
-
line.kind === 'line' && String(line.type ?? '').toUpperCase() === 'TAX'
|
|
148
|
-
? sum
|
|
149
|
-
: sum + lineAmount(line),
|
|
150
|
-
0,
|
|
151
|
-
),
|
|
152
|
-
);
|
|
153
|
-
const receiptTaxDelta = roundMoney(
|
|
154
|
-
taxRate != null && nonTaxChangeSubtotal > 0
|
|
155
|
-
? nonTaxChangeSubtotal * taxRate
|
|
156
|
-
: newReceipt.tax - originalReceipt.tax,
|
|
157
|
-
);
|
|
158
|
-
const changeLines = useMemo(() => {
|
|
159
|
-
if (hasTaxLine(calculatedChangeLines) || Math.abs(receiptTaxDelta) < 0.005) {
|
|
160
|
-
return calculatedChangeLines;
|
|
161
|
-
}
|
|
162
|
-
return [
|
|
163
|
-
...calculatedChangeLines,
|
|
164
|
-
{
|
|
165
|
-
kind: 'line' as const,
|
|
166
|
-
label: receiptTaxDelta >= 0 ? 'Additional tax' : 'Tax reduction',
|
|
167
|
-
amount: receiptTaxDelta,
|
|
168
|
-
type: 'TAX',
|
|
169
|
-
},
|
|
170
|
-
];
|
|
171
|
-
}, [calculatedChangeLines, receiptTaxDelta]);
|
|
172
|
-
const changeLinesIncludeTaxRow = hasTaxLine(changeLines);
|
|
173
|
-
const changeTax = roundMoney(
|
|
174
|
-
changeLines.reduce(
|
|
175
|
-
(sum, line) =>
|
|
176
|
-
line.kind === 'line' && String(line.type ?? '').toUpperCase() === 'TAX'
|
|
177
|
-
? sum + line.amount
|
|
178
|
-
: sum,
|
|
179
|
-
0,
|
|
180
|
-
),
|
|
181
|
-
);
|
|
182
|
-
const displayedAmountDue = roundMoney(changeLines.reduce((sum, line) => sum + lineAmount(line), 0));
|
|
183
|
-
const displayedUpdatedTotal = roundMoney(originalReceipt.total + displayedAmountDue);
|
|
184
|
-
const displayedUpdatedSubtotal = roundMoney(originalReceipt.subtotal + nonTaxChangeSubtotal);
|
|
185
|
-
const displayedUpdatedTax = roundMoney(originalReceipt.tax + changeTax);
|
|
57
|
+
const existingLines = originalLines(originalReceipt);
|
|
186
58
|
const originalCurrency = originalReceipt.currency ?? currency;
|
|
187
|
-
const
|
|
188
|
-
() =>
|
|
189
|
-
originalReceiptLines.map((line) =>
|
|
190
|
-
line.kind === 'ticket' && line.qty > 0
|
|
191
|
-
? {
|
|
192
|
-
...line,
|
|
193
|
-
breakdown: line.breakdown ?? {
|
|
194
|
-
lineItems: [],
|
|
195
|
-
subtotalAfterAdjustments: roundMoney(line.itemTotal / line.qty),
|
|
196
|
-
feeLines: [],
|
|
197
|
-
taxRate: 0,
|
|
198
|
-
taxAmount: 0,
|
|
199
|
-
isTaxIncluded: false,
|
|
200
|
-
finalPrice: roundMoney(line.itemTotal / line.qty),
|
|
201
|
-
},
|
|
202
|
-
}
|
|
203
|
-
: line,
|
|
204
|
-
),
|
|
205
|
-
[originalReceiptLines],
|
|
206
|
-
);
|
|
59
|
+
const quoteReady = amendmentLines != null && amountDue != null && updatedTotal != null;
|
|
207
60
|
const amountDueClass =
|
|
208
|
-
|
|
61
|
+
amountDue != null && amountDue < -0.005
|
|
209
62
|
? 'text-emerald-700'
|
|
210
|
-
:
|
|
63
|
+
: amountDue != null && amountDue > 0.005
|
|
211
64
|
? 'text-stone-900'
|
|
212
65
|
: 'text-stone-700';
|
|
213
66
|
|
|
214
67
|
return (
|
|
215
|
-
<div className="space-y-3
|
|
68
|
+
<div className="min-w-0 space-y-3 overflow-visible">
|
|
216
69
|
<div className="grid gap-4 md:grid-cols-2">
|
|
217
70
|
<section className="min-w-0 overflow-visible rounded-lg border border-stone-200 bg-stone-50/70 p-3">
|
|
218
71
|
<div className="mb-3 flex items-center justify-between gap-3">
|
|
@@ -222,82 +75,72 @@ export function AdminChangeReceiptComparison({
|
|
|
222
75
|
</span>
|
|
223
76
|
</div>
|
|
224
77
|
<PriceSummary
|
|
225
|
-
lines={
|
|
78
|
+
lines={existingLines}
|
|
226
79
|
total={originalReceipt.total}
|
|
227
80
|
currency={originalCurrency}
|
|
228
81
|
locale={displayLocale}
|
|
229
82
|
subtotal={originalReceipt.subtotal}
|
|
230
|
-
taxAmount={
|
|
83
|
+
taxAmount={hasTaxLine(existingLines) ? 0 : originalReceipt.tax}
|
|
231
84
|
taxRate={taxRate}
|
|
232
85
|
size="sm"
|
|
233
86
|
t={t}
|
|
234
87
|
/>
|
|
235
88
|
</section>
|
|
89
|
+
|
|
236
90
|
<section className="min-w-0 overflow-visible rounded-lg border border-sky-200 bg-sky-50/50 p-3">
|
|
237
91
|
<div className="mb-3 flex items-center justify-between gap-3">
|
|
238
92
|
<div>
|
|
239
93
|
<h3 className="text-sm font-semibold text-stone-900">This change</h3>
|
|
240
|
-
<p className="mt-0.5 text-xs text-stone-500">
|
|
94
|
+
<p className="mt-0.5 text-xs text-stone-500">Server-priced amendment</p>
|
|
241
95
|
</div>
|
|
242
96
|
<span className="shrink-0 text-xs font-medium text-stone-500">
|
|
243
|
-
{formatCurrencyAmount(
|
|
97
|
+
{amountDue == null ? '—' : formatCurrencyAmount(amountDue, currency, displayLocale)}
|
|
244
98
|
</span>
|
|
245
99
|
</div>
|
|
246
|
-
{
|
|
100
|
+
{!quoteReady ? (
|
|
101
|
+
<div className="rounded-md border border-dashed border-stone-300 bg-white/70 px-3 py-4 text-center">
|
|
102
|
+
<p className="text-sm font-medium text-stone-700">Waiting for server price…</p>
|
|
103
|
+
</div>
|
|
104
|
+
) : amendmentLines.length === 0 && Math.abs(amountDue) < 0.005 ? (
|
|
247
105
|
<div className="rounded-md border border-dashed border-stone-300 bg-white/70 px-3 py-4 text-center">
|
|
248
106
|
<p className="text-sm font-medium text-stone-700">No pricing changes</p>
|
|
249
|
-
<p className="mt-1 text-xs text-stone-500">The selected booking details match the existing purchase.</p>
|
|
250
107
|
</div>
|
|
251
108
|
) : (
|
|
252
109
|
<PriceSummary
|
|
253
|
-
lines={
|
|
254
|
-
total={
|
|
110
|
+
lines={amendmentLines}
|
|
111
|
+
total={amountDue}
|
|
255
112
|
currency={currency}
|
|
256
113
|
locale={displayLocale}
|
|
257
|
-
subtotal={nonTaxChangeSubtotal}
|
|
258
|
-
taxAmount={!changeLinesIncludeTaxRow ? changeTax : 0}
|
|
259
|
-
taxRate={taxRate}
|
|
260
114
|
size="sm"
|
|
261
115
|
t={t}
|
|
262
116
|
/>
|
|
263
117
|
)}
|
|
264
118
|
</section>
|
|
265
119
|
</div>
|
|
266
|
-
|
|
267
|
-
|
|
120
|
+
|
|
121
|
+
{quoteReady ? (
|
|
122
|
+
<section className="flex flex-wrap items-center justify-between gap-2 rounded-lg border border-stone-300 bg-white px-3 py-3 shadow-sm">
|
|
268
123
|
<div>
|
|
269
|
-
<h3 className="text-sm font-semibold text-stone-900">Updated booking
|
|
270
|
-
<p className="mt-0.5 text-xs text-stone-500">
|
|
124
|
+
<h3 className="text-sm font-semibold text-stone-900">Updated booking total</h3>
|
|
125
|
+
<p className="mt-0.5 text-xs text-stone-500">Authoritative total returned by the server quote</p>
|
|
271
126
|
</div>
|
|
272
|
-
<span className="
|
|
273
|
-
{formatCurrencyAmount(
|
|
127
|
+
<span className="text-lg font-semibold tabular-nums text-stone-900">
|
|
128
|
+
{formatCurrencyAmount(updatedTotal, currency, displayLocale)}
|
|
274
129
|
</span>
|
|
275
|
-
</
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
currency={currency}
|
|
280
|
-
locale={displayLocale}
|
|
281
|
-
subtotal={displayedUpdatedSubtotal}
|
|
282
|
-
taxAmount={!hasTaxLine([...originalReceiptLines, ...changeLines]) ? displayedUpdatedTax : 0}
|
|
283
|
-
taxRate={taxRate}
|
|
284
|
-
size="sm"
|
|
285
|
-
t={t}
|
|
286
|
-
/>
|
|
287
|
-
</section>
|
|
288
|
-
{newReceiptAdjustments ? (
|
|
130
|
+
</section>
|
|
131
|
+
) : null}
|
|
132
|
+
|
|
133
|
+
{adjustments ? (
|
|
289
134
|
<section className="rounded-lg border border-amber-200 bg-amber-50/40 p-3">
|
|
290
|
-
<
|
|
291
|
-
|
|
292
|
-
<p className="mt-0.5 text-xs text-stone-500">Optional structured charges or credits applied to this amendment</p>
|
|
293
|
-
</div>
|
|
294
|
-
{newReceiptAdjustments}
|
|
135
|
+
<h3 className="mb-2 text-sm font-semibold text-stone-900">Admin adjustments</h3>
|
|
136
|
+
{adjustments}
|
|
295
137
|
</section>
|
|
296
138
|
) : null}
|
|
139
|
+
|
|
297
140
|
<div className="flex flex-wrap items-center justify-between gap-2 rounded-lg border border-stone-200 bg-stone-50 px-3 py-2">
|
|
298
141
|
<span className="text-sm font-semibold text-stone-900">{amountDueLabel}</span>
|
|
299
142
|
<span className={`text-lg font-semibold tabular-nums ${amountDueClass}`}>
|
|
300
|
-
{formatCurrencyAmount(
|
|
143
|
+
{amountDue == null ? '—' : formatCurrencyAmount(amountDue, currency, displayLocale)}
|
|
301
144
|
</span>
|
|
302
145
|
</div>
|
|
303
146
|
</div>
|
|
@@ -336,7 +336,7 @@ export function useAdminChangePriceSummary({
|
|
|
336
336
|
|
|
337
337
|
const checkoutPriceSummaryLinesForCheckout = useMemo(() => {
|
|
338
338
|
let raw: PriceSummaryLine[];
|
|
339
|
-
if (
|
|
339
|
+
if (suppressSelfServeCurrencyUi && selfServePricingConfirmed) {
|
|
340
340
|
raw =
|
|
341
341
|
latestServerPriceSummaryLines && latestServerPriceSummaryLines.length > 0
|
|
342
342
|
? latestServerPriceSummaryLines
|
|
@@ -366,7 +366,6 @@ export function useAdminChangePriceSummary({
|
|
|
366
366
|
}, [
|
|
367
367
|
suppressSelfServeCurrencyUi,
|
|
368
368
|
selfServePricingConfirmed,
|
|
369
|
-
useAdminFeAuthoritativeQuote,
|
|
370
369
|
checkoutPriceSummaryLines,
|
|
371
370
|
latestServerPriceSummaryLines,
|
|
372
371
|
effectivePromoDiscountAmount,
|
|
@@ -261,9 +261,11 @@ export function useAdminChangeQuotePreview({
|
|
|
261
261
|
hasChangesFromInitial: hasEffectiveChangeSelection,
|
|
262
262
|
selectionTotal:
|
|
263
263
|
originalReceipt
|
|
264
|
-
?
|
|
265
|
-
?
|
|
266
|
-
:
|
|
264
|
+
? selfServePricingConfirmed && latestChangeQuote?.serverPreview?.totalNewBooking != null
|
|
265
|
+
? latestChangeQuote.serverPreview.totalNewBooking
|
|
266
|
+
: suppressSelfServeCurrencyUi
|
|
267
|
+
? null
|
|
268
|
+
: displayChangeFlowProposedTotalWithEditableLines
|
|
267
269
|
: totalPrice,
|
|
268
270
|
selectionCurrency: currency,
|
|
269
271
|
};
|
|
@@ -281,7 +283,7 @@ export function useAdminChangeQuotePreview({
|
|
|
281
283
|
displayChangeFlowProposedTotalWithEditableLines,
|
|
282
284
|
suppressSelfServeCurrencyUi,
|
|
283
285
|
selfServePricingConfirmed,
|
|
284
|
-
|
|
286
|
+
latestChangeQuote?.serverPreview?.totalNewBooking,
|
|
285
287
|
]);
|
|
286
288
|
|
|
287
289
|
useEffect(() => {
|
|
@@ -188,7 +188,12 @@ export function useAdminChangeReceiptDerivation({
|
|
|
188
188
|
});
|
|
189
189
|
})();
|
|
190
190
|
const changeFlowClientEstimateDue = normalizeNearZeroOwed(
|
|
191
|
-
roundMoney(
|
|
191
|
+
roundMoney(
|
|
192
|
+
changeFlowClientEstimateDueBase +
|
|
193
|
+
(latestChangeQuote?.pricingVersion === 'booking-amendment-v1'
|
|
194
|
+
? 0
|
|
195
|
+
: editableSummaryPreSubtotalDelta),
|
|
196
|
+
),
|
|
192
197
|
);
|
|
193
198
|
const changeFlowAmountDue = normalizeNearZeroOwed(changeFlowClientEstimateDue);
|
|
194
199
|
|