Booking — Common cases
Real-world situations and how the system responds.
Double-booking
- The same attendee cannot book the same TermProduct on overlapping date/time.
- Checked at create, submit, and confirm.
- Admin can bypass with
IgnoreDoubleBooking = true. - Records of type
Subscription/WaitingListare not counted as a normal booking conflict.
Real-world example: A parent books two children into the same Monday morning session. → Avoid accidentally recording the same child twice. → The system blocks the second child at submit unless admin enables
IgnoreDoubleBooking.
Height / weight (measurement)
- A course may configure height/weight bounds. An attendee out of bounds → invalid selection, blocks submit.
- A missing required value (e.g. weight) → submit fails for that selection.
- Admin overrides each rule: turning off Height still lets Weight block, and vice versa.
Real-world example: A swimming class sets a minimum weight threshold; the child has no weight recorded. → Ensure the child meets safety conditions before joining the class. → Submit fails for that selection until weight is entered, or admin turns off the Weight rule only.
Booking Cut-off (selecting past sessions)
- By default sessions past the cut-off cannot be selected.
- Admin turns off Booking Cut-off in Booking Rule Options → past sessions become selectable (all categories: Term Care, Holiday, Class).
Real-world example: A child attended Tuesday but registration was forgotten and needs to be back-booked. → Let ops record a session that already happened for billing/attendance. → Admin turns off Booking Cut-off, selects that past session again, then confirms.
Pay Later & auto-cancel timeout
- PayNow / Deposit / Direct Debit may arm a timeout: if unpaid past the deadline, auto-cancel.
- PayLater has no timeout but must pass eligibility validation at submit.
Real-world example: A parent chooses Deposit, then leaves without paying. → Avoid holding a slot indefinitely and blocking capacity. → When the timeout expires, the booking auto-cancels and the slot opens for someone else.
Capacity
- Limited by
TermProduct.CapacityandTermProgramSetCapacity(limited weeks). ValidateCapacityForConfirmBookingruns before confirm.
Real-world example: One place remains, and two parents click confirm almost at the same time. → Do not exceed class capacity. →
ValidateCapacityForConfirmBookingblocks the later confirm and reports no availability.
Add-on capacity (required add-on capacity)
- Add-ons (e.g. bus route) have their own capacity; when an order line fills up → confirm is blocked with
FULLY_ADDON_CAPACITY_BOOKING_FOUND, unless admin enablesIgnoreDoubleBooking. - Different from session capacity: this is the capacity of the add-on attached to the booking, not the session headcount.
Real-world example: A new booking includes a bus route that is already fully subscribed. → Avoid taking on more children than the bus can carry. → Confirm is blocked at the plan-build step; admin enables
IgnoreDoubleBookingto allow it deliberately.
Confirm only when the order is in an allowed state
- Only
TypeId ∈ {Booking, TrialSession}andStatusId ∈ {Submitted, Submitted_Change, Attendance_Approved, Quote}can be confirmed; other orders fail the confirm. Confirm_Booking→Approved,Confirm_Attendance→Attendance_Approved(skips Billing Difference),Confirm_Invoice→Quote.
Real-world example: Admin clicks Confirm on a
WaitingListorder or an alreadyCanceledbooking. → Avoid creating a runtime booking from an invalid state. → Confirm is blocked, the booking does not change state.
Quote → Accept
- Instead of confirming immediately, you can
GenerateQuotes(statusQuote) → customerAcceptQuote(Accept) → confirm (Approved).
Real-world example: Admin finalizes a holiday program for a family but lets them approve the price themselves. → Let the customer review and accept the quote before committing to payment. →
GenerateQuotessends the quote, the customer runsAcceptQuote, then the booking is confirmed.
Cancel → credit note
- Cancelling a confirmed booking: status →
Canceled, reverses invoices via credit note, deactivatesTermAttendance— even if some lines previously leftApproved.
Real-world example: A family cancels a course that was already paid and attended. → Keep the ledger correct and remove sessions that are no longer valid. → Booking becomes
Canceled, the system issues a credit note to reverse the invoice, andTermAttendanceis deactivated.
Stale attendance
- A dry-run process detects
TermAttendancestill active while its parentTermBookingisCanceled(returns a count + sample, without modifying data) so ops can repair safely.
Real-world example: An old issue left attendance behind for a canceled booking, making the attendance report wrong. → Detect bad data before deleting anything automatically. → Dry-run lists the count + sample records so ops can inspect and handle them manually.
Combined edit
- A category with
CombinedBookingOptionId = 1: editing one booking loads same-ActionLogIdbookings across courses in the category. Removing one from the group on reconfirm → cancel + deactivate its attendance. Details: Combine booking rule.
Real-world example: A parent changes one child's schedule that was booked as part of a combined multi-course group. → Keep the whole group in sync instead of drifting booking by booking. → Editing one booking loads bookings with the same
ActionLogId; removing one from the group cancels it and deactivates its attendance.
Confirm Attendance (Submit & Confirm Attendance)
- After submit → calls confirm with action type
Confirm_Attendanceimmediately → statusAttendance_Approved(not yet fully confirmed), does not open the Billing Difference preview. - Submit fails, or does not return the complete
TermBookingOrderIds→ reports an error, does not confirm.
Real-world example: Admin needs to lock in roll call for a booking that already attended but was not financially settled. → Avoid forcing a meaningless financial-delta review. →
Submit & Confirm Attendancesubmits then locksAttendance_Approvedimmediately, no preview.
Editing a confirmed booking (Billing Difference)
Submit & Confirmis split into two steps: submit (muted) → review Billing Difference → click Confirm.- Submit returns the complete set of
TermBookingOrderIdsto preview & confirm together (invariantINV-BOOK-21); preview/confirm uses exactly that id set, not re-read from the selection UI (invariantINV-BOOK-25). - Edit submit is muted server-side even when the client sends
muteNotification = false(invariantINV-BOOK-26); the final mute choice is collected at the confirm popup and sent with the confirm request. - UI order: Billing Difference preview → action confirmation popup → then submit/confirm. Closing the preview → booking is not confirmed.
Real-world example: Admin adds one session to an already confirmed booking, and the customer needs to know the extra amount. → Show the billing difference before committing. → Submit (muted) shows Billing Difference; admin reviews it, then clicks Confirm to finalize.
Keeping roll call on edit (roll call preserve)
- Edit confirm recreates
TermBookingLineand replacesTermAttendancebecause price/discount changed → keeps the roll call from the old attendance when it hasRollCallStatusId > 0— even if the old one was already deactivated in the flow. - Copies the full
RollCallHistory(action id, timestamps, signed-by, signature, absent reason, note); a confirm retry does not duplicate history. RollCallStatusId = 0(not yet signed) → normal new attendance, no copy. A removed/cancelled session → roll call is not copied elsewhere.
Real-world example: A booking already has Monday's roll call, then admin changes that session's price and confirm recreates the attendance. → Avoid losing the attendance/leaving traces. → The new attendance copies the roll call + history from the old one (invariant
INV-BOOK-27).
Submit & Confirm Attendance (admin)
- Admin Mode has a Submit & Confirm Attendance action: submit first, then call confirm with action type
Confirm_Attendanceusing theTermBookingOrderIdsthe submit returned. - The booking ends at
Attendance_Approved— roll call locked, not a full confirm. - Not gated by Billing Difference (invariant
INV-BOOK-26). - Submit ok but confirm-attendance fails → reports an error so admin can retry or finish in Booking Manager; the booking stays at
Submitted. Submit returns noTermBookingOrderIds→ reports an error, no confirm call.
Real-world example: A swimming class already ran but admin forgot to lock the first session and needs roll call right away. → No fee-delta review needed because this action changes no money. →
Submit & Confirm Attendancebrings the booking toAttendance_Approvedso roll-call staff can work, without a full confirm.
Pending notification suppression (Submit & Confirm)
POST /BookingOrdersin admin mode withStatusId= Confirmed (Submit & Confirm) → does not enqueue/send the pending-booking notification for that request.- A request submit that does not direct-confirm (booking stays pending) → the pending-booking notification runs as usual.
- Backend-only behavior: the
POST /BookingOrderscontract is not changed. - Mute Notifications (admin confirmation popup) → suppresses the action's eligible notification jobs:
- Submit → suppress submitted; Confirm → suppress confirmed.
- Submit & Confirm → submitted already suppressed (direct-confirm) + confirmed suppressed.
- Cancel → suppress cancelled.
- Mute not selected → existing notification behavior stays unchanged.
Real-world example: Admin bulk-finalizes with
Submit & Confirmbut does not want to spam parents. → Avoid unnecessary intermediate-state emails. → A direct-confirm request sends no pending notification; with Mute Notifications on, the confirmed job is not sent either.