BR 1.14: Склад Phase 1 — остатки, приёмки, списания

Зависит от:

Контекст

Для расчёта себестоимости блюд нужны закупочные цены ингредиентов. Закупочные цены появляются из приёмок на склад. Склад также нужен для контроля остатков — знать сколько чего есть на каждой ТТ.

В YumaPos складской учёт включает: склады per-store, приёмки (акты прихода), списания, перемещения, инвентаризацию, производство. Phase 1 — минимум: склады + приёмки + списания + остатки.

Только бэкенд (Warehouse Service). Фронт — отдельная BR.


Требования

1. Склады (warehouses)

  • Один склад на торговую точку — создаётся автоматически при создании ТТ
  • Название склада = название ТТ
  • Нельзя создать вручную, нельзя удалить (привязан к жизненному циклу ТТ)
  • Склад принадлежит franchise_id + store_id

2. Складские остатки (stock balances)

  • Текущее количество каждого ингредиента на каждом складе
  • Обновляется автоматически при проводке документов (приёмка +, списание −)
  • Поля: warehouse_id, ingredient_id, current_quantity, unit_of_measure, last_updated
  • Остатки не могут быть отрицательными (валидация при списании)

3. Складские партии (stock batches)

  • Каждая приёмка создаёт партию с закупочной ценой
  • Поля: warehouse_id, ingredient_id, purchase_price, quantity, received_date, shelf_life_date (опционально)
  • При списании — FIFO (первая поступившая партия списывается первой)
  • Средневзвешенная цена = сумма(цена × количество) / общее количество по всем партиям с qty > 0

4. Акты приёмки (receipt acts)

Документ поступления ингредиентов на склад.

Жизненный цикл: draft → posted

  • draft — можно редактировать (добавлять/удалять строки, менять количества)
  • posted — зафиксирован, создаёт партии, обновляет остатки. Нельзя редактировать.

Поля документа:

  • Склад (warehouse_id)
  • Номер документа (автоинкремент per franchise)
  • Дата приёмки
  • Комментарий (опционально)
  • Статус (draft / posted)
  • Общая сумма (вычисляется из строк)

Строки документа:

  • Ингредиент (ingredient_id)
  • Количество
  • Единица измерения
  • Цена за единицу (закупочная)
  • Сумма строки (кол-во × цена)
  • Срок годности (опционально)

При проводке (post):

  1. Для каждой строки создать stock_batch (партия)
  2. Обновить stock_balance (+quantity)
  3. Пересчитать average_cost ингредиента на этом складе
  4. Заблокировать редактирование документа

Без поставщика — Phase 1 без справочника поставщиков. Просто “поступление на склад”.

5. Акты списания (write-off acts)

Документ списания ингредиентов со склада (порча, истечение срока, недостача).

Жизненный цикл: draft → posted

Поля документа:

  • Склад (warehouse_id)
  • Номер документа
  • Дата списания
  • Причина (текст, обязательно)
  • Статус (draft / posted)
  • Общая стоимость списания (вычисляется)

Строки документа:

  • Ингредиент (ingredient_id)
  • Количество к списанию
  • Себестоимость единицы (из средневзвешенной цены)
  • Стоимость строки

При проводке:

  1. Проверить что на складе достаточно остатков
  2. Списать по FIFO (с самых старых партий)
  3. Обновить stock_balance (−quantity)
  4. Зафиксировать стоимость списания

Валидация: нельзя списать больше чем есть на складе.

6. Средневзвешенная цена (average cost)

  • Per-ingredient per-warehouse
  • Формула: ∑(batch.purchase_price × batch.quantity) / ∑(batch.quantity) где batch.quantity > 0
  • Пересчитывается при каждой приёмке и списании
  • Используется для расчёта себестоимости в техкартах (CostCalculationService)

Бизнес-правила

  1. Склад = ТТ — один склад на ТТ, автоматическое создание
  2. Draft → Posted — документ проводится один раз, после этого неизменяем
  3. FIFO — при списании сначала расходуются самые старые партии
  4. Неотрицательные остатки — нельзя списать больше чем есть
  5. Средневзвешенная — цена считается по партиям с положительным количеством
  6. Единица измерения — при приёмке/списании должна совпадать с единицей ингредиента (или использовать unit_conversions)

Ролевой доступ

ДействиеФраншизаФранчайзиМенеджерКассир
Просмотр остатковВсе складыСвои складыСвой складНет
Создать приёмкуЛюбой складСвои складыСвой складНет
Провести приёмкуЛюбой складСвои складыСвой складНет
Создать списаниеЛюбой складСвои складыСвой складНет
Провести списаниеЛюбой складСвои складыСвой складНет
Просмотр средней ценыВсеСвоиСвойНет

Затронутые сервисы

СервисЧто меняется
Warehouse ServiceНовые таблицы: warehouses, stock_balances, stock_batches, receipt_acts, receipt_act_lines, write_off_acts, write_off_act_lines. Новые CRUD + post эндпоинты. Обновление CostCalculationService для average_cost.
Store ServiceПри создании ТТ — Kafka-событие store.created → Warehouse создаёт склад. Или синхронный вызов.

Что НЕ входит (Phase 2+)

  • Поставщики (справочник контрагентов)
  • Акты перемещения между складами
  • Инвентаризация (сверка факт vs учёт)
  • Акты производства (списание по техкарте при изготовлении)
  • Автоматическое списание при продаже (интеграция с Order Service)
  • Авто-стоп по остаткам (интеграция со стоп-листами)
  • Фронт-интерфейс складского учёта
  • Партионный учёт по срокам годности (алерты)
  • Возвраты поставщику

Ссылки

  • Техкарты — себестоимость зависит от закупочных цен
  • Warehouse Service
  • YumaPos: _reference/yumapos/inventory-items.md, acceptance-acts.md, write-off-acts.md