From a096dc44271dcd46769c197bd1419a08a189d89c Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <yohann.danello@gmail.com>
Date: Tue, 4 Aug 2020 20:04:41 +0200
Subject: [PATCH] :sparkles: Adhere to parent clubs automatically, adhere to
 Kfet automatically when registering to the WEI

---
 apps/member/models.py                   |  90 +++++--
 apps/member/views.py                    | 137 +++++++----
 apps/treasury/views.py                  |  19 +-
 apps/wei/forms/registration.py          |  31 +++
 apps/wei/tables.py                      |  33 ++-
 apps/wei/views.py                       | 128 ++++++++--
 locale/fr/LC_MESSAGES/django.po         | 301 ++++++++++++++----------
 templates/base.html                     |  12 +-
 templates/member/add_members.html       |  27 ++-
 templates/member/club_detail.html       |   2 -
 templates/member/profile_detail.html    |   6 +-
 templates/member/profile_info.html      |  28 +--
 templates/member/profile_tables.html    |   6 +-
 templates/wei/weiclub_detail.html       |  12 +-
 templates/wei/weimembership_form.html   |  12 +-
 templates/wei/weiregistration_list.html |  10 +
 16 files changed, 588 insertions(+), 266 deletions(-)

diff --git a/apps/member/models.py b/apps/member/models.py
index 5d6544af..7bde2680 100644
--- a/apps/member/models.py
+++ b/apps/member/models.py
@@ -10,6 +10,7 @@ from django.core.exceptions import ValidationError
 from django.db import models
 from django.template import loader
 from django.urls import reverse, reverse_lazy
+from django.utils import timezone
 from django.utils.encoding import force_bytes
 from django.utils.http import urlsafe_base64_encode
 from django.utils.translation import gettext_lazy as _
@@ -301,13 +302,34 @@ class Membership(models.Model):
         else:
             return self.date_start.toordinal() <= datetime.datetime.now().toordinal()
 
+    def renew(self):
+        if Membership.objects.filter(
+                user=self.user,
+                club=self.club,
+                date_start__gte=self.club.membership_start,
+        ).exists():
+            # Membership is already renewed
+            return
+        new_membership = Membership(
+            user=self.user,
+            club=self.club,
+            date_start=max(self.date_end + datetime.timedelta(days=1), self.club.membership_start),
+        )
+        from django.forms import model_to_dict
+        if hasattr(self, '_force_renew_parent') and self._force_renew_parent:
+            new_membership._force_renew_parent = True
+        if hasattr(self, '_soge') and self._soge:
+            new_membership._soge = True
+        if hasattr(self, '_force_save') and self._force_save:
+            new_membership._force_save = True
+        new_membership.save()
+        new_membership.roles.set(self.roles.all())
+        new_membership.save()
+
     def save(self, *args, **kwargs):
         """
         Calculate fee and end date before saving the membership and creating the transaction if needed.
         """
-        if self.club.parent_club is not None:
-            if not Membership.objects.filter(user=self.user, club=self.club.parent_club).exists():
-                raise ValidationError(_('User is not a member of the parent club') + ' ' + self.club.parent_club.name)
 
         if self.pk:
             for role in self.roles.all():
@@ -327,17 +349,54 @@ class Membership(models.Model):
             ).exists():
                 raise ValidationError(_('User is already a member of the club'))
 
-            if self.user.profile.paid:
-                self.fee = self.club.membership_fee_paid
-            else:
-                self.fee = self.club.membership_fee_unpaid
+        if self.club.parent_club is not None:
+            if not Membership.objects.filter(
+                user=self.user,
+                club=self.club.parent_club,
+                date_start__gte=self.club.parent_club.membership_start,
+            ).exists():
+                if hasattr(self, '_force_renew_parent') and self._force_renew_parent:
+                    parent_membership = Membership.objects.filter(
+                        user=self.user,
+                        club=self.club.parent_club,
+                    ).order_by("-date_start")
+                    if parent_membership.exists():
+                        # Renew the previous membership of the parent club
+                        parent_membership = parent_membership.first()
+                        parent_membership._force_renew_parent = True
+                        if hasattr(self, '_soge'):
+                            parent_membership._soge = True
+                        if hasattr(self, '_force_save'):
+                            parent_membership._force_save = True
+                        parent_membership.renew()
+                    else:
+                        # Create a new membership in the parent club
+                        parent_membership = Membership(
+                            user=self.user,
+                            club=self.club.parent_club,
+                            date_start=self.date_start,
+                        )
+                        parent_membership._force_renew_parent = True
+                        if hasattr(self, '_soge'):
+                            parent_membership._soge = True
+                        if hasattr(self, '_force_save'):
+                            parent_membership._force_save = True
+                        parent_membership.save()
+                else:
+                    raise ValidationError(_('User is not a member of the parent club')
+                                          + ' ' + self.club.parent_club.name)
+
+        if self.user.profile.paid:
+            self.fee = self.club.membership_fee_paid
+        else:
+            self.fee = self.club.membership_fee_unpaid
 
-            if self.club.membership_duration is not None:
-                self.date_end = self.date_start + datetime.timedelta(days=self.club.membership_duration)
-            else:
-                self.date_end = self.date_start + datetime.timedelta(days=424242)
-            if self.club.membership_end is not None and self.date_end > self.club.membership_end:
-                self.date_end = self.club.membership_end
+        if self.club.membership_duration is not None:
+            self.date_end = self.date_start + datetime.timedelta(days=self.club.membership_duration)
+        else:
+            self.date_end = self.date_start + datetime.timedelta(days=424242)
+        if self.club.membership_end is not None and self.date_end > self.club.membership_end:
+            self.date_end = self.club.membership_end
 
         super().save(*args, **kwargs)
 
@@ -360,8 +419,9 @@ class Membership(models.Model):
                 reason="Adhésion " + self.club.name,
             )
             transaction._force_save = True
-            print(hasattr(self, '_soge'))
-            if hasattr(self, '_soge') and "treasury" in settings.INSTALLED_APPS:
+            if hasattr(self, '_soge') and "treasury" in settings.INSTALLED_APPS\
+                    and (self.club.name == "BDE" or self.club.name == "Kfet" or
+                         ("wei" in settings.INSTALLED_APPS and hasattr(self.club, "weiclub") and self.club.weiclub)):
                 # If the soge pays, then the transaction is unvalidated in a first time, then submitted for control
                 # to treasurers.
                 transaction.valid = False
diff --git a/apps/member/views.py b/apps/member/views.py
index edcb0feb..cc1264a1 100644
--- a/apps/member/views.py
+++ b/apps/member/views.py
@@ -442,6 +442,16 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
                 .get(pk=self.kwargs["club_pk"], weiclub=None)
             form.fields['credit_amount'].initial = club.membership_fee_paid
 
+            c = club
+            clubs_renewal = []
+            additional_fee_renewal = 0
+            while c.parent_club is not None:
+                c = c.parent_club
+                clubs_renewal.append(c)
+                additional_fee_renewal += c.membership_fee_paid if user.profile.paid else c.membership_fee_unpaid
+            context["clubs_renewal"] = clubs_renewal
+            context["additional_fee_renewal"] = additional_fee_renewal
+
             # If the concerned club is the BDE, then we add the option that Société générale pays the membership.
             if club.name != "BDE":
                 del form.fields['soge']
@@ -454,26 +464,54 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
                 context["total_fee"] = "{:.02f}".format(fee / 100, )
         else:
             # This is a renewal. Fields can be pre-completed.
+            context["renewal"] = True
+
             old_membership = self.get_queryset().get(pk=self.kwargs["pk"])
             club = old_membership.club
             user = old_membership.user
+
+            c = club
+            clubs_renewal = []
+            additional_fee_renewal = 0
+            while c.parent_club is not None:
+                c = c.parent_club
+                if not Membership.objects.filter(
+                        club=c,
+                        user=user,
+                        date_start__gte=c.membership_start,
+                ).exists():
+                    clubs_renewal.append(c)
+                    additional_fee_renewal += c.membership_fee_paid if user.profile.paid else c.membership_fee_unpaid
+            context["clubs_renewal"] = clubs_renewal
+            context["additional_fee_renewal"] = additional_fee_renewal
+
             form.fields['user'].initial = user
             form.fields['user'].disabled = True
             form.fields['date_start'].initial = old_membership.date_end + timedelta(days=1)
-            form.fields['credit_amount'].initial = club.membership_fee_paid if user.profile.paid \
-                else club.membership_fee_unpaid
+            form.fields['credit_amount'].initial = (club.membership_fee_paid if user.profile.paid
+                else club.membership_fee_unpaid) + additional_fee_renewal
             form.fields['last_name'].initial = user.last_name
             form.fields['first_name'].initial = user.first_name
 
             # If this is a renewal of a BDE membership, Société générale can pays, if it is not yet done
-            if club.name != "BDE" or user.profile.soge:
+            if (club.name != "BDE" and club.name != "Kfet") or user.profile.soge:
                 del form.fields['soge']
             else:
                 fee = 0
                 bde = Club.objects.get(name="BDE")
-                fee += bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid
+                if not Membership.objects.filter(
+                    club=bde,
+                    user=user,
+                    date_start__gte=bde.membership_start,
+                ).exists():
+                    fee += bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid
                 kfet = Club.objects.get(name="Kfet")
-                fee += kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
+                if not Membership.objects.filter(
+                    club=kfet,
+                    user=user,
+                    date_start__gte=bde.membership_start,
+                ).exists():
+                    fee += kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
                 context["total_fee"] = "{:.02f}".format(fee / 100, )
 
         context['club'] = club
@@ -502,7 +540,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
         last_name = form.cleaned_data["last_name"]
         first_name = form.cleaned_data["first_name"]
         bank = form.cleaned_data["bank"]
-        soge = form.cleaned_data["soge"] and not user.profile.soge and club.name == "BDE"
+        soge = form.cleaned_data["soge"] and not user.profile.soge and (club.name == "BDE" or club.name == "Kfet")
 
         # If Société générale pays, then we store that information but the payment must be controlled by treasurers
         # later. The membership transaction will be invalidated.
@@ -513,10 +551,17 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
         if credit_type is None:
             credit_amount = 0
 
-        if user.profile.paid:
-            fee = club.membership_fee_paid
-        else:
-            fee = club.membership_fee_unpaid
+        fee = 0
+        c = club
+        while c is not None:
+            if not Membership.objects.filter(
+                    club=c,
+                    user=user,
+                    date_start__gte=c.membership_start,
+            ).exists():
+                fee += c.membership_fee_paid if user.profile.paid else c.membership_fee_unpaid
+            c = c.parent_club
+
         if user.note.balance + credit_amount < fee and not Membership.objects.filter(
                 club__name="Kfet",
                 user=user,
@@ -524,22 +569,11 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
                 date_end__gte=datetime.now().date(),
         ).exists():
             # Users without a valid Kfet membership can't have a negative balance.
-            # Club 2 = Kfet (hard-code :'( )
             # TODO Send a notification to the user (with a mail?) to tell her/him to credit her/his note
             form.add_error('user',
                            _("This user don't have enough money to join this club, and can't have a negative balance."))
             return super().form_invalid(form)
 
-        if club.parent_club is not None:
-            if not Membership.objects.filter(
-                user=form.instance.user,
-                club=club.parent_club,
-                date_start__lte=form.instance.date_start,
-                date_end__gte=form.instance.date_start,
-            ).exists():
-                form.add_error('user', _('User is not a member of the parent club') + ' ' + club.parent_club.name)
-                return super().form_invalid(form)
-
         if Membership.objects.filter(
                 user=form.instance.user,
                 club=club,
@@ -561,7 +595,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
 
         # Now, all is fine, the membership can be created.
 
-        if club.name == "BDE":
+        if club.name == "BDE" or club.name == "Kfet":
             # When we renew the BDE membership, we update the profile section.
             # We could automate that and remove the section field from the Profile model,
             # but with this way users can customize their section as they want.
@@ -593,6 +627,8 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
             transaction._force_save = True
             transaction.save()
 
+        form.instance._force_renew_parent = True
+
         ret = super().form_valid(form)
 
         member_role = Role.objects.filter(name="Membre de club").all()
@@ -603,33 +639,40 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
         # If Société générale pays, then we assume that this is the BDE membership, and we auto-renew the
         # Kfet membership.
         if soge:
+            # If not already done, create BDE and Kfet memberships
+            bde = Club.objects.get(name="BDE")
             kfet = Club.objects.get(name="Kfet")
-            kfet_fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
 
-            # Get current membership, to get the end date
-            old_membership = Membership.objects.filter(
-                club__name="Kfet",
-                user=user,
-                date_start__lte=datetime.today(),
-                date_end__gte=datetime.today(),
-            )
-
-            membership = Membership(
-                club=kfet,
-                user=user,
-                fee=kfet_fee,
-                date_start=old_membership.get().date_end + timedelta(days=1)
-                if old_membership.exists() else form.instance.date_start,
-            )
-            membership._force_save = True
-            membership._soge = True
-            membership.save()
-            membership.refresh_from_db()
-            if old_membership.exists():
-                membership.roles.set(old_membership.get().roles.all())
-            else:
-                membership.roles.add(Role.objects.get(name="Adhérent Kfet"))
-            membership.save()
+            soge_clubs = [bde, kfet]
+            for club in soge_clubs:
+                fee = club.membership_fee_paid if user.profile.paid else club.membership_fee_unpaid
+
+                # Get current membership, to get the end date
+                old_membership = Membership.objects.filter(
+                    club=club,
+                    user=user,
+                ).order_by("-date_start")
+
+                if old_membership.filter(date_start__gte=club.membership_start).exists():
+                    # Membership is already renewed
+                    continue
+
+                membership = Membership(
+                    club=club,
+                    user=user,
+                    fee=fee,
+                    date_start=max(old_membership.first().date_end + timedelta(days=1), club.membership_start)
+                    if old_membership.exists() else form.instance.date_start,
+                )
+                membership._force_save = True
+                membership._soge = True
+                membership.save()
+                membership.refresh_from_db()
+                if old_membership.exists():
+                    membership.roles.set(old_membership.get().roles.all())
+                else:
+                    membership.roles.add(Role.objects.get(name="Adhérent Kfet"))
+                membership.save()
 
         return ret
 
diff --git a/apps/treasury/views.py b/apps/treasury/views.py
index 22215254..088345b8 100644
--- a/apps/treasury/views.py
+++ b/apps/treasury/views.py
@@ -353,18 +353,13 @@ class SogeCreditListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableVi
         qs = super().get_queryset()
         if "search" in self.request.GET:
             pattern = self.request.GET["search"]
-
-            if not pattern:
-                return qs.none()
-
-            qs = qs.filter(
-                Q(user__first_name__iregex=pattern)
-                | Q(user__last_name__iregex=pattern)
-                | Q(user__note__alias__name__iregex="^" + pattern)
-                | Q(user__note__alias__normalized_name__iregex="^" + Alias.normalize(pattern))
-            )
-        else:
-            qs = qs.none()
+            if pattern:
+                qs = qs.filter(
+                    Q(user__first_name__iregex=pattern)
+                    | Q(user__last_name__iregex=pattern)
+                    | Q(user__note__alias__name__iregex="^" + pattern)
+                    | Q(user__note__alias__normalized_name__iregex="^" + Alias.normalize(pattern))
+                )
 
         if "valid" in self.request.GET:
             q = Q(credit_transaction=None)
diff --git a/apps/wei/forms/registration.py b/apps/wei/forms/registration.py
index 738db4e2..98215f4b 100644
--- a/apps/wei/forms/registration.py
+++ b/apps/wei/forms/registration.py
@@ -6,6 +6,8 @@ from django.contrib.auth.models import User
 from django.db.models import Q
 from django.forms import CheckboxSelectMultiple
 from django.utils.translation import gettext_lazy as _
+
+from note.models import NoteSpecial
 from note_kfet.inputs import AmountInput, DatePickerInput, Autocomplete, ColorWidget
 
 from ..models import WEIClub, WEIRegistration, Bus, BusTeam, WEIMembership, WEIRole
@@ -75,6 +77,35 @@ class WEIMembershipForm(forms.ModelForm):
         widget=CheckboxSelectMultiple(),
     )
 
+    credit_type = forms.ModelChoiceField(
+        queryset=NoteSpecial.objects.all(),
+        label=_("Credit type"),
+        empty_label=_("No credit"),
+        required=False,
+    )
+
+    credit_amount = forms.IntegerField(
+        label=_("Credit amount"),
+        widget=AmountInput(),
+        initial=0,
+        required=False,
+    )
+
+    last_name = forms.CharField(
+        label=_("Last name"),
+        required=False,
+    )
+
+    first_name = forms.CharField(
+        label=_("First name"),
+        required=False,
+    )
+
+    bank = forms.CharField(
+        label=_("Bank"),
+        required=False,
+    )
+
     def clean(self):
         cleaned_data = super().clean()
         if cleaned_data["team"] is not None and cleaned_data["team"].bus != cleaned_data["bus"]:
diff --git a/apps/wei/tables.py b/apps/wei/tables.py
index 41c35a47..0fae6848 100644
--- a/apps/wei/tables.py
+++ b/apps/wei/tables.py
@@ -3,9 +3,13 @@
 
 import django_tables2 as tables
 from django.urls import reverse_lazy
+from django.utils import timezone
+from django.utils.html import format_html
 from django.utils.translation import gettext_lazy as _
 from django_tables2 import A
 
+from note_kfet.middlewares import get_current_authenticated_user
+from permission.backends import PermissionBackend
 from .models import WEIClub, WEIRegistration, Bus, BusTeam, WEIMembership
 
 
@@ -53,8 +57,12 @@ class WEIRegistrationTable(tables.Table):
         verbose_name=_("Validate"),
         text=_("Validate"),
         attrs={
+            'th': {
+                'id': 'validate-membership-header'
+            },
             'a': {
-                'class': 'btn btn-success'
+                'class': 'btn btn-success',
+                'data-type': 'validate-membership'
             }
         }
     )
@@ -65,12 +73,33 @@ class WEIRegistrationTable(tables.Table):
         verbose_name=_("delete"),
         text=_("Delete"),
         attrs={
+            'th': {
+                'id': 'delete-membership-header'
+            },
             'a': {
-                'class': 'btn btn-danger'
+                'class': 'btn btn-danger',
+                'data-type': 'delete-membership'
             }
         },
     )
 
+    def render_validate(self, record):
+        if PermissionBackend.check_perm(get_current_authenticated_user(), "wei.add_weimembership", WEIMembership(
+                club=record.wei,
+                user=record.user,
+                date_start=timezone.now().date(),
+                date_end=timezone.now().date(),
+                fee=0,
+                registration=record,
+        )):
+            return _("Validate")
+        return format_html("<span class='no-perm'></span>")
+
+    def render_delete(self, record):
+        if PermissionBackend.check_perm(get_current_authenticated_user(), "wei.delete_weimembership", record):
+            return _("Delete")
+        return format_html("<span class='no-perm'></span>")
+
     class Meta:
         attrs = {
             'class': 'table table-condensed table-striped table-hover'
diff --git a/apps/wei/views.py b/apps/wei/views.py
index 7731f08b..fe2a47a5 100644
--- a/apps/wei/views.py
+++ b/apps/wei/views.py
@@ -24,10 +24,11 @@ from django.utils.translation import gettext_lazy as _
 from django.views.generic.edit import BaseFormView, DeleteView
 from django_tables2 import SingleTableView
 from member.models import Membership, Club
-from note.models import Transaction, NoteClub, Alias
+from note.models import Transaction, NoteClub, Alias, SpecialTransaction, NoteSpecial
 from note.tables import HistoryTable
 from note_kfet.settings import BASE_DIR
 from permission.backends import PermissionBackend
+from permission.models import Role
 from permission.views import ProtectQuerysetMixin
 
 from .forms.registration import WEIChooseBusForm
@@ -666,6 +667,15 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
             survey = CurrentSurvey(self.object)
             if not survey.is_complete():
                 return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk})
+        if PermissionBackend.check_perm(self.request.user, "wei.add_weimembership", WEIMembership(
+            club=self.object.wei,
+            user=self.object.user,
+            date_start=timezone.now().date(),
+            date_end=timezone.now().date(),
+            fee=0,
+            registration=self.object,
+        )):
+            return reverse_lazy("wei:validate_registration", kwargs={"pk": self.object.pk})
         return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.wei.pk})
 
 
@@ -723,19 +733,55 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Crea
         if survey.information.valid:
             context["suggested_bus"] = survey.information.get_selected_bus()
         context["club"] = registration.wei
-        context["fee"] = registration.wei.membership_fee_paid if registration.user.profile.paid \
-            else registration.wei.membership_fee_unpaid
+
+        kfet = registration.wei.parent_club
+        bde = kfet.parent_club
+
         context["kfet_member"] = Membership.objects.filter(
-            club__name="Kfet",
+            club__name=kfet.name,
+            user=registration.user,
+            date_start__gte=kfet.membership_start,
+        ).exists()
+        context["bde_member"] = Membership.objects.filter(
+            club__name=bde.name,
             user=registration.user,
-            date_start__gte=registration.wei.parent_club.membership_start,
+            date_start__gte=bde.membership_start,
         ).exists()
 
+        fee = registration.wei.membership_fee_paid if registration.user.profile.paid \
+            else registration.wei.membership_fee_unpaid
+        if not context["kfet_member"]:
+            fee += kfet.membership_fee_paid if registration.user.profile.paid \
+                else kfet.membership_fee_unpaid
+        if not context["bde_member"]:
+            fee += bde.membership_fee_paid if registration.user.profile.paid \
+                else bde.membership_fee_unpaid
+
+        context["fee"] = fee
+
+        form = context["form"]
+        if registration.soge_credit:
+            form.fields["credit_amount"].initial = fee
+        else:
+            form.fields["credit_amount"].initial = max(0, fee - registration.user.note.balance)
+
         return context
 
     def get_form(self, form_class=None):
         form = super().get_form(form_class)
         registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])
+        form.fields["last_name"].initial = registration.user.last_name
+        form.fields["first_name"].initial = registration.user.first_name
+
+        if registration.soge_credit:
+            form.fields["credit_type"].disabled = True
+            form.fields["credit_type"].initial = NoteSpecial.objects.get(special_type="Virement bancaire")
+            form.fields["credit_amount"].disabled = True
+            form.fields["last_name"].disabled = True
+            form.fields["first_name"].disabled = True
+            form.fields["bank"].disabled = True
+            form.fields["bank"].initial = "Société générale"
+
         form.fields["bus"].widget.attrs["api_url"] = "/api/wei/bus/?wei=" + str(registration.wei.pk)
         if registration.first_year:
             # Use the results of the survey to fill initial data
@@ -750,7 +796,7 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Crea
             if "preferred_bus_pk" in information and len(information["preferred_bus_pk"]) == 1:
                 form["bus"].initial = Bus.objects.get(pk=information["preferred_bus_pk"][0])
             if "preferred_team_pk" in information and len(information["preferred_team_pk"]) == 1:
-                form["team"].initial = Bus.objects.get(pk=information["preferred_team_pk"][0])
+                form["team"].initial = BusTeam.objects.get(pk=information["preferred_team_pk"][0])
             if "preferred_roles_pk" in information:
                 form["roles"].initial = WEIRole.objects.filter(
                     Q(pk__in=information["preferred_roles_pk"]) | Q(name="Adhérent WEI")
@@ -770,38 +816,80 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Crea
         membership.club = club
         membership.date_start = min(date.today(), club.date_start)
         membership.registration = registration
+        # Force the membership of the clubs BDE and Kfet
+        membership._force_renew_parent = True
 
         if user.profile.paid:
             fee = club.membership_fee_paid
         else:
             fee = club.membership_fee_unpaid
 
-        if not registration.soge_credit and user.note.balance < fee:
+        kfet = club.parent_club
+        bde = kfet.parent_club
+
+        kfet_member = Membership.objects.filter(
+            club__name=kfet.name,
+            user=registration.user,
+            date_start__gte=kfet.membership_start,
+        ).exists()
+        bde_member = Membership.objects.filter(
+            club__name=bde.name,
+            user=registration.user,
+            date_start__gte=bde.membership_start,
+        ).exists()
+
+        if not kfet_member:
+            fee += kfet.membership_fee_paid if registration.user.profile.paid else kfet.membership_fee_unpaid
+        if not bde_member:
+            fee += bde.membership_fee_paid if registration.user.profile.paid else bde.membership_fee_unpaid
+
+        credit_type = form.cleaned_data["credit_type"]
+        credit_amount = form.cleaned_data["credit_amount"]
+        last_name = form.cleaned_data["last_name"]
+        first_name = form.cleaned_data["first_name"]
+        bank = form.cleaned_data["bank"]
+
+        if credit_type is None or registration.soge_credit:
+            credit_amount = 0
+
+        if not registration.caution_check and not registration.first_year:
+            form.add_error('bus', _("This user didn't give her/his caution check."))
+            return super().form_invalid(form)
+
+        if not registration.soge_credit and user.note.balance < fee + credit_amount:
             # Users must have money before registering to the WEI.
-            # TODO Send a notification to the user (with a mail?) to tell her/him to credit her/his note
             form.add_error('bus',
                            _("This user don't have enough money to join this club, and can't have a negative balance."))
             return super().form_invalid(form)
 
-        if not registration.caution_check and not registration.first_year:
-            form.add_error('bus', _("This user didn't give her/his caution check."))
-            return super().form_invalid(form)
+        if credit_amount:
+            if not last_name:
+                form.add_error('last_name', _("This field is required."))
+                return super().form_invalid(form)
 
-        if club.parent_club is not None:   # parent_club is never None: this is Kfet.
-            # We want that the user is member of the Kfet club *of this year*: the Kfet membership is included
-            # in the WEI registration.
-            if not Membership.objects.filter(
-                user=form.instance.user,
-                club=club.parent_club,  # Kfet
-                date_start__gte=club.parent_club.membership_start,
-            ).exists():
-                form.add_error('bus', _('User is not a member of the parent club') + ' ' + club.parent_club.name)
+            if not first_name:
+                form.add_error('first_name', _("This field is required."))
                 return super().form_invalid(form)
 
+            # Credit note before adding the membership
+            SpecialTransaction.objects.create(
+                source=credit_type,
+                destination=registration.user.note,
+                amount=credit_amount,
+                reason="Crédit " + str(credit_type) + " (WEI)",
+                last_name=last_name,
+                first_name=first_name,
+                bank=bank,
+            )
+
         # Now, all is fine, the membership can be created.
 
+        if registration.soge_credit:
+            form.instance._soge = True
+
         if registration.first_year:
             membership = form.instance
+            # If the user is not a member of the club Kfet, then the membership is created.
             membership.save()
             membership.refresh_from_db()
             membership.roles.set(WEIRole.objects.filter(name="1A").all())
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index fa49f7cc..6875a00d 100644
--- a/locale/fr/LC_MESSAGES/django.po
+++ b/locale/fr/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-08-03 23:53+0200\n"
+"POT-Creation-Date: 2020-08-04 20:03+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -45,7 +45,7 @@ msgid "You can't invite more than 3 people to this activity."
 msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité."
 
 #: apps/activity/models.py:24 apps/activity/models.py:49
-#: apps/member/models.py:158 apps/note/models/notes.py:212
+#: apps/member/models.py:159 apps/note/models/notes.py:212
 #: apps/note/models/transactions.py:25 apps/note/models/transactions.py:45
 #: apps/note/models/transactions.py:268 apps/permission/models.py:339
 #: apps/wei/models.py:65 apps/wei/models.py:117
@@ -84,7 +84,7 @@ msgstr "description"
 msgid "type"
 msgstr "type"
 
-#: apps/activity/models.py:67 apps/logs/models.py:22 apps/member/models.py:266
+#: apps/activity/models.py:67 apps/logs/models.py:22 apps/member/models.py:267
 #: apps/note/models/notes.py:126 apps/treasury/models.py:222
 #: apps/wei/models.py:159 templates/treasury/sogecredit_detail.html:14
 #: templates/wei/survey.html:16
@@ -189,12 +189,13 @@ msgstr "Type"
 
 #: apps/activity/tables.py:77 apps/member/forms.py:103
 #: apps/registration/forms.py:70 apps/treasury/forms.py:120
+#: apps/wei/forms/registration.py:95
 msgid "Last name"
 msgstr "Nom de famille"
 
 #: apps/activity/tables.py:79 apps/member/forms.py:108
 #: apps/registration/forms.py:75 apps/treasury/forms.py:122
-#: templates/note/transaction_form.html:129
+#: apps/wei/forms/registration.py:100 templates/note/transaction_form.html:129
 msgid "First name"
 msgstr "Prénom"
 
@@ -268,7 +269,7 @@ msgid "edit"
 msgstr "Modifier"
 
 #: apps/logs/models.py:63 apps/note/tables.py:137 apps/note/tables.py:165
-#: apps/permission/models.py:137 apps/wei/tables.py:65
+#: apps/permission/models.py:137 apps/wei/tables.py:73
 msgid "delete"
 msgstr "Supprimer"
 
@@ -292,25 +293,25 @@ msgstr "journal de modification"
 msgid "changelogs"
 msgstr "journaux de modifications"
 
-#: apps/member/admin.py:52 apps/member/models.py:185
+#: apps/member/admin.py:52 apps/member/models.py:186
 #: templates/member/club_info.html:41
 msgid "membership fee (paid students)"
 msgstr "cotisation pour adhérer (normalien élève)"
 
-#: apps/member/admin.py:53 apps/member/models.py:190
+#: apps/member/admin.py:53 apps/member/models.py:191
 #: templates/member/club_info.html:44
 msgid "membership fee (unpaid students)"
 msgstr "cotisation pour adhérer (normalien étudiant)"
 
-#: apps/member/admin.py:67 apps/member/models.py:277
+#: apps/member/admin.py:67 apps/member/models.py:278
 msgid "roles"
 msgstr "rôles"
 
-#: apps/member/admin.py:68 apps/member/models.py:291
+#: apps/member/admin.py:68 apps/member/models.py:292
 msgid "fee"
 msgstr "cotisation"
 
-#: apps/member/apps.py:14 apps/wei/tables.py:150 apps/wei/tables.py:181
+#: apps/member/apps.py:14 apps/wei/tables.py:179 apps/wei/tables.py:210
 msgid "member"
 msgstr "adhérent"
 
@@ -328,10 +329,12 @@ msgid "Check this case is the Société Générale paid the inscription."
 msgstr "Cochez cette case si la Société Générale a payé l'inscription."
 
 #: apps/member/forms.py:89 apps/registration/forms.py:57
+#: apps/wei/forms/registration.py:82
 msgid "Credit type"
 msgstr "Type de rechargement"
 
 #: apps/member/forms.py:90 apps/registration/forms.py:58
+#: apps/wei/forms/registration.py:83
 msgid "No credit"
 msgstr "Pas de rechargement"
 
@@ -340,11 +343,13 @@ msgid "You can credit the note of the user."
 msgstr "Vous pouvez créditer la note de l'utisateur avant l'adhésion."
 
 #: apps/member/forms.py:96 apps/registration/forms.py:63
+#: apps/wei/forms/registration.py:88
 msgid "Credit amount"
 msgstr "Montant à créditer"
 
 #: apps/member/forms.py:113 apps/registration/forms.py:80
-#: apps/treasury/forms.py:124 templates/note/transaction_form.html:135
+#: apps/treasury/forms.py:124 apps/wei/forms/registration.py:105
+#: templates/note/transaction_form.html:135
 msgid "Bank"
 msgstr "Banque"
 
@@ -356,171 +361,171 @@ msgstr "Utilisateur"
 msgid "Roles"
 msgstr "Rôles"
 
-#: apps/member/models.py:34
+#: apps/member/models.py:35
 #: templates/registration/future_profile_detail.html:40
 #: templates/wei/weimembership_form.html:48
 msgid "phone number"
 msgstr "numéro de téléphone"
 
-#: apps/member/models.py:41 templates/member/profile_info.html:29
+#: apps/member/models.py:42 templates/member/profile_info.html:29
 #: templates/registration/future_profile_detail.html:34
 #: templates/wei/weimembership_form.html:42
 msgid "section"
 msgstr "section"
 
-#: apps/member/models.py:42
+#: apps/member/models.py:43
 msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
 msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
 
-#: apps/member/models.py:50 templates/wei/weimembership_form.html:36
+#: apps/member/models.py:51 templates/wei/weimembership_form.html:36
 msgid "department"
 msgstr "département"
 
-#: apps/member/models.py:52
+#: apps/member/models.py:53
 msgid "Informatics (A0)"
 msgstr "Informatique (A0)"
 
-#: apps/member/models.py:53
+#: apps/member/models.py:54
 msgid "Mathematics (A1)"
 msgstr "Mathématiques (A1)"
 
-#: apps/member/models.py:54
+#: apps/member/models.py:55
 msgid "Physics (A2)"
 msgstr "Physique (A2)"
 
-#: apps/member/models.py:55
+#: apps/member/models.py:56
 msgid "Applied physics (A'2)"
 msgstr "Physique appliquée (A'2)"
 
-#: apps/member/models.py:56
+#: apps/member/models.py:57
 msgid "Chemistry (A''2)"
 msgstr "Chimie (A''2)"
 
-#: apps/member/models.py:57
+#: apps/member/models.py:58
 msgid "Biology (A3)"
 msgstr "Biologie (A3)"
 
-#: apps/member/models.py:58
+#: apps/member/models.py:59
 msgid "SAPHIRE (B1234)"
 msgstr "SAPHIRE (B1234)"
 
-#: apps/member/models.py:59
+#: apps/member/models.py:60
 msgid "Mechanics (B1)"
 msgstr "Mécanique (B1)"
 
-#: apps/member/models.py:60
+#: apps/member/models.py:61
 msgid "Civil engineering (B2)"
 msgstr "Génie civil (B2)"
 
-#: apps/member/models.py:61
+#: apps/member/models.py:62
 msgid "Mechanical engineering (B3)"
 msgstr "Génie mécanique (B3)"
 
-#: apps/member/models.py:62
+#: apps/member/models.py:63
 msgid "EEA (B4)"
 msgstr "EEA (B4)"
 
-#: apps/member/models.py:63
+#: apps/member/models.py:64
 msgid "Design (C)"
 msgstr "Design (C)"
 
-#: apps/member/models.py:64
+#: apps/member/models.py:65
 msgid "Economy-management (D2)"
 msgstr "Économie-gestion (D2)"
 
-#: apps/member/models.py:65
+#: apps/member/models.py:66
 msgid "Social sciences (D3)"
 msgstr "Sciences sociales (D3)"
 
-#: apps/member/models.py:66
+#: apps/member/models.py:67
 msgid "English (E)"
 msgstr "Anglais (E)"
 
-#: apps/member/models.py:67
+#: apps/member/models.py:68
 msgid "External (EXT)"
 msgstr "Externe (EXT)"
 
-#: apps/member/models.py:74
+#: apps/member/models.py:75
 msgid "promotion"
 msgstr "promotion"
 
-#: apps/member/models.py:75
+#: apps/member/models.py:76
 msgid "Year of entry to the school (None if not ENS student)"
 msgstr "Année d'entrée dans l'école (None si non-étudiant·e de l'ENS)"
 
-#: apps/member/models.py:79 templates/member/profile_info.html:32
+#: apps/member/models.py:80 templates/member/profile_info.html:32
 #: templates/registration/future_profile_detail.html:37
 #: templates/wei/weimembership_form.html:45
 msgid "address"
 msgstr "adresse"
 
-#: apps/member/models.py:86
+#: apps/member/models.py:87
 #: templates/registration/future_profile_detail.html:43
 #: templates/wei/weimembership_form.html:51
 msgid "paid"
 msgstr "payé"
 
-#: apps/member/models.py:87
+#: apps/member/models.py:88
 msgid "Tells if the user receive a salary."
 msgstr "Indique si l'utilisateur perçoit un salaire."
 
-#: apps/member/models.py:92
+#: apps/member/models.py:93
 msgid "email confirmed"
 msgstr "adresse email confirmée"
 
-#: apps/member/models.py:97
+#: apps/member/models.py:98
 msgid "registration valid"
 msgstr "inscription valid"
 
-#: apps/member/models.py:126 apps/member/models.py:127
+#: apps/member/models.py:127 apps/member/models.py:128
 msgid "user profile"
 msgstr "profil utilisateur"
 
-#: apps/member/models.py:134
+#: apps/member/models.py:135
 msgid "Activate your Note Kfet account"
 msgstr "Activez votre compte Note Kfet"
 
-#: apps/member/models.py:163 templates/member/club_info.html:57
+#: apps/member/models.py:164 templates/member/club_info.html:57
 #: templates/registration/future_profile_detail.html:22
 #: templates/wei/weiclub_info.html:52 templates/wei/weimembership_form.html:24
 msgid "email"
 msgstr "courriel"
 
-#: apps/member/models.py:170
+#: apps/member/models.py:171
 msgid "parent club"
 msgstr "club parent"
 
-#: apps/member/models.py:179
+#: apps/member/models.py:180
 msgid "require memberships"
 msgstr "nécessite des adhésions"
 
-#: apps/member/models.py:180
+#: apps/member/models.py:181
 msgid "Uncheck if this club don't require memberships."
 msgstr "Décochez si ce club n'utilise pas d'adhésions."
 
-#: apps/member/models.py:196 templates/member/club_info.html:33
+#: apps/member/models.py:197 templates/member/club_info.html:33
 msgid "membership duration"
 msgstr "durée de l'adhésion"
 
-#: apps/member/models.py:197
+#: apps/member/models.py:198
 msgid "The longest time (in days) a membership can last (NULL = infinite)."
 msgstr "La durée maximale (en jours) d'une adhésion (NULL = infinie)."
 
-#: apps/member/models.py:204 templates/member/club_info.html:23
+#: apps/member/models.py:205 templates/member/club_info.html:23
 msgid "membership start"
 msgstr "début de l'adhésion"
 
-#: apps/member/models.py:205
+#: apps/member/models.py:206
 msgid "How long after January 1st the members can renew their membership."
 msgstr ""
 "Combien de temps après le 1er Janvier les adhérents peuvent renouveler leur "
 "adhésion."
 
-#: apps/member/models.py:212 templates/member/club_info.html:28
+#: apps/member/models.py:213 templates/member/club_info.html:28
 msgid "membership end"
 msgstr "fin de l'adhésion"
 
-#: apps/member/models.py:213
+#: apps/member/models.py:214
 msgid ""
 "How long the membership can last after January 1st of the next year after "
 "members can renew their membership."
@@ -528,46 +533,46 @@ msgstr ""
 "Combien de temps l'adhésion peut durer après le 1er Janvier de l'année "
 "suivante avant que les adhérents peuvent renouveler leur adhésion."
 
-#: apps/member/models.py:247 apps/member/models.py:272
+#: apps/member/models.py:248 apps/member/models.py:273
 #: apps/note/models/notes.py:163
 msgid "club"
 msgstr "club"
 
-#: apps/member/models.py:248
+#: apps/member/models.py:249
 msgid "clubs"
 msgstr "clubs"
 
-#: apps/member/models.py:282
+#: apps/member/models.py:283
 msgid "membership starts on"
 msgstr "l'adhésion commence le"
 
-#: apps/member/models.py:286
+#: apps/member/models.py:287
 msgid "membership ends on"
 msgstr "l'adhésion finit le"
 
-#: apps/member/models.py:310 apps/member/views.py:540 apps/wei/views.py:798
-msgid "User is not a member of the parent club"
-msgstr "L'utilisateur n'est pas membre du club parent"
-
-#: apps/member/models.py:317
+#: apps/member/models.py:339
 #, python-brace-format
 msgid "The role {role} does not apply to the club {club}."
 msgstr "Le rôle {role} ne s'applique pas au club {club}."
 
-#: apps/member/models.py:328 apps/member/views.py:549
+#: apps/member/models.py:350 apps/member/views.py:583
 msgid "User is already a member of the club"
 msgstr "L'utilisateur est déjà membre du club"
 
-#: apps/member/models.py:379
+#: apps/member/models.py:386
+msgid "User is not a member of the parent club"
+msgstr "L'utilisateur n'est pas membre du club parent"
+
+#: apps/member/models.py:439
 #, python-brace-format
 msgid "Membership of {user} for the club {club}"
 msgstr "Adhésion de {user} pour le club {club}"
 
-#: apps/member/models.py:382
+#: apps/member/models.py:442
 msgid "membership"
 msgstr "adhésion"
 
-#: apps/member/models.py:383
+#: apps/member/models.py:443
 msgid "memberships"
 msgstr "adhésions"
 
@@ -625,7 +630,7 @@ msgstr "Modifier le club"
 msgid "Add new member to the club"
 msgstr "Ajouter un nouveau membre au club"
 
-#: apps/member/views.py:530 apps/wei/views.py:783
+#: apps/member/views.py:574 apps/wei/views.py:862
 msgid ""
 "This user don't have enough money to join this club, and can't have a "
 "negative balance."
@@ -633,25 +638,25 @@ msgstr ""
 "Cet utilisateur n'a pas assez d'argent pour rejoindre ce club et ne peut pas "
 "avoir un solde négatif."
 
-#: apps/member/views.py:553
+#: apps/member/views.py:587
 msgid "The membership must start after {:%m-%d-%Y}."
 msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}."
 
-#: apps/member/views.py:558
+#: apps/member/views.py:592
 msgid "The membership must begin before {:%m-%d-%Y}."
 msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}."
 
-#: apps/member/views.py:575 apps/member/views.py:577 apps/member/views.py:579
+#: apps/member/views.py:609 apps/member/views.py:611 apps/member/views.py:613
 #: apps/registration/views.py:290 apps/registration/views.py:292
-#: apps/registration/views.py:294
+#: apps/registration/views.py:294 apps/wei/views.py:867 apps/wei/views.py:871
 msgid "This field is required."
 msgstr "Ce champ est requis."
 
-#: apps/member/views.py:647
+#: apps/member/views.py:690
 msgid "Manage roles of an user in the club"
 msgstr "Gérer les rôles d'un utilisateur dans le club"
 
-#: apps/member/views.py:672
+#: apps/member/views.py:715
 msgid "Members of the club"
 msgstr "Membres du club"
 
@@ -950,13 +955,13 @@ msgstr "Cliquez pour valider"
 msgid "No reason specified"
 msgstr "Pas de motif spécifié"
 
-#: apps/note/tables.py:139 apps/note/tables.py:167 apps/wei/tables.py:66
-#: templates/treasury/sogecredit_detail.html:59
+#: apps/note/tables.py:139 apps/note/tables.py:167 apps/wei/tables.py:74
+#: apps/wei/tables.py:100 templates/treasury/sogecredit_detail.html:59
 #: templates/wei/weiregistration_confirm_delete.html:32
 msgid "Delete"
 msgstr "Supprimer"
 
-#: apps/note/tables.py:162 apps/wei/tables.py:42 apps/wei/tables.py:43
+#: apps/note/tables.py:162 apps/wei/tables.py:46 apps/wei/tables.py:47
 #: templates/member/club_info.html:67 templates/note/conso_form.html:128
 #: templates/wei/bus_tables.html:15 templates/wei/busteam_tables.html:15
 #: templates/wei/busteam_tables.html:33 templates/wei/weiclub_info.html:68
@@ -1190,7 +1195,7 @@ msgstr "Trésorerie"
 #: templates/activity/activity_form.html:9
 #: templates/activity/activity_invite.html:8
 #: templates/django_filters/rest_framework/form.html:5
-#: templates/member/add_members.html:14 templates/member/club_form.html:9
+#: templates/member/add_members.html:31 templates/member/club_form.html:9
 #: templates/note/transactiontemplate_form.html:15
 #: templates/treasury/invoice_form.html:46 templates/wei/bus_form.html:13
 #: templates/wei/busteam_form.html:13 templates/wei/weiclub_form.html:15
@@ -1410,7 +1415,7 @@ msgstr "Joindre une transaction à une remise"
 msgid "List of credits from the Société générale"
 msgstr "Liste des crédits de la Société générale"
 
-#: apps/treasury/views.py:384
+#: apps/treasury/views.py:379
 msgid "Manage credits from the Société générale"
 msgstr "Gérer les crédits de la Société générale"
 
@@ -1419,12 +1424,12 @@ msgstr "Gérer les crédits de la Société générale"
 msgid "WEI"
 msgstr "WEI"
 
-#: apps/wei/forms/registration.py:48 apps/wei/models.py:112
+#: apps/wei/forms/registration.py:50 apps/wei/models.py:112
 #: apps/wei/models.py:297
 msgid "bus"
 msgstr "Bus"
 
-#: apps/wei/forms/registration.py:49
+#: apps/wei/forms/registration.py:51
 msgid ""
 "This choice is not definitive. The WEI organizers are free to attribute for "
 "you a bus and a team, in particular if you are a free eletron."
@@ -1433,11 +1438,11 @@ msgstr ""
 "attribuer un bus et une équipe, en particulier si vous êtes un électron "
 "libre."
 
-#: apps/wei/forms/registration.py:56
+#: apps/wei/forms/registration.py:58
 msgid "Team"
 msgstr "Équipe"
 
-#: apps/wei/forms/registration.py:58
+#: apps/wei/forms/registration.py:60
 msgid ""
 "Leave this field empty if you won't be in a team (staff, bus chief, free "
 "electron)"
@@ -1445,16 +1450,16 @@ msgstr ""
 "Laissez ce champ vide si vous ne serez pas dans une équipe (staff, chef de "
 "bus ou électron libre)"
 
-#: apps/wei/forms/registration.py:64 apps/wei/forms/registration.py:74
+#: apps/wei/forms/registration.py:66 apps/wei/forms/registration.py:76
 #: apps/wei/models.py:147
 msgid "WEI Roles"
 msgstr "Rôles au WEI"
 
-#: apps/wei/forms/registration.py:65
+#: apps/wei/forms/registration.py:67
 msgid "Select the roles that you are interested in."
 msgstr "Sélectionnez les rôles qui vous intéressent."
 
-#: apps/wei/forms/registration.py:81
+#: apps/wei/forms/registration.py:112
 msgid "This team doesn't belong to the given bus."
 msgstr "Cette équipe n'appartient pas à ce bus."
 
@@ -1624,97 +1629,97 @@ msgstr "adhésion au WEI"
 msgid "WEI memberships"
 msgstr "adhésions au WEI"
 
-#: apps/wei/tables.py:53 apps/wei/tables.py:54
+#: apps/wei/tables.py:57 apps/wei/tables.py:58 apps/wei/tables.py:95
 #: templates/treasury/sogecredit_detail.html:57
 msgid "Validate"
 msgstr "Valider"
 
-#: apps/wei/tables.py:96
+#: apps/wei/tables.py:125
 msgid "Year"
 msgstr "Année"
 
-#: apps/wei/tables.py:134 templates/wei/bus_tables.html:26
+#: apps/wei/tables.py:163 templates/wei/bus_tables.html:26
 #: templates/wei/busteam_tables.html:43
 msgid "Teams"
 msgstr "Équipes"
 
-#: apps/wei/tables.py:143 apps/wei/tables.py:184
+#: apps/wei/tables.py:172 apps/wei/tables.py:213
 msgid "Members count"
 msgstr "Nombre de membres"
 
-#: apps/wei/tables.py:150 apps/wei/tables.py:181
+#: apps/wei/tables.py:179 apps/wei/tables.py:210
 msgid "members"
 msgstr "adhérents"
 
-#: apps/wei/views.py:56
+#: apps/wei/views.py:57
 msgid "Search WEI"
 msgstr "Chercher un WEI"
 
-#: apps/wei/views.py:74 templates/wei/weiclub_list.html:10
+#: apps/wei/views.py:75 templates/wei/weiclub_list.html:10
 msgid "Create WEI"
 msgstr "Créer un WEI"
 
-#: apps/wei/views.py:94
+#: apps/wei/views.py:95
 msgid "WEI Detail"
 msgstr "Détails du WEI"
 
-#: apps/wei/views.py:189
+#: apps/wei/views.py:190
 msgid "View members of the WEI"
 msgstr "Voir les membres du WEI"
 
-#: apps/wei/views.py:217
+#: apps/wei/views.py:218
 msgid "Find WEI Membership"
 msgstr "Trouver une adhésion au WEI"
 
-#: apps/wei/views.py:227
+#: apps/wei/views.py:228
 msgid "View registrations to the WEI"
 msgstr "Voir les inscriptions au WEI"
 
-#: apps/wei/views.py:251
+#: apps/wei/views.py:252
 msgid "Find WEI Registration"
 msgstr "Trouver une inscription au WEI"
 
-#: apps/wei/views.py:262
+#: apps/wei/views.py:263
 msgid "Update the WEI"
 msgstr "Modifier le WEI"
 
-#: apps/wei/views.py:283
+#: apps/wei/views.py:284
 msgid "Create new bus"
 msgstr "Ajouter un nouveau bus"
 
-#: apps/wei/views.py:314
+#: apps/wei/views.py:315
 msgid "Update bus"
 msgstr "Modifier le bus"
 
-#: apps/wei/views.py:344
+#: apps/wei/views.py:345
 msgid "Manage bus"
 msgstr "Gérer le bus"
 
-#: apps/wei/views.py:371
+#: apps/wei/views.py:372
 msgid "Create new team"
 msgstr "Créer une nouvelle équipe"
 
-#: apps/wei/views.py:403
+#: apps/wei/views.py:404
 msgid "Update team"
 msgstr "Modifier l'équipe"
 
-#: apps/wei/views.py:434
+#: apps/wei/views.py:435
 msgid "Manage WEI team"
 msgstr "Gérer l'équipe WEI"
 
-#: apps/wei/views.py:456
+#: apps/wei/views.py:457
 msgid "Register first year student to the WEI"
 msgstr "Inscrire un 1A au WEI"
 
-#: apps/wei/views.py:468 templates/wei/weiclub_info.html:62
+#: apps/wei/views.py:469 templates/wei/weiclub_info.html:62
 msgid "Register 1A"
 msgstr "Inscrire un 1A"
 
-#: apps/wei/views.py:489 apps/wei/views.py:560
+#: apps/wei/views.py:490 apps/wei/views.py:561
 msgid "This user is already registered to this WEI."
 msgstr "Cette personne est déjà inscrite au WEI."
 
-#: apps/wei/views.py:494
+#: apps/wei/views.py:495
 msgid ""
 "This user can't be in her/his first year since he/she has already participed "
 "to a WEI."
@@ -1722,39 +1727,39 @@ msgstr ""
 "Cet utilisateur ne peut pas être en première année puisqu'iel a déjà "
 "participé à un WEI."
 
-#: apps/wei/views.py:511
+#: apps/wei/views.py:512
 msgid "Register old student to the WEI"
 msgstr "Inscrire un 2A+ au WEI"
 
-#: apps/wei/views.py:523 templates/wei/weiclub_info.html:65
+#: apps/wei/views.py:524 templates/wei/weiclub_info.html:65
 msgid "Register 2A+"
 msgstr "Inscrire un 2A+"
 
-#: apps/wei/views.py:542 apps/wei/views.py:627
+#: apps/wei/views.py:543 apps/wei/views.py:628
 msgid "You already opened an account in the Société générale."
 msgstr "Vous avez déjà ouvert un compte auprès de la société générale."
 
-#: apps/wei/views.py:590
+#: apps/wei/views.py:591
 msgid "Update WEI Registration"
 msgstr "Modifier l'inscription WEI"
 
-#: apps/wei/views.py:677
+#: apps/wei/views.py:687
 msgid "Delete WEI registration"
 msgstr "Supprimer l'inscription WEI"
 
-#: apps/wei/views.py:688
+#: apps/wei/views.py:698
 msgid "You don't have the right to delete this WEI registration."
 msgstr "Vous n'avez pas la permission de supprimer cette inscription au WEI."
 
-#: apps/wei/views.py:707
+#: apps/wei/views.py:717
 msgid "Validate WEI registration"
 msgstr "Valider l'inscription WEI"
 
-#: apps/wei/views.py:787
+#: apps/wei/views.py:856
 msgid "This user didn't give her/his caution check."
 msgstr "Cet utilisateur n'a pas donné son chèque de caution."
 
-#: apps/wei/views.py:830 apps/wei/views.py:883 apps/wei/views.py:893
+#: apps/wei/views.py:918 apps/wei/views.py:971 apps/wei/views.py:981
 #: templates/wei/survey.html:12 templates/wei/survey_closed.html:12
 #: templates/wei/survey_end.html:12
 msgid "Survey WEI"
@@ -1807,7 +1812,7 @@ msgstr "Page inexistante"
 msgid ""
 "The requested path <code>%(request_path)s</code> was not found on the server."
 msgstr ""
-"The  chemin demandé <code>%(request_path)s</code> n'a pas été trouvé sur le "
+"Le chemin demandé <code>%(request_path)s</code> n'a pas été trouvé sur le "
 "serveur."
 
 #: templates/500.html:6
@@ -1926,13 +1931,33 @@ msgstr ""
 msgid "Field filters"
 msgstr ""
 
+#: templates/member/add_members.html:15
+#, python-format
+msgid ""
+"The user is not a member of the club·s %(clubs)s. An additional fee of "
+"%(pretty_fee)s will be charged to renew automatically the membership in this/"
+"these club·s."
+msgstr ""
+"Cet utilisateur n'est pas membre du/des club·s parent·s %(clubs)s. Un "
+"montant supplémentaire de%(pretty_fee)s sera débitée afin de renouveler "
+"automatiquement l'adhésion dans ce·s club·s."
+
+#: templates/member/add_members.html:20
+#, python-format
+msgid ""
+"This club has parents %(clubs)s. An additional fee of %(pretty_fee)s will be "
+"charged to adhere automatically to this/these club·s."
+msgstr ""
+"Ce club a pour parent·s %(clubs)s. Un coût supplémentaire de %(pretty_fee)s "
+"peut être ajouté pour adhérer automatiquement à ce·s club·s."
+
 #: templates/member/alias_update.html:5
 msgid "Add alias"
 msgstr "Ajouter un alias"
 
 #: templates/member/autocomplete_model.html:11
 msgid "Reset"
-msgstr ""
+msgstr "Réinitialiser"
 
 #: templates/member/club_info.html:17
 msgid "Club Parent"
@@ -2478,7 +2503,7 @@ msgid ""
 "validated, but no credit will be operated."
 msgstr ""
 "Si vous supprimez cette demande de crédit, alors toutes les transactions "
-"d'adhésion seront aussi validées, but il n'y aura pas de transaction de "
+"d'adhésion seront aussi validées, mais il n'y aura pas de transaction de "
 "crédit créée."
 
 #: templates/treasury/sogecredit_detail.html:37
@@ -2678,6 +2703,7 @@ msgstr ""
 "aura validé la création du compte, ou bien changer de moyen de paiement."
 
 #: templates/wei/weimembership_form.html:162
+#, python-format
 msgid ""
 "The note don't have enough money (%(balance)s, %(pretty_fee)s required). The "
 "registration may fail."
@@ -2686,25 +2712,29 @@ msgstr ""
 "L'inscription va échouer."
 
 #: templates/wei/weimembership_form.html:169
-msgid "The note has enough money, the registration is possible."
+#, fuzzy, python-format
+#| msgid "The note has enough money, the registration is possible."
+msgid ""
+"The note has enough money (%(pretty_fee)s required), the registration is "
+"possible."
 msgstr "La note a assez d'argent, l'inscription est possible."
 
-#: templates/wei/weimembership_form.html:176
+#: templates/wei/weimembership_form.html:178
 msgid "The user didn't give her/his caution check."
 msgstr "L'utilisateur n'a pas donné son chèque de caution."
 
-#: templates/wei/weimembership_form.html:184
+#: templates/wei/weimembership_form.html:186
+#, fuzzy
+#| msgid ""
+#| "You must also go to the Kfet to pay your membership. The WEI registration "
+#| "includes the BDE membership."
 msgid ""
-"This user is not a member of the Kfet club for the comming year. Please "
-"adhere <a href=\"%(future_user_detail)s\">here if he/she is in her/his first "
-"year</a> or <a href=\"%(club_detail)s\">here if he/she was an old member</a> "
-"before you validate the registration of the WEI."
+"This user is not a member of the Kfet club for the coming year. The "
+"membership will be processed automatically, the WEI registration includes "
+"the membership fee."
 msgstr ""
-"Cet utilisateur n'est pas membre du club Kfet pour l'année à venir. Merci de le faire adhérer\n"
-"<a href=\"%(future_user_detail)s\">ici s'iel est en première année</a>\n"
-"ou <a href=\"%(club_detail)s\">ici s'iel est un ancien membre</a> avant de "
-"valider\n"
-"l'inscription au WEI."
+"Vous devrez également vous rendre à la Kfet pour payer votre adhésion. "
+"L'inscription au WEI inclut l'adhésion au BDE."
 
 #: templates/wei/weimembership_list.html:24
 msgid "View unvalidated registrations..."
@@ -2730,3 +2760,16 @@ msgstr "Il n'y a pas de pré-inscription en attente avec cette entrée."
 #: templates/wei/weiregistration_list.html:24
 msgid "View validated memberships..."
 msgstr "Voir les adhésions validées ..."
+
+#~ msgid ""
+#~ "This user is not a member of the Kfet club for the comming year. Please "
+#~ "adhere <a href=\"%(future_user_detail)s\">here if he/she is in her/his "
+#~ "first year</a> or <a href=\"%(club_detail)s\">here if he/she was an old "
+#~ "member</a> before you validate the registration of the WEI."
+#~ msgstr ""
+#~ "Cet utilisateur n'est pas membre du club Kfet pour l'année à venir. Merci "
+#~ "de le faire adhérer\n"
+#~ "<a href=\"%(future_user_detail)s\">ici s'iel est en première année</a>\n"
+#~ "ou <a href=\"%(club_detail)s\">ici s'iel est un ancien membre</a> avant "
+#~ "de valider\n"
+#~ "l'inscription au WEI."
diff --git a/templates/base.html b/templates/base.html
index 9b1f463e..8b45d914 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -124,27 +124,27 @@ SPDX-License-Identifier: GPL-3.0-or-later
                         <a class="nav-link" href="{% url 'wei:current_wei_detail' %}"><i class="fas fa-bus"></i> {% trans 'WEI' %}</a>
                     </li>
                 {% endif %}
-                {% if user.is_authenticated %}
+                {% if request.user.is_authenticated %}
                     <li class="nav-item active">
                         <a class="nav-link" href="{% url 'permission:rights' %}"><i class="fas fa-balance-scale"></i> {% trans 'Rights' %}</a>
                     </li>
                 {% endif %}
-                {% if user.is_staff and ""|has_perm:user %}
+                {% if request.user.is_staff and ""|has_perm:user %}
                     <li class="nav-item active">
                         <a data-turbolinks="false" class="nav-link" href="{% url 'admin:index' %}"><i class="fas fa-user-cog"></i> {% trans 'Admin' %}</a>
                     </li>
                 {% endif %}
             </ul>
             <ul class="navbar-nav ml-auto">
-                {% if user.is_authenticated %}
+                {% if request.user.is_authenticated %}
                     <li class="dropdown">
                         <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                             <i class="fas fa-user"></i>
-                            <span id="user_balance">{{ user.username }} ({{ user.note.balance | pretty_money }})</span>
+                            <span id="user_balance">{{ request.user.username }} ({{ request.user.note.balance | pretty_money }})</span>
                         </a>
                         <div class="dropdown-menu dropdown-menu-right"
                              aria-labelledby="navbarDropdownMenuLink">
-                            <a class="dropdown-item" href="{% url 'member:user_detail' pk=user.pk %}">
+                            <a class="dropdown-item" href="{% url 'member:user_detail' pk=request.user.pk %}">
                                 <i class="fas fa-user"></i> Mon compte
                             </a>
                             <a class="dropdown-item" href="{% url 'logout' %}">
@@ -168,7 +168,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
         </div>
     </nav>
     <div class="container-fluid my-3" style="max-width: 1600px;">
-        {% if user.is_authenticated and not user.profile.email_confirmed %}
+        {% if request.user.is_authenticated and not request.user.profile.email_confirmed %}
             <div class="alert alert-warning">
                 {% trans "Your e-mail address is not validated. Please check your mail inbox and click on the validation link." %}
             </div>
diff --git a/templates/member/add_members.html b/templates/member/add_members.html
index ad6f1f29..8f429541 100644
--- a/templates/member/add_members.html
+++ b/templates/member/add_members.html
@@ -2,17 +2,34 @@
 {% load crispy_forms_tags %}
 {% load static %}
 {% load i18n %}
+{% load pretty_money %}
 
 {% block profile_info %}
 {% include "member/club_info.html" %}
 {% endblock %}
 
 {% block profile_content %}
-<form method="post" action="">
-    {% csrf_token %}
-    {{ form|crispy }}
-    <button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
-</form>
+    {% if additional_fee_renewal %}
+        <div class="alert alert-warning">
+            {% if renewal %}
+                {% blocktrans trimmed with clubs=clubs_renewal|join:", " pretty_fee=additional_fee_renewal|pretty_money %}
+                    The user is not a member of the club·s {{ clubs }}. An additional fee of {{ pretty_fee }}
+                    will be charged to renew automatically the membership in this/these club·s.
+                {% endblocktrans %}
+            {% else %}
+                {% blocktrans trimmed with clubs=clubs_renewal|join:", " pretty_fee=additional_fee_renewal|pretty_money %}
+                    This club has parents {{ clubs }}. An additional fee of {{ pretty_fee }}
+                    will be charged to adhere automatically to this/these club·s.
+                {% endblocktrans %}
+            {% endif %}
+        </div>
+    {% endif %}
+
+    <form method="post" action="">
+        {% csrf_token %}
+        {{ form|crispy }}
+        <button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
+    </form>
 {% endblock %}
 
 {% block extrajavascript %}
diff --git a/templates/member/club_detail.html b/templates/member/club_detail.html
index fedd43fa..451e4d76 100644
--- a/templates/member/club_detail.html
+++ b/templates/member/club_detail.html
@@ -14,7 +14,5 @@
             $("#history_list").load("{% url 'member:club_detail' pk=object.pk %} #history_list");
             $("#profile_infos").load("{% url 'member:club_detail' pk=object.pk %} #profile_infos");
         }
-
-        window.history.replaceState({}, document.title, location.pathname);
     </script>
 {% endblock %}
diff --git a/templates/member/profile_detail.html b/templates/member/profile_detail.html
index 04b15d3b..297af21c 100644
--- a/templates/member/profile_detail.html
+++ b/templates/member/profile_detail.html
@@ -11,10 +11,8 @@
 {% block extrajavascript %}
     <script>
         function refreshHistory() {
-            $("#history_list").load("{% url 'member:user_detail' pk=object.pk %} #history_list");
-            $("#profile_infos").load("{% url 'member:user_detail' pk=object.pk %} #profile_infos");
+            $("#history_list").load("{% url 'member:user_detail' pk=user_object.pk %} #history_list");
+            $("#profile_infos").load("{% url 'member:user_detail' pk=user_object.pk %} #profile_infos");
         }
-
-        window.history.replaceState({}, document.title, location.pathname);
     </script>
 {% endblock %}
diff --git a/templates/member/profile_info.html b/templates/member/profile_info.html
index 35d3e4ea..3694fde3 100644
--- a/templates/member/profile_info.html
+++ b/templates/member/profile_info.html
@@ -2,22 +2,22 @@
 
 <div class="card bg-light shadow">
     <div class="card-header text-center" >
-        <h4> {% trans "Account #" %}  {{ user.pk }}</h4>
+        <h4> {% trans "Account #" %}  {{ user_object.pk }}</h4>
     </div>
     <div class="card-top text-center">
-        <a  href="{% url 'member:user_update_pic' user.pk  %}">
-            <img src="{{ user.note.display_image.url }}" class="img-thumbnail mt-2" >
+        <a  href="{% url 'member:user_update_pic' user_object.pk  %}">
+            <img src="{{ user_object.note.display_image.url }}" class="img-thumbnail mt-2" >
         </a>
     </div>
     <div class="card-body" id="profile_infos">
         <dl class="row">
             <dt class="col-xl-6">{% trans 'name'|capfirst %}, {% trans 'first name' %}</dt>
-            <dd class="col-xl-6">{{ user.last_name }} {{ user.first_name }}</dd>
+            <dd class="col-xl-6">{{ user_object.last_name }} {{ user_object.first_name }}</dd>
 
             <dt class="col-xl-6">{% trans 'username'|capfirst %}</dt>
-            <dd class="col-xl-6">{{ user.username }}</dd>
+            <dd class="col-xl-6">{{ user_object.username }}</dd>
 
-            {% if user.pk == user.pk %}
+            {% if user_object.pk == user_object.pk %}
                 <dt class="col-xl-6">{% trans 'password'|capfirst %}</dt>
                 <dd class="col-xl-6">
                     <a class="small" href="{% url 'password_change' %}">
@@ -27,25 +27,25 @@
             {% endif %}
 
             <dt class="col-xl-6">{% trans 'section'|capfirst %}</dt>
-            <dd class="col-xl-6">{{ user.profile.section }}</dd>
+            <dd class="col-xl-6">{{ user_object.profile.section }}</dd>
 
             <dt class="col-xl-6">{% trans 'address'|capfirst %}</dt>
-            <dd class="col-xl-6">{{ user.profile.address }}</dd>
+            <dd class="col-xl-6">{{ user_object.profile.address }}</dd>
 
             <dt class="col-xl-6">{% trans 'balance'|capfirst %}</dt>
-            <dd class="col-xl-6">{{ user.note.balance | pretty_money }}</dd>
+            <dd class="col-xl-6">{{ user_object.note.balance | pretty_money }}</dd>
 
-            <dt class="col-xl-6"> <a href="{% url 'member:user_alias' user.pk %}">{% trans 'aliases'|capfirst %}</a></dt>
-            <dd class="col-xl-6 text-truncate">{{ user.note.alias_set.all|join:", " }}</dd>
+            <dt class="col-xl-6"> <a href="{% url 'member:user_alias' user_object.pk %}">{% trans 'aliases'|capfirst %}</a></dt>
+            <dd class="col-xl-6 text-truncate">{{ user_object.note.alias_set.all|join:", " }}</dd>
         </dl>
 
-        {% if user.pk == user.pk %}
+        {% if user_object.pk == user_object.pk %}
         <a class="small" href="{% url 'member:auth_token' %}">{% trans 'Manage auth token' %}</a>
         {% endif %}
     </div>
     <div class="card-footer text-center">
-        <a class="btn btn-primary btn-sm" href="{% url 'member:user_update_profile' user.pk %}">{% trans 'Update Profile' %}</a>
-        {% url 'member:user_detail' user.pk as user_profile_url %}
+        <a class="btn btn-primary btn-sm" href="{% url 'member:user_update_profile' user_object.pk %}">{% trans 'Update Profile' %}</a>
+        {% url 'member:user_detail' user_object.pk as user_profile_url %}
         {%if request.path_info != user_profile_url %}
         <a class="btn btn-primary btn-sm" href="{{ user_profile_url }}">{% trans 'View Profile' %}</a>
         {% endif %}
diff --git a/templates/member/profile_tables.html b/templates/member/profile_tables.html
index ff66155a..6c4cf662 100644
--- a/templates/member/profile_tables.html
+++ b/templates/member/profile_tables.html
@@ -2,10 +2,10 @@
 {% load i18n %}
 {% load perms %}
 
-{% if not object.profile.email_confirmed and "member.change_profile_email_confirmed"|has_perm:user.profile %}
+{% if not object.profile.email_confirmed and "member.change_profile_email_confirmed"|has_perm:user_object.profile %}
     <div class="alert alert-warning">
         {% trans "This user doesn't have confirmed his/her e-mail address." %}
-        <a href="{% url "registration:email_validation_resend" pk=user.pk %}">{% trans "Click here to resend a validation link." %}</a>
+        <a href="{% url "registration:email_validation_resend" pk=user_object.pk %}">{% trans "Click here to resend a validation link." %}</a>
     </div>
 {% endif %}
 
@@ -22,7 +22,7 @@
 
 <div class="card">
     <div class="card-header position-relative" id="historyListHeading">
-            <a class="stretched-link font-weight-bold" {% if "note.view_note"|has_perm:user.note %} href="{% url 'note:transactions' pk=user.note.pk %}" {% endif %}>
+            <a class="stretched-link font-weight-bold" {% if "note.view_note"|has_perm:user_object.note %} href="{% url 'note:transactions' pk=user_object.note.pk %}" {% endif %}>
             <i class="fa fa-euro"></i> {% trans "Transaction history" %}
         </a>
     </div>
diff --git a/templates/wei/weiclub_detail.html b/templates/wei/weiclub_detail.html
index 72c70aa5..d0e8f8e9 100644
--- a/templates/wei/weiclub_detail.html
+++ b/templates/wei/weiclub_detail.html
@@ -15,6 +15,16 @@
             $("#profile_infos").load("{% url 'wei:wei_detail' pk=object.pk %} #profile_infos");
         }
 
-        window.history.replaceState({}, document.title, location.pathname);
+        $(document).ready(function() {
+            $(".no-perm").parent().addClass("d-none");
+            if ($("a[data-type='validate-membership']:not(.d-none)").length === 0) {
+                $("a[data-type='validate-membership']").parent().addClass("d-none");
+                $("#validate-membership-header").addClass("d-none");
+            }
+            if ($("a[data-type='delete-membership']:not(.d-none)").length === 0) {
+                $("a[data-type='delete-membership']").parent().addClass("d-none");
+                $("#delete-membership-header").addClass("d-none");
+            }
+        });
     </script>
 {% endblock %}
diff --git a/templates/wei/weimembership_form.html b/templates/wei/weimembership_form.html
index 7c74457b..1e26414b 100644
--- a/templates/wei/weimembership_form.html
+++ b/templates/wei/weimembership_form.html
@@ -166,7 +166,9 @@
                         </div>
                     {% else %}
                         <div class="alert alert-success">
-                            {% trans "The note has enough money, the registration is possible." %}
+                            {% blocktrans trimmed with pretty_fee=fee|pretty_money %}
+                                The note has enough money ({{ pretty_fee }} required), the registration is possible.
+                            {% endblocktrans %}
                         </div>
                     {% endif %}
                 {% endif %}
@@ -178,14 +180,12 @@
                 {% endif %}
 
                 {% if not kfet_member %}
-                    <div class="alert alert-danger">
+                    <div class="alert alert-warning">
                         {% url 'registration:future_user_detail' pk=registration.user.pk as future_user_detail %}
                     {% url 'member:club_detail' pk=club.parent_club.parent_club.pk as club_detail %}
                         {% blocktrans trimmed %}
-                            This user is not a member of the Kfet club for the comming year. Please adhere
-                            <a href="{{ future_user_detail }}">here if he/she is in her/his first year</a>
-                            or <a href="{{ club_detail }}">here if he/she was an old member</a> before you validate
-                            the registration of the WEI.
+                            This user is not a member of the Kfet club for the coming year. The membership will be
+                            processed automatically, the WEI registration includes the membership fee.
                         {% endblocktrans %}
                     </div>
                 {% endif %}
diff --git a/templates/wei/weiregistration_list.html b/templates/wei/weiregistration_list.html
index 05626dd2..88c217aa 100644
--- a/templates/wei/weiregistration_list.html
+++ b/templates/wei/weiregistration_list.html
@@ -41,6 +41,16 @@
         }
 
         searchbar_obj.keyup(reloadTable);
+
+        $(".no-perm").parent().addClass("d-none");
+        if ($("a[data-type='validate-membership']:not(.d-none)").length === 0) {
+            $("a[data-type='validate-membership']").parent().addClass("d-none");
+            $("#validate-membership-header").addClass("d-none");
+        }
+        if ($("a[data-type='delete-membership']:not(.d-none)").length === 0) {
+            $("a[data-type='delete-membership']").parent().addClass("d-none");
+            $("#delete-membership-header").addClass("d-none");
+        }
     });
 </script>
 {% endblock %}
-- 
GitLab