Booking — Overview
Booking (here, normal booking — regular session booking) is the flow where customers / admins book individual sessions within a term: select sessions → submit request → confirm into an actual booking, with pricing, discounts, and combine-booking rules.
This is a different flow from Subscription (long-term recurring billing). Both share many tables (Billing, Invoice, TermAttendance) and intersect on the double-booking guard, but their lifecycles and entry points are separate.
Subscription Booking has an additional boundary: Enrichment is created as a TermBookingLine and processed by Booking Billing/Reconciliation, but it does not turn Subscription into a normal booking and does not enter the Subscription Billing schedule.
Quick mental model
Think of a centre that lets you book per session / per care week:
- Customer (or admin) opens Booking Flow, picks sessions for one or more attendees.
- The system creates a TermBookingOrder (in-flight request) — like a cart.
- Submit → creates
Billing, validates (double-booking, capacity, height/weight, pay-later). - Confirm (or Quote → Accept) → creates the actual TermBooking +
TermAttendance, triggers invoicing. - Along the way: apply automatic discount rules, optionally combine bookings by program category.
Normal booking vs Subscription booking
| Normal booking | Subscription booking | |
|---|---|---|
| Entry point | ConsumerBooking (Booking Flow) | ConsumerEnrollment (Enrollment) |
| Record | TermBookingOrder → TermBooking | Enrollment (+ internal booking) |
TypeId | Booking (0) | Subscription (4) |
| Payment | Chosen at checkout (PayNow/PayLater/Direct Debit) | Recurring per billing schedule |
| Lifecycle | Submit → Confirm (2 steps) | Driven by enrollment schedule |
| Pricing rules | Discount rule + combine booking rule | Per-period discounts |
⚠️ Order types
SubscriptionandWaitingListare not treated as a "normal booking conflict" by the double-booking guard — subscriptions are validated through the Enrollment path.
Key entities
| Name | One line |
|---|---|
| Term / ProgramCategory / Program | Setup objects that define the period, program type, and concrete program before booking. |
| TermProgramSet (TPS) | Offering/course within a term; holds schedule, price, capacity, and available discounts. |
| TermProduct / Session | Concrete session/slot selected by the user; booking lines record this level. |
| Holiday / Activity | Holiday is a special category/program type; Activity is a holiday day theme, not the main priced session. |
| TermBookingOrder | In-flight booking request, has a status lifecycle. |
| TermBooking | Confirmed booking (the actual runtime record). |
| TermBookingLine / Extra / Discount | A booking's session / add-on / discount rows. |
| Subscription Enrichment line | TermBookingLine with TermProductId + Date, authoritative price/item; one line per occurrence. |
| Discount (rule) | Automatic discount rule (sibling, early bird, attendee, profile…). |
| CombinedBookingOption | Config to combine same-ActionLogId bookings across courses in a category. |
| TermAttendance | Roll-call record created when a booking is confirmed. |
| BookingReconciliationRule | Penalty/refund rules when attendance differs from the booked schedule (see Booking Reconciliation). |
See the hierarchy and terminology details in Core concepts.
Suggested reading order
- End-user view — what customers and admins do.
- Core concepts — Order vs Booking, status, confirmation type.
- Architecture & data — tables and relationships.
- Codebase flow — controller, service, validation.
- Billing impact when editing a confirmed booking — conditions and how to review the difference.
- Discount rule — discount rules & how they apply.
- Combine booking rule — combining bookings on edit.
- Booking Reconciliation — reconcile roll call: penalties/refunds when off schedule.
- Data & external links — read/write tables, external concepts.
- Common cases — double-booking, cancel, quote…
Current implementation
- The Customized Bookings and Subscriptions Course List shows four cards per row at the
xxlbreakpoint; smaller breakpoints and content remain unchanged. - Clicking a Booking Calendar event opens a read-only popup with TPS, session, time and the full attendee list. The calendar library's default popups are disabled; Transportation/Makeup calendars keep their own behavior.
- Special-day programmes are not bookable. Session grouping/matching uses
sessionGroupKeyand the shared bucket without changing normal booking behavior.
Real-world example: A month tile truncates an event with four attendees. → Click the event. → The popup shows one attendee per line using the same time zone/format as the tile.