Методика расчёта в прогнозах
В рамках аналитики для Дарксторов и Вайтсторов рассчитываются разли
1. Совокупное отклонение плана и прогноза
Почасовые величины
Для каждого часа h = 0..23:
-
Прогноз (в тултипе «прогноз»):
stat = round( cnt_прогноза × extraNorm, 1 )
гдеcnt— изforecastStat[restaurant][date][hour]. -
План / факт (вторая колонка):
plan = round( sum_qty по часу, 1 ), источник зависит от даты:-
будущее: план из смен сборщиков — часы смен × скорость пользователя (
userSpeedAvgили средняя$speed); -
прошлое / сегодня: факт из
ttStat—sum_qtyза час.
-
-
Дельта по часу:
-
прошлое/сегодня:
delta = round(plan − stat, 1)(факт минус прогноз); -
будущее:
delta = round((stat − plan) / speed)— нехватка/избыток в чел.-часах.
-
Дневная совокупная дельта (число на карточке)
ordersDayDeltaRaw = round( round(Σ plan, 1) − round(Σ stat, 1) )
Показывается |ordersDayDeltaRaw| в компактном формате. Знак влияет на цвет фона (>0 — danger, <0 — warning).
Процент отклонения (sMAPE)
По часу:
smape_h = 2 × (plan − stat) / (plan + stat)
«Норма» по часу в прошлом: |delta| ≤ 3 или |smape_h| < 0.15 (15%). Иначе err: недопрогноз / перепрогноз.
По дню (фон карточки, не отдельное число в UI):
daySmape = 2 × (Σplan − Σstat) / (Σplan + Σstat) // если Σplan + Σstat > 0
Зелёный фон, если есть прогноз хотя бы в одном часу (stat > 0) и |daySmape| < 0.15.
В prevDayPlaceTransform.jsonata та же логика явно названа $daySmape; в актуальном dayPlaceTransform формула встроена в $buildOrdersContentColor() без отдельной переменной.
Важно: ordersDayDeltaRaw — это разница сумм в заказах (шт.), а не процент. Процент — только sMAPE для подсветки и почасовых статусов.
2) Выручка
Формула в UI:
dayRevenue = statDayNumber(revenueStat)
statDayNumber берёт число по place.id и дате ($dateStr, запасной ключ $dateStatStr).
Откуда данные:
revenueStat[restaurant][YYYY-MM-DD] = Σ value // time_series type 42 за день
Процент отклонения: в dayPlaceTransform нет sMAPE/плана для выручки — только факт из API; при отсутствии числа показывается «—».
3) Продуктивность
Формула:
dayEmployeesCount = count(distinct userId) по сменам с all = 1
dayProductivity =
если dayRevenue != null:
если dayEmployeesCount \> 0 → dayRevenue / dayEmployeesCount
иначе → 0
иначе → null → в UI «—»
Смысл: выручка за день / число сотрудников на смене
Процент отклонения: нет.
4) Средний заработок
Формула в UI:
dayAvgEarnings = statDayNumber(avgEarningsStat)
Откуда данные:
avgEarningsStat[restaurant][YYYY-MM-DD] = Σ value // time_series type 100
В биндинге поле суммируется ($sum(avgEarnings)), в интерфейсе подпись «Средний заработок» / «Сред. заработок» — это готовое дневное значение из ряда 100, без дополнительного деления в Jsonata.
Процент отклонения: нет.
5) Трудоёмкость — факт / прогноз
Прогноз (чел.-ч)
workHoursByDay = hoursStat[ place.id ][dateStr]
workHoursForecastHours = round( 0.9 × [ h53 + h52, h52 ][0] )
Берётся h53+h52, если есть h53, иначе h52; затем 90% и математическое округление.
Типы 52/53 в time_series — прогнозные часы трудоёмкости.
Факт (чел.-ч)
workHoursFactDuration = Σ duration по сменам дня ($total.duration), иначе 0
В тултипе при наличии stat по полям h51/c51:
факт_часы = [ h51 + c51, h51 ][0]
если нет stat — round(workHoursFactDuration) из смен.
Тип 51 — фактические часы в ряде.
Дельта (бейдж на карточке)
workHoursDelta = workHoursForecastHours − round(workHoursFactDuration)
Отображение: +N / -N / галочка при 0; цвет по порогам (>0, −5..0, ≤−5). Это разница в часах, не процент.
Процент отклонения (sMAPE): для трудоёмкости не используется.