Fixed: Re-reserve stock when transitioning from cancelled to other statuses

This commit is contained in:
2025-12-01 12:22:50 +03:00
parent 702d42e943
commit 1168659df8

View File

@@ -498,6 +498,94 @@ def release_reservations_on_cancellation(sender, instance, created, **kwargs):
) )
@receiver(post_save, sender=Order)
@transaction.atomic
def reserve_stock_on_uncancellation(sender, instance, created, **kwargs):
"""
Сигнал: Резервирование товара при переходе ОТ статуса 'cancelled' к другим статусам.
Триггер: cancelled → любой НЕ отменённый статус (draft, pending, completed и т.д.)
Процесс:
1. Проверяем что предыдущий статус был 'cancelled' (is_negative_end)
2. Проверяем что текущий статус НЕ 'cancelled'
3. Находим резервы в статусе 'released'
4. Переводим их обратно в 'reserved'
5. Stock автоматически обновится через сигнал
ПРИМЕРЫ сценариев:
- cancelled → pending: резервы 'released''reserved'
- cancelled → draft: резервы 'released''reserved'
- cancelled → completed: резервы 'released''reserved', затем create_sale_on_order_completion обработает ✅
"""
import logging
logger = logging.getLogger(__name__)
# Пропускаем новые заказы
if created:
return
# Проверяем наличие статуса
if not instance.status:
return
current_status = instance.status
# Проверяем: текущий статус НЕ отмена?
if current_status.is_negative_end:
return # Всё ещё в отмене, выходим
# === Получаем предыдущий статус ===
try:
history_count = instance.history.count()
if history_count < 2:
return # Нет истории для сравнения
previous_record = instance.history.all()[1]
if not previous_record.status_id:
return
from orders.models import OrderStatus
previous_status = OrderStatus.objects.get(id=previous_record.status_id)
except (instance.history.model.DoesNotExist, OrderStatus.DoesNotExist, IndexError):
return
# Проверяем: был ли предыдущий статус = cancelled?
if not previous_status.is_negative_end:
return # Не было перехода от cancelled, выходим
# === Резервируем товар заново ===
# Ищем резервы в статусе 'released'
reservations = Reservation.objects.filter(
order_item__order=instance,
status='released'
)
reservations_count = reservations.count()
if reservations_count > 0:
logger.info(
f"🔄 Переход от статуса '{previous_status.name}' к '{current_status.name}' для заказа {instance.order_number}. "
f"Резервируем {reservations_count} освобождённых резервов..."
)
# Обновляем резервы через .save() чтобы сработал сигнал обновления Stock
for reservation in reservations:
reservation.status = 'reserved'
reservation.reserved_at = timezone.now() # Обновляем время резервирования
reservation.save(update_fields=['status', 'reserved_at'])
logger.info(
f"✅ Зарезервировано {reservations_count} резервов: released → reserved"
)
else:
logger.debug(
f" Для заказа {instance.order_number} нет резервов в статусе 'released'"
)
@receiver(pre_delete, sender=Order) @receiver(pre_delete, sender=Order)
@transaction.atomic @transaction.atomic
def release_stock_on_order_delete(sender, instance, **kwargs): def release_stock_on_order_delete(sender, instance, **kwargs):