Исправлено: резервирование теперь работает с единицами продажи

- Проблема: сигнал на Order срабатывал ДО вычисления quantity_in_base_units в OrderItem.save()

- Решение: переместили резервирование на сигнал post_save для OrderItem

- Теперь quantity_in_base_units гарантированно вычислено перед резервированием

- Изменения:

  - signals.py: reserve_stock_on_order_create → reserve_stock_on_item_create

  - Сигнал теперь на OrderItem вместо Order

  - Резервы создаются для каждой позиции отдельно после её сохранения
This commit is contained in:
2026-01-02 14:36:13 +03:00
parent baa9780ce1
commit 25f2ba6b82

View File

@@ -121,14 +121,16 @@ def check_released_reservations_available(order):
return True
@receiver(post_save, sender=Order)
def reserve_stock_on_order_create(sender, instance, created, **kwargs):
@receiver(post_save, sender='orders.OrderItem')
def reserve_stock_on_item_create(sender, instance, created, **kwargs):
"""
Сигнал: При создании нового заказа зарезервировать товар.
Сигнал: При создании позиции заказа резервируем товар.
ВАЖНО: Срабатывает ПОСЛЕ OrderItem.save(), когда quantity_in_base_units уже вычислено!
Процесс:
1. Проверяем, новый ли заказ (создан только что)
2. Для обычных товаров - создаём резерв напрямую
1. Проверяем, новая ли позиция (создана только что)
2. Для обычных товаров - создаём резерв с учетом единиц продажи
3. Для комплектов - резервируем компоненты (группируя одинаковые товары)
4. Статус резерва = 'reserved'
5. Проверяем на существующие резервы (защита от дубликатов)
@@ -136,50 +138,50 @@ def reserve_stock_on_order_create(sender, instance, created, **kwargs):
from collections import defaultdict
if not created:
return # Только для новых заказов
return # Только для новых позиций
order = instance.order
# Определяем склад (используем склад самовывоза из заказа или первый активный)
warehouse = instance.pickup_warehouse or Warehouse.objects.filter(is_active=True).first()
warehouse = order.pickup_warehouse or Warehouse.objects.filter(is_active=True).first()
if not warehouse:
# Если нет активных складов, зарезервировать не можем
# Можно логировать ошибку или выбросить исключение
return
# Для каждого товара в заказе
for item in instance.items.all():
if item.product:
# Обычный товар - резервируем с учетом единиц продажи
# Используем quantity_in_base_units если заполнено, иначе quantity
reservation_quantity = item.quantity_in_base_units if item.quantity_in_base_units else Decimal(str(item.quantity))
_create_or_update_reservation(
item,
item.product,
warehouse,
reservation_quantity,
sales_unit=item.sales_unit
)
# Резервируем товар или комплект
if instance.product:
# Обычный товар - резервируем с учетом единиц продажи
# quantity_in_base_units УЖЕ вычислено в OrderItem.save()
reservation_quantity = instance.quantity_in_base_units if instance.quantity_in_base_units else Decimal(str(instance.quantity))
_create_or_update_reservation(
instance,
instance.product,
warehouse,
reservation_quantity,
sales_unit=instance.sales_unit
)
elif item.product_kit and item.kit_snapshot:
# Комплект - резервируем КОМПОНЕНТЫ из снимка
# Группируем одинаковые товары для создания одного резерва
product_quantities = defaultdict(Decimal)
elif instance.product_kit and instance.kit_snapshot:
# Комплект - резервируем КОМПОНЕНТЫ из снимка
# Группируем одинаковые товары для создания одного резерва
product_quantities = defaultdict(Decimal)
for kit_item in item.kit_snapshot.items.select_related('original_product'):
if kit_item.original_product:
# Суммируем количество: qty компонента * qty комплектов в заказе
product_quantities[kit_item.original_product_id] += (
kit_item.quantity * Decimal(str(item.quantity))
)
# Создаём по одному резерву на каждый уникальный товар
from products.models import Product
for product_id, total_qty in product_quantities.items():
product = Product.objects.get(pk=product_id)
_create_or_update_reservation(
item, product, warehouse, total_qty, product_kit=item.product_kit
for kit_item in instance.kit_snapshot.items.select_related('original_product'):
if kit_item.original_product:
# Суммируем количество: qty компонента * qty комплектов в заказе
product_quantities[kit_item.original_product_id] += (
kit_item.quantity * Decimal(str(instance.quantity))
)
# Создаём по одному резерву на каждый уникальный товар
from products.models import Product
for product_id, total_qty in product_quantities.items():
product = Product.objects.get(pk=product_id)
_create_or_update_reservation(
instance, product, warehouse, total_qty, product_kit=instance.product_kit
)
def _create_or_update_reservation(order_item, product, warehouse, quantity, product_kit=None, sales_unit=None):
"""