Concepts¶
Booking models four content entities plus a tenant. Capacity lives on the Category, never on the Slot - this is what lets a single time window carry several independently-counted pools (ticket tiers, room categories, price classes).
Entity model¶
erDiagram
TENANT ||--o{ RESOURCE : scopes
RESOURCE ||--o{ SLOT : "offers"
SLOT ||--|{ CATEGORY : "split into 1+"
CATEGORY ||--o{ BOOKING : "consumed by"
SLOT ||..o{ BOOKING : "denormalised"
RESOURCE ||..o{ BOOKING : "denormalised"
RESOURCE {
int id PK
string tenant
string label
bool status
int capacity "default; NULL = unlimited"
int hold_ttl "seconds"
int lead_time
string lead_time_unit "hours|days"
}
SLOT {
int id PK
string tenant
int resource FK
int start "timestamp"
int end "timestamp"
int capacity "optional overall cap; NULL = none"
bool status
bool published
string rule "recurrence origin, nullable"
}
CATEGORY {
int id PK
string tenant
int slot FK
string key "e.g. default, adult, vip"
string label
int capacity "NULL = unlimited"
int weight
}
BOOKING {
int id PK
string tenant
int category FK
int slot FK
int resource FK
int quantity
string state "held|confirmed|released|expired|cancelled"
int hold_expires "timestamp, nullable"
string subject "consumer correlation handle"
}
Resource¶
A bookable thing - a room, a piece of equipment, an event. Holds the defaults its slots inherit and the booking window rule:
capacity- the default capacity used to seed a slot's default category.hold_ttl- how long a hold lives before expiring (the payment window).lead_time/lead_time_unit- the minimum delay before a slot may be booked (enforced by thebooking_windowconstraint).
The core Resource holds only what the engine itself uses. Policy that lives
outside the engine - automatic vs manual confirmation, deposit amounts,
node binding - is added by consumer submodules as extra fields (entities are
fieldable), keeping the core lean. See
Deposits and payment.
Slot¶
A concrete time window of a resource (start/end). The old "date +
time-slot" two-level model collapses into this: a full-day booking is one
slot; hourly slots are several sibling slots.
capacityhere is an optional slot-wide overall cap across all its categories (e.g. "200 total, however the tiers split").NULLmeans the slot is governed solely by its categories.published/statusgate public visibility and bookability.rulerecords the recurrence rule that generated the slot, if any (see Recurrence).
Category¶
The "sub-slot" - a named portion of a slot's capacity. This is where availability is tracked and what the engine locks.
- Every slot has at least one category. Simple bookings use a single
implicit
defaultcategory and behave exactly as if capacity were on the slot. - Ticketing/tiered use cases create several (
adult,child,vip), each with its owncapacity. capacity = NULLmeans unlimited.- Price is not stored here - a pricing layer keys to the category; see Deposits and payment.
Booking¶
A consumer of capacity on a category. Carries a quantity (the number of
places) and a lifecycle state. held and confirmed consume capacity;
released, expired and cancelled do not. See
Capacity and holds.
The subject is a free-form string the consumer fills with whatever
correlation handle it needs (an order id, a request uuid, a user reference).
The core never interprets it, keeping the engine decoupled from who booked.
Order lifecycle¶
The yoyaku_order module decorates a booking transaction as an order, whose
state is derived from its lines: pending while any line is held, then
confirmed or partial, and once every line is settled cancelled,
completed or unused. Operators drive it from the orders overview, and
the lifecycle runs active → settled → archived → deleted:
- Active (pending/confirmed/partial): confirm or cancel it.
- Settled (cancelled/completed/unused): archive it to set it aside. Archived orders leave the active list; a Show archived filter reveals them, and Unarchive returns one to the list.
- Deletable only once the order is archived or cancelled (a void order needs no archive step first), and never while its Orchestra workflow is still running. Archiving is the deliberate "cleared for removal / retained" step, so completed and no-show history is never purged by a stray click.
Tenant¶
Every row is stamped with a tenant. Single-site installs transparently use
the shipped default tenant; multi-site / multi-customer deployments
partition data per tenant. See Multi-tenancy.