From 2dcdc0941f9f6be377d3e90d7290cee17ba1a954 Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Fri, 12 Dec 2025 00:03:10 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=88=D0=B8=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B2=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D0=B2=D0=BE=D0=B7=D0=B2=D1=80?= =?UTF-8?q?=D0=B0=D1=89=D1=91=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B7=D0=B0=D0=BA?= =?UTF-8?q?=D0=B0=D0=B7=D0=BE=D0=B2:=20=D0=B7=D0=B0=D0=BF=D1=80=D0=B5?= =?UTF-8?q?=D1=82=20=D0=BB=D1=8E=D0=B1=D1=8B=D1=85=20=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D1=82=D1=83=D1=81=D0=BE=D0=B2=20=D0=BA=D1=80=D0=BE=D0=BC=D0=B5?= =?UTF-8?q?=20=D0=BE=D1=82=D1=80=D0=B8=D1=86=D0=B0=D1=82=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблема: Для заказа с is_returned=True без резервов (товар продан в другом заказе) можно было установить промежуточные статусы (В доставке, Черновик и т.п.), что не имеет смысла, т.к. физически продавать уже нечего. Решение: Валидация теперь проверяет ДО проверки is_positive_end: - Если is_returned=True И резервов нет И статус НЕ отрицательный → запрещаем ЛЮБОЕ изменение статуса - Разрешены только статусы с is_negative_end=True (отменён и т.п.) Улучшено сообщение об ошибке: - Убраны длинные объяснения - Короткая структура с переносами строк - Чёткое указание: «товары проданы в другом заказе» - Действие: «создайте новый заказ» Теперь возвращённый заказ без резервов навсегда остаётся в статусе отрицательного исхода — как и должно быть в реальности. --- myproject/inventory/signals.py | 40 ++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/myproject/inventory/signals.py b/myproject/inventory/signals.py index 3e18647..db34f74 100644 --- a/myproject/inventory/signals.py +++ b/myproject/inventory/signals.py @@ -133,31 +133,33 @@ def create_sale_on_order_completion(sender, instance, created, **kwargs): if not instance.status: return - # Проверяем: это положительный финальный статус? - is_positive_end = instance.status.is_positive_end - - if not is_positive_end: - return # Только для положительных финальных статусов (completed и т.п.) - - # === ВАЛИДАЦИЯ: Запрет повторного completed для возвращённых заказов без резервов === + # === ВАЛИДАЦИЯ: Запрет изменения статуса для возвращённых заказов без резервов === + # Если заказ был возвращён (is_returned=True) и резервов нет, можно использовать + # только статусы отрицательного исхода (отменён и т.п.) if instance.is_returned: - # Заказ уже был продан и возвращён — проверяем наличие резервов has_reservations = Reservation.objects.filter( order_item__order=instance ).exists() if not has_reservations: - # Резервов нет — товар уже ушёл в другой заказ или был освобождён - logger.error( - f"❌ Заказ {instance.order_number} имеет флаг is_returned=True и не имеет резервов. " - f"Невозможно перевести в статус '{instance.status.name}'." - ) - raise ValidationError( - f"Невозможно установить статус '{instance.status.name}' для заказа {instance.order_number}. " - f"Этот заказ уже был отменён после продажи, резервы отсутствуют. " - f"Товары могли быть проданы в другом заказе. " - f"Пожалуйста, оставьте статус отрицательного исхода (отменён) или создайте новый заказ." - ) + # Резервов нет — разрешены только отрицательные статусы + if not instance.status.is_negative_end: + logger.error( + f"❌ Заказ {instance.order_number}: is_returned=True, резервов нет. " + f"Попытка установить '{instance.status.name}' запрещена." + ) + raise ValidationError( + f"Невозможно изменить статус заказа {instance.order_number}.\n\n" + f"Этот заказ был отменён после продажи, товары проданы в другом заказе.\n" + f"Разрешены только статусы отрицательного исхода (отменён).\n\n" + f"Для новой продажи создайте новый заказ." + ) + + # Проверяем: это положительный финальный статус? + is_positive_end = instance.status.is_positive_end + + if not is_positive_end: + return # Только для положительных финальных статусов (completed и т.п.) # Защита от повторного списания: проверяем, не созданы ли уже Sale для этого заказа if Sale.objects.filter(order=instance).exists():