Fixed: Re-reserve stock when transitioning from cancelled to other statuses
This commit is contained in:
@@ -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)
|
||||
@transaction.atomic
|
||||
def release_stock_on_order_delete(sender, instance, **kwargs):
|
||||
|
||||
Reference in New Issue
Block a user