Исправлены баги витринных комплектов: резервы и валидация восстановления заказов
This commit is contained in:
@@ -732,76 +732,102 @@ def rollback_sale_on_status_change(sender, instance, created, **kwargs):
|
||||
f"converted_to_sale → {reservation_target_status}"
|
||||
)
|
||||
|
||||
# Витринные временные комплекты ВСЕГДА возвращаются в reserved (остаются на витрине)
|
||||
# Витринные временные комплекты: логика зависит от сценария
|
||||
if showcase_count > 0:
|
||||
for reservation in showcase_kit_reservations:
|
||||
reservation.status = 'reserved'
|
||||
# Очищаем ТОЛЬКО блокировки корзины при отмене заказа
|
||||
# НЕ трогаем order_item - он нужен для повторной продажи при возврате в completed
|
||||
reservation.cart_lock_expires_at = None
|
||||
reservation.locked_by_user = None
|
||||
reservation.cart_session_id = None
|
||||
# Не трогаем showcase и product_kit - они остаются привязанными
|
||||
# converted_at оставляем (для истории)
|
||||
reservation.save(update_fields=['status', 'cart_lock_expires_at', 'locked_by_user', 'cart_session_id'])
|
||||
|
||||
logger.info(
|
||||
f"✓ Обновлено {showcase_count} резервов витринных комплектов: "
|
||||
f"converted_to_sale → reserved (возвращены на витрину, блокировки сняты, order_item сохранён)"
|
||||
)
|
||||
if is_cancellation:
|
||||
# Сценарий Б: Отмена - возвращаем на витрину
|
||||
for reservation in showcase_kit_reservations:
|
||||
reservation.status = 'reserved'
|
||||
# КРИТИЧНО: Отвязываем резервы от заказа при отмене
|
||||
reservation.order_item = None
|
||||
# Очищаем блокировки корзины
|
||||
reservation.cart_lock_expires_at = None
|
||||
reservation.locked_by_user = None
|
||||
reservation.cart_session_id = None
|
||||
# showcase_item и product_kit остаются - букет на витрине
|
||||
# converted_at оставляем (для истории)
|
||||
reservation.save(update_fields=['status', 'order_item', 'cart_lock_expires_at', 'locked_by_user', 'cart_session_id'])
|
||||
|
||||
logger.info(
|
||||
f"✓ Обновлено {showcase_count} резервов витринных комплектов: "
|
||||
f"converted_to_sale → reserved (возвращены на витрину, отвязаны от заказа, блокировки сняты)"
|
||||
)
|
||||
else:
|
||||
# Сценарий А: Возврат к нейтральному - резервы ОСТАЮТСЯ в заказе
|
||||
for reservation in showcase_kit_reservations:
|
||||
reservation.status = 'reserved'
|
||||
# Очищаем ТОЛЬКО блокировки корзины
|
||||
# order_item НЕ ТРОГАЕМ - резерв остаётся за заказом!
|
||||
reservation.cart_lock_expires_at = None
|
||||
reservation.locked_by_user = None
|
||||
reservation.cart_session_id = None
|
||||
# converted_at оставляем (для истории)
|
||||
reservation.save(update_fields=['status', 'cart_lock_expires_at', 'locked_by_user', 'cart_session_id'])
|
||||
|
||||
logger.info(
|
||||
f"✓ Обновлено {showcase_count} резервов витринных комплектов: "
|
||||
f"converted_to_sale → reserved (остаются в заказе, блокировки сняты)"
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"⚠ Для заказа {instance.order_number} нет резервов в статусе 'converted_to_sale'"
|
||||
)
|
||||
|
||||
# === Возвращаем витринные экземпляры обратно на витрину ===
|
||||
from inventory.models import ShowcaseItem
|
||||
|
||||
# Находим все ShowcaseItem, проданные в рамках этого заказа
|
||||
showcase_items = ShowcaseItem.objects.filter(
|
||||
sold_order_item__order=instance,
|
||||
status='sold'
|
||||
)
|
||||
|
||||
showcase_items_count = showcase_items.count()
|
||||
|
||||
if showcase_items_count > 0:
|
||||
logger.info(
|
||||
f"🔄 Возвращаем {showcase_items_count} витринных экземпляров обратно на витрину..."
|
||||
# ТОЛЬКО при отмене (отрицательный статус)!
|
||||
if is_cancellation:
|
||||
from inventory.models import ShowcaseItem
|
||||
|
||||
# Находим все ShowcaseItem, проданные в рамках этого заказа
|
||||
showcase_items = ShowcaseItem.objects.filter(
|
||||
sold_order_item__order=instance,
|
||||
status='sold'
|
||||
)
|
||||
|
||||
# Возвращаем каждый экземпляр на витрину
|
||||
for item in showcase_items:
|
||||
item.status = 'available'
|
||||
item.sold_order_item = None
|
||||
item.sold_at = None
|
||||
# showcase и product_kit не трогаем - букет остаётся на той же витрине
|
||||
item.save(update_fields=['status', 'sold_order_item', 'sold_at', 'updated_at'])
|
||||
|
||||
# КРИТИЧНО: Восстанавливаем связь между ShowcaseItem и Reservation
|
||||
# Находим все резервы этого комплекта для данного OrderItem
|
||||
order_item = item.sold_order_item if hasattr(item, '_original_sold_order_item') else None
|
||||
if not order_item:
|
||||
# Пытаемся найти через заказ и product_kit
|
||||
order_items = instance.items.filter(product_kit=item.product_kit)
|
||||
if order_items.exists():
|
||||
order_item = order_items.first()
|
||||
|
||||
if order_item:
|
||||
# Восстанавливаем связь showcase_item в резервах
|
||||
reservations_updated = Reservation.objects.filter(
|
||||
order_item=order_item,
|
||||
product_kit=item.product_kit,
|
||||
status='reserved'
|
||||
).update(showcase_item=item)
|
||||
|
||||
if reservations_updated > 0:
|
||||
logger.debug(
|
||||
f" ✅ Восстановлена связь для {reservations_updated} резервов ShowcaseItem #{item.id}"
|
||||
)
|
||||
showcase_items_count = showcase_items.count()
|
||||
|
||||
if showcase_items_count > 0:
|
||||
logger.info(
|
||||
f"🔄 Возвращаем {showcase_items_count} витринных экземпляров обратно на витрину..."
|
||||
)
|
||||
|
||||
# Возвращаем каждый экземпляр на витрину
|
||||
for item in showcase_items:
|
||||
item.status = 'available'
|
||||
item.sold_order_item = None
|
||||
item.sold_at = None
|
||||
# showcase и product_kit не трогаем - букет остаётся на той же витрине
|
||||
item.save(update_fields=['status', 'sold_order_item', 'sold_at', 'updated_at'])
|
||||
|
||||
# КРИТИЧНО: Восстанавливаем связь между ShowcaseItem и Reservation
|
||||
# Находим все резервы этого комплекта для данного OrderItem
|
||||
order_item = item.sold_order_item if hasattr(item, '_original_sold_order_item') else None
|
||||
if not order_item:
|
||||
# Пытаемся найти через заказ и product_kit
|
||||
order_items = instance.items.filter(product_kit=item.product_kit)
|
||||
if order_items.exists():
|
||||
order_item = order_items.first()
|
||||
|
||||
if order_item:
|
||||
# Восстанавливаем связь showcase_item в резервах
|
||||
reservations_updated = Reservation.objects.filter(
|
||||
order_item=order_item,
|
||||
product_kit=item.product_kit,
|
||||
status='reserved'
|
||||
).update(showcase_item=item)
|
||||
|
||||
if reservations_updated > 0:
|
||||
logger.debug(
|
||||
f" ✅ Восстановлена связь для {reservations_updated} резервов ShowcaseItem #{item.id}"
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"✅ {showcase_items_count} витринных экземпляров вернулись на витрину: sold → available со связью с резервами"
|
||||
)
|
||||
else:
|
||||
# Сценарий А: Возврат к нейтральному - ShowcaseItem ОСТАЁТСЯ sold
|
||||
logger.info(
|
||||
f"✅ {showcase_items_count} витринных экземпляров вернулись на витрину: sold → available со связью с резервами"
|
||||
f"ℹ️ Сценарий А: Витринные экземпляры остаются в статусе 'sold' (заказ в нейтральном статусе)"
|
||||
)
|
||||
|
||||
# === Обновляем is_returned ===
|
||||
@@ -916,15 +942,16 @@ def release_reservations_on_cancellation(sender, instance, created, **kwargs):
|
||||
|
||||
if showcase_count > 0:
|
||||
# Для витринных комплектов очищаем блокировки корзины
|
||||
# НЕ трогаем order_item - он нужен для повторной продажи если статус вернётся в completed
|
||||
# КРИТИЧНО: Отвязываем резервы от заказа при отмене
|
||||
for reservation in showcase_kit_reservations:
|
||||
reservation.order_item = None
|
||||
reservation.cart_lock_expires_at = None
|
||||
reservation.locked_by_user = None
|
||||
reservation.cart_session_id = None
|
||||
reservation.save(update_fields=['cart_lock_expires_at', 'locked_by_user', 'cart_session_id'])
|
||||
reservation.save(update_fields=['order_item', 'cart_lock_expires_at', 'locked_by_user', 'cart_session_id'])
|
||||
|
||||
logger.info(
|
||||
f"ℹ️ Найдено {showcase_count} резервов витринных комплектов - остаются в reserved (на витрине, блокировки сняты, order_item сохранён)"
|
||||
f"ℹ️ Найдено {showcase_count} резервов витринных комплектов - остаются в reserved (на витрине, отвязаны от заказа, блокировки сняты)"
|
||||
)
|
||||
|
||||
if normal_count == 0 and showcase_count == 0:
|
||||
@@ -996,7 +1023,7 @@ def reserve_stock_on_uncancellation(sender, instance, created, **kwargs):
|
||||
return # Не было перехода от cancelled, выходим
|
||||
|
||||
# === Резервируем товар заново ===
|
||||
# Ищем резервы в статусе 'released'
|
||||
# Ищем резервы в статусе 'released' (обычные резервы)
|
||||
reservations = Reservation.objects.filter(
|
||||
order_item__order=instance,
|
||||
status='released'
|
||||
@@ -1023,6 +1050,52 @@ def reserve_stock_on_uncancellation(sender, instance, created, **kwargs):
|
||||
logger.debug(
|
||||
f"ℹ️ Для заказа {instance.order_number} нет резервов в статусе 'released'"
|
||||
)
|
||||
|
||||
# === Привязываем витринные резервы обратно к заказу ===
|
||||
# Витринные резервы остаются в статусе 'reserved' при отмене,
|
||||
# но отвязываются от order_item. При возврате нужно привязать их обратно.
|
||||
|
||||
# Находим все OrderItem витринных комплектов в этом заказе
|
||||
showcase_order_items = instance.items.filter(
|
||||
product_kit__is_temporary=True,
|
||||
product_kit__showcase__isnull=False
|
||||
).select_related('product_kit')
|
||||
|
||||
showcase_items_count = showcase_order_items.count()
|
||||
|
||||
if showcase_items_count > 0:
|
||||
logger.info(
|
||||
f"🔄 Найдено {showcase_items_count} витринных комплектов в заказе. "
|
||||
f"Привязываем резервы обратно к заказу..."
|
||||
)
|
||||
|
||||
for order_item in showcase_order_items:
|
||||
kit = order_item.product_kit
|
||||
|
||||
# Находим витринные резервы для этого комплекта
|
||||
# (они в статусе 'reserved', но order_item=None)
|
||||
showcase_reservations = Reservation.objects.filter(
|
||||
product_kit=kit,
|
||||
showcase__isnull=False,
|
||||
status='reserved',
|
||||
order_item__isnull=True
|
||||
)
|
||||
|
||||
if showcase_reservations.exists():
|
||||
# Привязываем резервы обратно к OrderItem
|
||||
updated_count = showcase_reservations.update(order_item=order_item)
|
||||
|
||||
logger.info(
|
||||
f" ✅ Витринный комплект '{kit.name}': привязано {updated_count} резервов к OrderItem #{order_item.id}"
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f" ⚠ Витринный комплект '{kit.name}': не найдено витринных резервов без order_item"
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"✅ Обработано {showcase_items_count} витринных комплектов"
|
||||
)
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=Order)
|
||||
|
||||
Reference in New Issue
Block a user