Исправлена критическая проблема с резервами при смене статуса заказа
Проблема: - При смене статуса заказа на 'Выполнен' товар списывался со склада - Резервы обновлялись на статус 'converted_to_sale' - НО Stock.quantity_reserved не обновлялся автоматически - В результате резервы продолжали 'держать' товар, хотя он уже продан Решение: 1. Изменен сигнал create_sale_on_order_completion: - Используется .save(update_fields=[...]) вместо .update() - Это вызывает сигнал update_stock_on_reservation_change - Убран костыль с ручным вызовом refresh_from_batches() 2. Оптимизирован сигнал update_stock_on_reservation_change: - Stock обновляется ТОЛЬКО при изменении status или quantity - При изменении других полей (даты и т.д.) Stock НЕ пересчитывается - Предотвращены лишние пересчёты и улучшена производительность 3. Добавлены диагностические инструменты: - check_stock_103.py - для диагностики проблем с Stock - fix_stock_after_sale.py - команда для исправления старых заказов - diagnose_reservation_issue.py - универсальная диагностика Результат: - Элегантное решение без дублирования логики - Stock автоматически обновляется при изменении резервов - Работает везде, не только в заказах - Оптимизировано для производительности
This commit is contained in:
95
myproject/check_stock_103.py
Normal file
95
myproject/check_stock_103.py
Normal file
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Диагностика Stock для заказа 103 в схеме buba
|
||||
"""
|
||||
import os
|
||||
import django
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
|
||||
django.setup()
|
||||
|
||||
from django_tenants.utils import schema_context
|
||||
from orders.models import Order
|
||||
from inventory.models import Reservation, Stock, Sale
|
||||
from django.db.models import Sum
|
||||
|
||||
# Работаем в схеме buba
|
||||
with schema_context('buba'):
|
||||
# Получаем заказ 103
|
||||
order = Order.objects.get(order_number=103)
|
||||
|
||||
print("="*80)
|
||||
print(f"ДИАГНОСТИКА ЗАКАЗА #{order.order_number} (схема: buba)")
|
||||
print("="*80)
|
||||
|
||||
# Проверяем резервы
|
||||
print("\n📝 РЕЗЕРВЫ:")
|
||||
reservations = Reservation.objects.filter(order_item__order=order)
|
||||
for res in reservations:
|
||||
print(f" - {res.product.name}:")
|
||||
print(f" Количество: {res.quantity}")
|
||||
print(f" Статус: {res.status}")
|
||||
print(f" Склад: {res.warehouse.name}")
|
||||
print(f" Product ID: {res.product_id}")
|
||||
print(f" Warehouse ID: {res.warehouse_id}")
|
||||
|
||||
# Проверяем Sale
|
||||
print("\n💰 ПРОДАЖИ (Sale):")
|
||||
sales = Sale.objects.filter(order=order)
|
||||
for sale in sales:
|
||||
print(f" - {sale.product.name}: {sale.quantity} шт.")
|
||||
|
||||
# Проверяем Stock
|
||||
print("\n📊 STOCK:")
|
||||
for res in reservations:
|
||||
stock = Stock.objects.get(
|
||||
product_id=res.product_id,
|
||||
warehouse_id=res.warehouse_id
|
||||
)
|
||||
print(f" - {stock.product.name} на {stock.warehouse.name}:")
|
||||
print(f" quantity_available: {stock.quantity_available}")
|
||||
print(f" quantity_reserved: {stock.quantity_reserved}")
|
||||
print(f" quantity_free: {stock.quantity_free}")
|
||||
|
||||
# Проверяем: сколько РЕАЛЬНО резервов со статусом 'reserved'
|
||||
print("\n🔍 ПЕРЕСЧЁТ РЕЗЕРВОВ ВРУЧНУЮ:")
|
||||
for res in reservations.values('product_id', 'warehouse_id').distinct():
|
||||
product_id = res['product_id']
|
||||
warehouse_id = res['warehouse_id']
|
||||
|
||||
# Считаем резервы со статусом 'reserved'
|
||||
reserved_count = Reservation.objects.filter(
|
||||
product_id=product_id,
|
||||
warehouse_id=warehouse_id,
|
||||
status='reserved'
|
||||
).aggregate(Sum('quantity'))['quantity__sum'] or 0
|
||||
|
||||
# Считаем резервы со статусом 'converted_to_sale'
|
||||
converted_count = Reservation.objects.filter(
|
||||
product_id=product_id,
|
||||
warehouse_id=warehouse_id,
|
||||
status='converted_to_sale'
|
||||
).aggregate(Sum('quantity'))['quantity__sum'] or 0
|
||||
|
||||
print(f" Product ID {product_id}, Warehouse ID {warehouse_id}:")
|
||||
print(f" Резервов 'reserved': {reserved_count}")
|
||||
print(f" Резервов 'converted_to_sale': {converted_count}")
|
||||
|
||||
# Что должно быть в Stock
|
||||
stock = Stock.objects.get(product_id=product_id, warehouse_id=warehouse_id)
|
||||
print(f" Stock.quantity_reserved: {stock.quantity_reserved}")
|
||||
print(f" ❌ ПРОБЛЕМА!" if stock.quantity_reserved != reserved_count else " ✅ OK")
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("ВЫВОД:")
|
||||
print("="*80)
|
||||
if stock.quantity_reserved > 0 and converted_count > 0:
|
||||
print("❌ Stock НЕ обновился после конвертации резервов!")
|
||||
print(" quantity_reserved показывает старое значение")
|
||||
print("\n🔧 Попробуем обновить вручную...")
|
||||
stock.refresh_from_batches()
|
||||
print(f" После refresh_from_batches():")
|
||||
print(f" quantity_reserved: {stock.quantity_reserved}")
|
||||
print(f" ✅ ИСПРАВЛЕНО!" if stock.quantity_reserved == 0 else " ❌ НЕ ПОМОГЛО!")
|
||||
else:
|
||||
print("✅ Всё в порядке!")
|
||||
Reference in New Issue
Block a user