Проблема: - При смене статуса заказа на 'Выполнен' товар списывался со склада - Резервы обновлялись на статус '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 автоматически обновляется при изменении резервов - Работает везде, не только в заказах - Оптимизировано для производительности
118 lines
4.3 KiB
Python
118 lines
4.3 KiB
Python
"""
|
|
Команда для исправления Stock после обработки заказов.
|
|
Пересчитывает quantity_reserved для всех товаров.
|
|
"""
|
|
from django.core.management.base import BaseCommand
|
|
from inventory.models import Stock, Reservation
|
|
from django.db.models import Sum
|
|
from decimal import Decimal
|
|
|
|
|
|
class Command(BaseCommand):
|
|
help = 'Исправляет quantity_reserved в Stock после обработки заказов'
|
|
|
|
def add_arguments(self, parser):
|
|
parser.add_argument(
|
|
'--order',
|
|
type=int,
|
|
help='Номер заказа для исправления (опционально)'
|
|
)
|
|
|
|
def handle(self, *args, **options):
|
|
order_number = options.get('order')
|
|
|
|
if order_number:
|
|
self.stdout.write(f"Исправление Stock для заказа {order_number}...")
|
|
self.fix_for_order(order_number)
|
|
else:
|
|
self.stdout.write("Исправление всех Stock...")
|
|
self.fix_all_stock()
|
|
|
|
def fix_for_order(self, order_number):
|
|
"""Исправить Stock для конкретного заказа"""
|
|
from orders.models import Order
|
|
|
|
try:
|
|
order = Order.objects.get(order_number=order_number)
|
|
except Order.DoesNotExist:
|
|
self.stdout.write(
|
|
self.style.ERROR(f"Заказ {order_number} не найден!")
|
|
)
|
|
return
|
|
|
|
# Получаем все резервы для этого заказа
|
|
reservations = Reservation.objects.filter(
|
|
order_item__order=order
|
|
).values('product_id', 'warehouse_id').distinct()
|
|
|
|
fixed_count = 0
|
|
for res in reservations:
|
|
product_id = res['product_id']
|
|
warehouse_id = res['warehouse_id']
|
|
|
|
try:
|
|
stock = Stock.objects.get(
|
|
product_id=product_id,
|
|
warehouse_id=warehouse_id
|
|
)
|
|
|
|
old_reserved = stock.quantity_reserved
|
|
stock.refresh_from_batches()
|
|
new_reserved = stock.quantity_reserved
|
|
|
|
if old_reserved != new_reserved:
|
|
self.stdout.write(
|
|
self.style.SUCCESS(
|
|
f"✓ Обновлено: product_id={product_id}, "
|
|
f"warehouse_id={warehouse_id}, "
|
|
f"reserved: {old_reserved} → {new_reserved}"
|
|
)
|
|
)
|
|
fixed_count += 1
|
|
else:
|
|
self.stdout.write(
|
|
f" Без изменений: product_id={product_id}"
|
|
)
|
|
|
|
except Stock.DoesNotExist:
|
|
self.stdout.write(
|
|
self.style.WARNING(
|
|
f"⚠ Stock не найден: product_id={product_id}, "
|
|
f"warehouse_id={warehouse_id}"
|
|
)
|
|
)
|
|
|
|
self.stdout.write(
|
|
self.style.SUCCESS(
|
|
f"\n✅ Готово! Обновлено {fixed_count} Stock для заказа {order_number}"
|
|
)
|
|
)
|
|
|
|
def fix_all_stock(self):
|
|
"""Пересчитать все Stock"""
|
|
stocks = Stock.objects.all()
|
|
total = stocks.count()
|
|
fixed_count = 0
|
|
|
|
self.stdout.write(f"Найдено {total} Stock записей...")
|
|
|
|
for stock in stocks:
|
|
old_reserved = stock.quantity_reserved
|
|
stock.refresh_from_batches()
|
|
new_reserved = stock.quantity_reserved
|
|
|
|
if old_reserved != new_reserved:
|
|
self.stdout.write(
|
|
self.style.SUCCESS(
|
|
f"✓ {stock.product.name} на {stock.warehouse.name}: "
|
|
f"reserved {old_reserved} → {new_reserved}"
|
|
)
|
|
)
|
|
fixed_count += 1
|
|
|
|
self.stdout.write(
|
|
self.style.SUCCESS(
|
|
f"\n✅ Готово! Обновлено {fixed_count} из {total} Stock"
|
|
)
|
|
)
|