70 lines
2.6 KiB
Python
70 lines
2.6 KiB
Python
from django.contrib.auth.models import User
|
|
from django.core.exceptions import ValidationError
|
|
from django.db import models
|
|
from django_choices_field.fields import TextChoicesField
|
|
from core.models import Customer
|
|
from core.models.base import Contact
|
|
from core.models.enums import StatusChoices, RoleChoices
|
|
|
|
|
|
class Profile(Contact):
|
|
"""Base profile class with common functionality"""
|
|
user = models.OneToOneField(
|
|
User, on_delete=models.SET_NULL, related_name='%(class)s',
|
|
null=True, blank=True,
|
|
help_text="Optional Django user for admin access only"
|
|
)
|
|
status = TextChoicesField(choices_enum=StatusChoices, default=StatusChoices.ACTIVE,
|
|
help_text="Current status of the profile")
|
|
notes = models.TextField(blank=True, default="", verbose_name="Notes")
|
|
ory_kratos_id = models.CharField(
|
|
max_length=255, unique=True, null=True, blank=True,
|
|
verbose_name="Ory Kratos Identity ID",
|
|
help_text="Unique identifier from Ory Kratos authentication system"
|
|
)
|
|
email = models.EmailField(null=True, blank=True)
|
|
|
|
class Meta:
|
|
abstract = True
|
|
ordering = ['last_name', 'first_name']
|
|
|
|
def save(self, *args, **kwargs):
|
|
# Validate new users against existing profiles (if user is set)
|
|
# Strict enforcement of one profile per user
|
|
if not self.pk and self.user:
|
|
profile_types = [CustomerProfile, TeamProfile]
|
|
current_type = type(self)
|
|
|
|
for profile_type in profile_types:
|
|
if profile_type != current_type:
|
|
if profile_type.objects.filter(user=self.user).exists():
|
|
raise ValidationError(
|
|
f"User already has a {profile_type.__name__}. "
|
|
f"Cannot create {current_type.__name__}."
|
|
)
|
|
super().save(*args, **kwargs)
|
|
|
|
|
|
class CustomerProfile(Profile):
|
|
"""External/public-facing customer accounts"""
|
|
customers = models.ManyToManyField(
|
|
Customer,
|
|
related_name='profiles',
|
|
blank=True,
|
|
help_text="Customers this profile has access to"
|
|
)
|
|
|
|
class Meta:
|
|
verbose_name = "Customer Profile"
|
|
verbose_name_plural = "Customer Profiles"
|
|
|
|
|
|
class TeamProfile(Profile):
|
|
"""Internal team member accounts"""
|
|
role = TextChoicesField(choices_enum=RoleChoices, default=RoleChoices.TEAM_MEMBER,
|
|
help_text="Role of the team member")
|
|
|
|
class Meta:
|
|
verbose_name = "Team Profile"
|
|
verbose_name_plural = "Team Profiles"
|