Files
octopus/myproject/accounts/models.py
Andrey Smakotin 1b749ebe63 fix(pos): исправить загрузку витринных комплектов
- Добавить display_name в CustomUser (name или email)
- Исправить get_showcase_kits_api: заменить username на display_name
- Использовать Case/When с output_field для выбора имени на уровне БД

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 00:15:11 +03:00

160 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.utils import timezone
import uuid
class CustomUserManager(BaseUserManager):
def create_user(self, email, name, password=None, **extra_fields):
if not email:
raise ValueError('Email обязателен')
email = self.normalize_email(email)
# SECURITY FIX: Явно устанавливаем флаги безопасности в False по умолчанию
# Обычные пользователи НЕ должны иметь доступ к админке
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
user = self.model(email=email, name=name, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, name, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False) # CustomUser не должен иметь доступ к Django Admin
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
# Суперпользователь автоматически имеет подтвержденный email
extra_fields.setdefault('is_email_confirmed', True)
if extra_fields.get('is_superuser') is not True:
raise ValueError('Суперпользователь должен иметь is_superuser=True.')
user = self.create_user(email, name, password, **extra_fields)
# Устанавливаем дату подтверждения email
if user.is_email_confirmed and not user.email_confirmed_at:
user.email_confirmed_at = timezone.now()
user.save()
return user
class CustomUser(AbstractBaseUser):
"""
Пользователь тенанта (магазина).
ВАЖНО: Эта модель в TENANT_APPS - каждый тенант имеет свою таблицу!
Один email в разных тенантах = разные записи в разных схемах БД.
Полная изоляция обеспечивается на уровне PostgreSQL schemas.
НЕ является AUTH_USER_MODEL (это PlatformAdmin).
НЕ использует Django Groups/Permissions - используется своя система ролей (UserRole).
"""
email = models.EmailField(unique=True)
name = models.CharField(max_length=100)
# Стандартные поля для совместимости с Django auth
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False) # Для доступа к админке (если нужно)
is_superuser = models.BooleanField(default=False) # Для полных прав в тенанте
date_joined = models.DateTimeField(default=timezone.now)
is_email_confirmed = models.BooleanField(default=False)
email_confirmation_token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
email_confirmed_at = models.DateTimeField(null=True, blank=True)
password_reset_token = models.UUIDField(null=True, blank=True, editable=False, unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name']
objects = CustomUserManager()
class Meta:
verbose_name = "Пользователь магазина"
verbose_name_plural = "Пользователи магазина"
def __str__(self):
return self.email
@property
def display_name(self):
"""Отображаемое имя пользователя: имя если есть, иначе email"""
return self.name or self.email
def has_perm(self, perm, obj=None):
"""
Проверка разрешения через authentication backends.
Django вызывает все зарегистрированные backends по очереди.
"""
if not self.is_active:
return False
# Импортируем здесь, чтобы избежать циклических импортов
from django.contrib.auth import get_backends
for backend in get_backends():
if hasattr(backend, 'has_perm'):
result = backend.has_perm(self, perm, obj)
if result is not None: # Backend обработал запрос
return result
return False
def has_module_perms(self, app_label):
"""
Проверка разрешений для модуля через authentication backends.
"""
if not self.is_active:
return False
from django.contrib.auth import get_backends
for backend in get_backends():
if hasattr(backend, 'has_module_perms'):
result = backend.has_module_perms(self, app_label)
if result is not None: # Backend обработал запрос
return result
return False
def generate_confirmation_token(self):
"""Генерирует новый токен для подтверждения email"""
self.email_confirmation_token = uuid.uuid4()
self.save()
return self.email_confirmation_token
def confirm_email(self):
"""Подтверждает email пользователя"""
self.is_email_confirmed = True
self.email_confirmed_at = timezone.now()
self.save()
def get_tenant_role(self):
"""Получить роль пользователя в текущем тенанте"""
from user_roles.services import RoleService
return RoleService.get_user_role(self)
def has_role(self, *role_codes):
"""Проверить, имеет ли пользователь одну из указанных ролей"""
from user_roles.services import RoleService
return RoleService.user_has_role(self, *role_codes)
@property
def is_owner(self):
"""Является ли пользователь владельцем"""
return self.has_role('owner')
@property
def is_manager(self):
"""Является ли пользователь менеджером"""
return self.has_role('manager')
@property
def is_florist(self):
"""Является ли пользователь флористом"""
return self.has_role('florist')
@property
def is_courier(self):
"""Является ли пользователь курьером"""
return self.has_role('courier')