diff --git a/apps/MySU/admin/association_membership.py b/apps/MySU/admin/association_membership.py index d27c5fbc43942a3e5c8618ff61b85257ead036cf..0117ba6059e69590a6710908fdfcc53c20f723ff 100644 --- a/apps/MySU/admin/association_membership.py +++ b/apps/MySU/admin/association_membership.py @@ -1,11 +1,50 @@ from django.contrib import admin +from django import forms from django_admin_listfilter_dropdown.filters import RelatedDropdownFilter +from django.core.exceptions import ValidationError from apps.MySU.models import AssociationMembership +class AssociationMembershipForm(forms.ModelForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['user'].required = True + + class Meta: + model = AssociationMembership + fields = '__all__' + + def clean(self): + cleaned_data = super().clean() + association = cleaned_data.get("association") + user = cleaned_data.get("user") + profile = cleaned_data.get("profile") + type_a = cleaned_data.get("type") + new_type = cleaned_data.get("new_type") + change_type_on_period_end = cleaned_data.get("change_type_on_period_end") + date_left = cleaned_data.get("date_left") + date_joined = cleaned_data.get("date_joined") + if user is not None: + if user != profile.user: + raise ValidationError("Profile must belong to the user") + if association is not None: + if type_a.association != association: + raise ValidationError("Membertype must belong to the association") + if new_type: + if new_type.association != self.association: + raise ValidationError("Membertype must belong to the association") + if change_type_on_period_end and new_type is None: + raise ValidationError("You cannot say you change your type, but not have a type to change to") + if date_left: + if date_left < date_joined: + raise ValidationError( + {'date_left': "End date must be after start date."}, + ) + @admin.register(AssociationMembership) class AssociationMembershipAdmin(admin.ModelAdmin): + form = AssociationMembershipForm list_display = ( 'slug', 'user', diff --git a/apps/MySU/models/association_membership.py b/apps/MySU/models/association_membership.py index 45f6b03feccf792e7ab4da5d9d011e257bd50b90..1eb8bcd8ad387d0ecfb3646673b86c84c941ccea 100644 --- a/apps/MySU/models/association_membership.py +++ b/apps/MySU/models/association_membership.py @@ -28,7 +28,8 @@ class AssociationMembershipQuerySet(models.QuerySet): ) def active(self): - return self.filter(profile__group_member__association=F('association'), profile__group_member__date_left__isnull=True) + return self.filter(profile__group_member__association=F('association'), + profile__group_member__date_left__isnull=True) class AssociationMembershipManager(models.Manager): @@ -125,6 +126,7 @@ class AssociationMembership(ComputedFieldsModel): a new member type or keep the same. If change_type_on_period_end is true, the new_type will be used as the member type for the new period. """ + class Status(models.TextChoices): ACCEPTED = "Accepted", "Accepted" CLAIMED = "Claimed", "Claimed" @@ -156,8 +158,10 @@ class AssociationMembership(ComputedFieldsModel): sepa_mandate = models.ForeignKey('financial.SepaMandate', on_delete=models.PROTECT, null=True, blank=True) user = models.ForeignKey('MySU.User', on_delete=models.PROTECT, null=True, blank=True) type = models.ForeignKey(MemberType, on_delete=models.PROTECT, related_name='memberships') - new_type = models.ForeignKey(MemberType, on_delete=models.PROTECT, related_name='new_memberships', null=True, blank=True) - association = models.ForeignKey(Association, on_delete=models.PROTECT, related_name='memberships') + new_type = models.ForeignKey(MemberType, on_delete=models.PROTECT, related_name='new_memberships', null=True, + blank=True) + association = models.ForeignKey(Association, on_delete=models.PROTECT, related_name='memberships', null=False, + blank=False) data_fields = models.ManyToManyField('AssociationSpecificDataFields', through='AssociationSpecificData', related_name='association_memberships') @@ -181,23 +185,6 @@ class AssociationMembership(ComputedFieldsModel): def __str__(self): return "{slug}".format(slug=self.slug) - def clean(self): - if self.user_id: - if self.user != self.profile.user: - raise ValidationError("Profile must belong to the user") - if self.type.association != self.association: - raise ValidationError("Membertype must belong to the association") - if self.new_type: - if self.new_type.association != self.association: - raise ValidationError("Membertype must belong to the association") - if self.change_type_on_period_end and self.new_type is None: - raise ValidationError("You cannot say you change your type, but not have a type to change to") - if self.date_left: - if self.date_left < self.date_joined: - raise ValidationError( - {'date_left': "End date must be after start date."}, - ) - def save(self, *args, **kwargs): if self.date_left and not self.confirmation_of_request_to_end_email_send: self.send_confirmation_of_request_to_end_email(self.profile, self.association) @@ -224,12 +211,12 @@ class AssociationMembership(ComputedFieldsModel): to_email = [profile.email] subject = f"Confirmation of request to end membership of {association.short_name}" message = f"Hi {profile.given_name}\n\n" \ - f"You have indicated that you no longer want to be a member of {association.name}. Your membership will end by " \ - f"{association.full_year_membership_start_date.with_year_in_future()}. Afterwards your data will still be visible to the board until " \ - f"they have indicated that you have fulfilled your financial obligations. You will be informed when they do." \ - f"Kind regards\n\n" \ - f"The MySU team\n\n" \ - f"This is an automatically generated email. Please contact the board of {association.name} if you have any questions." + f"You have indicated that you no longer want to be a member of {association.name}. Your membership will end by " \ + f"{association.full_year_membership_start_date.with_year_in_future()}. Afterwards your data will still be visible to the board until " \ + f"they have indicated that you have fulfilled your financial obligations. You will be informed when they do." \ + f"Kind regards\n\n" \ + f"The MySU team\n\n" \ + f"This is an automatically generated email. Please contact the board of {association.name} if you have any questions." send_mail(subject=subject, message=message, from_email=from_email, recipient_list=to_email) @staticmethod @@ -238,13 +225,13 @@ class AssociationMembership(ComputedFieldsModel): to_email = [profile.email] subject = f"Membership of {association.short_name} accepted" message = f"Hi {profile.given_name}\n\n" \ - f"When you ended your membership you received an email that informed you the board will still be able to see " \ - f"your data until you have fulfilled your financial obligations. The {association.name} has now indicated you have " \ - f"fulfilled all your financial obligations.\n\n" \ - f"Your data will no longer be visible to {association.name}.\n\n" \ - f"Kind regards\n\n" \ - f"The MySU team\n\n" \ - f"This is an automatically generated email. Please contact the board of {association.name} if you have any questions." + f"When you ended your membership you received an email that informed you the board will still be able to see " \ + f"your data until you have fulfilled your financial obligations. The {association.name} has now indicated you have " \ + f"fulfilled all your financial obligations.\n\n" \ + f"Your data will no longer be visible to {association.name}.\n\n" \ + f"Kind regards\n\n" \ + f"The MySU team\n\n" \ + f"This is an automatically generated email. Please contact the board of {association.name} if you have any questions." send_mail(subject=subject, message=message, from_email=from_email, recipient_list=to_email) # pylint: disable=arguments-differ @@ -254,12 +241,12 @@ class AssociationMembership(ComputedFieldsModel): to_email = [profile.email] subject = f"Membership of {association.short_name} ended" message = f"Hi {profile.given_name}\n\n" \ - f"Your membership with {association.name} has ended. \n\n" \ - f"Once the board of {association.name} has indicated that you have fulfilled all your financial obligations, " \ - f"they will no longer be able to see your data. You will be informed when they do.\n\n" \ - f"Kind regards\n\n" \ - f"The MySU team\n\n" \ - f"This is an automatically generated email. Please contact the board of {association.name} if you have any questions." + f"Your membership with {association.name} has ended. \n\n" \ + f"Once the board of {association.name} has indicated that you have fulfilled all your financial obligations, " \ + f"they will no longer be able to see your data. You will be informed when they do.\n\n" \ + f"Kind regards\n\n" \ + f"The MySU team\n\n" \ + f"This is an automatically generated email. Please contact the board of {association.name} if you have any questions." send_mail(subject=subject, message=message, from_email=from_email, recipient_list=to_email) @staticmethod @@ -268,10 +255,10 @@ class AssociationMembership(ComputedFieldsModel): to_email = [profile.email] subject = f"Membership of {association.short_name} accepted" message = f"Hi {profile.given_name}\n\n" \ - f"Your membership request for {association.name} has been accepted.\n\n" \ - f"Kind regards\n\n" \ - f"The MySU team\n\n" \ - f"This is an automatically generated email. Please contact the board of {association.name} if you have any questions." + f"Your membership request for {association.name} has been accepted.\n\n" \ + f"Kind regards\n\n" \ + f"The MySU team\n\n" \ + f"This is an automatically generated email. Please contact the board of {association.name} if you have any questions." send_mail(subject=subject, message=message, from_email=from_email, recipient_list=to_email) @staticmethod