cargagep-v2/db/migrations/20260823153320_create_reservations.sql
2026-08-23 17:47:31 +02:00

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;