Рефакторинг: вынос логики онбординга тенанта в сервисный слой

Создан TenantOnboardingService как единый источник истины для:
- Активации заявки на регистрацию тенанта
- Создания Client, Domain, Subscription
- Инициализации системных данных (Customer, статусы, способы оплаты, склад, витрина)

Новые сервисы:
- TenantOnboardingService (tenants/services/onboarding.py)
- WarehouseService (inventory/services/warehouse_service.py)
- ShowcaseService (inventory/services/showcase_service.py)
- PaymentMethodService (orders/services/payment_method_service.py)

Рефакторинг:
- admin.py: 220 строк → 5 строк (делегирование сервису)
- init_tenant_data.py: 259 строк → 68 строк
- activate_registration.py: использует сервис
- Тесты обновлены для вызова сервиса напрямую

При создании тенанта автоматически создаются склад и витрина по умолчанию.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-30 14:52:55 +03:00
parent 658cd59511
commit b59ad725cb
13 changed files with 679 additions and 477 deletions

View File

@@ -6,21 +6,22 @@ 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 = 'Инициализация всех системных данных тенанта (клиент, статусы, способы оплаты)'
help = 'Инициализация всех системных данных тенанта (клиент, статусы, способы оплаты, склад, витрина)'
def add_arguments(self, parser):
parser.add_argument(
@@ -48,135 +49,16 @@ class Command(BaseCommand):
return
self.stdout.write(self.style.SUCCESS('\n=== Инициализация системных данных тенанта ===\n'))
self.stdout.write(f'Тенант: {tenant.name} ({schema_name})\n')
self.stdout.write(f'Тенант: {tenant.name} ({schema_name})')
if reset:
self.stdout.write(self.style.WARNING('Режим: RESET (пересоздание данных)\n'))
else:
self.stdout.write('')
# Переключаемся на схему тенанта
# Переключаемся на схему тенанта и вызываем сервис
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}'))
from tenants.services import TenantOnboardingService
TenantOnboardingService.init_tenant_data(reset=reset)
# Итоговое сообщение
self.stdout.write('\n' + '='*70)