Skip to content

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 the booking_window constraint).

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.

  • capacity here is an optional slot-wide overall cap across all its categories (e.g. "200 total, however the tiers split"). NULL means the slot is governed solely by its categories.
  • published / status gate public visibility and bookability.
  • rule records 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 default category and behave exactly as if capacity were on the slot.
  • Ticketing/tiered use cases create several (adult, child, vip), each with its own capacity.
  • capacity = NULL means 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.