Files
bit-flight-deck/docs/superpowers/specs/2026-05-13-mvp1-workload-design.md
T

32 KiB
Raw Blame History

MVP-1 «Загрузка сотрудников» — Design Document

Дата 2026-05-13
Статус Draft на ревью пользователя
Владелец Чесноков Роман (Руководитель проектного отдела)
Срок MVP 3-4 недели
Контекст-проект Workload & Project Control System (см. memory project_workload_control)

1. Контекст и проблема

В компании используются 4 учётных системы, ни одна из которых не даёт ответа на главные вопросы руководителя проектного отдела:

  • BIT.RA (1С) — ежедневные отчёты сотрудников о выполненной работе, проектная финансовая аналитика.
  • EVA Desk — задачи разработчиков/PM и Service Desk.
  • Битрикс24 — CRM, сделки, прогноз продаж.
  • Самописная 1С (финрез) — ручной агрегатор финреза по подразделениям.

Чтобы понять «загрузку сотрудника» сейчас, руководитель должен открыть 3 системы параллельно и склеить данные в голове или в Excel. Это не масштабируется и не даёт прогноза.

2. Цели MVP-1

Построить первую вертикальную автоматизацию — единый дашборд «Загрузка сотрудников» с 4 слоями (факт / текущая / плановая / прогнозная) для команды ~20 сотрудников за 3-4 недели работы.

Не-цели MVP-1

  • Контроль сроков проектов поэтапно (подсистема C).
  • Расчёт маржи, НЗП, актирования (подсистема C/F).
  • Компетенции сотрудников (подсистема D).
  • Расчёт финреза по подразделениям (подсистема F — отдельный мозговой штурм).
  • BI-дашборды для всех руководителей (подсистема G).
  • Multi-user интерфейс с правами по сотрудникам (зафиксировано MVP single-user).
  • Двусторонняя интеграция (мы только читаем, источники остаются мастерами).
  • Подключение CmfTimeTrackerHistory (под ACL deny, не используется).
  • Подключение CmfTaskResAssign (не используется в команде).
  • Все 16 воронок Битрикса (только CAT=16 «Проектные продажи (нов.)»).
  • ИНН-маппинг клиентов BIT.RA ↔ Битрикс (поле в BIT.RA не заполняется).
  • Прямая связь сделок Битрикса с проектами BIT.RA (UF_CRM_PROJECTID мёртвое поле).

3. Бизнес-вопросы и их витрины

Вопрос Витрина (mart) Слой Покрытие
Кто из сотрудников и сколько часов проработал коммерчески за месяц mart.workload_actual Факт 100% от BIT.RA-отчётности
Кто сейчас в работе на каких задачах и сколько часов на них уйдёт mart.workload_current Текущая ~43% задач EVA (по sample на 2026-05-13 — только задачи с responsible_id)
Сколько часов осталось доделать у каждого сотрудника (открытые задачи) mart.workload_planned Плановая (как «осталось») То же ~43%
Сколько часов может прилететь из сделок Битрикса в стадиях после Защиты КП mart.workload_forecast Прогноз Зависит от заполнения проектной команды
Сколько «безхозных» задач (без исполнителя или без проекта) — намёк команде mart.tasks_orphan Метрика качества данных

Дашборд в Metabase собирает всё в один экран.

4. Архитектура верхнего уровня

  Источники                       Транспорт              Оркестратор + Хранилище
  ─────────────                   ─────────              ────────────────────────

  BIT.RA (1C)        ──pull──>    HTTP-services    ─┐
  EVA Desk           ──pull──>    JSON-RPC + admin ─┤
  Bitrix24            ──pull──>    REST             ─┼──>  N8N orchestrator
  Bitrix24            ──push──>    Webhooks (tunnel)─┤      (cron + webhook triggers)
                                                     │             │
                                                     │             ▼
                                                     │     ┌──────────────────────┐
                                                     │     │     PostgreSQL       │
                                                     │     │  raw_bitra (JSONB)   │
                                                     │     │  raw_eva (JSONB)     │
                                                     │     │  raw_bitrix (JSONB)  │
                                                     │     │    ↓ SQL views/procs │
                                                     │     │  stg_*               │
                                                     │     │    ↓                 │
                                                     │     │  core.*              │
                                                     │     │    ↓                 │
                                                     │     │  mart.*              │
                                                     │     └─────┬────────────────┘
                                                     │           │
                                                     │     ┌─────┴─────┐
                                                     │     ▼           ▼
                                                     │  Metabase    NocoDB
                                                     │  (dashboards)(admin UI:
                                                     │              identity_map,
                                                     │              deal_team_weights)
                                                     │
                                                     └──>  Telegram (alerts, optional)

Компоненты

Используем существующую инфраструктуру на хосте (Ubuntu в WSL), репозиторий ~/infrastructure/:

Компонент Статус Назначение Как используем
pipeline_postgres (postgres:15-alpine) уже стоит Хранилище Создаём новую БД workload + пользователя workload_user
n8n уже стоит Оркестратор Создаём наши workflows в существующем контейнере
pipeline_litellm уже стоит (87+ моделей через OpenRouter) LLM для опц. fuzzy-match ФИО HTTP-нода в N8N → http://pipeline_litellm:4000
pipeline_gitea уже стоит Версионирование Новый репозиторий bit-flight-deck
Cloudflare tunnel (host-systemd) уже стоит Публичный endpoint для Bitrix webhooks Добавляем поддомен n8n.bigmadnekenny.rulocalhost:5678 (N8N webhook-trigger)

Ставим новое в ~/projects/bit-flight-deck/docker-compose.yml (подключение к external network pipeline_net):

Компонент Назначение
Metabase Дашборды поверх mart-таблиц
NocoDB UI для ручного управления core.identity_map, core.deal_team_weights

Источники данных (внешние):

Компонент Назначение Где
BIT.RA HTTP-сервисы Серверный модуль IntegrationAPI с REST-эндпоинтами под нужные core-сущности Внутри BIT.RA (одноразовая 1С-разработка)
EVA Desk JSON-RPC Стандартный API EVA с глобальным админ-токеном Внешний firstbit.evateam.ru
Bitrix24 REST + webhooks Pull через REST + push-приёмник через CF Tunnel Внешний vdst421.1cbit.ru

5. Транспорт по источникам

5.1. BIT.RA — HTTP-сервисы 1С (pull)

В BIT.RA пишется серверный модуль IntegrationAPI с эндпоинтами:

Метод Эндпоинт Содержимое
GET /api/works?modified_since=<ts>&limit=N Document.Работы + ТЧ за период с инкрементом
GET /api/employees?modified_since=<ts> Catalog.Пользователи + ContactInfo (email)
GET /api/projects?modified_since=<ts> Catalog.Проекты с EVA_ID
GET /api/dictionaries Подразделения, Офисы, Менеджеры, СценарииПланирования и т.д. (редкий полный)
GET /api/work_types Enum.ВидыРабот (статичный, тянем раз в сутки)
GET /api/dept_history?modified_since=<ts> InformationRegister.ПодразделениеСотрудников

Расписание: works/employees/projects — каждые 30 минут. Dictionaries/work_types — раз в сутки.

Аутентификация — выделенный 1С-пользователь с правами только на чтение релевантных объектов.

5.2. EVA Desk — Pull-гибрид + глобальный админ-токен

  • Nightly full sync (раз в сутки): .list на CmfPerson, CmfProject, CmfTask, CmfStatus — полный снимок в raw_eva.*.
  • Incremental sync (каждые 30 минут): CmfAudit.list?filter=cmf_created_at>=<last_sync> → список изменённых объектов → .get за деталями.

Глобальный админ-токен в env-переменной N8N. После разработки — пересоздаётся.

5.3. Bitrix24 — Webhooks + nightly reconcile

  • Outbound webhooks (push): на события ONCRMDEALADD, ONCRMDEALUPDATE, ONCRMDEALDELETE, ONUSERADD, ONUSERUPDATE, ONUSERDELETE. Битрикс POST'ит на наш endpoint через существующий Cloudflare Tunnel → новый поддомен → N8N webhook-trigger принимает → crm.deal.get за деталями → запись в raw.
  • Nightly reconcile pull (раз в сутки): crm.deal.list?filter[CATEGORY_ID]=16 со всеми UF-полями для сверки.

Поддомен для webhook'а: добавляется к существующему cloudflared (host-systemd сервис). Конфигурация — в cloudflared.env (route на localhost:5678). Webhook-URL — https://n8n.bigmadnekenny.ru/webhook/bitrix/<secret-token>.

Только воронка CAT=16. Только сделки в стадиях от «Защита КП» (FINAL_INVOICE) для прогноза + стадии реализации (UC_XPZ8Z5, UC_QYTFP3) как текущая загрузка.

5.4. finrez_1c — НЕ в MVP-1

Откладываем до подсистемы F. Доработка Catalog.Сотрудники (добавление email) — также вне MVP-1.

6. Data Model

6.1. Слои PostgreSQL

raw_bitra.*      — JSONB-снимки ответов BIT.RA HTTP-сервисов
raw_eva.*        — JSONB-снимки ответов EVA JSON-RPC
raw_bitrix.*     — JSONB-снимки сделок и пользователей Битрикса

stg_bitra.*      — нормализованные сущности BIT.RA (по одной таблице на сущность)
stg_eva.*        — нормализованные сущности EVA
stg_bitrix.*     — нормализованные сущности Битрикса

core.*           — унифицированная модель (см. ниже)

mart.*           — витрины для дашборда

6.2. core-таблицы (MVP-1 минимум)

core.employee (
    id bigserial PRIMARY KEY,
    email text UNIQUE NOT NULL,
    full_name text,
    is_active boolean,
    is_target_for_mvp1 boolean,     -- флаг «в фокусе MVP-1»
    bitra_user_id text,             -- guid из BIT.RA
    eva_person_id text,             -- CmfPerson:UUID
    bitrix_user_id bigint,          -- ID Битрикса
    rate decimal(10,2),             -- ставка
    department_id bigint REFERENCES core.department,
    office_id bigint REFERENCES core.office,
    last_synced timestamptz
)

core.department (
    id bigserial PRIMARY KEY,
    name text,
    bitra_id text,
    bitrix_id bigint,
    parent_id bigint REFERENCES core.department
)

core.office (
    id bigserial PRIMARY KEY,
    name text,
    bitra_id text
)

core.work_type (
    code text PRIMARY KEY,         -- ЛУРВ, ЛТ, Демо, ИТС, ...
    label text,
    category text,                  -- commercial | presale | internal | free | ignored
    is_billable boolean
)

core.project (
    id bigserial PRIMARY KEY,
    name text,
    bitra_id text,
    eva_id text,
    is_sd boolean DEFAULT false,    -- SD-проект (из явного списка 3 ID)
    status text,
    deadline date,
    budget decimal(15,2)
)

core.work_log (
    id bigserial PRIMARY KEY,
    employee_id bigint REFERENCES core.employee,
    work_date date NOT NULL,
    work_type_code text REFERENCES core.work_type,
    project_id bigint REFERENCES core.project,
    stage_id bigint,
    client_id bigint,
    hours decimal(10,2),
    description text,
    bitra_doc_id text,              -- ссылка на Document.Работы
    bitra_row_index int
)

core.task (
    id bigserial PRIMARY KEY,
    code text,                       -- PBSD-12582
    name text,
    eva_id text UNIQUE,              -- CmfTask:UUID
    cache_status_type text,          -- OPEN | IN_PROGRESS | IN_REVIEW | CLOSED
    project_id bigint REFERENCES core.project,
    responsible_id bigint REFERENCES core.employee,
    cmf_created_at timestamptz,
    cmf_modified_at timestamptz,
    status_in_progress_start timestamptz
)

core.deal (
    id bigserial PRIMARY KEY,
    bitrix_id bigint UNIQUE,
    title text,
    category_id int,                 -- 16
    stage_id text,                   -- C16:FINAL_INVOICE и т.д.
    stage_semantic_id char(1),       -- P | S | F
    opportunity decimal(15,2),
    begindate date,
    closedate date,
    assigned_to_id bigint REFERENCES core.employee,
    company_id bigint                -- ID Битрикс-компании (без маппинга на BIT.RA)
)

core.deal_team_member (
    deal_id bigint REFERENCES core.deal,
    employee_id bigint REFERENCES core.employee,
    weight decimal(5,2) DEFAULT 1.0,  -- вес сотрудника в команде (для распределения часов). По умолчанию 1.0 = поровну
    is_manual boolean DEFAULT false,  -- проставлен вручную через NocoDB
    PRIMARY KEY (deal_id, employee_id)
)

core.identity_map (
    entity_type text,                 -- employee | client | project
    core_id bigint,
    bitra_id text,
    eva_id text,
    bitrix_id bigint,
    confidence text,                  -- auto | confirmed | manual
    confirmed_by text,
    confirmed_at timestamptz
)

6.3. core.work_type — справочник видов работ

Загружается из BIT.RA Enum.ВидыРабот (14 значений), плюс правило категоризации:

Code (1С) Label Category is_billable
ЛУРВ ЛУРВ commercial true
ЛТ ЛТ commercial true
Демо Пресейл presale false
ИТС ИТС (договор) commercial true
ИТСПлатныеРаботы ИТС (доп. услуги) commercial true
Сертификация Сертификация internal false
Внутреннее Обучение internal false
Управленка Работа руководителя internal false
ВнутренниеРаботы Внутренние работы internal false
НеОпл Бесплатные часы в счёт ПП free false
Установка Установка в счёт ПП free false
Гарантия Гарантия free false
Коробка, Отложено (рудимент) ignored false

6.4. mart-таблицы (дашборд MVP-1)

mart.workload_actual_monthly       -- сотрудник × месяц → часы по категориям
mart.workload_current              -- сотрудник × текущие задачи EVA (статус IN_PROGRESS) → часы (если есть оценка) или штуки
mart.workload_planned              -- сотрудник × открытые задачи EVA (OPEN + IN_PROGRESS с responsible_id) → штуки и/или оценочные часы
mart.workload_forecast             -- сотрудник × сделки Битрикс (от защиты КП до создания EVA-проекта) × proекторное распределение по проектной команде
mart.tasks_orphan                  -- задачи без responsible_id или без project_id (для team awareness)
mart.workload_summary              -- composite: сотрудник × недели → факт/текущая/план/прогноз

7. Identity-resolution

7.1. Сотрудники — через email

Главный ключ. lower(email) сравнивается между:

  • BIT.RA Catalog.Пользователи.КонтактнаяИнформация (нужно дозаполнить если где-то пусто)
  • EVA CmfPerson.login (= email на firstbit, домен @1cbit.ru)
  • Bitrix24 User.EMAIL (домен @1cbit.ru у 117 из 133 активных)

Fallback: ФИО → ручное разрешение через core.identity_map в NocoDB.

7.2. Проекты — через регистры BIT.RA + EVA_ID

  • Catalog.Проекты.EVA_ID — частично заполнен.
  • InformationRegister.СоответствиеПроектовEVA_РА — есть данные, обработка синхронизации сломана. В рамках MVP-1 НЕ восстанавливаем (это отдельная 1С-задача). Используем то, что есть в регистре + EVA_ID.
  • Что не сматчилось — отмечаем confidence='manual' и ждём ручного разрешения в NocoDB.

7.3. Клиенты — не маппим

В MVP-1 связь BIT.RA-клиент ↔ Битрикс-компания не строим (ИНН в BIT.RA не заполняется, согласовано). Сделки Битрикса висят с bitrix_company_id без связки на BIT.RA.

7.4. Identity-map — таблица

core.identity_map (
    entity_type,         -- employee | project
    core_id,             -- наш id
    bitra_id,
    eva_id,
    bitrix_id,
    confidence,          -- auto (по точному email) | confirmed (через UI) | manual (без авто-сматчинга, ждёт человека)
    confirmed_by,
    confirmed_at
)

В NocoDB представление — отдельная страница «Identity issues» — все записи с confidence='manual' сортированные по дате создания. Пользователь пробегает раз в неделю и подтверждает.

8. Стратегия SQL-трансформаций

8.1. raw → stg

Stored procedure на каждую сущность каждого источника. Логика: INSERT INTO stg_X SELECT FROM raw_X WHERE NOT EXISTS (...) ON CONFLICT UPDATE. Идемпотентно.

8.2. stg → core

Stored procedure core.merge_employee(), core.merge_project(), core.merge_task(), core.merge_work_log(), core.merge_deal().

Логика merge:

  1. Найти core_id через core.identity_map.
  2. Если найден — UPDATE.
  3. Если нет — попытаться сматчить (email, EVA_ID и т.п.). Если успех — UPDATE + добавить запись в identity_map.
  4. Если не сматчилось — INSERT новый core_id + identity_map с confidence='manual'.

8.3. core → mart

Materialized views, обновляемые после каждой пачки sync'а. Триггер: N8N после sync'а делает SELECT mart.refresh_all().

8.4. Версионирование SQL

Структура проекта ~/projects/bit-flight-deck/ (имя финальное обсуждаем) — Gitea репозиторий:

~/projects/bit-flight-deck/
├── docker-compose.yml       -- Metabase + NocoDB (подключение к pipeline_net)
├── .env / .env.example
├── infra/
│   ├── init-workload-db.sql -- создание БД workload + workload_user в pipeline_postgres
│   └── cloudflared-routes.md-- документация по поддоменам tunnel
├── sql/
│   ├── migrations/          -- одноразовые DDL миграции (CREATE TABLE и т.п.)
│   ├── views/               -- view-определения
│   ├── procedures/          -- stored procedures
│   └── seed/                -- начальные данные (work_type категории, SD-projects whitelist)
├── n8n/
│   └── workflows/           -- JSON-экспорты workflows N8N
├── docs/superpowers/specs/  -- эта спека и будущие
└── README.md

9. Дашборд Metabase

9.1. Главный экран «Загрузка сотрудников MVP-1»

Раздел Содержимое
Заголовок Дата последней синхронизации (timestamp) с каждым источником
Plate 1 Средний % коммерческой загрузки команды за 30 дней (большая цифра, цвет)
Plate 2 Количество «безхозных» задач (без responsible_id или project_id) — намёк команде
Таблица По каждому из ~20 сотрудников: ФИО / Подразделение / Факт 30д часы / Факт 30д % коммерч. / Текущая (часы IN_PROGRESS задач) / Осталось (часы OPEN+IN_PROGRESS) / Прогноз 4 недели (Б24) / Итого 4 недели
Линейный график Динамика % коммерческой загрузки команды по неделям за 90 дней
Барчарт Свободные ресурсы — топ-N сотрудников с факт+план ниже 50%
Таблица Перегруженные — топ-N сотрудников с факт+план выше 90%

9.2. Принципы визуализации

  • Светофор: красный <50%, жёлтый 50-70%, зелёный 70-100%, серый >100% (перегруз).
  • Все таблицы — с сортировкой и фильтрами по подразделению, виду работ.
  • Цифры — округлены до часов, не до минут.

10. Operational concerns

10.1. Мониторинг

  • N8N-flow «health check» каждый час: проверяет последние sync-таймстампы. Если sync с любым источником > 2 часа — алёрт в Telegram (или просто logging).
  • Метрики на дашборде: количество записей в каждой raw_*, last_sync timestamp по каждому источнику.

10.2. Бэкап

  • PostgreSQL — автоматический pg_dumpall ещё не настроен в инфре (TODO в ~/infrastructure/ROADMAP.md). Для MVP-1 — ручной pg_dump workload > workload-YYYY-MM-DD.sql.gz раз в сутки (cron в WSL). Когда инфра-уровневый backup поднимется (sidecar pipeline_pg_backup) — наш проект пользуется им автоматом.
  • Schema-only бэкап перед каждой миграцией (pg_dump --schema-only).
  • Gitea-репозиторий проекта — резервируется автоматически с остальным Gitea (в рамках инфры).

10.3. Безопасность

  • EVA админ-токен в env N8N, не в git.
  • Bitrix webhook URL — с секретным токеном в URL (защита от случайных POST'ов).
  • PostgreSQL — отдельные роли: etl_writer (N8N) с правами на raw_/stg_, analyst (Metabase, NocoDB) с правами read-only на core/mart.
  • HTTPS на туннеле для Bitrix-webhooks (Битрикс требует валидный SSL).

10.4. Производительность

Объёмы маленькие (десятки сотрудников, тысячи задач, тысячи строк работ в год). PostgreSQL без напряга справится. Индексы — по timestamps (modified_at, work_date) + по FK.

11. Acceptance criteria MVP-1

  1. В Metabase открыт дашборд «Загрузка сотрудников MVP-1», виден список ~20 сотрудников с 4 слоями загрузки.
  2. Факт за вчера в дашборде совпадает с тем что в BIT.RA Document.Работы за вчера (выборочная проверка 3 сотрудников).
  3. Текущая загрузка показывает задачи EVA в статусе IN_PROGRESS для сотрудников с responsible_id. Сверка с EVA-UI (выборочно).
  4. Прогнозная загрузка показывает сотрудников из проектных команд сделок Битрикса CAT=16 от стадии FINAL_INVOICE («Защита сделки»).
  5. NocoDB-страница «Identity issues» показывает несматчившиеся сущности (с confidence='manual').
  6. Свежесть данных: BIT.RA — не старше 1 часа, EVA — не старше 1 часа, Bitrix — не старше 5 минут (webhook).
  7. Один полный цикл E2E: меняешь данные в источниках → через указанные временные окна они отражаются в дашборде.
  8. Дашборд отвечает на 5 бизнес-вопросов из секции 3 без ручной фильтрации.

12. Параллельные организационные задачи

Не блокеры запуска MVP-1, но запускаются параллельно:

  • Дисциплина EVA: внедрить в команде заполнение plan_start_date / plan_end_date / deadline / responsible_id для всех новых задач. Через 2-3 месяца — пересмотр MVP-1 с честной «плановой» через plan_start_date.
  • Email во всех системах: дозаполнить email сотрудников в BIT.RA Catalog.Пользователи где пусто. Аналогично в Битриксе для новых юзеров.

13. Open questions / TODO

  • Имя проектаbit-flight-deck. Gitea-репозиторий bit-flight-deck. Локальная папка ~/projects/bit-flight-deck/.
  • Поддомен для Bitrix-webhooksn8n.bigmadnekenny.ru. Добавить ingress-роут в cloudflared host-systemd.
  • Список ~20 сотрудников MVP-1. Какие конкретно email/department-id попадают в фокус? До начала разработки — выписать явный whitelist.
  • 3 SD-проекта. Извлечь точные ID/коды из https://gitea.bigmadnekenny.ru/admin/eva-bot/docs/.
  • Стадии Битрикса для прогноза. Подтвердить точный список стадий CAT=16, в которых сделка должна учитываться в прогнозе. Кандидаты: FINAL_INVOICE («Защита сделки»), UC_A02TUT («Отложено»), UC_U68WK1 («Подготовка рамочного договора»). Стадии UC_XPZ8Z5/UC_QYTFP3 (Реализация) — туда уже EVA, в прогноз НЕ включаем.
  • Заполняемость email в BIT.RA. Через MCP проверить какой % пользователей имеет email в КонтактнойИнформации. Если ниже 80% — параллельная организационная задача.
  • Распределение часов сделки по проектной команде. Базовая логика — поровну между членами команды. Веса можно править в NocoDB. Возможно нужна более умная логика (по компетенциям) — это backlog для подсистемы D.

14. Backlog после MVP-1

Не в MVP-1, но зафиксировано для последующих итераций:

  • MVP-2: Подвисшие задачи и контроль сроков — EVA CmfStatusHistory + детектор простоев + алёрты в Telegram.
  • MVP-3: Маржа, НЗП, актирование — BIT.RA ОборотыПроектныхПоказателей_v2 + расчёты маржинальности и НЗП.
  • MVP-4: Финрез по подразделениям — finrez_1c + отдельный мозговой штурм по схеме расчёта + доработка финрезной 1С.
  • Подсистема D: Компетенции (производная из core.work_log) — расчёт «что сотрудник реально умеет» из истории работ.
  • Восстановление обработки СинхронизацияДанныхEVA_РА в BIT.RA — если потребуется для подсистемы C/D.
  • Автоматическое распределение SD-задач по компетенциям сотрудников — подсистема D.
  • EVA TimeTracker — если получится получить токен с правами на TimeTracker.
  • DataLens вместо Metabase — если потребуются более сложные визуализации.
  • Полная Bitrix-интеграция — все воронки, лиды, контакты.