""" Management команда для восстановления недостающих резервов товаров Используется для исправления заказов, созданных напрямую через SQL """ from django.core.management.base import BaseCommand from django.db import connection, transaction from datetime import datetime from orders.models import OrderItem from inventory.models import Reservation, Warehouse class Command(BaseCommand): help = 'Создает недостающие резервы для существующих заказов' def add_arguments(self, parser): parser.add_argument( '--schema', type=str, default='grach', help='Схема базы данных (tenant) для работы' ) parser.add_argument( '--dry-run', action='store_true', help='Показать что будет сделано, но не применять изменения' ) def handle(self, *args, **options): schema_name = options['schema'] dry_run = options['dry_run'] # Устанавливаем схему для работы с tenant with connection.cursor() as cursor: cursor.execute(f'SET search_path TO {schema_name}') self.stdout.write(f'[НАЧАЛО] Поиск заказов без резервов в схеме {schema_name}...') if dry_run: self.stdout.write(self.style.WARNING('[DRY RUN] Режим просмотра - изменения НЕ будут применены')) # Получаем активный склад (если есть) try: warehouse = Warehouse.objects.filter(is_active=True).first() if warehouse: self.stdout.write(f'[INFO] Используем склад: {warehouse.name}') else: self.stdout.write(self.style.WARNING('[ВНИМАНИЕ] Нет активного склада, резервы будут созданы без привязки к складу')) except Exception as e: self.stdout.write(self.style.WARNING(f'[ВНИМАНИЕ] Не удалось получить склад: {e}')) warehouse = None # Находим все OrderItem без резервов items_without_reservations = OrderItem.objects.filter( reservations__isnull=True ).select_related('order', 'product', 'product_kit') total_items = items_without_reservations.count() if total_items == 0: self.stdout.write(self.style.SUCCESS('[OK] Все заказы имеют резервы!')) return self.stdout.write(f'[НАЙДЕНО] {total_items} позиций заказов без резервов') created_count = 0 errors_count = 0 with transaction.atomic(): for item in items_without_reservations: try: # Определяем товар (может быть product или product_kit) product = item.product if item.product else item.product_kit if not product: self.stdout.write( self.style.WARNING( f'[ПРОПУСК] OrderItem #{item.id}: нет товара' ) ) errors_count += 1 continue # Создаем резерв if not dry_run: reservation = Reservation.objects.create( order_item=item, product=product, warehouse=warehouse, quantity=item.quantity, status='reserved' ) self.stdout.write( f' [OK] Резерв #{reservation.id}: ' f'Заказ {item.order.order_number}, ' f'Товар "{product.name}", ' f'Кол-во: {item.quantity}' ) else: self.stdout.write( f' [DRY RUN] Будет создан резерв: ' f'Заказ {item.order.order_number}, ' f'Товар "{product.name}", ' f'Кол-во: {item.quantity}' ) created_count += 1 except Exception as e: self.stdout.write( self.style.ERROR( f'[ОШИБКА] OrderItem #{item.id}: {str(e)}' ) ) errors_count += 1 if dry_run: # В режиме dry-run откатываем транзакцию transaction.set_rollback(True) self.stdout.write( self.style.WARNING( f'\n[DRY RUN] Изменения НЕ применены (транзакция откачена)' ) ) # Итоги self.stdout.write('\n' + '=' * 60) if dry_run: self.stdout.write(self.style.WARNING('[DRY RUN] Результаты (БЕЗ изменений):')) self.stdout.write(f' Будет создано резервов: {created_count}') self.stdout.write(f' Ошибок/пропусков: {errors_count}') self.stdout.write('\nДля применения изменений запустите без флага --dry-run') else: self.stdout.write(self.style.SUCCESS('[ЗАВЕРШЕНО] Результаты:')) self.stdout.write(f' Создано резервов: {created_count}') self.stdout.write(f' Ошибок/пропусков: {errors_count}') self.stdout.write(self.style.SUCCESS('\n✓ Резервы успешно восстановлены!'))