diff --git a/src/servala/core/models/__init__.py b/src/servala/core/models/__init__.py new file mode 100644 index 0000000..3456eab --- /dev/null +++ b/src/servala/core/models/__init__.py @@ -0,0 +1,15 @@ +from .organization import ( + BillingEntity, + Organization, + OrganizationMembership, + OrganizationOrigin, + OrganizationRole, +) + +__all__ = [ + "BillingEntity", + "Organization", + "OrganizationMembership", + "OrganizationOrigin", + "OrganizationRole", +] diff --git a/src/servala/core/models/organization.py b/src/servala/core/models/organization.py new file mode 100644 index 0000000..371f0ee --- /dev/null +++ b/src/servala/core/models/organization.py @@ -0,0 +1,94 @@ +from django.contrib.auth import get_user_model +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class Organization(models.Model): + name = models.CharField(max_length=100, verbose_name=_("Name")) + + billing_entity = models.ForeignKey( + to="BillingEntity", + on_delete=models.PROTECT, + related_name="organizations", + verbose_name=_("Billing entity"), + ) + origin = models.ForeignKey( + to="OrganizationOrigin", + on_delete=models.PROTECT, + related_name="organizations", + verbose_name=_("Origin"), + ) + + members = models.ManyToManyField( + to=get_user_model(), + through="OrganizationMembership", + related_name="organizations", + verbose_name=_("Members"), + ) + + def __str__(self): + return self.name + + +class BillingEntity(models.Model): + """ + Every organization has a billing entity, though billing entities + may be shared across different organizations. + """ + + name = models.CharField(max_length=100, verbose_name=_("Name")) + description = models.TextField(blank=True, verbose_name=_("Description")) + erp_reference = models.CharField( + max_length=100, blank=True, verbose_name=_("ERP reference") + ) + + def __str__(self): + return self.name + + +class OrganizationOrigin(models.Model): + """ + Every organization has an origin, though origins may be + shared across different organizations. + """ + + name = models.CharField(max_length=100, verbose_name=_("Name")) + description = models.TextField(blank=True, verbose_name=_("Description")) + + def __str__(self): + return self.name + + +class OrganizationRole(models.TextChoices): + MEMBER = "member", _("Member") + ADMIN = "admin", _("Administrator") + OWNER = "owner", _("Owner") + + +class OrganizationMembership(models.Model): + """Through-model for the many-to-many relationship between organizations and users.""" + + user = models.ForeignKey( + to=get_user_model(), + on_delete=models.CASCADE, + related_name="organization_memberships", + verbose_name=_("User"), + ) + organization = models.ForeignKey( + to=Organization, + on_delete=models.CASCADE, + related_name="memberships", + verbose_name=_("Organization"), + ) + date_joined = models.DateTimeField( + verbose_name=_("Date joined"), auto_now_add=True, editable=False + ) + role = models.CharField( + max_length=20, + choices=OrganizationRole.choices, + default=OrganizationRole.MEMBER, + verbose_name=_("Role"), + ) + + def __str__(self): + return f"{self.user} in {self.organization} as {self.role}"