Skip to content
Snippets Groups Projects
models.py 9.4 KiB
Newer Older
ynerant's avatar
ynerant committed
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
ynerant's avatar
ynerant committed

ynerant's avatar
ynerant committed
import json
from django.conf import settings
ynerant's avatar
ynerant committed
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext_lazy as _
ynerant's avatar
ynerant committed
from phonenumber_field.modelfields import PhoneNumberField

ynerant's avatar
ynerant committed
from member.models import Club, Membership
from note.models import MembershipTransaction
ynerant's avatar
ynerant committed
from permission.models import Role
ynerant's avatar
ynerant committed
class WEIClub(Club):
ynerant's avatar
ynerant committed
    """
ynerant's avatar
ynerant committed
    The WEI is a club. Register to the WEI is equivalent than be member of the club.
ynerant's avatar
ynerant committed
    """
    year = models.PositiveIntegerField(
        unique=True,
ynerant's avatar
ynerant committed
        verbose_name=_("year"),
    )

ynerant's avatar
ynerant committed
    date_start = models.DateField(
        verbose_name=_("date start"),
    )

    date_end = models.DateField(
        verbose_name=_("date end"),
    )

ynerant's avatar
ynerant committed
    @property
    def is_current_wei(self):
        """
        We consider that this is the current WEI iff there is no future WEI planned.
        """
ynerant's avatar
ynerant committed
        return not WEIClub.objects.filter(date_start__gt=self.date_start).exists()

ynerant's avatar
ynerant committed
    def update_membership_dates(self):
        """
        We can't join the WEI next years.
        """
        return
ynerant's avatar
ynerant committed
    class Meta:
        verbose_name = _("WEI")
        verbose_name_plural = _("WEI")

ynerant's avatar
ynerant committed

class Bus(models.Model):
    """
    The best bus for the best WEI
    """
    wei = models.ForeignKey(
ynerant's avatar
ynerant committed
        WEIClub,
ynerant's avatar
ynerant committed
        on_delete=models.PROTECT,
        related_name="buses",
        verbose_name=_("WEI"),
    )

    name = models.CharField(
        max_length=255,
        verbose_name=_("name"),
    )

ynerant's avatar
ynerant committed
    description = models.TextField(
        blank=True,
        default="",
        verbose_name=_("description"),
    )

ynerant's avatar
ynerant committed
    information_json = models.TextField(
        default="{}",
        verbose_name=_("survey information"),
        help_text=_("Information about the survey for new members, encoded in JSON"),
    )

    @property
    def information(self):
        """
        The information about the survey for new members are stored in a dictionary that can evolve following the years.
         The dictionary is stored as a JSON string.
        """
        return json.loads(self.information_json)

    @information.setter
    def information(self, information):
        """
        Store information as a JSON string
        """
        self.information_json = json.dumps(information)

ynerant's avatar
ynerant committed
    def __str__(self):
        return self.name

    class Meta:
ynerant's avatar
ynerant committed
        verbose_name = _("Bus")
        verbose_name_plural = _("Buses")
ynerant's avatar
ynerant committed
        unique_together = ('wei', 'name',)


class BusTeam(models.Model):
    """
    A bus has multiple teams
    """
    bus = models.ForeignKey(
        Bus,
        on_delete=models.CASCADE,
        related_name="teams",
        verbose_name=_("bus"),
    )

    name = models.CharField(
        max_length=255,
ynerant's avatar
ynerant committed
        verbose_name=_("name"),
ynerant's avatar
ynerant committed
    )

    color = models.PositiveIntegerField(  # Use a color picker to get the hexa code
        verbose_name=_("color"),
        help_text=_("The color of the T-Shirt, stored with its number equivalent"),
    )

ynerant's avatar
ynerant committed
    description = models.TextField(
        blank=True,
        default="",
        verbose_name=_("description"),
    )

ynerant's avatar
ynerant committed
    def __str__(self):
        return self.name + " (" + str(self.bus) + ")"

    class Meta:
        unique_together = ('bus', 'name',)
        verbose_name = _("Bus team")
        verbose_name_plural = _("Bus teams")


ynerant's avatar
ynerant committed
class WEIRole(Role):
ynerant's avatar
ynerant committed
    """
    A Role for the WEI can be bus chief, team chief, free electron, ...
    """
ynerant's avatar
ynerant committed

    class Meta:
        verbose_name = _("WEI Role")
        verbose_name_plural = _("WEI Roles")
ynerant's avatar
ynerant committed
class WEIRegistration(models.Model):
ynerant's avatar
ynerant committed
    """
    Store personal data that can be useful for the WEI.
    """

    user = models.ForeignKey(
        User,
        on_delete=models.PROTECT,
        related_name="wei",
        verbose_name=_("user"),
    )

    wei = models.ForeignKey(
ynerant's avatar
ynerant committed
        WEIClub,
ynerant's avatar
ynerant committed
        on_delete=models.PROTECT,
        related_name="users",
        verbose_name=_("WEI"),
    )

ynerant's avatar
ynerant committed
    soge_credit = models.BooleanField(
ynerant's avatar
ynerant committed
        default=False,
ynerant's avatar
ynerant committed
        verbose_name=_("Credit from Société générale"),
ynerant's avatar
ynerant committed
    caution_check = models.BooleanField(
        default=False,
        verbose_name=_("Caution check given")
    )

ynerant's avatar
ynerant committed
    birth_date = models.DateField(
        verbose_name=_("birth date"),
    )

    gender = models.CharField(
        max_length=16,
        choices=(
            ('male', _("Male")),
            ('female', _("Female")),
            ('nonbinary', _("Non binary")),
        ),
        verbose_name=_("gender"),
    )

    clothing_cut = models.CharField(
        max_length=16,
        choices=(
            ('male', _("Male")),
            ('female', _("Female")),
        ),
        verbose_name=_("clothing cut"),
    )

    clothing_size = models.CharField(
        max_length=4,
        choices=(
ynerant's avatar
ynerant committed
            ('XS', "XS"),
            ('S', "S"),
            ('M', "M"),
            ('L', "L"),
            ('XL', "XL"),
            ('XXL', "XXL"),
        ),
        verbose_name=_("clothing size"),
    )

ynerant's avatar
ynerant committed
    health_issues = models.TextField(
        blank=True,
        default="",
ynerant's avatar
ynerant committed
        verbose_name=_("health issues"),
    )

    emergency_contact_name = models.CharField(
        max_length=255,
        verbose_name=_("emergency contact name"),
    )

ynerant's avatar
ynerant committed
    emergency_contact_phone = PhoneNumberField(
ynerant's avatar
ynerant committed
        max_length=32,
        verbose_name=_("emergency contact phone"),
    )

ynerant's avatar
ynerant committed
    ml_events_registration = models.BooleanField(
ynerant's avatar
ynerant committed
        default=False,
ynerant's avatar
ynerant committed
        verbose_name=_("Register on the mailing list to stay informed of the events of the campus (1 mail/week)"),
    )

ynerant's avatar
ynerant committed
    ml_sport_registration = models.BooleanField(
ynerant's avatar
ynerant committed
        default=False,
ynerant's avatar
ynerant committed
        verbose_name=_("Register on the mailing list to stay informed of the sport events of the campus (1 mail/week)"),
    )

ynerant's avatar
ynerant committed
    ml_art_registration = models.BooleanField(
ynerant's avatar
ynerant committed
        default=False,
ynerant's avatar
ynerant committed
        verbose_name=_("Register on the mailing list to stay informed of the art events of the campus (1 mail/week)"),
    )

    first_year = models.BooleanField(
        default=False,
        verbose_name=_("first year"),
        help_text=_("Tells if the user is new in the school.")
    )

ynerant's avatar
ynerant committed
    information_json = models.TextField(
ynerant's avatar
ynerant committed
        default="{}",
ynerant's avatar
ynerant committed
        verbose_name=_("registration information"),
        help_text=_("Information about the registration (buses for old members, survey fot the new members), "
                    "encoded in JSON"),
ynerant's avatar
ynerant committed
    @property
    def information(self):
        """
        The information about the registration (the survey for the new members, the bus for the older members, ...)
        are stored in a dictionary that can evolve following the years. The dictionary is stored as a JSON string.
        """
        return json.loads(self.information_json)

    @information.setter
    def information(self, information):
        """
        Store information as a JSON string
        """
        self.information_json = json.dumps(information)

ynerant's avatar
ynerant committed
    @property
    def is_validated(self):
        try:
            return self.membership is not None
        except AttributeError:
ynerant's avatar
ynerant committed
            return False

ynerant's avatar
ynerant committed
    def __str__(self):
        return str(self.user)

    class Meta:
        unique_together = ('user', 'wei',)
        verbose_name = _("WEI User")
        verbose_name_plural = _("WEI Users")


class WEIMembership(Membership):
    bus = models.ForeignKey(
ynerant's avatar
ynerant committed
        Bus,
        on_delete=models.PROTECT,
ynerant's avatar
ynerant committed
        related_name="memberships",
ynerant's avatar
ynerant committed
        null=True,
        default=None,
        verbose_name=_("bus"),
ynerant's avatar
ynerant committed
    team = models.ForeignKey(
        BusTeam,
ynerant's avatar
ynerant committed
        on_delete=models.PROTECT,
ynerant's avatar
ynerant committed
        related_name="memberships",
ynerant's avatar
ynerant committed
        null=True,
        blank=True,
ynerant's avatar
ynerant committed
        default=None,
        verbose_name=_("team"),
ynerant's avatar
ynerant committed
    registration = models.OneToOneField(
        WEIRegistration,
ynerant's avatar
ynerant committed
        on_delete=models.PROTECT,
        null=True,
        blank=True,
ynerant's avatar
ynerant committed
        default=None,
        related_name="membership",
        verbose_name=_("WEI registration"),
ynerant's avatar
ynerant committed

    class Meta:
        verbose_name = _("WEI membership")
        verbose_name_plural = _("WEI memberships")
ynerant's avatar
ynerant committed

    def make_transaction(self):
        """
        Create Membership transaction associated to this membership.
        """
        if not self.fee or MembershipTransaction.objects.filter(membership=self).exists():
            return

        if self.fee:
            transaction = MembershipTransaction(
                membership=self,
                source=self.user.note,
                destination=self.club.note,
                quantity=1,
                amount=self.fee,
                reason="Adhésion WEI " + self.club.name,
                valid=not self.registration.soge_credit  # Soge transactions are by default invalidated
            )
            transaction._force_save = True
            transaction.save(force_insert=True)

            if self.registration.soge_credit and "treasury" in settings.INSTALLED_APPS:
                # If the soge pays, then the transaction is unvalidated in a first time, then submitted for control
                # to treasurers.
                transaction.refresh_from_db()
                from treasury.models import SogeCredit
                soge_credit = SogeCredit.objects.get_or_create(user=self.user)[0]
                soge_credit.refresh_from_db()
                transaction.save()
                soge_credit.transactions.add(transaction)
                soge_credit.save()