From 609572940971c3a84f3379d3f01f5d754132d667 Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Mon, 5 Jan 2026 09:30:00 +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=BF=D0=BE=D0=B8=D1=81=D0=BA=20ShowcaseItem=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D1=85=D0=BE=D0=B4?= =?UTF-8?q?=D0=B5=20cancelled=20=E2=86=92=20completed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблема: - При отмене (cancelled) метод return_to_available() сбрасывает sold_order_item = None - При переходе cancelled → completed поиск ShowcaseItem по sold_order_item__order не находит букеты - Букеты оставались в статусе 'available' вместо 'sold' Решение: - inventory/signals.py: в сигнале create_sale_on_order_completion изменена логика поиска - Разделён поиск на два этапа: 1. Поиск по sold_order_item для букетов в 'reserved' (обычный flow) 2. Поиск по product_kit для букетов в 'available' (переход из cancelled) - Для букетов в 'available': ищем через product_kit + status='available' + sold_order_item__isnull=True - Вызываем mark_sold(order_item) для каждого найденного букета - Букет корректно переходит available → sold и привязывается к OrderItem Flow теперь работает: 1. draft → completed: ShowcaseItem reserved → sold ✅ 2. cancelled → completed: ShowcaseItem available → sold ✅ (ИСПРАВЛЕНО!) Защита от двойной продажи работает корректно. --- myproject/inventory/signals.py | 59 ++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/myproject/inventory/signals.py b/myproject/inventory/signals.py index 611fbd8..92999fc 100644 --- a/myproject/inventory/signals.py +++ b/myproject/inventory/signals.py @@ -500,33 +500,64 @@ def create_sale_on_order_completion(sender, instance, created, **kwargs): # - в статусе 'available' (переход из отмены: cancelled → completed) from inventory.models import ShowcaseItem + # Сначала ищем по sold_order_item (для букетов в reserved) showcase_items_to_finalize = ShowcaseItem.objects.filter( sold_order_item__order=instance, - status__in=['reserved', 'available'] + status='reserved' ) finalized_count = 0 for showcase_item in showcase_items_to_finalize: try: - if showcase_item.status == 'reserved': - # Обычный flow: reserved → sold - showcase_item.mark_sold_from_reserved() - logger.info( - f"✓ Витринный экземпляр #{showcase_item.id} финализирован: reserved → sold" - ) - elif showcase_item.status == 'available': - # Переход из отмены: available → sold (минуя reserved) - # Используем mark_sold() который работает с available - showcase_item.mark_sold(showcase_item.sold_order_item) - logger.info( - f"✓ Витринный экземпляр #{showcase_item.id} финализирован: available → sold (из отмены)" - ) + # Обычный flow: reserved → sold + showcase_item.mark_sold_from_reserved() finalized_count += 1 + logger.info( + f"✓ Витринный экземпляр #{showcase_item.id} финализирован: reserved → sold" + ) except Exception as e: logger.error( f"❌ Ошибка финализации ShowcaseItem #{showcase_item.id}: {e}" ) + # Теперь ищем букеты в available (переход из cancelled) + # При отмене sold_order_item сбрасывается, поэтому ищем через product_kit + showcase_order_items = instance.items.filter( + product_kit__is_temporary=True, + product_kit__showcase__isnull=False + ).select_related('product_kit') + + 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"Финализируем: available → sold (из отмены)..." + ) + + for item in available_items: + try: + # Переход из отмены: available → sold (минуя reserved) + # Используем mark_sold() который работает с available + item.mark_sold(order_item) + finalized_count += 1 + logger.info( + f" ✅ ShowcaseItem #{item.id}: available → sold (привязан к OrderItem #{order_item.id})" + ) + except Exception as e: + logger.error( + f" ❌ Ошибка финализации ShowcaseItem #{item.id}: {e}" + ) + if finalized_count > 0: logger.info( f"🎉 Финализировано {finalized_count} витринных экземпляров для заказа {instance.order_number}"