@ticketboothapp/booking 1.2.129 → 1.2.130

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ticketboothapp/booking",
3
- "version": "1.2.129",
3
+ "version": "1.2.130",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -48,27 +48,37 @@ function isPaymentCreditType(type?: string | null): boolean {
48
48
  return normalized === "GIFT_CARD" || normalized === "PAYMENT_CREDIT";
49
49
  }
50
50
 
51
- function isPaymentCreditSummaryLine(line: PriceSummaryLine): boolean {
52
- return line.kind === "line" && isPaymentCreditType(line.type);
51
+ function isPaymentCreditQuoteLine(line: PricingV2QuoteLineSnapshot): boolean {
52
+ return isPaymentCreditType(line.type);
53
53
  }
54
54
 
55
- function movePaymentCreditLinesAfterTax(lines: PriceSummaryLine[]): PriceSummaryLine[] {
56
- const paymentCreditLines = lines.filter(isPaymentCreditSummaryLine);
57
- if (paymentCreditLines.length === 0) return lines;
55
+ function isPostTaxDiscountQuoteLine(line: PricingV2QuoteLineSnapshot): boolean {
56
+ const discountBehavior = String(line.discountBehavior ?? "").trim().toUpperCase();
57
+ const source = String(line.source ?? "").trim().toUpperCase();
58
+ return discountBehavior === "POST_TAX_DISCOUNT" || source === "VOUCHER";
59
+ }
58
60
 
59
- const nonPaymentCreditLines = lines.filter((line) => !isPaymentCreditSummaryLine(line));
60
- const taxIndex = nonPaymentCreditLines.findIndex(
61
- (line) => line.kind === "line" && normalizedLineType(line.type) === "TAX",
61
+ function moveSettlementLinesAfterTax(
62
+ lines: PricingV2QuoteLineSnapshot[],
63
+ ): PricingV2QuoteLineSnapshot[] {
64
+ const settlementLines = lines.filter(
65
+ (line) => isPaymentCreditQuoteLine(line) || isPostTaxDiscountQuoteLine(line),
66
+ );
67
+ if (settlementLines.length === 0) return lines;
68
+
69
+ const nonSettlementLines = lines.filter(
70
+ (line) => !isPaymentCreditQuoteLine(line) && !isPostTaxDiscountQuoteLine(line),
62
71
  );
72
+ const taxIndex = nonSettlementLines.findIndex((line) => normalizedLineType(line.type) === "TAX");
63
73
 
64
74
  if (taxIndex < 0) {
65
- return [...nonPaymentCreditLines, ...paymentCreditLines];
75
+ return [...nonSettlementLines, ...settlementLines];
66
76
  }
67
77
 
68
78
  return [
69
- ...nonPaymentCreditLines.slice(0, taxIndex + 1),
70
- ...paymentCreditLines,
71
- ...nonPaymentCreditLines.slice(taxIndex + 1),
79
+ ...nonSettlementLines.slice(0, taxIndex + 1),
80
+ ...settlementLines,
81
+ ...nonSettlementLines.slice(taxIndex + 1),
72
82
  ];
73
83
  }
74
84
 
@@ -112,11 +122,10 @@ export function buildCheckoutModalSummaryFromPricingV2Quote(
112
122
  const total = pricingV2QuoteTotal(quote);
113
123
  if (total == null) return undefined;
114
124
 
115
- const mappedLines =
116
- quote?.lines
117
- ?.map((line, index) => priceSummaryLineFromQuoteLine(line, index))
118
- .filter((line): line is PriceSummaryLine => line != null) ?? [];
119
- const lines = movePaymentCreditLinesAfterTax(mappedLines);
125
+ const quoteLines = quote?.lines ?? [];
126
+ const lines = moveSettlementLinesAfterTax(quoteLines)
127
+ .map((line, index) => priceSummaryLineFromQuoteLine(line, index))
128
+ .filter((line): line is PriceSummaryLine => line != null);
120
129
 
121
130
  if (lines.length === 0) return undefined;
122
131
 
@@ -156,8 +165,9 @@ export function buildCheckoutModalSummaryFromPricingV2Quote(
156
165
  0,
157
166
  finiteNumber(quote?.paymentCreditTotal) ?? 0,
158
167
  );
168
+ const hasPostTaxDiscount = quoteLines.some(isPostTaxDiscountQuoteLine);
159
169
  const subtotal = finiteNumber(
160
- paymentCreditTotal > 0
170
+ paymentCreditTotal > 0 || hasPostTaxDiscount
161
171
  ? quote?.taxableSubtotal ??
162
172
  quote?.grossSubtotal ??
163
173
  quote?.netSubtotalBeforeTax ??
@@ -218,6 +218,59 @@ test('maps Pricing V2 gift cards as payment credits after tax', () => {
218
218
  );
219
219
  });
220
220
 
221
+ test('maps Pricing V2 post-tax vouchers after tax with full taxable subtotal', () => {
222
+ const summary = buildCheckoutModalSummaryFromPricingV2Quote(
223
+ {
224
+ currency: 'CAD',
225
+ grossSubtotal: 629.18,
226
+ discountTotal: -455.11,
227
+ paymentCreditTotal: 0,
228
+ netSubtotalBeforeTax: 174.07,
229
+ taxableSubtotal: 629.18,
230
+ taxAmount: 53.48,
231
+ payableTotal: 227.55,
232
+ amountToCharge: 227.55,
233
+ lines: [
234
+ {
235
+ lineId: 'ticket_1',
236
+ type: 'TICKET',
237
+ label: 'ADULT',
238
+ amount: 581.21,
239
+ quantity: 3,
240
+ },
241
+ {
242
+ lineId: 'fee_1',
243
+ type: 'FEE',
244
+ label: 'Moraine Lake Road Access Fee',
245
+ amount: 47.97,
246
+ },
247
+ {
248
+ lineId: 'voucher_1',
249
+ type: 'DISCOUNT',
250
+ label: "Les Clefs d'Or",
251
+ amount: -455.11,
252
+ discountBehavior: 'POST_TAX_DISCOUNT',
253
+ source: 'VOUCHER',
254
+ },
255
+ {
256
+ lineId: 'tax_1',
257
+ type: 'TAX',
258
+ label: 'Taxes and fees',
259
+ amount: 53.48,
260
+ },
261
+ ],
262
+ },
263
+ 'Rounding',
264
+ );
265
+
266
+ assert.equal(summary?.subtotal, 629.18);
267
+ assert.equal(summary?.fullTotalAmount, 227.55);
268
+ assert.deepEqual(
269
+ summary?.lines.map((line) => (line.kind === 'line' ? line.type : 'TICKET')),
270
+ ['TICKET', 'FEE', 'TAX', 'DISCOUNT'],
271
+ );
272
+ });
273
+
221
274
  test('builds paid checkout modal data with trimmed customer and change totals', () => {
222
275
  const modalData = buildChangeBookingPaidCheckoutModalData({
223
276
  bookingReference: 'bookRef_ABC-123',