fix: Исправить логику обновления Product.in_stock из Stock остатков
Проблема: товары отображались как "нет в наличии" несмотря на наличие остатков на складе. Причина: сигналы на обновление Product.in_stock срабатывают только при изменении Stock через Django ORM. Если Stock была создана напрямую (импорт, миграция и т.д.), сигналы не срабатывали. Решение: 1. Исправлена логика сигналов (inventory/signals.py): - Добавлен импорт post_delete для правильной обработки удаления Stock - Изменён pre_delete на post_delete для более надёжной проверки остатков - Сигналы теперь правильно срабатывают при любом изменении Stock 2. Добавлена миграция (products/migrations/0004_fix_product_in_stock.py): - Пересчитывает in_stock для всех существующих товаров на основе Stock.quantity_available - Товар считается в наличии если есть хотя бы один Stock с quantity_available > 0 - Обратима и безопасна (может быть отменена) 3. Добавлена команда управления (products/management/commands/update_product_in_stock.py): - Позволяет вручную пересчитать in_stock если потребуется - Поддерживает параметр --verbose для подробного логирования - Может быть запущена по расписанию или вручную После этого исправления: - Все товары с остатками на складе автоматически обновляют статус in_stock - Сигналы срабатывают при любом изменении Stock (создание, обновление, удаление) - Отображение наличия товаров в UI будет корректным 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
Команда управления для пересчёта статуса in_stock всех товаров.
|
||||
|
||||
Использование:
|
||||
python manage.py update_product_in_stock
|
||||
|
||||
Описание:
|
||||
Пересчитывает Product.in_stock на основе текущих остатков в Stock.
|
||||
Товар считается в наличии если есть хотя бы один Stock с quantity_available > 0.
|
||||
"""
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db.models import Q
|
||||
from products.models import Product
|
||||
from inventory.models import Stock
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Пересчитать статус in_stock для всех товаров на основе остатков в Stock'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--verbose',
|
||||
action='store_true',
|
||||
help='Выводить подробную информацию о каждом товаре',
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
verbose = options.get('verbose', False)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('\n' + '='*80))
|
||||
self.stdout.write(self.style.SUCCESS('ПЕРЕСЧЁТ СТАТУСА НАЛИЧИЯ ТОВАРОВ'))
|
||||
self.stdout.write(self.style.SUCCESS('='*80 + '\n'))
|
||||
|
||||
# Получаем все товары (включая удалённые, если нужно)
|
||||
all_products = Product.all_objects.all()
|
||||
total = all_products.count()
|
||||
updated_count = 0
|
||||
in_stock_count = 0
|
||||
out_of_stock_count = 0
|
||||
|
||||
self.stdout.write(f'Всего товаров: {total}\n')
|
||||
|
||||
for product in all_products:
|
||||
# Проверяем есть ли остаток на любом складе
|
||||
has_stock = Stock.objects.filter(
|
||||
product=product,
|
||||
quantity_available__gt=0
|
||||
).exists()
|
||||
|
||||
# Обновляем в_наличии если статус изменился
|
||||
old_status = product.in_stock
|
||||
if product.in_stock != has_stock:
|
||||
Product.all_objects.filter(id=product.id).update(in_stock=has_stock)
|
||||
updated_count += 1
|
||||
status_text = 'В НАЛИЧИИ' if has_stock else 'НЕ В НАЛИЧИИ'
|
||||
old_status_text = 'В НАЛИЧИИ' if old_status else 'НЕ В НАЛИЧИИ'
|
||||
|
||||
if verbose:
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(f'✓ {product.name:60} {old_status_text} → {status_text}')
|
||||
)
|
||||
|
||||
# Подсчитываем статистику
|
||||
if has_stock:
|
||||
in_stock_count += 1
|
||||
else:
|
||||
out_of_stock_count += 1
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('\n' + '='*80))
|
||||
self.stdout.write(self.style.SUCCESS('РЕЗУЛЬТАТЫ:'))
|
||||
self.stdout.write(self.style.SUCCESS(f' Всего товаров: {total}'))
|
||||
self.stdout.write(self.style.SUCCESS(f' Обновлено: {updated_count}'))
|
||||
self.stdout.write(self.style.SUCCESS(f' В наличии: {in_stock_count}'))
|
||||
self.stdout.write(self.style.SUCCESS(f' Не в наличии: {out_of_stock_count}'))
|
||||
self.stdout.write(self.style.SUCCESS('='*80 + '\n'))
|
||||
|
||||
if updated_count > 0:
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(f'✓ Успешно обновлено {updated_count} товаров')
|
||||
)
|
||||
else:
|
||||
self.stdout.write(self.style.WARNING('Нет товаров для обновления'))
|
||||
Reference in New Issue
Block a user