diff --git a/myproject/cleanup_orphaned_kits.sql b/myproject/cleanup_orphaned_kits.sql new file mode 100644 index 0000000..c9aafbf --- /dev/null +++ b/myproject/cleanup_orphaned_kits.sql @@ -0,0 +1,50 @@ +-- Скрипт для очистки витринных комплектов без резервов +-- Используется для схемы tenant: anatol + +-- Переключаемся на схему anatol +SET search_path TO anatol; + +-- 1. Проверяем проблемные комплекты (READ ONLY) +-- Витринные комплекты без зарезервированных компонентов + +SELECT + pk.id, + pk.name, + s.name as showcase_name, + pk.status, + pk.is_temporary, + (SELECT COUNT(*) FROM inventory_reservation r + WHERE r.product_kit_id = pk.id + AND r.showcase_id = pk.showcase_id + AND r.status = 'reserved') as reserved_count +FROM products_productkit pk +INNER JOIN inventory_showcase s ON pk.showcase_id = s.id +WHERE pk.is_temporary = TRUE + AND pk.showcase_id IS NOT NULL + AND pk.status = 'active' + AND NOT EXISTS ( + SELECT 1 FROM inventory_reservation r + WHERE r.product_kit_id = pk.id + AND r.showcase_id = pk.showcase_id + AND r.status = 'reserved' + ); + +-- 2. Удалить проблемные комплекты (ОПАСНО! Сначала проверьте результат выше) +-- Раскомментируйте следующие строки для удаления: + +/* +DELETE FROM products_productkit +WHERE id IN ( + SELECT pk.id + FROM products_productkit pk + WHERE pk.is_temporary = TRUE + AND pk.showcase_id IS NOT NULL + AND pk.status = 'active' + AND NOT EXISTS ( + SELECT 1 FROM inventory_reservation r + WHERE r.product_kit_id = pk.id + AND r.showcase_id = pk.showcase_id + AND r.status = 'reserved' + ) +); +*/ diff --git a/myproject/cleanup_showcase_kits.py b/myproject/cleanup_showcase_kits.py new file mode 100644 index 0000000..6c055af --- /dev/null +++ b/myproject/cleanup_showcase_kits.py @@ -0,0 +1,52 @@ +""" +Скрипт для очистки витринных комплектов без резервов. +Запуск: python manage.py shell < cleanup_showcase_kits.py +""" +from django.db import connection +from products.models import ProductKit +from inventory.models import Reservation + +# Устанавливаем схему tenant +schema_name = 'anatol' +with connection.cursor() as cursor: + cursor.execute(f'SET search_path TO {schema_name}') + +print('=' * 70) +print(f'ОЧИСТКА ВИТРИННЫХ КОМПЛЕКТОВ БЕЗ РЕЗЕРВОВ (схема: {schema_name})') +print('=' * 70) + +# Находим все активные витринные комплекты +showcase_kits = ProductKit.objects.filter( + is_temporary=True, + showcase__isnull=False, + status='active' +).select_related('showcase') + +orphaned_kits = [] + +for kit in showcase_kits: + # Проверяем наличие зарезервированных компонентов + has_reservations = Reservation.objects.filter( + product_kit=kit, + showcase=kit.showcase, + status='reserved' + ).exists() + + if not has_reservations: + orphaned_kits.append(kit) + +if not orphaned_kits: + print('\n✅ Проблемных комплектов не найдено. Все витринные комплекты имеют резервы.\n') +else: + print(f'\n⚠️ Найдено {len(orphaned_kits)} витринных комплектов БЕЗ РЕЗЕРВОВ:\n') + + for kit in orphaned_kits: + print( + f' • ID: {kit.id} | "{kit.name}" | Витрина: {kit.showcase.name} | ' + f'Цена: {kit.actual_price} руб.' + ) + + print('\n📝 Для удаления запустите скрипт с подтверждением') + print('Или удалите вручную через админку Django\n') + +print('=' * 70)