feat(phase-0,phase-2): bootstrap DB schemas + Metabase/NocoDB compose + work_types seed

This commit is contained in:
Roman Chesnokov
2026-05-14 15:26:59 +05:00
parent 7b282af5be
commit ba2059ae15
13 changed files with 398 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
-- 001_schemas.sql — базовые схемы для слоёв DWH
CREATE SCHEMA IF NOT EXISTS raw_bitra;
CREATE SCHEMA IF NOT EXISTS raw_eva;
CREATE SCHEMA IF NOT EXISTS raw_bitrix;
CREATE SCHEMA IF NOT EXISTS stg_bitra;
CREATE SCHEMA IF NOT EXISTS stg_eva;
CREATE SCHEMA IF NOT EXISTS stg_bitrix;
CREATE SCHEMA IF NOT EXISTS core;
CREATE SCHEMA IF NOT EXISTS mart;
-- Журнал применённых миграций
CREATE TABLE IF NOT EXISTS public.migrations (
filename text PRIMARY KEY,
applied_at timestamptz DEFAULT now()
);
+48
View File
@@ -0,0 +1,48 @@
-- 002_core_employee.sql — core.office, core.department, core.employee, history
CREATE TABLE core.office (
id bigserial PRIMARY KEY,
name text NOT NULL,
bitra_id text UNIQUE
);
CREATE TABLE core.department (
id bigserial PRIMARY KEY,
name text NOT NULL,
bitra_id text UNIQUE,
bitrix_id bigint UNIQUE,
parent_id bigint REFERENCES core.department,
source text NOT NULL DEFAULT 'bitra' -- bitra | bitrix
);
CREATE TABLE core.employee (
id bigserial PRIMARY KEY,
email text UNIQUE NOT NULL,
full_name text,
first_name text,
last_name text,
bitra_user_id text UNIQUE,
eva_person_id text UNIQUE,
bitrix_user_id bigint UNIQUE,
rate decimal(10,2),
office_id bigint REFERENCES core.office,
department_id bigint REFERENCES core.department,
is_active boolean DEFAULT true,
is_target_for_mvp1 boolean DEFAULT false,
last_synced timestamptz
);
CREATE INDEX idx_employee_email ON core.employee (lower(email));
CREATE INDEX idx_employee_bitra_id ON core.employee (bitra_user_id);
CREATE INDEX idx_employee_eva_id ON core.employee (eva_person_id);
CREATE INDEX idx_employee_bitrix_id ON core.employee (bitrix_user_id);
CREATE INDEX idx_employee_target_mvp1 ON core.employee (is_target_for_mvp1) WHERE is_target_for_mvp1 = true;
CREATE TABLE core.department_history (
employee_id bigint REFERENCES core.employee,
valid_from date NOT NULL,
valid_to date,
department_id bigint REFERENCES core.department,
source text NOT NULL DEFAULT 'bitra',
PRIMARY KEY (employee_id, valid_from)
);
+37
View File
@@ -0,0 +1,37 @@
-- 003_core_project.sql — core.project + core.stage
CREATE TABLE core.project (
id bigserial PRIMARY KEY,
name text NOT NULL,
bitra_id text UNIQUE,
eva_id text UNIQUE,
bitra_code text,
eva_code text,
is_sd boolean DEFAULT false, -- SD-проект (из явного списка)
status text,
cache_status_type text, -- из EVA: OPEN/IN_PROGRESS/IN_REVIEW/CLOSED
project_manager_id bigint REFERENCES core.employee,
bitrix_company_id bigint,
bitra_client_id text,
bitra_client_name text, -- для отображения
deadline date,
budget decimal(15,2),
last_synced timestamptz
);
CREATE INDEX idx_project_bitra ON core.project (bitra_id);
CREATE INDEX idx_project_eva ON core.project (eva_id);
CREATE INDEX idx_project_sd ON core.project (is_sd) WHERE is_sd = true;
CREATE TABLE core.stage (
id bigserial PRIMARY KEY,
name text NOT NULL,
bitra_id text UNIQUE,
project_id bigint REFERENCES core.project,
plan_start_date date,
plan_end_date date,
is_completed boolean DEFAULT false,
is_acted boolean DEFAULT false
);
CREATE INDEX idx_stage_project ON core.stage (project_id);
+29
View File
@@ -0,0 +1,29 @@
-- 004_core_work_log.sql — core.work_type + core.work_log
CREATE TABLE core.work_type (
code text PRIMARY KEY,
label text NOT NULL,
category text NOT NULL CHECK (category IN ('commercial','presale','internal','free','ignored')),
is_billable boolean NOT NULL DEFAULT false
);
CREATE TABLE core.work_log (
id bigserial PRIMARY KEY,
employee_id bigint NOT NULL REFERENCES core.employee,
work_date date NOT NULL,
work_type_code text NOT NULL REFERENCES core.work_type,
project_id bigint REFERENCES core.project,
stage_id bigint REFERENCES core.stage,
bitra_client_id text,
bitra_client_name text,
hours decimal(10,2) NOT NULL,
description text,
bitra_doc_id text NOT NULL,
bitra_row_index int NOT NULL,
UNIQUE (bitra_doc_id, bitra_row_index)
);
CREATE INDEX idx_work_log_employee_date ON core.work_log (employee_id, work_date DESC);
CREATE INDEX idx_work_log_project ON core.work_log (project_id);
CREATE INDEX idx_work_log_work_type ON core.work_log (work_type_code);
CREATE INDEX idx_work_log_date ON core.work_log (work_date DESC);
+23
View File
@@ -0,0 +1,23 @@
-- 005_core_task.sql — core.task (EVA-задачи)
CREATE TABLE core.task (
id bigserial PRIMARY KEY,
eva_id text UNIQUE NOT NULL,
code text,
name text,
project_id bigint REFERENCES core.project,
responsible_id bigint REFERENCES core.employee,
cache_status_type text NOT NULL CHECK (cache_status_type IN ('OPEN','IN_PROGRESS','IN_REVIEW','CLOSED')),
eva_status_id text,
eva_status_name text,
cmf_created_at timestamptz,
cmf_modified_at timestamptz,
status_in_progress_start timestamptz,
deadline timestamptz,
last_synced timestamptz
);
CREATE INDEX idx_task_responsible ON core.task (responsible_id);
CREATE INDEX idx_task_status ON core.task (cache_status_type);
CREATE INDEX idx_task_project ON core.task (project_id);
CREATE INDEX idx_task_modified ON core.task (cmf_modified_at DESC);
+33
View File
@@ -0,0 +1,33 @@
-- 006_core_deal.sql — core.deal + core.deal_team_member (Битрикс CAT=16)
CREATE TABLE core.deal (
id bigserial PRIMARY KEY,
bitrix_id bigint UNIQUE NOT NULL,
title text,
category_id int,
stage_id text,
stage_semantic_id char(1),
opportunity decimal(15,2),
begindate date,
closedate date,
assigned_to_id bigint REFERENCES core.employee,
bitrix_company_id bigint,
bitrix_company_name text,
project_manager_id bigint REFERENCES core.employee,
is_in_forecast boolean DEFAULT false,
last_synced timestamptz
);
CREATE INDEX idx_deal_stage ON core.deal (stage_id);
CREATE INDEX idx_deal_forecast ON core.deal (is_in_forecast) WHERE is_in_forecast = true;
CREATE INDEX idx_deal_category ON core.deal (category_id);
CREATE TABLE core.deal_team_member (
deal_id bigint REFERENCES core.deal ON DELETE CASCADE,
employee_id bigint REFERENCES core.employee,
weight decimal(5,2) NOT NULL DEFAULT 1.0,
is_manual_override boolean DEFAULT false,
PRIMARY KEY (deal_id, employee_id)
);
CREATE INDEX idx_deal_team_employee ON core.deal_team_member (employee_id);
+19
View File
@@ -0,0 +1,19 @@
-- 007_core_identity_map.sql — таблица соответствий ID между системами
CREATE TABLE core.identity_map (
id bigserial PRIMARY KEY,
entity_type text NOT NULL CHECK (entity_type IN ('employee','project','client')),
core_id bigint,
bitra_id text,
eva_id text,
bitrix_id bigint,
confidence text NOT NULL CHECK (confidence IN ('auto','confirmed','manual')),
match_key text,
confirmed_by text,
confirmed_at timestamptz,
created_at timestamptz DEFAULT now(),
UNIQUE (entity_type, bitra_id, eva_id, bitrix_id)
);
CREATE INDEX idx_identity_map_core ON core.identity_map (entity_type, core_id);
CREATE INDEX idx_identity_map_confidence ON core.identity_map (confidence) WHERE confidence = 'manual';
+89
View File
@@ -0,0 +1,89 @@
-- 008_raw_schemas.sql — JSONB-снимки источников
-- BIT.RA raw
CREATE TABLE raw_bitra.employees (
bitra_id text PRIMARY KEY,
payload jsonb NOT NULL,
synced_at timestamptz DEFAULT now()
);
CREATE TABLE raw_bitra.works (
bitra_doc_id text PRIMARY KEY,
payload jsonb NOT NULL,
synced_at timestamptz DEFAULT now()
);
CREATE TABLE raw_bitra.projects (
bitra_id text PRIMARY KEY,
payload jsonb NOT NULL,
synced_at timestamptz DEFAULT now()
);
CREATE TABLE raw_bitra.dictionaries (
kind text NOT NULL,
bitra_id text NOT NULL,
payload jsonb NOT NULL,
synced_at timestamptz DEFAULT now(),
PRIMARY KEY (kind, bitra_id)
);
CREATE TABLE raw_bitra.work_types (
bitra_id text PRIMARY KEY,
payload jsonb NOT NULL,
synced_at timestamptz DEFAULT now()
);
CREATE TABLE raw_bitra.dept_history (
bitra_employee_id text NOT NULL,
period date NOT NULL,
payload jsonb NOT NULL,
synced_at timestamptz DEFAULT now(),
PRIMARY KEY (bitra_employee_id, period)
);
-- EVA raw
CREATE TABLE raw_eva.persons (
eva_id text PRIMARY KEY,
payload jsonb NOT NULL,
synced_at timestamptz DEFAULT now()
);
CREATE TABLE raw_eva.projects (
eva_id text PRIMARY KEY,
payload jsonb NOT NULL,
synced_at timestamptz DEFAULT now()
);
CREATE TABLE raw_eva.tasks (
eva_id text PRIMARY KEY,
payload jsonb NOT NULL,
synced_at timestamptz DEFAULT now()
);
CREATE TABLE raw_eva.status_history (
eva_id text PRIMARY KEY,
payload jsonb NOT NULL,
synced_at timestamptz DEFAULT now()
);
-- Bitrix raw
CREATE TABLE raw_bitrix.deals (
bitrix_id bigint PRIMARY KEY,
payload jsonb NOT NULL,
synced_at timestamptz DEFAULT now()
);
CREATE TABLE raw_bitrix.users (
bitrix_id bigint PRIMARY KEY,
payload jsonb NOT NULL,
synced_at timestamptz DEFAULT now()
);
CREATE TABLE raw_bitrix.departments (
bitrix_id bigint PRIMARY KEY,
payload jsonb NOT NULL,
synced_at timestamptz DEFAULT now()
);
-- Журнал синхронизаций (общий для всех источников)
CREATE TABLE public.sync_log (
id bigserial PRIMARY KEY,
source text NOT NULL,
entity text NOT NULL,
last_sync_ts timestamptz,
records_count int,
status text,
error_message text,
synced_at timestamptz DEFAULT now()
);
CREATE INDEX idx_sync_log_source_entity ON public.sync_log (source, entity, synced_at DESC);
+4
View File
@@ -0,0 +1,4 @@
-- 009_stg_schemas.sql
-- stg-слой реализован как VIEW в sql/views/stg_*.sql.
-- Эта миграция — placeholder для трекинга порядка применения.
SELECT 1;