Проблемы:
1. При продаже витринного комплекта через POS создавались дубликаты резервов
- reserve_stock_on_item_create создавал новые резервы для витринного комплекта
- Хотя резервы уже существовали от ShowcaseManager.reserve_kit_to_showcase
2. При переходе заказа в Completed создавались дубликаты Sale
- ShowcaseManager.sell_showcase_items создавал Sale
- Затем сигнал create_sale_on_order_completion создавал Sale повторно
3. При отмене заказа (Completed → Cancelled) терялась связь ShowcaseItem с резервами
- ShowcaseItem возвращался на витрину, но резервы теряли поле showcase_item
- При повторном переходе в Completed резервы дублировались
Исправления:
1. inventory/signals.py - reserve_stock_on_item_create (строки 165-180):
- Добавлена проверка витринного комплекта (is_temporary && showcase)
- Для витринных комплектов сигнал пропускает создание новых резервов
- Привязка существующих резервов происходит в update_reservation_on_item_change
2. inventory/signals.py - create_sale_on_order_completion (строки 346-365):
- Добавлена проверка уже обработанных резервов (status='converted_to_sale')
- Сигнал пропускает витринные резервы, уже обработанные ShowcaseManager
- Логирует информацию о пропущенных резервах
3. inventory/signals.py - rollback_sale_on_status_change (строки 746-774):
- При возврате ShowcaseItem на витрину восстанавливается связь с резервами
- Обновляется поле showcase_item в резервах через Reservation.objects.update()
- Логируется количество восстановленных связей
4. inventory/services/showcase_manager.py - sell_showcase_items (строки 201-206):
- Добавлена проверка статуса резерва перед созданием Sale
- Если резерв уже в 'converted_to_sale', он пропускается
- Защита от двойного списания одного резерва
Результат:
✅ Резервы создаются только один раз при размещении на витрине
✅ Sale создаются только один раз при продаже
✅ ShowcaseItem корректно возвращается на витрину со связью с резервами
✅ Остатки на складе корректные (60 → 55 после продажи, 60 после отмены)
✅ Нет дублирования при многократных переходах Completed ↔ Cancelled