BUG-010 — Декомпозиция

Концепция

Полуфабрикат = ингредиент (в Warehouse) у которого есть своя техкарта. Не dish из Catalog.

Warehouse ingredients:
  Мука        — сырьё (без техкарты)
  Тесто       — полуфабрикат (ингредиент + техкарта: Мука 150г + Вода 80мл)
  Соус        — полуфабрикат (ингредиент + техкарта: Томаты 100г)

Catalog products:
  Пепперони (dish) — техкарта: Тесто + Соус + Моцарелла (всё из ingredients)

Код

erp-warehouse-service

  • Миграция: убрать semi_finished_product_id из recipe_items (если данных нет — просто дропнуть колонку)
  • RecipeItem entity: убрать semiFinishedProductId
  • CreateRecipeItemRequest: убрать semiFinishedProductId, сделать ingredientId обязательным (NOT NULL)
  • RecipeItemResponse: убрать semiFinishedProductId, semiFinishedProductName, itemType. Оставить ingredientId, ingredientName
  • RecipeItemService: убрать логику semi_finished, lookup только из IngredientRepository
  • TechCardService: убрать cross-service lookup для полуфабрикатов
  • CostCalculationService: себестоимость полуфабриката = рекурсивный расчёт через техкарту ингредиента (всё локально в Warehouse)
  • Техкарты для ингредиентов: tech_cards уже поддерживает product_id — нужно добавить ingredient_id (nullable) как альтернативу. Техкарта привязана либо к product (dish), либо к ingredient (полуфабрикат).
  • Миграция: добавить ingredient_id (nullable FK → ingredients) в tech_cards, сделать product_id nullable. CHECK: одно из двух NOT NULL.
  • Обновить TechCardController: эндпоинт GET /tech-cards?ingredient_id=... для поиска техкарт ингредиентов
  • Обновить POST /tech-cards: принимать ingredient_id вместо product_id при создании техкарты ингредиента
  • Валидация циклических ссылок: ингредиент A → техкарта содержит ингредиент B → техкарта содержит ингредиент A — запретить

erp-admin (web)

  • ProductViewPage.tsx — модалка “Добавить ингредиент”: убрать поиск catalogApi.getProducts(type=dish), оставить только warehouseApi.getIngredients()
  • Убрать selectedIngredientType state, убрать бейджи “Ингредиент”/“Полуфабрикат”
  • addRecipeItem: всегда отправлять ingredient_id (не semi_finished_product_id)
  • Таблица ингредиентов: убрать колонку “Тип” (item_type)
  • shared types: убрать semi_finished_product_id, item_type из RecipeItem и request

erp-admin (web) — техкарта ингредиента

  • На странице просмотра ингредиента (пока inline в модалке) — возможность создать техкарту для ингредиента (кнопка “Создать техкарту” если ингредиент — полуфабрикат)
  • Или: в модалке добавления ингредиента — пометка “Есть техкарта” у составных ингредиентов

Спеки

  • 08-Specs/Админка Франшизы/Техкарты.md — убрать semi_finished_product_id, обновить: полуфабрикат = ингредиент с техкартой
  • 03-Services/Warehouse Service/API.md — убрать semi_finished из request/response, добавить ingredient_id в tech_cards
  • 03-Services/Warehouse Service/Data Model.md — обновить recipe_items (только ingredient_id), tech_cards (+ ingredient_id)
  • 09-Frontend Specs/Админка Франшизы/Каталог — Товары.md — убрать dish из модалки, убрать item_type