BR 1.7: Каталог — товары и категории
Контекст
ЮЛ (BR 1.1), ТТ (BR 1.5), сотрудники (BR 1.4), авторизация (BR 1.3) — готовы. Каталог — последний обязательный блок Фазы 1. Без него невозможно запустить каталог на сайте/POS.
Каталог — единый справочник товаров и категорий франшизы. Управляется только франшизой. К блюдам каталога привязаны техкарты (BR 1.9, живут в Warehouse Service).
Двухуровневое версионирование
Товары версионные — каждое изменение товара (название, цена, описание, техкарта) создаёт новую версию товара:
Маргарита@v1 → Маргарита@v2 (изменилась техкарта)
Капучино@v1 → (не менялся, остаётся v1)
Каталог = версионный список ссылок на конкретные версии товаров:
Каталог v1 (archived): [Маргарита@v1, Капучино@v1, Тирамису@v1]
Каталог v2 (published): [Маргарита@v2, Капучино@v1, Тирамису@v1, Латте@v1]
Каталог v3 (draft): [Маргарита@v2, Капучино@v1, Тирамису@v2, Латте@v1]
Статусы каталога: draft → published → archived
- Одна published версия в любой момент
- При публикации draft → published, старая published → archived
- Архивные версии сохраняются для истории
- Published каталог “заморожен” — содержит конкретные версии товаров, изменение товара не ломает published
Механика изменения:
- Товар изменился (например, обновилась техкарта) → создаётся новая версия товара
- Автоматически создаётся draft каталога (если его нет) с обновлённой ссылкой на новую версию товара
- Ссылки на неизменённые товары остаются прежними
- Франшиза публикует draft → новый published каталог
Меню для клиента — вычисляется на лету (не отдельная сущность):
Каталог (published версия, конкретные версии товаров + техкарты)
+ Прейскурант (дефолтный или per-ТТ)
− Стоп-листы (per-ТТ)
− Проверка складских остатков (per-ТТ)
= Финальное меню для клиента
На MVP — вычисляется при каждом запросе. В целевом варианте — вычисляется и кэшируется в Redis.
Фронт: Админка Франшизы (erp-admin).
Порядок реализации BR каталога
1.7 Каталог (эта BR) ← фундамент (с версионированием)
├── 1.8 Модификаторы ← дополняет товары
├── 1.9 Прейскуранты ← цены (дефолтный + per-ТТ)
├── 1.10 Стоп-листы ← per-ТТ скрытие позиций
└── 1.12 Техкарты ← рецептуры (Warehouse Service)
Что нужно
1. Категории
Франшиза создаёт дерево категорий для структурирования товаров.
Поля категории:
- Название (обязательное)
- Родительская категория (необязательная — null = корневая)
- Порядок отображения (число, для сортировки внутри уровня)
- Активность (вкл/выкл — неактивная скрыта от клиентов, видна в админке)
Иерархия:
- Неограниченная вложенность (adjacency list с
parent_id, как в iiko) - На практике 2-3 уровня: “Еда” → “Горячее” → “Пицца”
- Товар привязывается к любой категории любого уровня
CRUD:
- Создание (с выбором родителя или без)
- Редактирование (название, порядок, активность, смена родителя)
- Удаление — блокируется если есть дочерние категории (
CATEGORY_HAS_CHILDREN) или привязанные товары (CATEGORY_HAS_PRODUCTS). Сначала перенести/удалить содержимое. Каскадное удаление не поддерживается (как в iiko) - Деактивация — каскадная: при отключении родительской категории все дочерние и их товары автоматически деактивируются. Обратная активация — поштучно или каскадно (выбор в UI)
Отображение в админке:
- Дерево с произвольной глубиной (рекурсивный рендеринг)
- На будущее: drag-and-drop для порядка. На MVP — числовое поле
2. Товары
Франшиза создаёт единый каталог товаров для всей сети. Каждый товар версионный — изменение создаёт новую версию.
Поля товара:
- Название (обязательное, уникальное в рамках каталога франшизы)
- Описание (текстовое, необязательное)
- Тип: Блюдо (dish) / Продукт (good) / Ингредиент (ingredient) (enum, ADR-009). dish — списание по техкарте, good — 1:1, ingredient — складская позиция
- Базовая цена (обязательная, >= 0 рублей, допускается 0 для бесплатных добавок). Прейскуранты могут перекрывать — BR 1.10
- Единица измерения (select): шт, кг, г, л, мл, порция
- Категория (обязательная)
- Статус: active / inactive (default active)
- Версия (автоинкремент, системное поле)
Версионность товара:
- Редактирование любого поля → создаётся новая версия товара (старая сохраняется)
- Изменение техкарты (BR 1.9) → тоже новая версия товара
- Каталог ссылается на конкретную версию товара → published каталог “заморожен”
Что НЕ входит:
- Фото (S3) — Phase 2
- Штрихкод, КБЖУ, аллергены — Phase 2
- Время приготовления — Phase 2
- Модификаторы — BR 1.8
3. Список товаров
- Таблица: название, категория, тип, цена, ед. изм., статус
- Поиск по названию
- Фильтры: категория, тип, статус
- Пагинация (20 на страницу)
4. Карточка товара
- Просмотр: все поля
- Создание: форма с обязательными полями + выбор категории
- Редактирование: все поля, включая смену категории
5. Удаление товара
- Soft delete (помечается
deleted_at, скрывается из общего списка) - Удалённые товары доступны в отдельной вкладке/выпадашке “Удалённые” с кнопкой “Восстановить”
- Без дополнительных проверок на MVP
6. Ролевой доступ
| Роль | Что может |
|---|---|
| Франшиза | CRUD всех товаров и категорий |
| Франчайзи | Только просмотр (видит только разрешённые для своих ТТ — BR 1.9) |
| Менеджер ТТ | Только просмотр |
| Кассир | Нет доступа в админке |
Структура фронта (Админка Франшизы)
Sidebar:
├── ...
├── Каталог
│ ├── /catalog/products — Список товаров
│ ├── /catalog/products/new — Создание товара
│ ├── /catalog/products/:id — Карточка товара
│ ├── /catalog/products/:id/edit — Редактирование товара
│ └── /catalog/categories — Дерево категорий (CRUD)
├── ...
Модульная структура sidebar — будущие разделы (Модификаторы, Прейскуранты, Стоп-листы) добавляются как отдельные пункты без рефакторинга.
Что НЕ входит (отложено)
- Фото товаров (S3) — Phase 2
- КБЖУ, аллергены, штрихкод — Phase 2
- Модификаторы — BR 1.8
- Прейскуранты — BR 1.10
- Стоп-листы — BR 1.10
- Техкарты — BR 1.9
- Фото товаров (S3) — Phase 2
- КБЖУ, аллергены, штрихкод — Phase 2
- Допродажи — Phase 2+
Решения
- Двухуровневое версионирование — товары версионные (каждое изменение = новая версия). Каталог = версионный список ссылок на конкретные версии товаров. Статусы каталога: draft → published → archived. При изменении товара → новая версия товара → draft каталога с обновлённой ссылкой. Неизменённые товары — ссылки те же.
- Иерархия категорий — неограниченная вложенность (adjacency list). Удаление — только пустые “листья” (как в iiko)
- Деактивация категорий — каскадная вниз, активация — поштучно или каскадно
- Уникальность названий — название товара уникально в рамках каталога
- Цена — базовая в товаре (>= 0₽), прейскурант перекрывает (BR 1.9)
- Тип товара — enum: dish / good / ingredient (ADR-009). dish — списание по техкарте, good — 1:1, ingredient — складская позиция
- Ед. измерения — select: шт, кг, г, л, мл, порция
- Soft delete — удалённые товары в отдельной вкладке с возможностью восстановления
- Меню = вычисляемое — не отдельная сущность.
BR 1.11 отменена.Меню вычисляется из каталога + прейскурант − стоп-листы − складские остатки. - Техкарты — привязаны к блюдам каталога по product_id, живут в Warehouse Service (BR 1.9)
Зависимости
- Catalog Service (:3004) — пустой, нужно создать
- Warehouse Service (:3008) — техкарты привязаны к блюдам (BR 1.9)
- Store Service — прейскуранты per-ТТ (BR 1.9), стоп-листы per-ТТ (BR 1.10)