@ticketboothapp/booking 1.2.60 → 1.2.61

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.60",
3
+ "version": "1.2.61",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -1754,12 +1754,16 @@ export function BookingFlow({
1754
1754
  return product.productId.trim();
1755
1755
  }, [initialValues?.productId, product.productId]);
1756
1756
 
1757
- /** Minimum paid price / receipt anchoring applies for any option under this parent product. */
1757
+ /**
1758
+ * Receipt paid floors (max(receipt, catalog) on protected seats / return) — **customer self-serve only**.
1759
+ * Provider dashboard uses live catalog so cheaper itinerary → lower total / refund (`changeFlowBalanceVsOriginal` provider).
1760
+ */
1758
1761
  const changeFlowApplyReceiptPaidFloors = useMemo(
1759
1762
  () =>
1760
1763
  isChangeFlow &&
1764
+ !isProviderDashboardChange &&
1761
1765
  changeFlowBookingParentProductIdForFloors === product.productId.trim(),
1762
- [isChangeFlow, changeFlowBookingParentProductIdForFloors, product.productId],
1766
+ [isChangeFlow, isProviderDashboardChange, changeFlowBookingParentProductIdForFloors, product.productId],
1763
1767
  );
1764
1768
 
1765
1769
  useEffect(() => {
@@ -2478,14 +2482,6 @@ export function BookingFlow({
2478
2482
  selectedReturnOption?.dateTime,
2479
2483
  ]);
2480
2484
  /** Same outbound + return as original booking: incremental seats use live pricing; receipt floors apply only after itinerary changes. */
2481
- const changeFlowSameItineraryAsOriginalBooking = useMemo(
2482
- () =>
2483
- isChangeFlow &&
2484
- changeFlowOutboundMatchesOriginalSelection &&
2485
- changeFlowReturnMatchesOriginalSelection,
2486
- [isChangeFlow, changeFlowOutboundMatchesOriginalSelection, changeFlowReturnMatchesOriginalSelection],
2487
- );
2488
-
2489
2485
  const returnOptionsWithFloor = useMemo(() => {
2490
2486
  const options = selectedAvailability?.returnOptions ?? [];
2491
2487
  if (!isChangeFlow && effectiveChangeFlowReturnUnitFloorPerPerson == null) return options;
@@ -2493,15 +2489,9 @@ export function BookingFlow({
2493
2489
  const vacancyCredit = changeFlowSeatCreditForReturnAvailabilityId(opt.returnAvailabilityId);
2494
2490
  const adjustedVacancies = Math.max(0, opt.vacancies ?? 0) + vacancyCredit;
2495
2491
  const rawPerPerson = opt.priceAdjustmentByCurrency?.[currency] ?? 0;
2492
+ // Floors on return cards only for self-serve; provider sees catalog prices (refunds when cheaper).
2496
2493
  const applyReturnFloor =
2497
- effectiveChangeFlowReturnUnitFloorPerPerson != null &&
2498
- (
2499
- // Public self-serve change flow should always display the paid return floor on cards.
2500
- isCustomerSelfServeChange ||
2501
- // For non-public paths, only apply floor after itinerary changes.
2502
- !isChangeFlow ||
2503
- !changeFlowSameItineraryAsOriginalBooking
2504
- );
2494
+ effectiveChangeFlowReturnUnitFloorPerPerson != null && isCustomerSelfServeChange;
2505
2495
  const flooredPerPerson = applyReturnFloor
2506
2496
  ? Math.max(rawPerPerson, effectiveChangeFlowReturnUnitFloorPerPerson!)
2507
2497
  : rawPerPerson;
@@ -2519,7 +2509,6 @@ export function BookingFlow({
2519
2509
  selectedAvailability?.returnOptions,
2520
2510
  isChangeFlow,
2521
2511
  effectiveChangeFlowReturnUnitFloorPerPerson,
2522
- changeFlowSameItineraryAsOriginalBooking,
2523
2512
  isCustomerSelfServeChange,
2524
2513
  currency,
2525
2514
  changeFlowSeatCreditForReturnAvailabilityId,
@@ -2527,7 +2516,7 @@ export function BookingFlow({
2527
2516
 
2528
2517
  const selectedReturnOptionWithFloor = useMemo(() => {
2529
2518
  if (!selectedReturnOption || !isChangeFlow || effectiveChangeFlowReturnUnitFloorPerPerson == null) return selectedReturnOption;
2530
- if (!isCustomerSelfServeChange && changeFlowSameItineraryAsOriginalBooking) return selectedReturnOption;
2519
+ if (!isCustomerSelfServeChange) return selectedReturnOption;
2531
2520
  const rawPerPerson = selectedReturnOption.priceAdjustmentByCurrency?.[currency] ?? 0;
2532
2521
  const flooredPerPerson = Math.max(rawPerPerson, effectiveChangeFlowReturnUnitFloorPerPerson);
2533
2522
  if (flooredPerPerson === rawPerPerson) return selectedReturnOption;
@@ -2542,29 +2531,15 @@ export function BookingFlow({
2542
2531
  selectedReturnOption,
2543
2532
  isChangeFlow,
2544
2533
  effectiveChangeFlowReturnUnitFloorPerPerson,
2545
- changeFlowSameItineraryAsOriginalBooking,
2546
2534
  isCustomerSelfServeChange,
2547
2535
  currency,
2548
2536
  ]);
2549
2537
 
2550
- const returnOptionForOrderSummary = useMemo(() => {
2551
- // Same itinerary + dashboard change: keep raw return option (floor only after itinerary changes).
2552
- // Public self-serve: always apply API return floor on options so totals match paid return.
2553
- if (
2554
- isChangeFlow &&
2555
- changeFlowSameItineraryAsOriginalBooking &&
2556
- !isCustomerSelfServeChange
2557
- ) {
2558
- return selectedReturnOption ?? null;
2559
- }
2560
- return selectedReturnOptionWithFloor;
2561
- }, [
2562
- isChangeFlow,
2563
- changeFlowSameItineraryAsOriginalBooking,
2564
- isCustomerSelfServeChange,
2565
- selectedReturnOption,
2566
- selectedReturnOptionWithFloor,
2567
- ]);
2538
+ /** Order-summary return row uses self-serve floors via [selectedReturnOptionWithFloor]; provider stays catalog-only. */
2539
+ const returnOptionForOrderSummary = useMemo(
2540
+ () => selectedReturnOptionWithFloor,
2541
+ [selectedReturnOptionWithFloor],
2542
+ );
2568
2543
  const effectiveSelectedPickupVacancies = useMemo(() => {
2569
2544
  if (!selectedAvailability) return 0;
2570
2545
  const seatCredit = changeFlowSeatCreditForOutboundAvailability(selectedAvailability);
@@ -4984,7 +4959,9 @@ export function BookingFlow({
4984
4959
  showCapacity={isAdmin}
4985
4960
  extraDiscountPercent={calendarDiscountPercent}
4986
4961
  capDiscountBadgesToBookingDate={
4987
- isChangeFlow && changeFlowTicketBookedUnitPriceByCategory.size > 0
4962
+ isChangeFlow &&
4963
+ changeFlowApplyReceiptPaidFloors &&
4964
+ changeFlowTicketBookedUnitPriceByCategory.size > 0
4988
4965
  ? changeFlowOriginalDate
4989
4966
  : null
4990
4967
  }
@@ -5111,7 +5088,11 @@ export function BookingFlow({
5111
5088
  t={t}
5112
5089
  onQuantityChange={handleQuantityChange}
5113
5090
  minimumQuantities={changeBookingMinimumQuantities}
5114
- ticketUnitFloorByCategory={isChangeFlow ? changeFlowTicketBookedUnitPriceByCategory : undefined}
5091
+ ticketUnitFloorByCategory={
5092
+ isChangeFlow && changeFlowApplyReceiptPaidFloors
5093
+ ? changeFlowTicketBookedUnitPriceByCategory
5094
+ : undefined
5095
+ }
5115
5096
  />
5116
5097
  )}
5117
5098
 
@@ -2,11 +2,12 @@
2
2
  * ## Change-booking pricing — product rules (frontend)
3
3
  *
4
4
  * Use this file as the **spec checklist** when debating behavior with product; BookingFlow adds **gates** (parent product,
5
- * channel, same-itinerary) on top of these formulas.
5
+ * channel) on top of these formulas.
6
6
  *
7
7
  * ### 1. When receipt “paid floors” apply
8
- * Only in change flow when the booking’s **parent catalog product** matches the **loaded product** (`changeFlowApplyReceiptPaidFloors`
9
- * in BookingFlow). Otherwise the session is treated like normal catalog pricing (no receipt floors in this layer).
8
+ * Only **customer self-serve** change flow, when the booking’s **parent catalog product** matches the **loaded product**
9
+ * (`changeFlowApplyReceiptPaidFloors` in BookingFlow). **Provider dashboard** change flow uses **live catalog only** (no
10
+ * floors) so a cheaper date/return yields a lower total / refund via signed balance.
10
11
  *
11
12
  * ### 2. Tickets (per category)
12
13
  * For each category where we can infer an average **unit paid** from the stored receipt:
@@ -22,15 +23,13 @@
22
23
  * passengers pay each line’s **live** per-person amount only.
23
24
  *
24
25
  * ### 4. Return option — **order total / checkout**
25
- * If a per-person return floor exists (API `returnUnitFloorPerPerson` or derived from receipt RETURN lines):
26
- * **per person** = **`max(live catalog return for the selected slot, floor)`**; **row total** = party size × that amount
27
- * (when floors apply).
26
+ * When §1 floors apply and a per-person return floor exists (API `returnUnitFloorPerPerson` or receipt RETURN lines):
27
+ * **per person** = **`max(live catalog return for the selected slot, floor)`**; **row total** = party size × that amount.
28
+ * Provider dashboard: **live catalog return only** (no floor).
28
29
  *
29
30
  * ### 5. Return option — **picker UI only** (BookingFlow)
30
- * **Self-serve:** return choice cards always show the floored per-person price when a floor exists.
31
- * **Provider / other:** if outbound **and** return still match the **original** booking exactly, cards show **catalog**
32
- * only (floor hidden) until the itinerary changes; after a change, same as self-serve. **Totals** still follow §4 when
33
- * floors are on.
31
+ * **Self-serve:** return cards use the floored per-person price when a floor exists (aligned with §4).
32
+ * **Provider dashboard:** cards show **catalog** prices only (no floor).
34
33
  *
35
34
  * ### 6. Quote “new booking” total & balance
36
35
  * **FE proposed total** = full cart math (subtotal + tax − promo), cent-rounded; optional **1¢ reconcile** to old receipt