diff --git a/myproject/inventory/services/showcase_manager.py b/myproject/inventory/services/showcase_manager.py index 1b4f75c..201c916 100644 --- a/myproject/inventory/services/showcase_manager.py +++ b/myproject/inventory/services/showcase_manager.py @@ -397,10 +397,11 @@ class ShowcaseManager: } # Берём только актуальные экземпляры на витрине + # (available, in_cart, reserved — все физически присутствующие) active_items = ShowcaseItem.objects.filter( showcase=showcase, product_kit=product_kit, - status__in=['available', 'in_cart'], + status__in=['available', 'in_cart', 'reserved'], ) item_count = active_items.count() @@ -488,7 +489,7 @@ class ShowcaseManager: active_items = ShowcaseItem.objects.filter( showcase=showcase, product_kit=product_kit, - status__in=['available', 'in_cart'], + status__in=['available', 'in_cart', 'reserved'], ) if not active_items.exists(): diff --git a/myproject/inventory/signals.py b/myproject/inventory/signals.py index 8f87147..3a1c18c 100644 --- a/myproject/inventory/signals.py +++ b/myproject/inventory/signals.py @@ -494,6 +494,33 @@ def create_sale_on_order_completion(sender, instance, created, **kwargs): f"✓ Обновлено {updated_count} резервов для заказа {instance.order_number}: reserved → converted_to_sale" ) + # === Финализация витринных экземпляров: reserved → sold === + # Находим все витринные комплекты в этом заказе, которые в статусе reserved + from inventory.models import ShowcaseItem + + showcase_items_to_finalize = ShowcaseItem.objects.filter( + sold_order_item__order=instance, + status='reserved' + ) + + finalized_count = 0 + for showcase_item in showcase_items_to_finalize: + try: + 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}" + ) + + if finalized_count > 0: + logger.info( + f"🎉 Финализировано {finalized_count} витринных экземпляров для заказа {instance.order_number}" + ) + logger.info( f"🎉 Заказ {instance.order_number} успешно обработан: создано {len(sales_created)} Sale, " f"обновлено {reservations_to_update.count() if reservations_to_update.exists() else 0} резервов" @@ -838,10 +865,37 @@ def rollback_sale_on_status_change(sender, instance, created, **kwargs): f"✅ {showcase_items_count} витринных экземпляров вернулись на витрину: sold → available со связью с резервами" ) else: - # Сценарий А: Возврат к нейтральному - ShowcaseItem ОСТАЁТСЯ sold - logger.info( - f"ℹ️ Сценарий А: Витринные экземпляры остаются в статусе 'sold' (заказ в нейтральном статусе)" + # Сценарий А: Возврат к нейтральному - ShowcaseItem sold → reserved + from inventory.models import ShowcaseItem + + # Находим все ShowcaseItem в статусе 'sold' для этого заказа + showcase_items_to_unreserve = ShowcaseItem.objects.filter( + sold_order_item__order=instance, + status='sold' ) + + unreserved_count = 0 + for showcase_item in showcase_items_to_unreserve: + try: + # Возвращаем в reserved (букет остаётся занят под заказ) + showcase_item.return_to_reserved(showcase_item.sold_order_item) + unreserved_count += 1 + logger.info( + f"✓ Витринный экземпляр #{showcase_item.id} возвращён в резерв: sold → reserved" + ) + except Exception as e: + logger.error( + f"❌ Ошибка возврата ShowcaseItem #{showcase_item.id} в reserved: {e}" + ) + + if unreserved_count > 0: + logger.info( + f"🔄 {unreserved_count} витринных экземпляров возвращено в резерв: sold → reserved (заказ в нейтральном статусе)" + ) + else: + logger.info( + f"ℹ️ Сценарий А: Нет витринных экземпляров для возврата в reserved" + ) # === Обновляем is_returned === # Используем единую функцию для обновления флага на основе фактического состояния @@ -972,6 +1026,33 @@ def release_reservations_on_cancellation(sender, instance, created, **kwargs): f"ℹ️ Для заказа {instance.order_number} нет резервов в статусе 'reserved'" ) + # === Освобождаем ShowcaseItem при отмене: reserved/sold → available === + from inventory.models import ShowcaseItem + + # Находим все ShowcaseItem для этого заказа в статусах reserved или sold + showcase_items_to_release = ShowcaseItem.objects.filter( + sold_order_item__order=instance, + status__in=['reserved', 'sold'] + ) + + released_showcase_count = 0 + for showcase_item in showcase_items_to_release: + try: + showcase_item.return_to_available() + released_showcase_count += 1 + logger.info( + f"✓ Витринный экземпляр #{showcase_item.id} освобождён: {showcase_item.status} → available" + ) + except Exception as e: + logger.error( + f"❌ Ошибка освобождения ShowcaseItem #{showcase_item.id}: {e}" + ) + + if released_showcase_count > 0: + logger.info( + f"🎉 {released_showcase_count} витринных экземпляров освобождено и возвращено на витрину при отмене заказа" + ) + # === Обновляем is_returned === # Используем единую функцию для обновления флага update_is_returned_flag(instance)