Initial commit MKD fixes
This commit is contained in:
66
backend/migrations/add_applications_manual_fields.sql
Executable file
66
backend/migrations/add_applications_manual_fields.sql
Executable file
@@ -0,0 +1,66 @@
|
||||
-- Миграция: source и поля для ручной карточки заявки (диспетчерская)
|
||||
-- Выполнить: psql -d mkd_control_center -f migrations/add_applications_manual_fields.sql
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
-- source: 'doma' | 'manual'
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'source') THEN
|
||||
ALTER TABLE applications ADD COLUMN source VARCHAR(20) NOT NULL DEFAULT 'doma';
|
||||
END IF;
|
||||
|
||||
-- Откуда поступила заявка (Звонок, Заявка с сайта и т.д.)
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'source_channel') THEN
|
||||
ALTER TABLE applications ADD COLUMN source_channel TEXT;
|
||||
END IF;
|
||||
|
||||
-- Заявка от жителя (true) / не от жителя (false)
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'is_from_resident') THEN
|
||||
ALTER TABLE applications ADD COLUMN is_from_resident BOOLEAN DEFAULT true;
|
||||
END IF;
|
||||
|
||||
-- Телефон, ФИО (для не от жителя)
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'contact_phone') THEN
|
||||
ALTER TABLE applications ADD COLUMN contact_phone TEXT;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'contact_name') THEN
|
||||
ALTER TABLE applications ADD COLUMN contact_name TEXT;
|
||||
END IF;
|
||||
|
||||
-- Классификатор: место инцидента, тип работ, в чём проблема
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'place_incident') THEN
|
||||
ALTER TABLE applications ADD COLUMN place_incident TEXT;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'work_type') THEN
|
||||
ALTER TABLE applications ADD COLUMN work_type TEXT;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'problem_detail') THEN
|
||||
ALTER TABLE applications ADD COLUMN problem_detail TEXT;
|
||||
END IF;
|
||||
|
||||
-- Чекбоксы: аварийная, платная, гарантийная
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'is_emergency') THEN
|
||||
ALTER TABLE applications ADD COLUMN is_emergency BOOLEAN DEFAULT false;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'is_paid') THEN
|
||||
ALTER TABLE applications ADD COLUMN is_paid BOOLEAN DEFAULT false;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'is_warranty') THEN
|
||||
ALTER TABLE applications ADD COLUMN is_warranty BOOLEAN DEFAULT false;
|
||||
END IF;
|
||||
|
||||
-- Назначение: исполнитель, ответственный, наблюдатели
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'executor_name') THEN
|
||||
ALTER TABLE applications ADD COLUMN executor_name TEXT;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'responsible_name') THEN
|
||||
ALTER TABLE applications ADD COLUMN responsible_name TEXT;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'observers_text') THEN
|
||||
ALTER TABLE applications ADD COLUMN observers_text TEXT;
|
||||
END IF;
|
||||
|
||||
-- Отображать заявку в мобильном приложении жителя
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'applications' AND column_name = 'show_in_app') THEN
|
||||
ALTER TABLE applications ADD COLUMN show_in_app BOOLEAN DEFAULT false;
|
||||
END IF;
|
||||
END $$;
|
||||
17
backend/migrations/add_closing_docs_files_to_payment_invoices.sql
Executable file
17
backend/migrations/add_closing_docs_files_to_payment_invoices.sql
Executable file
@@ -0,0 +1,17 @@
|
||||
-- Миграция для добавления массива файлов закрывающих документов в payment_invoices
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'payment_invoices'
|
||||
AND column_name = 'closing_docs_files'
|
||||
) THEN
|
||||
ALTER TABLE payment_invoices
|
||||
ADD COLUMN closing_docs_files JSONB DEFAULT '[]';
|
||||
|
||||
COMMENT ON COLUMN payment_invoices.closing_docs_files IS 'Список файлов закрывающих документов: [{filename, url, size, mimetype, uploadedAt, storedFilename}]';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
11
backend/migrations/add_event_to_payment_invoices.sql
Executable file
11
backend/migrations/add_event_to_payment_invoices.sql
Executable file
@@ -0,0 +1,11 @@
|
||||
-- Добавить назначение "мероприятие" и привязку к pr_events
|
||||
-- 1. Расширить CHECK purpose_type: добавить 'event'
|
||||
ALTER TABLE payment_invoices DROP CONSTRAINT IF EXISTS payment_invoices_purpose_type_check;
|
||||
ALTER TABLE payment_invoices ADD CONSTRAINT payment_invoices_purpose_type_check
|
||||
CHECK (purpose_type IN ('building', 'district', 'legal', 'office', 'hr', 'other', 'event'));
|
||||
|
||||
-- 2. Колонка привязки счёта к мероприятию
|
||||
ALTER TABLE payment_invoices ADD COLUMN IF NOT EXISTS purpose_event_id BIGINT REFERENCES pr_events(id) ON DELETE SET NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_invoices_purpose_event ON payment_invoices(purpose_event_id) WHERE purpose_event_id IS NOT NULL;
|
||||
|
||||
COMMENT ON COLUMN payment_invoices.purpose_event_id IS 'ID мероприятия (pr_events), если purpose_type = event';
|
||||
3
backend/migrations/add_expected_return_date_repair_requests.sql
Executable file
3
backend/migrations/add_expected_return_date_repair_requests.sql
Executable file
@@ -0,0 +1,3 @@
|
||||
-- Ожидаемая дата возврата из ремонта
|
||||
ALTER TABLE office_repair_requests
|
||||
ADD COLUMN IF NOT EXISTS expected_return_date DATE;
|
||||
4
backend/migrations/add_image_to_scheduled_posts.sql
Executable file
4
backend/migrations/add_image_to_scheduled_posts.sql
Executable file
@@ -0,0 +1,4 @@
|
||||
-- Добавить поле для изображения в pr_scheduled_posts
|
||||
ALTER TABLE pr_scheduled_posts ADD COLUMN IF NOT EXISTS image_url TEXT;
|
||||
|
||||
COMMENT ON COLUMN pr_scheduled_posts.image_url IS 'URL изображения для поста';
|
||||
8
backend/migrations/add_inventory_to_districts.sql
Executable file
8
backend/migrations/add_inventory_to_districts.sql
Executable file
@@ -0,0 +1,8 @@
|
||||
-- Миграция: добавление поля inventory в таблицу districts
|
||||
-- Склад участка хранится в JSONB поле для гибкости структуры
|
||||
|
||||
ALTER TABLE districts
|
||||
ADD COLUMN IF NOT EXISTS inventory JSONB DEFAULT '[]'::jsonb;
|
||||
|
||||
-- Создаем индекс для быстрого поиска по inventory (опционально, если нужно)
|
||||
-- CREATE INDEX IF NOT EXISTS idx_districts_inventory ON districts USING GIN (inventory);
|
||||
62
backend/migrations/add_item_type_to_payment_invoices.sql
Executable file
62
backend/migrations/add_item_type_to_payment_invoices.sql
Executable file
@@ -0,0 +1,62 @@
|
||||
-- Миграция для добавления полей item_type, service_items, material_items в таблицу payment_invoices
|
||||
-- (если таблица уже существует без этих полей)
|
||||
|
||||
-- Проверяем и добавляем item_type
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'payment_invoices'
|
||||
AND column_name = 'item_type'
|
||||
) THEN
|
||||
ALTER TABLE payment_invoices
|
||||
ADD COLUMN item_type VARCHAR(20) NOT NULL DEFAULT 'service'
|
||||
CHECK (item_type IN ('service', 'materials'));
|
||||
|
||||
COMMENT ON COLUMN payment_invoices.item_type IS 'Тип предмета счета: service (услуга) или materials (ТМЦ)';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Проверяем и добавляем service_items
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'payment_invoices'
|
||||
AND column_name = 'service_items'
|
||||
) THEN
|
||||
ALTER TABLE payment_invoices
|
||||
ADD COLUMN service_items JSONB DEFAULT '[]';
|
||||
|
||||
COMMENT ON COLUMN payment_invoices.service_items IS 'Список услуг: [{name, amount}]';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Проверяем и добавляем material_items
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'payment_invoices'
|
||||
AND column_name = 'material_items'
|
||||
) THEN
|
||||
ALTER TABLE payment_invoices
|
||||
ADD COLUMN material_items JSONB DEFAULT '[]';
|
||||
|
||||
COMMENT ON COLUMN payment_invoices.material_items IS 'Список ТМЦ: [{name, quantity, unit, pricePerUnit, amount}]';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Делаем service_description необязательным (для обратной совместимости)
|
||||
DO $$
|
||||
BEGIN
|
||||
ALTER TABLE payment_invoices
|
||||
ALTER COLUMN service_description DROP NOT NULL;
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
-- Игнорируем ошибку, если колонка уже не NOT NULL
|
||||
NULL;
|
||||
END $$;
|
||||
7
backend/migrations/add_outage_fields.sql
Executable file
7
backend/migrations/add_outage_fields.sql
Executable file
@@ -0,0 +1,7 @@
|
||||
-- Дополнительные поля журнала отключений (категория, тип работ, что сказать жителю и т.д.)
|
||||
ALTER TABLE outages ADD COLUMN IF NOT EXISTS author_name VARCHAR(255);
|
||||
ALTER TABLE outages ADD COLUMN IF NOT EXISTS category VARCHAR(100);
|
||||
ALTER TABLE outages ADD COLUMN IF NOT EXISTS problem_detail VARCHAR(255);
|
||||
ALTER TABLE outages ADD COLUMN IF NOT EXISTS work_type VARCHAR(50);
|
||||
ALTER TABLE outages ADD COLUMN IF NOT EXISTS resident_message TEXT;
|
||||
ALTER TABLE outages ADD COLUMN IF NOT EXISTS generate_news BOOLEAN NOT NULL DEFAULT false;
|
||||
9
backend/migrations/add_payment_ref_and_is_cash_to_payment_invoices.sql
Executable file
9
backend/migrations/add_payment_ref_and_is_cash_to_payment_invoices.sql
Executable file
@@ -0,0 +1,9 @@
|
||||
-- Номер платежки и признак оплаты наличными
|
||||
ALTER TABLE payment_invoices ADD COLUMN IF NOT EXISTS payment_ref TEXT;
|
||||
ALTER TABLE payment_invoices ADD COLUMN IF NOT EXISTS is_cash BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
ALTER TABLE payment_invoices ADD COLUMN IF NOT EXISTS postponed_date DATE;
|
||||
ALTER TABLE payment_invoices ADD COLUMN IF NOT EXISTS cancel_reason TEXT;
|
||||
COMMENT ON COLUMN payment_invoices.payment_ref IS 'Номер платежного поручения / платежки';
|
||||
COMMENT ON COLUMN payment_invoices.is_cash IS 'Оплата наличными';
|
||||
COMMENT ON COLUMN payment_invoices.postponed_date IS 'Дата переноса (при отложении)';
|
||||
COMMENT ON COLUMN payment_invoices.cancel_reason IS 'Причина отмены';
|
||||
6
backend/migrations/add_plan_item_to_payment_invoices.sql
Executable file
6
backend/migrations/add_plan_item_to_payment_invoices.sql
Executable file
@@ -0,0 +1,6 @@
|
||||
-- Связь счета с пунктом плана работ (план/факт)
|
||||
ALTER TABLE payment_invoices ADD COLUMN IF NOT EXISTS plan_item_id TEXT;
|
||||
ALTER TABLE payment_invoices ADD COLUMN IF NOT EXISTS plan_item_building_id TEXT;
|
||||
COMMENT ON COLUMN payment_invoices.plan_item_id IS 'ID пункта плана работ (building.annualPlan[].id)';
|
||||
COMMENT ON COLUMN payment_invoices.plan_item_building_id IS 'ID дома плана (building.id)';
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_invoices_plan_item ON payment_invoices(plan_item_building_id, plan_item_id) WHERE plan_item_id IS NOT NULL;
|
||||
8
backend/migrations/add_pr_events_location_place.sql
Executable file
8
backend/migrations/add_pr_events_location_place.sql
Executable file
@@ -0,0 +1,8 @@
|
||||
-- Место проведения для мероприятий "для жителей": участок или дома отдельно
|
||||
ALTER TABLE pr_events ADD COLUMN IF NOT EXISTS location_place_type VARCHAR(20) CHECK (location_place_type IN ('district', 'buildings'));
|
||||
ALTER TABLE pr_events ADD COLUMN IF NOT EXISTS location_district_id VARCHAR(50) REFERENCES districts(id) ON DELETE SET NULL;
|
||||
ALTER TABLE pr_events ADD COLUMN IF NOT EXISTS location_building_ids JSONB DEFAULT '[]';
|
||||
|
||||
COMMENT ON COLUMN pr_events.location_place_type IS 'Для жителей: district = участок, buildings = дома отдельно';
|
||||
COMMENT ON COLUMN pr_events.location_district_id IS 'ID участка, если location_place_type = district';
|
||||
COMMENT ON COLUMN pr_events.location_building_ids IS 'Массив ID домов, если location_place_type = buildings';
|
||||
5
backend/migrations/add_repair_request_statuses.sql
Executable file
5
backend/migrations/add_repair_request_statuses.sql
Executable file
@@ -0,0 +1,5 @@
|
||||
-- Новые статусы заявок на ремонт техники
|
||||
ALTER TYPE repair_request_status ADD VALUE IF NOT EXISTS 'search_contractor';
|
||||
ALTER TYPE repair_request_status ADD VALUE IF NOT EXISTS 'waiting_delivery';
|
||||
ALTER TYPE repair_request_status ADD VALUE IF NOT EXISTS 'taken_for_repair';
|
||||
ALTER TYPE repair_request_status ADD VALUE IF NOT EXISTS 'self_repair';
|
||||
9
backend/migrations/add_repair_status_fields.sql
Executable file
9
backend/migrations/add_repair_status_fields.sql
Executable file
@@ -0,0 +1,9 @@
|
||||
-- Новый статус: Договорились с подрядчиком
|
||||
ALTER TYPE repair_request_status ADD VALUE IF NOT EXISTS 'agreed_with_contractor';
|
||||
|
||||
-- Поля для статусов: ожидание поставки, увезли на ремонт, договорились
|
||||
ALTER TABLE office_repair_requests ADD COLUMN IF NOT EXISTS waiting_delivery_deadline TEXT;
|
||||
ALTER TABLE office_repair_requests ADD COLUMN IF NOT EXISTS waiting_delivery_contacts TEXT;
|
||||
ALTER TABLE office_repair_requests ADD COLUMN IF NOT EXISTS taken_for_repair_deadline TEXT;
|
||||
ALTER TABLE office_repair_requests ADD COLUMN IF NOT EXISTS taken_for_repair_contacts TEXT;
|
||||
ALTER TABLE office_repair_requests ADD COLUMN IF NOT EXISTS agreed_contractor_price NUMERIC(10, 2);
|
||||
12
backend/migrations/add_report_type.sql
Executable file
12
backend/migrations/add_report_type.sql
Executable file
@@ -0,0 +1,12 @@
|
||||
-- Миграция: добавление поля report_type в таблицу financial_reports
|
||||
-- Типы отчетов: debtors (должники), balance_sheet (оборотная сальдовая ведомость), other (другие)
|
||||
|
||||
ALTER TABLE financial_reports
|
||||
ADD COLUMN IF NOT EXISTS report_type VARCHAR(50) DEFAULT 'other'
|
||||
CHECK (report_type IN ('debtors', 'balance_sheet', 'other'));
|
||||
|
||||
-- Создаем индекс для быстрого поиска по типу отчета
|
||||
CREATE INDEX IF NOT EXISTS idx_financial_reports_type ON financial_reports(report_type);
|
||||
|
||||
-- Комментарий к колонке
|
||||
COMMENT ON COLUMN financial_reports.report_type IS 'Тип финансового отчета: debtors (должники), balance_sheet (оборотная сальдовая ведомость), other (другие)';
|
||||
7
backend/migrations/add_report_type_balance_sheet_76.sql
Executable file
7
backend/migrations/add_report_type_balance_sheet_76.sql
Executable file
@@ -0,0 +1,7 @@
|
||||
-- Расширение report_type: добавление balance_sheet_76 (ОСВ по счёту 76.06 — лицевые счета)
|
||||
-- Имя ограничения в PostgreSQL при ADD COLUMN CHECK часто: financial_reports_report_type_check
|
||||
ALTER TABLE financial_reports DROP CONSTRAINT IF EXISTS financial_reports_report_type_check;
|
||||
ALTER TABLE financial_reports ADD CONSTRAINT financial_reports_report_type_check
|
||||
CHECK (report_type IN ('debtors', 'balance_sheet', 'balance_sheet_76', 'other'));
|
||||
|
||||
COMMENT ON COLUMN financial_reports.report_type IS 'Тип отчета: debtors, balance_sheet (ОСВ 20), balance_sheet_76 (ОСВ 76 — лицевые счета), other';
|
||||
10
backend/migrations/add_scheduled_date_to_post_topics.sql
Executable file
10
backend/migrations/add_scheduled_date_to_post_topics.sql
Executable file
@@ -0,0 +1,10 @@
|
||||
-- Добавить scheduled_date в pr_post_topics (дата планируемой публикации)
|
||||
ALTER TABLE pr_post_topics ADD COLUMN IF NOT EXISTS scheduled_date DATE;
|
||||
|
||||
-- Если scheduled_date пустое, заполнить из month (первый день месяца)
|
||||
UPDATE pr_post_topics SET scheduled_date = (month || '-01')::date WHERE scheduled_date IS NULL;
|
||||
|
||||
-- Создать индекс
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_post_topics_scheduled_date ON pr_post_topics(scheduled_date);
|
||||
|
||||
COMMENT ON COLUMN pr_post_topics.scheduled_date IS 'Дата планируемой публикации поста по этой теме';
|
||||
5
backend/migrations/add_task_id_to_work_photos.sql
Executable file
5
backend/migrations/add_task_id_to_work_photos.sql
Executable file
@@ -0,0 +1,5 @@
|
||||
-- Привязка фото отчёта (до/после) к задаче
|
||||
ALTER TABLE work_photos
|
||||
ADD COLUMN IF NOT EXISTS task_id VARCHAR(50);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_work_photos_task ON work_photos(task_id);
|
||||
30
backend/migrations/add_user_profile_fields.sql
Executable file
30
backend/migrations/add_user_profile_fields.sql
Executable file
@@ -0,0 +1,30 @@
|
||||
-- Миграция: расширение профиля пользователя (план User Profile System)
|
||||
-- Добавляет поля в portal_users и создает таблицу user_preferences
|
||||
|
||||
-- 1. Добавление полей в portal_users
|
||||
ALTER TABLE portal_users ADD COLUMN IF NOT EXISTS phone VARCHAR(20);
|
||||
ALTER TABLE portal_users ADD COLUMN IF NOT EXISTS given_name TEXT;
|
||||
ALTER TABLE portal_users ADD COLUMN IF NOT EXISTS family_name TEXT;
|
||||
ALTER TABLE portal_users ADD COLUMN IF NOT EXISTS birth_date DATE;
|
||||
ALTER TABLE portal_users ADD COLUMN IF NOT EXISTS language VARCHAR(10) DEFAULT 'ru';
|
||||
ALTER TABLE portal_users ADD COLUMN IF NOT EXISTS theme VARCHAR(20) DEFAULT 'light';
|
||||
ALTER TABLE portal_users ADD COLUMN IF NOT EXISTS notification_email BOOLEAN DEFAULT true;
|
||||
ALTER TABLE portal_users ADD COLUMN IF NOT EXISTS notification_push BOOLEAN DEFAULT true;
|
||||
ALTER TABLE portal_users ADD COLUMN IF NOT EXISTS email_verified BOOLEAN DEFAULT false;
|
||||
ALTER TABLE portal_users ADD COLUMN IF NOT EXISTS last_login TIMESTAMPTZ;
|
||||
ALTER TABLE portal_users ADD COLUMN IF NOT EXISTS password_changed_at TIMESTAMPTZ;
|
||||
|
||||
-- 2. Создание таблицы user_preferences для расширяемых настроек
|
||||
CREATE TABLE IF NOT EXISTS user_preferences (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL REFERENCES portal_users(id) ON DELETE CASCADE,
|
||||
key TEXT NOT NULL,
|
||||
value JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(user_id, key)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_user_preferences_user_id ON user_preferences(user_id);
|
||||
|
||||
COMMENT ON TABLE user_preferences IS 'Расширяемые настройки и предпочтения пользователя';
|
||||
25
backend/migrations/create_application_comments_history.sql
Executable file
25
backend/migrations/create_application_comments_history.sql
Executable file
@@ -0,0 +1,25 @@
|
||||
-- Комментарии к заявкам (внутренние и с жителем)
|
||||
CREATE TABLE IF NOT EXISTS application_comments (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
application_id BIGINT NOT NULL REFERENCES applications(id) ON DELETE CASCADE,
|
||||
author_name TEXT NOT NULL,
|
||||
type VARCHAR(20) NOT NULL DEFAULT 'internal',
|
||||
text TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_application_comments_app ON application_comments(application_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_application_comments_type ON application_comments(type);
|
||||
|
||||
-- История изменений заявки
|
||||
CREATE TABLE IF NOT EXISTS application_history (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
application_id BIGINT NOT NULL REFERENCES applications(id) ON DELETE CASCADE,
|
||||
changed_by TEXT NOT NULL,
|
||||
changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
field_name TEXT NOT NULL,
|
||||
old_value TEXT,
|
||||
new_value TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_application_history_app ON application_history(application_id);
|
||||
21
backend/migrations/create_building_personal_accounts.sql
Executable file
21
backend/migrations/create_building_personal_accounts.sql
Executable file
@@ -0,0 +1,21 @@
|
||||
-- Лицевые счета домов: отдельная таблица, чтобы не терять их при обновлении buildings.data
|
||||
CREATE TABLE IF NOT EXISTS building_personal_accounts (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
building_id VARCHAR(255) NOT NULL REFERENCES buildings(id) ON DELETE CASCADE,
|
||||
data JSONB NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_building_personal_accounts_building_id ON building_personal_accounts(building_id);
|
||||
COMMENT ON TABLE building_personal_accounts IS 'Лицевые счета (собственники, прописанные, приборы учёта) — вынесены из buildings.data для защиты от перезаписи';
|
||||
|
||||
-- Перенос существующих счетов из buildings.data->accounts в новую таблицу (идемпотентно: ON CONFLICT DO NOTHING)
|
||||
INSERT INTO building_personal_accounts (id, building_id, data)
|
||||
SELECT
|
||||
COALESCE(acc->>'id', b.id || '-acc-migrated-' || t.ord),
|
||||
b.id,
|
||||
CASE WHEN (acc ? 'id') AND (acc->>'id' IS NOT NULL) AND (acc->>'id' <> '') THEN acc ELSE acc || jsonb_build_object('id', COALESCE(acc->>'id', b.id || '-acc-migrated-' || t.ord)) END
|
||||
FROM buildings b,
|
||||
LATERAL jsonb_array_elements(
|
||||
CASE WHEN jsonb_typeof(COALESCE(b.data->'accounts', '[]'::jsonb)) = 'array' THEN COALESCE(b.data->'accounts', '[]'::jsonb) ELSE '[]'::jsonb END
|
||||
) WITH ORDINALITY AS t(acc, ord)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
19
backend/migrations/create_company_news.sql
Executable file
19
backend/migrations/create_company_news.sql
Executable file
@@ -0,0 +1,19 @@
|
||||
-- Новости компании: блок на сводке и реестр в офисе
|
||||
CREATE TABLE IF NOT EXISTS company_news (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
body TEXT,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'pending', 'published')),
|
||||
created_by BIGINT REFERENCES portal_users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
published_at TIMESTAMPTZ,
|
||||
notify_departments JSONB DEFAULT '[]',
|
||||
notify_employee_ids JSONB DEFAULT '[]'
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_company_news_status ON company_news(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_company_news_created_at ON company_news(created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_company_news_published_at ON company_news(published_at DESC NULLS LAST) WHERE status = 'published';
|
||||
|
||||
COMMENT ON TABLE company_news IS 'Новости компании: черновики и публикации с уведомлением по отделам/сотрудникам';
|
||||
18
backend/migrations/create_debtor_report_data.sql
Executable file
18
backend/migrations/create_debtor_report_data.sql
Executable file
@@ -0,0 +1,18 @@
|
||||
-- Таблица строк отчёта по задолженности (один отчёт — много строк по лицевым счетам)
|
||||
CREATE TABLE IF NOT EXISTS debtor_report_data (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
report_id BIGINT NOT NULL REFERENCES financial_reports(id) ON DELETE CASCADE,
|
||||
row_index INTEGER NOT NULL,
|
||||
account TEXT NOT NULL,
|
||||
responsible_name TEXT,
|
||||
object_address TEXT,
|
||||
months_debt INTEGER,
|
||||
total_debt NUMERIC(14, 2) NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_debtor_report_data_report_id ON debtor_report_data(report_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_debtor_report_data_account ON debtor_report_data(account);
|
||||
CREATE INDEX IF NOT EXISTS idx_debtor_report_data_total_debt ON debtor_report_data(total_debt);
|
||||
|
||||
COMMENT ON TABLE debtor_report_data IS 'Строки отчёта по задолженности: лицевой счёт, ФИО, адрес, месяцы долга, сумма';
|
||||
18
backend/migrations/create_employee_districts.sql
Executable file
18
backend/migrations/create_employee_districts.sql
Executable file
@@ -0,0 +1,18 @@
|
||||
-- Связь сотрудник — участки (многие ко многим): один сотрудник может быть назначен на несколько участков
|
||||
CREATE TABLE IF NOT EXISTS employee_districts (
|
||||
employee_id VARCHAR(50) NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
|
||||
district_id VARCHAR(50) NOT NULL REFERENCES districts(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (employee_id, district_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_employee_districts_employee ON employee_districts(employee_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_employee_districts_district ON employee_districts(district_id);
|
||||
COMMENT ON TABLE employee_districts IS 'Назначения сотрудников на участки; один сотрудник может быть на нескольких участках';
|
||||
|
||||
-- Перенос существующих назначений из employees.assigned_district_id
|
||||
INSERT INTO employee_districts (employee_id, district_id)
|
||||
SELECT id, assigned_district_id
|
||||
FROM employees
|
||||
WHERE assigned_district_id IS NOT NULL AND assigned_district_id <> ''
|
||||
ON CONFLICT (employee_id, district_id) DO NOTHING;
|
||||
14
backend/migrations/create_employee_responsibility.sql
Executable file
14
backend/migrations/create_employee_responsibility.sql
Executable file
@@ -0,0 +1,14 @@
|
||||
-- Зоны ответственности: привязка сотрудников к подразделам модулей (уведомления, эффективность).
|
||||
-- Запуск: psql -U user -d your_db -f create_employee_responsibility.sql
|
||||
|
||||
CREATE TABLE IF NOT EXISTS employee_responsibility (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
employee_id VARCHAR(50) NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
|
||||
section VARCHAR(50) NOT NULL,
|
||||
sub_id VARCHAR(50) NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(employee_id, section, sub_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_employee_responsibility_employee ON employee_responsibility(employee_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_employee_responsibility_zone ON employee_responsibility(section, sub_id);
|
||||
42
backend/migrations/create_expense_directory.sql
Executable file
42
backend/migrations/create_expense_directory.sql
Executable file
@@ -0,0 +1,42 @@
|
||||
-- Справочник статей расходов
|
||||
-- Категории расходов
|
||||
CREATE TABLE IF NOT EXISTS expense_categories (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
code VARCHAR(50) UNIQUE, -- Код категории (опционально)
|
||||
description TEXT,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Статьи расходов внутри категорий
|
||||
CREATE TABLE IF NOT EXISTS expense_items (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
category_id BIGINT NOT NULL REFERENCES expense_categories(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
code VARCHAR(50), -- Код статьи (опционально)
|
||||
description TEXT,
|
||||
parent_item_id BIGINT REFERENCES expense_items(id) ON DELETE CASCADE, -- Для вложенных статей
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(category_id, name)
|
||||
);
|
||||
|
||||
-- Индексы
|
||||
CREATE INDEX IF NOT EXISTS idx_expense_items_category ON expense_items(category_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_expense_items_parent ON expense_items(parent_item_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_expense_items_active ON expense_items(is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_expense_categories_active ON expense_categories(is_active);
|
||||
|
||||
-- Связь данных отчета со справочником
|
||||
-- Добавляем поле для хранения ID статьи из справочника
|
||||
ALTER TABLE building_financial_data
|
||||
ADD COLUMN IF NOT EXISTS expense_item_mappings JSONB DEFAULT '{}';
|
||||
|
||||
COMMENT ON TABLE expense_categories IS 'Справочник категорий расходов';
|
||||
COMMENT ON TABLE expense_items IS 'Справочник статей расходов внутри категорий';
|
||||
COMMENT ON COLUMN building_financial_data.expense_item_mappings IS 'Маппинг расходов на статьи из справочника: { "item_id": amount, ... }';
|
||||
16
backend/migrations/create_finance_accounts.sql
Executable file
16
backend/migrations/create_finance_accounts.sql
Executable file
@@ -0,0 +1,16 @@
|
||||
-- Счета: банки и наличка (кошельки) для отображения остатков в календаре
|
||||
CREATE TABLE IF NOT EXISTS finance_accounts (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
type VARCHAR(20) NOT NULL CHECK (type IN ('bank', 'cash')),
|
||||
name TEXT NOT NULL,
|
||||
balance NUMERIC(15, 2) NOT NULL DEFAULT 0,
|
||||
currency VARCHAR(10) NOT NULL DEFAULT 'RUB',
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_finance_accounts_type ON finance_accounts(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_finance_accounts_active ON finance_accounts(is_active);
|
||||
COMMENT ON TABLE finance_accounts IS 'Банковские счета и кошельки (наличка) для отображения остатков в календаре оплат';
|
||||
21
backend/migrations/create_office_equipment_history.sql
Executable file
21
backend/migrations/create_office_equipment_history.sql
Executable file
@@ -0,0 +1,21 @@
|
||||
-- История перемещений и событий по оборудованию (закупка, выдача, перемещения, ремонты, списание)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'office_equipment_history_type') THEN
|
||||
CREATE TYPE office_equipment_history_type AS ENUM ('purchase', 'issue', 'transfer', 'repair', 'write_off');
|
||||
END IF;
|
||||
END$$;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS office_equipment_history (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
equipment_id BIGINT NOT NULL REFERENCES office_equipment(id) ON DELETE CASCADE,
|
||||
event_type office_equipment_history_type NOT NULL,
|
||||
event_date DATE NOT NULL,
|
||||
assigned_to TEXT,
|
||||
assigned_from TEXT,
|
||||
reason TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_equipment_history_equipment ON office_equipment_history(equipment_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_equipment_history_date ON office_equipment_history(event_date DESC);
|
||||
16
backend/migrations/create_outages.sql
Executable file
16
backend/migrations/create_outages.sql
Executable file
@@ -0,0 +1,16 @@
|
||||
-- Журнал отключений (вода, свет и т.д.) по домам
|
||||
CREATE TABLE IF NOT EXISTS outages (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
building_id VARCHAR(50) NOT NULL REFERENCES buildings(id) ON DELETE CASCADE,
|
||||
start_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
end_at TIMESTAMPTZ,
|
||||
type VARCHAR(50),
|
||||
description TEXT,
|
||||
active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_outages_building ON outages(building_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_outages_active ON outages(active);
|
||||
CREATE INDEX IF NOT EXISTS idx_outages_start_at ON outages(start_at);
|
||||
28
backend/migrations/create_payment_calendar_entries.sql
Executable file
28
backend/migrations/create_payment_calendar_entries.sql
Executable file
@@ -0,0 +1,28 @@
|
||||
-- Записи платежного календаря (расходы и поступления, по счету / без счета / наличка)
|
||||
CREATE TABLE IF NOT EXISTS payment_calendar_entries (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
direction VARCHAR(20) NOT NULL CHECK (direction IN ('outgoing', 'incoming')),
|
||||
type VARCHAR(20) NOT NULL CHECK (type IN ('invoice', 'manual', 'cash')),
|
||||
payment_invoice_id BIGINT REFERENCES payment_invoices(id) ON DELETE SET NULL,
|
||||
category TEXT NOT NULL DEFAULT '',
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
amount NUMERIC(15, 2) NOT NULL,
|
||||
scheduled_date DATE NOT NULL,
|
||||
payment_date DATE,
|
||||
probability VARCHAR(20) NOT NULL DEFAULT 'confirmed' CHECK (probability IN ('confirmed', 'high', 'medium', 'low')),
|
||||
currency VARCHAR(10) NOT NULL DEFAULT 'RUB',
|
||||
is_cash BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
contractor_name TEXT NOT NULL DEFAULT '',
|
||||
notes TEXT,
|
||||
created_by TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_calendar_entries_direction ON payment_calendar_entries(direction);
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_calendar_entries_type ON payment_calendar_entries(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_calendar_entries_scheduled_date ON payment_calendar_entries(scheduled_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_calendar_entries_payment_date ON payment_calendar_entries(payment_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_calendar_entries_payment_invoice_id ON payment_calendar_entries(payment_invoice_id);
|
||||
|
||||
COMMENT ON TABLE payment_calendar_entries IS 'Записи платежного календаря: расходы и поступления (по счету, без счета, наличка)';
|
||||
18
backend/migrations/create_payment_categories.sql
Executable file
18
backend/migrations/create_payment_categories.sql
Executable file
@@ -0,0 +1,18 @@
|
||||
-- Справочник статей доходов и расходов для платежного календаря
|
||||
CREATE TABLE IF NOT EXISTS payment_categories (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
code VARCHAR(50),
|
||||
direction VARCHAR(20) NOT NULL CHECK (direction IN ('income', 'expense')),
|
||||
parent_id BIGINT REFERENCES payment_categories(id) ON DELETE SET NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_categories_direction ON payment_categories(direction);
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_categories_parent ON payment_categories(parent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_categories_active ON payment_categories(is_active);
|
||||
|
||||
COMMENT ON TABLE payment_categories IS 'Справочник статей доходов и расходов для платежного календаря';
|
||||
82
backend/migrations/create_payment_invoices.sql
Executable file
82
backend/migrations/create_payment_invoices.sql
Executable file
@@ -0,0 +1,82 @@
|
||||
-- Миграция для создания таблицы счетов на оплату
|
||||
CREATE TABLE IF NOT EXISTS payment_invoices (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
invoice_number TEXT UNIQUE NOT NULL,
|
||||
created_by TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
-- Назначение счета
|
||||
purpose_type VARCHAR(50) NOT NULL CHECK (purpose_type IN ('building', 'district', 'legal', 'office', 'hr', 'other')),
|
||||
purpose_building_ids JSONB DEFAULT '[]', -- массив ID домов
|
||||
purpose_district_ids JSONB DEFAULT '[]', -- массив ID участков
|
||||
purpose_description TEXT, -- описание для 'other'
|
||||
|
||||
-- Формат оплаты
|
||||
payment_format VARCHAR(20) NOT NULL CHECK (payment_format IN ('prepayment', 'postpayment', 'advance')),
|
||||
|
||||
-- Тип предмета счета (услуга или ТМЦ)
|
||||
item_type VARCHAR(20) NOT NULL DEFAULT 'service' CHECK (item_type IN ('service', 'materials')),
|
||||
|
||||
-- Информация о подрядчике
|
||||
contractor_name TEXT NOT NULL,
|
||||
contractor_inn TEXT,
|
||||
|
||||
-- Описание услуги или ТМЦ (для обратной совместимости)
|
||||
service_description TEXT,
|
||||
-- Список услуг (если item_type = 'service')
|
||||
service_items JSONB DEFAULT '[]',
|
||||
-- Список ТМЦ (если item_type = 'materials')
|
||||
material_items JSONB DEFAULT '[]',
|
||||
total_amount NUMERIC(15, 2) NOT NULL,
|
||||
|
||||
-- Распределение суммы
|
||||
distribution_method VARCHAR(20) CHECK (distribution_method IN ('equal', 'by_area', 'manual')),
|
||||
distribution_data JSONB DEFAULT '{}', -- детали распределения по домам/участкам
|
||||
|
||||
-- Workflow статусы
|
||||
status VARCHAR(30) NOT NULL DEFAULT 'draft' CHECK (status IN (
|
||||
'draft',
|
||||
'pending_manager_approval',
|
||||
'pending_finance_manager_approval',
|
||||
'approved',
|
||||
'scheduled',
|
||||
'paid',
|
||||
'postponed',
|
||||
'cancelled',
|
||||
'rejected',
|
||||
'completed'
|
||||
)),
|
||||
current_approver_role VARCHAR(50), -- текущий этап согласования
|
||||
approval_history JSONB DEFAULT '[]', -- история согласований
|
||||
|
||||
-- Отклонение
|
||||
rejection_reason TEXT,
|
||||
|
||||
-- Даты
|
||||
scheduled_date DATE, -- дата в графике платежей
|
||||
payment_date DATE, -- фактическая дата оплаты
|
||||
|
||||
-- Статусы выполнения
|
||||
is_completed BOOLEAN DEFAULT FALSE, -- для постоплаты - выполнено ли
|
||||
closing_docs_received BOOLEAN DEFAULT FALSE, -- закрывающие документы получены
|
||||
|
||||
-- Дополнительно
|
||||
notes TEXT,
|
||||
file_urls JSONB DEFAULT '[]' -- массив путей к файлам счета
|
||||
);
|
||||
|
||||
-- Индексы для быстрого поиска
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_invoices_status ON payment_invoices(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_invoices_created_by ON payment_invoices(created_by);
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_invoices_purpose_type ON payment_invoices(purpose_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_invoices_payment_format ON payment_invoices(payment_format);
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_invoices_scheduled_date ON payment_invoices(scheduled_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_invoices_invoice_number ON payment_invoices(invoice_number);
|
||||
|
||||
-- Комментарии к таблице и колонкам
|
||||
COMMENT ON TABLE payment_invoices IS 'Счета на оплату с workflow согласования';
|
||||
COMMENT ON COLUMN payment_invoices.purpose_type IS 'Назначение: building, district, legal, office, hr, other';
|
||||
COMMENT ON COLUMN payment_invoices.payment_format IS 'Формат оплаты: prepayment, postpayment, advance';
|
||||
COMMENT ON COLUMN payment_invoices.distribution_method IS 'Метод распределения: equal, by_area, manual';
|
||||
COMMENT ON COLUMN payment_invoices.approval_history IS 'История согласований: [{role, userId, action, date, comment}]';
|
||||
12
backend/migrations/create_positions.sql
Executable file
12
backend/migrations/create_positions.sql
Executable file
@@ -0,0 +1,12 @@
|
||||
-- Справочник должностей (для панели управления и выбора при создании сотрудника)
|
||||
CREATE TABLE IF NOT EXISTS positions (
|
||||
id VARCHAR(50) PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
is_managerial BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_positions_sort ON positions(sort_order);
|
||||
COMMENT ON TABLE positions IS 'Справочник должностей; is_managerial — руководящая должность';
|
||||
19
backend/migrations/create_pr_attraction_actions.sql
Executable file
19
backend/migrations/create_pr_attraction_actions.sql
Executable file
@@ -0,0 +1,19 @@
|
||||
-- PR Привлечение: действия, которыми привлекали людей (рассылка, мероприятие, пост и т.д.)
|
||||
CREATE TABLE IF NOT EXISTS pr_attraction_actions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
channel_id BIGINT REFERENCES pr_smm_channels(id) ON DELETE SET NULL,
|
||||
action_type VARCHAR(20) NOT NULL CHECK (action_type IN ('mailing', 'event', 'post', 'other')),
|
||||
action_date DATE NOT NULL DEFAULT CURRENT_DATE,
|
||||
new_subscribers_attributed INT,
|
||||
event_id BIGINT REFERENCES pr_events(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_attraction_actions_channel ON pr_attraction_actions(channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_attraction_actions_type ON pr_attraction_actions(action_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_attraction_actions_date ON pr_attraction_actions(action_date DESC);
|
||||
|
||||
COMMENT ON TABLE pr_attraction_actions IS 'Действия привлечения: рассылка, мероприятие, пост и т.д., с приростом подписчиков';
|
||||
52
backend/migrations/create_pr_events.sql
Executable file
52
backend/migrations/create_pr_events.sql
Executable file
@@ -0,0 +1,52 @@
|
||||
-- PR Мероприятия: pr_events, pr_event_assignees, pr_event_photos
|
||||
-- Таблица мероприятий
|
||||
CREATE TABLE IF NOT EXISTS pr_events (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
type VARCHAR(20) NOT NULL CHECK (type IN ('resident', 'internal')),
|
||||
category VARCHAR(20) NOT NULL CHECK (category IN ('holiday', 'eco', 'sport', 'training', 'meeting')),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'planned' CHECK (status IN ('planned', 'in_progress', 'completed', 'canceled')),
|
||||
location TEXT,
|
||||
location_type VARCHAR(20) CHECK (location_type IN ('building', 'office')),
|
||||
location_building_id VARCHAR(50) REFERENCES buildings(id) ON DELETE SET NULL,
|
||||
attendees_count INTEGER NOT NULL DEFAULT 0,
|
||||
budget NUMERIC(15, 2),
|
||||
short_plan TEXT,
|
||||
announcement TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_events_date ON pr_events(date DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_events_status ON pr_events(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_events_type ON pr_events(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_events_location_building ON pr_events(location_building_id) WHERE location_building_id IS NOT NULL;
|
||||
|
||||
-- Сотрудники, назначенные на помощь по мероприятию
|
||||
CREATE TABLE IF NOT EXISTS pr_event_assignees (
|
||||
event_id BIGINT NOT NULL REFERENCES pr_events(id) ON DELETE CASCADE,
|
||||
employee_id VARCHAR(50) NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (event_id, employee_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_event_assignees_event ON pr_event_assignees(event_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_event_assignees_employee ON pr_event_assignees(employee_id);
|
||||
|
||||
-- Фотоотчёт по мероприятию (привязка к дому/офису для внутренних)
|
||||
CREATE TABLE IF NOT EXISTS pr_event_photos (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
event_id BIGINT NOT NULL REFERENCES pr_events(id) ON DELETE CASCADE,
|
||||
photo_url TEXT NOT NULL,
|
||||
caption TEXT,
|
||||
location_type VARCHAR(20) CHECK (location_type IN ('building', 'office')),
|
||||
location_building_id VARCHAR(50) REFERENCES buildings(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_event_photos_event ON pr_event_photos(event_id);
|
||||
|
||||
COMMENT ON TABLE pr_events IS 'Мероприятия PR: для жителей и внутренние';
|
||||
COMMENT ON TABLE pr_event_assignees IS 'Сотрудники для помощи по мероприятию';
|
||||
COMMENT ON TABLE pr_event_photos IS 'Фотоотчёт по мероприятию';
|
||||
21
backend/migrations/create_pr_post_topics.sql
Executable file
21
backend/migrations/create_pr_post_topics.sql
Executable file
@@ -0,0 +1,21 @@
|
||||
-- PR График публикации (календарь тем для постов)
|
||||
CREATE TABLE IF NOT EXISTS pr_post_topics (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
scheduled_date DATE, -- дата планируемой публикации (может быть NULL для старых записей)
|
||||
month VARCHAR(7) NOT NULL, -- формат YYYY-MM для фильтрации
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'pending_approval', 'approved', 'rejected')),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by TEXT,
|
||||
approved_at TIMESTAMPTZ,
|
||||
approved_by TEXT,
|
||||
rejection_reason TEXT,
|
||||
created_at_month VARCHAR(7) GENERATED ALWAYS AS (TO_CHAR(created_at, 'YYYY-MM')) STORED
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_post_topics_month ON pr_post_topics(month DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_post_topics_scheduled_date ON pr_post_topics(scheduled_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_post_topics_status ON pr_post_topics(status);
|
||||
|
||||
COMMENT ON TABLE pr_post_topics IS 'График публикации (календарь тем) - план публикаций на месяц. По этим темам создаются посты с контентом.';
|
||||
23
backend/migrations/create_pr_scheduled_posts.sql
Executable file
23
backend/migrations/create_pr_scheduled_posts.sql
Executable file
@@ -0,0 +1,23 @@
|
||||
-- PR Отложенные посты (для одобрения перед публикацией)
|
||||
CREATE TABLE IF NOT EXISTS pr_scheduled_posts (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
channel_ids JSONB DEFAULT '[]', -- массив ID каналов из pr_smm_channels
|
||||
scheduled_at TIMESTAMPTZ NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'pending_approval', 'approved', 'rejected', 'edited', 'published')),
|
||||
topic_id BIGINT REFERENCES pr_post_topics(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by TEXT,
|
||||
approved_at TIMESTAMPTZ,
|
||||
approved_by TEXT,
|
||||
rejection_reason TEXT,
|
||||
edited_content TEXT, -- если статус edited, здесь новая версия
|
||||
published_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_scheduled_posts_scheduled ON pr_scheduled_posts(scheduled_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_scheduled_posts_status ON pr_scheduled_posts(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_scheduled_posts_topic ON pr_scheduled_posts(topic_id);
|
||||
|
||||
COMMENT ON TABLE pr_scheduled_posts IS 'Отложенные посты для одобрения перед публикацией';
|
||||
28
backend/migrations/create_pr_smm_channels.sql
Executable file
28
backend/migrations/create_pr_smm_channels.sql
Executable file
@@ -0,0 +1,28 @@
|
||||
-- PR SMM: каналы и снимки подписчиков
|
||||
-- Каналы (Telegram, VK, WhatsApp и др.)
|
||||
CREATE TABLE IF NOT EXISTS pr_smm_channels (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
type VARCHAR(20) NOT NULL CHECK (type IN ('tg', 'vk', 'wa', 'other')),
|
||||
url TEXT,
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_smm_channels_sort ON pr_smm_channels(sort_order);
|
||||
|
||||
-- Снимки количества подписчиков по дате (история фиксаций)
|
||||
CREATE TABLE IF NOT EXISTS pr_smm_subscriber_snapshots (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
channel_id BIGINT NOT NULL REFERENCES pr_smm_channels(id) ON DELETE CASCADE,
|
||||
subscribers_count INT NOT NULL,
|
||||
recorded_at DATE NOT NULL DEFAULT CURRENT_DATE,
|
||||
note TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_smm_snapshots_channel ON pr_smm_subscriber_snapshots(channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_pr_smm_snapshots_recorded ON pr_smm_subscriber_snapshots(recorded_at DESC);
|
||||
|
||||
COMMENT ON TABLE pr_smm_channels IS 'SMM-каналы УК: Telegram, VK, WhatsApp и др.';
|
||||
COMMENT ON TABLE pr_smm_subscriber_snapshots IS 'Фиксации количества подписчиков по дате для истории';
|
||||
33
backend/migrations/create_report_76_tables.sql
Executable file
33
backend/migrations/create_report_76_tables.sql
Executable file
@@ -0,0 +1,33 @@
|
||||
-- Таблица строк ОСВ по счёту 76.06 (лицевые счета жителей)
|
||||
CREATE TABLE IF NOT EXISTS report_76_rows (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
report_id BIGINT NOT NULL REFERENCES financial_reports(id) ON DELETE CASCADE,
|
||||
row_index INTEGER NOT NULL,
|
||||
account_label TEXT NOT NULL,
|
||||
account_ls TEXT,
|
||||
saldo_start_debet NUMERIC(15, 2) DEFAULT 0,
|
||||
turnover_debet NUMERIC(15, 2) DEFAULT 0,
|
||||
turnover_credit NUMERIC(15, 2) DEFAULT 0,
|
||||
saldo_end_debet NUMERIC(15, 2) DEFAULT 0,
|
||||
saldo_end_credit NUMERIC(15, 2) DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_report_76_rows_report_id ON report_76_rows(report_id);
|
||||
COMMENT ON TABLE report_76_rows IS 'Строки ОСВ по счёту 76.06 — лицевые счета (жители) из загруженного отчёта';
|
||||
|
||||
-- Таблица сопоставления домов и лицевых счетов (для фильтра ОСВ 76 по дому и отображения в карточке дома)
|
||||
CREATE TABLE IF NOT EXISTS building_personal_account_mappings (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
building_id VARCHAR(50) NOT NULL REFERENCES buildings(id) ON DELETE CASCADE,
|
||||
account_ls TEXT NOT NULL,
|
||||
account_label TEXT,
|
||||
apartment TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(building_id, account_ls)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_building_personal_account_mappings_building ON building_personal_account_mappings(building_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_building_personal_account_mappings_account_ls ON building_personal_account_mappings(account_ls);
|
||||
COMMENT ON TABLE building_personal_account_mappings IS 'Сопоставление домов и лицевых счетов (для ОСВ 76): фильтр по дому, отображение в карточке дома';
|
||||
24
backend/migrations/create_user_roles.sql
Executable file
24
backend/migrations/create_user_roles.sql
Executable file
@@ -0,0 +1,24 @@
|
||||
-- Миграция для создания таблицы ролей пользователей
|
||||
CREATE TABLE IF NOT EXISTS user_roles (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
role VARCHAR(50) NOT NULL CHECK (role IN (
|
||||
'manager',
|
||||
'finance_manager',
|
||||
'financier',
|
||||
'finance_director',
|
||||
'director',
|
||||
'top_management'
|
||||
)),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(user_id, role)
|
||||
);
|
||||
|
||||
-- Индексы
|
||||
CREATE INDEX IF NOT EXISTS idx_user_roles_user_id ON user_roles(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_roles_role ON user_roles(role);
|
||||
|
||||
-- Комментарии
|
||||
COMMENT ON TABLE user_roles IS 'Роли пользователей для workflow согласования счетов';
|
||||
COMMENT ON COLUMN user_roles.role IS 'Роль: manager, finance_manager, financier, finance_director, director, top_management';
|
||||
27
backend/migrations/development_audits_status_and_inspection.sql
Executable file
27
backend/migrations/development_audits_status_and_inspection.sql
Executable file
@@ -0,0 +1,27 @@
|
||||
-- Аудиты: статусы, индекс сложности, данные осмотра по пунктам/подпунктам
|
||||
|
||||
-- Статус аудита: новый, в работе, завершён
|
||||
ALTER TABLE development_audits ADD COLUMN IF NOT EXISTS status VARCHAR(20) DEFAULT 'new';
|
||||
UPDATE development_audits SET status = 'completed' WHERE status IS NULL OR status = '';
|
||||
ALTER TABLE development_audits ALTER COLUMN status SET DEFAULT 'new';
|
||||
ALTER TABLE development_audits ALTER COLUMN status SET NOT NULL;
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'development_audits_status_check') THEN
|
||||
ALTER TABLE development_audits ADD CONSTRAINT development_audits_status_check
|
||||
CHECK (status IN ('new', 'in_progress', 'completed'));
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Индекс сложности дома (0–100)
|
||||
ALTER TABLE development_audits ADD COLUMN IF NOT EXISTS complexity_index NUMERIC(5, 2);
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'development_audits_complexity_index_check') THEN
|
||||
ALTER TABLE development_audits ADD CONSTRAINT development_audits_complexity_index_check
|
||||
CHECK (complexity_index IS NULL OR (complexity_index >= 0 AND complexity_index <= 100));
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Данные осмотра: пункты и подпункты (JSONB)
|
||||
ALTER TABLE development_audits ADD COLUMN IF NOT EXISTS inspection_data JSONB DEFAULT '{}';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dev_audits_status ON development_audits(status);
|
||||
7
backend/migrations/development_oss_agenda_and_votes.sql
Executable file
7
backend/migrations/development_oss_agenda_and_votes.sql
Executable file
@@ -0,0 +1,7 @@
|
||||
-- ОСС: повестка (пункты) и голоса по каждому пункту в реестре
|
||||
|
||||
-- Повестка собрания: массив формулировок пунктов (JSON array of strings)
|
||||
ALTER TABLE development_oss_sessions ADD COLUMN IF NOT EXISTS agenda_items JSONB NOT NULL DEFAULT '[]';
|
||||
|
||||
-- Голоса по пунктам в реестре: объект { "0": "for", "1": "against", "2": "abstain" } (индекс пункта -> for/against/abstain)
|
||||
ALTER TABLE development_oss_registry ADD COLUMN IF NOT EXISTS votes_by_item JSONB NOT NULL DEFAULT '{}';
|
||||
17
backend/migrations/development_pipeline_add_analysis.sql
Executable file
17
backend/migrations/development_pipeline_add_analysis.sql
Executable file
@@ -0,0 +1,17 @@
|
||||
-- Добавляем этап «Анализ» (analysis) в воронку развития (после Входящие)
|
||||
|
||||
ALTER TABLE development_pipeline DROP CONSTRAINT IF EXISTS development_pipeline_status_check;
|
||||
|
||||
ALTER TABLE development_pipeline ADD CONSTRAINT development_pipeline_status_check
|
||||
CHECK (status IN (
|
||||
'incoming',
|
||||
'analysis',
|
||||
'agenda_approval',
|
||||
'in_person',
|
||||
'absentee',
|
||||
'protocol_formation',
|
||||
'protocol_to_gzhi',
|
||||
'gzhi_order',
|
||||
'success',
|
||||
'failure'
|
||||
));
|
||||
31
backend/migrations/development_pipeline_new_stages.sql
Executable file
31
backend/migrations/development_pipeline_new_stages.sql
Executable file
@@ -0,0 +1,31 @@
|
||||
-- Воронка развития: новые этапы (9 пунктов)
|
||||
-- 1) Входящие 2) Согласование повестки 3) Очная часть 4) Заочная часть
|
||||
-- 5) Формирование протокола 6) Отправка протокола в ГЖИ 7) Приказ ГЖИ 8) Успех 9) Провал
|
||||
|
||||
-- Удаляем старый CHECK по status (автоимя в PG обычно development_pipeline_status_check)
|
||||
ALTER TABLE development_pipeline DROP CONSTRAINT IF EXISTS development_pipeline_status_check;
|
||||
|
||||
-- Миграция старых статусов в новые
|
||||
UPDATE development_pipeline SET status = 'incoming' WHERE status = 'analysis';
|
||||
UPDATE development_pipeline SET status = 'agenda_approval' WHERE status = 'negotiation';
|
||||
UPDATE development_pipeline SET status = 'in_person' WHERE status = 'preparation';
|
||||
UPDATE development_pipeline SET status = 'absentee' WHERE status = 'voting';
|
||||
UPDATE development_pipeline SET status = 'success' WHERE status = 'transfer';
|
||||
|
||||
-- Добавляем новый CHECK (включая этап «Анализ»)
|
||||
ALTER TABLE development_pipeline ADD CONSTRAINT development_pipeline_status_check
|
||||
CHECK (status IN (
|
||||
'incoming',
|
||||
'analysis',
|
||||
'agenda_approval',
|
||||
'in_person',
|
||||
'absentee',
|
||||
'protocol_formation',
|
||||
'protocol_to_gzhi',
|
||||
'gzhi_order',
|
||||
'success',
|
||||
'failure'
|
||||
));
|
||||
|
||||
-- Дефолт для новых записей (на случай если где-то вставляют без status)
|
||||
-- ALTER TABLE development_pipeline ALTER COLUMN status SET DEFAULT 'incoming';
|
||||
15
backend/migrations/update_building_financial_data_unique.sql
Executable file
15
backend/migrations/update_building_financial_data_unique.sql
Executable file
@@ -0,0 +1,15 @@
|
||||
-- Обновление UNIQUE constraint для building_financial_data
|
||||
-- Разрешаем накопление данных по периодам из разных отчетов
|
||||
|
||||
-- Удаляем старый constraint
|
||||
ALTER TABLE building_financial_data
|
||||
DROP CONSTRAINT IF EXISTS building_financial_data_building_id_period_start_period_end_period_type_key;
|
||||
|
||||
-- Создаем новый constraint, который включает report_id
|
||||
-- Это позволяет иметь несколько записей для одного периода, но из разных отчетов
|
||||
ALTER TABLE building_financial_data
|
||||
ADD CONSTRAINT building_financial_data_unique
|
||||
UNIQUE (building_id, report_id, period_start, period_end, period_type);
|
||||
|
||||
COMMENT ON CONSTRAINT building_financial_data_unique ON building_financial_data IS
|
||||
'Уникальность по дому, отчету и периоду. Позволяет накапливать данные по периодам из разных отчетов.';
|
||||
Reference in New Issue
Block a user