@ticketboothapp/booking 1.2.161 → 1.2.162

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.161",
3
+ "version": "1.2.162",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -691,7 +691,9 @@ export function AdminChangeBookingFlow({
691
691
  returnAvailabilityId: selectedReturnOption?.returnAvailabilityId ?? null,
692
692
  quantities,
693
693
  addOnSelections,
694
+ cancellationPolicyId,
694
695
  adminCustomReceiptLines,
696
+ structuredAdjustments: adminStructuredAdjustments,
695
697
  useAdminFeAuthoritativeQuote,
696
698
  }), [
697
699
  initialValues?.bookingReference,
@@ -702,7 +704,9 @@ export function AdminChangeBookingFlow({
702
704
  selectedReturnOption?.returnAvailabilityId,
703
705
  quantities,
704
706
  addOnSelections,
707
+ cancellationPolicyId,
705
708
  adminCustomReceiptLines,
709
+ adminStructuredAdjustments,
706
710
  useAdminFeAuthoritativeQuote,
707
711
  ]);
708
712
  const responseRefundDecisionOperations = latestChangeQuote?.refundDecisionOperations ?? [];
@@ -782,7 +786,9 @@ export function AdminChangeBookingFlow({
782
786
  pickupLocationId,
783
787
  quantities,
784
788
  addOnSelections,
789
+ cancellationPolicyId,
785
790
  adminCustomReceiptLines,
791
+ adminStructuredAdjustments,
786
792
  refundDispositionsByOperationId: activeRefundDispositionsByOperationId,
787
793
  useAdminFeAuthoritativeQuote,
788
794
  latestChangeQuote,
@@ -809,7 +815,6 @@ export function AdminChangeBookingFlow({
809
815
  changeFlowNewBookingTotal,
810
816
  effectiveSubtotalForCheckout,
811
817
  effectiveTax,
812
- adminCustomLinesAsAdditionalAdjustments,
813
818
  adminFeAuthoritativeReceipt,
814
819
  isTaxIncludedInPrice,
815
820
  t,
@@ -36,6 +36,8 @@ export interface BuildAdminChangeProviderPayloadParams {
36
36
  refundDispositionsByOperationId: NonNullable<
37
37
  ProviderDashboardChangeBookingPayload['refundDispositionsByOperationId']
38
38
  >;
39
+ pricingV2QuoteId?: string | null;
40
+ pricingV2QuotedTotal?: number | null;
39
41
  providerApplyAuthoritativeReceipt?: ProviderDashboardChangeBookingPayload['authoritativeReceipt'];
40
42
  previousPassengerCount: number;
41
43
  previousAvailabilityId?: string | null;
@@ -59,6 +61,8 @@ export function buildAdminChangeProviderPayload({
59
61
  mergedProviderAdditionalAdjustments,
60
62
  adminStructuredAdjustments,
61
63
  refundDispositionsByOperationId,
64
+ pricingV2QuoteId,
65
+ pricingV2QuotedTotal,
62
66
  providerApplyAuthoritativeReceipt,
63
67
  previousPassengerCount,
64
68
  previousAvailabilityId,
@@ -73,6 +77,7 @@ export function buildAdminChangeProviderPayload({
73
77
  return {
74
78
  productId: availabilityProductOptionId,
75
79
  parentProductId: product.productId,
80
+ availabilityId: selectedAvailability.availabilityId ?? null,
76
81
  dateTime: selectedAvailability.dateTime,
77
82
  bookingItems,
78
83
  returnAvailabilityId: selectedReturnOption?.returnAvailabilityId ?? null,
@@ -83,7 +88,8 @@ export function buildAdminChangeProviderPayload({
83
88
  passengerCount: null,
84
89
  childSafetySeatsCount: null,
85
90
  foodRestrictions: null,
86
- addOnSelections: addOnSelections.length > 0 ? addOnSelections : null,
91
+ // The admin editor owns the complete target selection. [] intentionally means clear all.
92
+ addOnSelections,
87
93
  cancellationPolicyId: cancellationPolicyId ?? initialCancellationPolicyId ?? null,
88
94
  promoCode: appliedPromoCode ?? null,
89
95
  newTotalAmount,
@@ -101,6 +107,8 @@ export function buildAdminChangeProviderPayload({
101
107
  authoritativeReceipt: providerApplyAuthoritativeReceipt,
102
108
  structuredAdjustments: adminStructuredAdjustments,
103
109
  refundDispositionsByOperationId,
110
+ pricingV2QuoteId: pricingV2QuoteId ?? null,
111
+ pricingV2QuotedTotal: pricingV2QuotedTotal ?? null,
104
112
  capacitySeatCredit: {
105
113
  enabled: true,
106
114
  previousPassengerCount,
@@ -10,7 +10,9 @@ interface AdminChangeQuoteRequestKeyInput {
10
10
  returnAvailabilityId: string | null;
11
11
  quantities: Record<string, number>;
12
12
  addOnSelections: Array<{ addOnId: string; variantId?: string; quantity?: number }>;
13
- adminCustomReceiptLines: Array<{ label: string; amountInput: string; amountSign?: number }>;
13
+ adminCustomReceiptLines: unknown[];
14
+ cancellationPolicyId?: string | null;
15
+ structuredAdjustments?: unknown[];
14
16
  refundDispositionsByOperationId?: Record<string, string>;
15
17
  useAdminFeAuthoritativeQuote: boolean;
16
18
  }
@@ -33,6 +35,8 @@ export function buildAdminChangeQuoteRequestKey(input: AdminChangeQuoteRequestKe
33
35
  quantities: input.quantities,
34
36
  addOnSelections: input.addOnSelections,
35
37
  adminCustomReceiptLines: input.adminCustomReceiptLines,
38
+ cancellationPolicyId: input.cancellationPolicyId ?? null,
39
+ structuredAdjustments: input.structuredAdjustments ?? [],
36
40
  refundDispositionsByOperationId: Object.fromEntries(
37
41
  Object.entries(input.refundDispositionsByOperationId ?? {}).sort(([a], [b]) => a.localeCompare(b)),
38
42
  ),
@@ -74,9 +74,7 @@ export async function quoteCustomerChangeForCheckout({
74
74
  newPickupLocationId: pickupLocationId || null,
75
75
  newReturnAvailabilityId: returnAvailabilityId ?? null,
76
76
  newPassengerCounts: bookingItems,
77
- ...(addOnSelections && addOnSelections.length > 0
78
- ? { newAddOnSelections: addOnSelections }
79
- : {}),
77
+ ...(addOnSelections ? { newAddOnSelections: addOnSelections } : {}),
80
78
  clientProposedTotal,
81
79
  });
82
80
  const { quoteSlice, mergedQuote } = sliceAndMergeChangeQuoteForUi(
@@ -163,9 +161,7 @@ export async function quoteAdminChangeForCheckout({
163
161
  newPickupLocationId: pickupLocationId || null,
164
162
  newReturnAvailabilityId: returnAvailabilityId ?? null,
165
163
  newPassengerCounts: bookingItems,
166
- ...(addOnSelections && addOnSelections.length > 0
167
- ? { newAddOnSelections: addOnSelections }
168
- : {}),
164
+ ...(addOnSelections ? { newAddOnSelections: addOnSelections } : {}),
169
165
  ...(manualLineAdjustments && manualLineAdjustments.length > 0
170
166
  ? { manualLineAdjustments }
171
167
  : {}),
@@ -37,6 +37,8 @@ export type ProviderDashboardChangeBookingPayload = {
37
37
  parentProductId?: string;
38
38
  /** Product option id selected for the new booking. */
39
39
  productId: string;
40
+ /** Exact outbound availability selected and priced by the server. */
41
+ availabilityId?: string | null;
40
42
  dateTime: string;
41
43
  bookingItems: Array<{ category: string; count: number }>;
42
44
  returnAvailabilityId?: string | null;
@@ -67,6 +69,9 @@ export type ProviderDashboardChangeBookingPayload = {
67
69
  string,
68
70
  'REFUND_TO_ORIGINAL_PAYMENT' | 'PENDING_REFUND' | 'NO_REFUND'
69
71
  >;
72
+ /** Exact server quote shown to the operator. Apply reuses this quote when it is still valid. */
73
+ pricingV2QuoteId?: string | null;
74
+ pricingV2QuotedTotal?: number | null;
70
75
  /**
71
76
  * Admin/provider change flow: when FE-authoritative quoting is used, apply must persist
72
77
  * the exact receipt that was quoted instead of rebuilding from catalog pricing.
@@ -338,6 +338,8 @@ export function useAdminChangeCheckoutController({
338
338
  mergedProviderAdditionalAdjustments,
339
339
  adminStructuredAdjustments,
340
340
  refundDispositionsByOperationId,
341
+ pricingV2QuoteId: latestChangeQuote?.quoteId ?? null,
342
+ pricingV2QuotedTotal: latestChangeQuote?.quotedTotal ?? latestChangeQuote?.serverDisplay?.total ?? null,
341
343
  providerApplyAuthoritativeReceipt,
342
344
  previousPassengerCount: changeFlowInitialTicketCount,
343
345
  previousAvailabilityId: initialValues?.availabilityId ?? null,
@@ -417,6 +419,31 @@ export function useAdminChangeCheckoutController({
417
419
  return;
418
420
  }
419
421
 
422
+ // Provider changes are applied by the dashboard through the exact Pricing V2 quote.
423
+ // Do not create the legacy FE-authoritative change intent or collect a card payment first;
424
+ // apply-v2 records any positive delta as a quote-linked pending balance.
425
+ if (isProviderDashboardChange && isChangeBookingContext) {
426
+ if (!onChangeBooking) {
427
+ throw new Error('Provider change handler is unavailable.');
428
+ }
429
+ if (!latestChangeQuote?.quoteId || latestChangeQuote.canProceed === false) {
430
+ throw new Error(latestChangeQuote?.reasonIfBlocked || 'The authoritative change quote is not ready.');
431
+ }
432
+ const providerPayload = buildProviderChangePayload(availabilityProductOptionId, bookingItems);
433
+ if (!providerPayload) {
434
+ throw new Error('No availability selected');
435
+ }
436
+ await onChangeBooking(providerPayload);
437
+ const bookingReference = initialValues?.bookingReference?.trim();
438
+ if (!bookingReference) {
439
+ throw new Error('Missing booking reference.');
440
+ }
441
+ onSuccess?.({ reservationReference: bookingReference });
442
+ finishManageFlow(bookingReference);
443
+ setLoading(false);
444
+ return;
445
+ }
446
+
420
447
  const bookingSourceContext = buildBookingSourceContext(bookingSourceAttribution, {
421
448
  clientChannelSource: inferClientBookingSourceFromProductIds(
422
449
  product.productId,
@@ -28,10 +28,18 @@ import {
28
28
  import type { Currency } from './CurrencySwitcher';
29
29
  import type { AdminChangeLatestQuote } from './useAdminChangeCheckoutController';
30
30
  import { buildAdminChangeQuoteRequestKey } from './admin-change-quote-request-key';
31
+ import type { ProviderDashboardChangeBookingPayload } from './provider-dashboard-change-booking';
31
32
 
32
33
  type AddOnSelection = { addOnId: string; variantId?: string; quantity?: number };
33
- type AdminCustomReceiptLine = { label: string; amountInput: string; amountSign?: number };
34
- type AdditionalAdjustment = { label: string; amount: number };
34
+ type AdminCustomReceiptLine = {
35
+ label: string;
36
+ amountInput: string;
37
+ amountSign?: number;
38
+ calculation: 'FIXED_AMOUNT' | 'PERCENTAGE';
39
+ scope: 'POSITIVE_AMENDMENT_VALUE' | 'CURRENT_ACTIVE_TICKETS' | 'CURRENT_ACTIVE_BOOKING';
40
+ taxBehavior: 'NON_TAXABLE' | 'TAXABLE' | 'PRE_TAX_DISCOUNT' | 'POST_TAX_DISCOUNT';
41
+ reasonCode: string;
42
+ };
35
43
 
36
44
  interface ChangeSelectionDetailsForPreview {
37
45
  dateChanged: boolean;
@@ -51,7 +59,9 @@ export interface UseAdminChangeQuotePreviewParams {
51
59
  pickupLocationId: string | null;
52
60
  quantities: Record<string, number>;
53
61
  addOnSelections: AddOnSelection[];
62
+ cancellationPolicyId: string | null;
54
63
  adminCustomReceiptLines: AdminCustomReceiptLine[];
64
+ adminStructuredAdjustments: NonNullable<ProviderDashboardChangeBookingPayload['structuredAdjustments']>;
55
65
  refundDispositionsByOperationId: Record<string, AdminAmendmentRefundDisposition>;
56
66
  useAdminFeAuthoritativeQuote: boolean;
57
67
  latestChangeQuote: AdminChangeLatestQuote | null;
@@ -78,7 +88,6 @@ export interface UseAdminChangeQuotePreviewParams {
78
88
  changeFlowNewBookingTotal: number;
79
89
  effectiveSubtotalForCheckout: number;
80
90
  effectiveTax: number;
81
- adminCustomLinesAsAdditionalAdjustments: AdditionalAdjustment[];
82
91
  adminFeAuthoritativeReceipt: AdminFeAuthoritativeReceipt;
83
92
  isTaxIncludedInPrice: boolean;
84
93
  t: (key: string) => string;
@@ -109,7 +118,9 @@ export function useAdminChangeQuotePreview({
109
118
  pickupLocationId,
110
119
  quantities,
111
120
  addOnSelections,
121
+ cancellationPolicyId,
112
122
  adminCustomReceiptLines,
123
+ adminStructuredAdjustments,
113
124
  refundDispositionsByOperationId,
114
125
  useAdminFeAuthoritativeQuote,
115
126
  latestChangeQuote,
@@ -136,7 +147,6 @@ export function useAdminChangeQuotePreview({
136
147
  changeFlowNewBookingTotal,
137
148
  effectiveSubtotalForCheckout,
138
149
  effectiveTax,
139
- adminCustomLinesAsAdditionalAdjustments,
140
150
  adminFeAuthoritativeReceipt,
141
151
  isTaxIncludedInPrice,
142
152
  t,
@@ -167,6 +177,8 @@ export function useAdminChangeQuotePreview({
167
177
  quantities,
168
178
  addOnSelections,
169
179
  adminCustomReceiptLines,
180
+ cancellationPolicyId,
181
+ structuredAdjustments: adminStructuredAdjustments,
170
182
  refundDispositionsByOperationId,
171
183
  useAdminFeAuthoritativeQuote,
172
184
  }), [
@@ -179,6 +191,8 @@ export function useAdminChangeQuotePreview({
179
191
  quantities,
180
192
  addOnSelections,
181
193
  adminCustomReceiptLines,
194
+ cancellationPolicyId,
195
+ adminStructuredAdjustments,
182
196
  refundDispositionsByOperationId,
183
197
  useAdminFeAuthoritativeQuote,
184
198
  ]);
@@ -420,11 +434,10 @@ export function useAdminChangeQuotePreview({
420
434
  newPickupLocationId: pickupLocationId || null,
421
435
  newReturnAvailabilityId: selectedReturnOption?.returnAvailabilityId ?? null,
422
436
  newPassengerCounts: bookingItems,
423
- // Omit when empty: backend treats [] as "clear all"; missing = preserve stored selections (BookingChangeIntentService).
424
- ...(addOnSelections.length > 0 ? { newAddOnSelections: addOnSelections } : {}),
425
- ...(adminCustomLinesAsAdditionalAdjustments.length > 0
426
- ? { manualLineAdjustments: adminCustomLinesAsAdditionalAdjustments }
427
- : {}),
437
+ // Admin owns the complete target selection. [] intentionally clears all add-ons.
438
+ newAddOnSelections: addOnSelections,
439
+ cancellationPolicyId,
440
+ structuredAdjustments: adminStructuredAdjustments,
428
441
  ...(Object.keys(refundDispositionsByOperationId).length > 0
429
442
  ? { refundDispositionsByOperationId }
430
443
  : {}),
@@ -1,4 +1,4 @@
1
- import { useMemo } from 'react';
1
+ import { useMemo, useState } from 'react';
2
2
  import { roundMoney } from '../../lib/booking/change-flow-pricing';
3
3
  import type {
4
4
  ProviderDashboardChangePricingUi,
@@ -55,6 +55,9 @@ export function useAdminProviderPricingAdjustments({
55
55
  adminCustomReceiptLines,
56
56
  isManualOverrideEligibleLine,
57
57
  }: UseAdminProviderPricingAdjustmentsParams) {
58
+ const [adjustmentIdNamespace] = useState(() =>
59
+ `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`,
60
+ );
58
61
  const providerTotalsPreview =
59
62
  showProviderPricingInlineEditor && providerPricingUi?.totalsPreview
60
63
  ? providerPricingUi.totalsPreview
@@ -137,17 +140,18 @@ export function useAdminProviderPricingAdjustments({
137
140
  );
138
141
 
139
142
  const adminStructuredAdjustments = useMemo(
140
- (): AdminStructuredAdjustment[] =>
141
- adminCustomReceiptLines.flatMap((line) => {
143
+ (): AdminStructuredAdjustment[] => {
144
+ const customAdjustments = adminCustomReceiptLines.flatMap((line) => {
142
145
  const value = Number(line.amountInput.trim());
143
146
  if (!Number.isFinite(value) || value <= 0) return [];
144
147
  const discount = (line.amountSign ?? 1) < 0;
145
148
  const percentage = line.calculation === 'PERCENTAGE';
149
+ const mode: AdminStructuredAdjustment['mode'] = percentage
150
+ ? discount ? 'PERCENTAGE_DISCOUNT' : 'PERCENTAGE_SURCHARGE'
151
+ : 'FIXED_AMOUNT';
146
152
  return [{
147
153
  adjustmentId: line.id,
148
- mode: percentage
149
- ? discount ? 'PERCENTAGE_DISCOUNT' : 'PERCENTAGE_SURCHARGE'
150
- : 'FIXED_AMOUNT',
154
+ mode,
151
155
  ...(percentage
152
156
  ? { percentageBasisPoints: Math.round(value * 100) }
153
157
  : { fixedAmount: roundMoney((discount ? -1 : 1) * value) }),
@@ -156,8 +160,48 @@ export function useAdminProviderPricingAdjustments({
156
160
  reasonCode: line.reasonCode.trim() || 'STAFF_ADJUSTMENT',
157
161
  reason: line.label.trim() || null,
158
162
  }];
159
- }),
160
- [adminCustomReceiptLines],
163
+ });
164
+ const inlineAdjustments = isProviderDashboardChange
165
+ ? (providerPricingUi?.additionalAdjustments ?? []).flatMap((line) => {
166
+ const value = Number((line.amountInput ?? '').trim());
167
+ if (!Number.isFinite(value) || value <= 0) return [];
168
+ const discount = line.mode === 'DISCOUNT';
169
+ return [{
170
+ adjustmentId: line.id,
171
+ mode: 'FIXED_AMOUNT' as const,
172
+ fixedAmount: roundMoney((discount ? -1 : 1) * value),
173
+ scope: 'CURRENT_ACTIVE_BOOKING' as const,
174
+ taxBehavior: 'TAXABLE' as const,
175
+ reasonCode: 'PROVIDER_ADJUSTMENT',
176
+ reason: line.label?.trim() || (discount ? 'Provider discount' : 'Provider charge'),
177
+ }];
178
+ })
179
+ : [];
180
+ const lineOverrideAdjustments = providerPricingOverrides.flatMap((override, index) => {
181
+ const original = providerQuotedLines.find((line) => line.lineKey === override.lineKey);
182
+ if (!original) return [];
183
+ const delta = roundMoney(override.amount - original.amount);
184
+ if (Math.abs(delta) < 0.005) return [];
185
+ return [{
186
+ adjustmentId: `provider-line-${adjustmentIdNamespace}-${index}`,
187
+ mode: 'FIXED_AMOUNT' as const,
188
+ fixedAmount: delta,
189
+ scope: 'CURRENT_ACTIVE_BOOKING' as const,
190
+ taxBehavior: 'TAXABLE' as const,
191
+ reasonCode: 'PROVIDER_LINE_OVERRIDE',
192
+ reason: `Override ${original.label}`,
193
+ }];
194
+ });
195
+ return [...customAdjustments, ...inlineAdjustments, ...lineOverrideAdjustments];
196
+ },
197
+ [
198
+ adminCustomReceiptLines,
199
+ isProviderDashboardChange,
200
+ providerPricingUi?.additionalAdjustments,
201
+ providerPricingOverrides,
202
+ providerQuotedLines,
203
+ adjustmentIdNamespace,
204
+ ],
161
205
  );
162
206
 
163
207
  const mergedProviderAdditionalAdjustments = useMemo(
@@ -372,8 +372,8 @@ export function useChangeBookingQuotePreview({
372
372
  newPickupLocationId: pickupLocationId || null,
373
373
  newReturnAvailabilityId: selectedReturnOption?.returnAvailabilityId ?? null,
374
374
  newPassengerCounts: bookingItems,
375
- // Omit when empty: backend treats [] as "clear all"; missing = preserve stored selections (BookingChangeIntentService).
376
- ...(addOnSelections.length > 0 ? { newAddOnSelections: addOnSelections } : {}),
375
+ // The editor owns the complete target selection. [] intentionally clears all add-ons.
376
+ newAddOnSelections: addOnSelections,
377
377
  clientProposedTotal: changeFlowNewBookingTotal,
378
378
  capacitySeatCredit: {
379
379
  enabled: true,
@@ -1515,7 +1515,7 @@ export interface ApplyPaidChangeResponse {
1515
1515
  }
1516
1516
 
1517
1517
  export interface BookingChangeIntentStatusResponse {
1518
- status: 'CREATED' | 'PAYMENT_REQUIRED' | 'PAYMENT_SUCCEEDED' | 'APPLYING' | 'APPLIED' | 'FAILED' | 'EXPIRED';
1518
+ status: 'CREATED' | 'PAYMENT_REQUIRED' | 'PAYMENT_AUTHORIZED' | 'PAYMENT_SUCCEEDED' | 'APPLYING' | 'APPLIED' | 'FAILED' | 'EXPIRED';
1519
1519
  bookingReference?: string;
1520
1520
  booking?: unknown;
1521
1521
  failureCode?: string;
@@ -176,6 +176,8 @@ test('admin no-charge provider payload carries the selected pickup itinerary', (
176
176
  mergedProviderAdditionalAdjustments: [],
177
177
  adminStructuredAdjustments: [],
178
178
  refundDispositionsByOperationId: {},
179
+ pricingV2QuoteId: 'quote_reviewed',
180
+ pricingV2QuotedTotal: 202.87,
179
181
  previousPassengerCount: 1,
180
182
  previousAvailabilityId: 'availability_1',
181
183
  previousReturnAvailabilityId: 'return_1',
@@ -185,6 +187,10 @@ test('admin no-charge provider payload carries the selected pickup itinerary', (
185
187
  assert.deepEqual(payload?.itineraryDisplay, itineraryDisplay);
186
188
  assert.equal(payload?.pickupLocationId, 'pickup_new');
187
189
  assert.equal(payload?.travelerHotel, 'A Bear and Bison Inn');
190
+ assert.equal(payload?.availabilityId, 'availability_1');
191
+ assert.deepEqual(payload?.addOnSelections, []);
192
+ assert.equal(payload?.pricingV2QuoteId, 'quote_reviewed');
193
+ assert.equal(payload?.pricingV2QuotedTotal, 202.87);
188
194
  });
189
195
 
190
196
  test('public paid change preview shows amendment-only charges before combined tax', () => {
@@ -349,6 +355,35 @@ test('changing admin refund treatment creates a distinct authoritative quote req
349
355
  assert.notEqual(pending, noRefund);
350
356
  });
351
357
 
358
+ test('admin cancellation policy and structured adjustments are authoritative quote inputs', () => {
359
+ const base = {
360
+ bookingReference: 'DE8H04DX',
361
+ lastName: 'Tauro',
362
+ productId: 'p_1',
363
+ selectedAvailability: {
364
+ availabilityId: 'a_1',
365
+ productId: 'po_1',
366
+ dateTime: '2026-07-21T03:00:00-06:00',
367
+ vacancies: 10,
368
+ } as Parameters<typeof buildAdminChangeQuoteRequestKey>[0]['selectedAvailability'],
369
+ pickupLocationId: 'pickup_1',
370
+ returnAvailabilityId: 'return_1',
371
+ quantities: { ADULT: 1 },
372
+ addOnSelections: [],
373
+ adminCustomReceiptLines: [],
374
+ useAdminFeAuthoritativeQuote: true,
375
+ };
376
+ const original = buildAdminChangeQuoteRequestKey(base);
377
+ const policyChanged = buildAdminChangeQuoteRequestKey({ ...base, cancellationPolicyId: 'flexible' });
378
+ const adjustmentChanged = buildAdminChangeQuoteRequestKey({
379
+ ...base,
380
+ structuredAdjustments: [{ adjustmentId: 'adj_1', mode: 'FIXED_AMOUNT', fixedAmount: 10 }],
381
+ });
382
+
383
+ assert.notEqual(original, policyChanged);
384
+ assert.notEqual(original, adjustmentChanged);
385
+ });
386
+
352
387
  test('private shuttle pricing replaces the selected calendar summary with hydrated rate details', () => {
353
388
  const selectedSummary = {
354
389
  availabilityId: 'a_private',