Skip to content

Regression test cases — Cancellation, amendment and fees

Audience: QA. Scope: cancelling a booking or a single line, the cancellation-fee calculation, the fee override, reviving a cancelled booking, waitlist release, and the housekeeping jobs that cancel or delete.

Every case cites the microflow and node it derives from, so expected results can be re-derived rather than trusted. Mechanism detail: docs/deep-dives/cancellation-and-fees.md.

Priority: P1 blocks release, P2 must pass before sign-off, P3 when time allows.

Money warning. Sections D and E change what a client is charged. Treat any unexpected result there as P1 regardless of the priority column.

None of these cases have been executed. They state what the system currently does, derived from its decision logic. A pass means unchanged, not correct.

Revised 2026-08-07 against a re-generated model extract; section N is new. See quote-to-booking.md §12.


What to assert on

Cancellation is not a single status change. A correct cancellation touches five things:

Thing Cancelled, no fee Cancelled with costs Hard delete
Booking.WindowStatus CX ("Canc") CC ("CCost") row gone
Booking.InventoryStatus CX CX row gone
Booking.Cancelled true (only if Wish cancel succeeded) true
BookingLine.LIVE false true on fee-bearing lines
BookingLine.aSequence original + 1000 + 1000

A fee-bearing line stays LIVE on purpose — it carries the charge. Asserting "all lines are not LIVE" after a cancellation with fees is a wrong expectation.


A. Which outcome — delete vs cancel

ID Case Preconditions Steps Expected Pri Source
A1 Empty draft is deleted outright WindowStatus = Draft, no TravelLocation anywhere under the booking's legs, TourPlanReference empty or ≤ 0 Cancel Booking row is deleted, not marked CX. A DataManagement.ArchiveLog exists with Reason = 'No travellocations so delete' and Action = 'Delete booking' P1 CancelBooking [10],[68-71]
A2 Draft with a Tourplan reference is cancelled, not deleted Same but TourPlanReference > 0 Cancel Booking survives with WindowStatus = CX P1 Booking.CancelBooking [68]
A3 Draft with travel locations is cancelled, not deleted WindowStatus = Draft but a TravelLocation exists Cancel Booking survives, WindowStatus = CX P1 CancelBooking [10]
A4 Non-draft with no travel locations is cancelled No TravelLocation, WindowStatus = P Cancel Booking survives — the delete needs both conditions P2 CancelBooking [10]
A5 Quote-only cancellation takes the simple path Source = Quote_only Cancel WindowStatus = CX, InventoryStatus = CX, Cancelled = true, CancelledByUser set. ChangeDate is set, CancellationDate is NOT — defect C10 P1 CancelBooking [17],[24]
A6 Confirmed booking sets CancellationDate WindowStatus = C, Wish-backed Cancel CancellationDate populated at [35] P1 CancelBooking [35]
A7 CC is never demoted to CX Booking already WindowStatus = CC Cancel again Stays CC P1 CancelBooking [35]
A8 Travelled booking cannot be cancelled WindowStatus = Traveled Cancel Info "Booking is in an invalid state ( Status = Trav'd) to be Cancelled in the Window."; nothing changes P1 Booking.IsCancellationValid [3]
A9 Travelling booking cannot be cancelled WindowStatus = Travelling Cancel Same rejection P1 Booking.IsCancellationValid [2]

B. Entry-point guards

ID Case Preconditions Steps Expected Pri Source
B1 Travelled blocked on the standard route WindowStatus = Traveled Cancel from a booking grid Info "You cannot cancel a booking that is already travelling or has travelled (why would you do that?) Please get your manager to speak to WW support" P1 Booking.IVK_CancelBooking_Confirm [2-3]
B2 Ex-Window blocked Source = Ex_window Cancel Info "Booking is ex-Window and cannot be cancelled via the Window" P1 Booking.IVK_CancelBooking_Confirm [14]
B3 Tourplan-confirmed redirected to the booking file TourPlanFullReference populated Cancel from a grid Info "As this booking has been confirmed in Tourplan, please cancel from the booking file" P1 Booking.IVK_CancelBooking_Confirm [12]
B4 Simple confirmation page for draft-ish bookings WindowStatus in Draft, empty, Placeholder, Quote_only Cancel Page Booking.CancelBooking_confirm opens P2 Booking.IVK_CancelBooking_Confirm [10]
B5 Full confirmation page otherwise WindowStatus = P or C Cancel Page Booking.CancelBookingConfirmation opens P2 Booking.IVK_CancelBooking_Confirm [8]
B6 The two entry points disagree — travelled WindowStatus = Traveled, user is a Manager Cancel via the standard route, then via the _NoRedirect route Standard route blocks. _NoRedirect allows the manager through. Log as defect C2 unless product confirms the divergence is intended P1 Booking.IVK_CancelBooking_Confirm [2] vs ..._NoRedirect [12-15]
B7 The two entry points disagree — Tourplan reference TourPlanFullReference populated Cancel via _NoRedirect No Tourplan check is applied — cancellation proceeds where the standard route would have blocked it. Defect C2 P1 ..._NoRedirect [4-5]
B8 Bulk cancel bypasses all wrapper guards Debug user, a list including a travelled and an ex-Window booking Run IVK_CancelBookings Only IsCancellationValid applies, so travelled bookings are rejected but ex-Window and Tourplan-confirmed bookings are not. Confirm this is acceptable for a Debug-only tool P2 Booking.IVK_CancelBookings [3], IsCancellationValid
B9 Orphaned booking-user-history row BookingUserHistory whose booking has been deleted Cancel from booking-user history The history row is deleted, no error P3 Booking.IVK_CancelBooking_Confirm_FromBookingUserHistory [6]
B10 Role coverage One user per role in Administrator, Agent, Employee, Manager, Debug, ExternalConsultant, plus one outside Attempt cancel Only the six listed roles can reach IVK_CancelBooking_Confirm; CancelBooking itself is Agent, Debug, Employee, ExternalConsultant P2 model role lists

C. Cancellation side effects

ID Case Preconditions Steps Expected Pri Source
C1 Lines are marked and delisted Booking with several live lines Cancel Each line: Cancel = true, LIVE = false, aSequence increased by exactly 1000, TravelDay and TravelLocation detached P1 CancelBooking [31-34], Booking.CancelBL [4]
C2 Tourplan flagged for update Line was Chargeable with TourPlanStatus != Draft Cancel TPUpdateRequired = true; line TourPlanStatus = Pending; booking TourplanStatus = Pending unless already CC/CX P1 Booking.CancelBL [3-5]
C3 Draft Tourplan status is preserved Line TourPlanStatus = Draft Cancel Stays Draft — not moved to Pending P2 Booking.CancelBL [4]
C4 Wish cancel failure aborts Wish-backed booking; force CancelWishHeader to fail Cancel Error "Your booking could not be cancelled die to Wish error"; a Critical Logging.SystemLog naming the Wish and Window references; booking is NOT marked cancelled P1 CancelBooking [59-63]
C5 Wish failure leaves a support trail As C4 Cancel, inspect the log Resolution text: "The cancellation of this booking failed due to a system issue. The booking has been handed over to a consultant to complete the cancellation." P2 Booking.CancelBooking [61]
C6 Third-party inventory released Booking with LiveRequest lines Cancel DataManagement.CancelBooking_LR runs P2 Booking.CancelBooking [36]
C7 Itineraries removed from Wetu Booking with PartyCostGroups that have itineraries Cancel Itinerary.DeleteItineraryInWetu runs per PartyCostGroup P2 CancelBooking [38-39]
C8 Guests cancelled Any booking Cancel BookingGuestManagement.CancelBooking_Guests runs P2 Booking.CancelBooking [18],[30]
C9 Explorations cleared Booking with an exploration Cancel Explorations.ClearExploration runs P3 Booking.CancelBooking [15]
C10 Open system logs auto-resolved Booking with unresolved Logging.SystemLog rows (Status not Resolved/Audit) Cancel All become Resolved, ResolvedBy = 'System', Resolution = 'Booking cancelled', DateTimeResolved set. Diagnostic trail is erased — defect C11 P2 CancelBooking [46-49]
C11 Confirmation message — no Tourplan reference TourPlanReference empty or ≤ 0, ShowConfirmationAfterCancel = true Cancel from an overview Info "Your booking has been successfully cancelled and the reserved inventory has been released where applicable" P2 Booking.CancelBooking [55]
C12 Confirmation message — Tourplan reference present TourPlanReference > 0 Cancel Info "A consultant has been informed of the cancellation of this booking and will be in contact with you in the event of any financial settlement. Please be aware that we can not show this financial settlement in the Wilderness Window." P1 Booking.CancelBooking [52]
C13 Silent cancel from bulk paths ShowConfirmationAfterCancel = false Run IVK_CancelBookings No confirmation message at all P3 Booking.CancelBooking [50],[57]
C14 Notification e-mail status Booking that ends up CC Cancel EmailConsultantsAgentBooking is called with BookingStatus.CX even though the booking is CC. Check the template wording is not misleading P2 Booking.CancelBooking [40]
C15 Repricing triggered Booking with chargeable lines Cancel Pricing.SumDetailedBookingLines($Booking, false) runs; booking totals reflect fees only P1 Booking.CancelBooking [41]
C16 Audit trail Any cancellation Cancel Booking.Event.Cancellation "Booking cancelled by user" at [8]; BHL_LogEntry_Create "Cancel Booking" with Booking.Event.Cancel at [43] P2 CancelBooking [8],[43]
C17 Archive log on manual cancel Cancel from an overview grid Cancel ArchiveLog with process = 'IVK_CancelBooking_Overview', Reason = 'Manual by :<user>', Whom set P2 Booking.IVK_CancelBooking_Overview [5]

D. The fee engine — when a fee applies

Each of D1–D5 must produce no fee. Assert DetailedBookingLine.SellingPrice is unchanged.

ID Case Preconditions Steps Expected Pri Source
D1 Override suppresses all fees Booking.NoCancelFees = true Cancel No fee on any line, whatever the dates P1 Booking.CancelFees [2]
D2 Non-chargeable lines are free Line Chargeable = false Cancel No fee P1 Booking.CancelFees [3]
D3 Unconfirmed lines are free Line BookingLineStatus = Provisional (not Confirmed) Cancel No fee P1 Booking.CancelFees [4]
D4 Lines never written to Tourplan are free Confirmed chargeable line with no TourplanBookingServiceLine records Cancel No fee, regardless of proximity to travel. High-value case — this is how provisional-only bookings cancel free P1 Booking.CancelFees [7]
D5 Zero percentage means no fee Payment terms configured with CancellationFee = 0 Cancel HasCancelFee = false, ApplyCancelFees is not called P1 Booking.CancelFees [14-16]
D6 Configured payment terms win Supplier with PaymentTerms/CancellationFee = 35 Cancel Fee is 35%, not any fallback-ladder value P1 Booking.GetCancelFeePerc [7]
D7 Empty configured fee is 0% Payment terms found but CancellationFee empty Cancel 0% → no fee P2 Booking.GetCancelFeePerc [7]
D8 Missing Day_First charges 100% Confirmed chargeable line with Day_First empty Cancel GetCancelFeePerc returns empty, which Booking.CancelFees [13] converts to 1full-value fee. Verify this is intended and not a data-quality trap P1 Booking.GetCancelFeePerc [2], Booking.CancelFees [13]
D9 Relaxed payment terms are honoured Booking.RelaxedPmtTermsApply = true Cancel Pricing.GetPmtTerms receives the flag and may return different terms P2 Booking.GetCancelFeePerc [5]

E. The fee ladder — fallback when no payment terms exist

All of section E requires a supplier with no matching PaymentTerms. Fee is a fraction: 0.25 = 25%.

ID Line type Days before travel Expected fee Pri Source
E1 Accommodation 90 25% P1 Booking.GetCancelFeePerc [11]
E2 Accommodation 61 25% P1 [11] — boundary is > 60
E3 Accommodation 60 100% P1 [11] — exactly 60 falls to the else branch
E4 Accommodation 5 100% P1 [11]
E5 Exploration 90 / 60 25% / 100% P2 [10-11] same branch as accommodation
E6 Private Activity 90 / 60 25% / 100% P2 [10-11]
E7 Flight 10 0% P1 [14] — > 7
E8 Flight 8 0% P1 [14] boundary
E9 Flight 7 25% P1 [14] — exactly 7 drops a band
E10 Flight 4 25% P1 [14] — > 3
E11 Flight 3 50% P1 [14] boundary
E12 Flight 2 75% P1 [14] — > 1
E13 Flight 1 100% P1 [14] final else
E14 Flight 0 / same day 100% P1 [14]
E15 Transfer, Sundry, Road Transfer, or any other type any 0% P1 [13]
E16 Associated product — supplier OwnershipID in M1, J1, J2, K1, CR, DM, N1, TN any, even 365 days out 100% P1 [8-9] returns empty → Booking.CancelFees [13] → 1
E17 Same supplier codes with payment terms configured any The configured percentage — the $IsAP branch is only reached when terms are missing P1 [6-7]
E18 Supplier with an ownership code not in either hardcoded list 90 days, accommodation Falls to the ordinary accommodation ladder (25%). Confirms new supplier codes silently get different treatment — defect C1 P2 [3],[10]

E16 is the highest-risk row in this suite: a master-data gap on an associated-product supplier charges the client the full value of the line.


F. Applying the fee

ID Case Preconditions Steps Expected Pri Source
F1 Prices are overwritten with the fee Line with SellingPrice = 1000, CostPrice = 600, fee 25% Cancel SellingPrice = 250, SellingPrice_CancellationAdjustment = 750, CostPrice = 150, CostPrice_CancellationAdjustment = 450 P1 Booking.ApplyCancelFees [6-8]
F2 Original prices are preserved in the "after discounts" fields Same, with SellingPriceAfterDiscounts = 0 beforehand Cancel SellingPriceAfterDiscounts becomes the original 1000; CostPriceAfterDiscounts becomes 600 P1 Booking.ApplyCancelFees [8]
F3 Existing "after discounts" values are not clobbered SellingPriceAfterDiscounts = 900 beforehand Cancel Stays 900 — only a zero is overwritten P1 Booking.ApplyCancelFees [8]
F4 Gross profit is the post-fee margin F1's numbers Cancel GrossProfit = 250 − 150 = 100 P1 Booking.ApplyCancelFees [8]
F5 GrossProfitPerc is the pre-fee margin F1's numbers Cancel GrossProfitPerc = (1000 − 600) / 1000 × 100 = 40, i.e. the original margin, not 100/250 = 40%... assert the actual value and log the mismatch against GrossProfit. Defect C6 P2 Booking.ApplyCancelFees [8]
F6 Fee percentage recorded on the detail line fee 25% Cancel CancelFee_Cost = CancelFee_Sell = 25.00 (rounded to 2dp) P2 Booking.ApplyCancelFees [8]
F7 Fee percentage recorded on the booking line fee 25% Cancel CancelFeeAdj_Cost = CancelFeeAdj_Sell = 25unrounded, unlike F6 P3 Booking.ApplyCancelFees [17]
F8 Party cost group receives the waived fraction fee 25% Cancel CancelFee_PCG_BL is called with 0.75, not 0.25 P2 Booking.ApplyCancelFees [16]
F9 Tax recomputed inline when rates are present TaxRate_Cost and TaxRate_Mkup both set Cancel TaxOnCost, TaxOnMarkup, TaxOnSell recomputed; TaxedSuccessfully = true P1 Booking.ApplyCancelFees [10-13]
F10 Tax falls back when rates are missing Either rate empty Cancel Pricing.CalculateDetailedBookingLineTax is called instead P1 Booking.ApplyCancelFees [14]
F11 Fee-bearing line stays LIVE Confirmed chargeable line attracting a fee Cancel the booking The line remains LIVE = true so it can carry the charge. Do not assert all lines are delisted P1 see §1 of the deep-dive
F12 Booking lands on CC when fees apply Booking where at least one line attracts a fee Cancel WindowStatus = CC ("CCost"), not CX P1 CancelBooking [35]
F13 Booking lands on CX when no fees apply All lines free (any of D1–D5) Cancel WindowStatus = CX P1 CancelBooking [35]

G. The fee override

ID Case Preconditions Steps Expected Pri Source
G1 Toggle on NoCancelFees = false Click "Override cancellation fees" NoCancelFees = true; LogEvent(Event.Cancellation, 'Cancellation fees turned off') P1 Booking.IVK_ToggleCancelFees [2-3]
G2 Toggle off NoCancelFees = true Click "Cancel fees overridden" NoCancelFees = false; log says 'Cancellation fees turned on' P1 Booking.IVK_ToggleCancelFees [2-3]
G3 No approval and no manager gate Users in Employee and ExternalConsultant Toggle the override Both succeed. There is no manager check and no approval record beyond the event log. Defect C3 — treat as a control finding and confirm with the business P1 model roles on IVK_ToggleCancelFees
G4 Manager role is not required Manager and non-manager Employee Toggle Both behave identically P2 model roles
G5 Cancel-with-no-fees button sets the flag then cancels Any cancellable booking Click the no-fees cancel button NoCancelFees = true set before CancelBooking runs, so no fee is calculated; log 'No cancellation fees to be calculated for this booking - assume a postponement' P1 Booking.IVK_CancelBooking_Overview_NoCancelFees [3-7]
G6 Post-cancel redirect by user type Employee, then Agent Cancel with no fees Employee → BookingOverview_Party; Agent → BookingOverview_Agent; other → no redirect P3 Booking.IVK_CancelBooking_Overview_NoCancelFees [11-17]
G7 Override is independent of the cancellation reason Cancel with reason Cancelled_but_not_charging_fees_as_a_favour_to_agent but NoCancelFees = false Cancel Fees are still charged. The reason is descriptive only. Confirm the business expects this P1 Booking.CancelFees [2]; reason enum is not read by the fee engine
G8 Waiver reporting Several cancellations, some with the override Report on waivers Waivers are identifiable only via NoCancelFees and the event log, not via CancelBookingReason P2 as G7

H. Reviving a cancelled booking

ID Case Preconditions Steps Expected Pri Source
H1 Only Debug users can revive Users in Employee, Manager, Agent, Debug Look for "Revive cancelled booking" Visible and executable only for Debug. There is no self-service undo P1 model roles on IVK_Uncancel
H2 Booking returns to Draft Cancelled booking Revive WindowStatus = Draft, InventoryStatus = Pending, Cancelled = false, ReadyForTravel = false P1 Booking.IVK_Uncancel [2]
H3 Wish linkage is destroyed Cancelled Wish-backed booking Revive WishBookingReference and WishPartyGroupId cleared; WishUpdateStatus and WishPartyGroupStatus both Update_required. The booking must be re-provisioned — the original holds are not recovered P1 Booking.IVK_Uncancel [2]
H4 Guest Wish IDs cleared Guests with WishGuestId Revive All cleared P2 Booking.IVK_Uncancel [4-7]
H5 Party cost group Wish IDs reset PCGs with WishPartyGroupId != 0 Revive All set to 0 P2 Booking.IVK_Uncancel [8-11]
H6 Room configs fully reset Wish AvailableRoomConfig rows Revive WishStatus = Draft, WishSectorBookingId empty, Waitlist = No, all Booked_* cleared, every BookedRoomCount_* zeroed, HoldingSpace = false P1 Booking.IVK_Uncancel [12-15]
H7 Tourplan header reset Cancelled booking with a Tourplan record Revive TP_ResetTPHeaderStatus runs; on success TourplanStatus = Pending P1 DataManagement.TP_UncancelBooking [2-4]
H8 Tourplan reset failure is tolerated Force TP_ResetTPHeaderStatus to fail Revive Returns false; the Window-side revive has already happened. Assert the resulting inconsistent state and whether the user is told P1 DataManagement.TP_UncancelBooking [7], Booking.IVK_Uncancel [23-25]
H9 Service lines re-attached Cancelled line with Window-created Tourplan service lines, and a matching live line (same Option, same Day_First) Revive Service lines re-point at the live line; TPServiceLineStatus = VY; new line TourPlanStatus = Pending P1 DataManagement.TP_ReviveCancelledBookingLines [10-11]
H10 Ambiguous match picks arbitrarily Two cancelled lines for the same Option on the same Day_First (e.g. two rooms, two party cost groups) Revive The match takes first with no further discriminator, so service lines can attach to the wrong line. Defect C7 — assert which line receives them and whether it is stable across runs P1 DataManagement.TP_ReviveCancelledBookingLines [5]
H11 Audit trail Any revive Revive Booking.Event.Update with 'Booking UNCANCELLED' P2 Booking.IVK_Uncancel [17]
H12 Step-2 checks gate the Tourplan revive Booking failing Step2Checks Revive The Window-side reset happens but TP_UncancelBooking is not reached P1 Booking.IVK_Uncancel [19-21]

I. Waitlist release

ID Case Preconditions Steps Expected Pri Source
I1 Weekend no-op Run on a Saturday or Sunday Run SE_WaitlistRelease Nothing is processed P1 Booking.SE_WaitlistRelease [2],[30]
I2 Release on availability Waitlisted live line, Waitlist = Yes, WaitListNowAvailable, WaitlistAvailableFrom at least 2 days old, booking Source = Wilderness_window Run on a weekday Lines flagged Cancel = true and passed to Sub_DeleteBookingline_Mem; a warning e-mail is sent P1 Booking.SE_WaitlistRelease [3-4],[22-24]
I3 Two-day cut-off respected WaitlistAvailableFrom is 1 day old Run Not released by the set-A rule P2 Booking.SE_WaitlistRelease [3-4]
I4 Release on proximity to travel alone Waitlisted live line, Waitlist = Yes, booking TripStartDate within 42 days, WaitListNowAvailable false Run Released anyway — set B ignores availability. High-value case; confirm the business expects waitlists inside 6 weeks to be dropped regardless P1 Booking.SE_WaitlistRelease [5-6]
I5 Outside 42 days and not available Trip 60 days out, not available Run Not released P1 Booking.SE_WaitlistRelease [5-6]
I6 Source filter Waitlisted line on a booking with Source = Quote_only or Ex_window Run Not processed — only Wilderness_window and EX_Tourplan P2 Booking.SE_WaitlistRelease [4],[6]
I7 Agent-owned bookings are skipped entirely Waitlisted line on a booking whose Booking_Owner is not an Administration.Employee Run Nothing happens — no release, no e-mail. Defect C8. Confirm whether these are handled elsewhere P1 Booking.SE_WaitlistRelease [26-27]
I8 E-mail routing Employee-owned booking with and without a branch/department e-mail Run Sent to GetBranchDeptEmail when present, otherwise the owner's own address P2 Booking.SE_WaitlistRelease [17-18]
I9 E-mail content I2 Run Subject "WARNING - we have released your waitlist of : for booking :"; type AdvEmail.EmailType.Waitlist_notification; linked to the booking P2 Booking.SE_WaitlistRelease [18-19]
I10 Already-cancelled lines excluded Booking with Cancelled and CancelledWithCosts lines Run Those are excluded from the working list P2 Booking.SE_WaitlistRelease [12]

J. Housekeeping

ID Case Preconditions Steps Expected Pri Source
J1 Orphan drafts removed Booking with Source = Wilderness_window, BookingStep = Step1, empty PartyName, empty BookingReferenceDescriptive, empty WishBookingReference, empty trip dates, NotificationEmail = false, status not CX/CC Run Sub_CancelOrphanDraftBookings ArchiveLog with Reason = 'Step 1 no party name or reference or dates', then the booking is deleted (it meets CancelBooking's delete conditions) P1 Booking.Sub_CancelOrphanDraftBookings [2-5]
J2 Partially-filled drafts survive Same but PartyName populated Run Untouched — every condition is ANDed P1 Booking.Sub_CancelOrphanDraftBookings [2]
J3 Already-cancelled excluded Same but WindowStatus = CX Run Untouched P2 Booking.Sub_CancelOrphanDraftBookings [2]
J4 No batch limit Seed several thousand qualifying orphan drafts Run All are processed in one pass — the retrieve is unbounded, unlike the 150-row sweeps in SE_CancelExpiredBookings. Measure runtime and lock behaviour. Defect C9 P2 Booking.Sub_CancelOrphanDraftBookings [2]
J5 Expired-booking sweeps See regression-quote-to-booking.md §I SE_CancelExpiredBookings is covered there

N. Intent-vs-implementation checks (new)

These derive from developer annotations recovered from the model canvas. Each asserts current behaviour and flags a documented intent the behaviour does not match — so each needs a business answer, not just a pass/fail.

ID Case Preconditions Steps Expected Pri Source
N1 Waitlist released at 6 weeks, not 8 Waitlisted live line, Waitlist = Yes, booking TripStartDate 50 days away (inside 8 weeks, outside 6), WaitListNowAvailable false Run SE_WaitlistRelease on a weekday Not released. The annotation says waitlists inside "8 weeks" are cleared, but the constant is 42 days. Defect C14 — confirm which the business wants P1 Booking.SE_WaitlistRelease [5], flow annotation
N2 Waitlist released at 40 days Same but TripStartDate 40 days away Run Released P1 [5-6]
N3 48-hour rule matches its note Waitlist available for exactly 48 h Run Released — note and code agree here P2 [3-4]
N4 Step-2 cancellation sends no ARC Cancel a booking from wizard step 2 Cancel No supplier availability-release notification is sent. Annotation says "if cancel from step 2 - need to send ARCs". Defect C15 — confirm whether camps should be notified P1 CancelBooking annotation
N5 OnBase told about cancellations Cancel a booking through a confirmation path Cancel Onbase.BookingStatus_Post invoked asynchronously; failure is silent P2 CancelBooking, executeMicroflowInBackground
N6 Retention periods match the 2023 agreement Quote 3 years past trip end; another 5 years past Run SE_CancelExpiredBookings 3-year survives, 5-year deleted. The 4-year rule was agreed 2023-05-05 by Joe and Francis; the ArchiveLog.Reason text still says "6 months"/"one year" and is stale. Assert the queries, and log the strings for correction P1 flow annotations, sweeps 2–4
N7 Legacy fee ladder is not in use Accommodation line, no payment terms, 58 days before travel Cancel 100% under the current ladder. The pre-2021 ladder in the canvas note would have charged 20% at ≥56 days. Confirms the old graduated scale is retired P2 Booking.GetCancelFeePerc [11] and its annotation
N8 BookingLine.Cancel is set by amendments, not just cancellations A live booking; no cancellation performed Remove a night via API.ChangeAccomNumberOfNights (or DeleteAccomodation, or AddDaysToBooking) Affected lines have Cancel = true without any cancellation having occurred. Assert that BookingLineStatus is not Cancelled/CancelledWithCosts and Booking.Cancelled is still false P1 Booking.DeleteTravelLocation, ClearAccommodationRelatedBookingLines, AdjustBookingLinesForDayChanges
N9 Cancellation reporting uses the right signal Perform one real cancellation and one amendment that drops a line Query both ways Counting BookingLine.Cancel returns 2; counting BookingLineStatus in (Cancelled, CancelledWithCosts) returns 1. Any report using the former over-counts cancellations P1 as N8

K. Known defects to assert against

ID Defect Test
K1 C1 — supplier ownership codes hardcoded in two flows with different lists E16, E18. Assert that a supplier with a new code silently changes fee treatment
K2 C2 — the two cancel entry points enforce different rules B6, B7
K3 C3 — fees waivable by Employee/ExternalConsultant with no approval G3. Standing control test
K4 C4CancelFees computes $Invoiced and discards it Cancel an already-invoiced line and an uninvoiced one at the same fee percentage; assert the fee is identical, then raise the question of whether it should be
K5 C6GrossProfitPerc stores the pre-fee margin F5
K6 C7 — revive matches on (Option, Day_First) and takes first H10
K7 C8 — waitlist release skips agent-owned bookings I7
K8 C9 — orphan cleanup is unbounded J4
K9 C10CancellationDate null on the simple cancel path A5. Any cancellation report filtering on it must be checked
K10 C11 — cancellation auto-resolves open system logs C10
K11 C12 — visibility conditions listing every enum value Cosmetic; confirm the no-fee cancel buttons are meant to be always visible
K12 C13 — dated clones (CancelBooking_230226, SE_WaitlistRelease_230226, SE_WaitlistRelease_260326) After any fix, assert clones are updated or deleted
K13 C14 — waitlist released at 42 days against a documented intent of 8 weeks N1, N2
K14 C15 — step-2 cancellation ARC notification never implemented N4
K15 C16 — 2021 "temporary while we sort out the statuses" workaround still live Identify what it guards before changing cancellation status logic

L. Suggested smoke path

Fifteen minutes, two bookings, covers the spine and the money.

  1. A1 — create an empty Step-1 draft, cancel it. Assert the row is deleted and an ArchiveLog exists.
  2. Build and confirm a second booking with an accommodation line 90 days out and a flight line 5 days out, from a supplier with no payment terms.
  3. D4 — before cancelling, check the lines have Tourplan service lines. If not, the fee is zero and the rest of the test is void.
  4. E1 / E10 — cancel. Assert accommodation charged 25%, flight charged 25%.
  5. F1 / F2 — assert SellingPrice now holds the fee and SellingPrice_CancellationAdjustment holds the write-off.
  6. F11 / F12 — assert the fee-bearing lines are still LIVE and the booking is CC.
  7. C12 — assert the financial-settlement message appeared.
  8. G1 / G5 — on a third booking, set the fee override and cancel. Assert CX, no fees, and the 'assume a postponement' log entry.
  9. H1 — confirm the "Revive cancelled booking" button is unavailable to a normal user.

M. Environment notes

  • Fee cases need controlled PaymentTerms master data. Seed a supplier with terms and one without, and one carrying an $IsAP ownership code (M1, J1, J2, K1, CR, DM, N1, TN) to exercise E16.
  • Section E depends entirely on daysBetween(now, BookingLine/Day_First). Use fixtures with dates relative to run time; the boundary cases (E2/E3, E8/E9, E11/E12) rot within a day otherwise.
  • SE_WaitlistRelease no-ops at weekends (I1). Either run it on a weekday or control the clock.
  • Wish and Tourplan failure cases (C4, C5, H8) need stubbed server settings — see docs/02-architecture.md.
  • Reviving (section H) destroys Wish linkage and cannot be undone by re-cancelling. Use throwaway bookings.