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)
|
@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):
|
||||||
|
|||||||
Reference in New Issue
Block a user