feat: Добавить систему мультитенантности с регистрацией магазинов

Реализована полноценная система мультитенантности на базе django-tenants.
Каждый магазин получает изолированную схему БД и поддомен.

Основные компоненты:

Django-tenants интеграция:
- Модели Client (тенант) и Domain в приложении tenants/
- Разделение на SHARED_APPS и TENANT_APPS
- Public schema для общей админки
- Tenant schemas для изолированных данных магазинов

Система регистрации магазинов:
- Публичная форма регистрации на /register/
- Модель TenantRegistration для заявок со статусами (pending/approved/rejected)
- Валидация schema_name (латиница, 3-63 символа, уникальность)
- Проверка на зарезервированные имена (admin, api, www и т.д.)
- Админ-панель для модерации заявок с кнопками активации/отклонения

Система подписок:
- Модель Subscription с планами (триал 90 дней, месяц, квартал, год)
- Автоматическое создание триальной подписки при активации
- Методы is_expired() и days_left() для проверки статуса
- Цветовая индикация в админке (зеленый/оранжевый/красный)

Приложения:
- tenants/ - управление тенантами, регистрация, подписки
- shops/ - точки магазинов/самовывоза (tenant app)
- Обновлены миграции для всех приложений

Утилиты:
- switch_to_tenant.py - переключение между схемами тенантов
- Обновлены image_processor и image_service

Конфигурация:
- urls_public.py - роуты для public schema (админка + регистрация)
- urls.py - роуты для tenant schemas (магазины)
- requirements.txt - добавлены django-tenants, django-environ, phonenumber-field

Документация:
- DJANGO_TENANTS_SETUP.md - настройка мультитенантности
- TENANT_REGISTRATION_GUIDE.md - руководство по регистрации
- QUICK_START.md - быстрый старт
- START_HERE.md - общая документация

Использование:
1. Пользователь: http://localhost:8000/register/ → заполняет форму
2. Админ: http://localhost:8000/admin/ → активирует заявку
3. Результат: http://{schema_name}.localhost:8000/ - готовый магазин

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-27 19:13:10 +03:00
parent 4b44624f86
commit 097d4ea304
43 changed files with 3186 additions and 553 deletions

67
myproject/shops/admin.py Normal file
View File

@@ -0,0 +1,67 @@
from django.contrib import admin
from .models import Shop
@admin.register(Shop)
class ShopAdmin(admin.ModelAdmin):
"""
Админ-панель для управления магазинами/пунктами самовывоза.
"""
list_display = [
'name',
'full_address',
'phone',
'working_hours',
'is_active',
'is_pickup_point',
]
list_filter = [
'is_active',
'is_pickup_point',
'district',
]
search_fields = [
'name',
'street',
'building_number',
'phone',
'email',
]
fieldsets = (
('Основная информация', {
'fields': ('name', 'description')
}),
('Адрес', {
'fields': ('street', 'building_number', 'district')
}),
('Контакты', {
'fields': ('phone', 'email')
}),
('Режим работы', {
'fields': ('opening_time', 'closing_time', 'working_days')
}),
('Настройки', {
'fields': ('is_active', 'is_pickup_point')
}),
('Дополнительно', {
'fields': ('delivery_instructions', 'latitude', 'longitude'),
'classes': ('collapse',)
}),
)
readonly_fields = ['created_at', 'updated_at']
def get_fieldsets(self, request, obj=None):
"""Добавляем временные метки для существующих объектов"""
fieldsets = super().get_fieldsets(request, obj)
if obj: # Если объект уже существует
fieldsets = fieldsets + (
('Системная информация', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
return fieldsets