From 5b03a95b5a232462f338385f2c8d900a611ac9a6 Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Mon, 8 Dec 2025 18:15: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=D0=B0=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BE=D1=81=D0=B2=D0=BE=D0=B1=D0=BE=D0=B6=D0=B4=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D1=80=D0=B5=D0=B7=D0=B5=D1=80=D0=B2=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D0=B2=D0=B8=D1=82=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D1=85=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BB=D0=B5=D0=BA?= =?UTF-8?q?=D1=82=D0=BE=D0=B2=20=D0=B2=D0=BE=20=D0=B2=D1=81=D0=B5=D1=85=20?= =?UTF-8?q?=D1=81=D0=B8=D0=B3=D0=BD=D0=B0=D0=BB=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- myproject/inventory/signals.py | 59 +++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/myproject/inventory/signals.py b/myproject/inventory/signals.py index 3c4f907..678bdaf 100644 --- a/myproject/inventory/signals.py +++ b/myproject/inventory/signals.py @@ -557,24 +557,42 @@ def release_reservations_on_cancellation(sender, instance, created, **kwargs): status='reserved' ) - reservations_count = reservations.count() + # Исключаем витринные временные комплекты - они остаются на витрине + showcase_kit_reservations = reservations.filter( + product_kit__is_temporary=True, + product_kit__showcase__isnull=False + ) - if reservations_count > 0: + # Освобождаем только обычные резервы + normal_reservations = reservations.exclude( + id__in=showcase_kit_reservations.values_list('id', flat=True) + ) + + normal_count = normal_reservations.count() + showcase_count = showcase_kit_reservations.count() + + if normal_count > 0: logger.info( f"🔄 Переход к статусу '{current_status.name}' для заказа {instance.order_number}. " - f"Освобождаем {reservations_count} резервов..." + f"Освобождаем {normal_count} обычных резервов..." ) # Обновляем резервы через .save() чтобы сработал сигнал обновления Stock - for reservation in reservations: + for reservation in normal_reservations: reservation.status = 'released' reservation.released_at = timezone.now() reservation.save(update_fields=['status', 'released_at']) logger.info( - f"✅ Освобождено {reservations_count} резервов: reserved → released" + f"✅ Освобождено {normal_count} обычных резервов: reserved → released" ) - else: + + if showcase_count > 0: + logger.info( + f"ℹ️ Найдено {showcase_count} резервов витринных комплектов - остаются в reserved (на витрине)" + ) + + if normal_count == 0 and showcase_count == 0: logger.debug( f"ℹ️ Для заказа {instance.order_number} нет резервов в статусе 'reserved'" ) @@ -676,25 +694,44 @@ def release_stock_on_order_delete(sender, instance, **kwargs): Процесс: 1. Ищем все резервы для этого заказа ДО удаления - 2. Освобождаем резервы ПОСЛЕ успешного коммита транзакции + 2. Освобождаем резервы ПОСЛЕ успешного коммита транзакции (кроме витринных комплектов) 3. Это гарантирует, что резервы освободятся только если удаление успешно + + ИСКЛЮЧЕНИЕ: Витринные временные комплекты (is_temporary=True, showcase!=null) + остаются в статусе 'reserved' даже при удалении заказа. """ # Находим все резервы для этого заказа ДО удаления # Используем list() чтобы выполнить запрос сейчас, пока Order ещё существует - reservations_to_release = list( + all_reservations = list( Reservation.objects.filter( order_item__order=instance, status='reserved' - ) + ).select_related('product_kit') ) + + # Разделяем на витринные комплекты и обычные резервы + showcase_reservations = [ + r for r in all_reservations + if r.product_kit and r.product_kit.is_temporary and r.product_kit.showcase + ] + + normal_reservations = [ + r for r in all_reservations + if r not in showcase_reservations + ] - # Освобождаем резервы ПОСЛЕ успешного коммита транзакции + # Освобождаем только обычные резервы ПОСЛЕ успешного коммита транзакции # Это гарантирует целостность: резервы освободятся только если удаление прошло успешно def release_reservations(): - for res in reservations_to_release: + for res in normal_reservations: res.status = 'released' res.released_at = timezone.now() res.save() + + # Витринные комплекты остаются зарезервированными, но отвязываем их от заказа + for res in showcase_reservations: + res.order_item = None + res.save(update_fields=['order_item']) transaction.on_commit(release_reservations)