@ticketboothapp/booking 1.2.167 → 1.2.169

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.
Files changed (55) hide show
  1. package/package.json +1 -1
  2. package/src/components/booking/AdminChangeBookingContent.tsx +13 -1
  3. package/src/components/booking/AdminChangeBookingFlow.tsx +180 -65
  4. package/src/components/booking/AdminChangeCheckoutPanel.tsx +43 -13
  5. package/src/components/booking/AdminChangePricingPanel.tsx +400 -134
  6. package/src/components/booking/AdminChangeReceiptComparison.tsx +166 -145
  7. package/src/components/booking/ChangeBookingFlow.tsx +7 -3
  8. package/src/components/booking/ChangeBookingQuoteStatusPlaceholder.tsx +7 -2
  9. package/src/components/booking/ChangeBookingSelectionControlsPanel.tsx +6 -0
  10. package/src/components/booking/ChangeBookingTicketsAndAddOnsPanel.tsx +80 -0
  11. package/src/components/booking/PickupLocationSelector.module.css +22 -0
  12. package/src/components/booking/PickupLocationSelector.tsx +3 -3
  13. package/src/components/booking/PriceSummary.tsx +4 -0
  14. package/src/components/booking/PrivateShuttleBookingFlow.tsx +27 -13
  15. package/src/components/booking/PrivateShuttleCheckoutSection.tsx +95 -54
  16. package/src/components/booking/admin-adjustment-components.ts +44 -0
  17. package/src/components/booking/admin-adjustment-tax-behavior.ts +50 -0
  18. package/src/components/booking/admin-change-flow-state-helpers.ts +10 -0
  19. package/src/components/booking/admin-change-provider-payload.ts +3 -0
  20. package/src/components/booking/admin-change-quote-request-key.ts +27 -0
  21. package/src/components/booking/admin-change-v2-quote-request.ts +10 -2
  22. package/src/components/booking/admin-refund-decision-context.ts +37 -0
  23. package/src/components/booking/admin-refund-disposition.ts +71 -2
  24. package/src/components/booking/booking-flow-types.ts +4 -0
  25. package/src/components/booking/booking-flow.css +5 -0
  26. package/src/components/booking/change-booking-error-message.ts +137 -0
  27. package/src/components/booking/change-booking-flow-helpers.ts +77 -2
  28. package/src/components/booking/change-booking-quote-guards.ts +4 -1
  29. package/src/components/booking/change-booking-quote-state.ts +44 -1
  30. package/src/components/booking/incompatible-add-on-selections.ts +19 -0
  31. package/src/components/booking/private-shuttle-availability.ts +14 -0
  32. package/src/components/booking/private-shuttle-cancellation-policy.ts +9 -0
  33. package/src/components/booking/private-shuttle-checkout-controller.ts +5 -0
  34. package/src/components/booking/private-shuttle-provider-change-runner.ts +21 -0
  35. package/src/components/booking/private-shuttle-reservation-runner.ts +5 -3
  36. package/src/components/booking/provider-dashboard-change-booking.ts +16 -1
  37. package/src/components/booking/useAdminChangeCheckoutController.ts +16 -0
  38. package/src/components/booking/useAdminChangeProductReset.ts +6 -0
  39. package/src/components/booking/useAdminChangeProtectedPricing.ts +4 -0
  40. package/src/components/booking/useAdminChangeQuoteDisplayState.tsx +6 -4
  41. package/src/components/booking/useAdminChangeQuotePreview.ts +76 -20
  42. package/src/components/booking/useAdminCustomReceiptLines.ts +171 -10
  43. package/src/components/booking/useAdminProviderPricingAdjustments.ts +50 -19
  44. package/src/components/booking/useBookingAvailabilityAddOns.ts +19 -2
  45. package/src/components/booking/useChangeBookingAddOnFloorController.ts +15 -9
  46. package/src/components/booking/useChangeBookingInitialHydration.ts +10 -7
  47. package/src/components/booking/useChangeBookingQuotePreview.ts +3 -1
  48. package/src/components/booking/useChangeBookingSelectionDetails.ts +23 -17
  49. package/src/lib/booking/change-booking-server-preview.ts +26 -9
  50. package/src/lib/booking/change-flow-pricing.ts +12 -0
  51. package/src/lib/booking/i18n/messages/en.json +1 -0
  52. package/src/lib/booking/i18n/messages/fr.json +1 -0
  53. package/src/lib/booking-api.ts +64 -0
  54. package/test/change-booking-helpers.test.ts +853 -3
  55. package/src/components/booking/useAdminChangePricingDebugPanel.tsx +0 -178
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ticketboothapp/booking",
3
- "version": "1.2.167",
3
+ "version": "1.2.169",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -142,6 +142,8 @@ const selectionControlsPropKeys = [
142
142
  'applyReceiptPaidFloors',
143
143
  'ticketUnitFloorByCategory',
144
144
  'addOns',
145
+ 'knownAddOns',
146
+ 'addOnCatalogLoaded',
145
147
  'addOnSelections',
146
148
  'onAddOnSelectionsChange',
147
149
  'minimumTotalByAddOnId',
@@ -157,8 +159,13 @@ const checkoutPanelPropKeys = [
157
159
  'originalReceipt',
158
160
  'priceSummaryLinesIncludeTaxRow',
159
161
  'serverAmendmentLines',
162
+ 'serverResultingReceiptLines',
160
163
  'serverAmountDue',
161
164
  'serverUpdatedTotal',
165
+ 'serverNetPaid',
166
+ 'serverCurrentPaymentCredit',
167
+ 'serverProjectedPaymentCredit',
168
+ 'serverRefundCandidate',
162
169
  'serverQuoteError',
163
170
  'refundDecisionOperations',
164
171
  'returnPriceTreatmentOperations',
@@ -167,6 +174,8 @@ const checkoutPanelPropKeys = [
167
174
  'refundDispositionsByOperationId',
168
175
  'onRefundDispositionChange',
169
176
  'refundQuoteLoading',
177
+ 'sourceProductName',
178
+ 'targetProductName',
170
179
  'isTaxIncludedInPrice',
171
180
  'taxRate',
172
181
  'currency',
@@ -175,15 +184,18 @@ const checkoutPanelPropKeys = [
175
184
  'showProviderPricingInlineEditor',
176
185
  'providerPricingUi',
177
186
  'providerQuotedLines',
178
- 'debugPanel',
179
187
  'promoEditor',
180
188
  'promoCode',
181
189
  'showChangeFlowManualPriceLines',
182
190
  'showAdminCustomLineEditor',
183
191
  'adminCustomReceiptLines',
192
+ 'hasPendingAdminCustomReceiptLineChanges',
193
+ 'pendingAdminCustomReceiptLineIds',
194
+ 'adjustmentComponentOptions',
184
195
  'onAddAdminCustomReceiptLine',
185
196
  'onUpdateAdminCustomReceiptLine',
186
197
  'onRemoveAdminCustomReceiptLine',
198
+ 'onApplyAdminCustomReceiptLines',
187
199
  'firstName',
188
200
  'lastName',
189
201
  'email',
@@ -4,8 +4,10 @@ import { useState, useEffect, useMemo, useRef, useCallback } from 'react';
4
4
  import {
5
5
  type AdminAmendmentRefundDisposition,
6
6
  type AdminPromoApplicationScope,
7
+ type AddOn,
7
8
  type Availability,
8
9
  type ReturnOption,
10
+ getAddOns,
9
11
  } from '../../lib/booking-api';
10
12
  import {
11
13
  AdminChangeBookingContent,
@@ -20,12 +22,14 @@ import { filterAvailabilitiesAfterCurrentTime } from '../../lib/booking/booking-
20
22
  import type { ChangeBookingFlowProps } from './booking-flow-types';
21
23
  import {
22
24
  normalizeAddOnSelections,
25
+ resolveChangeBookingCancellationPolicyId,
23
26
  } from './change-booking-flow-helpers';
24
27
  import {
25
28
  buildChangeBookingMinimumQuantities,
26
29
  isAdminManualOverrideEligibleLine,
27
30
  resolveChangeFlowOriginalDate,
28
31
  resolveInitialChangeProductOptionId,
32
+ shouldLockExistingAddOnQuantities,
29
33
  } from './admin-change-flow-state-helpers';
30
34
  import { useAdminChangeProducts } from './useAdminChangeProducts';
31
35
  import {
@@ -42,6 +46,8 @@ import { useAdminChangePriceSummary } from './useAdminChangePriceSummary';
42
46
  import { useAdminChangeReceiptDerivation } from './useAdminChangeReceiptDerivation';
43
47
  import { useAdminChangeQuotePreview } from './useAdminChangeQuotePreview';
44
48
  import { useBookingAvailabilityAddOns } from './useBookingAvailabilityAddOns';
49
+ import { incompatibleAddOnSelections } from './incompatible-add-on-selections';
50
+ import { incompatibleAddOnChangeError } from './change-booking-error-message';
45
51
  import { useBookingPromoAndQuantityController } from './useBookingPromoAndQuantityController';
46
52
  import { useChangeBookingItineraryUiState } from './useChangeBookingItineraryUiState';
47
53
  import { useChangeBookingAutoSelections } from './useChangeBookingAutoSelections';
@@ -50,7 +56,6 @@ import { useChangeBookingAddOnFloorController } from './useChangeBookingAddOnFlo
50
56
  import { useChangeBookingReceiptFloorController } from './useChangeBookingReceiptFloorController';
51
57
  import { useAdminCustomReceiptLines } from './useAdminCustomReceiptLines';
52
58
  import { useAdminChangeQuoteDisplayState } from './useAdminChangeQuoteDisplayState';
53
- import { useAdminChangePricingDebugPanel } from './useAdminChangePricingDebugPanel';
54
59
  import { useAdminChangeOrderSummaryModel } from './useAdminChangeOrderSummaryModel';
55
60
  import { useActiveProductOptions } from './useActiveProductOptions';
56
61
  import { useSeedPromoCodeInput } from './useSeedPromoCodeInput';
@@ -61,6 +66,8 @@ import { AdminChangePromoEditor } from './AdminChangePromoEditor';
61
66
  import { buildAdminChangeQuoteRequestKey } from './admin-change-quote-request-key';
62
67
  import type { AdminReleaseRefundDisposition } from './admin-refund-disposition';
63
68
  import {
69
+ defaultKeepOriginalReturnPriceDispositions,
70
+ defaultPendingRefundDispositions,
64
71
  mergeAdminRefundDecisionContext,
65
72
  selectRefundDispositionsForOperations,
66
73
  type AdminRefundDecisionContext,
@@ -179,27 +186,79 @@ export function AdminChangeBookingFlow({
179
186
  useState<AdminPromoApplicationScope>('CHANGE_ONLY');
180
187
  const [pickupLocationId, setPickupLocationId] = useState<string | null>(null);
181
188
  const [pickupLocationSkipped, setPickupLocationSkipped] = useState(false);
182
- /** Cancellation policy is fixed to the existing booking snapshot not user-editable in change flow. */
183
- const cancellationPolicyId = useMemo(
189
+ /** The policy remains non-editable, but a family conversion must adopt the target default. */
190
+ const [cancellationPolicyId, setCancellationPolicyId] = useState<string | null>(
184
191
  () => initialValues?.cancellationPolicyId?.trim() ?? null,
185
- [initialValues?.cancellationPolicyId],
186
192
  );
187
193
  /** Add-on selections (lunch, animals, etc.) - filtered by selected product option */
188
194
  const [addOnSelections, setAddOnSelections] = useState<Array<{ addOnId: string; variantId?: string; quantity?: number }>>(() =>
189
195
  normalizeAddOnSelections(initialValues?.addOnSelections ?? [])
190
196
  );
191
197
  /** Fetched add-ons for the selected product option */
192
- const { addOns, setAddOns } = useBookingAvailabilityAddOns({
198
+ const {
199
+ addOns,
200
+ setAddOns,
201
+ availabilityProductOptionId,
202
+ loadedProductOptionId,
203
+ } = useBookingAvailabilityAddOns({
193
204
  selectedAvailability,
194
205
  companyId: product.companyId,
195
206
  });
196
- const {
197
- adminCustomReceiptLines,
198
- handleAddAdminCustomReceiptLine,
199
- handleUpdateAdminCustomReceiptLine,
200
- handleRemoveAdminCustomReceiptLine,
201
- } = useAdminCustomReceiptLines(selectedChangeProductId);
202
-
207
+ const [knownAddOns, setKnownAddOns] = useState<AddOn[]>([]);
208
+ const mergeKnownAddOns = useCallback((rows: AddOn[]) => {
209
+ if (rows.length === 0) return;
210
+ setKnownAddOns((current) => {
211
+ const merged = new Map(current.map((addOn) => [addOn.addOnId, addOn]));
212
+ let changed = false;
213
+ for (const addOn of rows) {
214
+ if (merged.get(addOn.addOnId) !== addOn) {
215
+ merged.set(addOn.addOnId, addOn);
216
+ changed = true;
217
+ }
218
+ }
219
+ return changed ? Array.from(merged.values()) : current;
220
+ });
221
+ }, []);
222
+ useEffect(() => {
223
+ mergeKnownAddOns(addOns);
224
+ }, [addOns, mergeKnownAddOns]);
225
+ useEffect(() => {
226
+ let cancelled = false;
227
+ if (!product.companyId) return;
228
+ getAddOns(product.companyId, {
229
+ preCheckout: true,
230
+ dateTime: initialValues?.dateTime?.trim() || undefined,
231
+ })
232
+ .then((rows) => {
233
+ if (!cancelled) mergeKnownAddOns(rows);
234
+ })
235
+ .catch(() => {
236
+ // The selected tour's catalog still renders; the warning keeps a generic fallback.
237
+ });
238
+ return () => {
239
+ cancelled = true;
240
+ };
241
+ }, [product.companyId, initialValues?.dateTime, mergeKnownAddOns]);
242
+ const addOnCatalogLoaded =
243
+ availabilityProductOptionId != null &&
244
+ loadedProductOptionId === availabilityProductOptionId;
245
+ const incompatibleSelections = incompatibleAddOnSelections(
246
+ addOnSelections,
247
+ addOns,
248
+ addOnCatalogLoaded,
249
+ );
250
+ const incompatibleAddOnIds = Array.from(
251
+ new Set(incompatibleSelections.map((selection) => selection.addOnId)),
252
+ );
253
+ const hasIncompatibleAddOnSelections = incompatibleAddOnIds.length > 0;
254
+ const incompatibleAddOnError = hasIncompatibleAddOnSelections
255
+ ? incompatibleAddOnChangeError(
256
+ incompatibleAddOnIds.map((addOnId) => (
257
+ knownAddOns.find((addOn) => addOn.addOnId === addOnId)?.name ?? 'Selected add-on'
258
+ )),
259
+ product.name,
260
+ )
261
+ : '';
203
262
  useEffect(() => {
204
263
  if (!pickupLocationId) return;
205
264
  const pickupLocations = product.pickupLocations ?? [];
@@ -212,7 +271,7 @@ export function AdminChangeBookingFlow({
212
271
  useSeedPromoCodeInput(autoAppliedPromoCode, setPromoCodeInput);
213
272
  const [loading, setLoading] = useState(false);
214
273
  const [error, setError] = useState('');
215
- const [termsAccepted, setTermsAccepted] = useState(false);
274
+ const [termsAccepted, setTermsAccepted] = useState(true);
216
275
  const [termsAcceptedAt, setTermsAcceptedAt] = useState<string | null>(null);
217
276
  const [partnerAttributionConfirmed, setPartnerAttributionConfirmed] = useState(false);
218
277
  /** Admin only: skip sending confirmation at creation (provider dashboard). */
@@ -261,6 +320,21 @@ export function AdminChangeBookingFlow({
261
320
  [isCustomerSelfServeChange, initialValues?.bookingItems, isAdmin],
262
321
  );
263
322
  const [latestChangeQuote, setLatestChangeQuote] = useState<AdminChangeLatestQuote | null>(null);
323
+ const {
324
+ adminCustomReceiptLines,
325
+ appliedAdminCustomReceiptLines,
326
+ adminReverseAdjustmentIds,
327
+ hasPendingAdminCustomReceiptLineChanges,
328
+ pendingAdminCustomReceiptLineIds,
329
+ hasHydratedExistingAdjustments,
330
+ handleAddAdminCustomReceiptLine,
331
+ handleUpdateAdminCustomReceiptLine,
332
+ handleRemoveAdminCustomReceiptLine,
333
+ handleApplyAdminCustomReceiptLines: handleApplyAdminCustomReceiptLineDrafts,
334
+ } = useAdminCustomReceiptLines(
335
+ selectedChangeProductId,
336
+ latestChangeQuote?.reversibleAdjustments,
337
+ );
264
338
  const [refundDecisionContext, setRefundDecisionContext] = useState<AdminRefundDecisionContext | null>(null);
265
339
  const [refundDispositionsByOperationId, setRefundDispositionsByOperationId] = useState<
266
340
  Record<string, AdminAmendmentRefundDisposition>
@@ -278,6 +352,12 @@ export function AdminChangeBookingFlow({
278
352
  (latestChangeQuote.refundDecisionOperations?.length ?? 0) > 0) &&
279
353
  latestChangeQuote.serverDisplay != null;
280
354
  const changeQuoteRequestSeq = useRef(0);
355
+ const handleApplyAdminCustomReceiptLines = useCallback(() => {
356
+ setLatestChangeQuote(null);
357
+ setChangeQuoteFetchError(null);
358
+ setChangeQuoteLoading(true);
359
+ handleApplyAdminCustomReceiptLineDrafts();
360
+ }, [handleApplyAdminCustomReceiptLineDrafts]);
281
361
  const { activeOptions, activeOptionIdsKey, optionsMap } = useActiveProductOptions(product.options);
282
362
 
283
363
  const {
@@ -311,8 +391,31 @@ export function AdminChangeBookingFlow({
311
391
  setError,
312
392
  });
313
393
 
394
+ useEffect(() => {
395
+ const policies = pricingConfig?.cancellationPolicies;
396
+ if (!policies?.length) return;
397
+ const isProductFamilyConversion =
398
+ isInitialPrivateShuttleBooking !== (product.productType === 'PRIVATE_SHUTTLE');
399
+ const resolvedPolicyId = resolveChangeBookingCancellationPolicyId(
400
+ cancellationPolicyId,
401
+ policies,
402
+ currency,
403
+ isProductFamilyConversion,
404
+ );
405
+ if (resolvedPolicyId !== cancellationPolicyId) {
406
+ setCancellationPolicyId(resolvedPolicyId);
407
+ }
408
+ }, [
409
+ cancellationPolicyId,
410
+ currency,
411
+ isInitialPrivateShuttleBooking,
412
+ pricingConfig?.cancellationPolicies,
413
+ product.productType,
414
+ ]);
415
+
314
416
  useAdminChangeProductReset({
315
417
  selectedChangeProductId,
418
+ setQuantities,
316
419
  setSelectedAvailability,
317
420
  setSelectedReturnOption,
318
421
  setAddOns,
@@ -376,6 +479,8 @@ export function AdminChangeBookingFlow({
376
479
  timesForSelectedDate,
377
480
  companyTimezone,
378
481
  changeFlowResolvedInitialProductOptionId,
482
+ mostPopularProductOptionId:
483
+ activeOptions.find((option) => option.mostPopular)?.optionId ?? null,
379
484
  precomputedPricesByOption,
380
485
  currency,
381
486
  originalReceiptSubtotal: originalReceipt?.subtotal,
@@ -396,7 +501,12 @@ export function AdminChangeBookingFlow({
396
501
  setError,
397
502
  });
398
503
 
504
+ const lockExistingAddOnQuantities = shouldLockExistingAddOnQuantities({
505
+ isCustomerSelfServeChange,
506
+ isAdmin,
507
+ });
399
508
  const {
509
+ initialAddOnQtyByKey,
400
510
  initialAddOnMinQtyByKey,
401
511
  initialAddOnMinTotalByAddOnId,
402
512
  updateAddOnSelections,
@@ -404,7 +514,7 @@ export function AdminChangeBookingFlow({
404
514
  initialValues,
405
515
  originalReceiptLineItems: originalReceipt?.lineItems,
406
516
  addOns,
407
- isCustomerSelfServeChange,
517
+ lockExistingAddOnQuantities,
408
518
  setAddOnSelections,
409
519
  });
410
520
 
@@ -530,7 +640,6 @@ export function AdminChangeBookingFlow({
530
640
  editableSummaryLineAmountInputs,
531
641
  editableSummaryLineLabelInputs,
532
642
  editableSummaryPreSubtotalDelta,
533
- editableSummaryPreSubtotalDebugRows,
534
643
  handleEditableLineAmountInputChange,
535
644
  handleEditableLineAmountInputBlur,
536
645
  handleEditableLineAmountReset,
@@ -558,7 +667,7 @@ export function AdminChangeBookingFlow({
558
667
  originalAddOnSelections: initialValues?.addOnSelections ?? null,
559
668
  originalProductOptionId: initialValues?.productOptionId ?? null,
560
669
  addOns,
561
- adminCustomReceiptLines,
670
+ adminCustomReceiptLines: appliedAdminCustomReceiptLines,
562
671
  quantities,
563
672
  flowUi,
564
673
  isProviderDashboardChange,
@@ -596,8 +705,6 @@ export function AdminChangeBookingFlow({
596
705
  unchangedReceiptAnchor,
597
706
  showChangeFlowManualPriceLines,
598
707
  showAdminCustomLineEditor,
599
- displayedChangeAmountsRaw,
600
- displayLayerUsesExternalPricing,
601
708
  displayChangeFlowTax,
602
709
  displayChangeFlowProposedTotal,
603
710
  displayChangeFlowProposedTotalWithEditableLines,
@@ -627,7 +734,7 @@ export function AdminChangeBookingFlow({
627
734
  pickupLocationId,
628
735
  quantities,
629
736
  addOnSelections,
630
- initialAddOnMinQtyByKey,
737
+ initialAddOnQtyByKey,
631
738
  changeFlowResolvedInitialProductOptionId,
632
739
  selectedParentProductChanged,
633
740
  isProviderDashboardChange,
@@ -636,7 +743,8 @@ export function AdminChangeBookingFlow({
636
743
  providerQuotedLines,
637
744
  editableSummaryPreSubtotalDelta,
638
745
  adminCustomAdjustmentTotal,
639
- adminCustomReceiptLines,
746
+ adminCustomReceiptLines: appliedAdminCustomReceiptLines,
747
+ adminReverseAdjustmentIds,
640
748
  isManualOverrideEligibleLine,
641
749
  isAdmin,
642
750
  useAdminFeAuthoritativeQuote,
@@ -674,37 +782,6 @@ export function AdminChangeBookingFlow({
674
782
  editableSummaryPreSubtotalDelta,
675
783
  });
676
784
 
677
- const changeFlowAdminPricingDebugPanel = useAdminChangePricingDebugPanel({
678
- isAdmin,
679
- hasSelectedAvailability: selectedAvailability != null,
680
- totalQuantity,
681
- useAdminFeAuthoritativeQuote,
682
- currency,
683
- locale,
684
- originalReceipt,
685
- isCustomerSelfServeChange,
686
- latestChangeQuote,
687
- changeQuoteFetchError,
688
- displayLayerUsesExternalPricing,
689
- selfServePricingConfirmed,
690
- changeQuoteLoading,
691
- effectiveSubtotal,
692
- adminCustomAdjustmentTotal,
693
- editableSummaryPreSubtotalDebugRows,
694
- effectiveSubtotalForCheckout,
695
- effectiveTax,
696
- effectivePromoDiscountAmount,
697
- totalPrice,
698
- changeFlowNewBookingTotal,
699
- displayedChangeAmountsRaw,
700
- editableSummaryPreSubtotalDelta,
701
- displayChangeFlowProposedTotal,
702
- displayChangeFlowProposedTotalWithEditableLines,
703
- adminFeAuthoritativeReceipt,
704
- changeFlowClientEstimateDue,
705
- changeFlowAmountDue,
706
- });
707
-
708
785
  const refundDecisionSelectionKey = useMemo(() => buildAdminChangeQuoteRequestKey({
709
786
  bookingReference: initialValues?.bookingReference,
710
787
  lastName,
@@ -717,8 +794,9 @@ export function AdminChangeBookingFlow({
717
794
  cancellationPolicyId,
718
795
  promoCode: appliedPromoCode,
719
796
  promoApplicationScope,
720
- adminCustomReceiptLines,
797
+ adminCustomReceiptLines: appliedAdminCustomReceiptLines,
721
798
  structuredAdjustments: adminStructuredAdjustments,
799
+ reverseAdjustmentIds: adminReverseAdjustmentIds,
722
800
  useAdminFeAuthoritativeQuote,
723
801
  }), [
724
802
  initialValues?.bookingReference,
@@ -732,8 +810,9 @@ export function AdminChangeBookingFlow({
732
810
  cancellationPolicyId,
733
811
  appliedPromoCode,
734
812
  promoApplicationScope,
735
- adminCustomReceiptLines,
813
+ appliedAdminCustomReceiptLines,
736
814
  adminStructuredAdjustments,
815
+ adminReverseAdjustmentIds,
737
816
  useAdminFeAuthoritativeQuote,
738
817
  ]);
739
818
  const responseRefundDecisionOperations = latestChangeQuote?.refundDecisionOperations ?? [];
@@ -754,6 +833,12 @@ export function AdminChangeBookingFlow({
754
833
  latestChangeQuote?.allowedRefundDispositionsByOperationId,
755
834
  latestChangeQuote?.removedValueByOperationId,
756
835
  ]);
836
+ useEffect(() => {
837
+ if (!isProviderDashboardChange || responseRefundDecisionOperations.length === 0) return;
838
+ setRefundDispositionsByOperationId((current) =>
839
+ defaultPendingRefundDispositions(responseRefundDecisionOperations, current),
840
+ );
841
+ }, [isProviderDashboardChange, responseRefundDecisionOperations]);
757
842
  const activeRefundDecisionContext = useMemo(() => mergeAdminRefundDecisionContext({
758
843
  current: refundDecisionContext,
759
844
  selectionKey: refundDecisionSelectionKey,
@@ -772,10 +857,14 @@ export function AdminChangeBookingFlow({
772
857
  ? responseReturnPriceTreatmentOperations
773
858
  : [];
774
859
  const activeRefundDispositionsByOperationId = useMemo(() => {
860
+ const dispositionsWithReturnPriceDefaults = defaultKeepOriginalReturnPriceDispositions(
861
+ activeReturnPriceTreatmentOperations,
862
+ refundDispositionsByOperationId,
863
+ );
775
864
  return selectRefundDispositionsForOperations(
776
865
  activeRefundDecisionContext,
777
866
  activeReturnPriceTreatmentOperations,
778
- refundDispositionsByOperationId,
867
+ dispositionsWithReturnPriceDefaults,
779
868
  );
780
869
  }, [
781
870
  activeRefundDecisionContext?.operations,
@@ -816,8 +905,11 @@ export function AdminChangeBookingFlow({
816
905
  cancellationPolicyId,
817
906
  appliedPromoCode,
818
907
  promoApplicationScope,
819
- adminCustomReceiptLines,
908
+ adminCustomReceiptLines: appliedAdminCustomReceiptLines,
820
909
  adminStructuredAdjustments,
910
+ adminReverseAdjustmentIds,
911
+ needsAdjustmentDiscovery:
912
+ isProviderDashboardChange && !hasHydratedExistingAdjustments,
821
913
  refundDispositionsByOperationId: activeRefundDispositionsByOperationId,
822
914
  useAdminFeAuthoritativeQuote,
823
915
  latestChangeQuote,
@@ -1036,6 +1128,7 @@ export function AdminChangeBookingFlow({
1036
1128
  providerPricingOverrides,
1037
1129
  mergedProviderAdditionalAdjustments,
1038
1130
  adminStructuredAdjustments,
1131
+ adminReverseAdjustmentIds,
1039
1132
  refundDispositionsByOperationId: activeRefundDispositionsByOperationId,
1040
1133
  providerApplyAuthoritativeReceipt,
1041
1134
  onSuccess,
@@ -1050,7 +1143,7 @@ export function AdminChangeBookingFlow({
1050
1143
  effectiveCancellationPolicyLabel,
1051
1144
  addOns,
1052
1145
  feeLineItems,
1053
- adminCustomReceiptLines,
1146
+ adminCustomReceiptLines: appliedAdminCustomReceiptLines,
1054
1147
  isTaxIncludedInPrice,
1055
1148
  originalReceiptPromoAdjustment,
1056
1149
  effectivePromoLineType,
@@ -1126,9 +1219,9 @@ export function AdminChangeBookingFlow({
1126
1219
  isSimplifiedPricingView, onQuantityChange: handleQuantityChange,
1127
1220
  minimumQuantities: changeBookingMinimumQuantities, applyReceiptPaidFloors: changeFlowApplyReceiptPaidFloors,
1128
1221
  ticketUnitFloorByCategory: changeFlowTicketBookedUnitPriceByCategory,
1129
- ...{ addOns, addOnSelections },
1222
+ ...{ addOns, knownAddOns, addOnCatalogLoaded, addOnSelections },
1130
1223
  onAddOnSelectionsChange: updateAddOnSelections,
1131
- minimumTotalByAddOnId: isCustomerSelfServeChange ? initialAddOnMinTotalByAddOnId : undefined,
1224
+ minimumTotalByAddOnId: lockExistingAddOnQuantities ? initialAddOnMinTotalByAddOnId : undefined,
1132
1225
  priceSummaryLines: taxIncludedReconciledCheckoutPriceSummaryLines,
1133
1226
  replacePriceSummary: hasChangeSelection ? selfServeCheckoutPlaceholder : undefined,
1134
1227
  changeFlowAmountDue,
@@ -1140,13 +1233,23 @@ export function AdminChangeBookingFlow({
1140
1233
  // normalized server preview, render it; do not discard valid data because an
1141
1234
  // optional/versioned metadata string is missing or renamed.
1142
1235
  serverAmendmentLines: latestChangeQuote?.serverPreview?.priceSummaryLines ?? null,
1236
+ serverResultingReceiptLines:
1237
+ latestChangeQuote?.serverPreview?.resultingReceiptLines ?? null,
1143
1238
  serverAmountDue: latestChangeQuote?.serverPreview?.amountDue ?? null,
1144
1239
  serverUpdatedTotal: latestChangeQuote?.serverPreview?.totalNewBooking ?? null,
1240
+ serverNetPaid: latestChangeQuote?.amountPreviouslyPaid ?? null,
1241
+ serverCurrentPaymentCredit: latestChangeQuote?.currentPaymentCreditTotal ?? 0,
1242
+ serverProjectedPaymentCredit: latestChangeQuote?.projectedPaymentCreditTotal ?? 0,
1243
+ serverRefundCandidate: latestChangeQuote?.refundCandidate ?? 0,
1145
1244
  serverQuoteError:
1146
- changeQuoteFetchError ??
1147
- (isChangeQuoteBlocked
1148
- ? (latestChangeQuote?.reasonIfBlocked ?? 'This change cannot be priced right now.')
1149
- : null),
1245
+ hasIncompatibleAddOnSelections
1246
+ ? incompatibleAddOnError
1247
+ : (
1248
+ changeQuoteFetchError ??
1249
+ (isChangeQuoteBlocked
1250
+ ? (latestChangeQuote?.reasonIfBlocked ?? 'This change cannot be priced right now.')
1251
+ : null)
1252
+ ),
1150
1253
  refundDecisionOperations: activeRefundDecisionContext?.operations ?? [],
1151
1254
  returnPriceTreatmentOperations: activeReturnPriceTreatmentOperations,
1152
1255
  allowedRefundDispositionsByOperationId: {
@@ -1158,16 +1261,21 @@ export function AdminChangeBookingFlow({
1158
1261
  refundDispositionsByOperationId: activeRefundDispositionsByOperationId,
1159
1262
  onRefundDispositionChange: handleRefundDispositionChange,
1160
1263
  refundQuoteLoading: changeQuoteLoading,
1264
+ sourceProductName: initialProduct.name,
1265
+ targetProductName: product.name,
1161
1266
  ...{ priceSummaryLinesIncludeTaxRow, isTaxIncludedInPrice, showProviderPricingInlineEditor },
1162
1267
  taxRate: pricingConfig?.taxRate,
1163
1268
  ...{ providerPricingUi, providerQuotedLines },
1164
- debugPanel: changeFlowAdminPricingDebugPanel,
1165
1269
  promoEditor: adminPromoEditor,
1166
1270
  promoCode: appliedPromoCode,
1167
1271
  ...{ showChangeFlowManualPriceLines, showAdminCustomLineEditor, adminCustomReceiptLines },
1272
+ hasPendingAdminCustomReceiptLineChanges,
1273
+ pendingAdminCustomReceiptLineIds,
1274
+ adjustmentComponentOptions: latestChangeQuote?.selectableAdjustmentComponents ?? [],
1168
1275
  onAddAdminCustomReceiptLine: handleAddAdminCustomReceiptLine,
1169
1276
  onUpdateAdminCustomReceiptLine: handleUpdateAdminCustomReceiptLine,
1170
1277
  onRemoveAdminCustomReceiptLine: handleRemoveAdminCustomReceiptLine,
1278
+ onApplyAdminCustomReceiptLines: handleApplyAdminCustomReceiptLines,
1171
1279
  onFirstNameChange: setFirstName, onLastNameChange: setLastName, onEmailChange: setEmail,
1172
1280
  onClearError: () => setError(''),
1173
1281
  pickupLocations: product.pickupLocations, destinations: product.destinations, highlightedPickupLocationIds,
@@ -1177,11 +1285,18 @@ export function AdminChangeBookingFlow({
1177
1285
  ...{ skipConfirmationCommunications, disableAutoCommunications },
1178
1286
  onSkipConfirmationChange: setSkipConfirmationCommunications,
1179
1287
  onDisableCommunicationsChange: setDisableAutoCommunications,
1180
- checkoutError: checkoutFormError,
1288
+ checkoutError: incompatibleAddOnError || checkoutFormError,
1181
1289
  onCheckout: handleCheckout,
1182
1290
  submitLabel: changeCheckoutButtonLabel ?? deferredInvoiceSubmitLabel,
1183
1291
  ...{ hasEffectiveChangeSelection, isChangeQuoteBlocked },
1184
- submitDisabled: changeFlowSubmitDisabled,
1292
+ submitDisabled:
1293
+ changeFlowSubmitDisabled ||
1294
+ hasIncompatibleAddOnSelections ||
1295
+ hasPendingAdminCustomReceiptLineChanges ||
1296
+ adminCustomReceiptLines.some(
1297
+ (line) =>
1298
+ line.scope === 'SELECTED_COMPONENTS' && line.selectedComponentIds.length === 0,
1299
+ ),
1185
1300
  attributionSummary: flowUi?.partnerAttributionSummary, attributionConfirmLabel: flowUi?.partnerAttributionConfirmLabel,
1186
1301
  attributionConfirmed: partnerAttributionConfirmed,
1187
1302
  onAttributionConfirmedChange: setPartnerAttributionConfirmed,