Dynavera/apps/orgs/models.py
2026-01-17 15:51:11 +00:00

94 lines
3.7 KiB
Python

from datetime import timedelta
from uuid import uuid4
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.db.models import BigAutoField, BooleanField, CASCADE, CharField, DateTimeField, ForeignKey, ManyToManyField, Model, TextField, UUIDField
from apps.users.mixins import TimeStampMixin
from apps.users.models import User
class Organization(TimeStampMixin, Model):
id = BigAutoField(primary_key = True)
uuid = UUIDField(default = uuid4, unique = True, editable = False)
name = CharField(max_length = 255, unique = True)
description = TextField(blank = True, default = '')
owner = ForeignKey(User, on_delete = CASCADE, related_name = 'owned_organizations')
members = ManyToManyField(User, through = 'OrganizationMembership', related_name = 'organizations')
class Meta:
verbose_name = _('Organization')
verbose_name_plural = _('Organizations')
def __str__(self) -> str:
return self.name
class OrganizationMembership(TimeStampMixin, Model):
id = BigAutoField(primary_key = True)
user = ForeignKey(User, on_delete = CASCADE, related_name = 'organization_memberships')
organization = ForeignKey(Organization, on_delete = CASCADE, related_name = 'memberships')
is_manager = BooleanField(default = False)
class Meta:
verbose_name = _('Organization Membership')
verbose_name_plural = _('Organization Memberships')
unique_together = [['user', 'organization']]
def __str__(self) -> str:
return f'{self.user.full_name} - {self.organization.name} ({self.is_manager})'
class OrganizationInvitation(TimeStampMixin, Model):
id = BigAutoField(primary_key = True)
token = UUIDField(default = uuid4, unique = True, editable = False)
organization = ForeignKey(Organization, on_delete = CASCADE, related_name = "invite_tokens")
created_by = ForeignKey(User, on_delete = CASCADE, related_name = "created_invites")
expires_at = DateTimeField()
used_by = ForeignKey(User, on_delete = CASCADE, null = True, blank = True, related_name = "used_invites")
used_at = DateTimeField(null = True, blank = True)
is_active = BooleanField(default = True)
class Meta:
verbose_name = _("Invite Token")
verbose_name_plural = _("Invite Tokens")
def save(self, *args, **kwargs):
if not self.expires_at:
self.expires_at = timezone.now() + timedelta(days=7)
super().save(*args, **kwargs)
def is_valid(self):
return self.is_active and not self.used_by and timezone.now() < self.expires_at
def __str__(self) -> str:
return f"Invite for {self.organization.name} by {self.created_by.full_name} (expires {self.expires_at})"
class Role(TimeStampMixin, Model):
id = BigAutoField(primary_key = True)
name = CharField(max_length = 100, unique = True)
uuid = UUIDField(default = uuid4, editable = False, unique = True)
organization = ForeignKey(Organization, on_delete = CASCADE, related_name = "roles")
members = ManyToManyField(User, through = "RoleMembership", related_name = "roles")
class Meta:
verbose_name = _('Role')
verbose_name_plural = _('Roles')
def __str__(self) -> str:
return self.name
class RoleMembership(TimeStampMixin, Model):
user = ForeignKey(User, on_delete = CASCADE, related_name = "role_memberships")
role = ForeignKey(Role, on_delete = CASCADE, related_name = "memberships")
class Meta:
verbose_name = _("Role Membership")
verbose_name_plural = _("Role Memberships")
unique_together = [["user", "role"]]
def __str__(self) -> str:
return f"{self.user.full_name} - {self.role.name}"