ADR-010: Версионность техкарт и метод расчёта себестоимости

Статус

proposed — два открытых вопроса из BR 1.9


Вопрос 1: Версионность техкарт

В чём проблема

Франшиза меняет рецепт пиццы: убрала оливки, добавила рукколу. Техкарта перезаписана. Теперь:

  • Себестоимость за прошлый месяц — считалась по старому рецепту (с оливками). Но техкарта уже новая (с рукколой). Если пересчитать — цифра изменится. Отчёт за прошлый месяц “поплывёт”.
  • Списание ингредиентов — вчера продали 10 пицц по старому рецепту (списали оливки). Сегодня рецепт новый. Если кто-то смотрит “почему списались оливки?” — в текущей техкарте оливок нет.
  • Аудит Роспотребнадзора — проверяющий спрашивает “по какому рецепту готовили в марте?“. Если техкарта перезаписана — ответа нет.

Рассмотренные варианты

A. Мутабельно (как каталог) + аудит-лог (ADR-008)

Техкарта перезаписывается. Аудит-лог фиксирует: “2026-04-15, убран ингредиент оливки, добавлена руккола”. История есть, но как diff-записи, не как полные снапшоты.

ПлюсМинус
Просто — одна запись в БДНельзя “восстановить” старый рецепт как целый объект
Аудит-лог уже планируется (ADR-008)Для отчётов нужно “восстановить” рецепт из цепочки дифов — сложно
Как в iiko (мутабельно)Роспотребнадзор: формально ТТК должна быть документом, не цепочкой правок

B. Версионность (как меню BR 1.11)

Каждое изменение = новая версия техкарты. Старые версии в архиве. Published-версия используется для списания.

ПлюсМинус
Полная история, можно посмотреть рецепт на любую датуСложнее: таблица версий, lifecycle
Отчёты за период — по версии, актуальной на тот моментOverhead для частых мелких правок
Роспотребнадзор: ТТК = конкретная версия документа
Откат к старому рецепту — одной кнопкой

C. Мутабельно + периодические снапшоты

Техкарта мутабельна. Но при каждом списании (продаже) в строке списания сохраняется snapshot рецепта (как снапшот цены в заказе).

ПлюсМинус
Каждое списание самодостаточноОгромный объём данных (снапшот рецепта × каждая продажа)
Не нужна версионность техкартыНельзя посмотреть “рецепт в марте” без анализа списаний

Рекомендация

Вариант A на MVP — мутабельно + аудит-лог. Как в iiko. Достаточно для операционной работы. Если понадобится полная версионность (Роспотребнадзор, детальная аналитика) — перейти к варианту B. Переход аддитивный (добавить таблицу версий), не рефакторинг.

Решение

Ожидает подтверждения.


Вопрос 2: Метод расчёта себестоимости

В чём проблема

Себестоимость блюда = сумма (количество_ингредиента × цена_ингредиента). Но какая цена ингредиента? Мука закупалась трижды по разным ценам:

ДатаЗакупкаЦена за кгОстаток после
1 марта50 кг60₽/кг50 кг
10 марта30 кг65₽/кг62 кг (12 осталось от первой + 30 новых + 20 со второй)
20 марта20 кг70₽/кг45 кг

Сегодня продали пиццу, в рецепте 0.2 кг муки. Себестоимость муки в этой пицце = 0.2 × ???.

Рассмотренные варианты

A. По последней закупке (Last Purchase Price)

Цена = 70₽/кг (последний акт прихода). Себестоимость муки = 0.2 × 70 = 14₽.

ПлюсМинус
Просто — одно поле last_priceНе отражает реальность: на складе ещё есть мука по 60₽
Легко реализоватьСкачки себестоимости при каждой закупке
БыстроМожет искажать маржу

B. Средневзвешенная (Weighted Average Cost)

Цена = (50×60 + 30×65 + 20×70) / (50+30+20) = 63.5₽/кг. Пересчитывается при каждом приходе. Себестоимость = 0.2 × 63.5 = 12.7₽.

ПлюсМинус
Сглаживает скачкиНужен пересчёт при каждом приходе
Отражает среднюю реальную стоимостьНе учитывает какая именно мука используется
Стандарт в общепитеЧуть сложнее
Так работает iiko

C. FIFO (First In, First Out)

Сначала списывается мука по 60₽ (она пришла первой), потом по 65₽, потом по 70₽. Себестоимость зависит от порядка.

ПлюсМинус
Максимально точноСложная реализация: нужно отслеживать партии
Бухгалтерски корректноНужна партионность на складе
Overhead для общепита (не промышленный склад)

Как в iiko

iiko использует средневзвешенную (Weighted Average). При каждом акте прихода пересчитывает среднюю цену: (старый_остаток × старая_цена + новый_приход × новая_цена) / (старый_остаток + новый_приход). Это стандарт для общепита.

Рекомендация

Вариант B — средневзвешенная. Стандарт отрасли, используется в iiko, баланс между точностью и простотой. FIFO — overkill для общепита. По последней закупке — слишком грубо.

Решение

Ожидает подтверждения.


Последствия

При выборе A (мутабельно) + B (средневзвешенная)

  • Техкарта — одна запись, перезаписывается. Аудит-лог фиксирует diff.
  • Себестоимость блюда пересчитывается при каждом акте прихода (изменении средней цены ингредиента).
  • Warehouse Service хранит average_cost per-ingredient per-store. Обновляется формулой при каждом приходе.
  • Простая реализация, как в iiko.

Риски

  • Мутабельные техкарты: при аудите Роспотребнадзора может не хватить diff-лога. Потребуется перейти к версионности.
  • Средневзвешенная: при резком скачке цен (инфляция) себестоимость реагирует с задержкой (старый дешёвый остаток “сглаживает”).

Ссылки