73 lines
2.6 KiB
SQL
73 lines
2.6 KiB
SQL
-- 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;
|