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