Исправлены баги витринных комплектов: резервы и валидация восстановления заказов

This commit is contained in:
2026-01-04 22:53:53 +03:00
parent 595cf6a018
commit 8041ceb04a
3 changed files with 224 additions and 64 deletions

View File

@@ -177,6 +177,8 @@ class Order(models.Model):
def save(self, *args, **kwargs):
from django.db import transaction
from django.core.exceptions import ValidationError
# Генерируем уникальный номер заказа при создании (начиная с 100 для 3-значного поиска)
if not self.order_number:
last_order = Order.objects.order_by('-order_number').first()
@@ -185,6 +187,81 @@ class Order(models.Model):
self.order_number = max(last_order.order_number + 1, 100)
else:
self.order_number = 100
# === ВАЛИДАЦИЯ: Проверяем доступность витринных комплектов ===
# При переходе ИЗ cancelled к любому не-отменённому статусу
if self.pk: # Только при редактировании
try:
# Получаем старый статус из БД
old_instance = Order.objects.get(pk=self.pk)
old_status = old_instance.status
new_status = self.status
# Проверяем: переход от cancelled к не-cancelled?
if (old_status and old_status.is_negative_end and
new_status and not new_status.is_negative_end):
# Находим все витринные комплекты в этом заказе
from orders.models import OrderItem
showcase_items = OrderItem.objects.filter(
order=self,
product_kit__is_temporary=True,
product_kit__showcase__isnull=False
).select_related('product_kit')
if showcase_items.exists():
# Проверяем доступность резервов для каждого комплекта
from inventory.models import Reservation
unavailable_kits = []
for item in showcase_items:
kit = item.product_kit
# КРИТИЧНО: Ищем ВСЕ витринные резервы этого комплекта
# Проверяем не привязаны ли они к ДРУГОМУ заказу (в ЛЮБОМ статусе)
occupied_reservations = Reservation.objects.filter(
product_kit=kit,
showcase__isnull=False,
order_item__isnull=False # Привязаны к какому-то заказу
).exclude(
order_item__order=self # Исключаем текущий заказ
).select_related('order_item__order')
if occupied_reservations.exists():
# Резервы заняты другим заказом - блокируем переход
occupied_res = occupied_reservations.first()
other_order_number = occupied_res.order_item.order.order_number
other_order_status = occupied_res.order_item.order.status.name if occupied_res.order_item.order.status else 'неизвестен'
unavailable_kits.append(
f"Витринный комплект '{kit.name}' занят заказом #{other_order_number} (статус: {other_order_status})"
)
else:
# Проверяем что вообще есть резервы для этого комплекта
any_reservations = Reservation.objects.filter(
product_kit=kit,
showcase__isnull=False
).exists()
if not any_reservations:
# Комплект демонтирован или удалён
unavailable_kits.append(
f"Витринный комплект '{kit.name}' больше не существует на витрине"
)
# Если есть недоступные комплекты - блокируем переход
if unavailable_kits:
error_message = (
f"Невозможно восстановить заказ #{self.order_number}. "
f"Витринные комплекты уже проданы:\n\n" +
"\n".join(f"\u2022 {msg}" for msg in unavailable_kits) +
f"\n\nОтмените сначала соответствующие заказы или удалите эти позиции из заказа."
)
raise ValidationError(error_message)
except Order.DoesNotExist:
# Заказ ещё не создан в БД (не должно произойти, но на всякий случай)
pass
# Оборачиваем в транзакцию чтобы ValidationError в сигналах откатывал save()
with transaction.atomic():