BookableResource Model (PL-T185)
At a glance
A polymorphic BookableResource model that unifies courts, tables, packages, equipment and event spaces under one schema. Every sellable thing on the floor is one document type with consistent CRUD, per-resource booking-channel toggles, color-type defaults, lead-time and duration profiles, soft-delete for historical integrity, and floorplan coordinates baked into the resource itself.
How it works
BookableResource is a Beanie document scoped by tenant_id and location_id, with a compound unique index on (tenant_id, location_id, code) so a venue can't accidentally create two "Lane 3" resources. Each resource declares a type (court, table, package, equipment, event_space), a list of allowed booking channels (online, phone, operator, drop_in), a default color_type pulled from the booking-color token system (with operator override available), and a duration / lead-time profile that tells the booking engine the minimum and maximum slot length plus how far in advance customers can book. Floorplan position fields (x, y, rotation, width, height) live on the resource itself so the live floorplan view and the editor don't need a side table.
Soft-delete is encoded as retired status plus is_deleted flag — historical bookings still resolve their resource references without breakage. The CRUD API exposes GET list, POST create, PATCH update, and DELETE (which retires rather than removes). The admin UI presents a filterable resource list (by type and status) and a detail form where operators flip booking-channel toggles per resource — for example, allowing online booking for petanque lanes but restricting equipment hire to the operator console.
A migration script backfills existing Court documents into the new polymorphic shape so the cutover is non-breaking.
Key capabilities
- One model for courts, tables, packages, equipment, event spaces
- Per-resource booking-channel toggles (online / phone / operator / drop-in)
- Color-type defaults per booking type with operator override
- Floorplan position fields on the resource itself
- Soft-delete preserves historical booking integrity
- Compound unique index prevents duplicate codes per location
- Filterable admin list and detail form with channel toggles
In practice
A venue manager onboards a new summer terrace with twelve lanes, eight bistro tables, and three private cabanas. In the admin she opens Resources → Add, creates twelve court resources in bulk (codes L1-L12, online + operator channels, default color_type court_play), eight table resources (codes T1-T8, online + operator + drop_in), and three event_space cabanas (operator-only, requires manager approval per the channel filter). She drags each resource onto the floorplan editor to set x/y coordinates.
When the season ends, she retires the cabanas with one click — they disappear from new booking flows but historical reservations still render correctly in analytics.
Features in this subsystem
12| ID | Status | Features |
|---|---|---|
| F22.01.01 | Shipped | BookableResource Beanie document with tenant/location scoping ✅ |
| F22.01.02 | Shipped | Resource types: court, table, package, equipment, event_space ✅ |
| F22.01.03 | Shipped | Booking channels: online, phone, operator, drop_in ✅ |
| F22.01.04 | Shipped | Color-type system with per-type defaults and operator override ✅ |
| F22.01.05 | Shipped | Duration and lead-time configuration per resource ✅ |
| F22.01.06 | Shipped | Floorplan position storage (x/y/rotation/width/height) ✅ |
| F22.01.07 | Shipped | Compound index (tenant_id, location_id, code) — unique per location ✅ |
| F22.01.08 | Shipped | Soft-delete (retired status + is_deleted flag) ✅ |
| F22.01.09 | Shipped | CRUD API: GET list, POST create, PATCH update, DELETE retire ✅ |
| F22.01.10 | Shipped | Admin UI: resource list with type + status filter ✅ |
| F22.01.11 | Shipped | Admin UI: resource detail form with booking channel toggles ✅ |
| F22.01.12 | Shipped | Migration script: backfill existing Court documents ✅ |