From 366ead7404546f9fa15c5e688fca58f606bd975f Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Mon, 5 Jan 2026 09:09:41 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=B1=D0=B0=D0=B3:=20ShowcaseItem=20=D1=82?= =?UTF-8?q?=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=B2=D0=BE=D0=B7=D0=B2=D1=80?= =?UTF-8?q?=D0=B0=D1=89=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20=D0=B2=20reserved?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D1=85=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=20=D0=B8=D0=B7=20=D0=BE=D1=82=D1=80=D0=B8=D1=86?= =?UTF-8?q?=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=D0=B3=D0=BE=20=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D1=82=D1=83=D1=81=D0=B0=20=D0=B2=20=D0=BD=D0=B5?= =?UTF-8?q?=D0=B9=D1=82=D1=80=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблема: - При отмене заказа (cancelled) ShowcaseItem переходил в 'available' - При возврате из cancelled в нейтральный статус (draft/pending) резервы привязывались обратно - НО ShowcaseItem оставался в 'available', что позволяло добавить букет в другой заказ - Итог: один физический букет в двух заказах Решение: - inventory/signals.py: в сигнале reserve_stock_on_uncancellation добавлена логика - При переходе cancelled → нейтральный: * Находим ShowcaseItem витринных комплектов в статусе 'available' * Вызываем return_to_reserved(order_item) для каждого экземпляра * ShowcaseItem: available → reserved (привязан к OrderItem) - Теперь букет корректно возвращается в резерв и недоступен на витрине Lifecycle при откате отмены: 1. cancelled: ShowcaseItem = available, резервы отвязаны 2. cancelled → draft/pending: ShowcaseItem = reserved, резервы привязаны 3. Букет остаётся за заказом, защищён от двойной продажи --- myproject/inventory/signals.py | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/myproject/inventory/signals.py b/myproject/inventory/signals.py index 3a1c18c..7021bfc 100644 --- a/myproject/inventory/signals.py +++ b/myproject/inventory/signals.py @@ -1190,6 +1190,41 @@ def reserve_stock_on_uncancellation(sender, instance, created, **kwargs): logger.info( f"✅ Обработано {showcase_items_count} витринных комплектов" ) + + # === Возвращаем ShowcaseItem из available обратно в reserved === + # При отмене (cancelled) ShowcaseItem переходит в 'available'. + # При возврате к нейтральному статусу нужно вернуть в 'reserved'. + from inventory.models import ShowcaseItem + + # Находим все ShowcaseItem которые были освобождены при отмене + # (у них sold_order_item сброшен в return_to_available, нужно найти через резервы) + for order_item in showcase_order_items: + kit = order_item.product_kit + + # Находим ShowcaseItem этого комплекта в статусе 'available' + # Их sold_order_item = None, поэтому ищем через product_kit + available_items = ShowcaseItem.objects.filter( + product_kit=kit, + status='available', + sold_order_item__isnull=True + ) + + if available_items.exists(): + logger.info( + f" 🔄 Найдено {available_items.count()} ShowcaseItem комплекта '{kit.name}' в статусе 'available'. " + f"Возвращаем в reserved..." + ) + + for item in available_items: + try: + item.return_to_reserved(order_item) + logger.info( + f" ✅ ShowcaseItem #{item.id}: available → reserved (привязан к OrderItem #{order_item.id})" + ) + except Exception as e: + logger.error( + f" ❌ Ошибка возврата ShowcaseItem #{item.id} в reserved: {e}" + ) @receiver(pre_delete, sender=Order)