На главной странице сверху висят четыре цифры — это сводный финансовый портрет текущего пула рекомендаций. Ниже разбираем, как считается каждая, из каких полей в МойСклад берутся данные, и где формула делает упрощения, которые имеет смысл уточнить со временем.
Сколько денег компания теряет за неделю прямо сейчас из-за дефицита товара — в сумме по всем 1011 pending-рекомендациям к закупке.
Логика: если на складе осталось столько, что хватит на неделю — упущения нет. Если нет (например, средняя продажа 3 шт/день, а на складе 5 шт.) — за неделю мы могли продать 21 шт., но продадим только 5; разницу × цену продажи и теряем.
ср_продажи_день берётся
из витрины marts.velocity.avg_daily, которая считается как
(отгрузки 90д − возвраты 90д) / 90. Это уже чистый сигнал спроса.
Сумма рекомендованных закупок в штуках по всем pending-строкам.
Логика: на каждом SKU стремимся к запасу на 60 дней вперёд. Если на складе уже больше — рекомендуем 0 (и SKU не попадает в список). 60 дней выбраны как разумная цель: достаточно, чтобы не перезаказывать каждую неделю, и не слишком много, чтобы не омертвлять капитал в неликвиде.
marts.recommendations.qty_recommended). На главной странице её можно
уменьшить — закупщик может ввести своё число при утверждении (кнопка «±»).
Сколько денег нужно потратить, чтобы закрыть все pending-рекомендации по закупочным ценам.
sale_price × 0.3 как грубую оценку
(наценка ~3× для plus-size, значение уточнено клиентом 17.05.2026). После того как клиент дозальёт
реальные закупки в карточки товаров, эта цифра станет точной.
Сколько чистых денег принесёт заказ, если всё закупленное будет распродано по цене продажи (валовая маржа без учёта операционных расходов).
Потенциальная выручка по этому пулу: 25 457 025 ₽.
Затраты: 6 583 850 ₽.
Маржа: 18 873 175 ₽.
Откуда берутся исходные значения:
marts.velocity.avg_daily = qty_90d / 90, где qty_90d —
отгрузки за 90 дней минус возвраты за 90 дней. Источник: staging.sales_position (поднимается из
raw.demand и raw.salesreturn МойСклад).Остаток − Резерв + Ожидание (в пути).marts.stock_current.available
(= quantity − reserve) плюс in_transit_total по всем складам.
Источник: МойСклад API report/stock/bystore. Обновляется при каждом ETL-проходе.доступно = 0 − 0 + N. Если N покрывает 60 дней спроса по текущим
продажам, кол-во к закупке = 0 (заказывать не нужно, всё уже в пути).
В обосновании строки тогда видно «в пути N шт».marts.product_master.sale_price = первая позиция массива salePrices
из карточки товара МойСклад, делённая на 100 (API возвращает в копейках).marts.product_master.buy_price = buyPrice.value из карточки, делённая на 100.
Заполнена не у всех SKU — там, где пусто, используем sale_price × 0.3.marts.recommendations.qty_recommended = max(0, ⌈avg_daily × 60⌉ − available_total).marts.velocity.abc_class — ABC по выручке за 90 дней (80/15/5).marts.velocity.xyz_class — XYZ по коэф. вариации продаж (≤0.5 → X, ≤1.0 → Y, >1.0 → Z).Скоркард-агрегаты вычисляются на лету при каждом открытии главной страницы
(SQL-запрос в api/main.py, функция index). Это значит, что любая утверждённая
или отклонённая рекомендация сразу пересчитает все четыре числа — в реальном времени.
Витрины (marts.*), на которых строится этот расчёт, перестраиваются ETL-задачей —
каждые 30 минут инкрементально и по запросу полностью. Свежесть данных написана в шапке главной
страницы — «Последний ETL: …».