56 lines
3.2 KiB
SQL
56 lines
3.2 KiB
SQL
-- 0006_ticket_events — журнал событий заявки.
|
||
--
|
||
-- Каждый переход статусной машины оставляет запись: кто сделал, что было,
|
||
-- что стало, причина (для rejected/canceled) и когда. Запись создаётся
|
||
-- **в той же SQL-операции**, что и сам переход (CTE: UPDATE … RETURNING
|
||
-- → INSERT в журнал), поэтому «заявка перешла, а истории нет» невозможно:
|
||
-- либо обе части, либо ни одной.
|
||
--
|
||
-- from_status NULL — только у события «создана»: заявка появилась
|
||
-- из ничего. to_status всегда заполнен. actor_id может стать NULL
|
||
-- при удалении аккаунта — история не пропадает, остаётся «кто-то».
|
||
--
|
||
-- Действия: created, assigned, started, done, confirmed, rejected, canceled.
|
||
-- Названия действий отделены от статусов: одинаковый to_status='closed'
|
||
-- дают и принятие клиента (confirmed), и отмена (canceled) — журналу
|
||
-- важно именно «что сделали», а не только «куда ушло».
|
||
--
|
||
-- Старые заявки (до этой миграции) журнала не имеют — история начинается
|
||
-- с момента наката.
|
||
--
|
||
-- Применённый файл не редактировать (checksum в schema_migrations).
|
||
|
||
CREATE TABLE ticket_events (
|
||
id bigserial PRIMARY KEY,
|
||
ticket_id bigint NOT NULL REFERENCES tickets(id) ON DELETE CASCADE,
|
||
-- NULL — аккаунт автора удалён (ON DELETE SET NULL)
|
||
actor_id uuid REFERENCES profiles(id) ON DELETE SET NULL,
|
||
action text NOT NULL,
|
||
from_status text,
|
||
to_status text NOT NULL,
|
||
-- причина отклонения/отмены, для остальных действий пусто
|
||
comment text,
|
||
created_at timestamptz NOT NULL DEFAULT now(),
|
||
|
||
CONSTRAINT ticket_events_action_check
|
||
CHECK (action IN ('created', 'assigned', 'started', 'done',
|
||
'confirmed', 'rejected', 'canceled')),
|
||
CONSTRAINT ticket_events_from_status_check
|
||
CHECK (from_status IS NULL OR from_status IN
|
||
('new', 'assigned', 'in_progress', 'done', 'confirmed',
|
||
'rejected', 'closed')),
|
||
CONSTRAINT ticket_events_to_status_check
|
||
CHECK (to_status IN
|
||
('new', 'assigned', 'in_progress', 'done', 'confirmed',
|
||
'rejected', 'closed')),
|
||
-- причина нужна ровно там, где её требует машина статусов
|
||
CONSTRAINT ticket_events_comment_shape
|
||
CHECK (action NOT IN ('rejected', 'canceled')
|
||
OR comment IS NOT NULL)
|
||
);
|
||
|
||
-- Журнал читается по заявке в хронологии: (ticket_id, id) покрывает
|
||
-- и выборку страницы, и сортировку по умолчанию.
|
||
CREATE INDEX IF NOT EXISTS idx_ticket_events_ticket
|
||
ON ticket_events (ticket_id, id);
|