Files
octopus/myproject/check_stock_103.py
Andrey Smakotin e4cb175db2 Исправлена критическая проблема с резервами при смене статуса заказа
Проблема:
- При смене статуса заказа на 'Выполнен' товар списывался со склада
- Резервы обновлялись на статус '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 автоматически обновляется при изменении резервов
- Работает везде, не только в заказах
- Оптимизировано для производительности
2025-12-01 02:34:54 +03:00

96 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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("✅ Всё в порядке!")