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

Создан 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

@@ -1,56 +1,19 @@
# -*- coding: utf-8 -*-
"""
Management команда для создания стандартных способов оплаты.
"""
from django.core.management.base import BaseCommand
from orders.models import PaymentMethod
from orders.services import PaymentMethodService
class Command(BaseCommand):
help = 'Создаёт стандартные способы оплаты для цветочного магазина'
def handle(self, *args, **options):
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
},
]
results = PaymentMethodService.create_default_methods()
created_count = 0
for method_data in payment_methods:
method, created = PaymentMethod.objects.get_or_create(
code=method_data['code'],
defaults=method_data
)
for method, created in results:
if created:
created_count += 1
self.stdout.write(

View File

@@ -2,4 +2,10 @@
Сервисный слой для приложения orders.
"""
__all__ = []
from .order_status_service import OrderStatusService
from .payment_method_service import PaymentMethodService
__all__ = [
'OrderStatusService',
'PaymentMethodService',
]

View File

@@ -0,0 +1,90 @@
# -*- coding: utf-8 -*-
"""
Сервис для работы со способами оплаты.
"""
from orders.models import PaymentMethod
class PaymentMethodService:
"""Сервис управления способами оплаты"""
# Системные способы оплаты
DEFAULT_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
},
]
@classmethod
def get_by_code(cls, code: str) -> PaymentMethod | None:
"""
Получить способ оплаты по коду.
Args:
code: Код способа оплаты
Returns:
PaymentMethod или None
"""
return PaymentMethod.objects.filter(code=code).first()
@classmethod
def create_default_methods(cls) -> list[tuple[PaymentMethod, bool]]:
"""
Создать системные способы оплаты.
Returns:
Список кортежей (PaymentMethod, created)
"""
results = []
for method_data in cls.DEFAULT_METHODS:
method, created = PaymentMethod.objects.get_or_create(
code=method_data['code'],
defaults=method_data
)
results.append((method, created))
return results
@classmethod
def reset_default_methods(cls) -> list[PaymentMethod]:
"""
Удалить и пересоздать системные способы оплаты.
Returns:
Список созданных PaymentMethod
"""
PaymentMethod.objects.filter(is_system=True).delete()
results = cls.create_default_methods()
return [method for method, _ in results]