axe/internal/db/migrations/0014_recurring_rules.sql

82 lines
4.6 KiB
MySQL
Raw Permalink Normal View History

2026-10-18 19:07:25 +00:00
-- 0014_recurring_rules — повторяющиеся заявки по расписанию.
--
-- Зачем: периодические работы («осмотр вентиляции раз в 3 месяца»)
-- заводятся один раз правилом, а фоновый планировщик сам создаёт
-- заявку, когда истекает интервал. Правило — шаблон будущей заявки
-- (объект, категория, описание, приоритет) плюс интервал в месяцах.
--
-- Кто владеет: client_id — клиент-владелец (заявка создаётся от его
-- имени, как ручная); created_by — кто завёл правило (админ или сам
-- клиент). Правило живёт в проекте и объекте: каскады унесут его
-- вместе с ними (удаление проекта админом из 0013).
--
-- Повторный запуск: правило созрело, но прошлая заявка ещё не
-- закрыта — новую не создаём (ждём закрытия, без дублей). Состояние
-- хранится в last_run_at (когда последний раз срабатывали) и в
-- recurring_runs (история: какая заявка создана по какому правилу).
--
-- Применённый файл не редактировать (checksum в schema_migrations).
-- ============ recurring_rules ============
CREATE TABLE IF NOT EXISTS recurring_rules (
id bigserial PRIMARY KEY,
-- Владелец будущих заявок: client_id проекта продублирован, чтобы
-- заявка создавалась без JOIN и составной FK projects был соблюдён.
client_id uuid NOT NULL REFERENCES profiles (id),
project_id bigint NOT NULL REFERENCES projects (id) ON DELETE CASCADE,
object_id bigint NOT NULL REFERENCES project_objects (id) ON DELETE CASCADE,
-- Шаблон заявки (те же колонки, что в tickets).
category text NOT NULL,
description text NOT NULL,
priority text NOT NULL DEFAULT 'normal',
floor text,
-- Расписание: 3 — квартальный осмотр, 12 — годовой.
interval_months int NOT NULL,
is_active boolean NOT NULL DEFAULT true,
-- Когда последний раз срабатывали; NULL — ещё ни разу.
last_run_at timestamptz,
created_by uuid REFERENCES profiles (id),
created_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT recurring_rules_category_check
CHECK (category IN ('electric', 'plumbing', 'climate', 'elevator', 'other')),
CONSTRAINT recurring_rules_priority_check
CHECK (priority IN ('low', 'normal', 'high')),
CONSTRAINT recurring_rules_interval_check
CHECK (interval_months >= 1),
-- Правило обязано жить в объекте своего проекта (то же ядро, что
-- у заявки): составной FK держит объект и проект согласованными.
CONSTRAINT recurring_rules_object_project_fk
FOREIGN KEY (object_id, project_id)
REFERENCES project_objects (id, project_id),
CONSTRAINT recurring_rules_project_client_fk
FOREIGN KEY (project_id, client_id)
REFERENCES projects (id, client_id)
);
-- Лента правил: клиент — свои, админ — все проекты.
CREATE INDEX IF NOT EXISTS recurring_rules_client_idx ON recurring_rules (client_id);
CREATE INDEX IF NOT EXISTS recurring_rules_project_idx ON recurring_rules (project_id);
-- Отбор созревших правил поллером.
CREATE INDEX IF NOT EXISTS recurring_rules_due_idx
ON recurring_rules (last_run_at) WHERE is_active;
-- ============ recurring_runs ============
-- История срабатываний: аудит «кто и когда создал авто-заявку».
CREATE TABLE IF NOT EXISTS recurring_runs (
id bigserial PRIMARY KEY,
rule_id bigint NOT NULL REFERENCES recurring_rules (id) ON DELETE CASCADE,
-- NULL, если созданную заявку потом удалили каскадом (проект).
ticket_id bigint REFERENCES tickets (id) ON DELETE SET NULL,
run_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS recurring_runs_rule_idx ON recurring_runs (rule_id, run_at DESC);