diff --git a/apps/activity/fixtures/initial.json b/apps/activity/fixtures/initial.json
new file mode 100644
index 0000000000000000000000000000000000000000..1856bce401594c6f1da41994ec6068bd04d1f457
--- /dev/null
+++ b/apps/activity/fixtures/initial.json
@@ -0,0 +1,20 @@
+[
+  {
+    "model": "activity.activitytype",
+    "pk": 1,
+    "fields": {
+      "name": "Pot",
+      "can_invite": true,
+      "guest_entry_fee": 500
+    }
+  },
+  {
+    "model": "activity.activitytype",
+    "pk": 2,
+    "fields": {
+      "name": "Soir\u00e9e de club",
+      "can_invite": false,
+      "guest_entry_fee": 0
+    }
+  }
+]
\ No newline at end of file
diff --git a/apps/activity/models.py b/apps/activity/models.py
index ed2d94c9a4394a5314d5e90cd94a382389de9017..215aa0cc0c808486c2ddf1237903ee6ad235387e 100644
--- a/apps/activity/models.py
+++ b/apps/activity/models.py
@@ -130,6 +130,8 @@ class Entry(models.Model):
 
     class Meta:
         unique_together = (('activity', 'note', 'guest', ), )
+        verbose_name = _("entry")
+        verbose_name_plural = _("entries")
 
     def save(self, force_insert=False, force_update=False, using=None,
              update_fields=None):
diff --git a/apps/activity/views.py b/apps/activity/views.py
index 51e2ebf535a8ba762e8da65ee91c749ceeec556b..714d0dbe4432bdf91a98ce78a2da7ab9c13f2429 100644
--- a/apps/activity/views.py
+++ b/apps/activity/views.py
@@ -139,6 +139,7 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView):
                     | Q(name__regex=pattern)
                     | Q(normalized_name__regex=Alias.normalize(pattern)))) \
             .filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view"))\
+            .filter(note__noteuser__user__profile__registration_valid=True)\
             .distinct("username")[:20]
         for note in note_qs:
             note.type = "Adhérent"
@@ -154,4 +155,8 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView):
         ctx["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk
         ctx["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk
 
+        ctx["activities_open"] = Activity.objects.filter(open=True).filter(
+            PermissionBackend.filter_queryset(self.request.user, Activity, "view")).filter(
+            PermissionBackend.filter_queryset(self.request.user, Activity, "change")).all()
+
         return ctx
diff --git a/apps/logs/models.py b/apps/logs/models.py
index 10e2651f2f8475c4241c05a37f9c56b37c8d5bb5..94e2b4cebecc9835f20b958fe08aa146014a46d4 100644
--- a/apps/logs/models.py
+++ b/apps/logs/models.py
@@ -75,3 +75,7 @@ class Changelog(models.Model):
 
     def delete(self, using=None, keep_parents=False):
         raise ValidationError(_("Logs cannot be destroyed."))
+
+    class Meta:
+        verbose_name = _("changelog")
+        verbose_name_plural = _("changelogs")
diff --git a/apps/member/forms.py b/apps/member/forms.py
index a37d143ef4d75d3e4481385bef27f45fada5865b..6fe95f5a1bf37369dbd9ecdc41dc1d773bf62f89 100644
--- a/apps/member/forms.py
+++ b/apps/member/forms.py
@@ -2,8 +2,10 @@
 # SPDX-License-Identifier: GPL-3.0-or-later
 
 from django import forms
-from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
+from django.contrib.auth.forms import AuthenticationForm
 from django.contrib.auth.models import User
+from django.utils.translation import gettext_lazy as _
+from note.models import NoteSpecial
 from note_kfet.inputs import Autocomplete, AmountInput, DatePickerInput
 from permission.models import PermissionMask
 
@@ -18,17 +20,6 @@ class CustomAuthenticationForm(AuthenticationForm):
     )
 
 
-class SignUpForm(UserCreationForm):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.fields['username'].widget.attrs.pop("autofocus", None)
-        self.fields['first_name'].widget.attrs.update({"autofocus": "autofocus"})
-
-    class Meta:
-        model = User
-        fields = ['first_name', 'last_name', 'username', 'email']
-
-
 class ProfileForm(forms.ModelForm):
     """
     A form for the extras field provided by the :model:`member.Profile` model.
@@ -37,7 +28,7 @@ class ProfileForm(forms.ModelForm):
     class Meta:
         model = Profile
         fields = '__all__'
-        exclude = ['user']
+        exclude = ('user', 'email_confirmed', 'registration_valid', 'soge', )
 
 
 class ClubForm(forms.ModelForm):
@@ -59,6 +50,42 @@ class ClubForm(forms.ModelForm):
 
 
 class MembershipForm(forms.ModelForm):
+    soge = forms.BooleanField(
+        label=_("Inscription paid by Société Générale"),
+        required=False,
+        help_text=_("Check this case is the Société Générale paid the inscription."),
+    )
+
+    credit_type = forms.ModelChoiceField(
+        queryset=NoteSpecial.objects,
+        label=_("Credit type"),
+        empty_label=_("No credit"),
+        required=False,
+        help_text=_("You can credit the note of the user."),
+    )
+
+    credit_amount = forms.IntegerField(
+        label=_("Credit amount"),
+        required=False,
+        initial=0,
+        widget=AmountInput(),
+    )
+
+    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,
+    )
+
     class Meta:
         model = Membership
         fields = ('user', 'roles', 'date_start')
diff --git a/apps/member/models.py b/apps/member/models.py
index 693854afa046ff17d3668ff774b16521f9781254..3a0224341ae62baeafe514593fde4a8b61111221 100644
--- a/apps/member/models.py
+++ b/apps/member/models.py
@@ -2,13 +2,18 @@
 # SPDX-License-Identifier: GPL-3.0-or-later
 
 import datetime
+import os
 
 from django.conf import settings
 from django.contrib.auth.models import User
 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.encoding import force_bytes
+from django.utils.http import urlsafe_base64_encode
 from django.utils.translation import gettext_lazy as _
+from registration.tokens import email_validation_token
 from note.models import MembershipTransaction
 
 
@@ -45,6 +50,23 @@ class Profile(models.Model):
     )
     paid = models.BooleanField(
         verbose_name=_("paid"),
+        help_text=_("Tells if the user receive a salary."),
+        default=False,
+    )
+
+    email_confirmed = models.BooleanField(
+        verbose_name=_("email confirmed"),
+        default=False,
+    )
+
+    registration_valid = models.BooleanField(
+        verbose_name=_("registration valid"),
+        default=False,
+    )
+
+    soge = models.BooleanField(
+        verbose_name=_("Société générale"),
+        help_text=_("Has the user ever be paid by the Société générale?"),
         default=False,
     )
 
@@ -56,6 +78,17 @@ class Profile(models.Model):
     def get_absolute_url(self):
         return reverse('user_detail', args=(self.pk,))
 
+    def send_email_validation_link(self):
+        subject = "Activate your Note Kfet account"
+        message = loader.render_to_string('registration/mails/email_validation_email.html',
+                                          {
+                                              'user': self.user,
+                                              'domain': os.getenv("NOTE_URL", "note.example.com"),
+                                              'token': email_validation_token.make_token(self.user),
+                                              'uid': urlsafe_base64_encode(force_bytes(self.user.pk)).decode('UTF-8'),
+                                          })
+        self.user.email_user(subject, message)
+
 
 class Club(models.Model):
     """
@@ -202,6 +235,7 @@ class Membership(models.Model):
     )
 
     date_start = models.DateField(
+        default=datetime.date.today,
         verbose_name=_('membership starts on'),
     )
 
@@ -215,12 +249,18 @@ class Membership(models.Model):
     )
 
     def valid(self):
+        """
+        A membership is valid if today is between the start and the end date.
+        """
         if self.date_end is not None:
             return self.date_start.toordinal() <= datetime.datetime.now().toordinal() < self.date_end.toordinal()
         else:
             return self.date_start.toordinal() <= datetime.datetime.now().toordinal()
 
     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)
@@ -252,6 +292,9 @@ class Membership(models.Model):
         self.make_transaction()
 
     def make_transaction(self):
+        """
+        Create Membership transaction associated to this membership.
+        """
         if not self.fee or MembershipTransaction.objects.filter(membership=self).exists():
             return
 
diff --git a/apps/member/signals.py b/apps/member/signals.py
index 2b03e3ced7fac6f67efaa9f509d978c0df8e7987..fbb66c1f6055de516653e62bef2bef2348bc6e24 100644
--- a/apps/member/signals.py
+++ b/apps/member/signals.py
@@ -10,7 +10,7 @@ def save_user_profile(instance, created, raw, **_kwargs):
         # When provisionning data, do not try to autocreate
         return
 
-    if created:
+    if created and instance.is_active:
         from .models import Profile
         Profile.objects.get_or_create(user=instance)
-    instance.profile.save()
+        instance.profile.save()
diff --git a/apps/member/tables.py b/apps/member/tables.py
index c8a510ff8d5a85c6e7a7195b85c8426d5436a64c..515d78368a55ee98290b8dd8cad87f318d9e5f6c 100644
--- a/apps/member/tables.py
+++ b/apps/member/tables.py
@@ -15,6 +15,9 @@ from .models import Club, Membership
 
 
 class ClubTable(tables.Table):
+    """
+    List all clubs.
+    """
     class Meta:
         attrs = {
             'class': 'table table-condensed table-striped table-hover'
@@ -30,6 +33,9 @@ class ClubTable(tables.Table):
 
 
 class UserTable(tables.Table):
+    """
+    List all users.
+    """
     section = tables.Column(accessor='profile.section')
 
     balance = tables.Column(accessor='note.balance', verbose_name=_("Balance"))
@@ -51,6 +57,9 @@ class UserTable(tables.Table):
 
 
 class MembershipTable(tables.Table):
+    """
+    List all memberships.
+    """
     roles = tables.Column(
         attrs={
             "td": {
@@ -59,7 +68,17 @@ class MembershipTable(tables.Table):
         }
     )
 
+    def render_user(self, value):
+        # If the user has the right, link the displayed user with the page of its detail.
+        s = value.username
+        if PermissionBackend.check_perm(get_current_authenticated_user(), "auth.view_user", value):
+            s = format_html("<a href={url}>{name}</a>",
+                            url=reverse_lazy('member:user_detail', kwargs={"pk": value.pk}), name=s)
+
+        return s
+
     def render_club(self, value):
+        # If the user has the right, link the displayed club with the page of its detail.
         s = value.name
         if PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_club", value):
             s = format_html("<a href={url}>{name}</a>",
@@ -94,6 +113,7 @@ class MembershipTable(tables.Table):
         return t
 
     def render_roles(self, record):
+        # If the user has the right to manage the roles, display the link to manage them
         roles = record.roles.all()
         s = ", ".join(str(role) for role in roles)
         if PermissionBackend.check_perm(get_current_authenticated_user(), "member.change_membership_roles", record):
diff --git a/apps/member/urls.py b/apps/member/urls.py
index 1214f0243c5aebe98e3b2da5469eda3240e659cb..4be4ceaefc1c303cac592494bfd302abacec2b64 100644
--- a/apps/member/urls.py
+++ b/apps/member/urls.py
@@ -7,14 +7,12 @@ from . import views
 
 app_name = 'member'
 urlpatterns = [
-    path('signup/', views.UserCreateView.as_view(), name="signup"),
-
     path('club/', views.ClubListView.as_view(), name="club_list"),
     path('club/create/', views.ClubCreateView.as_view(), name="club_create"),
     path('club/<int:pk>/', views.ClubDetailView.as_view(), name="club_detail"),
-    path('club/<int:pk>/add_member/', views.ClubAddMemberView.as_view(), name="club_add_member"),
+    path('club/<int:club_pk>/add_member/', views.ClubAddMemberView.as_view(), name="club_add_member"),
     path('club/manage_roles/<int:pk>/', views.ClubManageRolesView.as_view(), name="club_manage_roles"),
-    path('club/renew_membership/<int:pk>/', views.ClubRenewMembershipView.as_view(), name="club_renew_membership"),
+    path('club/renew_membership/<int:pk>/', views.ClubAddMemberView.as_view(), name="club_renew_membership"),
     path('club/<int:pk>/update/', views.ClubUpdateView.as_view(), name="club_update"),
     path('club/<int:pk>/update_pic/', views.ClubPictureUpdateView.as_view(), name="club_update_pic"),
     path('club/<int:pk>/aliases/', views.ClubAliasView.as_view(), name="club_alias"),
diff --git a/apps/member/views.py b/apps/member/views.py
index f695002f9d141afe31d645b149bb8fa8ed160a22..381314b22e61cc2afe452b413981c4d8a6e639be 100644
--- a/apps/member/views.py
+++ b/apps/member/views.py
@@ -9,30 +9,30 @@ from django.conf import settings
 from django.contrib.auth.mixins import LoginRequiredMixin
 from django.contrib.auth.models import User
 from django.contrib.auth.views import LoginView
-from django.core.exceptions import ValidationError
 from django.db.models import Q
-from django.forms import HiddenInput
 from django.shortcuts import redirect
 from django.urls import reverse_lazy
 from django.utils.translation import gettext_lazy as _
 from django.views.generic import CreateView, DetailView, UpdateView, TemplateView
-from django.views.generic.base import View
 from django.views.generic.edit import FormMixin
 from django_tables2.views import SingleTableView
 from rest_framework.authtoken.models import Token
 from note.forms import ImageForm
-from note.models import Alias, NoteUser
-from note.models.transactions import Transaction
+from note.models import Alias, NoteUser, NoteSpecial
+from note.models.transactions import Transaction, SpecialTransaction
 from note.tables import HistoryTable, AliasTable
 from permission.backends import PermissionBackend
 from permission.views import ProtectQuerysetMixin
 
-from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, CustomAuthenticationForm
-from .models import Club, Membership
+from .forms import ProfileForm, ClubForm, MembershipForm, CustomAuthenticationForm
+from .models import Club, Membership, Role
 from .tables import ClubTable, UserTable, MembershipTable
 
 
 class CustomLoginView(LoginView):
+    """
+    Login view, where the user can select its permission mask.
+    """
     form_class = CustomAuthenticationForm
 
     def form_valid(self, form):
@@ -40,33 +40,10 @@ class CustomLoginView(LoginView):
         return super().form_valid(form)
 
 
-class UserCreateView(CreateView):
+class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
     """
-    Une vue pour inscrire un utilisateur et lui créer un profile
+    Update the user information.
     """
-
-    form_class = SignUpForm
-    success_url = reverse_lazy('login')
-    template_name = 'member/signup.html'
-    second_form = ProfileForm
-
-    def get_context_data(self, **kwargs):
-        context = super().get_context_data(**kwargs)
-        context["profile_form"] = self.second_form()
-
-        return context
-
-    def form_valid(self, form):
-        profile_form = ProfileForm(self.request.POST)
-        if form.is_valid() and profile_form.is_valid():
-            user = form.save(commit=False)
-            user.profile = profile_form.save(commit=False)
-            user.save()
-            user.profile.save()
-        return super().form_valid(form)
-
-
-class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
     model = User
     fields = ['first_name', 'last_name', 'username', 'email']
     template_name = 'member/profile_update.html'
@@ -75,14 +52,20 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
 
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
+
+        form = context['form']
+        form.fields['username'].widget.attrs.pop("autofocus", None)
+        form.fields['first_name'].widget.attrs.update({"autofocus": "autofocus"})
+        form.fields['first_name'].required = True
+        form.fields['last_name'].required = True
+        form.fields['email'].required = True
+        form.fields['email'].help_text = _("This address must be valid.")
+
         context['profile_form'] = self.profile_form(instance=context['user_object'].profile)
         context['title'] = _("Update Profile")
         return context
 
-    def get_form(self, form_class=None):
-        form = super().get_form(form_class)
-        if 'username' not in form.data:
-            return form
+    def form_valid(self, form):
         new_username = form.data['username']
         # Si l'utilisateur cherche à modifier son pseudo, le nouveau pseudo ne doit pas être proche d'un alias existant
         note = NoteUser.objects.filter(
@@ -90,9 +73,8 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
         if note.exists() and note.get().user != self.object:
             form.add_error('username',
                            _("An alias with a similar name already exists."))
-        return form
+            return super().form_invalid(form)
 
-    def form_valid(self, form):
         profile_form = ProfileForm(
             data=self.request.POST,
             instance=self.object.profile,
@@ -108,19 +90,24 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
                 if similar.exists():
                     similar.delete()
 
+            olduser = User.objects.get(pk=form.instance.pk)
+
             user = form.save(commit=False)
             profile = profile_form.save(commit=False)
             profile.user = user
             profile.save()
             user.save()
+
+            if olduser.email != user.email:
+                # If the user changed her/his email, then it is unvalidated and a confirmation link is sent.
+                user.profile.email_confirmed = False
+                user.profile.send_email_validation_link()
+
         return super().form_valid(form)
 
     def get_success_url(self, **kwargs):
-        if kwargs:
-            return reverse_lazy('member:user_detail',
-                                kwargs={'pk': kwargs['id']})
-        else:
-            return reverse_lazy('member:user_detail', args=(self.object.id,))
+        url = 'member:user_detail' if self.object.profile.registration_valid else 'registration:future_user_detail'
+        return reverse_lazy(url, args=(self.object.id,))
 
 
 class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
@@ -131,29 +118,43 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
     context_object_name = "user_object"
     template_name = "member/profile_detail.html"
 
+    def get_queryset(self, **kwargs):
+        """
+        We can't display information of a not registered user.
+        """
+        return super().get_queryset().filter(profile__registration_valid=True)
+
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
         user = context['user_object']
         history_list = \
             Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note)).order_by("-id")\
             .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view"))
-        context['history_list'] = HistoryTable(history_list)
+        history_table = HistoryTable(history_list, prefix='transaction-')
+        history_table.paginate(per_page=20, page=self.request.GET.get("transaction-page", 1))
+        context['history_list'] = history_table
+
         club_list = Membership.objects.filter(user=user, date_end__gte=datetime.today())\
             .filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view"))
-        context['club_list'] = MembershipTable(data=club_list)
+        membership_table = MembershipTable(data=club_list, prefix='membership-')
+        membership_table.paginate(per_page=10, page=self.request.GET.get("membership-page", 1))
+        context['club_list'] = membership_table
         return context
 
 
 class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
     """
-    Affiche la liste des utilisateurs, avec une fonction de recherche statique
+    Display user list, with a search bar
     """
     model = User
     table_class = UserTable
     template_name = 'member/user_list.html'
 
     def get_queryset(self, **kwargs):
-        qs = super().get_queryset()
+        """
+        Filter the user list with the given pattern.
+        """
+        qs = super().get_queryset().filter(profile__registration_valid=True)
         if "search" in self.request.GET:
             pattern = self.request.GET["search"]
 
@@ -164,6 +165,7 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
                 Q(first_name__iregex=pattern)
                 | Q(last_name__iregex=pattern)
                 | Q(profile__section__iregex=pattern)
+                | Q(profile__username__iregex="^" + pattern)
                 | Q(note__alias__name__iregex="^" + pattern)
                 | Q(note__alias__normalized_name__iregex=Alias.normalize("^" + pattern))
             )
@@ -181,6 +183,9 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
 
 
 class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
+    """
+    View and manage user aliases.
+    """
     model = User
     template_name = 'member/profile_alias.html'
     context_object_name = 'user_object'
@@ -193,6 +198,9 @@ class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
 
 
 class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, DetailView):
+    """
+    Update profile picture of the user note.
+    """
     form_class = ImageForm
 
     def get_context_data(self, **kwargs):
@@ -292,6 +300,9 @@ class ClubListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
 
 
 class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
+    """
+    Display details of a club
+    """
     model = Club
     context_object_name = "club"
 
@@ -304,14 +315,19 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
 
         club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))\
             .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")).order_by('-id')
-        context['history_list'] = HistoryTable(club_transactions)
+        history_table = HistoryTable(club_transactions, prefix="history-")
+        history_table.paginate(per_page=20, page=self.request.GET.get('history-page', 1))
+        context['history_list'] = history_table
         club_member = Membership.objects.filter(
             club=club,
             date_end__gte=datetime.today(),
         ).filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view"))
 
-        context['member_list'] = MembershipTable(data=club_member)
+        membership_table = MembershipTable(data=club_member, prefix="membership-")
+        membership_table.paginate(per_page=20, page=self.request.GET.get('membership-page', 1))
+        context['member_list'] = membership_table
 
+        # Check if the user has the right to create a membership, to display the button.
         empty_membership = Membership(
             club=club,
             user=User.objects.first(),
@@ -326,6 +342,9 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
 
 
 class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
+    """
+    Manage aliases of a club.
+    """
     model = Club
     template_name = 'member/club_alias.html'
     context_object_name = 'club'
@@ -338,6 +357,9 @@ class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
 
 
 class ClubUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
+    """
+    Update the information of a club.
+    """
     model = Club
     context_object_name = "club"
     form_class = ClubForm
@@ -348,6 +370,9 @@ class ClubUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
 
 
 class ClubPictureUpdateView(PictureUpdateView):
+    """
+    Update the profile picture of a club.
+    """
     model = Club
     template_name = 'member/club_picture_update.html'
     context_object_name = 'club'
@@ -357,29 +382,107 @@ class ClubPictureUpdateView(PictureUpdateView):
 
 
 class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
+    """
+    Add a membership to a club.
+    """
     model = Membership
     form_class = MembershipForm
     template_name = 'member/add_members.html'
 
     def get_context_data(self, **kwargs):
-        club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
-            .get(pk=self.kwargs["pk"])
         context = super().get_context_data(**kwargs)
+        form = context['form']
+
+        if "club_pk" in self.kwargs:
+            # We create a new membership.
+            club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
+                .get(pk=self.kwargs["club_pk"])
+            form.fields['credit_amount'].initial = club.membership_fee_paid
+            form.fields['roles'].initial = Role.objects.filter(name="Membre de club").all()
+
+            # 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']
+            else:
+                fee = 0
+                bde = Club.objects.get(name="BDE")
+                fee += bde.membership_fee_paid
+                kfet = Club.objects.get(name="Kfet")
+                fee += kfet.membership_fee_paid
+                context["total_fee"] = "{:.02f}".format(fee / 100, )
+        else:
+            # This is a renewal. Fields can be pre-completed.
+            old_membership = self.get_queryset().get(pk=self.kwargs["pk"])
+            club = old_membership.club
+            user = old_membership.user
+            form.fields['user'].initial = user
+            form.fields['user'].disabled = True
+            form.fields['roles'].initial = old_membership.roles.all()
+            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['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:
+                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
+                kfet = Club.objects.get(name="Kfet")
+                fee += kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
+                context["total_fee"] = "{:.02f}".format(fee / 100, )
+
         context['club'] = club
 
         return context
 
     def form_valid(self, form):
-        club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
-            .get(pk=self.kwargs["pk"])
-        user = self.request.user
+        """
+        Create membership, check that all is good, make transactions
+        """
+        # Get the club that is concerned by the membership
+        if "club_pk" in self.kwargs:
+            club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view")) \
+                .get(pk=self.kwargs["club_pk"])
+            user = form.instance.user
+        else:
+            old_membership = self.get_queryset().get(pk=self.kwargs["pk"])
+            club = old_membership.club
+            user = old_membership.user
+
         form.instance.club = club
 
+        # Get form data
+        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"]
+        soge = form.cleaned_data["soge"] and not user.profile.soge and club.name == "BDE"
+
+        # If Société générale pays, then we auto-fill some data
+        if soge:
+            credit_type = NoteSpecial.objects.get(special_type="Virement bancaire")
+            bde = club
+            kfet = Club.objects.get(name="Kfet")
+            if user.profile.paid:
+                fee = bde.membership_fee_paid + kfet.membership_fee_paid
+            else:
+                fee = bde.membership_fee_unpaid + kfet.membership_fee_unpaid
+            credit_amount = fee
+            bank = "Société générale"
+
+        if credit_type is None:
+            credit_amount = 0
+
         if user.profile.paid:
             fee = club.membership_fee_paid
         else:
             fee = club.membership_fee_unpaid
-        if user.note.balance < fee and not Membership.objects.filter(
+        if user.note.balance + credit_amount < fee and not Membership.objects.filter(
                 club__name="Kfet",
                 user=user,
                 date_start__lte=datetime.now().date(),
@@ -390,6 +493,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
             # 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).exists():
@@ -405,16 +509,70 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
             form.add_error('user', _('User is already a member of the club'))
             return super().form_invalid(form)
 
-        if form.instance.club.membership_start and form.instance.date_start < form.instance.club.membership_start:
+        if club.membership_start and form.instance.date_start < club.membership_start:
             form.add_error('user', _("The membership must start after {:%m-%d-%Y}.")
                            .format(form.instance.club.membership_start))
             return super().form_invalid(form)
 
-        if form.instance.club.membership_end and form.instance.date_start > form.instance.club.membership_end:
+        if club.membership_end and form.instance.date_start > club.membership_end:
             form.add_error('user', _("The membership must begin before {:%m-%d-%Y}.")
                            .format(form.instance.club.membership_start))
             return super().form_invalid(form)
 
+        # Now, all is fine, the membership can be created.
+
+        # Credit note before the membership is created.
+        if credit_amount > 0:
+            if not last_name or not first_name or (not bank and credit_type.special_type == "Chèque"):
+                if not last_name:
+                    form.add_error('last_name', _("This field is required."))
+                if not first_name:
+                    form.add_error('first_name', _("This field is required."))
+                if not bank and credit_type.special_type == "Chèque":
+                    form.add_error('bank', _("This field is required."))
+                return self.form_invalid(form)
+
+            SpecialTransaction.objects.create(
+                source=credit_type,
+                destination=user.note,
+                quantity=1,
+                amount=credit_amount,
+                reason="Crédit " + credit_type.special_type + " (Adhésion " + club.name + ")",
+                last_name=last_name,
+                first_name=first_name,
+                bank=bank,
+                valid=True,
+            )
+
+        # If Société générale pays, then we store the information: the bank can't pay twice to a same person.
+        if soge:
+            user.profile.soge = True
+            user.profile.save()
+
+            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.objects.create(
+                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,
+            )
+            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 super().form_valid(form)
 
     def get_success_url(self):
@@ -422,6 +580,9 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
 
 
 class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
+    """
+    Manage the roles of a user in a club
+    """
     model = Membership
     form_class = MembershipForm
     template_name = 'member/add_members.html'
@@ -430,49 +591,19 @@ class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
         context = super().get_context_data(**kwargs)
         club = self.object.club
         context['club'] = club
-        form = context['form']
-        form.fields['user'].disabled = True
-        form.fields['date_start'].widget = HiddenInput()
-
         return context
 
-    def form_valid(self, form):
-        if form.instance.club.membership_start and form.instance.date_start < form.instance.club.membership_start:
-            form.add_error('user', _("The membership must start after {:%m-%d-%Y}.")
-                           .format(form.instance.club.membership_start))
-            return super().form_invalid(form)
-
-        if form.instance.club.membership_end and form.instance.date_start > form.instance.club.membership_end:
-            form.add_error('user', _("The membership must begin before {:%m-%d-%Y}.")
-                           .format(form.instance.club.membership_start))
-            return super().form_invalid(form)
-
-        return super().form_valid(form)
+    def get_form(self, form_class=None):
+        form = super().get_form(form_class)
+        # We don't create a full membership, we only update one field
+        form.fields['user'].disabled = True
+        del form.fields['date_start']
+        del form.fields['credit_type']
+        del form.fields['credit_amount']
+        del form.fields['last_name']
+        del form.fields['first_name']
+        del form.fields['bank']
+        return form
 
     def get_success_url(self):
         return reverse_lazy('member:club_detail', kwargs={'pk': self.object.club.id})
-
-
-class ClubRenewMembershipView(ProtectQuerysetMixin, LoginRequiredMixin, View):
-    def get(self, *args, **kwargs):
-        user = self.request.user
-        membership = Membership.objects.filter(PermissionBackend.filter_queryset(user, Membership, "change"))\
-            .filter(pk=self.kwargs["pk"]).get()
-
-        if Membership.objects.filter(
-            club=membership.club,
-            user=membership.user,
-            date_start__gte=membership.club.membership_start,
-            date_end__lte=membership.club.membership_end,
-        ).exists():
-            raise ValidationError(_("This membership is already renewed"))
-
-        new_membership = Membership.objects.create(
-            user=user,
-            club=membership.club,
-            date_start=membership.date_end + timedelta(days=1),
-        )
-        new_membership.roles.set(membership.roles.all())
-        new_membership.save()
-
-        return redirect(reverse_lazy('member:club_detail', kwargs={'pk': membership.club.pk}))
diff --git a/apps/note/admin.py b/apps/note/admin.py
index 7b4ba870c64331eb10501ba13ef68df493418127..dc6470d2d8090f7d0fe78762fb41a67f7cc169bc 100644
--- a/apps/note/admin.py
+++ b/apps/note/admin.py
@@ -8,7 +8,7 @@ from polymorphic.admin import PolymorphicChildModelAdmin, \
 
 from .models.notes import Alias, Note, NoteClub, NoteSpecial, NoteUser
 from .models.transactions import Transaction, TemplateCategory, TransactionTemplate, \
-    RecurrentTransaction, MembershipTransaction
+    RecurrentTransaction, MembershipTransaction, SpecialTransaction
 
 
 class AliasInlines(admin.TabularInline):
@@ -102,7 +102,7 @@ class TransactionAdmin(PolymorphicParentModelAdmin):
     """
     Admin customisation for Transaction
     """
-    child_models = (RecurrentTransaction, MembershipTransaction)
+    child_models = (RecurrentTransaction, MembershipTransaction, SpecialTransaction)
     list_display = ('created_at', 'poly_source', 'poly_destination',
                     'quantity', 'amount', 'valid')
     list_filter = ('valid',)
@@ -141,7 +141,14 @@ class TransactionAdmin(PolymorphicParentModelAdmin):
 @admin.register(MembershipTransaction)
 class MembershipTransactionAdmin(PolymorphicChildModelAdmin):
     """
-    Admin customisation for Transaction
+    Admin customisation for MembershipTransaction
+    """
+
+
+@admin.register(SpecialTransaction)
+class SpecialTransactionAdmin(PolymorphicChildModelAdmin):
+    """
+    Admin customisation for SpecialTransaction
     """
 
 
diff --git a/apps/note/signals.py b/apps/note/signals.py
index e62115b30ce014ac480b167e900960a2fd660b7c..37737a45d7fe671dda9ce214af24820b6292872e 100644
--- a/apps/note/signals.py
+++ b/apps/note/signals.py
@@ -2,7 +2,7 @@
 # SPDX-License-Identifier: GPL-3.0-or-later
 
 
-def save_user_note(instance, created, raw, **_kwargs):
+def save_user_note(instance, raw, **_kwargs):
     """
     Hook to create and save a note when an user is updated
     """
@@ -10,10 +10,11 @@ def save_user_note(instance, created, raw, **_kwargs):
         # When provisionning data, do not try to autocreate
         return
 
-    if created:
-        from .models import NoteUser
-        NoteUser.objects.create(user=instance)
-    instance.note.save()
+    if (instance.is_superuser or instance.profile.registration_valid) and instance.is_active:
+        # Create note only when the registration is validated
+        from note.models import NoteUser
+        NoteUser.objects.get_or_create(user=instance)
+        instance.note.save()
 
 
 def save_club_note(instance, created, raw, **_kwargs):
diff --git a/apps/note/views.py b/apps/note/views.py
index ac9b3e40623ce1ec676f340ddf7caa3a81639e43..88d47847288fe6b79c07ea8c01ad90d106b45343 100644
--- a/apps/note/views.py
+++ b/apps/note/views.py
@@ -1,6 +1,7 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
+from django.conf import settings
 from django.contrib.auth.mixins import LoginRequiredMixin
 from django.contrib.contenttypes.models import ContentType
 from django.utils.translation import gettext_lazy as _
@@ -29,7 +30,7 @@ class TransactionCreateView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTabl
     table_class = HistoryTable
 
     def get_queryset(self, **kwargs):
-        return super().get_queryset(**kwargs).order_by("-id").all()[:50]
+        return super().get_queryset(**kwargs).order_by("-id").all()[:20]
 
     def get_context_data(self, **kwargs):
         """
@@ -44,12 +45,19 @@ class TransactionCreateView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTabl
             .filter(PermissionBackend.filter_queryset(self.request.user, NoteSpecial, "view"))\
             .order_by("special_type").all()
 
+        # Add a shortcut for entry page for open activities
+        if "activity" in settings.INSTALLED_APPS:
+            from activity.models import Activity
+            context["activities_open"] = Activity.objects.filter(open=True).filter(
+                PermissionBackend.filter_queryset(self.request.user, Activity, "view")).filter(
+                PermissionBackend.filter_queryset(self.request.user, Activity, "change")).all()
+
         return context
 
 
 class TransactionTemplateCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
     """
-    Create TransactionTemplate
+    Create Transaction template
     """
     model = TransactionTemplate
     form_class = TransactionTemplateForm
@@ -58,7 +66,7 @@ class TransactionTemplateCreateView(ProtectQuerysetMixin, LoginRequiredMixin, Cr
 
 class TransactionTemplateListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
     """
-    List TransactionsTemplates
+    List Transaction templates
     """
     model = TransactionTemplate
     table_class = ButtonTable
@@ -66,6 +74,7 @@ class TransactionTemplateListView(ProtectQuerysetMixin, LoginRequiredMixin, Sing
 
 class TransactionTemplateUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
     """
+    Update Transaction template
     """
     model = TransactionTemplate
     form_class = TransactionTemplateForm
@@ -84,7 +93,7 @@ class ConsoView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
     table_class = HistoryTable
 
     def get_queryset(self, **kwargs):
-        return super().get_queryset(**kwargs).order_by("-id").all()[:50]
+        return super().get_queryset(**kwargs).order_by("-id").all()[:20]
 
     def get_context_data(self, **kwargs):
         """
diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json
index 4cf3ecfa738ee500f3d6f42f53f9be9ce5ca0792..d7eca508ba5647b51412e0d435b0589a52ec13fa 100644
--- a/apps/permission/fixtures/initial.json
+++ b/apps/permission/fixtures/initial.json
@@ -17,54 +17,61 @@
     "model": "member.role",
     "pk": 3,
     "fields": {
-      "name": "Pr\u00e9sident\u00b7e BDE"
+      "name": "Membre de club"
     }
   },
   {
     "model": "member.role",
     "pk": 4,
     "fields": {
-      "name": "Tr\u00e9sorier\u00b7\u00e8re BDE"
+      "name": "Bureau de club"
     }
   },
   {
     "model": "member.role",
     "pk": 5,
     "fields": {
-      "name": "Respo info"
+      "name": "Pr\u00e9sident\u00b7e de club"
     }
   },
   {
     "model": "member.role",
     "pk": 6,
     "fields": {
-      "name": "GC Kfet"
+      "name": "Tr\u00e9sorier\u00b7\u00e8re de club"
     }
   },
   {
     "model": "member.role",
     "pk": 7,
     "fields": {
-      "name": "Pr\u00e9sident\u00b7e de club"
+      "name": "Pr\u00e9sident\u00b7e BDE"
     }
   },
   {
     "model": "member.role",
     "pk": 8,
     "fields": {
-      "name": "Tr\u00e9sorier\u00b7\u00e8re de club"
+      "name": "Tr\u00e9sorier\u00b7\u00e8re BDE"
     }
   },
   {
     "model": "member.role",
-    "pk": 8,
+    "pk": 9,
     "fields": {
-      "name": "Tr\u00e9sorier\u00b7\u00e8re de club"
+      "name": "Respo info"
     }
   },
   {
     "model": "member.role",
-    "pk": 9,
+    "pk": 10,
+    "fields": {
+      "name": "GC Kfet"
+    }
+  },
+  {
+    "model": "member.role",
+    "pk": 11,
     "fields": {
       "name": "Res[pot]"
     }
@@ -97,10 +104,7 @@
     "model": "permission.permission",
     "pk": 1,
     "fields": {
-      "model": [
-        "auth",
-        "user"
-      ],
+      "model": 4,
       "query": "{\"pk\": [\"user\", \"pk\"]}",
       "type": "view",
       "mask": 1,
@@ -112,10 +116,7 @@
     "model": "permission.permission",
     "pk": 2,
     "fields": {
-      "model": [
-        "member",
-        "profile"
-      ],
+      "model": 17,
       "query": "{\"user\": [\"user\"]}",
       "type": "view",
       "mask": 1,
@@ -127,10 +128,7 @@
     "model": "permission.permission",
     "pk": 3,
     "fields": {
-      "model": [
-        "note",
-        "noteuser"
-      ],
+      "model": 27,
       "query": "{\"pk\": [\"user\", \"note\", \"pk\"]}",
       "type": "view",
       "mask": 1,
@@ -142,10 +140,7 @@
     "model": "permission.permission",
     "pk": 4,
     "fields": {
-      "model": [
-        "authtoken",
-        "token"
-      ],
+      "model": 8,
       "query": "{\"user\": [\"user\"]}",
       "type": "view",
       "mask": 1,
@@ -157,10 +152,7 @@
     "model": "permission.permission",
     "pk": 5,
     "fields": {
-      "model": [
-        "note",
-        "transaction"
-      ],
+      "model": 22,
       "query": "[\"OR\", {\"source\": [\"user\", \"note\"]}, {\"destination\": [\"user\", \"note\"]}]",
       "type": "view",
       "mask": 1,
@@ -172,10 +164,7 @@
     "model": "permission.permission",
     "pk": 6,
     "fields": {
-      "model": [
-        "note",
-        "alias"
-      ],
+      "model": 19,
       "query": "[\"OR\", {\"note__in\": [\"NoteUser\", \"objects\", [\"filter\", {\"user__membership__club__name\": \"Kfet\"}], [\"all\"]]}, {\"note__in\": [\"NoteClub\", \"objects\", [\"all\"]]}]",
       "type": "view",
       "mask": 1,
@@ -187,10 +176,7 @@
     "model": "permission.permission",
     "pk": 7,
     "fields": {
-      "model": [
-        "auth",
-        "user"
-      ],
+      "model": 4,
       "query": "{\"pk\": [\"user\", \"pk\"]}",
       "type": "change",
       "mask": 1,
@@ -202,10 +188,7 @@
     "model": "permission.permission",
     "pk": 8,
     "fields": {
-      "model": [
-        "auth",
-        "user"
-      ],
+      "model": 4,
       "query": "{\"pk\": [\"user\", \"pk\"]}",
       "type": "change",
       "mask": 1,
@@ -217,10 +200,7 @@
     "model": "permission.permission",
     "pk": 9,
     "fields": {
-      "model": [
-        "auth",
-        "user"
-      ],
+      "model": 4,
       "query": "{\"pk\": [\"user\", \"pk\"]}",
       "type": "change",
       "mask": 1,
@@ -232,10 +212,7 @@
     "model": "permission.permission",
     "pk": 10,
     "fields": {
-      "model": [
-        "auth",
-        "user"
-      ],
+      "model": 4,
       "query": "{\"pk\": [\"user\", \"pk\"]}",
       "type": "change",
       "mask": 1,
@@ -247,10 +224,7 @@
     "model": "permission.permission",
     "pk": 11,
     "fields": {
-      "model": [
-        "auth",
-        "user"
-      ],
+      "model": 4,
       "query": "{\"pk\": [\"user\", \"pk\"]}",
       "type": "change",
       "mask": 1,
@@ -262,10 +236,7 @@
     "model": "permission.permission",
     "pk": 12,
     "fields": {
-      "model": [
-        "authtoken",
-        "token"
-      ],
+      "model": 8,
       "query": "{\"user\": [\"user\"]}",
       "type": "delete",
       "mask": 1,
@@ -277,10 +248,7 @@
     "model": "permission.permission",
     "pk": 13,
     "fields": {
-      "model": [
-        "authtoken",
-        "token"
-      ],
+      "model": 8,
       "query": "{\"user\": [\"user\"]}",
       "type": "add",
       "mask": 1,
@@ -292,10 +260,7 @@
     "model": "permission.permission",
     "pk": 14,
     "fields": {
-      "model": [
-        "note",
-        "alias"
-      ],
+      "model": 19,
       "query": "{\"note\": [\"user\", \"note\"]}",
       "type": "delete",
       "mask": 1,
@@ -307,10 +272,7 @@
     "model": "permission.permission",
     "pk": 15,
     "fields": {
-      "model": [
-        "note",
-        "alias"
-      ],
+      "model": 19,
       "query": "{\"note\": [\"user\", \"note\"]}",
       "type": "add",
       "mask": 1,
@@ -322,10 +284,7 @@
     "model": "permission.permission",
     "pk": 16,
     "fields": {
-      "model": [
-        "note",
-        "noteuser"
-      ],
+      "model": 27,
       "query": "{\"pk\": [\"user\", \"note\", \"pk\"]}",
       "type": "change",
       "mask": 1,
@@ -337,10 +296,7 @@
     "model": "permission.permission",
     "pk": 17,
     "fields": {
-      "model": [
-        "note",
-        "transaction"
-      ],
+      "model": 22,
       "query": "[\"AND\", {\"source\": [\"user\", \"note\"]}, [\"OR\", {\"amount__lte\": [\"user\", \"note\", \"balance\"]}, {\"valid\": false}]]",
       "type": "add",
       "mask": 1,
@@ -352,10 +308,7 @@
     "model": "permission.permission",
     "pk": 18,
     "fields": {
-      "model": [
-        "note",
-        "note"
-      ],
+      "model": 20,
       "query": "{}",
       "type": "change",
       "mask": 1,
@@ -367,10 +320,7 @@
     "model": "permission.permission",
     "pk": 19,
     "fields": {
-      "model": [
-        "note",
-        "note"
-      ],
+      "model": 20,
       "query": "[\"OR\", {\"pk\": [\"club\", \"note\", \"pk\"]}, {\"pk__in\": [\"NoteUser\", \"objects\", [\"filter\", {\"user__membership__club\": [\"club\"]}], [\"all\"]]}]",
       "type": "view",
       "mask": 2,
@@ -382,10 +332,7 @@
     "model": "permission.permission",
     "pk": 20,
     "fields": {
-      "model": [
-        "note",
-        "transaction"
-      ],
+      "model": 22,
       "query": "[\"AND\", [\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], [\"OR\", {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}, {\"valid\": false}]]",
       "type": "add",
       "mask": 2,
@@ -397,10 +344,7 @@
     "model": "permission.permission",
     "pk": 21,
     "fields": {
-      "model": [
-        "note",
-        "recurrenttransaction"
-      ],
+      "model": 28,
       "query": "[\"AND\", {\"destination\": [\"club\", \"note\"]}, [\"OR\", {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}, {\"valid\": false}]]",
       "type": "add",
       "mask": 2,
@@ -412,10 +356,7 @@
     "model": "permission.permission",
     "pk": 22,
     "fields": {
-      "model": [
-        "member",
-        "club"
-      ],
+      "model": 15,
       "query": "{\"pk\": [\"club\", \"pk\"]}",
       "type": "view",
       "mask": 1,
@@ -427,10 +368,7 @@
     "model": "permission.permission",
     "pk": 23,
     "fields": {
-      "model": [
-        "note",
-        "transaction"
-      ],
+      "model": 22,
       "query": "{}",
       "type": "change",
       "mask": 1,
@@ -442,10 +380,7 @@
     "model": "permission.permission",
     "pk": 24,
     "fields": {
-      "model": [
-        "note",
-        "transaction"
-      ],
+      "model": 22,
       "query": "{}",
       "type": "view",
       "mask": 2,
@@ -457,10 +392,7 @@
     "model": "permission.permission",
     "pk": 25,
     "fields": {
-      "model": [
-        "note",
-        "notespecial"
-      ],
+      "model": 26,
       "query": "{}",
       "type": "view",
       "mask": 2,
@@ -472,10 +404,7 @@
     "model": "permission.permission",
     "pk": 26,
     "fields": {
-      "model": [
-        "note",
-        "specialtransaction"
-      ],
+      "model": 29,
       "query": "{}",
       "type": "add",
       "mask": 2,
@@ -487,10 +416,7 @@
     "model": "permission.permission",
     "pk": 27,
     "fields": {
-      "model": [
-        "note",
-        "templatecategory"
-      ],
+      "model": 21,
       "query": "{}",
       "type": "view",
       "mask": 2,
@@ -502,10 +428,7 @@
     "model": "permission.permission",
     "pk": 28,
     "fields": {
-      "model": [
-        "note",
-        "templatecategory"
-      ],
+      "model": 21,
       "query": "{}",
       "type": "change",
       "mask": 3,
@@ -517,10 +440,7 @@
     "model": "permission.permission",
     "pk": 29,
     "fields": {
-      "model": [
-        "note",
-        "templatecategory"
-      ],
+      "model": 21,
       "query": "{}",
       "type": "add",
       "mask": 3,
@@ -532,10 +452,7 @@
     "model": "permission.permission",
     "pk": 30,
     "fields": {
-      "model": [
-        "note",
-        "transactiontemplate"
-      ],
+      "model": 23,
       "query": "{}",
       "type": "view",
       "mask": 2,
@@ -547,10 +464,7 @@
     "model": "permission.permission",
     "pk": 31,
     "fields": {
-      "model": [
-        "note",
-        "transactiontemplate"
-      ],
+      "model": 23,
       "query": "{}",
       "type": "add",
       "mask": 3,
@@ -562,10 +476,7 @@
     "model": "permission.permission",
     "pk": 32,
     "fields": {
-      "model": [
-        "note",
-        "transactiontemplate"
-      ],
+      "model": 23,
       "query": "{}",
       "type": "change",
       "mask": 3,
@@ -577,10 +488,7 @@
     "model": "permission.permission",
     "pk": 33,
     "fields": {
-      "model": [
-        "note",
-        "transaction"
-      ],
+      "model": 22,
       "query": "{}",
       "type": "add",
       "mask": 2,
@@ -592,10 +500,7 @@
     "model": "permission.permission",
     "pk": 34,
     "fields": {
-      "model": [
-        "activity",
-        "activity"
-      ],
+      "model": 9,
       "query": "[\"OR\", {\"valid\": true}, {\"creater\": [\"user\"]}]",
       "type": "view",
       "mask": 1,
@@ -607,10 +512,7 @@
     "model": "permission.permission",
     "pk": 35,
     "fields": {
-      "model": [
-        "activity",
-        "activity"
-      ],
+      "model": 9,
       "query": "[\"AND\", {\"valid\": false}, {\"creater\": [\"user\"]}]",
       "type": "change",
       "mask": 1,
@@ -622,10 +524,7 @@
     "model": "permission.permission",
     "pk": 36,
     "fields": {
-      "model": [
-        "activity",
-        "activity"
-      ],
+      "model": 9,
       "query": "{\"creater\": [\"user\"], \"valid\": false}",
       "type": "add",
       "mask": 1,
@@ -637,10 +536,7 @@
     "model": "permission.permission",
     "pk": 37,
     "fields": {
-      "model": [
-        "activity",
-        "activity"
-      ],
+      "model": 9,
       "query": "{}",
       "type": "change",
       "mask": 2,
@@ -652,10 +548,7 @@
     "model": "permission.permission",
     "pk": 38,
     "fields": {
-      "model": [
-        "activity",
-        "activity"
-      ],
+      "model": 9,
       "query": "{}",
       "type": "change",
       "mask": 2,
@@ -667,10 +560,7 @@
     "model": "permission.permission",
     "pk": 39,
     "fields": {
-      "model": [
-        "activity",
-        "guest"
-      ],
+      "model": 12,
       "query": "{\"inviter\": [\"user\", \"note\"], \"activity__activity_type__can_invite\": true}",
       "type": "add",
       "mask": 1,
@@ -682,10 +572,7 @@
     "model": "permission.permission",
     "pk": 40,
     "fields": {
-      "model": [
-        "activity",
-        "guest"
-      ],
+      "model": 12,
       "query": "{\"inviter\": [\"user\", \"note\"]}",
       "type": "view",
       "mask": 1,
@@ -697,10 +584,7 @@
     "model": "permission.permission",
     "pk": 41,
     "fields": {
-      "model": [
-        "activity",
-        "activity"
-      ],
+      "model": 9,
       "query": "{}",
       "type": "view",
       "mask": 2,
@@ -712,10 +596,7 @@
     "model": "permission.permission",
     "pk": 42,
     "fields": {
-      "model": [
-        "activity",
-        "guest"
-      ],
+      "model": 12,
       "query": "{}",
       "type": "view",
       "mask": 2,
@@ -727,10 +608,7 @@
     "model": "permission.permission",
     "pk": 43,
     "fields": {
-      "model": [
-        "activity",
-        "entry"
-      ],
+      "model": 11,
       "query": "{}",
       "type": "add",
       "mask": 2,
@@ -742,10 +620,7 @@
     "model": "permission.permission",
     "pk": 44,
     "fields": {
-      "model": [
-        "activity",
-        "guesttransaction"
-      ],
+      "model": 13,
       "query": "{}",
       "type": "add",
       "mask": 2,
@@ -757,10 +632,7 @@
     "model": "permission.permission",
     "pk": 45,
     "fields": {
-      "model": [
-        "activity",
-        "guesttransaction"
-      ],
+      "model": 13,
       "query": "{}",
       "type": "view",
       "mask": 1,
@@ -772,10 +644,7 @@
     "model": "permission.permission",
     "pk": 46,
     "fields": {
-      "model": [
-        "activity",
-        "guesttransaction"
-      ],
+      "model": 13,
       "query": "{}",
       "type": "change",
       "mask": 2,
@@ -787,10 +656,7 @@
     "model": "permission.permission",
     "pk": 47,
     "fields": {
-      "model": [
-        "member",
-        "club"
-      ],
+      "model": 15,
       "query": "{\"pk\": [\"club\", \"pk\"]}",
       "type": "change",
       "mask": 1,
@@ -802,10 +668,7 @@
     "model": "permission.permission",
     "pk": 48,
     "fields": {
-      "model": [
-        "member",
-        "membership"
-      ],
+      "model": 16,
       "query": "{\"user\": [\"user\"]}",
       "type": "view",
       "mask": 1,
@@ -817,10 +680,7 @@
     "model": "permission.permission",
     "pk": 49,
     "fields": {
-      "model": [
-        "member",
-        "membership"
-      ],
+      "model": 16,
       "query": "{\"club\": [\"club\"]}",
       "type": "view",
       "mask": 1,
@@ -832,10 +692,7 @@
     "model": "permission.permission",
     "pk": 50,
     "fields": {
-      "model": [
-        "member",
-        "membership"
-      ],
+      "model": 16,
       "query": "{\"club\": [\"club\"]}",
       "type": "add",
       "mask": 2,
@@ -843,6 +700,234 @@
       "description": "Add a membership to a club"
     }
   },
+  {
+    "model": "permission.permission",
+    "pk": 51,
+    "fields": {
+      "model": 16,
+      "query": "{\"club\": [\"club\"]}",
+      "type": "change",
+      "mask": 2,
+      "field": "roles",
+      "description": "Update user roles"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 52,
+    "fields": {
+      "model": 17,
+      "query": "{\"user\": [\"user\"]}",
+      "type": "change",
+      "mask": 1,
+      "field": "",
+      "description": "Change own profile"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 53,
+    "fields": {
+      "model": 17,
+      "query": "{}",
+      "type": "change",
+      "mask": 2,
+      "field": "",
+      "description": "Change any profile"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 54,
+    "fields": {
+      "model": 4,
+      "query": "{}",
+      "type": "change",
+      "mask": 2,
+      "field": "",
+      "description": "Change any user"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 55,
+    "fields": {
+      "model": 4,
+      "query": "{}",
+      "type": "add",
+      "mask": 1,
+      "field": "",
+      "description": "Add user"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 56,
+    "fields": {
+      "model": 17,
+      "query": "{\"email_confirmed\": false, \"registration_valid\": false}",
+      "type": "add",
+      "mask": 1,
+      "field": "",
+      "description": "Add profile"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 57,
+    "fields": {
+      "model": 4,
+      "query": "{\"profile__registration_valid\": false}",
+      "type": "delete",
+      "mask": 2,
+      "field": "",
+      "description": "Delete pre-registered user"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 58,
+    "fields": {
+      "model": 17,
+      "query": "{\"registration_valid\": false}",
+      "type": "delete",
+      "mask": 2,
+      "field": "",
+      "description": "Delete pre-registered user profile"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 59,
+    "fields": {
+      "model": 23,
+      "query": "{\"destination\": [\"club\", \"note\"]}",
+      "type": "view",
+      "mask": 2,
+      "field": "",
+      "description": "New club button"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 60,
+    "fields": {
+      "model": 23,
+      "query": "{\"destination\": [\"club\", \"note\"]}",
+      "type": "add",
+      "mask": 2,
+      "field": "",
+      "description": "Create club button"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 61,
+    "fields": {
+      "model": 23,
+      "query": "{\"destination\": [\"club\", \"note\"]}",
+      "type": "change",
+      "mask": 2,
+      "field": "",
+      "description": "Update club button"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 62,
+    "fields": {
+      "model": 22,
+      "query": "[\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}]",
+      "type": "view",
+      "mask": 1,
+      "field": "",
+      "description": "View transactions of a club"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 63,
+    "fields": {
+      "model": 33,
+      "query": "{}",
+      "type": "view",
+      "mask": 3,
+      "field": "",
+      "description": "View invoices"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 64,
+    "fields": {
+      "model": 33,
+      "query": "{}",
+      "type": "add",
+      "mask": 3,
+      "field": "",
+      "description": "Add invoice"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 65,
+    "fields": {
+      "model": 33,
+      "query": "{}",
+      "type": "change",
+      "mask": 3,
+      "field": "",
+      "description": "Change invoice"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 66,
+    "fields": {
+      "model": 34,
+      "query": "{}",
+      "type": "view",
+      "mask": 3,
+      "field": "",
+      "description": "View products"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 67,
+    "fields": {
+      "model": 34,
+      "query": "{}",
+      "type": "add",
+      "mask": 3,
+      "field": "",
+      "description": "Add products"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 68,
+    "fields": {
+      "model": 34,
+      "query": "{}",
+      "type": "change",
+      "mask": 3,
+      "field": "",
+      "description": "Change product"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 69,
+    "fields": {
+      "model": 34,
+      "query": "{}",
+      "type": "delete",
+      "mask": 3,
+      "field": "",
+      "description": "Delete product"
+    }
+  },
   {
     "model": "permission.rolepermissions",
     "pk": 1,
@@ -851,12 +936,18 @@
       "permissions": [
         1,
         2,
+        3,
+        4,
+        5,
         7,
         8,
         9,
         10,
         11,
-        48
+        12,
+        13,
+        48,
+        52
       ]
     }
   },
@@ -866,19 +957,7 @@
     "fields": {
       "role": 2,
       "permissions": [
-        1,
-        2,
-        3,
-        4,
-        5,
         6,
-        7,
-        8,
-        9,
-        10,
-        11,
-        12,
-        13,
         14,
         15,
         16,
@@ -894,70 +973,97 @@
   },
   {
     "model": "permission.rolepermissions",
-    "pk": 3,
+    "pk": 4,
     "fields": {
-      "role": 8,
+      "role": 4,
       "permissions": [
-        19,
-        20,
-        21,
-        22
+        22,
+        47,
+        49
       ]
     }
   },
   {
     "model": "permission.rolepermissions",
-    "pk": 4,
+    "pk": 5,
     "fields": {
-      "role": 4,
+      "role": 5,
       "permissions": [
-        23,
-        24,
-        25,
-        26,
-        27,
-        28,
-        29,
-        30,
-        31,
-        32,
-        33
+        50,
+        51,
+        62
       ]
     }
   },
   {
     "model": "permission.rolepermissions",
-    "pk": 5,
+    "pk": 6,
     "fields": {
-      "role": 9,
+      "role": 6,
       "permissions": [
-        37,
-        38,
-        41,
-        42,
-        43,
-        44,
-        45,
-        46
+        19,
+        21,
+        27,
+        59,
+        60,
+        61,
+        20,
+        62
       ]
     }
   },
   {
     "model": "permission.rolepermissions",
-    "pk": 6,
+    "pk": 7,
     "fields": {
       "role": 7,
       "permissions": [
-        22,
-        47
+        33,
+        24,
+        25,
+        26,
+        27
       ]
     }
   },
   {
     "model": "permission.rolepermissions",
-    "pk": 7,
+    "pk": 8,
     "fields": {
-      "role": 5,
+      "role": 8,
+      "permissions": [
+        32,
+        33,
+        56,
+        58,
+        55,
+        57,
+        53,
+        54,
+        23,
+        24,
+        25,
+        26,
+        27,
+        28,
+        29,
+        30,
+        31,
+        64,
+        65,
+        66,
+        67,
+        68,
+        69,
+        63
+      ]
+    }
+  },
+  {
+    "model": "permission.rolepermissions",
+    "pk": 9,
+    "fields": {
+      "role": 9,
       "permissions": [
         1,
         2,
@@ -978,7 +1084,6 @@
         17,
         18,
         19,
-        20,
         21,
         22,
         23,
@@ -1008,7 +1113,71 @@
         47,
         48,
         49,
-        50
+        50,
+        51,
+        52,
+        53,
+        54,
+        55,
+        56,
+        57,
+        58,
+        59,
+        60,
+        61,
+        20,
+        62,
+        63,
+        64,
+        65,
+        66,
+        67,
+        68,
+        69
+      ]
+    }
+  },
+  {
+    "model": "permission.rolepermissions",
+    "pk": 10,
+    "fields": {
+      "role": 10,
+      "permissions": [
+        23,
+        24,
+        25,
+        26,
+        27,
+        28,
+        29,
+        30,
+        31,
+        32,
+        33,
+        52,
+        53,
+        54,
+        55,
+        56,
+        57,
+        58
+      ]
+    }
+  },
+  {
+    "model": "permission.rolepermissions",
+    "pk": 11,
+    "fields": {
+      "role": 11,
+      "permissions": [
+        37,
+        38,
+        41,
+        42,
+        43,
+        44,
+        45,
+        46
       ]
     }
   }
diff --git a/apps/permission/models.py b/apps/permission/models.py
index 8aaf416c0d69d394bf476bc3a5537a6fb82c3a63..8117438952e109186796732e9e3e2c22f92c0e90 100644
--- a/apps/permission/models.py
+++ b/apps/permission/models.py
@@ -106,6 +106,10 @@ class PermissionMask(models.Model):
     def __str__(self):
         return self.description
 
+    class Meta:
+        verbose_name = _("permission mask")
+        verbose_name_plural = _("permission masks")
+
 
 class Permission(models.Model):
 
@@ -153,6 +157,8 @@ class Permission(models.Model):
 
     class Meta:
         unique_together = ('model', 'query', 'type', 'field')
+        verbose_name = _("permission")
+        verbose_name_plural = _("permissions")
 
     def clean(self):
         self.query = json.dumps(json.loads(self.query))
@@ -293,3 +299,7 @@ class RolePermissions(models.Model):
 
     def __str__(self):
         return str(self.role)
+
+    class Meta:
+        verbose_name = _("role permissions")
+        verbose_name_plural = _("role permissions")
diff --git a/apps/registration/__init__.py b/apps/registration/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..700d9f003c8ed466ea22d3758e75eb74bf2f6fc9
--- /dev/null
+++ b/apps/registration/__init__.py
@@ -0,0 +1,4 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+default_app_config = 'registration.apps.RegistrationConfig'
diff --git a/apps/registration/apps.py b/apps/registration/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..dec89274d4cf962741a0645f0236ac37a859c8ca
--- /dev/null
+++ b/apps/registration/apps.py
@@ -0,0 +1,10 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.apps import AppConfig
+from django.utils.translation import gettext_lazy as _
+
+
+class RegistrationConfig(AppConfig):
+    name = 'registration'
+    verbose_name = _('registration')
diff --git a/apps/registration/forms.py b/apps/registration/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..cba5c2ae118e8980ae6c8f2aedd17d8fb8e58661
--- /dev/null
+++ b/apps/registration/forms.py
@@ -0,0 +1,80 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django import forms
+from django.contrib.auth.forms import UserCreationForm
+from django.contrib.auth.models import User
+from django.utils.translation import gettext_lazy as _
+from note.models import NoteSpecial
+from note_kfet.inputs import AmountInput
+
+
+class SignUpForm(UserCreationForm):
+    """
+    Pre-register users with all information
+    """
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.fields['username'].widget.attrs.pop("autofocus", None)
+        self.fields['first_name'].widget.attrs.update({"autofocus": "autofocus"})
+        self.fields['first_name'].required = True
+        self.fields['last_name'].required = True
+        self.fields['email'].required = True
+        self.fields['email'].help_text = _("This address must be valid.")
+
+    class Meta:
+        model = User
+        fields = ('first_name', 'last_name', 'username', 'email', )
+
+
+class ValidationForm(forms.Form):
+    """
+    Validate the inscription of the new users and pay memberships.
+    """
+    soge = forms.BooleanField(
+        label=_("Inscription paid by Société Générale"),
+        required=False,
+        help_text=_("Check this case is the Société Générale paid the inscription."),
+    )
+
+    credit_type = forms.ModelChoiceField(
+        queryset=NoteSpecial.objects,
+        label=_("Credit type"),
+        empty_label=_("No credit"),
+        required=False,
+    )
+
+    credit_amount = forms.IntegerField(
+        label=_("Credit amount"),
+        required=False,
+        initial=0,
+        widget=AmountInput(),
+    )
+
+    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,
+    )
+
+    join_BDE = forms.BooleanField(
+        label=_("Join BDE Club"),
+        required=False,
+        initial=True,
+    )
+
+    # The user can join the Kfet club at the inscription
+    join_Kfet = forms.BooleanField(
+        label=_("Join Kfet Club"),
+        required=False,
+        initial=True,
+    )
diff --git a/apps/registration/migrations/__init__.py b/apps/registration/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/apps/registration/tables.py b/apps/registration/tables.py
new file mode 100644
index 0000000000000000000000000000000000000000..7068f6ca0646d60cbaa9df09e7271913fc8e0ea8
--- /dev/null
+++ b/apps/registration/tables.py
@@ -0,0 +1,26 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+import django_tables2 as tables
+from django.contrib.auth.models import User
+
+
+class FutureUserTable(tables.Table):
+    """
+    Display the list of pre-registered users
+    """
+    phone_number = tables.Column(accessor='profile.phone_number')
+
+    section = tables.Column(accessor='profile.section')
+
+    class Meta:
+        attrs = {
+            'class': 'table table-condensed table-striped table-hover'
+        }
+        template_name = 'django_tables2/bootstrap4.html'
+        fields = ('last_name', 'first_name', 'username', 'email', )
+        model = User
+        row_attrs = {
+            'class': 'table-row',
+            'data-href': lambda record: record.pk
+        }
diff --git a/apps/registration/tokens.py b/apps/registration/tokens.py
new file mode 100644
index 0000000000000000000000000000000000000000..c5ddc82bdb78b0fe60d1fd0c2a3a517de1157e9e
--- /dev/null
+++ b/apps/registration/tokens.py
@@ -0,0 +1,30 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+# Copied from https://gitlab.crans.org/bombar/codeflix/-/blob/master/codeflix/codeflix/tokens.py
+
+from django.contrib.auth.tokens import PasswordResetTokenGenerator
+
+
+class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
+    """
+    Create a unique token generator to confirm email addresses.
+    """
+    def _make_hash_value(self, user, timestamp):
+        """
+        Hash the user's primary key and some user state that's sure to change
+        after an account validation to produce a token that invalidated when
+        it's used:
+        1. The user.profile.email_confirmed field will change upon an account
+        validation.
+        2. The last_login field will usually be updated very shortly after
+           an account validation.
+        Failing those things, settings.PASSWORD_RESET_TIMEOUT_DAYS eventually
+        invalidates the token.
+        """
+        # Truncate microseconds so that tokens are consistent even if the
+        # database doesn't support microseconds.
+        login_timestamp = '' if user.last_login is None else user.last_login.replace(microsecond=0, tzinfo=None)
+        return str(user.pk) + str(user.profile.email_confirmed) + str(login_timestamp) + str(timestamp)
+
+
+email_validation_token = AccountActivationTokenGenerator()
diff --git a/apps/registration/urls.py b/apps/registration/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..14678cbb521c3db57929f40a11178c90fd6a559f
--- /dev/null
+++ b/apps/registration/urls.py
@@ -0,0 +1,18 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.urls import path
+
+from . import views
+
+app_name = 'registration'
+urlpatterns = [
+    path('signup/', views.UserCreateView.as_view(), name="signup"),
+    path('validate_email/sent/', views.UserValidationEmailSentView.as_view(), name='email_validation_sent'),
+    path('validate_email/resend/<int:pk>/', views.UserResendValidationEmailView.as_view(),
+         name='email_validation_resend'),
+    path('validate_email/<uidb64>/<token>/', views.UserValidateView.as_view(), name='email_validation'),
+    path('validate_user/', views.FutureUserListView.as_view(), name="future_user_list"),
+    path('validate_user/<int:pk>/', views.FutureUserDetailView.as_view(), name="future_user_detail"),
+    path('validate_user/<int:pk>/invalidate/', views.FutureUserInvalidateView.as_view(), name="future_user_invalidate"),
+]
diff --git a/apps/registration/views.py b/apps/registration/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..35391b05364871fddafbe673922b3b0ca85b844d
--- /dev/null
+++ b/apps/registration/views.py
@@ -0,0 +1,358 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.conf import settings
+from django.contrib.auth.mixins import LoginRequiredMixin
+from django.contrib.auth.models import User
+from django.core.exceptions import ValidationError
+from django.db.models import Q
+from django.shortcuts import resolve_url, redirect
+from django.urls import reverse_lazy
+from django.utils.http import urlsafe_base64_decode
+from django.utils.translation import gettext_lazy as _
+from django.views import View
+from django.views.generic import CreateView, TemplateView, DetailView, FormView
+from django.views.generic.edit import FormMixin
+from django_tables2 import SingleTableView
+from member.forms import ProfileForm
+from member.models import Membership, Club, Role
+from note.models import SpecialTransaction, NoteSpecial
+from note.templatetags.pretty_money import pretty_money
+from permission.backends import PermissionBackend
+from permission.views import ProtectQuerysetMixin
+
+from .forms import SignUpForm, ValidationForm
+from .tables import FutureUserTable
+from .tokens import email_validation_token
+
+
+class UserCreateView(CreateView):
+    """
+    Une vue pour inscrire un utilisateur et lui créer un profil
+    """
+
+    form_class = SignUpForm
+    success_url = reverse_lazy('registration:email_validation_sent')
+    template_name = 'registration/signup.html'
+    second_form = ProfileForm
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context["profile_form"] = self.second_form()
+
+        return context
+
+    def form_valid(self, form):
+        """
+        If the form is valid, then the user is created with is_active set to False
+        so that the user cannot log in until the email has been validated.
+        The user must also wait that someone validate her/his account.
+        """
+        profile_form = ProfileForm(data=self.request.POST)
+        if not profile_form.is_valid():
+            return self.form_invalid(form)
+
+        # Save the user and the profile
+        user = form.save(commit=False)
+        user.is_active = False
+        profile_form.instance.user = user
+        profile = profile_form.save(commit=False)
+        user.profile = profile
+        user.save()
+        user.refresh_from_db()
+        profile.user = user
+        profile.save()
+
+        user.profile.send_email_validation_link()
+
+        return super().form_valid(form)
+
+
+class UserValidateView(TemplateView):
+    """
+    A view to validate the email address.
+    """
+    title = _("Email validation")
+    template_name = 'registration/email_validation_complete.html'
+
+    def get(self, *args, **kwargs):
+        """
+        With a given token and user id (in params), validate the email address.
+        """
+        assert 'uidb64' in kwargs and 'token' in kwargs
+
+        self.validlink = False
+        user = self.get_user(kwargs['uidb64'])
+        token = kwargs['token']
+
+        # Validate the token
+        if user is not None and email_validation_token.check_token(user, token):
+            self.validlink = True
+            # The user must wait that someone validates the account before the user can be active and login.
+            user.is_active = user.profile.registration_valid or user.is_superuser
+            user.profile.email_confirmed = True
+            user.save()
+            user.profile.save()
+            return super().dispatch(*args, **kwargs)
+        else:
+            # Display the "Email validation unsuccessful" page.
+            return self.render_to_response(self.get_context_data())
+
+    def get_user(self, uidb64):
+        """
+        Get user from the base64-encoded string.
+        """
+        try:
+            # urlsafe_base64_decode() decodes to bytestring
+            uid = urlsafe_base64_decode(uidb64).decode()
+            user = User.objects.get(pk=uid)
+        except (TypeError, ValueError, OverflowError, User.DoesNotExist, ValidationError):
+            user = None
+        return user
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context['user'] = self.get_user(self.kwargs["uidb64"])
+        context['login_url'] = resolve_url(settings.LOGIN_URL)
+        if self.validlink:
+            context['validlink'] = True
+        else:
+            context.update({
+                'title': _('Email validation unsuccessful'),
+                'validlink': False,
+            })
+        return context
+
+
+class UserValidationEmailSentView(TemplateView):
+    """
+    Display the information that the validation link has been sent.
+    """
+    template_name = 'registration/email_validation_email_sent.html'
+    title = _('Email validation email sent')
+
+
+class UserResendValidationEmailView(LoginRequiredMixin, ProtectQuerysetMixin, DetailView):
+    """
+    Rensend the email validation link.
+    """
+    model = User
+
+    def get(self, request, *args, **kwargs):
+        user = self.get_object()
+
+        user.profile.send_email_validation_link()
+
+        url = 'member:user_detail' if user.profile.registration_valid else 'registration:future_user_detail'
+        return redirect(url, user.id)
+
+
+class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
+    """
+    Display pre-registered users, with a search bar
+    """
+    model = User
+    table_class = FutureUserTable
+    template_name = 'registration/future_user_list.html'
+
+    def get_queryset(self, **kwargs):
+        """
+        Filter the table with the given parameter.
+        :param kwargs:
+        :return:
+        """
+        qs = super().get_queryset().filter(profile__registration_valid=False)
+        if "search" in self.request.GET:
+            pattern = self.request.GET["search"]
+
+            if not pattern:
+                return qs.none()
+
+            qs = qs.filter(
+                Q(first_name__iregex=pattern)
+                | Q(last_name__iregex=pattern)
+                | Q(profile__section__iregex=pattern)
+                | Q(username__iregex="^" + pattern)
+            )
+        else:
+            qs = qs.none()
+
+        return qs[:20]
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+
+        context["title"] = _("Unregistered users")
+
+        return context
+
+
+class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, DetailView):
+    """
+    Display information about a pre-registered user, in order to complete the registration.
+    """
+    model = User
+    form_class = ValidationForm
+    context_object_name = "user_object"
+    template_name = "registration/future_profile_detail.html"
+
+    def post(self, request, *args, **kwargs):
+        form = self.get_form()
+        self.object = self.get_object()
+        if form.is_valid():
+            return self.form_valid(form)
+        else:
+            return self.form_invalid(form)
+
+    def get_queryset(self, **kwargs):
+        """
+        We only display information of a not registered user.
+        """
+        return super().get_queryset().filter(profile__registration_valid=False)
+
+    def get_context_data(self, **kwargs):
+        ctx = super().get_context_data(**kwargs)
+
+        user = self.get_object()
+        fee = 0
+        bde = Club.objects.get(name="BDE")
+        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
+        ctx["total_fee"] = "{:.02f}".format(fee / 100, )
+
+        return ctx
+
+    def get_form(self, form_class=None):
+        form = super().get_form(form_class)
+        user = self.get_object()
+        form.fields["last_name"].initial = user.last_name
+        form.fields["first_name"].initial = user.first_name
+        return form
+
+    def form_valid(self, form):
+        user = self.get_object()
+
+        # Get form data
+        soge = form.cleaned_data["soge"]
+        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"]
+        join_BDE = form.cleaned_data["join_BDE"]
+        join_Kfet = form.cleaned_data["join_Kfet"]
+
+        if soge:
+            # If Société Générale pays the inscription, the user joins the two clubs
+            join_BDE = True
+            join_Kfet = True
+
+        if not join_BDE:
+            form.add_error('join_BDE', _("You must join the BDE."))
+            return super().form_invalid(form)
+
+        fee = 0
+        bde = Club.objects.get(name="BDE")
+        bde_fee = bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid
+        if join_BDE:
+            fee += bde_fee
+        kfet = Club.objects.get(name="Kfet")
+        kfet_fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
+        if join_Kfet:
+            fee += kfet_fee
+
+        if soge:
+            # Fill payment information if Société Générale pays the inscription
+            credit_type = NoteSpecial.objects.get(special_type="Virement bancaire")
+            credit_amount = fee
+            bank = "Société générale"
+
+        print("OK")
+
+        if join_Kfet and not join_BDE:
+            form.add_error('join_Kfet', _("You must join BDE club before joining Kfet club."))
+
+        if fee > credit_amount:
+            # Check if the user credits enough money
+            form.add_error('credit_type',
+                           _("The entered amount is not enough for the memberships, should be at least {}")
+                           .format(pretty_money(fee)))
+            return self.form_invalid(form)
+
+        if credit_type is not None and credit_amount > 0:
+            if not last_name or not first_name or (not bank and credit_type.special_type == "Chèque"):
+                if not last_name:
+                    form.add_error('last_name', _("This field is required."))
+                if not first_name:
+                    form.add_error('first_name', _("This field is required."))
+                if not bank and credit_type.special_type == "Chèque":
+                    form.add_error('bank', _("This field is required."))
+                return self.form_invalid(form)
+
+        # Save the user and finally validate the registration
+        # Saving the user creates the associated note
+        ret = super().form_valid(form)
+        user.is_active = user.profile.email_confirmed or user.is_superuser
+        user.profile.registration_valid = True
+        # Store if Société générale paid for next years
+        user.profile.soge = soge
+        user.save()
+        user.profile.save()
+
+        if credit_type is not None and credit_amount > 0:
+            # Credit the note
+            SpecialTransaction.objects.create(
+                source=credit_type,
+                destination=user.note,
+                quantity=1,
+                amount=credit_amount,
+                reason="Crédit " + ("Société générale" if soge else credit_type.special_type) + " (Inscription)",
+                last_name=last_name,
+                first_name=first_name,
+                bank=bank,
+                valid=True,
+            )
+
+        if join_BDE:
+            # Create membership for the user to the BDE starting today
+            membership = Membership.objects.create(
+                club=bde,
+                user=user,
+                fee=bde_fee,
+            )
+            membership.roles.add(Role.objects.get(name="Adhérent BDE"))
+            membership.save()
+
+        if join_Kfet:
+            # Create membership for the user to the Kfet starting today
+            membership = Membership.objects.create(
+                club=kfet,
+                user=user,
+                fee=kfet_fee,
+            )
+            membership.roles.add(Role.objects.get(name="Adhérent Kfet"))
+            membership.save()
+
+        return ret
+
+    def get_success_url(self):
+        return reverse_lazy('member:user_detail', args=(self.get_object().pk, ))
+
+
+class FutureUserInvalidateView(ProtectQuerysetMixin, LoginRequiredMixin, View):
+    """
+    Delete a pre-registered user.
+    """
+
+    def get(self, request, *args, **kwargs):
+        """
+        Delete the pre-registered user which id is given in the URL.
+        """
+        user = User.objects.filter(profile__registration_valid=False)\
+            .filter(PermissionBackend.filter_queryset(request.user, User, "change", "is_valid"))\
+            .get(pk=self.kwargs["pk"])
+
+        user.delete()
+
+        return redirect('registration:future_user_list')
diff --git a/apps/treasury/forms.py b/apps/treasury/forms.py
index ad479e143ed33ba07275261ecf86a0c2c09fc8b9..b09a46c750badb975f455d71fa0216b5311a4fd1 100644
--- a/apps/treasury/forms.py
+++ b/apps/treasury/forms.py
@@ -53,7 +53,7 @@ ProductFormSet = forms.inlineformset_factory(
 
 class ProductFormSetHelper(FormHelper):
     """
-    Specify some template informations for the product form.
+    Specify some template information for the product form.
     """
 
     def __init__(self, form=None):
diff --git a/apps/treasury/models.py b/apps/treasury/models.py
index bcd89db964dbcdf9da9cdee7e30be23fd4c597eb..ca1da3a40927a531fa99e23e8235533026208f82 100644
--- a/apps/treasury/models.py
+++ b/apps/treasury/models.py
@@ -59,6 +59,10 @@ class Invoice(models.Model):
         verbose_name=_("Acquitted"),
     )
 
+    class Meta:
+        verbose_name = _("invoice")
+        verbose_name_plural = _("invoices")
+
 
 class Product(models.Model):
     """
@@ -95,6 +99,10 @@ class Product(models.Model):
     def total_euros(self):
         return self.total / 100
 
+    class Meta:
+        verbose_name = _("product")
+        verbose_name_plural = _("products")
+
 
 class RemittanceType(models.Model):
     """
@@ -109,6 +117,10 @@ class RemittanceType(models.Model):
     def __str__(self):
         return str(self.note)
 
+    class Meta:
+        verbose_name = _("remittance type")
+        verbose_name_plural = _("remittance types")
+
 
 class Remittance(models.Model):
     """
@@ -136,6 +148,10 @@ class Remittance(models.Model):
         verbose_name=_("Closed"),
     )
 
+    class Meta:
+        verbose_name = _("remittance")
+        verbose_name_plural = _("remittances")
+
     @property
     def transactions(self):
         """
@@ -187,3 +203,7 @@ class SpecialTransactionProxy(models.Model):
         null=True,
         verbose_name=_("Remittance"),
     )
+
+    class Meta:
+        verbose_name = _("special transaction proxy")
+        verbose_name_plural = _("special transaction proxies")
diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po
index 16c73f358acba2b9afdb2ad6ef07d99bc6f03aca..f616ffd6d71f7c6e154c7c1dd43f356e1b7a5b55 100644
--- a/locale/de/LC_MESSAGES/django.po
+++ b/locale/de/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-04-01 18:39+0200\n"
+"POT-Creation-Date: 2020-04-09 21:59+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"
@@ -18,36 +18,37 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: apps/activity/apps.py:10 apps/activity/models.py:111
-#: apps/activity/models.py:120
+#: apps/activity/apps.py:10 apps/activity/models.py:102
+#: apps/activity/models.py:111
 msgid "activity"
 msgstr ""
 
-#: apps/activity/forms.py:45 apps/activity/models.py:217
+#: apps/activity/forms.py:45 apps/activity/models.py:208
 msgid "You can't invite someone once the activity is started."
 msgstr ""
 
-#: apps/activity/forms.py:48 apps/activity/models.py:220
+#: apps/activity/forms.py:48 apps/activity/models.py:211
 msgid "This activity is not validated yet."
 msgstr ""
 
-#: apps/activity/forms.py:58 apps/activity/models.py:228
+#: apps/activity/forms.py:58 apps/activity/models.py:219
 msgid "This person has been already invited 5 times this year."
 msgstr ""
 
-#: apps/activity/forms.py:62 apps/activity/models.py:232
+#: apps/activity/forms.py:62 apps/activity/models.py:223
 msgid "This person is already invited."
 msgstr ""
 
-#: apps/activity/forms.py:66 apps/activity/models.py:236
+#: apps/activity/forms.py:66 apps/activity/models.py:227
 msgid "You can't invite more than 3 people to this activity."
 msgstr ""
 
 #: apps/activity/models.py:23 apps/activity/models.py:48
-#: apps/member/models.py:66 apps/member/models.py:169
+#: apps/member/models.py:99 apps/member/models.py:202
 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
-#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:232
+#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:237
 #: templates/member/club_info.html:13 templates/member/profile_info.html:14
+#: templates/registration/future_profile_detail.html:16
 msgid "name"
 msgstr ""
 
@@ -67,18 +68,18 @@ msgstr ""
 msgid "activity types"
 msgstr ""
 
-#: apps/activity/models.py:53 apps/note/models/transactions.py:69
+#: apps/activity/models.py:53 apps/note/models/transactions.py:74
 #: apps/permission/models.py:103 templates/activity/activity_detail.html:16
 msgid "description"
 msgstr ""
 
 #: apps/activity/models.py:60 apps/note/models/notes.py:164
-#: apps/note/models/transactions.py:62
+#: apps/note/models/transactions.py:64
 #: templates/activity/activity_detail.html:19
 msgid "type"
 msgstr ""
 
-#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:190
+#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:223
 #: apps/note/models/notes.py:117
 msgid "user"
 msgstr ""
@@ -87,73 +88,82 @@ msgstr ""
 msgid "organizer"
 msgstr ""
 
-#: apps/activity/models.py:82 apps/activity/models.py:131 apps/note/apps.py:14
-#: apps/note/models/notes.py:58
-msgid "note"
-msgstr ""
-
-#: apps/activity/models.py:89 templates/activity/activity_detail.html:36
+#: apps/activity/models.py:80 templates/activity/activity_detail.html:36
 msgid "attendees club"
 msgstr ""
 
-#: apps/activity/models.py:93 templates/activity/activity_detail.html:22
+#: apps/activity/models.py:84 templates/activity/activity_detail.html:22
 msgid "start date"
 msgstr ""
 
-#: apps/activity/models.py:97 templates/activity/activity_detail.html:25
+#: apps/activity/models.py:88 templates/activity/activity_detail.html:25
 msgid "end date"
 msgstr ""
 
-#: apps/activity/models.py:102 apps/note/models/transactions.py:134
+#: apps/activity/models.py:93 apps/note/models/transactions.py:139
 #: templates/activity/activity_detail.html:47
 msgid "valid"
 msgstr ""
 
-#: apps/activity/models.py:107 templates/activity/activity_detail.html:61
+#: apps/activity/models.py:98 templates/activity/activity_detail.html:61
 msgid "open"
 msgstr ""
 
-#: apps/activity/models.py:112
+#: apps/activity/models.py:103
 msgid "activities"
 msgstr ""
 
-#: apps/activity/models.py:125
+#: apps/activity/models.py:116
 msgid "entry time"
 msgstr ""
 
-#: apps/activity/models.py:148
+#: apps/activity/models.py:122 apps/note/apps.py:14
+#: apps/note/models/notes.py:58
+msgid "note"
+msgstr ""
+
+#: apps/activity/models.py:133 templates/activity/activity_entry.html:38
+msgid "entry"
+msgstr ""
+
+#: apps/activity/models.py:134 templates/activity/activity_entry.html:38
+msgid "entries"
+msgstr ""
+
+#: apps/activity/models.py:141
 msgid "Already entered on "
 msgstr ""
 
-#: apps/activity/models.py:148 apps/activity/tables.py:54
+#: apps/activity/models.py:141 apps/activity/tables.py:54
 msgid "{:%Y-%m-%d %H:%M:%S}"
 msgstr ""
 
-#: apps/activity/models.py:156
+#: apps/activity/models.py:149
 msgid "The balance is negative."
 msgstr ""
 
-#: apps/activity/models.py:188
+#: apps/activity/models.py:179
 msgid "last name"
 msgstr ""
 
-#: apps/activity/models.py:193 templates/member/profile_info.html:14
+#: apps/activity/models.py:184 templates/member/profile_info.html:14
+#: templates/registration/future_profile_detail.html:16
 msgid "first name"
 msgstr ""
 
-#: apps/activity/models.py:200
+#: apps/activity/models.py:191
 msgid "inviter"
 msgstr ""
 
-#: apps/activity/models.py:241
+#: apps/activity/models.py:232
 msgid "guest"
 msgstr ""
 
-#: apps/activity/models.py:242
+#: apps/activity/models.py:233
 msgid "guests"
 msgstr ""
 
-#: apps/activity/models.py:254
+#: apps/activity/models.py:245
 msgid "Invitation"
 msgstr ""
 
@@ -165,16 +175,18 @@ msgstr ""
 msgid "remove"
 msgstr ""
 
-#: apps/activity/tables.py:75 apps/treasury/models.py:126
+#: apps/activity/tables.py:75 apps/treasury/models.py:138
 msgid "Type"
 msgstr ""
 
-#: apps/activity/tables.py:77 apps/treasury/forms.py:121
+#: apps/activity/tables.py:77 apps/member/forms.py:75
+#: apps/registration/forms.py:55 apps/treasury/forms.py:121
 msgid "Last name"
 msgstr ""
 
-#: apps/activity/tables.py:79 apps/treasury/forms.py:123
-#: templates/note/transaction_form.html:92
+#: apps/activity/tables.py:79 apps/member/forms.py:80
+#: apps/registration/forms.py:60 apps/treasury/forms.py:123
+#: templates/note/transaction_form.html:97
 msgid "First name"
 msgstr ""
 
@@ -182,15 +194,15 @@ msgstr ""
 msgid "Note"
 msgstr ""
 
-#: apps/activity/tables.py:83
+#: apps/activity/tables.py:83 apps/member/tables.py:41
 msgid "Balance"
 msgstr ""
 
-#: apps/activity/views.py:45 templates/base.html:94
+#: apps/activity/views.py:45 templates/base.html:106
 msgid "Activities"
 msgstr ""
 
-#: apps/activity/views.py:153
+#: apps/activity/views.py:154
 msgid "Entry for activity \"{}\""
 msgstr ""
 
@@ -226,12 +238,12 @@ msgstr ""
 msgid "create"
 msgstr ""
 
-#: apps/logs/models.py:61 apps/note/tables.py:142
+#: apps/logs/models.py:61 apps/note/tables.py:144
 #: templates/activity/activity_detail.html:67
 msgid "edit"
 msgstr ""
 
-#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:146
+#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:149
 msgid "delete"
 msgstr ""
 
@@ -247,170 +259,243 @@ msgstr ""
 msgid "Logs cannot be destroyed."
 msgstr ""
 
+#: apps/logs/models.py:80
+msgid "changelog"
+msgstr ""
+
+#: apps/logs/models.py:81
+msgid "changelogs"
+msgstr ""
+
 #: apps/member/apps.py:14
 msgid "member"
 msgstr ""
 
-#: apps/member/models.py:28
+#: apps/member/forms.py:54 apps/registration/forms.py:35
+msgid "Inscription paid by Société Générale"
+msgstr ""
+
+#: apps/member/forms.py:56 apps/registration/forms.py:37
+msgid "Check this case is the Société Générale paid the inscription."
+msgstr ""
+
+#: apps/member/forms.py:61 apps/registration/forms.py:42
+msgid "Credit type"
+msgstr ""
+
+#: apps/member/forms.py:62 apps/registration/forms.py:43
+msgid "No credit"
+msgstr ""
+
+#: apps/member/forms.py:64
+msgid "You can credit the note of the user."
+msgstr ""
+
+#: apps/member/forms.py:68 apps/registration/forms.py:48
+msgid "Credit amount"
+msgstr ""
+
+#: apps/member/forms.py:85 apps/registration/forms.py:65
+#: apps/treasury/forms.py:125 templates/note/transaction_form.html:103
+msgid "Bank"
+msgstr ""
+
+#: apps/member/models.py:33
+#: templates/registration/future_profile_detail.html:47
 msgid "phone number"
 msgstr ""
 
-#: apps/member/models.py:34 templates/member/profile_info.html:27
+#: apps/member/models.py:39 templates/member/profile_info.html:27
+#: templates/registration/future_profile_detail.html:41
 msgid "section"
 msgstr ""
 
-#: apps/member/models.py:35
+#: apps/member/models.py:40
 msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
 msgstr ""
 
-#: apps/member/models.py:41 templates/member/profile_info.html:30
+#: apps/member/models.py:46 templates/member/profile_info.html:30
+#: templates/registration/future_profile_detail.html:44
 msgid "address"
 msgstr ""
 
-#: apps/member/models.py:47
+#: apps/member/models.py:52
+#: templates/registration/future_profile_detail.html:50
 msgid "paid"
 msgstr ""
 
-#: apps/member/models.py:52 apps/member/models.py:53
+#: apps/member/models.py:53
+msgid "Tells if the user receive a salary."
+msgstr ""
+
+#: apps/member/models.py:58
+msgid "email confirmed"
+msgstr ""
+
+#: apps/member/models.py:63
+msgid "registration valid"
+msgstr ""
+
+#: apps/member/models.py:68
+msgid "Société générale"
+msgstr ""
+
+#: apps/member/models.py:69
+msgid "Has the user ever be paid by the Société générale?"
+msgstr ""
+
+#: apps/member/models.py:74 apps/member/models.py:75
 msgid "user profile"
 msgstr ""
 
-#: apps/member/models.py:71 templates/member/club_info.html:46
+#: apps/member/models.py:104 templates/member/club_info.html:46
+#: templates/registration/future_profile_detail.html:22
 msgid "email"
 msgstr ""
 
-#: apps/member/models.py:78
+#: apps/member/models.py:111
 msgid "parent club"
 msgstr ""
 
-#: apps/member/models.py:87
+#: apps/member/models.py:120
 msgid "require memberships"
 msgstr ""
 
-#: apps/member/models.py:88
+#: apps/member/models.py:121
 msgid "Uncheck if this club don't require memberships."
 msgstr ""
 
-#: apps/member/models.py:93 templates/member/club_info.html:35
+#: apps/member/models.py:126 templates/member/club_info.html:35
 msgid "membership fee (paid students)"
 msgstr ""
 
-#: apps/member/models.py:98 templates/member/club_info.html:38
+#: apps/member/models.py:131 templates/member/club_info.html:38
 msgid "membership fee (unpaid students)"
 msgstr ""
 
-#: apps/member/models.py:104 templates/member/club_info.html:28
+#: apps/member/models.py:137 templates/member/club_info.html:28
 msgid "membership duration"
 msgstr ""
 
-#: apps/member/models.py:105
+#: apps/member/models.py:138
 msgid "The longest time (in days) a membership can last (NULL = infinite)."
 msgstr ""
 
-#: apps/member/models.py:112 templates/member/club_info.html:22
+#: apps/member/models.py:145 templates/member/club_info.html:22
 msgid "membership start"
 msgstr ""
 
-#: apps/member/models.py:113
+#: apps/member/models.py:146
 msgid "How long after January 1st the members can renew their membership."
 msgstr ""
 
-#: apps/member/models.py:120 templates/member/club_info.html:25
+#: apps/member/models.py:153 templates/member/club_info.html:25
 msgid "membership end"
 msgstr ""
 
-#: apps/member/models.py:121
+#: apps/member/models.py:154
 msgid ""
 "How long the membership can last after January 1st of the next year after "
 "members can renew their membership."
 msgstr ""
 
-#: apps/member/models.py:154 apps/member/models.py:196
+#: apps/member/models.py:187 apps/member/models.py:229
 #: apps/note/models/notes.py:139
 msgid "club"
 msgstr ""
 
-#: apps/member/models.py:155
+#: apps/member/models.py:188
 msgid "clubs"
 msgstr ""
 
-#: apps/member/models.py:175 apps/permission/models.py:288
+#: apps/member/models.py:208 apps/permission/models.py:294
 msgid "role"
 msgstr ""
 
-#: apps/member/models.py:176 apps/member/models.py:201
+#: apps/member/models.py:209 apps/member/models.py:234
 msgid "roles"
 msgstr ""
 
-#: apps/member/models.py:205
+#: apps/member/models.py:239
 msgid "membership starts on"
 msgstr ""
 
-#: apps/member/models.py:209
+#: apps/member/models.py:243
 msgid "membership ends on"
 msgstr ""
 
-#: apps/member/models.py:214
+#: apps/member/models.py:248
 msgid "fee"
 msgstr ""
 
-#: apps/member/models.py:226 apps/member/views.py:383
+#: apps/member/models.py:266 apps/member/views.py:500
 msgid "User is not a member of the parent club"
 msgstr ""
 
-#: apps/member/models.py:236 apps/member/views.py:392
+#: apps/member/models.py:276 apps/member/views.py:509
 msgid "User is already a member of the club"
 msgstr ""
 
-#: apps/member/models.py:271
+#: apps/member/models.py:314
 #, python-brace-format
 msgid "Membership of {user} for the club {club}"
 msgstr ""
 
-#: apps/member/models.py:274
+#: apps/member/models.py:317
 msgid "membership"
 msgstr ""
 
-#: apps/member/models.py:275
+#: apps/member/models.py:318
 msgid "memberships"
 msgstr ""
 
-#: apps/member/tables.py:73
+#: apps/member/tables.py:112
 msgid "Renew"
 msgstr ""
 
-#: apps/member/views.py:80 templates/member/profile_info.html:45
+#: apps/member/views.py:62 apps/registration/forms.py:23
+msgid "This address must be valid."
+msgstr ""
+
+#: apps/member/views.py:65 templates/member/profile_info.html:45
+#: templates/registration/future_profile_detail.html:55
 msgid "Update Profile"
 msgstr ""
 
-#: apps/member/views.py:93
+#: apps/member/views.py:75
 msgid "An alias with a similar name already exists."
 msgstr ""
 
-#: apps/member/views.py:379
+#: apps/member/views.py:180
+msgid "Search user"
+msgstr ""
+
+#: apps/member/views.py:495
 msgid ""
 "This user don't have enough money to join this club, and can't have a "
 "negative balance."
 msgstr ""
 
-#: apps/member/views.py:396 apps/member/views.py:428
+#: apps/member/views.py:513
 msgid "The membership must start after {:%m-%d-%Y}."
 msgstr ""
 
-#: apps/member/views.py:401 apps/member/views.py:433
+#: apps/member/views.py:518
 msgid "The membership must begin before {:%m-%d-%Y}."
 msgstr ""
 
-#: apps/member/views.py:455
-msgid "This membership is already renewed"
+#: apps/member/views.py:528 apps/member/views.py:530 apps/member/views.py:532
+#: apps/registration/views.py:286 apps/registration/views.py:288
+#: apps/registration/views.py:290
+msgid "This field is required."
 msgstr ""
 
-#: apps/note/admin.py:120 apps/note/models/transactions.py:94
+#: apps/note/admin.py:120 apps/note/models/transactions.py:99
 msgid "source"
 msgstr ""
 
-#: apps/note/admin.py:128 apps/note/admin.py:163
-#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:107
+#: apps/note/admin.py:128 apps/note/admin.py:170
+#: apps/note/models/transactions.py:54 apps/note/models/transactions.py:112
 msgid "destination"
 msgstr ""
 
@@ -452,7 +537,7 @@ msgstr ""
 msgid "display image"
 msgstr ""
 
-#: apps/note/models/notes.py:53 apps/note/models/transactions.py:117
+#: apps/note/models/notes.py:53 apps/note/models/transactions.py:122
 msgid "created at"
 msgstr ""
 
@@ -535,78 +620,85 @@ msgstr ""
 msgid "A template with this name already exist"
 msgstr ""
 
-#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:125
+#: apps/note/models/transactions.py:58 apps/note/models/transactions.py:130
 msgid "amount"
 msgstr ""
 
-#: apps/note/models/transactions.py:57
+#: apps/note/models/transactions.py:59
 msgid "in centimes"
 msgstr ""
 
-#: apps/note/models/transactions.py:75
+#: apps/note/models/transactions.py:70
+msgid "display"
+msgstr ""
+
+#: apps/note/models/transactions.py:80
 msgid "transaction template"
 msgstr ""
 
-#: apps/note/models/transactions.py:76
+#: apps/note/models/transactions.py:81
 msgid "transaction templates"
 msgstr ""
 
-#: apps/note/models/transactions.py:100 apps/note/models/transactions.py:113
+#: apps/note/models/transactions.py:105 apps/note/models/transactions.py:118
 #: apps/note/tables.py:33 apps/note/tables.py:42
 msgid "used alias"
 msgstr ""
 
-#: apps/note/models/transactions.py:121
+#: apps/note/models/transactions.py:126
 msgid "quantity"
 msgstr ""
 
-#: apps/note/models/transactions.py:129
+#: apps/note/models/transactions.py:134
 msgid "reason"
 msgstr ""
 
-#: apps/note/models/transactions.py:139 apps/note/tables.py:95
+#: apps/note/models/transactions.py:144 apps/note/tables.py:95
 msgid "invalidity reason"
 msgstr ""
 
-#: apps/note/models/transactions.py:147
+#: apps/note/models/transactions.py:152
 msgid "transaction"
 msgstr ""
 
-#: apps/note/models/transactions.py:148
+#: apps/note/models/transactions.py:153
 msgid "transactions"
 msgstr ""
 
-#: apps/note/models/transactions.py:202 templates/base.html:84
+#: apps/note/models/transactions.py:207
+#: templates/activity/activity_entry.html:13 templates/base.html:84
 #: templates/note/transaction_form.html:19
-#: templates/note/transaction_form.html:140
+#: templates/note/transaction_form.html:145
 msgid "Transfer"
 msgstr ""
 
-#: apps/note/models/transactions.py:222
+#: apps/note/models/transactions.py:227
 msgid "Template"
 msgstr ""
 
-#: apps/note/models/transactions.py:237
+#: apps/note/models/transactions.py:242
 msgid "first_name"
 msgstr ""
 
-#: apps/note/models/transactions.py:242
+#: apps/note/models/transactions.py:247
 msgid "bank"
 msgstr ""
 
-#: apps/note/models/transactions.py:248 templates/note/transaction_form.html:24
+#: apps/note/models/transactions.py:253
+#: templates/activity/activity_entry.html:17
+#: templates/note/transaction_form.html:24
 msgid "Credit"
 msgstr ""
 
-#: apps/note/models/transactions.py:248 templates/note/transaction_form.html:28
+#: apps/note/models/transactions.py:253 templates/note/transaction_form.html:28
 msgid "Debit"
 msgstr ""
 
-#: apps/note/models/transactions.py:264 apps/note/models/transactions.py:269
+#: apps/note/models/transactions.py:269 apps/note/models/transactions.py:274
 msgid "membership transaction"
 msgstr ""
 
-#: apps/note/models/transactions.py:265
+#: apps/note/models/transactions.py:270
 msgid "membership transactions"
 msgstr ""
 
@@ -622,20 +714,29 @@ msgstr ""
 msgid "No reason specified"
 msgstr ""
 
-#: apps/note/views.py:39
+#: apps/note/tables.py:122 apps/note/tables.py:151
+msgid "Delete"
+msgstr ""
+
+#: apps/note/tables.py:146 templates/member/club_info.html:55
+#: templates/note/conso_form.html:121
+msgid "Edit"
+msgstr ""
+
+#: apps/note/views.py:40
 msgid "Transfer money"
 msgstr ""
 
-#: apps/note/views.py:100 templates/base.html:79
+#: apps/note/views.py:109 templates/base.html:79
 msgid "Consumptions"
 msgstr ""
 
-#: apps/permission/models.py:82 apps/permission/models.py:275
+#: apps/permission/models.py:82 apps/permission/models.py:281
 #, python-brace-format
 msgid "Can {type} {model}.{field} in {query}"
 msgstr ""
 
-#: apps/permission/models.py:84 apps/permission/models.py:277
+#: apps/permission/models.py:84 apps/permission/models.py:283
 #, python-brace-format
 msgid "Can {type} {model} in {query}"
 msgstr ""
@@ -644,11 +745,72 @@ msgstr ""
 msgid "rank"
 msgstr ""
 
+#: apps/permission/models.py:110
+msgid "permission mask"
+msgstr ""
+
+#: apps/permission/models.py:111
+msgid "permission masks"
+msgstr ""
+
 #: apps/permission/models.py:160
+msgid "permission"
+msgstr ""
+
+#: apps/permission/models.py:161
+msgid "permissions"
+msgstr ""
+
+#: apps/permission/models.py:166
 msgid "Specifying field applies only to view and change permission types."
 msgstr ""
 
-#: apps/treasury/apps.py:12 templates/base.html:99
+#: apps/permission/models.py:304 apps/permission/models.py:305
+msgid "role permissions"
+msgstr ""
+
+#: apps/registration/apps.py:10
+msgid "registration"
+msgstr ""
+
+#: apps/registration/forms.py:70
+msgid "Join BDE Club"
+msgstr ""
+
+#: apps/registration/forms.py:77
+msgid "Join Kfet Club"
+msgstr ""
+
+#: apps/registration/views.py:75
+msgid "Email validation"
+msgstr ""
+
+#: apps/registration/views.py:121
+msgid "Email validation unsuccessful"
+msgstr ""
+
+#: apps/registration/views.py:132
+msgid "Email validation email sent"
+msgstr ""
+
+#: apps/registration/views.py:185
+msgid "Unregistered users"
+msgstr ""
+
+#: apps/registration/views.py:252
+msgid "You must join the BDE."
+msgstr ""
+
+#: apps/registration/views.py:274
+msgid "You must join BDE club before joining Kfet club."
+msgstr ""
+
+#: apps/registration/views.py:279
+msgid ""
+"The entered amount is not enough for the memberships, should be at least {}"
+msgstr ""
+
+#: apps/treasury/apps.py:12 templates/base.html:111
 msgid "Treasury"
 msgstr ""
 
@@ -673,12 +835,8 @@ msgstr ""
 msgid "You can't change the type of the remittance."
 msgstr ""
 
-#: apps/treasury/forms.py:125 templates/note/transaction_form.html:98
-msgid "Bank"
-msgstr ""
-
 #: apps/treasury/forms.py:127 apps/treasury/tables.py:47
-#: templates/note/transaction_form.html:128
+#: templates/note/transaction_form.html:133
 #: templates/treasury/remittance_form.html:18
 msgid "Amount"
 msgstr ""
@@ -699,7 +857,7 @@ msgstr ""
 msgid "Description"
 msgstr ""
 
-#: apps/treasury/models.py:46 templates/note/transaction_form.html:86
+#: apps/treasury/models.py:46 templates/note/transaction_form.html:91
 msgid "Name"
 msgstr ""
 
@@ -715,40 +873,80 @@ msgstr ""
 msgid "Acquitted"
 msgstr ""
 
-#: apps/treasury/models.py:75
-msgid "Designation"
+#: apps/treasury/models.py:63
+msgid "invoice"
+msgstr ""
+
+#: apps/treasury/models.py:64
+msgid "invoices"
 msgstr ""
 
 #: apps/treasury/models.py:79
-msgid "Quantity"
+msgid "Designation"
 msgstr ""
 
 #: apps/treasury/models.py:83
+msgid "Quantity"
+msgstr ""
+
+#: apps/treasury/models.py:87
 msgid "Unit price"
 msgstr ""
 
-#: apps/treasury/models.py:120
+#: apps/treasury/models.py:103
+msgid "product"
+msgstr ""
+
+#: apps/treasury/models.py:104
+msgid "products"
+msgstr ""
+
+#: apps/treasury/models.py:121
+msgid "remittance type"
+msgstr ""
+
+#: apps/treasury/models.py:122
+msgid "remittance types"
+msgstr ""
+
+#: apps/treasury/models.py:132
 msgid "Date"
 msgstr ""
 
-#: apps/treasury/models.py:131
+#: apps/treasury/models.py:143
 msgid "Comment"
 msgstr ""
 
-#: apps/treasury/models.py:136
+#: apps/treasury/models.py:148
 msgid "Closed"
 msgstr ""
 
-#: apps/treasury/models.py:169
+#: apps/treasury/models.py:152
+msgid "remittance"
+msgstr ""
+
+#: apps/treasury/models.py:153
+msgid "remittances"
+msgstr ""
+
+#: apps/treasury/models.py:185
 msgid "Remittance #{:d}: {}"
 msgstr ""
 
-#: apps/treasury/models.py:188 apps/treasury/tables.py:76
+#: apps/treasury/models.py:204 apps/treasury/tables.py:76
 #: apps/treasury/tables.py:84 templates/treasury/invoice_list.html:13
 #: templates/treasury/remittance_list.html:13
 msgid "Remittance"
 msgstr ""
 
+#: apps/treasury/models.py:208
+msgid "special transaction proxy"
+msgstr ""
+
+#: apps/treasury/models.py:209
+msgid "special transaction proxies"
+msgstr ""
+
 #: apps/treasury/tables.py:19
 msgid "Invoice #{:d}"
 msgstr ""
@@ -781,15 +979,15 @@ msgid ""
 "again unless your session expires or you logout."
 msgstr ""
 
-#: note_kfet/settings/base.py:152
+#: note_kfet/settings/base.py:153
 msgid "German"
 msgstr ""
 
-#: note_kfet/settings/base.py:153
+#: note_kfet/settings/base.py:154
 msgid "English"
 msgstr ""
 
-#: note_kfet/settings/base.py:154
+#: note_kfet/settings/base.py:155
 msgid "French"
 msgstr ""
 
@@ -825,16 +1023,13 @@ msgstr ""
 msgid "Guests list"
 msgstr ""
 
-#: templates/activity/activity_entry.html:10
-msgid "Return to activity page"
-msgstr ""
-
-#: templates/activity/activity_entry.html:18
-msgid "entries"
+#: templates/activity/activity_entry.html:22
+#: templates/note/transaction_form.html:33
+msgid "Entries"
 msgstr ""
 
-#: templates/activity/activity_entry.html:18
-msgid "entry"
+#: templates/activity/activity_entry.html:30
+msgid "Return to activity page"
 msgstr ""
 
 #: templates/activity/activity_list.html:5
@@ -858,9 +1053,23 @@ msgid "The ENS Paris-Saclay BDE note."
 msgstr ""
 
 #: templates/base.html:89
+msgid "Users"
+msgstr ""
+
+#: templates/base.html:94
 msgid "Clubs"
 msgstr ""
 
+#: templates/base.html:100
+msgid "Registrations"
+msgstr ""
+
+#: templates/base.html:150
+msgid ""
+"Your e-mail address is not validated. Please check your mail inbox and click "
+"on the validation link."
+msgstr ""
+
 #: templates/cas_server/base.html:7
 msgid "Central Authentication Service"
 msgstr ""
@@ -936,10 +1145,6 @@ msgstr ""
 msgid "Add member"
 msgstr ""
 
-#: templates/member/club_info.html:55 templates/note/conso_form.html:121
-msgid "Edit"
-msgstr ""
-
 #: templates/member/club_info.html:59 templates/member/profile_info.html:48
 msgid "View Profile"
 msgstr ""
@@ -956,11 +1161,11 @@ msgstr ""
 msgid "club listing "
 msgstr ""
 
-#: templates/member/club_tables.html:9
+#: templates/member/club_tables.html:6
 msgid "Member of the Club"
 msgstr ""
 
-#: templates/member/club_tables.html:22 templates/member/profile_tables.html:22
+#: templates/member/club_tables.html:17 templates/member/profile_tables.html:28
 msgid "Transaction history"
 msgstr ""
 
@@ -977,18 +1182,22 @@ msgid "Regenerate token"
 msgstr ""
 
 #: templates/member/profile_info.html:5
+#: templates/registration/future_profile_detail.html:12
 msgid "Account #"
 msgstr ""
 
 #: templates/member/profile_info.html:17
+#: templates/registration/future_profile_detail.html:19
 msgid "username"
 msgstr ""
 
 #: templates/member/profile_info.html:20
+#: templates/registration/future_profile_detail.html:34
 msgid "password"
 msgstr ""
 
 #: templates/member/profile_info.html:23
+#: templates/registration/future_profile_detail.html:37
 msgid "Change password"
 msgstr ""
 
@@ -1000,7 +1209,17 @@ msgstr ""
 msgid "Manage auth token"
 msgstr ""
 
-#: templates/member/profile_tables.html:9
+#: templates/member/profile_tables.html:7
+#: templates/registration/future_profile_detail.html:28
+msgid "This user doesn't have confirmed his/her e-mail address."
+msgstr ""
+
+#: templates/member/profile_tables.html:8
+#: templates/registration/future_profile_detail.html:29
+msgid "Click here to resend a validation link."
+msgstr ""
+
+#: templates/member/profile_tables.html:15
 msgid "View my memberships"
 msgstr ""
 
@@ -1008,12 +1227,12 @@ msgstr ""
 msgid "Save Changes"
 msgstr ""
 
-#: templates/member/signup.html:5 templates/member/signup.html:8
-#: templates/member/signup.html:14
-msgid "Sign up"
+#: templates/member/user_list.html:14
+#: templates/registration/future_user_list.html:17
+msgid "There is no pending user with this pattern."
 msgstr ""
 
-#: templates/note/conso_form.html:28 templates/note/transaction_form.html:50
+#: templates/note/conso_form.html:28 templates/note/transaction_form.html:55
 msgid "Select emitters"
 msgstr ""
 
@@ -1037,7 +1256,7 @@ msgstr ""
 msgid "Double consumptions"
 msgstr ""
 
-#: templates/note/conso_form.html:141 templates/note/transaction_form.html:147
+#: templates/note/conso_form.html:141 templates/note/transaction_form.html:152
 msgid "Recent transactions history"
 msgstr ""
 
@@ -1045,29 +1264,29 @@ msgstr ""
 msgid "Gift"
 msgstr ""
 
-#: templates/note/transaction_form.html:68
+#: templates/note/transaction_form.html:73
 msgid "External payment"
 msgstr ""
 
-#: templates/note/transaction_form.html:76
+#: templates/note/transaction_form.html:81
 msgid "Transfer type"
 msgstr ""
 
-#: templates/note/transaction_form.html:111
-#: templates/note/transaction_form.html:164
-#: templates/note/transaction_form.html:171
+#: templates/note/transaction_form.html:116
+#: templates/note/transaction_form.html:169
+#: templates/note/transaction_form.html:176
 msgid "Select receivers"
 msgstr ""
 
-#: templates/note/transaction_form.html:133
+#: templates/note/transaction_form.html:138
 msgid "Reason"
 msgstr ""
 
-#: templates/note/transaction_form.html:178
+#: templates/note/transaction_form.html:183
 msgid "Credit note"
 msgstr ""
 
-#: templates/note/transaction_form.html:185
+#: templates/note/transaction_form.html:190
 msgid "Debit note"
 msgstr ""
 
@@ -1087,14 +1306,50 @@ msgstr ""
 msgid "buttons listing "
 msgstr ""
 
-#: templates/note/transactiontemplate_list.html:71
+#: templates/note/transactiontemplate_list.html:70
 msgid "button successfully deleted "
 msgstr ""
 
-#: templates/note/transactiontemplate_list.html:75
+#: templates/note/transactiontemplate_list.html:74
 msgid "Unable to delete button "
 msgstr ""
 
+#: templates/registration/email_validation_complete.html:6
+msgid "Your email have successfully been validated."
+msgstr ""
+
+#: templates/registration/email_validation_complete.html:8
+#, python-format
+msgid "You can now <a href=\"%(login_url)s\">log in</a>."
+msgstr ""
+
+#: templates/registration/email_validation_complete.html:10
+msgid ""
+"You must pay now your membership in the Kfet to complete your registration."
+msgstr ""
+
+#: templates/registration/email_validation_complete.html:13
+msgid ""
+"The link was invalid. The token may have expired. Please send us an email to "
+"activate your account."
+msgstr ""
+
+#: templates/registration/future_profile_detail.html:56
+msgid "Delete registration"
+msgstr ""
+
+#: templates/registration/future_profile_detail.html:64
+msgid "Validate account"
+msgstr ""
+
+#: templates/registration/future_profile_detail.html:71
+msgid "Validate registration"
+msgstr ""
+
+#: templates/registration/future_user_list.html:7
+msgid "New user"
+msgstr ""
+
 #: templates/registration/logged_out.html:8
 msgid "Thanks for spending some quality time with the Web site today."
 msgstr ""
@@ -1128,6 +1383,36 @@ msgstr ""
 msgid "Forgotten your password or username?"
 msgstr ""
 
+#: templates/registration/mails/email_validation_email.html:3
+msgid "Hi"
+msgstr ""
+
+#: templates/registration/mails/email_validation_email.html:5
+msgid ""
+"You recently registered on the Note Kfet. Please click on the link below to "
+"confirm your registration."
+msgstr ""
+
+#: templates/registration/mails/email_validation_email.html:9
+msgid ""
+"This link is only valid for a couple of days, after that you will need to "
+"contact us to validate your email."
+msgstr ""
+
+#: templates/registration/mails/email_validation_email.html:11
+msgid ""
+"After that, you'll have to wait that someone validates your account before "
+"you can log in. You will need to pay your membership in the Kfet."
+msgstr ""
+
+#: templates/registration/mails/email_validation_email.html:13
+msgid "Thanks"
+msgstr ""
+
+#: templates/registration/mails/email_validation_email.html:15
+msgid "The Note Kfet team."
+msgstr ""
+
 #: templates/registration/password_change_done.html:8
 msgid "Your password was changed."
 msgstr ""
@@ -1181,6 +1466,11 @@ msgstr ""
 msgid "Reset my password"
 msgstr ""
 
+#: templates/registration/signup.html:5 templates/registration/signup.html:8
+#: templates/registration/signup.html:14
+msgid "Sign up"
+msgstr ""
+
 #: templates/treasury/invoice_form.html:6
 msgid "Invoices list"
 msgstr ""
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index 9754fa9c4e2aa3dc91b5cb21af50d286e2036c62..27636a65c5aee6787b817e86e8bbfbfc9af1a1dd 100644
--- a/locale/fr/LC_MESSAGES/django.po
+++ b/locale/fr/LC_MESSAGES/django.po
@@ -3,7 +3,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-04-01 18:39+0200\n"
+"POT-Creation-Date: 2020-04-09 21:59+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"
@@ -13,37 +13,38 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 
-#: apps/activity/apps.py:10 apps/activity/models.py:111
-#: apps/activity/models.py:120
+#: apps/activity/apps.py:10 apps/activity/models.py:102
+#: apps/activity/models.py:111
 msgid "activity"
 msgstr "activité"
 
-#: apps/activity/forms.py:45 apps/activity/models.py:217
+#: apps/activity/forms.py:45 apps/activity/models.py:208
 msgid "You can't invite someone once the activity is started."
 msgstr ""
 "Vous ne pouvez pas inviter quelqu'un une fois que l'activité a démarré."
 
-#: apps/activity/forms.py:48 apps/activity/models.py:220
+#: apps/activity/forms.py:48 apps/activity/models.py:211
 msgid "This activity is not validated yet."
 msgstr "Cette activité n'est pas encore validée."
 
-#: apps/activity/forms.py:58 apps/activity/models.py:228
+#: apps/activity/forms.py:58 apps/activity/models.py:219
 msgid "This person has been already invited 5 times this year."
 msgstr "Cette personne a déjà été invitée 5 fois cette année."
 
-#: apps/activity/forms.py:62 apps/activity/models.py:232
+#: apps/activity/forms.py:62 apps/activity/models.py:223
 msgid "This person is already invited."
 msgstr "Cette personne est déjà invitée."
 
-#: apps/activity/forms.py:66 apps/activity/models.py:236
+#: apps/activity/forms.py:66 apps/activity/models.py:227
 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:23 apps/activity/models.py:48
-#: apps/member/models.py:66 apps/member/models.py:169
+#: apps/member/models.py:99 apps/member/models.py:202
 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
-#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:232
+#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:237
 #: templates/member/club_info.html:13 templates/member/profile_info.html:14
+#: templates/registration/future_profile_detail.html:16
 msgid "name"
 msgstr "nom"
 
@@ -63,18 +64,18 @@ msgstr "type d'activité"
 msgid "activity types"
 msgstr "types d'activité"
 
-#: apps/activity/models.py:53 apps/note/models/transactions.py:69
+#: apps/activity/models.py:53 apps/note/models/transactions.py:74
 #: apps/permission/models.py:103 templates/activity/activity_detail.html:16
 msgid "description"
 msgstr "description"
 
 #: apps/activity/models.py:60 apps/note/models/notes.py:164
-#: apps/note/models/transactions.py:62
+#: apps/note/models/transactions.py:64
 #: templates/activity/activity_detail.html:19
 msgid "type"
 msgstr "type"
 
-#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:190
+#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:223
 #: apps/note/models/notes.py:117
 msgid "user"
 msgstr "utilisateur"
@@ -83,73 +84,82 @@ msgstr "utilisateur"
 msgid "organizer"
 msgstr "organisateur"
 
-#: apps/activity/models.py:82 apps/activity/models.py:131 apps/note/apps.py:14
-#: apps/note/models/notes.py:58
-msgid "note"
-msgstr "note"
-
-#: apps/activity/models.py:89 templates/activity/activity_detail.html:36
+#: apps/activity/models.py:80 templates/activity/activity_detail.html:36
 msgid "attendees club"
 msgstr "club attendu"
 
-#: apps/activity/models.py:93 templates/activity/activity_detail.html:22
+#: apps/activity/models.py:84 templates/activity/activity_detail.html:22
 msgid "start date"
 msgstr "date de début"
 
-#: apps/activity/models.py:97 templates/activity/activity_detail.html:25
+#: apps/activity/models.py:88 templates/activity/activity_detail.html:25
 msgid "end date"
 msgstr "date de fin"
 
-#: apps/activity/models.py:102 apps/note/models/transactions.py:134
+#: apps/activity/models.py:93 apps/note/models/transactions.py:139
 #: templates/activity/activity_detail.html:47
 msgid "valid"
 msgstr "valide"
 
-#: apps/activity/models.py:107 templates/activity/activity_detail.html:61
+#: apps/activity/models.py:98 templates/activity/activity_detail.html:61
 msgid "open"
 msgstr "ouvrir"
 
-#: apps/activity/models.py:112
+#: apps/activity/models.py:103
 msgid "activities"
 msgstr "activités"
 
-#: apps/activity/models.py:125
+#: apps/activity/models.py:116
 msgid "entry time"
 msgstr "heure d'entrée"
 
-#: apps/activity/models.py:148
+#: apps/activity/models.py:122 apps/note/apps.py:14
+#: apps/note/models/notes.py:58
+msgid "note"
+msgstr "note"
+
+#: apps/activity/models.py:133 templates/activity/activity_entry.html:38
+msgid "entry"
+msgstr "entrée"
+
+#: apps/activity/models.py:134 templates/activity/activity_entry.html:38
+msgid "entries"
+msgstr "entrées"
+
+#: apps/activity/models.py:141
 msgid "Already entered on "
 msgstr "Déjà rentré le "
 
-#: apps/activity/models.py:148 apps/activity/tables.py:54
+#: apps/activity/models.py:141 apps/activity/tables.py:54
 msgid "{:%Y-%m-%d %H:%M:%S}"
 msgstr "{:%d/%m/%Y %H:%M:%S}"
 
-#: apps/activity/models.py:156
+#: apps/activity/models.py:149
 msgid "The balance is negative."
 msgstr "La note est en négatif."
 
-#: apps/activity/models.py:188
+#: apps/activity/models.py:179
 msgid "last name"
 msgstr "nom de famille"
 
-#: apps/activity/models.py:193 templates/member/profile_info.html:14
+#: apps/activity/models.py:184 templates/member/profile_info.html:14
+#: templates/registration/future_profile_detail.html:16
 msgid "first name"
 msgstr "prénom"
 
-#: apps/activity/models.py:200
+#: apps/activity/models.py:191
 msgid "inviter"
 msgstr "hôte"
 
-#: apps/activity/models.py:241
+#: apps/activity/models.py:232
 msgid "guest"
 msgstr "invité"
 
-#: apps/activity/models.py:242
+#: apps/activity/models.py:233
 msgid "guests"
 msgstr "invités"
 
-#: apps/activity/models.py:254
+#: apps/activity/models.py:245
 msgid "Invitation"
 msgstr "Invitation"
 
@@ -161,16 +171,18 @@ msgstr "Entré le "
 msgid "remove"
 msgstr "supprimer"
 
-#: apps/activity/tables.py:75 apps/treasury/models.py:126
+#: apps/activity/tables.py:75 apps/treasury/models.py:138
 msgid "Type"
 msgstr "Type"
 
-#: apps/activity/tables.py:77 apps/treasury/forms.py:121
+#: apps/activity/tables.py:77 apps/member/forms.py:75
+#: apps/registration/forms.py:55 apps/treasury/forms.py:121
 msgid "Last name"
 msgstr "Nom de famille"
 
-#: apps/activity/tables.py:79 apps/treasury/forms.py:123
-#: templates/note/transaction_form.html:92
+#: apps/activity/tables.py:79 apps/member/forms.py:80
+#: apps/registration/forms.py:60 apps/treasury/forms.py:123
+#: templates/note/transaction_form.html:97
 msgid "First name"
 msgstr "Prénom"
 
@@ -178,15 +190,15 @@ msgstr "Prénom"
 msgid "Note"
 msgstr "Note"
 
-#: apps/activity/tables.py:83
+#: apps/activity/tables.py:83 apps/member/tables.py:41
 msgid "Balance"
 msgstr "Solde du compte"
 
-#: apps/activity/views.py:45 templates/base.html:94
+#: apps/activity/views.py:45 templates/base.html:106
 msgid "Activities"
 msgstr "Activités"
 
-#: apps/activity/views.py:153
+#: apps/activity/views.py:154
 msgid "Entry for activity \"{}\""
 msgstr "Entrées pour l'activité « {} »"
 
@@ -222,12 +234,12 @@ msgstr "Nouvelles données"
 msgid "create"
 msgstr "Créer"
 
-#: apps/logs/models.py:61 apps/note/tables.py:142
+#: apps/logs/models.py:61 apps/note/tables.py:144
 #: templates/activity/activity_detail.html:67
 msgid "edit"
 msgstr "Modifier"
 
-#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:146
+#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:149
 msgid "delete"
 msgstr "Supprimer"
 
@@ -243,81 +255,143 @@ msgstr "Date"
 msgid "Logs cannot be destroyed."
 msgstr "Les logs ne peuvent pas être détruits."
 
+#: apps/logs/models.py:80
+msgid "changelog"
+msgstr ""
+
+#: apps/logs/models.py:81
+msgid "changelogs"
+msgstr ""
+
 #: apps/member/apps.py:14
 msgid "member"
 msgstr "adhérent"
 
-#: apps/member/models.py:28
+#: apps/member/forms.py:54 apps/registration/forms.py:35
+msgid "Inscription paid by Société Générale"
+msgstr "Inscription payée par la Société générale"
+
+#: apps/member/forms.py:56 apps/registration/forms.py:37
+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:61 apps/registration/forms.py:42
+msgid "Credit type"
+msgstr "Type de rechargement"
+
+#: apps/member/forms.py:62 apps/registration/forms.py:43
+msgid "No credit"
+msgstr "Pas de rechargement"
+
+#: apps/member/forms.py:64
+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:68 apps/registration/forms.py:48
+msgid "Credit amount"
+msgstr "Montant à créditer"
+
+#: apps/member/forms.py:85 apps/registration/forms.py:65
+#: apps/treasury/forms.py:125 templates/note/transaction_form.html:103
+msgid "Bank"
+msgstr "Banque"
+
+#: apps/member/models.py:33
+#: templates/registration/future_profile_detail.html:47
 msgid "phone number"
 msgstr "numéro de téléphone"
 
-#: apps/member/models.py:34 templates/member/profile_info.html:27
+#: apps/member/models.py:39 templates/member/profile_info.html:27
+#: templates/registration/future_profile_detail.html:41
 msgid "section"
 msgstr "section"
 
-#: apps/member/models.py:35
+#: apps/member/models.py:40
 msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
 msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
 
-#: apps/member/models.py:41 templates/member/profile_info.html:30
+#: apps/member/models.py:46 templates/member/profile_info.html:30
+#: templates/registration/future_profile_detail.html:44
 msgid "address"
 msgstr "adresse"
 
-#: apps/member/models.py:47
+#: apps/member/models.py:52
+#: templates/registration/future_profile_detail.html:50
 msgid "paid"
 msgstr "payé"
 
-#: apps/member/models.py:52 apps/member/models.py:53
+#: apps/member/models.py:53
+msgid "Tells if the user receive a salary."
+msgstr "Indique si l'utilisateur perçoit un salaire."
+
+#: apps/member/models.py:58
+msgid "email confirmed"
+msgstr "adresse email confirmée"
+
+#: apps/member/models.py:63
+msgid "registration valid"
+msgstr "inscription valid"
+
+#: apps/member/models.py:68
+msgid "Société générale"
+msgstr "Société générale"
+
+#: apps/member/models.py:69
+msgid "Has the user ever be paid by the Société générale?"
+msgstr "Est-ce que l'utilisateur a déjà été payé par la Société Générale ?"
+
+#: apps/member/models.py:74 apps/member/models.py:75
 msgid "user profile"
 msgstr "profil utilisateur"
 
-#: apps/member/models.py:71 templates/member/club_info.html:46
+#: apps/member/models.py:104 templates/member/club_info.html:46
+#: templates/registration/future_profile_detail.html:22
 msgid "email"
 msgstr "courriel"
 
-#: apps/member/models.py:78
+#: apps/member/models.py:111
 msgid "parent club"
 msgstr "club parent"
 
-#: apps/member/models.py:87
+#: apps/member/models.py:120
 msgid "require memberships"
 msgstr "nécessite des adhésions"
 
-#: apps/member/models.py:88
+#: apps/member/models.py:121
 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:93 templates/member/club_info.html:35
+#: apps/member/models.py:126 templates/member/club_info.html:35
 msgid "membership fee (paid students)"
 msgstr "cotisation pour adhérer (normalien élève)"
 
-#: apps/member/models.py:98 templates/member/club_info.html:38
+#: apps/member/models.py:131 templates/member/club_info.html:38
 msgid "membership fee (unpaid students)"
 msgstr "cotisation pour adhérer (normalien étudiant)"
 
-#: apps/member/models.py:104 templates/member/club_info.html:28
+#: apps/member/models.py:137 templates/member/club_info.html:28
 msgid "membership duration"
 msgstr "durée de l'adhésion"
 
-#: apps/member/models.py:105
+#: apps/member/models.py:138
 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:112 templates/member/club_info.html:22
+#: apps/member/models.py:145 templates/member/club_info.html:22
 msgid "membership start"
 msgstr "début de l'adhésion"
 
-#: apps/member/models.py:113
+#: apps/member/models.py:146
 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:120 templates/member/club_info.html:25
+#: apps/member/models.py:153 templates/member/club_info.html:25
 msgid "membership end"
 msgstr "fin de l'adhésion"
 
-#: apps/member/models.py:121
+#: apps/member/models.py:154
 msgid ""
 "How long the membership can last after January 1st of the next year after "
 "members can renew their membership."
@@ -325,92 +399,105 @@ 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:154 apps/member/models.py:196
+#: apps/member/models.py:187 apps/member/models.py:229
 #: apps/note/models/notes.py:139
 msgid "club"
 msgstr "club"
 
-#: apps/member/models.py:155
+#: apps/member/models.py:188
 msgid "clubs"
 msgstr "clubs"
 
-#: apps/member/models.py:175 apps/permission/models.py:288
+#: apps/member/models.py:208 apps/permission/models.py:294
 msgid "role"
 msgstr "rôle"
 
-#: apps/member/models.py:176 apps/member/models.py:201
+#: apps/member/models.py:209 apps/member/models.py:234
 msgid "roles"
 msgstr "rôles"
 
-#: apps/member/models.py:205
+#: apps/member/models.py:239
 msgid "membership starts on"
 msgstr "l'adhésion commence le"
 
-#: apps/member/models.py:209
+#: apps/member/models.py:243
 msgid "membership ends on"
 msgstr "l'adhésion finit le"
 
-#: apps/member/models.py:214
+#: apps/member/models.py:248
 msgid "fee"
 msgstr "cotisation"
 
-#: apps/member/models.py:226 apps/member/views.py:383
+#: apps/member/models.py:266 apps/member/views.py:500
 msgid "User is not a member of the parent club"
 msgstr "L'utilisateur n'est pas membre du club parent"
 
-#: apps/member/models.py:236 apps/member/views.py:392
+#: apps/member/models.py:276 apps/member/views.py:509
 msgid "User is already a member of the club"
 msgstr "L'utilisateur est déjà membre du club"
 
-#: apps/member/models.py:271
+#: apps/member/models.py:314
 #, python-brace-format
 msgid "Membership of {user} for the club {club}"
 msgstr "Adhésion de {user} pour le club {club}"
 
-#: apps/member/models.py:274
+#: apps/member/models.py:317
 msgid "membership"
 msgstr "adhésion"
 
-#: apps/member/models.py:275
+#: apps/member/models.py:318
 msgid "memberships"
 msgstr "adhésions"
 
-#: apps/member/tables.py:73
+#: apps/member/tables.py:112
 msgid "Renew"
-msgstr ""
+msgstr "Renouveler"
+
+#: apps/member/views.py:62 apps/registration/forms.py:23
+msgid "This address must be valid."
+msgstr "Cette adresse doit être valide."
 
-#: apps/member/views.py:80 templates/member/profile_info.html:45
+#: apps/member/views.py:65 templates/member/profile_info.html:45
+#: templates/registration/future_profile_detail.html:55
 msgid "Update Profile"
 msgstr "Modifier le profil"
 
-#: apps/member/views.py:93
+#: apps/member/views.py:75
 msgid "An alias with a similar name already exists."
 msgstr "Un alias avec un nom similaire existe déjà."
 
-#: apps/member/views.py:379
+#: apps/member/views.py:180
+msgid "Search user"
+msgstr "Chercher un utilisateur"
+
+#: apps/member/views.py:495
 msgid ""
 "This user don't have enough money to join this club, and can't have a "
 "negative balance."
 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:396 apps/member/views.py:428
+#: apps/member/views.py:513
 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:401 apps/member/views.py:433
+#: apps/member/views.py:518
 msgid "The membership must begin before {:%m-%d-%Y}."
 msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}."
 
-#: apps/member/views.py:455
-msgid "This membership is already renewed"
-msgstr "Cette adhésion est déjà renouvelée"
+#: apps/member/views.py:528 apps/member/views.py:530 apps/member/views.py:532
+#: apps/registration/views.py:286 apps/registration/views.py:288
+#: apps/registration/views.py:290
+msgid "This field is required."
+msgstr "Ce champ est requis."
 
-#: apps/note/admin.py:120 apps/note/models/transactions.py:94
+#: apps/note/admin.py:120 apps/note/models/transactions.py:99
 msgid "source"
 msgstr "source"
 
-#: apps/note/admin.py:128 apps/note/admin.py:163
-#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:107
+#: apps/note/admin.py:128 apps/note/admin.py:170
+#: apps/note/models/transactions.py:54 apps/note/models/transactions.py:112
 msgid "destination"
 msgstr "destination"
 
@@ -453,7 +540,7 @@ msgstr ""
 msgid "display image"
 msgstr "image affichée"
 
-#: apps/note/models/notes.py:53 apps/note/models/transactions.py:117
+#: apps/note/models/notes.py:53 apps/note/models/transactions.py:122
 msgid "created at"
 msgstr "créée le"
 
@@ -536,80 +623,87 @@ msgstr "catégories de transaction"
 msgid "A template with this name already exist"
 msgstr "Un modèle de transaction avec un nom similaire existe déjà."
 
-#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:125
+#: apps/note/models/transactions.py:58 apps/note/models/transactions.py:130
 msgid "amount"
 msgstr "montant"
 
-#: apps/note/models/transactions.py:57
+#: apps/note/models/transactions.py:59
 msgid "in centimes"
 msgstr "en centimes"
 
-#: apps/note/models/transactions.py:75
+#: apps/note/models/transactions.py:70
+msgid "display"
+msgstr "afficher"
+
+#: apps/note/models/transactions.py:80
 msgid "transaction template"
 msgstr "modèle de transaction"
 
-#: apps/note/models/transactions.py:76
+#: apps/note/models/transactions.py:81
 msgid "transaction templates"
 msgstr "modèles de transaction"
 
-#: apps/note/models/transactions.py:100 apps/note/models/transactions.py:113
+#: apps/note/models/transactions.py:105 apps/note/models/transactions.py:118
 #: apps/note/tables.py:33 apps/note/tables.py:42
 msgid "used alias"
 msgstr "alias utilisé"
 
-#: apps/note/models/transactions.py:121
+#: apps/note/models/transactions.py:126
 msgid "quantity"
 msgstr "quantité"
 
-#: apps/note/models/transactions.py:129
+#: apps/note/models/transactions.py:134
 msgid "reason"
 msgstr "raison"
 
-#: apps/note/models/transactions.py:139 apps/note/tables.py:95
+#: apps/note/models/transactions.py:144 apps/note/tables.py:95
 msgid "invalidity reason"
 msgstr "Motif d'invalidité"
 
-#: apps/note/models/transactions.py:147
+#: apps/note/models/transactions.py:152
 msgid "transaction"
 msgstr "transaction"
 
-#: apps/note/models/transactions.py:148
+#: apps/note/models/transactions.py:153
 msgid "transactions"
 msgstr "transactions"
 
-#: apps/note/models/transactions.py:202 templates/base.html:84
+#: apps/note/models/transactions.py:207
+#: templates/activity/activity_entry.html:13 templates/base.html:84
 #: templates/note/transaction_form.html:19
-#: templates/note/transaction_form.html:140
+#: templates/note/transaction_form.html:145
 msgid "Transfer"
 msgstr "Virement"
 
-#: apps/note/models/transactions.py:222
+#: apps/note/models/transactions.py:227
 msgid "Template"
 msgstr "Bouton"
 
-#: apps/note/models/transactions.py:237
+#: apps/note/models/transactions.py:242
 msgid "first_name"
 msgstr "prénom"
 
-#: apps/note/models/transactions.py:242
+#: apps/note/models/transactions.py:247
 msgid "bank"
 msgstr "banque"
 
-#: apps/note/models/transactions.py:248 templates/note/transaction_form.html:24
+#: apps/note/models/transactions.py:253
+#: templates/activity/activity_entry.html:17
+#: templates/note/transaction_form.html:24
 msgid "Credit"
 msgstr "Crédit"
 
-#: apps/note/models/transactions.py:248 templates/note/transaction_form.html:28
+#: apps/note/models/transactions.py:253 templates/note/transaction_form.html:28
 msgid "Debit"
 msgstr "Débit"
 
-#: apps/note/models/transactions.py:264 apps/note/models/transactions.py:269
+#: apps/note/models/transactions.py:269 apps/note/models/transactions.py:274
 msgid "membership transaction"
-msgstr "transaction d'adhésion"
+msgstr "Transaction d'adhésion"
 
-#: apps/note/models/transactions.py:265
+#: apps/note/models/transactions.py:270
 msgid "membership transactions"
-msgstr "transactions d'adhésion"
+msgstr "Transactions d'adhésion"
 
 #: apps/note/tables.py:57
 msgid "Click to invalidate"
@@ -623,20 +717,29 @@ msgstr "Cliquez pour valider"
 msgid "No reason specified"
 msgstr "Pas de motif spécifié"
 
-#: apps/note/views.py:39
+#: apps/note/tables.py:122 apps/note/tables.py:151
+msgid "Delete"
+msgstr "Supprimer"
+
+#: apps/note/tables.py:146 templates/member/club_info.html:55
+#: templates/note/conso_form.html:121
+msgid "Edit"
+msgstr "Éditer"
+
+#: apps/note/views.py:40
 msgid "Transfer money"
 msgstr "Transférer de l'argent"
 
-#: apps/note/views.py:100 templates/base.html:79
+#: apps/note/views.py:109 templates/base.html:79
 msgid "Consumptions"
 msgstr "Consommations"
 
-#: apps/permission/models.py:82 apps/permission/models.py:275
+#: apps/permission/models.py:82 apps/permission/models.py:281
 #, python-brace-format
 msgid "Can {type} {model}.{field} in {query}"
 msgstr ""
 
-#: apps/permission/models.py:84 apps/permission/models.py:277
+#: apps/permission/models.py:84 apps/permission/models.py:283
 #, python-brace-format
 msgid "Can {type} {model} in {query}"
 msgstr ""
@@ -645,11 +748,74 @@ msgstr ""
 msgid "rank"
 msgstr "Rang"
 
+#: apps/permission/models.py:110
+msgid "permission mask"
+msgstr "masque de permissions"
+
+#: apps/permission/models.py:111
+msgid "permission masks"
+msgstr "masques de permissions"
+
 #: apps/permission/models.py:160
+msgid "permission"
+msgstr "permission"
+
+#: apps/permission/models.py:161
+msgid "permissions"
+msgstr "permissions"
+
+#: apps/permission/models.py:166
 msgid "Specifying field applies only to view and change permission types."
 msgstr ""
 
-#: apps/treasury/apps.py:12 templates/base.html:99
+#: apps/permission/models.py:304 apps/permission/models.py:305
+msgid "role permissions"
+msgstr "Permissions par rôles"
+
+#: apps/registration/apps.py:10
+msgid "registration"
+msgstr "inscription"
+
+#: apps/registration/forms.py:70
+msgid "Join BDE Club"
+msgstr "Adhérer au club BDE"
+
+#: apps/registration/forms.py:77
+msgid "Join Kfet Club"
+msgstr "Adhérer au club Kfet"
+
+#: apps/registration/views.py:75
+msgid "Email validation"
+msgstr "Validation de l'adresse mail"
+
+#: apps/registration/views.py:121
+msgid "Email validation unsuccessful"
+msgstr " La validation de l'adresse mail a échoué"
+
+#: apps/registration/views.py:132
+msgid "Email validation email sent"
+msgstr "L'email de vérification de l'adresse email a bien été envoyé."
+
+#: apps/registration/views.py:185
+msgid "Unregistered users"
+msgstr "Utilisateurs en attente d'inscription"
+
+#: apps/registration/views.py:252
+msgid "You must join the BDE."
+msgstr "Vous devez adhérer au BDE."
+
+#: apps/registration/views.py:274
+msgid "You must join BDE club before joining Kfet club."
+msgstr "Vous devez adhérer au club BDE avant d'adhérer au club Kfet."
+
+#: apps/registration/views.py:279
+msgid ""
+"The entered amount is not enough for the memberships, should be at least {}"
+msgstr ""
+"Le montant crédité est trop faible pour adhérer, il doit être au minimum de "
+"{}"
+
+#: apps/treasury/apps.py:12 templates/base.html:111
 msgid "Treasury"
 msgstr "Trésorerie"
 
@@ -674,12 +840,8 @@ msgstr "La remise est déjà fermée."
 msgid "You can't change the type of the remittance."
 msgstr "Vous ne pouvez pas changer le type de la remise."
 
-#: apps/treasury/forms.py:125 templates/note/transaction_form.html:98
-msgid "Bank"
-msgstr "Banque"
-
 #: apps/treasury/forms.py:127 apps/treasury/tables.py:47
-#: templates/note/transaction_form.html:128
+#: templates/note/transaction_form.html:133
 #: templates/treasury/remittance_form.html:18
 msgid "Amount"
 msgstr "Montant"
@@ -700,7 +862,7 @@ msgstr "Objet"
 msgid "Description"
 msgstr "Description"
 
-#: apps/treasury/models.py:46 templates/note/transaction_form.html:86
+#: apps/treasury/models.py:46 templates/note/transaction_form.html:91
 msgid "Name"
 msgstr "Nom"
 
@@ -716,40 +878,80 @@ msgstr "Lieu"
 msgid "Acquitted"
 msgstr "Acquittée"
 
-#: apps/treasury/models.py:75
+#: apps/treasury/models.py:63
+msgid "invoice"
+msgstr "facture"
+
+#: apps/treasury/models.py:64
+msgid "invoices"
+msgstr "factures"
+
+#: apps/treasury/models.py:79
 msgid "Designation"
 msgstr "Désignation"
 
-#: apps/treasury/models.py:79
+#: apps/treasury/models.py:83
 msgid "Quantity"
 msgstr "Quantité"
 
-#: apps/treasury/models.py:83
+#: apps/treasury/models.py:87
 msgid "Unit price"
 msgstr "Prix unitaire"
 
-#: apps/treasury/models.py:120
+#: apps/treasury/models.py:103
+msgid "product"
+msgstr "produit"
+
+#: apps/treasury/models.py:104
+msgid "products"
+msgstr "produits"
+
+#: apps/treasury/models.py:121
+msgid "remittance type"
+msgstr "type de remise"
+
+#: apps/treasury/models.py:122
+msgid "remittance types"
+msgstr "types de remises"
+
+#: apps/treasury/models.py:132
 msgid "Date"
 msgstr "Date"
 
-#: apps/treasury/models.py:131
+#: apps/treasury/models.py:143
 msgid "Comment"
 msgstr "Commentaire"
 
-#: apps/treasury/models.py:136
+#: apps/treasury/models.py:148
 msgid "Closed"
 msgstr "Fermée"
 
-#: apps/treasury/models.py:169
+#: apps/treasury/models.py:152
+msgid "remittance"
+msgstr "remise"
+
+#: apps/treasury/models.py:153
+msgid "remittances"
+msgstr "remises"
+
+#: apps/treasury/models.py:185
 msgid "Remittance #{:d}: {}"
 msgstr "Remise n°{:d} : {}"
 
-#: apps/treasury/models.py:188 apps/treasury/tables.py:76
+#: apps/treasury/models.py:204 apps/treasury/tables.py:76
 #: apps/treasury/tables.py:84 templates/treasury/invoice_list.html:13
 #: templates/treasury/remittance_list.html:13
 msgid "Remittance"
 msgstr "Remise"
 
+#: apps/treasury/models.py:208
+msgid "special transaction proxy"
+msgstr "Proxy de transaction spéciale"
+
+#: apps/treasury/models.py:209
+msgid "special transaction proxies"
+msgstr "Proxys de transactions spéciales"
+
 #: apps/treasury/tables.py:19
 msgid "Invoice #{:d}"
 msgstr "Facture n°{:d}"
@@ -782,15 +984,15 @@ msgid ""
 "again unless your session expires or you logout."
 msgstr ""
 
-#: note_kfet/settings/base.py:152
+#: note_kfet/settings/base.py:153
 msgid "German"
 msgstr ""
 
-#: note_kfet/settings/base.py:153
+#: note_kfet/settings/base.py:154
 msgid "English"
 msgstr ""
 
-#: note_kfet/settings/base.py:154
+#: note_kfet/settings/base.py:155
 msgid "French"
 msgstr ""
 
@@ -826,18 +1028,15 @@ msgstr "Inviter"
 msgid "Guests list"
 msgstr "Liste des invités"
 
-#: templates/activity/activity_entry.html:10
+#: templates/activity/activity_entry.html:22
+#: templates/note/transaction_form.html:33
+msgid "Entries"
+msgstr "Entrées"
+
+#: templates/activity/activity_entry.html:30
 msgid "Return to activity page"
 msgstr "Retour à la page de l'activité"
 
-#: templates/activity/activity_entry.html:18
-msgid "entries"
-msgstr "entrées"
-
-#: templates/activity/activity_entry.html:18
-msgid "entry"
-msgstr "entrée"
-
 #: templates/activity/activity_list.html:5
 msgid "Upcoming activities"
 msgstr "Activités à venir"
@@ -859,9 +1058,23 @@ msgid "The ENS Paris-Saclay BDE note."
 msgstr "La note du BDE de l'ENS Paris-Saclay."
 
 #: templates/base.html:89
+msgid "Users"
+msgstr "Utilisateurs"
+
+#: templates/base.html:94
 msgid "Clubs"
 msgstr "Clubs"
 
+#: templates/base.html:100
+msgid "Registrations"
+msgstr "Inscriptions"
+
+#: templates/base.html:150
+msgid ""
+"Your e-mail address is not validated. Please check your mail inbox and click "
+"on the validation link."
+msgstr ""
+
 #: templates/cas_server/base.html:7
 msgid "Central Authentication Service"
 msgstr ""
@@ -939,10 +1152,6 @@ msgstr "cotisation pour adhérer"
 msgid "Add member"
 msgstr "Ajouter un membre"
 
-#: templates/member/club_info.html:55 templates/note/conso_form.html:121
-msgid "Edit"
-msgstr "Éditer"
-
 #: templates/member/club_info.html:59 templates/member/profile_info.html:48
 msgid "View Profile"
 msgstr "Voir le profil"
@@ -959,11 +1168,11 @@ msgstr "Créer un club"
 msgid "club listing "
 msgstr "Liste des clubs"
 
-#: templates/member/club_tables.html:9
+#: templates/member/club_tables.html:6
 msgid "Member of the Club"
 msgstr "Membre du club"
 
-#: templates/member/club_tables.html:22 templates/member/profile_tables.html:22
+#: templates/member/club_tables.html:17 templates/member/profile_tables.html:28
 msgid "Transaction history"
 msgstr "Historique des transactions"
 
@@ -980,18 +1189,22 @@ msgid "Regenerate token"
 msgstr "Regénérer le jeton"
 
 #: templates/member/profile_info.html:5
+#: templates/registration/future_profile_detail.html:12
 msgid "Account #"
 msgstr "Compte n°"
 
 #: templates/member/profile_info.html:17
+#: templates/registration/future_profile_detail.html:19
 msgid "username"
 msgstr "pseudo"
 
 #: templates/member/profile_info.html:20
+#: templates/registration/future_profile_detail.html:34
 msgid "password"
 msgstr "mot de passe"
 
 #: templates/member/profile_info.html:23
+#: templates/registration/future_profile_detail.html:37
 msgid "Change password"
 msgstr "Changer le mot de passe"
 
@@ -1003,7 +1216,17 @@ msgstr "solde du compte"
 msgid "Manage auth token"
 msgstr "Gérer les jetons d'authentification"
 
-#: templates/member/profile_tables.html:9
+#: templates/member/profile_tables.html:7
+#: templates/registration/future_profile_detail.html:28
+msgid "This user doesn't have confirmed his/her e-mail address."
+msgstr "Cet utilisateur n'a pas encore confirmé son adresse e-mail."
+
+#: templates/member/profile_tables.html:8
+#: templates/registration/future_profile_detail.html:29
+msgid "Click here to resend a validation link."
+msgstr "Cliquez ici pour renvoyer un lien de validation."
+
+#: templates/member/profile_tables.html:15
 msgid "View my memberships"
 msgstr "Voir mes adhésions"
 
@@ -1011,12 +1234,12 @@ msgstr "Voir mes adhésions"
 msgid "Save Changes"
 msgstr "Sauvegarder les changements"
 
-#: templates/member/signup.html:5 templates/member/signup.html:8
-#: templates/member/signup.html:14
-msgid "Sign up"
-msgstr "Inscription"
+#: templates/member/user_list.html:14
+#: templates/registration/future_user_list.html:17
+msgid "There is no pending user with this pattern."
+msgstr "Il n'y a pas d'inscription en attente avec cette entrée."
 
-#: templates/note/conso_form.html:28 templates/note/transaction_form.html:50
+#: templates/note/conso_form.html:28 templates/note/transaction_form.html:55
 msgid "Select emitters"
 msgstr "Sélection des émetteurs"
 
@@ -1040,7 +1263,7 @@ msgstr "Consommations simples"
 msgid "Double consumptions"
 msgstr "Consommations doubles"
 
-#: templates/note/conso_form.html:141 templates/note/transaction_form.html:147
+#: templates/note/conso_form.html:141 templates/note/transaction_form.html:152
 msgid "Recent transactions history"
 msgstr "Historique des transactions récentes"
 
@@ -1048,29 +1271,29 @@ msgstr "Historique des transactions récentes"
 msgid "Gift"
 msgstr "Don"
 
-#: templates/note/transaction_form.html:68
+#: templates/note/transaction_form.html:73
 msgid "External payment"
 msgstr "Paiement externe"
 
-#: templates/note/transaction_form.html:76
+#: templates/note/transaction_form.html:81
 msgid "Transfer type"
 msgstr "Type de transfert"
 
-#: templates/note/transaction_form.html:111
-#: templates/note/transaction_form.html:164
-#: templates/note/transaction_form.html:171
+#: templates/note/transaction_form.html:116
+#: templates/note/transaction_form.html:169
+#: templates/note/transaction_form.html:176
 msgid "Select receivers"
 msgstr "Sélection des destinataires"
 
-#: templates/note/transaction_form.html:133
+#: templates/note/transaction_form.html:138
 msgid "Reason"
 msgstr "Raison"
 
-#: templates/note/transaction_form.html:178
+#: templates/note/transaction_form.html:183
 msgid "Credit note"
 msgstr "Note à recharger"
 
-#: templates/note/transaction_form.html:185
+#: templates/note/transaction_form.html:190
 msgid "Debit note"
 msgstr "Note à débiter"
 
@@ -1090,14 +1313,53 @@ msgstr "Nouveau bouton"
 msgid "buttons listing "
 msgstr "Liste des boutons"
 
-#: templates/note/transactiontemplate_list.html:71
+#: templates/note/transactiontemplate_list.html:70
 msgid "button successfully deleted "
 msgstr "Le bouton a bien été supprimé"
 
-#: templates/note/transactiontemplate_list.html:75
+#: templates/note/transactiontemplate_list.html:74
 msgid "Unable to delete button "
 msgstr "Impossible de supprimer le bouton "
 
+#: templates/registration/email_validation_complete.html:6
+msgid "Your email have successfully been validated."
+msgstr "Votre adresse e-mail a bien été validée."
+
+#: templates/registration/email_validation_complete.html:8
+#, python-format
+msgid "You can now <a href=\"%(login_url)s\">log in</a>."
+msgstr "Vous pouvez désormais <a href=\"%(login_url)s\">vous connecter</a>."
+
+#: templates/registration/email_validation_complete.html:10
+msgid ""
+"You must pay now your membership in the Kfet to complete your registration."
+msgstr ""
+"Vous devez désormais payer votre adhésion à la Kfet pour compléter votre inscription."
+
+#: templates/registration/email_validation_complete.html:13
+msgid ""
+"The link was invalid. The token may have expired. Please send us an email to "
+"activate your account."
+msgstr ""
+"Le lien est invalide. Le jeton a sans doute expiré. Merci de nous contacter pour "
+"activer votre compte."
+
+#: templates/registration/future_profile_detail.html:56
+msgid "Delete registration"
+msgstr "Supprimer l'inscription"
+
+#: templates/registration/future_profile_detail.html:64
+msgid "Validate account"
+msgstr "Valider le compte"
+
+#: templates/registration/future_profile_detail.html:71
+msgid "Validate registration"
+msgstr "Valider l'inscription"
+
+#: templates/registration/future_user_list.html:7
+msgid "New user"
+msgstr "Nouvel utilisateur"
+
 #: templates/registration/logged_out.html:8
 msgid "Thanks for spending some quality time with the Web site today."
 msgstr ""
@@ -1131,9 +1393,41 @@ msgstr ""
 msgid "Forgotten your password or username?"
 msgstr ""
 
+#: templates/registration/mails/email_validation_email.html:3
+msgid "Hi"
+msgstr ""
+
+#: templates/registration/mails/email_validation_email.html:5
+msgid ""
+"You recently registered on the Note Kfet. Please click on the link below to "
+"confirm your registration."
+msgstr ""
+
+#: templates/registration/mails/email_validation_email.html:9
+msgid ""
+"This link is only valid for a couple of days, after that you will need to "
+"contact us to validate your email."
+msgstr ""
+
+#: templates/registration/mails/email_validation_email.html:11
+msgid ""
+"After that, you'll have to wait that someone validates your account before "
+"you can log in. You will need to pay your membership in the Kfet."
+msgstr ""
+"Après cela, vous devrez attendre que quelqu'un valide votre compte avant "
+"de pouvoir vous connecter. Vous devrez payer votre adhésion à la Kfet."
+
+#: templates/registration/mails/email_validation_email.html:13
+msgid "Thanks"
+msgstr "Merci"
+
+#: templates/registration/mails/email_validation_email.html:15
+msgid "The Note Kfet team."
+msgstr "L'équipe de la Note Kfet."
+
 #: templates/registration/password_change_done.html:8
 msgid "Your password was changed."
-msgstr ""
+msgstr "Votre mot de passe a bien été changé."
 
 #: templates/registration/password_change_form.html:9
 msgid ""
@@ -1184,6 +1478,11 @@ msgstr ""
 msgid "Reset my password"
 msgstr ""
 
+#: templates/registration/signup.html:5 templates/registration/signup.html:8
+#: templates/registration/signup.html:14
+msgid "Sign up"
+msgstr "Inscription"
+
 #: templates/treasury/invoice_form.html:6
 msgid "Invoices list"
 msgstr "Liste des factures"
@@ -1252,3 +1551,6 @@ msgstr "Il n'y a pas de transaction associée à une remise ouverte."
 #: templates/treasury/remittance_list.html:54
 msgid "Closed remittances"
 msgstr "Remises fermées"
+
+#~ msgid "This membership is already renewed"
+#~ msgstr "Cette adhésion est déjà renouvelée"
diff --git a/note_kfet/inputs.py b/note_kfet/inputs.py
index ecd758e058cfa995f54974bec65e12503a80660b..b3cccbcec9705294907034064aa17d5a047d5955 100644
--- a/note_kfet/inputs.py
+++ b/note_kfet/inputs.py
@@ -13,7 +13,7 @@ class AmountInput(NumberInput):
     template_name = "note/amount_input.html"
 
     def format_value(self, value):
-        return None if value is None or value == "" else "{:.02f}".format(value / 100, )
+        return None if value is None or value == "" else "{:.02f}".format(int(value) / 100, )
 
     def value_from_datadict(self, data, files, name):
         val = super().value_from_datadict(data, files, name)
diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py
index 61e5ea519acf223b1155242fc077040ec79c15c8..283f8e5606dec26e89b738314b4c4d0f881005b8 100644
--- a/note_kfet/settings/base.py
+++ b/note_kfet/settings/base.py
@@ -54,13 +54,14 @@ INSTALLED_APPS = [
     'rest_framework.authtoken',
 
     # Note apps
+    'api',
     'activity',
+    'logs',
     'member',
     'note',
-    'treasury',
     'permission',
-    'api',
-    'logs',
+    'registration',
+    'treasury',
 ]
 LOGIN_REDIRECT_URL = '/note/transfer/'
 
diff --git a/note_kfet/urls.py b/note_kfet/urls.py
index a7afab29c19491762e45de07d1ecab6d50788ea4..90d44a07e5dbae32e70629291a43fedc1519ba4f 100644
--- a/note_kfet/urls.py
+++ b/note_kfet/urls.py
@@ -16,6 +16,7 @@ urlpatterns = [
     # Include project routers
     path('note/', include('note.urls')),
     path('accounts/', include('member.urls')),
+    path('registration/', include('registration.urls')),
     path('activity/', include('activity.urls')),
     path('treasury/', include('treasury.urls')),
 
@@ -37,14 +38,7 @@ if "cas_server" in settings.INSTALLED_APPS:
         # Include CAS Server routers
         path('cas/', include('cas_server.urls', namespace="cas_server")),
     ]
-if "cas" in settings.INSTALLED_APPS:
-    from cas import views as cas_views
-    urlpatterns += [
-        # Include CAS Client routers
-        path('accounts/login/cas/', cas_views.login, name='cas_login'),
-        path('accounts/logout/cas/', cas_views.logout, name='cas_logout'),
 
-    ]
 if "debug_toolbar" in settings.INSTALLED_APPS:
     import debug_toolbar
     urlpatterns = [
diff --git a/static/js/autocomplete_model.js b/static/js/autocomplete_model.js
index e2a3f0cb1ec765df2da11a38a3c4f155ccf316a4..8c3f6f0915b963853219b9a21e8ca81a87cb08d7 100644
--- a/static/js/autocomplete_model.js
+++ b/static/js/autocomplete_model.js
@@ -24,6 +24,9 @@ $(document).ready(function () {
                 $("#" + prefix + "_" + obj.id).click(function() {
                     target.val(obj[name_field]);
                     $("#" + prefix + "_pk").val(obj.id);
+
+                    if (typeof autocompleted != 'undefined')
+                        autocompleted(obj, prefix)
                 });
 
                 if (input === obj[name_field])
diff --git a/static/js/base.js b/static/js/base.js
index 7febd3d663968a699119a2cf850ba48d1b145d7d..bb73b328ca7503048d188e52064195d0fecf07f4 100644
--- a/static/js/base.js
+++ b/static/js/base.js
@@ -19,23 +19,32 @@ function pretty_money(value) {
  * Add a message on the top of the page.
  * @param msg The message to display
  * @param alert_type The type of the alert. Choices: info, success, warning, danger
+ * @param timeout The delay (in millis) after that the message is auto-closed. If negative, then it is ignored.
  */
-function addMsg(msg, alert_type) {
+function addMsg(msg, alert_type, timeout=-1) {
     let msgDiv = $("#messages");
     let html = msgDiv.html();
+    let id = Math.floor(10000 * Math.random() + 1);
     html += "<div class=\"alert alert-" + alert_type + " alert-dismissible\">" +
-        "<button class=\"close\" data-dismiss=\"alert\" href=\"#\"><span aria-hidden=\"true\">×</span></button>"
+        "<button id=\"close-message-" + id + "\" class=\"close\" data-dismiss=\"alert\" href=\"#\"><span aria-hidden=\"true\">×</span></button>"
         + msg + "</div>\n";
     msgDiv.html(html);
+
+    if (timeout > 0) {
+        setTimeout(function () {
+            $("#close-message-" + id).click();
+        }, timeout);
+    }
 }
 
 /**
  * add Muliple error message from err_obj
  * @param errs_obj [{error_code:erro_message}]
+ * @param timeout The delay (in millis) after that the message is auto-closed. If negative, then it is ignored.
  */
-function errMsg(errs_obj){
+function errMsg(errs_obj, timeout=-1) {
     for (const err_msg of Object.values(errs_obj)) {
-              addMsg(err_msg,'danger');
+              addMsg(err_msg,'danger', timeout);
           }
 }
 
diff --git a/static/js/transfer.js b/static/js/transfer.js
index cf62e45376ec9f201964cf71332055f29008b01a..e5aafc3903864e3ecf256cb6ed4c0f601c2ad581 100644
--- a/static/js/transfer.js
+++ b/static/js/transfer.js
@@ -61,16 +61,24 @@ $(document).ready(function() {
 
 
     // Ensure we begin in gift mode. Removing these lines may cause problems when reloading.
-    $("#type_gift").prop('checked', 'true');
+    let type_gift = $("#type_gift"); // Default mode
+    type_gift.removeAttr('checked');
     $("#type_transfer").removeAttr('checked');
     $("#type_credit").removeAttr('checked');
     $("#type_debit").removeAttr('checked');
+    $("label[for='type_gift']").attr('class', 'btn btn-sm btn-outline-primary');
     $("label[for='type_transfer']").attr('class', 'btn btn-sm btn-outline-primary');
     $("label[for='type_credit']").attr('class', 'btn btn-sm btn-outline-primary');
     $("label[for='type_debit']").attr('class', 'btn btn-sm btn-outline-primary');
+
+    if (location.hash)
+        $("#type_" + location.hash.substr(1)).click();
+    else
+        type_gift.click();
+    location.hash = "";
 });
 
-$("#transfer").click(function() {
+$("#btn_transfer").click(function() {
     if ($("#type_gift").is(':checked')) {
         dests_notes_display.forEach(function (dest) {
             $.post("/api/note/transaction/transaction/",
diff --git a/templates/activity/activity_detail.html b/templates/activity/activity_detail.html
index 841820650e3ee5b1fce310fb83dbdf5abb8b4e83..7ee9f7c0f82322613072cb45d300c5bf1c042b5e 100644
--- a/templates/activity/activity_detail.html
+++ b/templates/activity/activity_detail.html
@@ -118,7 +118,6 @@
     });
 
     $("#validate_activity").click(function () {
-        console.log(42);
         $.ajax({
             url: "/api/activity/activity/{{ activity.pk }}/",
             type: "PATCH",
diff --git a/templates/activity/activity_entry.html b/templates/activity/activity_entry.html
index c06a5188d4aedca5482f138a92ee2d761cb5f61c..a712228d202a3408ef9ea63bd66fecda75334c49 100644
--- a/templates/activity/activity_entry.html
+++ b/templates/activity/activity_entry.html
@@ -6,6 +6,26 @@
 {% load perms %}
 
 {% block content %}
+    <div class="row">
+        <div class="col-xl-12">
+            <div class="btn-group btn-group-toggle" style="width: 100%; padding: 0 0 2em 0" data-toggle="buttons">
+                <a href="{% url "note:transfer" %}#transfer" class="btn btn-sm btn-outline-primary">
+                    {% trans "Transfer" %}
+                </a>
+                {% if "note.notespecial"|not_empty_model_list %}
+                    <a href="{% url "note:transfer" %}#credit" class="btn btn-sm btn-outline-primary">
+                        {% trans "Credit" %}
+                    </a>
+                {% endif %}
+                {% for a in activities_open %}
+                    <a href="{% url "activity:activity_entry" pk=a.pk %}" class="btn btn-sm btn-outline-primary{% if a.pk == activity.pk %} active{% endif %}">
+                        {% trans "Entries" %} {{ a.name }}
+                    </a>
+                {% endfor %}
+            </div>
+        </div>
+    </div>
+
     <a href="{% url "activity:activity_detail" pk=activity.pk %}">
         <button class="btn btn-light">{% trans "Return to activity page" %}</button>
     </a>
@@ -56,10 +76,10 @@
                         note: id,
                         guest: null
                     }).done(function () {
-                        addMsg("Entrée effectuée !", "success");
+                        addMsg("Entrée effectuée !", "success", 4000);
                         reloadTable(true);
                     }).fail(function(xhr) {
-                        errMsg(xhr.responseJSON);
+                        errMsg(xhr.responseJSON, 4000);
                     });
                 }
                 else {
@@ -84,10 +104,10 @@
                             note: target.attr("data-inviter"),
                             guest: id
                         }).done(function () {
-                            addMsg("Entrée effectuée !", "success");
+                            addMsg("Entrée effectuée !", "success", 4000);
                             reloadTable(true);
                         }).fail(function (xhr) {
-                            errMsg(xhr.responseJSON);
+                            errMsg(xhr.responseJSON, 4000);
                         });
                     };
 
@@ -111,7 +131,7 @@
                                 makeTransaction();
                                 reset();
                             }).fail(function (xhr) {
-                                errMsg(xhr.responseJSON);
+                                errMsg(xhr.responseJSON, 4000);
                             });
                         };
                     };
diff --git a/templates/base.html b/templates/base.html
index c44e24676fc4bcdc44ed89bc2ff252602153956e..3c2c637f332621e6626a3008323bf24f0d62d30a 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -94,6 +94,13 @@ SPDX-License-Identifier: GPL-3.0-or-later
                         <a class="nav-link" href="{% url 'member:club_list' %}"><i class="fa fa-users"></i> {% trans 'Clubs' %}</a>
                     </li>
                 {% endif %}
+                {% if "member.change_profile_registration_valid"|has_perm:user %}
+                    <li class="nav-item active">
+                        <a class="nav-link" href="{% url 'registration:future_user_list' %}">
+                            <i class="fa fa-user-plus"></i> {% trans "Registrations" %}
+                        </a>
+                    </li>
+                {% endif %}
                 {% if "activity.activity"|not_empty_model_list %}
                     <li class="nav-item active">
                         <a class="nav-link" href="{% url 'activity:activity_list' %}"><i class="fa fa-calendar"></i> {% trans 'Activities' %}</a>
@@ -124,7 +131,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
                     </li>
                 {% else %}
                     <li class="nav-item active">
-                        <a class="nav-link" href="{% url 'member:signup' %}">
+                        <a class="nav-link" href="{% url 'registration:signup' %}">
                             <i class="fa fa-user-plus"></i> S'inscrire
                         </a>
                     </li>
@@ -138,6 +145,11 @@ 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 %}
+            <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>
+        {% endif %}
         {% block contenttitle %}<h1>{{ title }}</h1>{% endblock %}
         <div id="messages"></div>
         {% block content %}
diff --git a/templates/member/add_members.html b/templates/member/add_members.html
index c44440bf40df596a4c7e3087bc262cd1711f30eb..ad6f1f297a77ec7e2d58e401405fd0414f4a61ce 100644
--- a/templates/member/add_members.html
+++ b/templates/member/add_members.html
@@ -16,6 +16,40 @@
 {% endblock %}
 
 {% block extrajavascript %}
-<script>
-</script>
+    <script>
+        function autocompleted(user) {
+            $("#id_last_name").val(user.last_name);
+            $("#id_first_name").val(user.first_name);
+            $.getJSON("/api/members/profile/" + user.id + "/", function(profile) {
+                let fee = profile.paid ? {{ club.membership_fee_paid }} : {{ club.membership_fee_unpaid }};
+                $("#id_credit_amount").val((fee / 100).toFixed(2));
+            });
+        }
+
+        soge_field = $("#id_soge");
+
+        function fillFields() {
+            let checked = soge_field.is(':checked');
+            if (!checked) {
+                $("input").attr('disabled', false);
+                $("#id_user").attr('disabled', true);
+                $("select").attr('disabled', false);
+                return;
+            }
+
+            let credit_type = $("#id_credit_type");
+            credit_type.attr('disabled', true);
+            credit_type.val(4);
+
+            let credit_amount = $("#id_credit_amount");
+            credit_amount.attr('disabled', true);
+            credit_amount.val('{{ total_fee }}');
+
+            let bank = $("#id_bank");
+            bank.attr('disabled', true);
+            bank.val('Société générale');
+        }
+
+        soge_field.change(fillFields);
+    </script>
 {% endblock %}
diff --git a/templates/member/club_detail.html b/templates/member/club_detail.html
index 3ad299010a5d375e6fd895ee407400f248539147..fedd43fa85e392dd0d1015474b23bb607389fe6e 100644
--- a/templates/member/club_detail.html
+++ b/templates/member/club_detail.html
@@ -10,9 +10,11 @@
 
 {% block extrajavascript %}
     <script>
-    function refreshHistory() {
-        $("#history_list").load("{% url 'member:club_detail' pk=object.pk %} #history_list");
-        $("#profile_infos").load("{% url 'member:club_detail' pk=object.pk %} #profile_infos");
-    }
+        function refreshHistory() {
+            $("#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/club_info.html b/templates/member/club_info.html
index a781bea85d7d1a08ac216c2e0c95d41cf2b00646..93c76d59e9f608ec8218b7cee102c9b49d59e074 100644
--- a/templates/member/club_info.html
+++ b/templates/member/club_info.html
@@ -49,13 +49,13 @@
     </div>
     <div class="card-footer text-center">
         {% if can_add_members %}
-            <a class="btn btn-primary btn-sm my-1" href="{% url 'member:club_add_member' pk=club.pk %}"> {% trans "Add member" %}</a>
+            <a class="btn btn-primary btn-sm my-1" href="{% url 'member:club_add_member' club_pk=club.pk %}"> {% trans "Add member" %}</a>
         {% endif %}
         {% if ".change_"|has_perm:club %}
             <a class="btn btn-primary btn-sm my-1" href="{% url 'member:club_update' pk=club.pk %}"> {% trans "Edit" %}</a>
         {% endif %}
         {% url 'member:club_detail' club.pk as club_detail_url %}
-        {%if request.get_full_path != club_detail_url %}
+        {%if request.path_info != club_detail_url %}
         <a class="btn btn-primary btn-sm my-1" href="{{ club_detail_url }}">{% trans 'View Profile' %}</a>
         {% endif %}    </div>
 </div>
diff --git a/templates/member/club_list.html b/templates/member/club_list.html
index 2653ace8388704aa45e3ba8cd235de9ab4e4ab63..4682164cfb44384bf192bb05df60a2dff6bf7e0c 100644
--- a/templates/member/club_list.html
+++ b/templates/member/club_list.html
@@ -36,7 +36,6 @@ function getInfo() {
     if (asked.length >= 1) {
         $.getJSON("/api/members/club/?format=json&search="+asked, function(buttons){
             let selected_id = buttons.results.map((a => "#row-"+a.id));
-            console.log(selected_id.join());
             $(".table-row,"+selected_id.join()).show();
             $(".table-row").not(selected_id.join()).hide();
             
diff --git a/templates/member/club_tables.html b/templates/member/club_tables.html
index fbded9c3b194088e74b178ba20f220af127a0f43..32be9bd43b7652618eeaa8a55be50f672e2a1786 100644
--- a/templates/member/club_tables.html
+++ b/templates/member/club_tables.html
@@ -1,31 +1,23 @@
 {% load render_table from django_tables2 %}
 {% load i18n %}
-<div class="accordion shadow" id="accordionProfile">
-    <div class="card">
-        <div class="card-header position-relative" id="clubListHeading">
-            <a class="btn btn-link stretched-link font-weight-bold"
-               data-toggle="collapse" data-target="#clubListCollapse"
-               aria-expanded="true" aria-controls="clubListCollapse">
-                <i class="fa fa-users"></i> {% trans "Member of the Club" %}
-            </a>
-        </div>
-        <div id="clubListCollapse" class="collapse show" style="overflow:auto hidden" aria-labelledby="clubListHeading" data-parent="#accordionProfile">
-            {% render_table member_list %}
-        </div>
+<div class="card">
+    <div class="card-header position-relative" id="clubListHeading">
+        <a class="btn btn-link stretched-link font-weight-bold">
+            <i class="fa fa-users"></i> {% trans "Member of the Club" %}
+        </a>
     </div>
+        {% render_table member_list %}
+</div>
 
-    <div class="card">
-        <div class="card-header position-relative" id="historyListHeading">
-            <a class="btn btn-link stretched-link collapsed font-weight-bold"
-               data-toggle="collapse" data-target="#historyListCollapse"
-               aria-expanded="false" aria-controls="historyListCollapse">
-                <i class="fa fa-euro"></i> {% trans "Transaction history" %}
-            </a>
-        </div>
-        <div id="historyListCollapse" class="collapse" style="overflow:auto hidden" aria-labelledby="historyListHeading" data-parent="#accordionProfile">
-            <div id="history_list">
-                {% render_table history_list %}
-            </div>
-        </div>
+<hr>
+
+<div class="card">
+    <div class="card-header position-relative" id="historyListHeading">
+        <a class="btn btn-link stretched-link font-weight-bold">
+            <i class="fa fa-euro"></i> {% trans "Transaction history" %}
+        </a>
     </div>
+        <div id="history_list">
+            {% render_table history_list %}
+        </div>
 </div>
diff --git a/templates/member/profile_detail.html b/templates/member/profile_detail.html
index 42d03d8b2c26a1ca859b6acd4cb16944721d51e2..04b15d3bdab67e6c375a956461b0b094a99140f1 100644
--- a/templates/member/profile_detail.html
+++ b/templates/member/profile_detail.html
@@ -7,3 +7,14 @@
 {% block profile_content %}
 {% include "member/profile_tables.html" %}
 {% endblock %}
+
+{% 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");
+        }
+
+        window.history.replaceState({}, document.title, location.pathname);
+    </script>
+{% endblock %}
diff --git a/templates/member/profile_info.html b/templates/member/profile_info.html
index 9ff20385de4f9d201af1bb01918c13b89840006c..748563559ff82a2b52fdbb4b931551795132cd6a 100644
--- a/templates/member/profile_info.html
+++ b/templates/member/profile_info.html
@@ -44,7 +44,7 @@
     <div class="card-footer text-center">
         <a class="btn btn-primary btn-sm" href="{% url 'member:user_update_profile' object.pk %}">{% trans 'Update Profile' %}</a>
         {% url 'member:user_detail' object.pk as user_profile_url %}
-        {%if request.get_full_path != 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 %}
     </div>
diff --git a/templates/member/profile_tables.html b/templates/member/profile_tables.html
index 9d2c687f23274092472660dcbdbe0adb01654738..9629ff14950ab74becccf93182095c1f35b6d85a 100644
--- a/templates/member/profile_tables.html
+++ b/templates/member/profile_tables.html
@@ -1,31 +1,34 @@
 {% load render_table from django_tables2 %}
 {% load i18n %}
-<div class="accordion shadow" id="accordionProfile">
-    <div class="card">
-        <div class="card-header position-relative" id="clubListHeading">
-            <a class="btn btn-link stretched-link font-weight-bold"
-               data-toggle="collapse" data-target="#clubListCollapse"
-               aria-expanded="true" aria-controls="clubListCollapse">
-                <i class="fa fa-users"></i> {% trans "View my memberships" %}
-            </a>
-        </div>
-        <div id="clubListCollapse" class="collapse show" style="overflow:auto hidden" aria-labelledby="clubListHeading" data-parent="#accordionProfile">
-            {% render_table club_list %}
-        </div>
+{% load perms %}
+
+{% if not object.profile.email_confirmed and "member.change_profile_email_confirmed"|has_perm: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=object.pk %}">{% trans "Click here to resend a validation link." %}</a>
+    </div>
+{% endif %}
+
+<div class="card">
+    <div class="card-header position-relative" id="clubListHeading">
+        <a class="btn btn-link stretched-link font-weight-bold">
+            <i class="fa fa-users"></i> {% trans "View my memberships" %}
+        </a>
     </div>
+    {% render_table club_list %}
+</div>
+
+<hr>
 
-    <div class="card">
-        <div class="card-header position-relative" id="historyListHeading">
-            <a class="btn btn-link stretched-link collapsed font-weight-bold"
-               data-toggle="collapse" data-target="#historyListCollapse"
-               aria-expanded="false" aria-controls="historyListCollapse">
-                <i class="fa fa-euro"></i> {% trans "Transaction history" %}
-            </a>
-        </div>
-        <div id="historyListCollapse" class="collapse" style="overflow:auto hidden" aria-labelledby="historyListHeading" data-parent="#accordionProfile">
-            <div id="history_list">
-                {% render_table history_list %}
-            </div>
-        </div>
+<div class="card">
+    <div class="card-header position-relative" id="historyListHeading">
+        <a class="btn btn-link stretched-link collapsed font-weight-bold"
+           data-toggle="collapse" data-target="#historyListCollapse"
+           aria-expanded="true" aria-controls="historyListCollapse">
+            <i class="fa fa-euro"></i> {% trans "Transaction history" %}
+        </a>
+    </div>
+    <div id="history_list">
+        {% render_table history_list %}
     </div>
 </div>
diff --git a/templates/member/user_list.html b/templates/member/user_list.html
index d0eaaedb288c85d568eff2a4a7af449404c1fe50..0bcd7e89c2dd9e2db54b72e2fd69ccdb70a0d6a4 100644
--- a/templates/member/user_list.html
+++ b/templates/member/user_list.html
@@ -7,7 +7,13 @@
     <hr>
 
     <div id="user_table">
-        {% render_table table %}
+        {% if table.data %}
+            {% render_table table %}
+        {% else %}
+            <div class="alert alert-warning">
+                {% trans "There is no pending user with this pattern." %}
+            </div>
+        {% endif %}
     </div>
 
 {% endblock %}
diff --git a/templates/note/transaction_form.html b/templates/note/transaction_form.html
index 65aaa635373a9a70c8f880dda6c7536cd6954113..0b53df61f2586fd0bfc7e4e386230bf38e45e793 100644
--- a/templates/note/transaction_form.html
+++ b/templates/note/transaction_form.html
@@ -28,6 +28,11 @@ SPDX-License-Identifier: GPL-2.0-or-later
                         {% trans "Debit" %}
                     </label>
                 {% endif %}
+                {% for activity in activities_open %}
+                    <a href="{% url "activity:activity_entry" pk=activity.pk %}" class="btn btn-sm btn-outline-primary">
+                        {% trans "Entries" %} {{ activity.name }}
+                    </a>
+                {% endfor %}
             </div>
         </div>
     </div>
@@ -137,7 +142,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
 
     <div class="form-row">
         <div class="col-md-12">
-            <button id="transfer" class="form-control btn btn-primary">{% trans 'Transfer' %}</button>
+            <button id="btn_transfer" class="form-control btn btn-primary">{% trans 'Transfer' %}</button>
         </div>
     </div>
 
diff --git a/templates/note/transactiontemplate_list.html b/templates/note/transactiontemplate_list.html
index af0a02b4ebbbd9d7bf748f7e8d1b04e00421edba..cf9bc5edc91b2fb9ce28dacba346b19b24cbf6ec 100644
--- a/templates/note/transactiontemplate_list.html
+++ b/templates/note/transactiontemplate_list.html
@@ -37,7 +37,6 @@ function getInfo() {
     if (asked.length >= 1) {
         $.getJSON("/api/note/transaction/template/?format=json&search="+asked, function(buttons){
             let selected_id = buttons.results.map((a => "#row-"+a.id));
-            console.log(selected_id.join());
             $(".table-row,"+selected_id.join()).show();
             $(".table-row").not(selected_id.join()).hide();
             
diff --git a/templates/registration/email_validation_complete.html b/templates/registration/email_validation_complete.html
new file mode 100644
index 0000000000000000000000000000000000000000..4835cfa16f2ee9c0fd5bcda1d121487171339400
--- /dev/null
+++ b/templates/registration/email_validation_complete.html
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+{% load i18n %}
+
+{% block content %}
+    {% if validlink %}
+        {% trans "Your email have successfully been validated." %}
+        {% if user.profile.registration_valid %}
+            {% blocktrans %}You can now <a href="{{ login_url }}">log in</a>.{% endblocktrans %}
+        {% else %}
+            {% trans "You must pay now your membership in the Kfet to complete your registration." %}
+        {% endif %}
+    {% else %}
+        {% trans "The link was invalid. The token may have expired. Please send us an email to activate your account." %}
+    {% endif %}
+{% endblock %}
diff --git a/templates/registration/email_validation_email_sent.html b/templates/registration/email_validation_email_sent.html
new file mode 100644
index 0000000000000000000000000000000000000000..bd4cf8d8923af01d7d6668cdb670b17bdcffdd34
--- /dev/null
+++ b/templates/registration/email_validation_email_sent.html
@@ -0,0 +1,7 @@
+{% extends "base.html" %}
+
+{% block content %}
+<h2>Account Activation</h2>
+
+An email has been sent. Please click on the link to activate your account.
+{% endblock %}
\ No newline at end of file
diff --git a/templates/registration/future_profile_detail.html b/templates/registration/future_profile_detail.html
new file mode 100644
index 0000000000000000000000000000000000000000..8c78fb8dd480da490099e0d7b0ac4c7a66c53f6c
--- /dev/null
+++ b/templates/registration/future_profile_detail.html
@@ -0,0 +1,119 @@
+{% extends "base.html" %}
+{% load static %}
+{% load i18n %}
+{% load crispy_forms_tags %}
+{% load perms %}
+
+{% block content %}
+    <div class="row mt-4">
+        <div class="col-md-3 mb-4">
+            <div class="card bg-light shadow">
+                <div class="card-header text-center" >
+                    <h4> {% trans "Account #" %}  {{ object.pk }}</h4>
+                </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">{{ object.last_name }} {{ object.first_name }}</dd>
+
+                        <dt class="col-xl-6">{% trans 'username'|capfirst %}</dt>
+                        <dd class="col-xl-6">{{ object.username }}</dd>
+
+                        <dt class="col-xl-6">{% trans 'email'|capfirst %}</dt>
+                        <dd class="col-xl-6"><a href="mailto:{{ object.email }}">{{ object.email }}</a></dd>
+
+                        {% if not object.profile.email_confirmed and "member.change_profile_email_confirmed"|has_perm:object.profile %}
+                            <dd class="col-xl-12">
+                                <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=object.pk %}">{% trans "Click here to resend a validation link." %}</a>
+                                </div>
+                            </dd>
+                        {% endif %}
+
+                        <dt class="col-xl-6">{% trans 'password'|capfirst %}</dt>
+                        <dd class="col-xl-6">
+                            <a class="small" href="{% url 'password_change' %}">
+                                {% trans 'Change password' %}
+                            </a>
+                        </dd>
+
+                        <dt class="col-xl-6">{% trans 'section'|capfirst %}</dt>
+                        <dd class="col-xl-6">{{ object.profile.section }}</dd>
+
+                        <dt class="col-xl-6">{% trans 'address'|capfirst %}</dt>
+                        <dd class="col-xl-6">{{ object.profile.address }}</dd>
+
+                        <dt class="col-xl-6">{% trans 'phone number'|capfirst %}</dt>
+                        <dd class="col-xl-6">{{ object.profile.phone_number }}</dd>
+
+                        <dt class="col-xl-6">{% trans 'paid'|capfirst %}</dt>
+                        <dd class="col-xl-6">{{ object.profile.paid|yesno }}</dd>
+                    </dl>
+                </div>
+                <div class="card-footer text-center">
+                    <a class="btn btn-primary btn-sm" href="{% url 'member:user_update_profile' object.pk %}">{% trans 'Update Profile' %}</a>
+                    <a class="btn btn-danger btn-sm" href="{% url 'registration:future_user_invalidate' object.pk %}">{% trans 'Delete registration' %}</a>
+                </div>
+            </div>
+        </div>
+        <div class="col-md-9">
+            <div class="card bg-light shadow">
+                <form method="post">
+                    <div class="card-header text-center" >
+                        <h4> {% trans "Validate account" %}</h4>
+                    </div>
+                    <div class="card-body" id="profile_infos">
+                        {% csrf_token %}
+                        {{ form|crispy }}
+                    </div>
+                    <div class="card-footer text-center">
+                        <button class="btn btn-success btn-sm">{% trans 'Validate registration' %}</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+{% endblock %}
+
+{% block extrajavascript %}
+    <script>
+        soge_field = $("#id_soge");
+
+        function fillFields() {
+            let checked = soge_field.is(':checked');
+            if (!checked) {
+                $("input").attr('disabled', false);
+                $("select").attr('disabled', false);
+                return;
+            }
+
+            let credit_type = $("#id_credit_type");
+            credit_type.attr('disabled', true);
+            credit_type.val(4);
+
+            let credit_amount = $("#id_credit_amount");
+            credit_amount.attr('disabled', true);
+            credit_amount.val('{{ total_fee }}');
+
+            let bank = $("#id_bank");
+            bank.attr('disabled', true);
+            bank.val('Société générale');
+
+            let join_BDE = $("#id_join_BDE");
+            join_BDE.attr('disabled', true);
+            join_BDE.attr('checked', 'checked');
+
+            let join_Kfet = $("#id_join_Kfet");
+            join_Kfet.attr('disabled', true);
+            join_Kfet.attr('checked', 'checked');
+        }
+
+        soge_field.change(fillFields);
+
+        {% if object.profile.soge %}
+            soge_field.attr('checked', true);
+            fillFields();
+        {% endif %}
+    </script>
+{% endblock %}
\ No newline at end of file
diff --git a/templates/registration/future_user_list.html b/templates/registration/future_user_list.html
new file mode 100644
index 0000000000000000000000000000000000000000..1e10dcbb099214d7b9a9ac0a7d928c46063953e2
--- /dev/null
+++ b/templates/registration/future_user_list.html
@@ -0,0 +1,53 @@
+{% extends "base.html" %}
+{% load render_table from django_tables2 %}
+{% load crispy_forms_tags %}
+{% load i18n %}
+
+{% block content %}
+    <a href="{% url 'registration:signup' %}"><button class="btn btn-primary btn-block">{% trans "New user" %}</button></a>
+    <hr>
+    <input id="searchbar" type="text" class="form-control" placeholder="Nom/prénom/note/section ...">
+    <hr>
+
+    <div id="user_table">
+        {% if table.data %}
+            {% render_table table %}
+        {% else %}
+            <div class="alert alert-warning">
+                {% trans "There is no pending user with this pattern." %}
+            </div>
+        {% endif %}
+    </div>
+{% endblock %}
+
+{% block extrajavascript %}
+<script type="text/javascript">
+    $(document).ready(function() {
+        let old_pattern = null;
+        let searchbar_obj = $("#searchbar");
+
+        function reloadTable() {
+            let pattern = searchbar_obj.val();
+
+            if (pattern === old_pattern || pattern === "")
+                return;
+
+            $("#user_table").load(location.href + "?search=" + pattern.replace(" ", "%20") + " #user_table", init);
+
+            $(".table-row").click(function() {
+                window.document.location = $(this).data("href");
+            });
+        }
+
+        searchbar_obj.keyup(reloadTable);
+
+        function init() {
+            $(".table-row").click(function() {
+                window.document.location = $(this).data("href");
+            });
+        }
+
+        init();
+    });
+</script>
+{% endblock %}
diff --git a/templates/registration/mails/email_validation_email.html b/templates/registration/mails/email_validation_email.html
new file mode 100644
index 0000000000000000000000000000000000000000..577c1220abdcb9425a806bddfac0c444d89e79df
--- /dev/null
+++ b/templates/registration/mails/email_validation_email.html
@@ -0,0 +1,15 @@
+{% load i18n %}
+
+{% trans "Hi" %} {{ user.username }},
+
+{% trans "You recently registered on the Note Kfet. Please click on the link below to confirm your registration." %}
+
+https://{{ domain }}{% url 'registration:email_validation' uidb64=uid token=token %}
+
+{% trans "This link is only valid for a couple of days, after that you will need to contact us to validate your email." %}
+
+{% trans "After that, you'll have to wait that someone validates your account before you can log in. You will need to pay your membership in the Kfet." %}
+
+{% trans "Thanks" %},
+
+{% trans "The Note Kfet team." %}
diff --git a/templates/member/signup.html b/templates/registration/signup.html
similarity index 100%
rename from templates/member/signup.html
rename to templates/registration/signup.html
diff --git a/tox.ini b/tox.ini
index 01bf4edbd0f9f6e21622bb04b4778c30ac4add57..73cf05257e26a19198df57a0bd0c911df250e1bc 100644
--- a/tox.ini
+++ b/tox.ini
@@ -34,7 +34,7 @@ commands =
 
 [flake8]
 # Ignore too many errors, should be reduced in the future
-ignore = D203, W503, E203, I100, I101
+ignore = D203, W503, E203, I100, I101, C901
 exclude =
     .tox,
     .git,