Дашборд руководства
Главная страница для руководителей: состояние проектов, денег, оборудования, логистики и дисциплины учёта времени одним экраном (ТЗ: MVP, раздел 9; «Единая панель управления» финансового ядра; дашборд «Финансовые риски проектов»).
Что на главной (с 2026-09-30)
По решению заказчика на первом экране только нужное и графиками, а не списками: четыре плитки (активные проекты, бюджет активных, взвешенная воронка, заполняемость времени) и четыре графика.
| Виджет | Что показывает | Данные |
|---|---|---|
| Воронка проектов | ступени от лида до сдачи; ширина — сколько проектов дошли до стадии (она и дальше, включая закрытые), число — сколько на ней сейчас, рядом плановый бюджет; «Ждём …» сведены в «Обсуждаем», потерянные и отменённые — строкой ниже; ступень ведёт в список проектов | funnel |
| Прибыль по проектам | столбики от нуля: прибыль вправо, убыток влево — пять лучших и пять худших проектов, от лучшего к худшему; подсказка — выручка и затраты | profit |
| Деньги по месяцам | приход и расход проведённых платежей столбиками, сальдо линией, за полгода; месяц без платежей — нули; ролям без отчёта о движении денег (403) виджет не показывается | GET /reports/cashflow |
| Перевозки и командировки | лента на 7/30 дней: полоса — от отправления до прибытия, выходные затенены, черта — сейчас; оборудование в пути — штрихованная полоса с числом единиц, прошедшие сроки — метка «сроки прошли» | logistics, equipment_in_transit |
Остальные блоки — заполняемость часов по отделам, загрузка людей, склады, логистика план/факт, оборудование в пути списком, риск недостоверности данных — во вкладке «Отчёты» → «Операции» (тот же GET /dashboard/executive). Графики нарисованы SVG и CSS на токенах цвета — тёмная тема без отдельной настройки.
Один запрос
GET /api/v1/dashboard/executive?horizon_days=7|30 — все блоки в одном ответе, чтобы главная загружалась одним запросом. horizon_days задаёт окно для логистики и командировок (по умолчанию 7, другое значение — 400).
Маршрут закрыт объектом reports_dashboards матрицы прав. Охват:
Охват reports_dashboards.read | Что попадает в выборку |
|---|---|
«все» (OWNER, OPS, FIN_DIR, FIN_ASSIST) | Вся компания, scope: "all" |
«свой» (TECH_DIR, DIV_HEAD, MANAGER) | Проекты своих дивизионов (основной отдел или project_departments), их логистика и командировки, сотрудники своих отделов; scope: "own" |
Склады не сужаются: остатки — общий ресурс компании.
Блоки и права
Блок, закрытый матрицей, не отдаётся, а его ключ попадает в hidden. Ограничение действует на сервере, а не только в интерфейсе (спец. п.11).
| Блок | Что считает | Нужен доступ | Ключ в hidden |
|---|---|---|---|
funnel.by_status | Проекты по статусам (без архивных) | — | — |
funnel.by_status[].planned_budget | Сумма бюджетов по статусу | client_budget | budgets |
funnel.by_city | Открытые проекты по городу площадки (locations.city, иначе название) | — | — |
portfolio.active_count | Проекты в approved, in_progress | — | — |
portfolio.active_budget_total | Сумма planned_budget активных проектов | client_budget | budgets |
portfolio.weighted_pipeline | Бюджет пресейла × вероятность закрытия (ручная или по статусу) | probability_weighted | weighted_pipeline |
profit.top / profit.bottom | 5 самых прибыльных и убыточных проектов в работе и после неё: фактическая выручка − фактические затраты | earnings_margin | profit |
equipment_in_transit | Операции в статусе in_transit: маршрут, плановое прибытие, количество | equipment_stock | equipment |
warehouses | Склады (locations.type = warehouse): позиции и количество оборудования по статусам | equipment_stock | equipment |
logistics.operations | Открытые операции в пути или с отправкой/прибытием в окне | — | — |
logistics.trips | Командировки в работе или с выездом в окне; в охвате «свой» — поездки своих отделов и собственные поездки пользователя | — | — |
logistics_costs | План/факт логистики за текущий месяц: операции по плановой отправке (только согласованные финансами, finance_status = approved — на проверке и отклонённые в бюджет не попадают), командировки по плановому выезду | costs | logistics_costs |
staff_load | 10 самых загруженных сотрудников месяца: часы / норма (APP_LABOR_MONTHLY_NORM_HOURS) | others_timesheets | staff_load |
time_fill | Заполняемость времени по отделам за прошлый месяц (пусто — за текущий по сегодня), светофор; период — в time_fill_period (from, to) | — | — |
data_risk | ТОП-5 проектов с риском недостоверности данных | — | — |
Заполняемость времени
Берётся из снимков KPI (kpi.ListDepartmentKPI, период — прошлый календарный месяц по часовому поясу компании APP_TIMEZONE). Если за прошлый месяц ни плана, ни часов (например, в первый месяц работы), блок берёт текущий месяц с первого числа по сегодня (period=current_month); какой период показан — в time_fill_period. Снимок не замораживается: при чтении он пересчитывается, если старше часа, а воркер KPI каждую ночь (в 01:00 по времени компании) заново считает вчера, 7 дней и прошлый месяц — часы, внесённые задним числом, попадают в показатель. Пересчёт заменяет снимки периода целиком.
С 2026-09-30 заполняемость считается по задачам, закрытым в периоде (tasks.completed_at, миграция 0163): план — их плановые часы, факт — все часы по этим задачам (в том числе соисполнителей и записанные раньше периода). План и факт относятся к исполнителю задачи и его отделу, поэтому строка отдела равна сумме строк его сотрудников. Раньше план брался по сроку задачи в периоде, а факт — по всем записям времени, и показатель сравнивал разные задачи: у задач без срока план пропадал, заполняемость выходила «—». Часы периода по задачам без плана и без задачи — отдельное поле unplanned_hours («Работа без плана»). completed_at ставит триггер при переходе в «Завершено» и снимает при выходе из него; старые завершённые задачи получили дату из журнала действий (последний переход в «Завершено»); без записи в журнале дата пустая и такая задача в KPI не входит. Заполняемость ограничена 9999,99 % (колонка NUMERIC(6,2)). Уровни светофора: зелёный > 95 %, жёлтый 85–95 %, красный < 85 %, none — нет закрытых задач с планом.
Риск недостоверности данных
Для проектов в approved, in_progress, done:
without_hours_percent— доля закрытых задач, по которым нет ни одной записи времени;unplanned_percent— доля задач без плановых часов (работа вне плана);score— среднее двух долей; проекты сортируются по нему.
Эти проекты — первые кандидаты на разбор на еженедельной встрече (ТЗ, «Финансовые риски»).
Динамика бюджета проекта
GET /api/v1/projects/:id/budget/dynamics — виджет «Динамика бюджета» в карточке проекта: план, накопленный факт по неделям и отклонение.
{
"project_id": 1,
"planned_revenue": 2500000.00,
"planned_costs": 2095000.00,
"actual_costs": 1395000.00,
"deviation": -700000.00,
"weeks": [{ "week_start": "2026-09-21", "actual_costs": 1395000.00, "cumulative_actual": 1395000.00 }]
}planned_costs/actual_costs— суммы плана и факта по статьям затрат проекта;deviation = actual − planned(положительное — перерасход).- Неделя статьи определяется по источнику: запись времени — дата записи; логистика — фактическое или плановое прибытие; командировка — фактическое возвращение или плановый выезд; ручная статья — дата создания.
- Доступ — как у сводки бюджета (раздел «Финансы» проекта);
planned_revenueскрывается безclient_budget.
Деньги
Все суммы дашборда, сводки GET /dashboard/overview и отчётов /reports/* — точный денежный тип money.Money, в JSON — число с копейками.
Архивные проекты в сводке GET /dashboard/overview и в отчётах /reports/* не участвуют (как в списке проектов по умолчанию). Плановая выручка, плановые затраты и плановая прибыль не учитывают проигранные (lost) и отменённые (cancelled) проекты; фактические суммы — по всем неархивным.