From 25f2ba6b82adeb830b3a9453275add7859b019cb Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Fri, 2 Jan 2026 14:36:13 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE:=20=D1=80=D0=B5=D0=B7=D0=B5=D1=80=D0=B2?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=82=D0=B5?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D1=8C=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0?= =?UTF-8?q?=D0=B5=D1=82=20=D1=81=20=D0=B5=D0=B4=D0=B8=D0=BD=D0=B8=D1=86?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8=20=D0=BF=D1=80=D0=BE=D0=B4=D0=B0=D0=B6=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Проблема: сигнал на 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 - Резервы создаются для каждой позиции отдельно после её сохранения --- myproject/inventory/signals.py | 92 +++++++++++++++++----------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/myproject/inventory/signals.py b/myproject/inventory/signals.py index ce3a962..8bf8a31 100644 --- a/myproject/inventory/signals.py +++ b/myproject/inventory/signals.py @@ -121,64 +121,66 @@ 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. Проверяем на существующие резервы (защита от дубликатов) """ 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 - ) - - elif item.product_kit and item.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 + + # Резервируем товар или комплект + 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 instance.product_kit and instance.kit_snapshot: + # Комплект - резервируем КОМПОНЕНТЫ из снимка + # Группируем одинаковые товары для создания одного резерва + product_quantities = defaultdict(Decimal) + + 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):