BUG-024: 500 при удалении ингредиента-полуфабриката с техкартой
Описание
При попытке удалить ингредиент, у которого есть собственная техкарта (ингредиент-полуфабрикат), бэкенд возвращает 500 INTERNAL_ERROR вместо осмысленного ответа.
Причина: Postgres блокирует удаление из-за FK fk_tech_cards_ingredient_id (tech_cards.ingredient_id с ON DELETE RESTRICT). Сервис не обрабатывает ConstraintViolationException и отдаёт общий 500.
Визуально для пользователя: кнопка «Удалить» на карточке ингредиента → INTERNAL_ERROR без объяснений. В UI нет способа понять что именно блокирует удаление (своя техкарта у ингредиента) и нет способа её удалить.
Шаги воспроизведения
- Создать ингредиент (например, «Лаваш»)
- Для этого ингредиента создать техкарту (полуфабрикат: Лаваш = Мука + Вода + …)
- Открыть карточку ингредиента
- Нажать «Удалить»
Ожидаемое поведение
Вариант 1 (рекомендуется): удаление ингредиента каскадно удаляет его собственную техкарту и её recipe_items — техкарта принадлежит ингредиенту. Всё в одной транзакции.
Вариант 2: сервис отвечает 409 CONFLICT с понятным сообщением: «У ингредиента есть техкарта “Лаваш-полуфабрикат”. Удалите техкарту сначала.» UI выводит это сообщение и кнопку «Удалить техкарту».
Фактическое поведение
DELETE /api/v1/admin/warehouse/ingredients/{id}
→ 500 Internal Server Error
{"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}}
В логах warehouse-service:
ConstraintViolationException: ERROR: update or delete on table "ingredients"
violates foreign key constraint "fk_tech_cards_ingredient_id" on table "tech_cards"
Detail: Key (id)=(7f0128f1-...) is still referenced from table "tech_cards".
Затронутые сервисы
- Warehouse Service —
IngredientService.delete()не обрабатывает случай «у ингредиента есть своя техкарта», эксепшен из БД прокидывается наружу как 500. - Admin Franchise (web) — карточка ингредиента не знает о связанной техкарте, нет способа её удалить/перенаправить пользователя.
Корневая причина
Семантическая: у ингредиента-полуфабриката есть собственная техкарта (tech_cards.ingredient_id IS NOT NULL AND product_id IS NULL, разрешено check-constraint’ом chk_tech_cards_product_or_ingredient). FK на ingredients стоит с ON DELETE RESTRICT, и в слое Hibernate IngredientService.delete() не проверяет наличие привязанных техкарт перед физическим удалением.
Воркэраунд (сделано вручную)
Удалена цепочка в warehouse_db:
DELETE FROM recipe_items WHERE tech_card_id = 'c8d3349b-2e5d-4ba5-8382-66a3eeb23bba';
DELETE FROM tech_cards WHERE id = 'c8d3349b-2e5d-4ba5-8382-66a3eeb23bba';
DELETE FROM ingredients WHERE id = '7f0128f1-9c9f-4651-96eb-3f010367ad26';