gh_UserManager/apps/api/apps/membership/models.py
bermooda-company 54d5891edf user
2026-08-23 23:59:14 +03:30

165 lines
4.7 KiB
Python

from django.db import models
from apps.common.models import BaseModel
class MembershipRole(models.TextChoices):
OWNER = "owner", "Owner"
ADMIN = "admin", "Admin"
MEMBER = "member", "Member"
class MembershipStatus(models.TextChoices):
ACTIVE = "active", "Active"
INVITED = "invited", "Invited"
PENDING = "pending", "Pending"
DEACTIVATED = "deactivated", "Deactivated"
class Membership(BaseModel):
user = models.ForeignKey(
"identity.User",
on_delete=models.CASCADE,
related_name="memberships",
)
organization = models.ForeignKey(
"organization.Organization",
on_delete=models.CASCADE,
related_name="memberships",
)
role = models.CharField(
max_length=16,
choices=MembershipRole.choices,
default=MembershipRole.MEMBER,
)
status = models.CharField(
max_length=16,
choices=MembershipStatus.choices,
default=MembershipStatus.ACTIVE,
)
joined_at = models.DateTimeField(auto_now_add=True)
ended_at = models.DateTimeField(
null=True, blank=True, help_text="Membership end date (تاریخ پایان)"
)
invited_by = models.ForeignKey(
"identity.User",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="invitations_sent",
)
class Meta:
ordering = ["-created_at"]
constraints = [
models.UniqueConstraint(
fields=["user", "organization"],
name="unique_membership",
)
]
indexes = [
models.Index(fields=["user"]),
models.Index(fields=["organization"]),
]
def __str__(self):
return f"{self.user} @ {self.organization}"
class Ownership(BaseModel):
"""Organization ownership history and current primary owner."""
class OwnershipType(models.TextChoices):
PRIMARY = "primary", "Primary Owner"
TRANSFERRED = "transferred", "Transferred Owner"
organization = models.ForeignKey(
"organization.Organization",
on_delete=models.CASCADE,
related_name="ownership_history",
)
owner = models.ForeignKey(
"identity.User",
on_delete=models.CASCADE,
related_name="ownership_transactions",
)
ownership_type = models.CharField(
max_length=16,
choices=OwnershipType.choices,
default=OwnershipType.PRIMARY,
)
transferred_from = models.ForeignKey(
"identity.User",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="ownership_transferred_from",
)
transferred_at = models.DateTimeField(null=True, blank=True)
reason = models.CharField(max_length=255, blank=True, default="")
class Meta:
ordering = ["-transferred_at"]
indexes = [
models.Index(fields=["organization", "ownership_type"]),
models.Index(fields=["owner"]),
]
def __str__(self):
return f"{self.get_ownership_type_display()} ownership of {self.organization} by {self.owner}"
class Invitation(BaseModel):
"""Invitation to join an organization."""
class InvitationStatus(models.TextChoices):
PENDING = "pending", "Pending"
ACCEPTED = "accepted", "Accepted"
REJECTED = "rejected", "Rejected"
EXPIRED = "expired", "Expired"
organization = models.ForeignKey(
"organization.Organization",
on_delete=models.CASCADE,
related_name="invitations",
)
email = models.EmailField()
invited_by = models.ForeignKey(
"identity.User",
on_delete=models.CASCADE,
related_name="invitations_created",
)
role = models.CharField(
max_length=16,
choices=MembershipRole.choices,
default=MembershipRole.MEMBER,
)
token = models.CharField(max_length=64, unique=True)
status = models.CharField(
max_length=16,
choices=InvitationStatus.choices,
default=InvitationStatus.PENDING,
)
created_at = models.DateTimeField(auto_now_add=True)
expires_at = models.DateTimeField()
accepted_at = models.DateTimeField(null=True, blank=True)
class Meta:
ordering = ["-created_at"]
indexes = [
models.Index(fields=["organization", "status"]),
models.Index(fields=["token"]),
]
def __str__(self):
return f"Invitation for {self.email} to join {self.organization}"
def is_valid(self):
from django.utils import timezone
return (
self.status == InvitationStatus.PENDING
and not self.expires_at < timezone.now()
and self.accepted_at is None
)