feat: Добавлены команды управления данными тенантов и исправлены фильтры по статусу товаров
Добавлено: - Команда clear_tenant_data для полной очистки данных тенанта без удаления схемы * Очищает все таблицы через TRUNCATE CASCADE * Сбрасывает ID-последовательности * Сохраняет схему БД и запись Client * Поддержка флага --noinput для автоматизации - Команда init_tenant_data для инициализации системных данных тенанта * Создаёт системного клиента (АНОНИМНЫЙ ПОКУПАТЕЛЬ для POS) * Создаёт 8 системных статусов заказов * Создаёт 5 системных способов оплаты * Поддержка флага --reset для пересоздания данных Исправлено: - Заменены устаревшие фильтры is_active на status='active' для Product и ProductKit * products/views/category_views.py: исправлены фильтры в build_category_tree и get_context_data * products/services/kit_pricing.py: исправлены фильтры при получении товаров из variant_group * products/models/kits.py: исправлен фильтр в get_available_products * Устранена ошибка FieldError при работе со списком категорий Улучшено: - Команда clear_tenant_data теперь предлагает пользователю инициализировать системные данные после очистки - Добавлена детальная информация о процессе очистки и инициализации данных
This commit is contained in:
185
myproject/tenants/management/commands/init_tenant_data.py
Normal file
185
myproject/tenants/management/commands/init_tenant_data.py
Normal file
@@ -0,0 +1,185 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Management команда для инициализации всех системных данных тенанта.
|
||||
|
||||
Создаёт:
|
||||
- Системного клиента (анонимный покупатель для POS)
|
||||
- Системные статусы заказов
|
||||
- Системные способы оплаты
|
||||
|
||||
Использование:
|
||||
# Инициализация для конкретного тенанта
|
||||
python manage.py init_tenant_data --schema=anatol
|
||||
|
||||
# С флагом --reset для пересоздания данных
|
||||
python manage.py init_tenant_data --schema=anatol --reset
|
||||
"""
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import connection
|
||||
from django_tenants.utils import get_tenant_model, schema_context
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Инициализация всех системных данных тенанта (клиент, статусы, способы оплаты)'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--schema',
|
||||
type=str,
|
||||
required=True,
|
||||
help='Имя схемы БД тенанта (пример: anatol)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--reset',
|
||||
action='store_true',
|
||||
help='Удалить и пересоздать все системные данные'
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
schema_name = options.get('schema')
|
||||
reset = options.get('reset', False)
|
||||
|
||||
# Проверяем что тенант существует
|
||||
Tenant = get_tenant_model()
|
||||
try:
|
||||
tenant = Tenant.objects.get(schema_name=schema_name)
|
||||
except Tenant.DoesNotExist:
|
||||
self.stdout.write(self.style.ERROR(f'\nОШИБКА: Тенант со схемой "{schema_name}" не найден\n'))
|
||||
return
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('\n=== Инициализация системных данных тенанта ===\n'))
|
||||
self.stdout.write(f'Тенант: {tenant.name} ({schema_name})\n')
|
||||
|
||||
# Переключаемся на схему тенанта
|
||||
with schema_context(schema_name):
|
||||
# 1. Создаём системного клиента
|
||||
self.stdout.write('\n' + '='*70)
|
||||
self.stdout.write('[1] Создание системного клиента...')
|
||||
self.stdout.write('='*70)
|
||||
|
||||
from customers.models import Customer
|
||||
|
||||
if reset:
|
||||
# Удаляем существующего системного клиента
|
||||
system_customers = Customer.objects.filter(email="system@pos.customer")
|
||||
if system_customers.exists():
|
||||
count = system_customers.count()
|
||||
system_customers.delete()
|
||||
self.stdout.write(self.style.WARNING(f' Удалено системных клиентов: {count}'))
|
||||
|
||||
try:
|
||||
system_customer, created = Customer.get_or_create_system_customer()
|
||||
if created:
|
||||
self.stdout.write(self.style.SUCCESS(f' ✓ Системный клиент создан: {system_customer.name}'))
|
||||
self.stdout.write(f' Email: {system_customer.email}')
|
||||
self.stdout.write(f' ID: {system_customer.id}')
|
||||
else:
|
||||
self.stdout.write(self.style.WARNING(f' • Системный клиент уже существует: {system_customer.name}'))
|
||||
self.stdout.write(f' ID: {system_customer.id}')
|
||||
except Exception as e:
|
||||
self.stdout.write(self.style.ERROR(f' ✗ Ошибка при создании системного клиента: {e}'))
|
||||
|
||||
# 2. Создаём системные статусы заказов
|
||||
self.stdout.write('\n' + '='*70)
|
||||
self.stdout.write('[2] Создание системных статусов заказов...')
|
||||
self.stdout.write('='*70)
|
||||
|
||||
from orders.models import OrderStatus
|
||||
from orders.services.order_status_service import OrderStatusService
|
||||
|
||||
if reset:
|
||||
count = OrderStatus.objects.filter(is_system=True).count()
|
||||
if count > 0:
|
||||
OrderStatus.objects.filter(is_system=True).delete()
|
||||
self.stdout.write(self.style.WARNING(f' Удалено системных статусов: {count}'))
|
||||
|
||||
try:
|
||||
OrderStatusService.create_default_statuses()
|
||||
statuses = OrderStatus.objects.filter(is_system=True).order_by('order')
|
||||
self.stdout.write(self.style.SUCCESS(f' ✓ Создано системных статусов: {statuses.count()}'))
|
||||
|
||||
for status in statuses:
|
||||
end_type = ''
|
||||
if status.is_positive_end:
|
||||
end_type = ' [Успешный]'
|
||||
elif status.is_negative_end:
|
||||
end_type = ' [Отрицательный]'
|
||||
self.stdout.write(f' - {status.name:<20} ({status.code:<15}){end_type}')
|
||||
|
||||
except Exception as e:
|
||||
self.stdout.write(self.style.ERROR(f' ✗ Ошибка при создании статусов: {e}'))
|
||||
|
||||
# 3. Создаём системные способы оплаты
|
||||
self.stdout.write('\n' + '='*70)
|
||||
self.stdout.write('[3] Создание системных способов оплаты...')
|
||||
self.stdout.write('='*70)
|
||||
|
||||
from orders.models import PaymentMethod
|
||||
|
||||
if reset:
|
||||
count = PaymentMethod.objects.filter(is_system=True).count()
|
||||
if count > 0:
|
||||
PaymentMethod.objects.filter(is_system=True).delete()
|
||||
self.stdout.write(self.style.WARNING(f' Удалено системных способов оплаты: {count}'))
|
||||
|
||||
payment_methods = [
|
||||
{
|
||||
'code': 'account_balance',
|
||||
'name': 'С баланса счёта',
|
||||
'description': 'Оплата из кошелька клиента',
|
||||
'is_system': True,
|
||||
'order': 0
|
||||
},
|
||||
{
|
||||
'code': 'cash',
|
||||
'name': 'Наличными',
|
||||
'description': 'Оплата наличными деньгами',
|
||||
'is_system': True,
|
||||
'order': 1
|
||||
},
|
||||
{
|
||||
'code': 'card',
|
||||
'name': 'Картой',
|
||||
'description': 'Оплата банковской картой',
|
||||
'is_system': True,
|
||||
'order': 2
|
||||
},
|
||||
{
|
||||
'code': 'online',
|
||||
'name': 'Онлайн',
|
||||
'description': 'Онлайн оплата через платежную систему',
|
||||
'is_system': True,
|
||||
'order': 3
|
||||
},
|
||||
{
|
||||
'code': 'legal_entity',
|
||||
'name': 'Безнал от ЮРЛИЦ',
|
||||
'description': 'Безналичный расчёт от юридических лиц',
|
||||
'is_system': True,
|
||||
'order': 4
|
||||
},
|
||||
]
|
||||
|
||||
created_count = 0
|
||||
try:
|
||||
for method_data in payment_methods:
|
||||
method, created = PaymentMethod.objects.get_or_create(
|
||||
code=method_data['code'],
|
||||
defaults=method_data
|
||||
)
|
||||
if created:
|
||||
created_count += 1
|
||||
self.stdout.write(self.style.SUCCESS(f' ✓ Создан способ оплаты: {method.name}'))
|
||||
else:
|
||||
self.stdout.write(self.style.WARNING(f' • Уже существует: {method.name}'))
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(f'\n Готово! Создано {created_count} новых способов оплаты.'))
|
||||
|
||||
except Exception as e:
|
||||
self.stdout.write(self.style.ERROR(f' ✗ Ошибка при создании способов оплаты: {e}'))
|
||||
|
||||
# Итоговое сообщение
|
||||
self.stdout.write('\n' + '='*70)
|
||||
self.stdout.write(self.style.SUCCESS('УСПЕХ: Инициализация системных данных завершена!'))
|
||||
self.stdout.write('='*70)
|
||||
self.stdout.write('\nТенант готов к работе.\n')
|
||||
Reference in New Issue
Block a user