Исправление конфликта сигналов при отмене трансформации

Исправлена проблема, когда при отмене проведенной трансформации оба сигнала выполнялись последовательно:
- rollback_transformation_on_cancel возвращал резервы в 'reserved'
- release_reservations_on_draft_cancel ошибочно освобождал их в 'released'

Изменена проверка в release_reservations_on_draft_cancel: вместо проверки наличия партий Output (которые уже удалены) теперь проверяется статус резервов ('converted_to_transformation') или наличие поля converted_at, что работает независимо от порядка выполнения сигналов.
This commit is contained in:
2025-12-25 22:54:39 +03:00
parent 30ee077963
commit bc13750d16
7 changed files with 624 additions and 235 deletions

View File

@@ -5,6 +5,7 @@
"""
from django.db.models.signals import post_save, pre_delete, post_delete
from django.db.models import Q
from django.db import transaction
from django.dispatch import receiver
from django.utils import timezone
@@ -1533,19 +1534,21 @@ def reserve_on_transformation_input_create(sender, instance, created, **kwargs):
"""
При создании входного товара в черновике - резервируем его.
"""
# Резервируем только при создании нового входного товара
if not created:
return
# Резервируем только если трансформация в draft
if instance.transformation.status != 'draft':
return
# Создаем или обновляем резерв
Reservation.objects.update_or_create(
# Создаем резерв
Reservation.objects.create(
transformation_input=instance,
product=instance.product,
warehouse=instance.transformation.warehouse,
defaults={
'quantity': instance.quantity,
'status': 'reserved'
}
quantity=instance.quantity,
status='reserved'
)
@@ -1582,7 +1585,8 @@ def process_transformation_on_complete(sender, instance, created, **kwargs):
allocations = StockBatchManager.write_off_by_fifo(
product=trans_input.product,
warehouse=instance.warehouse,
quantity_to_write_off=trans_input.quantity
quantity_to_write_off=trans_input.quantity,
exclude_transformation=instance # Исключаем резервы этой трансформации
)
# Суммируем себестоимость списанного
@@ -1590,13 +1594,22 @@ def process_transformation_on_complete(sender, instance, created, **kwargs):
total_input_cost += batch.cost_price * qty
# Обновляем резерв
Reservation.objects.filter(
reservations_updated = Reservation.objects.filter(
transformation_input=trans_input,
status='reserved'
).update(
status='converted_to_transformation',
converted_at=timezone.now()
)
# ВАЖНО: .update() не вызывает сигналы, поэтому нужно вручную обновить Stock
if reservations_updated > 0:
stock = Stock.objects.filter(
product=trans_input.product,
warehouse=instance.warehouse
).first()
if stock:
stock.refresh_from_batches()
# 2. Создаем партии Output
for trans_output in instance.outputs.all():
@@ -1678,8 +1691,16 @@ def release_reservations_on_draft_cancel(sender, instance, **kwargs):
if instance.status != 'cancelled':
return
# Проверяем что это был черновик (нет созданных партий)
if instance.outputs.filter(stock_batch__isnull=False).exists():
# Проверяем, были ли резервы в статусе 'converted_to_transformation'
# или имеют заполненное поле converted_at (что означает, что трансформация была проведена)
# Это работает независимо от порядка выполнения сигналов
has_converted_reservations = Reservation.objects.filter(
transformation_input__transformation=instance
).filter(
Q(status='converted_to_transformation') | Q(converted_at__isnull=False)
).exists()
if has_converted_reservations:
return # Это была проведенная трансформация, обрабатывается другим сигналом
# Освобождаем все резервы