# 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.ru` → `localhost: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=&limit=N` | Document.Работы + ТЧ за период с инкрементом | | GET | `/api/employees?modified_since=` | Catalog.Пользователи + ContactInfo (email) | | GET | `/api/projects?modified_since=` | Catalog.Проекты с EVA_ID | | GET | `/api/dictionaries` | Подразделения, Офисы, Менеджеры, СценарииПланирования и т.д. (редкий полный) | | GET | `/api/work_types` | Enum.ВидыРабот (статичный, тянем раз в сутки) | | GET | `/api/dept_history?modified_since=` | 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>=` → список изменённых объектов → `.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/`. Только воронка 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 минимум) ```sql 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) ```sql 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 — таблица ```sql 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-webhooks** — `n8n.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-интеграция** — все воронки, лиды, контакты.