-- migrate:up -- Lifecycle of a reservation: -- -- requested ──▶ refused -- │ -- ▼ -- approved ──▶ cancelled -- │ ▲ -- ▼ │ -- ongoing ────────┘ -- │ -- ▼ -- archived -- -- `refused`, `cancelled` and `archived` are final. The transitions are enforced -- in the controller (`ReservationStatus::can_transition_to`), not by a trigger, -- so the rule stays with the rest of the business logic. CREATE TYPE reservation_status AS ENUM ( 'requested', 'refused', 'approved', 'cancelled', 'ongoing', 'archived' ); CREATE TABLE reservations ( id SERIAL PRIMARY KEY, -- Exactly one unit borrows the bikes. Free text for the same reason as -- `units_users.unit_name`: the value comes from Whiskey. unit TEXT NOT NULL, start_time TIMESTAMPTZ NOT NULL, end_time TIMESTAMPTZ NOT NULL, -- Who filed the request; also part of `reservations_users` requester_id INTEGER NOT NULL REFERENCES users (id), -- Telegram handle to reach the group, stored with its leading '@' telegram TEXT NOT NULL, "description" TEXT NOT NULL DEFAULT '', status reservation_status NOT NULL DEFAULT 'requested', CONSTRAINT reservations_time_order CHECK (end_time > start_time), CONSTRAINT reservations_telegram_handle CHECK ( telegram ~ '^@[A-Za-z][A-Za-z0-9_]{4,31}$' ) ); CREATE INDEX reservations_unit_idx ON reservations (unit); CREATE INDEX reservations_status_idx ON reservations (status); CREATE INDEX reservations_period_idx ON reservations (start_time, end_time); -- People allowed to pick the bikes up for this reservation CREATE TABLE reservations_users ( reservation_id INTEGER NOT NULL REFERENCES reservations (id) ON DELETE CASCADE, user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE, PRIMARY KEY (reservation_id, user_id) ); -- Bikes booked by this reservation. A bike that has been booked cannot be -- deleted (ON DELETE RESTRICT): take it out of the fleet with -- `status = 'out_of_service'` instead. CREATE TABLE reservations_bikes ( reservation_id INTEGER NOT NULL REFERENCES reservations (id) ON DELETE CASCADE, bike_id INTEGER NOT NULL REFERENCES bikes (id) ON DELETE RESTRICT, PRIMARY KEY (reservation_id, bike_id) ); CREATE INDEX reservations_bikes_bike_id_idx ON reservations_bikes (bike_id); -- migrate:down DROP TABLE IF EXISTS reservations_bikes; DROP TABLE IF EXISTS reservations_users; DROP TABLE IF EXISTS reservations; DROP TYPE IF EXISTS reservation_status;