Исправлен поиск ShowcaseItem при переходе cancelled → completed

Проблема:
- При отмене (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  (ИСПРАВЛЕНО!)

Защита от двойной продажи работает корректно.
This commit is contained in:
2026-01-05 09:30:00 +03:00
parent 6c497bbde3
commit 6095729409

View File

@@ -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}"