diff --git a/.env_example b/.env_example
new file mode 100644
index 0000000000000000000000000000000000000000..5aba0d14603ad3939f8bd439de4b49f57e29c016
--- /dev/null
+++ b/.env_example
@@ -0,0 +1,13 @@
+DJANGO_APP_STAGE="dev"
+# Only used in dev mode, change to "postgresql" if you want to use PostgreSQL in dev
+DJANGO_DEV_STORE_METHOD="sqllite"
+DJANGO_DB_HOST="localhost"
+DJANGO_DB_NAME="note_db"
+DJANGO_DB_USER="note"
+DJANGO_DB_PASSWORD="CHANGE_ME"
+DJANGO_DB_PORT=""
+DJANGO_SECRET_KEY="CHANGE_ME"
+DJANGO_SETTINGS_MODULE="note_kfet.settings"
+DOMAIN="localhost"
+CONTACT_EMAIL="tresorerie.bde@localhost"
+NOTE_URL="localhost"
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..94cf1be69e8cbd2701c78623485f8f508ac64c9c
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "apps/scripts"]
+	path = apps/scripts
+	url = git@gitlab.crans.org:bde/nk20-scripts.git
diff --git a/Dockerfile b/Dockerfile
index 2c840829ae86929bace6c9c68396c7bd808a5679..d42bdd1f479c7155f33160d75271f54c1bd5ee6c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -9,10 +9,13 @@ RUN apt update && \
     apt install -y gettext nginx uwsgi uwsgi-plugin-python3 && \
     rm -rf /var/lib/apt/lists/*
 
-COPY requirements.txt /code/
-RUN pip install -r requirements.txt
-
 COPY . /code/
 
+# Comment what is not needed
+RUN pip install -r requirements/base.txt
+RUN pip install -r requirements/api.txt
+RUN pip install -r requirements/cas.txt
+RUN pip install -r requirements/production.txt
+
 ENTRYPOINT ["/code/entrypoint.sh"]
 EXPOSE 8000
diff --git a/README.md b/README.md
index 5ae8a3967704fa1d128b6375f5990d51a3a943d4..91f2f17d48de7d9e4c1077fca7826318745ec7a4 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ On supposera pour la suite que vous utiliser debian/ubuntu sur un serveur tout n
 
         $ python3 -m venv env
         $ source env/bin/activate
-        (env)$ pip3 install -r requirements.txt
+        (env)$ pip3 install -r requirements/base.txt
         (env)$ deactivate
 
 4. uwsgi  et Nginx
@@ -40,14 +40,13 @@ On supposera pour la suite que vous utiliser debian/ubuntu sur un serveur tout n
 
         $ cp nginx_note.conf_example nginx_note.conf
 
-***Modifier le fichier pour être en accord avec le reste de votre config***
+    ***Modifier le fichier pour être en accord avec le reste de votre config***
 
-    On utilise uwsgi et Nginx pour gérer le coté serveu :
+    On utilise uwsgi et Nginx pour gérer le coté serveur :
 
-        $ sudo ln -sf /var/www/note_kfet/nginx_note.conf /etc/nginx/sites-enabled/
+       $ sudo ln -sf /var/www/note_kfet/nginx_note.conf /etc/nginx/sites-enabled/
 
-
-    Si l'on a un emperor (plusieurs instance uwsgi):
+   Si l'on a un emperor (plusieurs instance uwsgi):
 
         $ sudo ln -sf /var/www/note_kfet/uwsgi_note.ini /etc/uwsgi/sites/
 
@@ -85,7 +84,7 @@ On supposera pour la suite que vous utiliser debian/ubuntu sur un serveur tout n
         postgres=# CREATE DATABASE note_db OWNER note;
         CREATE DATABASE
 
-    Si tout va bien:
+    Si tout va bien :
         
         postgres=#\list
         List of databases
@@ -96,22 +95,29 @@ On supposera pour la suite que vous utiliser debian/ubuntu sur un serveur tout n
          template0 | postgres | UTF8     | fr_FR.UTF-8 | fr_FR.UTF-8 | =c/postgres+postgres=CTc/postgres
          template1 | postgres | UTF8     | fr_FR.UTF-8 | fr_FR.UTF-8 | =c/postgres  +postgres=CTc/postgres
         (4 rows)
-
-    Dans un fichier `.env` à la racine du projet on renseigne des secrets:
     
-        DJANGO_APP_STAGE='prod'
-        DJANGO_DB_PASSWORD='le_mot_de_passe_de_la_bdd'
-        DJANGO_SECRET_KEY='une_secret_key_longue_et_compliquee'
-	ALLOWED_HOSTS='le_ndd_de_votre_instance'
-    
-
 6. Variable d'environnement et Migrations
         
-
-Ensuite on (re)bascule dans l'environement virtuel et on lance les migrations
+    On copie le fichier `.env_example` vers le fichier `.env` à la racine du projet 
+    et on renseigne des secrets et des paramètres :
+    
+        DJANGO_APP_STAGE="dev"
+        DJANGO_DEV_STORE_METHOD="sqllite"
+        DJANGO_DB_HOST="localhost"
+        DJANGO_DB_NAME="note_db"
+        DJANGO_DB_USER="note"
+        DJANGO_DB_PASSWORD="CHANGE_ME"
+        DJANGO_DB_PORT=""
+        DJANGO_SECRET_KEY="CHANGE_ME"
+        DJANGO_SETTINGS_MODULE="note_kfet.settings"
+        DOMAIN="localhost"
+        CONTACT_EMAIL="tresorerie.bde@localhost"
+        NOTE_URL="localhost"
+
+    Ensuite on (re)bascule dans l'environement virtuel et on lance les migrations
 
         $ source /env/bin/activate
-        (env)$ ./manage.py check # pas de bétise qui traine
+        (env)$ ./manage.py check # pas de bêtise qui traine
         (env)$ ./manage.py makemigrations
         (env)$ ./manage.py migrate
 
@@ -126,17 +132,21 @@ Il est possible de travailler sur une instance Docker.
     
         $ git clone git@gitlab.crans.org:bde/nk20.git
 
-2. Dans le fichier `docker_compose.yml`, qu'on suppose déjà configuré,
+2. Copiez le fichier `.env_example` à la racine du projet vers le fichier `.env`,
+et  mettez à jour vos variables d'environnement
+
+3. Dans le fichier `docker_compose.yml`, qu'on suppose déjà configuré,
    ajouter les lignes suivantes, en les adaptant à la configuration voulue :
 
         nk20:
           build: /chemin/vers/nk20
           volumes:
             - /chemin/vers/nk20:/code/
+          env_file: /chemin/vers/nk20/.env
           restart: always
           labels:
-            - traefik.domain=ndd.exemple.com
-            - traefik.frontend.rule=Host:ndd.exemple.com
+            - traefik.domain=ndd.example.com
+            - traefik.frontend.rule=Host:ndd.example.com
             - traefik.port=8000
 
 3. Enjoy :
@@ -159,17 +169,20 @@ un serveur de développement par exemple sur son ordinateur.
         $ source venv/bin/activate
         (env)$ pip install -r requirements.txt
 
-3. Migrations et chargement des données initiales :
+3. Copier le fichier `.env_example` vers `.env` à la racine du projet et mettre à jour
+ce qu'il faut
+
+4. Migrations et chargement des données initiales :
 
         (env)$ ./manage.py makemigrations
         (env)$ ./manage.py migrate
         (env)$ ./manage.py loaddata initial
 
-4. Créer un super-utilisateur :
+5. Créer un super-utilisateur :
 
         (env)$ ./manage.py createsuperuser
 
-5. Enjoy :
+6. Enjoy :
 
         (env)$ ./manage.py runserver 0.0.0.0:8000
 
@@ -184,4 +197,4 @@ Il est disponible [ici](https://wiki.crans.org/NoteKfet/NoteKfet2018/CdC).
 ## Documentation
 
 La documentation est générée par django et son module admindocs.
-**Commenter votre code !**
+**Commentez votre code !**
diff --git a/apps/activity/admin.py b/apps/activity/admin.py
index 5ceb4e8146bcf1c1392059117c34e2497d04c73a..0529d3064436cd00b2cf40b2ec6101f6e0e2bb27 100644
--- a/apps/activity/admin.py
+++ b/apps/activity/admin.py
@@ -11,7 +11,7 @@ class ActivityAdmin(admin.ModelAdmin):
     Admin customisation for Activity
     """
     list_display = ('name', 'activity_type', 'organizer')
-    list_filter = ('activity_type', )
+    list_filter = ('activity_type',)
     search_fields = ['name', 'organizer__name']
 
     # Organize activities by start date
diff --git a/apps/activity/api/serializers.py b/apps/activity/api/serializers.py
index 0b9302f17aa24b961be3f792a7afb7a87c6b5e8a..514515ef7a94c0081f39e5374aae42c1b86f6433 100644
--- a/apps/activity/api/serializers.py
+++ b/apps/activity/api/serializers.py
@@ -11,6 +11,7 @@ class ActivityTypeSerializer(serializers.ModelSerializer):
     REST API Serializer for Activity types.
     The djangorestframework plugin will analyse the model `ActivityType` and parse all fields in the API.
     """
+
     class Meta:
         model = ActivityType
         fields = '__all__'
@@ -21,6 +22,7 @@ class ActivitySerializer(serializers.ModelSerializer):
     REST API Serializer for Activities.
     The djangorestframework plugin will analyse the model `Activity` and parse all fields in the API.
     """
+
     class Meta:
         model = Activity
         fields = '__all__'
@@ -31,6 +33,7 @@ class GuestSerializer(serializers.ModelSerializer):
     REST API Serializer for Guests.
     The djangorestframework plugin will analyse the model `Guest` and parse all fields in the API.
     """
+
     class Meta:
         model = Guest
         fields = '__all__'
diff --git a/apps/activity/api/views.py b/apps/activity/api/views.py
index 5683d458011f3acba0e06bda1b0e6dc575ea513b..4ee2194d06c38cad2726feb8536c149248aa336b 100644
--- a/apps/activity/api/views.py
+++ b/apps/activity/api/views.py
@@ -1,10 +1,11 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
-
+from django_filters.rest_framework import DjangoFilterBackend
 from rest_framework import viewsets
+from rest_framework.filters import SearchFilter
 
-from ..models import ActivityType, Activity, Guest
 from .serializers import ActivityTypeSerializer, ActivitySerializer, GuestSerializer
+from ..models import ActivityType, Activity, Guest
 
 
 class ActivityTypeViewSet(viewsets.ModelViewSet):
@@ -15,6 +16,8 @@ class ActivityTypeViewSet(viewsets.ModelViewSet):
     """
     queryset = ActivityType.objects.all()
     serializer_class = ActivityTypeSerializer
+    filter_backends = [DjangoFilterBackend]
+    filterset_fields = ['name', 'can_invite', ]
 
 
 class ActivityViewSet(viewsets.ModelViewSet):
@@ -25,6 +28,8 @@ class ActivityViewSet(viewsets.ModelViewSet):
     """
     queryset = Activity.objects.all()
     serializer_class = ActivitySerializer
+    filter_backends = [DjangoFilterBackend]
+    filterset_fields = ['name', 'description', 'activity_type', ]
 
 
 class GuestViewSet(viewsets.ModelViewSet):
@@ -35,3 +40,5 @@ class GuestViewSet(viewsets.ModelViewSet):
     """
     queryset = Guest.objects.all()
     serializer_class = GuestSerializer
+    filter_backends = [SearchFilter]
+    search_fields = ['$name', ]
diff --git a/apps/api/urls.py b/apps/api/urls.py
index 7e59a8c0acfc87ff928d4392fd06f46be41ac769..95ed5f99e9005850fed2822d3ef081bf5ec55b85 100644
--- a/apps/api/urls.py
+++ b/apps/api/urls.py
@@ -3,10 +3,14 @@
 
 from django.conf.urls import url, include
 from django.contrib.auth.models import User
+from django.contrib.contenttypes.models import ContentType
+from django_filters.rest_framework import DjangoFilterBackend
 from rest_framework import routers, serializers, viewsets
+from rest_framework.filters import SearchFilter
 from activity.api.urls import register_activity_urls
 from member.api.urls import register_members_urls
 from note.api.urls import register_note_urls
+from logs.api.urls import register_logs_urls
 
 
 class UserSerializer(serializers.ModelSerializer):
@@ -14,6 +18,7 @@ class UserSerializer(serializers.ModelSerializer):
     REST API Serializer for Users.
     The djangorestframework plugin will analyse the model `User` and parse all fields in the API.
     """
+
     class Meta:
         model = User
         exclude = (
@@ -23,6 +28,17 @@ class UserSerializer(serializers.ModelSerializer):
         )
 
 
+class ContentTypeSerializer(serializers.ModelSerializer):
+    """
+    REST API Serializer for Users.
+    The djangorestframework plugin will analyse the model `User` and parse all fields in the API.
+    """
+
+    class Meta:
+        model = ContentType
+        fields = '__all__'
+
+
 class UserViewSet(viewsets.ModelViewSet):
     """
     REST API View set.
@@ -31,15 +47,30 @@ class UserViewSet(viewsets.ModelViewSet):
     """
     queryset = User.objects.all()
     serializer_class = UserSerializer
+    filter_backends = [DjangoFilterBackend, SearchFilter]
+    filterset_fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_superuser', 'is_staff', 'is_active', ]
+    search_fields = ['$username', '$first_name', '$last_name', ]
+
+
+class ContentTypeViewSet(viewsets.ReadOnlyModelViewSet):
+    """
+    REST API View set.
+    The djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer,
+    then render it on /api/users/
+    """
+    queryset = ContentType.objects.all()
+    serializer_class = ContentTypeSerializer
 
 
 # Routers provide an easy way of automatically determining the URL conf.
 # Register each app API router and user viewset
 router = routers.DefaultRouter()
+router.register('models', ContentTypeViewSet)
 router.register('user', UserViewSet)
 register_members_urls(router, 'members')
 register_activity_urls(router, 'activity')
 register_note_urls(router, 'note')
+register_logs_urls(router, 'logs')
 
 app_name = 'api'
 
diff --git a/apps/logs/api/__init__.py b/apps/logs/api/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/apps/logs/api/serializers.py b/apps/logs/api/serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..c76e3a5d8d43c0f52ef17f14971d88f864cb6580
--- /dev/null
+++ b/apps/logs/api/serializers.py
@@ -0,0 +1,19 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from rest_framework import serializers
+
+from ..models import Changelog
+
+
+class ChangelogSerializer(serializers.ModelSerializer):
+    """
+    REST API Serializer for Changelog types.
+    The djangorestframework plugin will analyse the model `Changelog` and parse all fields in the API.
+    """
+
+    class Meta:
+        model = Changelog
+        fields = '__all__'
+        # noinspection PyProtectedMember
+        read_only_fields = [f.name for f in model._meta.get_fields()]  # Changelogs are read-only protected
diff --git a/apps/logs/api/urls.py b/apps/logs/api/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a0ceaa8cec01b81f5bf5646bec374ab16199a42
--- /dev/null
+++ b/apps/logs/api/urls.py
@@ -0,0 +1,11 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from .views import ChangelogViewSet
+
+
+def register_logs_urls(router, path):
+    """
+    Configure router for Activity REST API.
+    """
+    router.register(path, ChangelogViewSet)
diff --git a/apps/logs/api/views.py b/apps/logs/api/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c47b7a2a5daf50ef5d56d1a487d1b9b4050a98c
--- /dev/null
+++ b/apps/logs/api/views.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django_filters.rest_framework import DjangoFilterBackend
+from rest_framework import viewsets
+from rest_framework.filters import OrderingFilter
+
+from .serializers import ChangelogSerializer
+from ..models import Changelog
+
+
+class ChangelogViewSet(viewsets.ReadOnlyModelViewSet):
+    """
+    REST API View set.
+    The djangorestframework plugin will get all `Changelog` objects, serialize it to JSON with the given serializer,
+    then render it on /api/logs/
+    """
+    queryset = Changelog.objects.all()
+    serializer_class = ChangelogSerializer
+    filter_backends = [DjangoFilterBackend, OrderingFilter]
+    filterset_fields = ['model', 'action', "instance_pk", 'user', 'ip', ]
+    ordering_fields = ['timestamp', ]
+    ordering = ['-timestamp', ]
diff --git a/apps/logs/apps.py b/apps/logs/apps.py
index f48820c7b91f2e9818aa179ce94ff2960d07884e..239f86cf45cd58326ae68e2349b0c1e6421dd307 100644
--- a/apps/logs/apps.py
+++ b/apps/logs/apps.py
@@ -2,6 +2,7 @@
 # SPDX-License-Identifier: GPL-3.0-or-later
 
 from django.apps import AppConfig
+from django.db.models.signals import pre_save, post_save, post_delete
 from django.utils.translation import gettext_lazy as _
 
 
@@ -11,4 +12,7 @@ class LogsConfig(AppConfig):
 
     def ready(self):
         # noinspection PyUnresolvedReferences
-        import logs.signals
+        from . import signals
+        pre_save.connect(signals.pre_save_object)
+        post_save.connect(signals.save_object)
+        post_delete.connect(signals.delete_object)
diff --git a/apps/logs/middlewares.py b/apps/logs/middlewares.py
new file mode 100644
index 0000000000000000000000000000000000000000..77f749b9da0663a4a3fd4e1b17aa8c8967fc5372
--- /dev/null
+++ b/apps/logs/middlewares.py
@@ -0,0 +1,55 @@
+# 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.models import AnonymousUser
+
+from threading import local
+
+
+USER_ATTR_NAME = getattr(settings, 'LOCAL_USER_ATTR_NAME', '_current_user')
+IP_ATTR_NAME = getattr(settings, 'LOCAL_IP_ATTR_NAME', '_current_ip')
+
+_thread_locals = local()
+
+
+def _set_current_user_and_ip(user=None, ip=None):
+    setattr(_thread_locals, USER_ATTR_NAME, user)
+    setattr(_thread_locals, IP_ATTR_NAME, ip)
+
+
+def get_current_user():
+    return getattr(_thread_locals, USER_ATTR_NAME, None)
+
+
+def get_current_ip():
+    return getattr(_thread_locals, IP_ATTR_NAME, None)
+
+
+def get_current_authenticated_user():
+    current_user = get_current_user()
+    if isinstance(current_user, AnonymousUser):
+        return None
+    return current_user
+
+
+class LogsMiddleware(object):
+    """
+    This middleware get the current user with his or her IP address on each request.
+    """
+
+    def __init__(self, get_response):
+        self.get_response = get_response
+
+    def __call__(self, request):
+        user = request.user
+        if 'HTTP_X_FORWARDED_FOR' in request.META:
+            ip = request.META.get('HTTP_X_FORWARDED_FOR')
+        else:
+            ip = request.META.get('REMOTE_ADDR')
+
+        _set_current_user_and_ip(user, ip)
+        response = self.get_response(request)
+        _set_current_user_and_ip(None, None)
+
+        return response
diff --git a/apps/logs/models.py b/apps/logs/models.py
index 337315bb1b215e840b581bcc157bb9f9cab0ac3b..10e2651f2f8475c4241c05a37f9c56b37c8d5bb5 100644
--- a/apps/logs/models.py
+++ b/apps/logs/models.py
@@ -1,16 +1,16 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
-from django.contrib.contenttypes.models import ContentType
-from django.utils.translation import gettext_lazy as _
 from django.conf import settings
+from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
 from django.db import models
+from django.utils.translation import gettext_lazy as _
 
 
 class Changelog(models.Model):
     """
-    Store each modification on the database (except sessions and logging),
+    Store each modification in the database (except sessions and logging),
     including creating, editing and deleting models.
     """
 
@@ -56,6 +56,12 @@ class Changelog(models.Model):
         max_length=16,
         null=False,
         blank=False,
+        choices=[
+            ('create', _('create')),
+            ('edit', _('edit')),
+            ('delete', _('delete')),
+        ],
+        default='edit',
         verbose_name=_('action'),
     )
 
diff --git a/apps/logs/signals.py b/apps/logs/signals.py
index 13194e5b3b175029561488daf8f460cee35d8726..fb17157a9fc14f8b22dc99bab586b95b60e6f834 100644
--- a/apps/logs/signals.py
+++ b/apps/logs/signals.py
@@ -1,66 +1,40 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
-import inspect
-
 from django.contrib.contenttypes.models import ContentType
-from django.core import serializers
-from django.db.models.signals import pre_save, post_save, post_delete
-from django.dispatch import receiver
-from .models import Changelog
-
+from rest_framework.renderers import JSONRenderer
+from rest_framework.serializers import ModelSerializer
 
-def get_request_in_signal(sender):
-    req = None
-    for entry in reversed(inspect.stack()):
-        try:
-            req = entry[0].f_locals['request']
-            # Check if there is a user
-            # noinspection PyStatementEffect
-            req.user
-            break
-        except:
-            pass
+import getpass
 
-    if not req:
-        print("WARNING: Attempt to save " + str(sender) + " with no user")
+from note.models import NoteUser, Alias
 
-    return req
+from .middlewares import get_current_authenticated_user, get_current_ip
+from .models import Changelog
 
 
-def get_user_and_ip(sender):
-    req = get_request_in_signal(sender)
-    try:
-        user = req.user
-        if 'HTTP_X_FORWARDED_FOR' in req.META:
-            ip = req.META.get('HTTP_X_FORWARDED_FOR')
-        else:
-            ip = req.META.get('REMOTE_ADDR')
-    except:
-        user = None
-        ip = None
-    return user, ip
+# Ces modèles ne nécessitent pas de logs
+EXCLUDED = [
+    'admin.logentry',
+    'authtoken.token',
+    'cas_server.proxygrantingticket',
+    'cas_server.proxyticket',
+    'cas_server.serviceticket',
+    'cas_server.user',
+    'cas_server.userattributes',
+    'contenttypes.contenttype',
+    'logs.changelog',  # Never remove this line
+    'migrations.migration',
+    'note.note'  # We only store the subclasses
+    'note.transaction',
+    'sessions.session',
+]
 
 
-EXCLUDED = [
-        'admin.logentry',
-        'authtoken.token',
-        'cas_server.user',
-        'cas_server.userattributes',
-        'contenttypes.contenttype',
-        'logs.changelog',
-        'migrations.migration',
-        'note.noteuser',
-        'note.noteclub',
-        'note.notespecial',
-        'sessions.session',
-        'reversion.revision',
-        'reversion.version',
-    ]
-
-
-@receiver(pre_save)
 def pre_save_object(sender, instance, **kwargs):
+    """
+    Before a model get saved, we get the previous instance that is currently in the database
+    """
     qs = sender.objects.filter(pk=instance.pk).all()
     if qs.exists():
         instance._previous = qs.get()
@@ -68,30 +42,51 @@ def pre_save_object(sender, instance, **kwargs):
         instance._previous = None
 
 
-@receiver(post_save)
 def save_object(sender, instance, **kwargs):
+    """
+    Each time a model is saved, an entry in the table `Changelog` is added in the database
+    in order to store each modification made
+    """
     # noinspection PyProtectedMember
     if instance._meta.label_lower in EXCLUDED:
         return
 
+    # noinspection PyProtectedMember
     previous = instance._previous
 
-    user, ip = get_user_and_ip(sender)
-
-    from django.contrib.auth.models import AnonymousUser
-    if isinstance(user, AnonymousUser):
-        user = None
+    # Si un utilisateur est connecté, on récupère l'utilisateur courant ainsi que son adresse IP
+    user, ip = get_current_authenticated_user(), get_current_ip()
+
+    if user is None:
+        # Si la modification n'a pas été faite via le client Web, on suppose que c'est du à `manage.py`
+        # On récupère alors l'utilisateur·trice connecté·e à la VM, et on récupère la note associée
+        # IMPORTANT : l'utilisateur dans la VM doit être un des alias note du respo info
+        ip = "127.0.0.1"
+        username = Alias.normalize(getpass.getuser())
+        note = NoteUser.objects.filter(alias__normalized_name=username)
+        # if not note.exists():
+        #     print("WARNING: A model attempted to be saved in the DB, but the actor is unknown: " + username)
+        # else:
+        if note.exists():
+            user = note.get().user
 
+    # noinspection PyProtectedMember
     if user is not None and instance._meta.label_lower == "auth.user" and previous:
-        # Don't save last login modifications
+        # On n'enregistre pas les connexions
         if instance.last_login != previous.last_login:
             return
 
-    previous_json = serializers.serialize('json', [previous, ])[1:-1] if previous else None
-    instance_json = serializers.serialize('json', [instance, ])[1:-1]
+    # On crée notre propre sérialiseur JSON pour pouvoir sauvegarder les modèles
+    class CustomSerializer(ModelSerializer):
+        class Meta:
+            model = instance.__class__
+            fields = '__all__'
+
+    previous_json = JSONRenderer().render(CustomSerializer(previous).data).decode("UTF-8") if previous else None
+    instance_json = JSONRenderer().render(CustomSerializer(instance).data).decode("UTF-8")
 
     if previous_json == instance_json:
-        # No modification
+        # Pas de log s'il n'y a pas de modification
         return
 
     Changelog.objects.create(user=user,
@@ -104,15 +99,38 @@ def save_object(sender, instance, **kwargs):
                              ).save()
 
 
-@receiver(post_delete)
 def delete_object(sender, instance, **kwargs):
+    """
+    Each time a model is deleted, an entry in the table `Changelog` is added in the database
+    """
     # noinspection PyProtectedMember
     if instance._meta.label_lower in EXCLUDED:
         return
 
-    user, ip = get_user_and_ip(sender)
+    # Si un utilisateur est connecté, on récupère l'utilisateur courant ainsi que son adresse IP
+    user, ip = get_current_authenticated_user(), get_current_ip()
+
+    if user is None:
+        # Si la modification n'a pas été faite via le client Web, on suppose que c'est du à `manage.py`
+        # On récupère alors l'utilisateur·trice connecté·e à la VM, et on récupère la note associée
+        # IMPORTANT : l'utilisateur dans la VM doit être un des alias note du respo info
+        ip = "127.0.0.1"
+        username = Alias.normalize(getpass.getuser())
+        note = NoteUser.objects.filter(alias__normalized_name=username)
+        # if not note.exists():
+        #     print("WARNING: A model attempted to be saved in the DB, but the actor is unknown: " + username)
+        # else:
+        if note.exists():
+            user = note.get().user
+
+    # On crée notre propre sérialiseur JSON pour pouvoir sauvegarder les modèles
+    class CustomSerializer(ModelSerializer):
+        class Meta:
+            model = instance.__class__
+            fields = '__all__'
+
+    instance_json = JSONRenderer().render(CustomSerializer(instance).data).decode("UTF-8")
 
-    instance_json = serializers.serialize('json', [instance, ])[1:-1]
     Changelog.objects.create(user=user,
                              ip=ip,
                              model=ContentType.objects.get_for_model(instance),
diff --git a/apps/member/admin.py b/apps/member/admin.py
index 70b004594e4a20c60c63d076857ba5e51173d466..48fbc035054c22ed01e900381766570359fb5ead 100644
--- a/apps/member/admin.py
+++ b/apps/member/admin.py
@@ -18,9 +18,9 @@ class ProfileInline(admin.StackedInline):
 
 
 class CustomUserAdmin(UserAdmin):
-    inlines = (ProfileInline, )
+    inlines = (ProfileInline,)
     list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
-    list_select_related = ('profile', )
+    list_select_related = ('profile',)
     form = ProfileForm
 
     def get_inline_instances(self, request, obj=None):
diff --git a/apps/member/api/serializers.py b/apps/member/api/serializers.py
index f4df67993dcfd3b4728d02d240d0e59aeb853a12..962841aebfac6d361c07dc87ebb61dcd0f253b53 100644
--- a/apps/member/api/serializers.py
+++ b/apps/member/api/serializers.py
@@ -11,6 +11,7 @@ class ProfileSerializer(serializers.ModelSerializer):
     REST API Serializer for Profiles.
     The djangorestframework plugin will analyse the model `Profile` and parse all fields in the API.
     """
+
     class Meta:
         model = Profile
         fields = '__all__'
@@ -21,6 +22,7 @@ class ClubSerializer(serializers.ModelSerializer):
     REST API Serializer for Clubs.
     The djangorestframework plugin will analyse the model `Club` and parse all fields in the API.
     """
+
     class Meta:
         model = Club
         fields = '__all__'
@@ -31,6 +33,7 @@ class RoleSerializer(serializers.ModelSerializer):
     REST API Serializer for Roles.
     The djangorestframework plugin will analyse the model `Role` and parse all fields in the API.
     """
+
     class Meta:
         model = Role
         fields = '__all__'
@@ -41,6 +44,7 @@ class MembershipSerializer(serializers.ModelSerializer):
     REST API Serializer for Memberships.
     The djangorestframework plugin will analyse the model `Memberships` and parse all fields in the API.
     """
+
     class Meta:
         model = Membership
         fields = '__all__'
diff --git a/apps/member/api/views.py b/apps/member/api/views.py
index 79ba4c12afca3dee2c00445bd280d33b46934101..c85df90330ae00463306d30b6bec3da8c22abc97 100644
--- a/apps/member/api/views.py
+++ b/apps/member/api/views.py
@@ -2,9 +2,10 @@
 # SPDX-License-Identifier: GPL-3.0-or-later
 
 from rest_framework import viewsets
+from rest_framework.filters import SearchFilter
 
-from ..models import Profile, Club, Role, Membership
 from .serializers import ProfileSerializer, ClubSerializer, RoleSerializer, MembershipSerializer
+from ..models import Profile, Club, Role, Membership
 
 
 class ProfileViewSet(viewsets.ModelViewSet):
@@ -25,6 +26,8 @@ class ClubViewSet(viewsets.ModelViewSet):
     """
     queryset = Club.objects.all()
     serializer_class = ClubSerializer
+    filter_backends = [SearchFilter]
+    search_fields = ['$name', ]
 
 
 class RoleViewSet(viewsets.ModelViewSet):
@@ -35,6 +38,8 @@ class RoleViewSet(viewsets.ModelViewSet):
     """
     queryset = Role.objects.all()
     serializer_class = RoleSerializer
+    filter_backends = [SearchFilter]
+    search_fields = ['$name', ]
 
 
 class MembershipViewSet(viewsets.ModelViewSet):
diff --git a/apps/member/filters.py b/apps/member/filters.py
index 418e52fc680fb7888574924eb06623176a03680c..951723e86b6d0fecd3c6c728836cef43331c42fc 100644
--- a/apps/member/filters.py
+++ b/apps/member/filters.py
@@ -1,11 +1,11 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
-from django_filters import FilterSet, CharFilter
-from django.contrib.auth.models import User
-from django.db.models import CharField
 from crispy_forms.helper import FormHelper
 from crispy_forms.layout import Layout, Submit
+from django.contrib.auth.models import User
+from django.db.models import CharField
+from django_filters import FilterSet, CharFilter
 
 
 class UserFilter(FilterSet):
diff --git a/apps/member/forms.py b/apps/member/forms.py
index abb35cd9cb2d4493e849ffcfd9315a5f14230c4a..d2134cddfda74bf1825b4d672c2c0d921744a961 100644
--- a/apps/member/forms.py
+++ b/apps/member/forms.py
@@ -1,23 +1,22 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
+from crispy_forms.bootstrap import Div
+from crispy_forms.helper import FormHelper
+from crispy_forms.layout import Layout
 from dal import autocomplete
+from django import forms
 from django.contrib.auth.forms import UserCreationForm
 from django.contrib.auth.models import User
-from django import forms
 
 from .models import Profile, Club, Membership
 
-from crispy_forms.helper import FormHelper
-from crispy_forms.bootstrap import Div
-from crispy_forms.layout import Layout
-
 
 class SignUpForm(UserCreationForm):
-    def __init__(self,*args,**kwargs):
-        super().__init__(*args,**kwargs)
+    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'].widget.attrs.update({"autofocus": "autofocus"})
 
     class Meta:
         model = User
@@ -28,6 +27,7 @@ class ProfileForm(forms.ModelForm):
     """
     A form for the extras field provided by the :model:`member.Profile` model.
     """
+
     class Meta:
         model = Profile
         fields = '__all__'
@@ -42,7 +42,7 @@ class ClubForm(forms.ModelForm):
 
 class AddMembersForm(forms.Form):
     class Meta:
-        fields = ('', )
+        fields = ('',)
 
 
 class MembershipForm(forms.ModelForm):
@@ -54,13 +54,13 @@ class MembershipForm(forms.ModelForm):
         # et récupère les noms d'utilisateur valides
         widgets = {
             'user':
-            autocomplete.ModelSelect2(
-                url='member:user_autocomplete',
-                attrs={
-                    'data-placeholder': 'Nom ...',
-                    'data-minimum-input-length': 1,
-                },
-            ),
+                autocomplete.ModelSelect2(
+                    url='member:user_autocomplete',
+                    attrs={
+                        'data-placeholder': 'Nom ...',
+                        'data-minimum-input-length': 1,
+                    },
+                ),
         }
 
 
diff --git a/apps/member/models.py b/apps/member/models.py
index 1ca82af0c7525a893f5df325e67349a78211e767..24e58830f6772249ae5481a1481358aea8dc920c 100644
--- a/apps/member/models.py
+++ b/apps/member/models.py
@@ -5,8 +5,8 @@ import datetime
 
 from django.conf import settings
 from django.db import models
-from django.utils.translation import gettext_lazy as _
 from django.urls import reverse, reverse_lazy
+from django.utils.translation import gettext_lazy as _
 
 
 class Profile(models.Model):
@@ -48,9 +48,10 @@ class Profile(models.Model):
     class Meta:
         verbose_name = _('user profile')
         verbose_name_plural = _('user profile')
+        indexes = [models.Index(fields=['user'])]
 
     def get_absolute_url(self):
-        return reverse('user_detail', args=(self.pk, ))
+        return reverse('user_detail', args=(self.pk,))
 
 
 
@@ -100,7 +101,7 @@ class Club(models.Model):
         return self.name
 
     def get_absolute_url(self):
-        return reverse_lazy('member:club_detail', args=(self.pk, ))
+        return reverse_lazy('member:club_detail', args=(self.pk,))
 
 
 class Role(models.Model):
@@ -161,7 +162,7 @@ class Membership(models.Model):
     class Meta:
         verbose_name = _('membership')
         verbose_name_plural = _('memberships')
-
+        indexes = [models.Index(fields=['user'])]
 
 class RolePermissions(models.Model):
     """
diff --git a/apps/member/signals.py b/apps/member/signals.py
index b17b3ae84dfdee1105204a863f1df53ed64c8cc1..2b03e3ced7fac6f67efaa9f509d978c0df8e7987 100644
--- a/apps/member/signals.py
+++ b/apps/member/signals.py
@@ -1,6 +1,7 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
+
 def save_user_profile(instance, created, raw, **_kwargs):
     """
     Hook to create and save a profile when an user is updated if it is not registered with the signup form
diff --git a/apps/member/views.py b/apps/member/views.py
index 870079cc4a387d2b61d4795512f8e8b731fcc657..dacfde3331439473a9182abf49bf01bb6ffc24a2 100644
--- a/apps/member/views.py
+++ b/apps/member/views.py
@@ -1,33 +1,33 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
+import io
+
+from PIL import Image
+from dal import autocomplete
+from django.conf import settings
+from django.contrib import messages
 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.http import HttpResponseRedirect
 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,DeleteView
+from django.views.generic import CreateView, DetailView, UpdateView, TemplateView, DeleteView
 from django.views.generic.edit import FormMixin
-from django.contrib.auth.models import User
-from django.contrib import messages
-from django.urls import reverse_lazy
-from django.http import HttpResponseRedirect
-from django.db.models import Q
-from django.core.exceptions import ValidationError
-from django.conf import settings
 from django_tables2.views import SingleTableView
 from rest_framework.authtoken.models import Token
-from dal import autocomplete
-from PIL import Image
-import io
-
+from note.forms import AliasForm, ImageForm
 from note.models import Alias, NoteUser
 from note.models.transactions import Transaction
 from note.tables import HistoryTable, AliasTable
-from note.forms import AliasForm, ImageForm
 
-from .models import Profile, Club, Membership
+from .filters import UserFilter, UserFilterFormHelper
 from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, MemberFormSet, FormSetHelper
+from .models import Club, Membership
 from .tables import ClubTable, UserTable
-from .filters import UserFilter, UserFilterFormHelper
 
 
 class UserCreateView(CreateView):
@@ -49,10 +49,10 @@ class UserCreateView(CreateView):
     def form_valid(self, form):
         profile_form = ProfileForm(self.request.POST)
         if form.is_valid() and profile_form.is_valid():
-            user = form.save()
-            profile = profile_form.save(commit=False)
-            profile.user = user
-            profile.save()
+            user = form.save(commit=False)
+            user.profile = profile_form.save(commit=False)
+            user.save()
+            user.profile.save()
         return super().form_valid(form)
 
 
@@ -109,7 +109,7 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
             return reverse_lazy('member:user_detail',
                                 kwargs={'pk': kwargs['id']})
         else:
-            return reverse_lazy('member:user_detail', args=(self.object.id, ))
+            return reverse_lazy('member:user_detail', args=(self.object.id,))
 
 
 class UserDetailView(LoginRequiredMixin, DetailView):
@@ -124,7 +124,7 @@ class UserDetailView(LoginRequiredMixin, DetailView):
         context = super().get_context_data(**kwargs)
         user = context['user_object']
         history_list = \
-            Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note))
+            Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note)).order_by("-id")
         context['history_list'] = HistoryTable(history_list)
         club_list = \
             Membership.objects.all().filter(user=user).only("club")
@@ -157,13 +157,14 @@ class UserListView(LoginRequiredMixin, SingleTableView):
         context["filter"] = self.filter
         return context
 
-class AliasView(LoginRequiredMixin,FormMixin,DetailView):
+
+class AliasView(LoginRequiredMixin, FormMixin, DetailView):
     model = User
     template_name = 'member/profile_alias.html'
     context_object_name = 'user_object'
     form_class = AliasForm
 
-    def get_context_data(self,**kwargs):
+    def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
         note = context['user_object'].note
         context["aliases"] = AliasTable(note.alias_set.all())
@@ -172,7 +173,7 @@ class AliasView(LoginRequiredMixin,FormMixin,DetailView):
     def get_success_url(self):
         return reverse_lazy('member:user_alias', kwargs={'pk': self.object.id})
 
-    def post(self,request,*args,**kwargs):
+    def post(self, request, *args, **kwargs):
         self.object = self.get_object()
         form = self.get_form()
         if form.is_valid():
@@ -186,42 +187,45 @@ class AliasView(LoginRequiredMixin,FormMixin,DetailView):
         alias.save()
         return super().form_valid(form)
 
+
 class DeleteAliasView(LoginRequiredMixin, DeleteView):
     model = Alias
 
-    def delete(self,request,*args,**kwargs):
+    def delete(self, request, *args, **kwargs):
         try:
             self.object = self.get_object()
             self.object.delete()
         except ValidationError as e:
             # TODO: pass message to redirected view.
-            messages.error(self.request,str(e))
+            messages.error(self.request, str(e))
         else:
-            messages.success(self.request,_("Alias successfully deleted"))
+            messages.success(self.request, _("Alias successfully deleted"))
         return HttpResponseRedirect(self.get_success_url())
-    
+
     def get_success_url(self):
         print(self.request)
-        return reverse_lazy('member:user_alias',kwargs={'pk':self.object.note.user.pk})
+        return reverse_lazy('member:user_alias', kwargs={'pk': self.object.note.user.pk})
 
     def get(self, request, *args, **kwargs):
         return self.post(request, *args, **kwargs)
 
+
 class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
     model = User
     template_name = 'member/profile_picture_update.html'
     context_object_name = 'user_object'
     form_class = ImageForm
-    def get_context_data(self,*args,**kwargs):
-        context = super().get_context_data(*args,**kwargs)
-        context['form'] = self.form_class(self.request.POST,self.request.FILES)
+
+    def get_context_data(self, *args, **kwargs):
+        context = super().get_context_data(*args, **kwargs)
+        context['form'] = self.form_class(self.request.POST, self.request.FILES)
         return context
-        
+
     def get_success_url(self):
         return reverse_lazy('member:user_detail', kwargs={'pk': self.object.id})
 
-    def post(self,request,*args,**kwargs):
-        form  = self.get_form()
+    def post(self, request, *args, **kwargs):
+        form = self.get_form()
         self.object = self.get_object()
         if form.is_valid():
             return self.form_valid(form)
@@ -230,7 +234,7 @@ class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
             print(form)
             return self.form_invalid(form)
 
-    def form_valid(self,form):
+    def form_valid(self, form):
         image_field = form.cleaned_data['image']
         x = form.cleaned_data['x']
         y = form.cleaned_data['y']
@@ -238,23 +242,24 @@ class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
         h = form.cleaned_data['height']
         # image crop and resize
         image_file = io.BytesIO(image_field.read())
-        ext = image_field.name.split('.')[-1]
+        # ext = image_field.name.split('.')[-1].lower()
+        # TODO: support GIF format
         image = Image.open(image_file)
-        image = image.crop((x, y, x+w, y+h))
+        image = image.crop((x, y, x + w, y + h))
         image_clean = image.resize((settings.PIC_WIDTH,
-                             settings.PIC_RATIO*settings.PIC_WIDTH),
-                             Image.ANTIALIAS)
+                                    settings.PIC_RATIO * settings.PIC_WIDTH),
+                                   Image.ANTIALIAS)
         image_file = io.BytesIO()
-        image_clean.save(image_file,ext)
+        image_clean.save(image_file, "PNG")
         image_field.file = image_file
         # renaming
-        filename = "{}_pic.{}".format(self.object.note.pk, ext)
+        filename = "{}_pic.png".format(self.object.note.pk)
         image_field.name = filename
         self.object.note.display_image = image_field
         self.object.note.save()
         return super().form_valid(form)
 
-    
+
 class ManageAuthTokens(LoginRequiredMixin, TemplateView):
     """
     Affiche le jeton d'authentification, et permet de le regénérer
@@ -282,6 +287,7 @@ class UserAutocomplete(autocomplete.Select2QuerySetView):
     """
     Auto complete users by usernames
     """
+
     def get_queryset(self):
         """
         Quand une personne cherche un utilisateur par pseudo, une requête est envoyée sur l'API dédiée à l'auto-complétion.
@@ -294,7 +300,7 @@ class UserAutocomplete(autocomplete.Select2QuerySetView):
         qs = User.objects.all()
 
         if self.q:
-            qs = qs.filter(username__regex=self.q)
+            qs = qs.filter(username__regex="^" + self.q)
 
         return qs
 
@@ -330,7 +336,7 @@ class ClubDetailView(LoginRequiredMixin, DetailView):
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
         club = context["club"]
-        club_transactions =  \
+        club_transactions = \
             Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))
         context['history_list'] = HistoryTable(club_transactions)
         club_member = \
diff --git a/apps/note/admin.py b/apps/note/admin.py
index 52c1cc1752066221c27782a303e975e497d278cf..a09286412591248b9d2fefb27f7715f70b7dbe4d 100644
--- a/apps/note/admin.py
+++ b/apps/note/admin.py
@@ -47,11 +47,11 @@ class NoteClubAdmin(PolymorphicChildModelAdmin):
     """
     Child for a club note, see NoteAdmin
     """
-    inlines = (AliasInlines, )
+    inlines = (AliasInlines,)
 
     # We can't change club after creation or the balance
     readonly_fields = ('club', 'balance')
-    search_fields = ('club', )
+    search_fields = ('club',)
 
     def has_add_permission(self, request):
         """
@@ -71,7 +71,7 @@ class NoteSpecialAdmin(PolymorphicChildModelAdmin):
     """
     Child for a special note, see NoteAdmin
     """
-    readonly_fields = ('balance', )
+    readonly_fields = ('balance',)
 
 
 @admin.register(NoteUser)
@@ -79,7 +79,7 @@ class NoteUserAdmin(PolymorphicChildModelAdmin):
     """
     Child for an user note, see NoteAdmin
     """
-    inlines = (AliasInlines, )
+    inlines = (AliasInlines,)
 
     # We can't change user after creation or the balance
     readonly_fields = ('user', 'balance')
@@ -133,7 +133,7 @@ class TransactionAdmin(PolymorphicParentModelAdmin):
         Else the amount of money would not be transferred
         """
         if obj:  # user is editing an existing object
-            return 'created_at', 'source', 'destination', 'quantity',\
+            return 'created_at', 'source', 'destination', 'quantity', \
                    'amount'
         return []
 
@@ -143,9 +143,9 @@ class TransactionTemplateAdmin(admin.ModelAdmin):
     """
     Admin customisation for TransactionTemplate
     """
-    list_display = ('name', 'poly_destination', 'amount', 'category', 'display', )
+    list_display = ('name', 'poly_destination', 'amount', 'category', 'display',)
     list_filter = ('category', 'display')
-    autocomplete_fields = ('destination', )
+    autocomplete_fields = ('destination',)
 
     def poly_destination(self, obj):
         """
@@ -161,5 +161,5 @@ class TemplateCategoryAdmin(admin.ModelAdmin):
     """
     Admin customisation for TransactionTemplate
     """
-    list_display = ('name', )
-    list_filter = ('name', )
+    list_display = ('name',)
+    list_filter = ('name',)
diff --git a/apps/note/api/serializers.py b/apps/note/api/serializers.py
index db0e35318ac4ca71b5902a22231ba11d098bd790..85f500ed6a1bd5c98fd12d8779ab01e2d73ac17f 100644
--- a/apps/note/api/serializers.py
+++ b/apps/note/api/serializers.py
@@ -5,7 +5,8 @@ from rest_framework import serializers
 from rest_polymorphic.serializers import PolymorphicSerializer
 
 from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
-from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction
+from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \
+    TemplateTransaction, SpecialTransaction
 
 
 class NoteSerializer(serializers.ModelSerializer):
@@ -13,15 +14,10 @@ class NoteSerializer(serializers.ModelSerializer):
     REST API Serializer for Notes.
     The djangorestframework plugin will analyse the model `Note` and parse all fields in the API.
     """
+
     class Meta:
         model = Note
         fields = '__all__'
-        extra_kwargs = {
-            'url': {
-                'view_name': 'project-detail',
-                'lookup_field': 'pk'
-            },
-        }
 
 
 class NoteClubSerializer(serializers.ModelSerializer):
@@ -29,40 +25,60 @@ class NoteClubSerializer(serializers.ModelSerializer):
     REST API Serializer for Club's notes.
     The djangorestframework plugin will analyse the model `NoteClub` and parse all fields in the API.
     """
+    name = serializers.SerializerMethodField()
+
     class Meta:
         model = NoteClub
         fields = '__all__'
 
+    def get_name(self, obj):
+        return str(obj)
+
 
 class NoteSpecialSerializer(serializers.ModelSerializer):
     """
     REST API Serializer for special notes.
     The djangorestframework plugin will analyse the model `NoteSpecial` and parse all fields in the API.
     """
+    name = serializers.SerializerMethodField()
+
     class Meta:
         model = NoteSpecial
         fields = '__all__'
 
+    def get_name(self, obj):
+        return str(obj)
+
 
 class NoteUserSerializer(serializers.ModelSerializer):
     """
     REST API Serializer for User's notes.
     The djangorestframework plugin will analyse the model `NoteUser` and parse all fields in the API.
     """
+    name = serializers.SerializerMethodField()
+
     class Meta:
         model = NoteUser
         fields = '__all__'
 
+    def get_name(self, obj):
+        return str(obj)
+
 
 class AliasSerializer(serializers.ModelSerializer):
     """
     REST API Serializer for Aliases.
     The djangorestframework plugin will analyse the model `Alias` and parse all fields in the API.
     """
+    note = serializers.SerializerMethodField()
+
     class Meta:
         model = Alias
         fields = '__all__'
 
+    def get_note(self, alias):
+        return NotePolymorphicSerializer().to_representation(alias.note)
+
 
 class NotePolymorphicSerializer(PolymorphicSerializer):
     model_serializer_mapping = {
@@ -73,11 +89,23 @@ class NotePolymorphicSerializer(PolymorphicSerializer):
     }
 
 
+class TemplateCategorySerializer(serializers.ModelSerializer):
+    """
+    REST API Serializer for Transaction templates.
+    The djangorestframework plugin will analyse the model `TemplateCategory` and parse all fields in the API.
+    """
+
+    class Meta:
+        model = TemplateCategory
+        fields = '__all__'
+
+
 class TransactionTemplateSerializer(serializers.ModelSerializer):
     """
     REST API Serializer for Transaction templates.
     The djangorestframework plugin will analyse the model `TransactionTemplate` and parse all fields in the API.
     """
+
     class Meta:
         model = TransactionTemplate
         fields = '__all__'
@@ -88,16 +116,49 @@ class TransactionSerializer(serializers.ModelSerializer):
     REST API Serializer for Transactions.
     The djangorestframework plugin will analyse the model `Transaction` and parse all fields in the API.
     """
+
     class Meta:
         model = Transaction
         fields = '__all__'
 
 
+class TemplateTransactionSerializer(serializers.ModelSerializer):
+    """
+    REST API Serializer for Transactions.
+    The djangorestframework plugin will analyse the model `TemplateTransaction` and parse all fields in the API.
+    """
+
+    class Meta:
+        model = TemplateTransaction
+        fields = '__all__'
+
+
 class MembershipTransactionSerializer(serializers.ModelSerializer):
     """
     REST API Serializer for Membership transactions.
     The djangorestframework plugin will analyse the model `MembershipTransaction` and parse all fields in the API.
     """
+
     class Meta:
         model = MembershipTransaction
         fields = '__all__'
+
+
+class SpecialTransactionSerializer(serializers.ModelSerializer):
+    """
+    REST API Serializer for Special transactions.
+    The djangorestframework plugin will analyse the model `SpecialTransaction` and parse all fields in the API.
+    """
+
+    class Meta:
+        model = SpecialTransaction
+        fields = '__all__'
+
+
+class TransactionPolymorphicSerializer(PolymorphicSerializer):
+    model_serializer_mapping = {
+        Transaction: TransactionSerializer,
+        TemplateTransaction: TemplateTransactionSerializer,
+        MembershipTransaction: MembershipTransactionSerializer,
+        SpecialTransaction: SpecialTransactionSerializer,
+    }
diff --git a/apps/note/api/urls.py b/apps/note/api/urls.py
index 54218796211fb21173c9501b38f5cacff85f65dd..796a397f746aefd02c2e97b9aaf73bb25454f65a 100644
--- a/apps/note/api/urls.py
+++ b/apps/note/api/urls.py
@@ -2,7 +2,7 @@
 # SPDX-License-Identifier: GPL-3.0-or-later
 
 from .views import NotePolymorphicViewSet, AliasViewSet, \
-    TransactionViewSet, TransactionTemplateViewSet, MembershipTransactionViewSet
+    TemplateCategoryViewSet, TransactionViewSet, TransactionTemplateViewSet
 
 
 def register_note_urls(router, path):
@@ -12,6 +12,6 @@ def register_note_urls(router, path):
     router.register(path + '/note', NotePolymorphicViewSet)
     router.register(path + '/alias', AliasViewSet)
 
+    router.register(path + '/transaction/category', TemplateCategoryViewSet)
     router.register(path + '/transaction/transaction', TransactionViewSet)
     router.register(path + '/transaction/template', TransactionTemplateViewSet)
-    router.register(path + '/transaction/membership', MembershipTransactionViewSet)
diff --git a/apps/note/api/views.py b/apps/note/api/views.py
index 94b4a47a287eec3d481ed3f5dc9d48f8a6cbb535..29c79bd8ba2b09d32344f5f6111470da26d62cc2 100644
--- a/apps/note/api/views.py
+++ b/apps/note/api/views.py
@@ -2,13 +2,15 @@
 # SPDX-License-Identifier: GPL-3.0-or-later
 
 from django.db.models import Q
+from django_filters.rest_framework import DjangoFilterBackend
 from rest_framework import viewsets
+from rest_framework.filters import OrderingFilter, SearchFilter
 
-from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
-from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction
 from .serializers import NoteSerializer, NotePolymorphicSerializer, NoteClubSerializer, NoteSpecialSerializer, \
     NoteUserSerializer, AliasSerializer, \
-    TransactionTemplateSerializer, TransactionSerializer, MembershipTransactionSerializer
+    TemplateCategorySerializer, TransactionTemplateSerializer, TransactionPolymorphicSerializer
+from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
+from ..models.transactions import TransactionTemplate, Transaction, TemplateCategory
 
 
 class NoteViewSet(viewsets.ModelViewSet):
@@ -59,6 +61,9 @@ class NotePolymorphicViewSet(viewsets.ModelViewSet):
     """
     queryset = Note.objects.all()
     serializer_class = NotePolymorphicSerializer
+    filter_backends = [SearchFilter, OrderingFilter]
+    search_fields = ['$alias__normalized_name', '$alias__name', '$polymorphic_ctype__model', ]
+    ordering_fields = ['alias__name', 'alias__normalized_name']
 
     def get_queryset(self):
         """
@@ -69,8 +74,8 @@ class NotePolymorphicViewSet(viewsets.ModelViewSet):
 
         alias = self.request.query_params.get("alias", ".*")
         queryset = queryset.filter(
-            Q(alias__name__regex=alias)
-            | Q(alias__normalized_name__regex=alias.lower()))
+            Q(alias__name__regex="^" + alias)
+            | Q(alias__normalized_name__regex="^" + alias.lower()))
 
         note_type = self.request.query_params.get("type", None)
         if note_type:
@@ -80,12 +85,11 @@ class NotePolymorphicViewSet(viewsets.ModelViewSet):
             elif "club" in types:
                 queryset = queryset.filter(polymorphic_ctype__model="noteclub")
             elif "special" in types:
-                queryset = queryset.filter(
-                    polymorphic_ctype__model="notespecial")
+                queryset = queryset.filter(polymorphic_ctype__model="notespecial")
             else:
                 queryset = queryset.none()
 
-        return queryset
+        return queryset.distinct()
 
 
 class AliasViewSet(viewsets.ModelViewSet):
@@ -96,6 +100,9 @@ class AliasViewSet(viewsets.ModelViewSet):
     """
     queryset = Alias.objects.all()
     serializer_class = AliasSerializer
+    filter_backends = [SearchFilter, OrderingFilter]
+    search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ]
+    ordering_fields = ['name', 'normalized_name']
 
     def get_queryset(self):
         """
@@ -107,7 +114,7 @@ class AliasViewSet(viewsets.ModelViewSet):
 
         alias = self.request.query_params.get("alias", ".*")
         queryset = queryset.filter(
-            Q(name__regex=alias) | Q(normalized_name__regex=alias.lower()))
+            Q(name__regex="^" + alias) | Q(normalized_name__regex="^" + alias.lower()))
 
         note_id = self.request.query_params.get("note", None)
         if note_id:
@@ -131,6 +138,18 @@ class AliasViewSet(viewsets.ModelViewSet):
         return queryset
 
 
+class TemplateCategoryViewSet(viewsets.ModelViewSet):
+    """
+    REST API View set.
+    The djangorestframework plugin will get all `TemplateCategory` objects, serialize it to JSON with the given serializer,
+    then render it on /api/note/transaction/category/
+    """
+    queryset = TemplateCategory.objects.all()
+    serializer_class = TemplateCategorySerializer
+    filter_backends = [SearchFilter]
+    search_fields = ['$name', ]
+
+
 class TransactionTemplateViewSet(viewsets.ModelViewSet):
     """
     REST API View set.
@@ -139,6 +158,8 @@ class TransactionTemplateViewSet(viewsets.ModelViewSet):
     """
     queryset = TransactionTemplate.objects.all()
     serializer_class = TransactionTemplateSerializer
+    filter_backends = [DjangoFilterBackend]
+    filterset_fields = ['name', 'amount', 'display', 'category', ]
 
 
 class TransactionViewSet(viewsets.ModelViewSet):
@@ -148,14 +169,6 @@ class TransactionViewSet(viewsets.ModelViewSet):
     then render it on /api/note/transaction/transaction/
     """
     queryset = Transaction.objects.all()
-    serializer_class = TransactionSerializer
-
-
-class MembershipTransactionViewSet(viewsets.ModelViewSet):
-    """
-    REST API View set.
-    The djangorestframework plugin will get all `MembershipTransaction` objects, serialize it to JSON with the given serializer,
-    then render it on /api/note/transaction/membership/
-    """
-    queryset = MembershipTransaction.objects.all()
-    serializer_class = MembershipTransactionSerializer
+    serializer_class = TransactionPolymorphicSerializer
+    filter_backends = [SearchFilter]
+    search_fields = ['$reason', ]
diff --git a/apps/note/fixtures/initial.json b/apps/note/fixtures/initial.json
index c0e92bda3aa9c6d2d7be0193b3b70b0df3300c0b..3654fa2f5f24c17498de4452295d1a899d798f1b 100644
--- a/apps/note/fixtures/initial.json
+++ b/apps/note/fixtures/initial.json
@@ -3,7 +3,7 @@
         "model": "note.note",
         "pk": 1,
         "fields": {
-            "polymorphic_ctype": 22,
+            "polymorphic_ctype": 40,
             "balance": 0,
             "is_active": true,
             "display_image": "",
@@ -14,7 +14,7 @@
         "model": "note.note",
         "pk": 2,
         "fields": {
-            "polymorphic_ctype": 22,
+            "polymorphic_ctype": 40,
             "balance": 0,
             "is_active": true,
             "display_image": "",
@@ -25,7 +25,7 @@
         "model": "note.note",
         "pk": 3,
         "fields": {
-            "polymorphic_ctype": 22,
+            "polymorphic_ctype": 40,
             "balance": 0,
             "is_active": true,
             "display_image": "",
@@ -36,7 +36,7 @@
         "model": "note.note",
         "pk": 4,
         "fields": {
-            "polymorphic_ctype": 22,
+            "polymorphic_ctype": 40,
             "balance": 0,
             "is_active": true,
             "display_image": "",
@@ -47,7 +47,7 @@
         "model": "note.note",
         "pk": 5,
         "fields": {
-            "polymorphic_ctype": 21,
+            "polymorphic_ctype": 39,
             "balance": 0,
             "is_active": true,
             "display_image": "",
@@ -58,7 +58,7 @@
         "model": "note.note",
         "pk": 6,
         "fields": {
-            "polymorphic_ctype": 21,
+            "polymorphic_ctype": 39,
             "balance": 0,
             "is_active": true,
             "display_image": "",
diff --git a/apps/note/forms.py b/apps/note/forms.py
index 819ed97a45aa2654646c666c7d767f318a951a10..ac6adaaf5d10a0f8157182062f8afd5d60412c5e 100644
--- a/apps/note/forms.py
+++ b/apps/note/forms.py
@@ -3,31 +3,25 @@
 
 from dal import autocomplete
 from django import forms
-from django.conf import settings
 from django.utils.translation import gettext_lazy as _
 
-import os
+from .models import Alias
+from .models import TransactionTemplate
 
-from crispy_forms.helper import FormHelper
-from crispy_forms.bootstrap import Div
-from crispy_forms.layout import Layout, HTML
-
-from .models import Transaction, TransactionTemplate, TemplateTransaction
-from .models import Note, Alias
 
 class AliasForm(forms.ModelForm):
     class Meta:
         model = Alias
         fields = ("name",)
 
-    def __init__(self,*args,**kwargs):
-        super().__init__(*args,**kwargs)
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
         self.fields["name"].label = False
-        self.fields["name"].widget.attrs={"placeholder":_('New Alias')}
-        
+        self.fields["name"].widget.attrs = {"placeholder": _('New Alias')}
+
 
 class ImageForm(forms.Form):
-    image = forms.ImageField(required = False,
+    image = forms.ImageField(required=False,
                              label=_('select an image'),
                              help_text=_('Maximal size: 2MB'))
     x = forms.FloatField(widget=forms.HiddenInput())
@@ -35,7 +29,7 @@ class ImageForm(forms.Form):
     width = forms.FloatField(widget=forms.HiddenInput())
     height = forms.FloatField(widget=forms.HiddenInput())
 
-   
+
 class TransactionTemplateForm(forms.ModelForm):
     class Meta:
         model = TransactionTemplate
@@ -48,92 +42,11 @@ class TransactionTemplateForm(forms.ModelForm):
         # forward=(forward.Const('TYPE', 'note_type') où TYPE est dans {user, club, special}
         widgets = {
             'destination':
-            autocomplete.ModelSelect2(
-                url='note:note_autocomplete',
-                attrs={
-                    'data-placeholder': 'Note ...',
-                    'data-minimum-input-length': 1,
-                },
-            ),
-        }
-
-
-class TransactionForm(forms.ModelForm):
-    def save(self, commit=True):
-        super().save(commit)
-
-
-    def clean(self):
-        """
-        If the user has no right to transfer funds, then it will be the source of the transfer by default.
-        Transactions between a note and the same note are not authorized.
-        """
-
-        cleaned_data = super().clean()
-        if not "source" in cleaned_data: # TODO Replace it with "if %user has no right to transfer funds"
-            cleaned_data["source"] = self.user.note
-
-        if cleaned_data["source"].pk == cleaned_data["destination"].pk:
-            self.add_error("destination", _("Source and destination must be different."))
-
-        return cleaned_data
-
-
-    class Meta:
-        model = Transaction
-        fields = (
-            'source',
-            'destination',
-            'reason',
-            'amount',
-        )
-
-        # Voir ci-dessus
-        widgets = {
-            'source':
-            autocomplete.ModelSelect2(
-                url='note:note_autocomplete',
-                attrs={
-                    'data-placeholder': 'Note ...',
-                    'data-minimum-input-length': 1,
-                },
-            ),
-            'destination':
-            autocomplete.ModelSelect2(
-                url='note:note_autocomplete',
-                attrs={
-                    'data-placeholder': 'Note ...',
-                    'data-minimum-input-length': 1,
-                },
-            ),
-        }
-
-
-class ConsoForm(forms.ModelForm):
-    def save(self, commit=True):
-        button: TransactionTemplate = TransactionTemplate.objects.filter(
-            name=self.data['button']).get()
-        self.instance.destination = button.destination
-        self.instance.amount = button.amount
-        self.instance.reason = '{} ({})'.format(button.name, button.category)
-        self.instance.name = button.name
-        self.instance.category = button.category
-        super().save(commit)
-
-    class Meta:
-        model = TemplateTransaction
-        fields = ('source', )
-
-        # Le champ d'utilisateur est remplacé par un champ d'auto-complétion.
-        # Quand des lettres sont tapées, une requête est envoyée sur l'API d'auto-complétion
-        # et récupère les aliases de note valides
-        widgets = {
-            'source':
-            autocomplete.ModelSelect2(
-                url='note:note_autocomplete',
-                attrs={
-                    'data-placeholder': 'Note ...',
-                    'data-minimum-input-length': 1,
-                },
-            ),
+                autocomplete.ModelSelect2(
+                    url='note:note_autocomplete',
+                    attrs={
+                        'data-placeholder': 'Note ...',
+                        'data-minimum-input-length': 1,
+                    },
+                ),
         }
diff --git a/apps/note/models/notes.py b/apps/note/models/notes.py
index 4b06c93adaba324d9faaf40825ea0ded8add3db4..b6b00aa8ec91fedda1449a20948625f4ea8256e1 100644
--- a/apps/note/models/notes.py
+++ b/apps/note/models/notes.py
@@ -9,6 +9,7 @@ from django.core.validators import RegexValidator
 from django.db import models
 from django.utils.translation import gettext_lazy as _
 from polymorphic.models import PolymorphicModel
+
 """
 Defines each note types
 """
@@ -27,7 +28,7 @@ class Note(PolymorphicModel):
         help_text=_('in centimes, money credited for this instance'),
         default=0,
     )
-    last_negative= models.DateTimeField(
+    last_negative = models.DateTimeField(
         verbose_name=_('last negative date'),
         help_text=_('last time the balance was negative'),
         null=True,
@@ -98,7 +99,7 @@ class Note(PolymorphicModel):
             # Alias exists, so check if it is linked to this note
             if aliases.first().note != self:
                 raise ValidationError(_('This alias is already taken.'),
-                                      code="same_alias",)
+                                      code="same_alias", )
         else:
             # Alias does not exist yet, so check if it can exist
             a = Alias(name=str(self))
@@ -208,6 +209,10 @@ class Alias(models.Model):
     class Meta:
         verbose_name = _("alias")
         verbose_name_plural = _("aliases")
+        indexes = [
+            models.Index(fields=['name']),
+            models.Index(fields=['normalized_name']),
+        ]
 
     def __str__(self):
         return self.name
@@ -230,13 +235,13 @@ class Alias(models.Model):
         try:
             sim_alias = Alias.objects.get(normalized_name=normalized_name)
             if self != sim_alias:
-                raise ValidationError(_('An alias with a similar name already exists: {} '.format(sim_alias)),
-                                       code="same_alias"
-                )
+                raise ValidationError(_('An alias with a similar name already exists: {} ').format(sim_alias),
+                                      code="same_alias"
+                                      )
         except Alias.DoesNotExist:
             pass
         self.normalized_name = normalized_name
-        
+
     def delete(self, using=None, keep_parents=False):
         if self.name == str(self.note):
             raise ValidationError(_("You can't delete your main alias."),
diff --git a/apps/note/models/transactions.py b/apps/note/models/transactions.py
index 598c119bc5e441413aaeefd2b5ddda4a004721b9..86c0073749fa3ad7e07deb0c20a07daa79d61bea 100644
--- a/apps/note/models/transactions.py
+++ b/apps/note/models/transactions.py
@@ -2,12 +2,12 @@
 # SPDX-License-Identifier: GPL-3.0-or-later
 
 from django.db import models
+from django.urls import reverse
 from django.utils import timezone
 from django.utils.translation import gettext_lazy as _
-from django.urls import reverse
 from polymorphic.models import PolymorphicModel
 
-from .notes import Note, NoteClub
+from .notes import Note, NoteClub, NoteSpecial
 
 """
 Defines transactions
@@ -44,7 +44,7 @@ class TransactionTemplate(models.Model):
         verbose_name=_('name'),
         max_length=255,
         unique=True,
-        error_messages={'unique':_("A template with this name already exist")},
+        error_messages={'unique': _("A template with this name already exist")},
     )
     destination = models.ForeignKey(
         NoteClub,
@@ -63,11 +63,12 @@ class TransactionTemplate(models.Model):
         max_length=31,
     )
     display = models.BooleanField(
-        default = True,
+        default=True,
     )
     description = models.CharField(
         verbose_name=_('description'),
         max_length=255,
+        blank=True,
     )
 
     class Meta:
@@ -75,7 +76,7 @@ class TransactionTemplate(models.Model):
         verbose_name_plural = _("transaction templates")
 
     def get_absolute_url(self):
-        return reverse('note:template_update', args=(self.pk, ))
+        return reverse('note:template_update', args=(self.pk,))
 
 
 class Transaction(PolymorphicModel):
@@ -106,7 +107,10 @@ class Transaction(PolymorphicModel):
         verbose_name=_('quantity'),
         default=1,
     )
-    amount = models.PositiveIntegerField(verbose_name=_('amount'), )
+    amount = models.PositiveIntegerField(
+        verbose_name=_('amount'),
+    )
+
     reason = models.CharField(
         verbose_name=_('reason'),
         max_length=255,
@@ -119,6 +123,11 @@ class Transaction(PolymorphicModel):
     class Meta:
         verbose_name = _("transaction")
         verbose_name_plural = _("transactions")
+        indexes = [
+            models.Index(fields=['created_at']),
+            models.Index(fields=['source']),
+            models.Index(fields=['destination']),
+        ]
 
     def save(self, *args, **kwargs):
         """
@@ -127,6 +136,7 @@ class Transaction(PolymorphicModel):
 
         if self.source.pk == self.destination.pk:
             # When source == destination, no money is transfered
+            super().save(*args, **kwargs)
             return
 
         created = self.pk is None
@@ -151,11 +161,14 @@ class Transaction(PolymorphicModel):
     def total(self):
         return self.amount * self.quantity
 
+    @property
+    def type(self):
+        return _('Transfer')
+
 
 class TemplateTransaction(Transaction):
     """
     Special type of :model:`note.Transaction` associated to a :model:`note.TransactionTemplate`.
-
     """
 
     template = models.ForeignKey(
@@ -168,6 +181,37 @@ class TemplateTransaction(Transaction):
         on_delete=models.PROTECT,
     )
 
+    @property
+    def type(self):
+        return _('Template')
+
+
+class SpecialTransaction(Transaction):
+    """
+    Special type of :model:`note.Transaction` associated to transactions with special notes
+    """
+
+    last_name = models.CharField(
+        max_length=255,
+        verbose_name=_("name"),
+    )
+
+    first_name = models.CharField(
+        max_length=255,
+        verbose_name=_("first_name"),
+    )
+
+    bank = models.CharField(
+        max_length=255,
+        verbose_name=_("bank"),
+        blank=True,
+    )
+
+    @property
+    def type(self):
+        return _('Credit') if isinstance(self.source, NoteSpecial) else _("Debit")
+
+
 class MembershipTransaction(Transaction):
     """
     Special type of :model:`note.Transaction` associated to a :model:`member.Membership`.
@@ -183,3 +227,7 @@ class MembershipTransaction(Transaction):
     class Meta:
         verbose_name = _("membership transaction")
         verbose_name_plural = _("membership transactions")
+
+    @property
+    def type(self):
+        return _('membership transaction')
diff --git a/apps/note/tables.py b/apps/note/tables.py
index 20476cb664460502b4daf1e500b22d29f7e015df..b9dac051f7bc902a232479b7bcef8fbd9c9cbb74 100644
--- a/apps/note/tables.py
+++ b/apps/note/tables.py
@@ -1,45 +1,77 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
+import html
+
 import django_tables2 as tables
 from django.db.models import F
 from django_tables2.utils import A
-from .models.transactions import Transaction
+from django.utils.translation import gettext_lazy as _
+
 from .models.notes import Alias
+from .models.transactions import Transaction
+from .templatetags.pretty_money import pretty_money
+
 
 class HistoryTable(tables.Table):
     class Meta:
         attrs = {
             'class':
-            'table table-condensed table-striped table-hover'
+                'table table-condensed table-striped table-hover'
         }
         model = Transaction
+        exclude = ("id", "polymorphic_ctype", )
         template_name = 'django_tables2/bootstrap4.html'
-        sequence = ('...', 'total', 'valid')
+        sequence = ('...', 'type', 'total', 'valid', )
+        orderable = False
+
+    type = tables.Column()
 
     total = tables.Column()  # will use Transaction.total() !!
 
+    valid = tables.Column(attrs={"td": {"id": lambda record: "validate_" + str(record.id),
+                                        "class": lambda record: str(record.valid).lower() + ' validate',
+                                        "onclick": lambda record: 'de_validate(' + str(record.id) + ', '
+                                                                  + str(record.valid).lower() + ')'}})
+
     def order_total(self, queryset, is_descending):
         # needed for rendering
         queryset = queryset.annotate(total=F('amount') * F('quantity')) \
             .order_by(('-' if is_descending else '') + 'total')
-        return (queryset, True)
+        return queryset, True
+
+    def render_amount(self, value):
+        return pretty_money(value)
+
+    def render_total(self, value):
+        return pretty_money(value)
+
+    def render_type(self, value):
+        return _(value)
+
+    # Django-tables escape strings. That's a wrong thing.
+    def render_reason(self, value):
+        return html.unescape(value)
+
+    def render_valid(self, value):
+        return "✔" if value else "✖"
+
 
 class AliasTable(tables.Table):
     class Meta:
         attrs = {
             'class':
-            'table table condensed table-striped table-hover'
+                'table table condensed table-striped table-hover'
         }
         model = Alias
-        fields =('name',)
+        fields = ('name',)
         template_name = 'django_tables2/bootstrap4.html'
 
     show_header = False
-    name = tables.Column(attrs={'td':{'class':'text-center'}})
+    name = tables.Column(attrs={'td': {'class': 'text-center'}})
     delete = tables.LinkColumn('member:user_alias_delete',
                                args=[A('pk')],
                                attrs={
-                                   'td': {'class':'col-sm-2'},
-                                   'a': {'class': 'btn btn-danger'} },
-                               text='delete',accessor='pk')
+                                   'td': {'class': 'col-sm-2'},
+                                   'a': {'class': 'btn btn-danger'}},
+                               text='delete', accessor='pk')
diff --git a/apps/note/templatetags/getenv.py b/apps/note/templatetags/getenv.py
new file mode 100644
index 0000000000000000000000000000000000000000..c133cb8ff4df0a1bfe11ec7726d604907ecde3d4
--- /dev/null
+++ b/apps/note/templatetags/getenv.py
@@ -0,0 +1,14 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django import template
+
+import os
+
+
+def getenv(value):
+    return os.getenv(value)
+
+
+register = template.Library()
+register.filter('getenv', getenv)
diff --git a/apps/note/templatetags/pretty_money.py b/apps/note/templatetags/pretty_money.py
index 12530c6e22d4ea0eb86b46d018ffbcddb0c44f8c..265870a85aadd70d949f13cdeb990db5c0955a18 100644
--- a/apps/note/templatetags/pretty_money.py
+++ b/apps/note/templatetags/pretty_money.py
@@ -11,7 +11,7 @@ def pretty_money(value):
             abs(value) // 100,
         )
     else:
-        return "{:s}{:d} € {:02d}".format(
+        return "{:s}{:d}.{:02d} €".format(
             "- " if value < 0 else "",
             abs(value) // 100,
             abs(value) % 100,
diff --git a/apps/note/views.py b/apps/note/views.py
index 5038df164c02555fa4b7083953537f38eaac1fdd..31a79be7c992c8a70899d5b6f4d2d19480fdc1da 100644
--- a/apps/note/views.py
+++ b/apps/note/views.py
@@ -3,56 +3,49 @@
 
 from dal import autocomplete
 from django.contrib.auth.mixins import LoginRequiredMixin
+from django.contrib.contenttypes.models import ContentType
 from django.db.models import Q
-from django.urls import reverse
 from django.utils.translation import gettext_lazy as _
 from django.views.generic import CreateView, ListView, UpdateView
+from django_tables2 import SingleTableView
 
-from .models import Transaction, TransactionTemplate, Alias, TemplateTransaction
-from .forms import TransactionForm, TransactionTemplateForm, ConsoForm
+from .forms import TransactionTemplateForm
+from .models import Transaction, TransactionTemplate, Alias, TemplateTransaction, NoteSpecial
+from .models.transactions import SpecialTransaction
+from .tables import HistoryTable
 
 
-class TransactionCreate(LoginRequiredMixin, CreateView):
+class TransactionCreate(LoginRequiredMixin, SingleTableView):
     """
     Show transfer page
 
     TODO: If user have sufficient rights, they can transfer from an other note
     """
-    model = Transaction
-    form_class = TransactionForm
+    queryset = Transaction.objects.order_by("-id").all()[:50]
+    template_name = "note/transaction_form.html"
+
+    # Transaction history table
+    table_class = HistoryTable
+    table_pagination = {"per_page": 50}
 
     def get_context_data(self, **kwargs):
         """
         Add some context variables in template such as page title
         """
         context = super().get_context_data(**kwargs)
-        context['title'] = _('Transfer money from your account '
-                             'to one or others')
-
-        context['no_cache'] = True
+        context['title'] = _('Transfer money')
+        context['polymorphic_ctype'] = ContentType.objects.get_for_model(Transaction).pk
+        context['special_polymorphic_ctype'] = ContentType.objects.get_for_model(SpecialTransaction).pk
+        context['special_types'] = NoteSpecial.objects.order_by("special_type").all()
 
         return context
 
-    def get_form(self, form_class=None):
-        """
-        If the user has no right to transfer funds, then it won't have the choice of the source of the transfer.
-        """
-        form = super().get_form(form_class)
-
-        if False:  # TODO: fix it with "if %user has no right to transfer funds"
-            del form.fields['source']
-            form.user = self.request.user
-
-        return form
-
-    def get_success_url(self):
-        return reverse('note:transfer')
-
 
 class NoteAutocomplete(autocomplete.Select2QuerySetView):
     """
     Auto complete note by aliases
     """
+
     def get_queryset(self):
         """
         Quand une personne cherche un alias, une requête est envoyée sur l'API dédiée à l'auto-complétion.
@@ -66,7 +59,7 @@ class NoteAutocomplete(autocomplete.Select2QuerySetView):
 
         # self.q est le paramètre de la recherche
         if self.q:
-            qs = qs.filter(Q(name__regex=self.q) | Q(normalized_name__regex=Alias.normalize(self.q)))\
+            qs = qs.filter(Q(name__regex="^" + self.q) | Q(normalized_name__regex="^" + Alias.normalize(self.q))) \
                 .order_by('normalized_name').distinct()
 
         # Filtrage par type de note (user, club, special)
@@ -120,31 +113,31 @@ class TransactionTemplateUpdateView(LoginRequiredMixin, UpdateView):
     form_class = TransactionTemplateForm
 
 
-class ConsoView(LoginRequiredMixin, CreateView):
+class ConsoView(LoginRequiredMixin, SingleTableView):
     """
     Consume
     """
-    model = TemplateTransaction
+    queryset = Transaction.objects.order_by("-id").all()[:50]
     template_name = "note/conso_form.html"
-    form_class = ConsoForm
+
+    # Transaction history table
+    table_class = HistoryTable
+    table_pagination = {"per_page": 50}
 
     def get_context_data(self, **kwargs):
         """
         Add some context variables in template such as page title
         """
         context = super().get_context_data(**kwargs)
-        context['transaction_templates'] = TransactionTemplate.objects.filter(display=True) \
-            .order_by('category')
-        context['title'] = _("Consommations")
+        from django.db.models import Count
+        buttons = TransactionTemplate.objects.filter(display=True) \
+            .annotate(clicks=Count('templatetransaction')).order_by('category__name', 'name')
+        context['transaction_templates'] = buttons
+        context['most_used'] = buttons.order_by('-clicks', 'name')[:10]
+        context['title'] = _("Consumptions")
+        context['polymorphic_ctype'] = ContentType.objects.get_for_model(TemplateTransaction).pk
 
         # select2 compatibility
         context['no_cache'] = True
 
         return context
-
-    def get_success_url(self):
-        """
-        When clicking a button, reload the same page
-        """
-        return reverse('note:consos')
-
diff --git a/entrypoint.sh b/entrypoint.sh
index f05e962ae116b4831ea506704b828ab9c5988748..e5a22a5a53f2f25df533f9370a89f7e77205cb93 100755
--- a/entrypoint.sh
+++ b/entrypoint.sh
@@ -2,12 +2,17 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
+if [ -z ${NOTE_URL+x} ]; then
+  echo "Warning: your env files are not configurated."
+else
+  sed -i -e "s/example.com/$DOMAIN/g" /code/apps/member/fixtures/initial.json
+  sed -i -e "s/localhost/$NOTE_URL/g" /code/note_kfet/fixtures/initial.json
+  sed -i -e "s/\.\*/https?:\/\/$NOTE_URL\/.*/g" /code/note_kfet/fixtures/cas.json
+  sed -i -e "s/REPLACEME/La Note Kfet \\\\ud83c\\\\udf7b/g" /code/note_kfet/fixtures/cas.json
+fi
+
 python manage.py compilemessages
 python manage.py makemigrations
-
-# Wait for database
-sleep 5
 python manage.py migrate
 
-# TODO: use uwsgi in production
 python manage.py runserver 0.0.0.0:8000
diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po
index 386db34c4f0fda73222f1ac61f9743c07c9265e8..e61efb2a6f2e68626aefb660983d2931fba657ac 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-02-27 17:39+0100\n"
+"POT-Creation-Date: 2020-03-16 11:53+0100\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"
@@ -23,9 +23,10 @@ msgid "activity"
 msgstr ""
 
 #: apps/activity/models.py:19 apps/activity/models.py:44
-#: apps/member/models.py:60 apps/member/models.py:111
-#: apps/note/models/notes.py:184 apps/note/models/transactions.py:24
-#: apps/note/models/transactions.py:44 templates/member/profile_detail.html:11
+#: apps/member/models.py:61 apps/member/models.py:112
+#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
+#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:202
+#: templates/member/profile_detail.html:15
 msgid "name"
 msgstr ""
 
@@ -49,8 +50,8 @@ msgstr ""
 msgid "description"
 msgstr ""
 
-#: apps/activity/models.py:54 apps/note/models/notes.py:160
-#: apps/note/models/transactions.py:62
+#: apps/activity/models.py:54 apps/note/models/notes.py:164
+#: apps/note/models/transactions.py:62 apps/note/models/transactions.py:115
 msgid "type"
 msgstr ""
 
@@ -86,43 +87,59 @@ msgstr ""
 msgid "API"
 msgstr ""
 
-#: apps/logs/apps.py:10
+#: apps/logs/apps.py:11
 msgid "Logs"
 msgstr ""
 
-#: apps/logs/models.py:20 apps/note/models/notes.py:105
+#: apps/logs/models.py:21 apps/note/models/notes.py:117
 msgid "user"
 msgstr ""
 
 #: apps/logs/models.py:27
+msgid "IP Address"
+msgstr ""
+
+#: apps/logs/models.py:35
 msgid "model"
 msgstr ""
 
-#: apps/logs/models.py:34
+#: apps/logs/models.py:42
 msgid "identifier"
 msgstr ""
 
-#: apps/logs/models.py:39
+#: apps/logs/models.py:47
 msgid "previous data"
 msgstr ""
 
-#: apps/logs/models.py:44
+#: apps/logs/models.py:52
 msgid "new data"
 msgstr ""
 
-#: apps/logs/models.py:51
+#: apps/logs/models.py:60
+msgid "create"
+msgstr ""
+
+#: apps/logs/models.py:61
+msgid "edit"
+msgstr ""
+
+#: apps/logs/models.py:62
+msgid "delete"
+msgstr ""
+
+#: apps/logs/models.py:65
 msgid "action"
 msgstr ""
 
-#: apps/logs/models.py:59
+#: apps/logs/models.py:73
 msgid "timestamp"
 msgstr ""
 
-#: apps/logs/models.py:63
+#: apps/logs/models.py:77
 msgid "Logs cannot be destroyed."
 msgstr ""
 
-#: apps/member/apps.py:10
+#: apps/member/apps.py:14
 msgid "member"
 msgstr ""
 
@@ -130,7 +147,7 @@ msgstr ""
 msgid "phone number"
 msgstr ""
 
-#: apps/member/models.py:29 templates/member/profile_detail.html:24
+#: apps/member/models.py:29 templates/member/profile_detail.html:28
 msgid "section"
 msgstr ""
 
@@ -138,7 +155,7 @@ msgstr ""
 msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
 msgstr ""
 
-#: apps/member/models.py:36 templates/member/profile_detail.html:27
+#: apps/member/models.py:36 templates/member/profile_detail.html:31
 msgid "address"
 msgstr ""
 
@@ -150,199 +167,207 @@ msgstr ""
 msgid "user profile"
 msgstr ""
 
-#: apps/member/models.py:65
+#: apps/member/models.py:66
 msgid "email"
 msgstr ""
 
-#: apps/member/models.py:70
+#: apps/member/models.py:71
 msgid "membership fee"
 msgstr ""
 
-#: apps/member/models.py:74
+#: apps/member/models.py:75
 msgid "membership duration"
 msgstr ""
 
-#: apps/member/models.py:75
+#: apps/member/models.py:76
 msgid "The longest time a membership can last (NULL = infinite)."
 msgstr ""
 
-#: apps/member/models.py:80
+#: apps/member/models.py:81
 msgid "membership start"
 msgstr ""
 
-#: apps/member/models.py:81
+#: apps/member/models.py:82
 msgid "How long after January 1st the members can renew their membership."
 msgstr ""
 
-#: apps/member/models.py:86
+#: apps/member/models.py:87
 msgid "membership end"
 msgstr ""
 
-#: apps/member/models.py:87
+#: apps/member/models.py:88
 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:93 apps/note/models/notes.py:135
+#: apps/member/models.py:94 apps/note/models/notes.py:139
 msgid "club"
 msgstr ""
 
-#: apps/member/models.py:94
+#: apps/member/models.py:95
 msgid "clubs"
 msgstr ""
 
-#: apps/member/models.py:117
+#: apps/member/models.py:118
 msgid "role"
 msgstr ""
 
-#: apps/member/models.py:118
+#: apps/member/models.py:119
 msgid "roles"
 msgstr ""
 
-#: apps/member/models.py:142
+#: apps/member/models.py:143
 msgid "membership starts on"
 msgstr ""
 
-#: apps/member/models.py:145
+#: apps/member/models.py:146
 msgid "membership ends on"
 msgstr ""
 
-#: apps/member/models.py:149
+#: apps/member/models.py:150
 msgid "fee"
 msgstr ""
 
-#: apps/member/models.py:153
+#: apps/member/models.py:154
 msgid "membership"
 msgstr ""
 
-#: apps/member/models.py:154
+#: apps/member/models.py:155
 msgid "memberships"
 msgstr ""
 
-#: apps/member/views.py:63 templates/member/profile_detail.html:42
+#: apps/member/views.py:69 templates/member/profile_detail.html:46
 msgid "Update Profile"
 msgstr ""
 
-#: apps/member/views.py:79
+#: apps/member/views.py:82
 msgid "An alias with a similar name already exists."
 msgstr ""
 
-#: apps/member/views.py:130
+#: apps/member/views.py:132
 #, python-format
 msgid "Account #%(id)s: %(username)s"
 msgstr ""
 
-#: apps/note/admin.py:120 apps/note/models/transactions.py:93
+#: apps/member/views.py:202
+msgid "Alias successfully deleted"
+msgstr ""
+
+#: apps/note/admin.py:120 apps/note/models/transactions.py:94
 msgid "source"
 msgstr ""
 
 #: apps/note/admin.py:128 apps/note/admin.py:156
-#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:99
+#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:100
 msgid "destination"
 msgstr ""
 
-#: apps/note/apps.py:14 apps/note/models/notes.py:54
+#: apps/note/apps.py:14 apps/note/models/notes.py:58
 msgid "note"
 msgstr ""
 
-#: apps/note/forms.py:49
-msgid "Source and destination must be different."
+#: apps/note/forms.py:20
+msgid "New Alias"
 msgstr ""
 
-#: apps/note/models/notes.py:26
-msgid "account balance"
+#: apps/note/forms.py:25
+msgid "select an image"
+msgstr ""
+
+#: apps/note/forms.py:26
+msgid "Maximal size: 2MB"
 msgstr ""
 
 #: apps/note/models/notes.py:27
+msgid "account balance"
+msgstr ""
+
+#: apps/note/models/notes.py:28
 msgid "in centimes, money credited for this instance"
 msgstr ""
 
-#: apps/note/models/notes.py:31
+#: apps/note/models/notes.py:32
 msgid "last negative date"
 msgstr ""
 
-#: apps/note/models/notes.py:32
+#: apps/note/models/notes.py:33
 msgid "last time the balance was negative"
 msgstr ""
 
-#: apps/note/models/notes.py:37
+#: apps/note/models/notes.py:38
 msgid "active"
 msgstr ""
 
-#: apps/note/models/notes.py:40
+#: apps/note/models/notes.py:41
 msgid ""
 "Designates whether this note should be treated as active. Unselect this "
 "instead of deleting notes."
 msgstr ""
 
-#: apps/note/models/notes.py:44
+#: apps/note/models/notes.py:45
 msgid "display image"
 msgstr ""
 
-#: apps/note/models/notes.py:49 apps/note/models/transactions.py:102
+#: apps/note/models/notes.py:53 apps/note/models/transactions.py:103
 msgid "created at"
 msgstr ""
 
-#: apps/note/models/notes.py:55
+#: apps/note/models/notes.py:59
 msgid "notes"
 msgstr ""
 
-#: apps/note/models/notes.py:63
+#: apps/note/models/notes.py:67
 msgid "Note"
 msgstr ""
 
-#: apps/note/models/notes.py:73 apps/note/models/notes.py:97
+#: apps/note/models/notes.py:77 apps/note/models/notes.py:101
 msgid "This alias is already taken."
 msgstr ""
 
-#: apps/note/models/notes.py:113
-msgid "user"
-msgstr ""
-
-#: apps/note/models/notes.py:117
+#: apps/note/models/notes.py:121
 msgid "one's note"
 msgstr ""
 
-#: apps/note/models/notes.py:118
+#: apps/note/models/notes.py:122
 msgid "users note"
 msgstr ""
 
-#: apps/note/models/notes.py:124
+#: apps/note/models/notes.py:128
 #, python-format
 msgid "%(user)s's note"
 msgstr ""
 
-#: apps/note/models/notes.py:139
+#: apps/note/models/notes.py:143
 msgid "club note"
 msgstr ""
 
-#: apps/note/models/notes.py:140
+#: apps/note/models/notes.py:144
 msgid "clubs notes"
 msgstr ""
 
-#: apps/note/models/notes.py:146
+#: apps/note/models/notes.py:150
 #, python-format
 msgid "Note of %(club)s club"
 msgstr ""
 
-#: apps/note/models/notes.py:166
+#: apps/note/models/notes.py:170
 msgid "special note"
 msgstr ""
 
-#: apps/note/models/notes.py:167
+#: apps/note/models/notes.py:171
 msgid "special notes"
 msgstr ""
 
-#: apps/note/models/notes.py:190
+#: apps/note/models/notes.py:194
 msgid "Invalid alias"
 msgstr ""
 
-#: apps/note/models/notes.py:206
+#: apps/note/models/notes.py:210
 msgid "alias"
 msgstr ""
 
-#: apps/note/models/notes.py:207 templates/member/profile_detail.html:33
+#: apps/note/models/notes.py:211 templates/member/profile_detail.html:37
 msgid "aliases"
 msgstr ""
 
@@ -351,10 +376,10 @@ msgid "Alias is too long."
 msgstr ""
 
 #: apps/note/models/notes.py:238
-msgid "An alias with a similar name already exists:"
+msgid "An alias with a similar name already exists: {} "
 msgstr ""
 
-#: apps/note/models/notes.py:246
+#: apps/note/models/notes.py:247
 msgid "You can't delete your main alias."
 msgstr ""
 
@@ -370,7 +395,7 @@ msgstr ""
 msgid "A template with this name already exist"
 msgstr ""
 
-#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:109
+#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:111
 msgid "amount"
 msgstr ""
 
@@ -378,59 +403,96 @@ msgstr ""
 msgid "in centimes"
 msgstr ""
 
-#: apps/note/models/transactions.py:74
+#: apps/note/models/transactions.py:75
 msgid "transaction template"
 msgstr ""
 
-#: apps/note/models/transactions.py:75
+#: apps/note/models/transactions.py:76
 msgid "transaction templates"
 msgstr ""
 
-#: apps/note/models/transactions.py:106
+#: apps/note/models/transactions.py:107
 msgid "quantity"
 msgstr ""
 
-#: apps/note/models/transactions.py:111
+#: apps/note/models/transactions.py:117 templates/note/transaction_form.html:15
+msgid "Gift"
+msgstr ""
+
+#: apps/note/models/transactions.py:118 templates/base.html:90
+#: templates/note/transaction_form.html:19
+#: templates/note/transaction_form.html:126
+msgid "Transfer"
+msgstr ""
+
+#: apps/note/models/transactions.py:119
+msgid "Template"
+msgstr ""
+
+#: apps/note/models/transactions.py:120 templates/note/transaction_form.html:23
+msgid "Credit"
+msgstr ""
+
+#: apps/note/models/transactions.py:121 templates/note/transaction_form.html:27
+msgid "Debit"
+msgstr ""
+
+#: apps/note/models/transactions.py:122 apps/note/models/transactions.py:230
+msgid "membership transaction"
+msgstr ""
+
+#: apps/note/models/transactions.py:129
 msgid "reason"
 msgstr ""
 
-#: apps/note/models/transactions.py:115
+#: apps/note/models/transactions.py:133
 msgid "valid"
 msgstr ""
 
-#: apps/note/models/transactions.py:120
+#: apps/note/models/transactions.py:138
 msgid "transaction"
 msgstr ""
 
-#: apps/note/models/transactions.py:121
+#: apps/note/models/transactions.py:139
 msgid "transactions"
 msgstr ""
 
-#: apps/note/models/transactions.py:184
-msgid "membership transaction"
+#: apps/note/models/transactions.py:207
+msgid "first_name"
 msgstr ""
 
-#: apps/note/models/transactions.py:185
+#: apps/note/models/transactions.py:212
+msgid "bank"
+msgstr ""
+
+#: apps/note/models/transactions.py:231
 msgid "membership transactions"
 msgstr ""
 
-#: apps/note/views.py:29
-msgid "Transfer money from your account to one or others"
+#: apps/note/views.py:31
+msgid "Transfer money"
 msgstr ""
 
-#: apps/note/views.py:138
-msgid "Consommations"
+#: apps/note/views.py:132 templates/base.html:78
+msgid "Consumptions"
 msgstr ""
 
-#: note_kfet/settings/base.py:155
-msgid "German"
+#: note_kfet/settings/__init__.py:61
+msgid ""
+"The Central Authentication Service grants you access to most of our websites "
+"by authenticating only once, so you don't need to type your credentials "
+"again unless your session expires or you logout."
 msgstr ""
 
 #: note_kfet/settings/base.py:156
-msgid "English"
+msgid "German"
 msgstr ""
 
 #: note_kfet/settings/base.py:157
+msgid "English"
+msgstr ""
+
+#: note_kfet/settings/base.py:158
 msgid "French"
 msgstr ""
 
@@ -438,6 +500,78 @@ msgstr ""
 msgid "The ENS Paris-Saclay BDE note."
 msgstr ""
 
+#: templates/base.html:81
+msgid "Clubs"
+msgstr ""
+
+#: templates/base.html:84
+msgid "Activities"
+msgstr ""
+
+#: templates/base.html:87
+msgid "Buttons"
+msgstr ""
+
+#: templates/cas_server/base.html:7
+msgid "Central Authentication Service"
+msgstr ""
+
+#: templates/cas_server/base.html:43
+#, python-format
+msgid ""
+"A new version of the application is available. This instance runs "
+"%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
+"upgrading."
+msgstr ""
+
+#: templates/cas_server/logged.html:4
+msgid ""
+"<h3>Log In Successful</h3>You have successfully logged into the Central "
+"Authentication Service.<br/>For security reasons, please Log Out and Exit "
+"your web browser when you are done accessing services that require "
+"authentication!"
+msgstr ""
+
+#: templates/cas_server/logged.html:8
+msgid "Log me out from all my sessions"
+msgstr ""
+
+#: templates/cas_server/logged.html:14
+msgid "Forget the identity provider"
+msgstr ""
+
+#: templates/cas_server/logged.html:18
+msgid "Logout"
+msgstr ""
+
+#: templates/cas_server/login.html:6
+msgid "Please log in"
+msgstr ""
+
+#: templates/cas_server/login.html:11
+msgid ""
+"If you don't have any Note Kfet account, please follow <a href='/accounts/"
+"signup'>this link to sign up</a>."
+msgstr ""
+
+#: templates/cas_server/login.html:17
+msgid "Login"
+msgstr ""
+
+#: templates/cas_server/warn.html:9
+msgid "Connect to the service"
+msgstr ""
+
+#: templates/django_filters/rest_framework/crispy_form.html:4
+#: templates/django_filters/rest_framework/form.html:2
+msgid "Field filters"
+msgstr ""
+
+#: templates/django_filters/rest_framework/form.html:5
+#: templates/member/club_form.html:10
+msgid "Submit"
+msgstr ""
+
 #: templates/member/club_detail.html:10
 msgid "Membership starts on"
 msgstr ""
@@ -450,10 +584,22 @@ msgstr ""
 msgid "Membership duration"
 msgstr ""
 
-#: templates/member/club_detail.html:18 templates/member/profile_detail.html:30
+#: templates/member/club_detail.html:18 templates/member/profile_detail.html:34
 msgid "balance"
 msgstr ""
 
+#: templates/member/club_detail.html:51 templates/member/profile_detail.html:75
+msgid "Transaction history"
+msgstr ""
+
+#: templates/member/club_form.html:6
+msgid "Clubs list"
+msgstr ""
+
+#: templates/member/club_list.html:8
+msgid "New club"
+msgstr ""
+
 #: templates/member/manage_auth_tokens.html:16
 msgid "Token"
 msgstr ""
@@ -466,27 +612,35 @@ msgstr ""
 msgid "Regenerate token"
 msgstr ""
 
-#: templates/member/profile_detail.html:11
+#: templates/member/profile_alias.html:10
+msgid "Add alias"
+msgstr ""
+
+#: templates/member/profile_detail.html:15
 msgid "first name"
 msgstr ""
 
-#: templates/member/profile_detail.html:14
+#: templates/member/profile_detail.html:18
 msgid "username"
 msgstr ""
 
-#: templates/member/profile_detail.html:17
+#: templates/member/profile_detail.html:21
 msgid "password"
 msgstr ""
 
-#: templates/member/profile_detail.html:20
+#: templates/member/profile_detail.html:24
 msgid "Change password"
 msgstr ""
 
-#: templates/member/profile_detail.html:38
+#: templates/member/profile_detail.html:42
 msgid "Manage auth token"
 msgstr ""
 
-#: templates/member/profile_detail.html:54
+#: templates/member/profile_detail.html:49
+msgid "View Profile"
+msgstr ""
+
+#: templates/member/profile_detail.html:62
 msgid "View my memberships"
 msgstr ""
 
@@ -494,12 +648,87 @@ msgstr ""
 msgid "Save Changes"
 msgstr ""
 
+#: templates/member/signup.html:5 templates/member/signup.html:8
 #: templates/member/signup.html:14
-msgid "Sign Up"
+msgid "Sign up"
 msgstr ""
 
-#: templates/note/transaction_form.html:35
-msgid "Transfer"
+#: templates/note/conso_form.html:28 templates/note/transaction_form.html:38
+msgid "Select emitters"
+msgstr ""
+
+#: templates/note/conso_form.html:45
+msgid "Select consumptions"
+msgstr ""
+
+#: templates/note/conso_form.html:51
+msgid "Consume!"
+msgstr ""
+
+#: templates/note/conso_form.html:64
+msgid "Most used buttons"
+msgstr ""
+
+#: templates/note/conso_form.html:121
+msgid "Edit"
+msgstr ""
+
+#: templates/note/conso_form.html:126
+msgid "Single consumptions"
+msgstr ""
+
+#: templates/note/conso_form.html:130
+msgid "Double consumptions"
+msgstr ""
+
+#: templates/note/conso_form.html:141
+msgid "Recent transactions history"
+msgstr ""
+
+#: templates/note/transaction_form.html:55
+msgid "External payment"
+msgstr ""
+
+#: templates/note/transaction_form.html:63
+msgid "Transfer type"
+msgstr ""
+
+#: templates/note/transaction_form.html:73
+msgid "Name"
+msgstr ""
+
+#: templates/note/transaction_form.html:79
+msgid "First name"
+msgstr ""
+
+#: templates/note/transaction_form.html:85
+msgid "Bank"
+msgstr ""
+
+#: templates/note/transaction_form.html:97
+#: templates/note/transaction_form.html:179
+#: templates/note/transaction_form.html:186
+msgid "Select receivers"
+msgstr ""
+
+#: templates/note/transaction_form.html:114
+msgid "Amount"
+msgstr ""
+
+#: templates/note/transaction_form.html:119
+msgid "Reason"
+msgstr ""
+
+#: templates/note/transaction_form.html:193
+msgid "Credit note"
+msgstr ""
+
+#: templates/note/transaction_form.html:200
+msgid "Debit note"
+msgstr ""
+
+#: templates/note/transactiontemplate_form.html:6
+msgid "Buttons list"
 msgstr ""
 
 #: templates/registration/logged_out.html:8
@@ -511,7 +740,7 @@ msgid "Log in again"
 msgstr ""
 
 #: templates/registration/login.html:7 templates/registration/login.html:8
-#: templates/registration/login.html:22
+#: templates/registration/login.html:26
 #: templates/registration/password_reset_complete.html:10
 msgid "Log in"
 msgstr ""
@@ -523,7 +752,7 @@ msgid ""
 "page. Would you like to login to a different account?"
 msgstr ""
 
-#: templates/registration/login.html:23
+#: templates/registration/login.html:27
 msgid "Forgotten your password or username?"
 msgstr ""
 
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index e73417406cf66d2ac35eaa003746c10b05a618ee..5e6e94704655dc49e1fb27ae2552aab43dc21301 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-02-27 17:39+0100\n"
+"POT-Creation-Date: 2020-03-16 11:53+0100\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,9 +18,10 @@ msgid "activity"
 msgstr "activité"
 
 #: apps/activity/models.py:19 apps/activity/models.py:44
-#: apps/member/models.py:60 apps/member/models.py:111
-#: apps/note/models/notes.py:184 apps/note/models/transactions.py:24
-#: apps/note/models/transactions.py:44 templates/member/profile_detail.html:11
+#: apps/member/models.py:61 apps/member/models.py:112
+#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
+#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:202
+#: templates/member/profile_detail.html:15
 msgid "name"
 msgstr "nom"
 
@@ -44,8 +45,8 @@ msgstr "types d'activité"
 msgid "description"
 msgstr "description"
 
-#: apps/activity/models.py:54 apps/note/models/notes.py:160
-#: apps/note/models/transactions.py:62
+#: apps/activity/models.py:54 apps/note/models/notes.py:164
+#: apps/note/models/transactions.py:62 apps/note/models/transactions.py:115
 msgid "type"
 msgstr "type"
 
@@ -81,47 +82,59 @@ msgstr "invités"
 msgid "API"
 msgstr ""
 
-#: apps/logs/apps.py:10
+#: apps/logs/apps.py:11
 msgid "Logs"
 msgstr ""
 
-#: apps/logs/models.py:20 apps/note/models/notes.py:105
+#: apps/logs/models.py:21 apps/note/models/notes.py:117
 msgid "user"
 msgstr "utilisateur"
 
 #: apps/logs/models.py:27
+msgid "IP Address"
+msgstr "Adresse IP"
+
+#: apps/logs/models.py:35
 msgid "model"
 msgstr "Modèle"
 
-#: apps/logs/models.py:34
+#: apps/logs/models.py:42
 msgid "identifier"
 msgstr "Identifiant"
 
-#: apps/logs/models.py:39
+#: apps/logs/models.py:47
 msgid "previous data"
 msgstr "Données précédentes"
 
-#: apps/logs/models.py:44
-#, fuzzy
-#| msgid "end date"
+#: apps/logs/models.py:52
 msgid "new data"
 msgstr "Nouvelles données"
 
-#: apps/logs/models.py:51
-#, fuzzy
-#| msgid "section"
+#: apps/logs/models.py:60
+msgid "create"
+msgstr "Créer"
+
+#: apps/logs/models.py:61
+msgid "edit"
+msgstr "Modifier"
+
+#: apps/logs/models.py:62
+msgid "delete"
+msgstr "Supprimer"
+
+#: apps/logs/models.py:65
 msgid "action"
 msgstr "Action"
 
-#: apps/logs/models.py:59
+#: apps/logs/models.py:73
 msgid "timestamp"
 msgstr "Date"
 
-#: apps/logs/models.py:63
+#: apps/logs/models.py:77
 msgid "Logs cannot be destroyed."
 msgstr "Les logs ne peuvent pas être détruits."
 
-#: apps/member/apps.py:10
+#: apps/member/apps.py:14
 msgid "member"
 msgstr "adhérent"
 
@@ -129,7 +142,7 @@ msgstr "adhérent"
 msgid "phone number"
 msgstr "numéro de téléphone"
 
-#: apps/member/models.py:29 templates/member/profile_detail.html:24
+#: apps/member/models.py:29 templates/member/profile_detail.html:28
 msgid "section"
 msgstr "section"
 
@@ -137,7 +150,7 @@ msgstr "section"
 msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
 msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
 
-#: apps/member/models.py:36 templates/member/profile_detail.html:27
+#: apps/member/models.py:36 templates/member/profile_detail.html:31
 msgid "address"
 msgstr "adresse"
 
@@ -149,37 +162,37 @@ msgstr "payé"
 msgid "user profile"
 msgstr "profil utilisateur"
 
-#: apps/member/models.py:65
+#: apps/member/models.py:66
 msgid "email"
 msgstr "courriel"
 
-#: apps/member/models.py:70
+#: apps/member/models.py:71
 msgid "membership fee"
 msgstr "cotisation pour adhérer"
 
-#: apps/member/models.py:74
+#: apps/member/models.py:75
 msgid "membership duration"
 msgstr "durée de l'adhésion"
 
-#: apps/member/models.py:75
+#: apps/member/models.py:76
 msgid "The longest time a membership can last (NULL = infinite)."
 msgstr "La durée maximale d'une adhésion (NULL = infinie)."
 
-#: apps/member/models.py:80
+#: apps/member/models.py:81
 msgid "membership start"
 msgstr "début de l'adhésion"
 
-#: apps/member/models.py:81
+#: apps/member/models.py:82
 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:86
+#: apps/member/models.py:87
 msgid "membership end"
 msgstr "fin de l'adhésion"
 
-#: apps/member/models.py:87
+#: apps/member/models.py:88
 msgid ""
 "How long the membership can last after January 1st of the next year after "
 "members can renew their membership."
@@ -187,166 +200,174 @@ 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:93 apps/note/models/notes.py:135
+#: apps/member/models.py:94 apps/note/models/notes.py:139
 msgid "club"
 msgstr "club"
 
-#: apps/member/models.py:94
+#: apps/member/models.py:95
 msgid "clubs"
 msgstr "clubs"
 
-#: apps/member/models.py:117
+#: apps/member/models.py:118
 msgid "role"
 msgstr "rôle"
 
-#: apps/member/models.py:118
+#: apps/member/models.py:119
 msgid "roles"
 msgstr "rôles"
 
-#: apps/member/models.py:142
+#: apps/member/models.py:143
 msgid "membership starts on"
 msgstr "l'adhésion commence le"
 
-#: apps/member/models.py:145
+#: apps/member/models.py:146
 msgid "membership ends on"
 msgstr "l'adhésion finie le"
 
-#: apps/member/models.py:149
+#: apps/member/models.py:150
 msgid "fee"
 msgstr "cotisation"
 
-#: apps/member/models.py:153
+#: apps/member/models.py:154
 msgid "membership"
 msgstr "adhésion"
 
-#: apps/member/models.py:154
+#: apps/member/models.py:155
 msgid "memberships"
 msgstr "adhésions"
 
-#: apps/member/views.py:63 templates/member/profile_detail.html:42
+#: apps/member/views.py:69 templates/member/profile_detail.html:46
 msgid "Update Profile"
 msgstr "Modifier le profil"
 
-#: apps/member/views.py:79
+#: apps/member/views.py:82
 msgid "An alias with a similar name already exists."
 msgstr "Un alias avec un nom similaire existe déjà."
 
-#: apps/member/views.py:130
+#: apps/member/views.py:132
 #, python-format
 msgid "Account #%(id)s: %(username)s"
 msgstr "Compte n°%(id)s : %(username)s"
 
-#: apps/note/admin.py:120 apps/note/models/transactions.py:93
+#: apps/member/views.py:202
+msgid "Alias successfully deleted"
+msgstr "L'alias a bien été supprimé"
+
+#: apps/note/admin.py:120 apps/note/models/transactions.py:94
 msgid "source"
 msgstr "source"
 
 #: apps/note/admin.py:128 apps/note/admin.py:156
-#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:99
+#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:100
 msgid "destination"
 msgstr "destination"
 
-#: apps/note/apps.py:14 apps/note/models/notes.py:54
+#: apps/note/apps.py:14 apps/note/models/notes.py:58
 msgid "note"
 msgstr "note"
 
-#: apps/note/forms.py:49
-msgid "Source and destination must be different."
-msgstr "La source et la destination doivent être différentes."
+#: apps/note/forms.py:20
+msgid "New Alias"
+msgstr "Nouvel alias"
+
+#: apps/note/forms.py:25
+msgid "select an image"
+msgstr "Choisissez une image"
 
-#: apps/note/models/notes.py:26
+#: apps/note/forms.py:26
+msgid "Maximal size: 2MB"
+msgstr "Taille maximale : 2 Mo"
+
+#: apps/note/models/notes.py:27
 msgid "account balance"
 msgstr "solde du compte"
 
-#: apps/note/models/notes.py:27
+#: apps/note/models/notes.py:28
 msgid "in centimes, money credited for this instance"
 msgstr "en centimes, argent crédité pour cette instance"
 
-#: apps/note/models/notes.py:31
+#: apps/note/models/notes.py:32
 msgid "last negative date"
 msgstr "dernier date de négatif"
 
-#: apps/note/models/notes.py:32
+#: apps/note/models/notes.py:33
 msgid "last time the balance was negative"
 msgstr "dernier instant où la note était en négatif"
 
-#: apps/note/models/notes.py:37
+#: apps/note/models/notes.py:38
 msgid "active"
 msgstr "actif"
 
-#: apps/note/models/notes.py:40
+#: apps/note/models/notes.py:41
 msgid ""
 "Designates whether this note should be treated as active. Unselect this "
 "instead of deleting notes."
 msgstr ""
 "Indique si la note est active. Désactiver cela plutôt que supprimer la note."
 
-#: apps/note/models/notes.py:44
+#: apps/note/models/notes.py:45
 msgid "display image"
 msgstr "image affichée"
 
-#: apps/note/models/notes.py:49 apps/note/models/transactions.py:102
+#: apps/note/models/notes.py:53 apps/note/models/transactions.py:103
 msgid "created at"
 msgstr "créée le"
 
-#: apps/note/models/notes.py:55
+#: apps/note/models/notes.py:59
 msgid "notes"
 msgstr "notes"
 
-#: apps/note/models/notes.py:63
+#: apps/note/models/notes.py:67
 msgid "Note"
 msgstr "Note"
 
-#: apps/note/models/notes.py:73 apps/note/models/notes.py:97
+#: apps/note/models/notes.py:77 apps/note/models/notes.py:101
 msgid "This alias is already taken."
 msgstr "Cet alias est déjà pris."
 
-#: apps/note/models/notes.py:113
-msgid "user"
-msgstr "utilisateur"
-
-#: apps/note/models/notes.py:117
+#: apps/note/models/notes.py:121
 msgid "one's note"
 msgstr "note d'un utilisateur"
 
-#: apps/note/models/notes.py:118
+#: apps/note/models/notes.py:122
 msgid "users note"
 msgstr "notes des utilisateurs"
 
-#: apps/note/models/notes.py:124
+#: apps/note/models/notes.py:128
 #, python-format
 msgid "%(user)s's note"
 msgstr "Note de %(user)s"
 
-#: apps/note/models/notes.py:139
+#: apps/note/models/notes.py:143
 msgid "club note"
 msgstr "note d'un club"
 
-#: apps/note/models/notes.py:140
+#: apps/note/models/notes.py:144
 msgid "clubs notes"
 msgstr "notes des clubs"
 
-#: apps/note/models/notes.py:146
+#: apps/note/models/notes.py:150
 #, python-format
 msgid "Note of %(club)s club"
 msgstr "Note du club %(club)s"
 
-#: apps/note/models/notes.py:166
+#: apps/note/models/notes.py:170
 msgid "special note"
 msgstr "note spéciale"
 
-#: apps/note/models/notes.py:167
+#: apps/note/models/notes.py:171
 msgid "special notes"
 msgstr "notes spéciales"
 
-#: apps/note/models/notes.py:190
+#: apps/note/models/notes.py:194
 msgid "Invalid alias"
 msgstr "Alias invalide"
 
-#: apps/note/models/notes.py:206
+#: apps/note/models/notes.py:210
 msgid "alias"
 msgstr "alias"
 
-#: apps/note/models/notes.py:207 templates/member/profile_detail.html:33
+#: apps/note/models/notes.py:211 templates/member/profile_detail.html:37
 msgid "aliases"
 msgstr "alias"
 
@@ -355,10 +376,10 @@ msgid "Alias is too long."
 msgstr "L'alias est trop long."
 
 #: apps/note/models/notes.py:238
-msgid "An alias with a similar name already exists:"
-msgstr "Un alias avec un nom similaire existe déjà."
+msgid "An alias with a similar name already exists: {} "
+msgstr "Un alias avec un nom similaire existe déjà : {}"
 
-#: apps/note/models/notes.py:246
+#: apps/note/models/notes.py:247
 msgid "You can't delete your main alias."
 msgstr "Vous ne pouvez pas supprimer votre alias principal."
 
@@ -371,11 +392,10 @@ msgid "transaction categories"
 msgstr "catégories de transaction"
 
 #: apps/note/models/transactions.py:47
-#, fuzzy
 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:109
+#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:111
 msgid "amount"
 msgstr "montant"
 
@@ -383,59 +403,96 @@ msgstr "montant"
 msgid "in centimes"
 msgstr "en centimes"
 
-#: apps/note/models/transactions.py:74
+#: apps/note/models/transactions.py:75
 msgid "transaction template"
 msgstr "modèle de transaction"
 
-#: apps/note/models/transactions.py:75
+#: apps/note/models/transactions.py:76
 msgid "transaction templates"
 msgstr "modèles de transaction"
 
-#: apps/note/models/transactions.py:106
+#: apps/note/models/transactions.py:107
 msgid "quantity"
 msgstr "quantité"
 
-#: apps/note/models/transactions.py:111
+#: apps/note/models/transactions.py:117 templates/note/transaction_form.html:15
+msgid "Gift"
+msgstr "Don"
+
+#: apps/note/models/transactions.py:118 templates/base.html:90
+#: templates/note/transaction_form.html:19
+#: templates/note/transaction_form.html:126
+msgid "Transfer"
+msgstr "Virement"
+
+#: apps/note/models/transactions.py:119
+msgid "Template"
+msgstr "Bouton"
+
+#: apps/note/models/transactions.py:120 templates/note/transaction_form.html:23
+msgid "Credit"
+msgstr "Crédit"
+
+#: apps/note/models/transactions.py:121 templates/note/transaction_form.html:27
+msgid "Debit"
+msgstr "Retrait"
+
+#: apps/note/models/transactions.py:122 apps/note/models/transactions.py:230
+msgid "membership transaction"
+msgstr "transaction d'adhésion"
+
+#: apps/note/models/transactions.py:129
 msgid "reason"
 msgstr "raison"
 
-#: apps/note/models/transactions.py:115
+#: apps/note/models/transactions.py:133
 msgid "valid"
 msgstr "valide"
 
-#: apps/note/models/transactions.py:120
+#: apps/note/models/transactions.py:138
 msgid "transaction"
 msgstr "transaction"
 
-#: apps/note/models/transactions.py:121
+#: apps/note/models/transactions.py:139
 msgid "transactions"
 msgstr "transactions"
 
-#: apps/note/models/transactions.py:184
-msgid "membership transaction"
-msgstr "transaction d'adhésion"
+#: apps/note/models/transactions.py:207
+msgid "first_name"
+msgstr "Prénom"
 
-#: apps/note/models/transactions.py:185
+#: apps/note/models/transactions.py:212
+msgid "bank"
+msgstr "Banque"
+
+#: apps/note/models/transactions.py:231
 msgid "membership transactions"
 msgstr "transactions d'adhésion"
 
-#: apps/note/views.py:29
-msgid "Transfer money from your account to one or others"
-msgstr "Transfert d'argent de ton compte vers un ou plusieurs autres"
+#: apps/note/views.py:31
+msgid "Transfer money"
+msgstr "Transferts d'argent"
 
-#: apps/note/views.py:138
-msgid "Consommations"
-msgstr "transactions"
+#: apps/note/views.py:132 templates/base.html:78
+msgid "Consumptions"
+msgstr "Consommations"
 
-#: note_kfet/settings/base.py:155
-msgid "German"
+#: note_kfet/settings/__init__.py:61
+msgid ""
+"The Central Authentication Service grants you access to most of our websites "
+"by authenticating only once, so you don't need to type your credentials "
+"again unless your session expires or you logout."
 msgstr ""
 
 #: note_kfet/settings/base.py:156
-msgid "English"
+msgid "German"
 msgstr ""
 
 #: note_kfet/settings/base.py:157
+msgid "English"
+msgstr ""
+
+#: note_kfet/settings/base.py:158
 msgid "French"
 msgstr ""
 
@@ -443,6 +500,80 @@ msgstr ""
 msgid "The ENS Paris-Saclay BDE note."
 msgstr "La note du BDE de l'ENS Paris-Saclay."
 
+#: templates/base.html:81
+msgid "Clubs"
+msgstr "Clubs"
+
+#: templates/base.html:84
+msgid "Activities"
+msgstr "Activités"
+
+#: templates/base.html:87
+msgid "Buttons"
+msgstr "Boutons"
+
+#: templates/cas_server/base.html:7
+msgid "Central Authentication Service"
+msgstr ""
+
+#: templates/cas_server/base.html:43
+#, python-format
+msgid ""
+"A new version of the application is available. This instance runs "
+"%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
+"upgrading."
+msgstr ""
+
+#: templates/cas_server/logged.html:4
+msgid ""
+"<h3>Log In Successful</h3>You have successfully logged into the Central "
+"Authentication Service.<br/>For security reasons, please Log Out and Exit "
+"your web browser when you are done accessing services that require "
+"authentication!"
+msgstr ""
+
+#: templates/cas_server/logged.html:8
+msgid "Log me out from all my sessions"
+msgstr ""
+
+#: templates/cas_server/logged.html:14
+msgid "Forget the identity provider"
+msgstr ""
+
+#: templates/cas_server/logged.html:18
+msgid "Logout"
+msgstr ""
+
+#: templates/cas_server/login.html:6
+msgid "Please log in"
+msgstr ""
+
+#: templates/cas_server/login.html:11
+msgid ""
+"If you don't have any Note Kfet account, please follow <a href='/accounts/"
+"signup'>this link to sign up</a>."
+msgstr ""
+"Si vous n'avez pas de compte Note Kfet, veuillez suivre <a href='/accounts/"
+"signup'>ce lien pour vous inscrire</a>."
+
+#: templates/cas_server/login.html:17
+msgid "Login"
+msgstr ""
+
+#: templates/cas_server/warn.html:9
+msgid "Connect to the service"
+msgstr ""
+
+#: templates/django_filters/rest_framework/crispy_form.html:4
+#: templates/django_filters/rest_framework/form.html:2
+msgid "Field filters"
+msgstr ""
+
+#: templates/django_filters/rest_framework/form.html:5
+#: templates/member/club_form.html:10
+msgid "Submit"
+msgstr "Envoyer"
+
 #: templates/member/club_detail.html:10
 msgid "Membership starts on"
 msgstr "L'adhésion commence le"
@@ -455,10 +586,22 @@ msgstr "L'adhésion finie le"
 msgid "Membership duration"
 msgstr "Durée de l'adhésion"
 
-#: templates/member/club_detail.html:18 templates/member/profile_detail.html:30
+#: templates/member/club_detail.html:18 templates/member/profile_detail.html:34
 msgid "balance"
 msgstr "solde du compte"
 
+#: templates/member/club_detail.html:51 templates/member/profile_detail.html:75
+msgid "Transaction history"
+msgstr "Historique des transactions"
+
+#: templates/member/club_form.html:6
+msgid "Clubs list"
+msgstr "Liste des clubs"
+
+#: templates/member/club_list.html:8
+msgid "New club"
+msgstr "Nouveau club"
+
 #: templates/member/manage_auth_tokens.html:16
 msgid "Token"
 msgstr "Jeton"
@@ -471,33 +614,35 @@ msgstr "Créé le"
 msgid "Regenerate token"
 msgstr "Regénérer le jeton"
 
-#: templates/member/profile_detail.html:11
+#: templates/member/profile_alias.html:10
+msgid "Add alias"
+msgstr "Ajouter un alias"
+
+#: templates/member/profile_detail.html:15
 msgid "first name"
 msgstr ""
 
-#: templates/member/profile_detail.html:14
+#: templates/member/profile_detail.html:18
 msgid "username"
 msgstr ""
 
-#: templates/member/profile_detail.html:17
-#, fuzzy
-#| msgid "Change password"
+#: templates/member/profile_detail.html:21
 msgid "password"
 msgstr ""
 
-#: templates/member/profile_detail.html:20
+#: templates/member/profile_detail.html:24
 msgid "Change password"
 msgstr "Changer le mot de passe"
 
-#: templates/member/profile_detail.html:38
+#: templates/member/profile_detail.html:42
 msgid "Manage auth token"
 msgstr "Gérer les jetons d'authentification"
 
-#: templates/member/profile_detail.html:51
-msgid "Transaction history"
-msgstr "Historique des transactions"
+#: templates/member/profile_detail.html:49
+msgid "View Profile"
+msgstr "Voir le profil"
 
-#: templates/member/profile_detail.html:54
+#: templates/member/profile_detail.html:62
 msgid "View my memberships"
 msgstr "Voir mes adhésions"
 
@@ -505,13 +650,88 @@ 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 ""
+msgid "Sign up"
+msgstr "Inscription"
 
-#: templates/note/transaction_form.html:35
-msgid "Transfer"
-msgstr "Virement"
+#: templates/note/conso_form.html:28 templates/note/transaction_form.html:38
+msgid "Select emitters"
+msgstr "Sélection des émetteurs"
+
+#: templates/note/conso_form.html:45
+msgid "Select consumptions"
+msgstr "Consommations"
+
+#: templates/note/conso_form.html:51
+msgid "Consume!"
+msgstr "Consommer !"
+
+#: templates/note/conso_form.html:64
+msgid "Most used buttons"
+msgstr "Boutons les plus utilisés"
+
+#: templates/note/conso_form.html:121
+msgid "Edit"
+msgstr "Éditer"
+
+#: templates/note/conso_form.html:126
+msgid "Single consumptions"
+msgstr "Consos simples"
+
+#: templates/note/conso_form.html:130
+msgid "Double consumptions"
+msgstr "Consos doubles"
+
+#: templates/note/conso_form.html:141
+msgid "Recent transactions history"
+msgstr "Historique des transactions récentes"
+
+#: templates/note/transaction_form.html:55
+msgid "External payment"
+msgstr "Paiement extérieur"
+
+#: templates/note/transaction_form.html:63
+msgid "Transfer type"
+msgstr "Type de transfert"
+
+#: templates/note/transaction_form.html:73
+msgid "Name"
+msgstr "Nom"
+
+#: templates/note/transaction_form.html:79
+msgid "First name"
+msgstr "Prénom"
+
+#: templates/note/transaction_form.html:85
+msgid "Bank"
+msgstr "Banque"
+
+#: templates/note/transaction_form.html:97
+#: templates/note/transaction_form.html:179
+#: templates/note/transaction_form.html:186
+msgid "Select receivers"
+msgstr "Sélection des destinataires"
+
+#: templates/note/transaction_form.html:114
+msgid "Amount"
+msgstr "Montant"
+
+#: templates/note/transaction_form.html:119
+msgid "Reason"
+msgstr "Raison"
+
+#: templates/note/transaction_form.html:193
+msgid "Credit note"
+msgstr "Note à créditer"
+
+#: templates/note/transaction_form.html:200
+msgid "Debit note"
+msgstr "Note à débiter"
+
+#: templates/note/transactiontemplate_form.html:6
+msgid "Buttons list"
+msgstr "Liste des boutons"
 
 #: templates/registration/logged_out.html:8
 msgid "Thanks for spending some quality time with the Web site today."
@@ -522,7 +742,7 @@ msgid "Log in again"
 msgstr ""
 
 #: templates/registration/login.html:7 templates/registration/login.html:8
-#: templates/registration/login.html:22
+#: templates/registration/login.html:26
 #: templates/registration/password_reset_complete.html:10
 msgid "Log in"
 msgstr ""
@@ -534,7 +754,7 @@ msgid ""
 "page. Would you like to login to a different account?"
 msgstr ""
 
-#: templates/registration/login.html:23
+#: templates/registration/login.html:27
 msgid "Forgotten your password or username?"
 msgstr ""
 
diff --git a/nginx_note.conf_example b/nginx_note.conf_example
index 1f7ce4caf3c4f90371bfbd017cfed32e761abcca..204784d06ff0d4d6b0c35f0c5ff426f2b726e325 100644
--- a/nginx_note.conf_example
+++ b/nginx_note.conf_example
@@ -9,7 +9,7 @@ server {
     # the port your site will be served on
     listen      80;
     # the domain name it will serve for
-    server_name note.comby.xyz; # substitute your machine's IP address or FQDN
+    server_name note.example.org; # substitute your machine's IP address or FQDN
     charset     utf-8;
 
     # max upload size
diff --git a/note_kfet/fixtures/cas.json b/note_kfet/fixtures/cas.json
new file mode 100644
index 0000000000000000000000000000000000000000..c3109d19d402cb277797dd5a1fbaf1b34bf75cbc
--- /dev/null
+++ b/note_kfet/fixtures/cas.json
@@ -0,0 +1,11 @@
+[
+    {
+        "model": "cas_server.servicepattern",
+        "pk": 1,
+        "fields": {
+            "pos": 1,
+            "pattern": ".*",
+            "name": "REPLACEME"
+        }
+    }
+]
diff --git a/note_kfet/fixtures/initial.json b/note_kfet/fixtures/initial.json
index 1b7799807cb835ced23a44adf79ee72dfdeae398..72e472340120c572d90d5306b1942b32b48d71e9 100644
--- a/note_kfet/fixtures/initial.json
+++ b/note_kfet/fixtures/initial.json
@@ -6,14 +6,5 @@
             "domain": "localhost",
             "name": "La Note Kfet \ud83c\udf7b"
         }
-    },
-    {
-        "model": "cas_server.servicepattern",
-        "pk": 1,
-        "fields": {
-            "pos": 1,
-            "pattern": ".*",
-            "name": "REPLACEME"
-        }
     }
-]
\ No newline at end of file
+]
diff --git a/note_kfet/middlewares.py b/note_kfet/middlewares.py
index 73b87e363c32faf1d0fa836122fb2a2674347894..b034e2bee3453486a1fb10fbabd687de9c70227f 100644
--- a/note_kfet/middlewares.py
+++ b/note_kfet/middlewares.py
@@ -1,10 +1,6 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
-from django.http import HttpResponseRedirect
-
-from urllib.parse import urlencode, parse_qs, urlsplit, urlunsplit
-
 
 class TurbolinksMiddleware(object):
     """
@@ -35,4 +31,3 @@ class TurbolinksMiddleware(object):
                     location = request.session.pop('_turbolinks_redirect_to')
                     response['Turbolinks-Location'] = location
         return response
-
diff --git a/note_kfet/settings/__init__.py b/note_kfet/settings/__init__.py
index 68a40b887c39d6dde8bd55514cbea624048772e3..28935deba3ee7ba6fc7aeb9fa6d7f5ea383a2623 100644
--- a/note_kfet/settings/__init__.py
+++ b/note_kfet/settings/__init__.py
@@ -1,8 +1,12 @@
-import os
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.utils.translation import gettext_lazy as _
 import re
 
 from .base import *
 
+
 def read_env():
     """Pulled from Honcho code with minor updates, reads local default
     environment variables from a .env file located in the project root
@@ -25,22 +29,53 @@ def read_env():
                 val = re.sub(r'\\(.)', r'\1', m3.group(1))
             os.environ.setdefault(key, val)
 
+
 read_env()
 
 app_stage = os.environ.get('DJANGO_APP_STAGE', 'dev')
 if app_stage == 'prod':
     from .production import *
-    DATABASES["default"]["PASSWORD"] = os.environ.get('DJANGO_DB_PASSWORD','CHANGE_ME_IN_ENV_SETTINGS')
-    SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY','CHANGE_ME_IN_ENV_SETTINGS')
-    ALLOWED_HOSTS.append(os.environ.get('ALLOWED_HOSTS','localhost'))
 else:
     from .development import *
 
 try:
+    #in secrets.py defines everything you want
     from .secrets import *
 except ImportError:
     pass
 
-# env variables set at the of in /env/bin/activate
-# don't forget to unset in deactivate !
+if "cas" in INSTALLED_APPS:
+    MIDDLEWARE += ['cas.middleware.CASMiddleware']
+    # CAS Settings
+    CAS_SERVER_URL = "https://" + os.getenv("NOTE_URL", "note.example.com") + "/cas/"
+    CAS_AUTO_CREATE_USER = False
+    CAS_LOGO_URL = "/static/img/Saperlistpopette.png"
+    CAS_FAVICON_URL = "/static/favicon/favicon-32x32.png"
+    CAS_SHOW_SERVICE_MESSAGES = True
+    CAS_SHOW_POWERED = False
+    CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT = False
+    CAS_PROVIDE_URL_TO_LOGOUT = True
+    CAS_INFO_MESSAGES = {
+        "cas_explained": {
+            "message": _(
+                u"The Central Authentication Service grants you access to most of our websites by "
+                u"authenticating only once, so you don't need to type your credentials again unless "
+                u"your session expires or you logout."
+            ),
+           "discardable": True,
+            "type": "info",  # one of info, success, info, warning, danger
+       },
+    }
+
+    CAS_INFO_MESSAGES_ORDER = [
+        'cas_explained',
+    ]
+    AUTHENTICATION_BACKENDS += ('cas.backends.CASBackend',)
+
+
+if "logs" in INSTALLED_APPS:
+    MIDDLEWARE += ('logs.middlewares.LogsMiddleware',)
 
+if "debug_toolbar" in INSTALLED_APPS:
+    MIDDLEWARE.insert(1, "debug_toolbar.middleware.DebugToolbarMiddleware")
+    INTERNAL_IPS = ['127.0.0.1']
diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py
index 20937fac17fb198afbf7a5f3d015d0bf483ba6d5..29ff49c52446915b157675df15cc53d23e3bc4bc 100644
--- a/note_kfet/settings/base.py
+++ b/note_kfet/settings/base.py
@@ -37,9 +37,10 @@ INSTALLED_APPS = [
 
     # External apps
     'polymorphic',
-    'reversion',
     'crispy_forms',
     'django_tables2',
+    'cas_server',
+    'cas',
     # Django contrib
     'django.contrib.admin',
     'django.contrib.admindocs',
@@ -55,9 +56,6 @@ INSTALLED_APPS = [
     # Autocomplete
     'dal',
     'dal_select2',
-    # CAS
-    'cas_server',
-    'cas',
 
     # Note apps
     'activity',
@@ -81,7 +79,6 @@ MIDDLEWARE = [
     'django.middleware.locale.LocaleMiddleware',
     'django.contrib.sites.middleware.CurrentSiteMiddleware',
     'note_kfet.middlewares.TurbolinksMiddleware',
-    'cas.middleware.CASMiddleware',
 ]
 
 ROOT_URLCONF = 'note_kfet.urls'
@@ -98,7 +95,7 @@ TEMPLATES = [
                 'django.contrib.auth.context_processors.auth',
                 'django.contrib.messages.context_processors.messages',
                 'django.template.context_processors.request',
-              #  'django.template.context_processors.media',
+                #  'django.template.context_processors.media',
             ],
         },
     },
@@ -133,7 +130,7 @@ PASSWORD_HASHERS = [
 # Django Guardian object permissions
 
 AUTHENTICATION_BACKENDS = (
-    #'django.contrib.auth.backends.ModelBackend',  # this is default
+    #  'django.contrib.auth.backends.ModelBackend',  # this is default
     'member.backends.PermissionBackend',
     'cas.backends.CASBackend',
 )
@@ -146,12 +143,13 @@ REST_FRAMEWORK = {
         'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
     ],
     'DEFAULT_AUTHENTICATION_CLASSES': [
+        'rest_framework.authentication.SessionAuthentication',
         'rest_framework.authentication.TokenAuthentication',
-    ]
+    ],
+    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
+    'PAGE_SIZE': 20,
 }
 
-ANONYMOUS_USER_NAME = None  # Disable guardian anonymous user
-
 # Internationalization
 # https://docs.djangoproject.com/en/2.2/topics/i18n/
 
@@ -182,7 +180,7 @@ FIXTURE_DIRS = [os.path.join(BASE_DIR, "note_kfet/fixtures")]
 # Don't put anything in this directory yourself; store your static files
 # in apps' "static/" subdirectories and in STATICFILES_DIRS.
 # Example: "/var/www/example.com/static/"
-STATIC_ROOT = os.path.join(BASE_DIR,"static/")
+STATIC_ROOT = os.path.join(BASE_DIR, "static/")
 # STATICFILES_DIRS = [
 #    os.path.join(BASE_DIR, 'static')]
 STATICFILES_DIRS = []
@@ -194,15 +192,9 @@ STATIC_URL = '/static/'
 
 ALIAS_VALIDATOR_REGEX = r''
 
-MEDIA_ROOT=os.path.join(BASE_DIR,"media")
-MEDIA_URL='/media/'
+MEDIA_ROOT = os.path.join(BASE_DIR, "media")
+MEDIA_URL = '/media/'
 
 # Profile Picture Settings
 PIC_WIDTH = 200
 PIC_RATIO = 1
-
-# CAS Settings
-CAS_AUTO_CREATE_USER = False
-CAS_LOGO_URL = "/static/img/Saperlistpopette.png"
-CAS_FAVICON_URL = "/static/favicon/favicon-32x32.png"
-
diff --git a/note_kfet/settings/development.py b/note_kfet/settings/development.py
index ad2cd2f1028d10e3fd2044e19f0e153c487e5832..66ad4fd44ed80b718cb1b430d9ad97b6692b6ba5 100644
--- a/note_kfet/settings/development.py
+++ b/note_kfet/settings/development.py
@@ -11,17 +11,30 @@
 #  - and more ...
 
 
+import os
+
 # Database
 # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
 from . import *
-import os
 
-DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.sqlite3',
-        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+if os.getenv("DJANGO_DEV_STORE_METHOD", "sqllite") == "postgresql":
+    DATABASES = {
+        'default': {
+            'ENGINE': 'django.db.backends.postgresql_psycopg2',
+            'NAME': os.environ.get('DJANGO_DB_NAME', 'note_db'),
+            'USER': os.environ.get('DJANGO_DB_USER', 'note'),
+            'PASSWORD': os.environ.get('DJANGO_DB_PASSWORD', 'CHANGE_ME_IN_ENV_SETTINGS'),
+            'HOST': os.environ.get('DJANGO_DB_HOST', 'localhost'),
+            'PORT': os.environ.get('DJANGO_DB_PORT', ''),  # Use default port
+        }
+    }
+else:
+    DATABASES = {
+        'default': {
+            'ENGINE': 'django.db.backends.sqlite3',
+            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+        }
     }
-}
 
 # Break it, fix it!
 DEBUG = True
@@ -38,7 +51,7 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 # EMAIL_HOST_USER = 'change_me'
 # EMAIL_HOST_PASSWORD = 'change_me'
 
-SERVER_EMAIL = 'no-reply@example.org'
+SERVER_EMAIL = 'no-reply@' + os.getenv("DOMAIN", "example.com")
 
 # Security settings
 SECURE_CONTENT_TYPE_NOSNIFF = False
@@ -51,4 +64,8 @@ SESSION_COOKIE_AGE = 60 * 60 * 3
 
 # CAS Client settings
 # Can be modified in secrets.py
-CAS_SERVER_URL = "https://note.comby.xyz/cas/"
+CAS_SERVER_URL = "http://localhost:8000/cas/"
+
+STATIC_ROOT = ''  # not needed in development settings
+STATICFILES_DIRS = [
+    os.path.join(BASE_DIR, 'static')]
diff --git a/note_kfet/settings/production.py b/note_kfet/settings/production.py
index 353d7b8a39f8a844c60f7376f30586508b6882e7..5be8a3b899c5093272b2db12c4aaab44060c233c 100644
--- a/note_kfet/settings/production.py
+++ b/note_kfet/settings/production.py
@@ -1,6 +1,8 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
+import os
+
 ########################
 # Production  Settings #
 ########################
@@ -14,11 +16,11 @@
 DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.postgresql_psycopg2',
-        'NAME': 'note_db',
-        'USER': 'note',
-        'PASSWORD': 'update_in_env_variable',
-        'HOST': '127.0.0.1',
-        'PORT': '',
+        'NAME': os.environ.get('DJANGO_DB_NAME', 'note_db'),
+        'USER': os.environ.get('DJANGO_DB_USER', 'note'),
+        'PASSWORD': os.environ.get('DJANGO_DB_PASSWORD', 'CHANGE_ME_IN_ENV_SETTINGS'),
+        'HOST': os.environ.get('DJANGO_DB_HOST', 'localhost'),
+        'PORT': os.environ.get('DJANGO_DB_PORT', ''),  # Use default port
     }
 }
 
@@ -26,7 +28,9 @@ DATABASES = {
 DEBUG = True
 
 # Mandatory !
-ALLOWED_HOSTS = ['127.0.0.1','note.comby.xyz']
+ALLOWED_HOSTS = [os.environ.get('NOTE_URL', 'localhost')]
+
+SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'CHANGE_ME_IN_ENV_SETTINGS')
 
 # Emails
 EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
@@ -37,7 +41,7 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 # EMAIL_HOST_USER = 'change_me'
 # EMAIL_HOST_PASSWORD = 'change_me'
 
-SERVER_EMAIL = 'no-reply@example.org'
+SERVER_EMAIL = 'no-reply@' + os.getenv("DOMAIN", "example.com")
 
 # Security settings
 SECURE_CONTENT_TYPE_NOSNIFF = False
@@ -49,4 +53,4 @@ X_FRAME_OPTIONS = 'DENY'
 SESSION_COOKIE_AGE = 60 * 60 * 3
 
 # CAS Client settings
-CAS_SERVER_URL = "https://note.crans.org/cas/"
+CAS_SERVER_URL = "https://" + os.getenv("NOTE_URL", "note.example.com") + "/cas/"
diff --git a/apps/logs/urls.py b/note_kfet/settings/secrets_example.py
similarity index 56%
rename from apps/logs/urls.py
rename to note_kfet/settings/secrets_example.py
index 6d76674c0b0835042b4c6ae15d843d29dd070612..70d17ad4330565837b440b177722e336cc0b6c58 100644
--- a/apps/logs/urls.py
+++ b/note_kfet/settings/secrets_example.py
@@ -1,8 +1,9 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
-app_name = 'logs'
-
-# TODO User interface
-urlpatterns = [
+# CAS
+OPTIONAL_APPS = [
+#    'cas_server',
+#    'cas',
+#    'debug_toolbar'
 ]
diff --git a/note_kfet/urls.py b/note_kfet/urls.py
index a261a9eb9891d9430989d46dfe2f2613e3c77c84..da2f9d6c246833c3962b8b0727869a9585b5c4f9 100644
--- a/note_kfet/urls.py
+++ b/note_kfet/urls.py
@@ -1,13 +1,11 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
+from django.conf import settings
+from django.conf.urls.static import static
 from django.contrib import admin
 from django.urls import path, include
 from django.views.generic import RedirectView
-from django.conf.urls.static import static
-from django.conf import settings
-
-from cas import views as cas_views
 
 urlpatterns = [
     # Dev so redirect to something random
@@ -16,25 +14,34 @@ urlpatterns = [
     # Include project routers
     path('note/', include('note.urls')),
 
-    # Include CAS Client routers
-    path('accounts/login/', cas_views.login, name='login'),
-    path('accounts/logout/', cas_views.logout, name='logout'),
-
     # Include Django Contrib and Core routers
     path('i18n/', include('django.conf.urls.i18n')),
     path('accounts/', include('member.urls')),
     path('accounts/', include('django.contrib.auth.urls')),
     path('admin/doc/', include('django.contrib.admindocs.urls')),
     path('admin/', admin.site.urls),
-
-    # Include CAS Server routers
-    path('cas/', include('cas_server.urls', namespace="cas_server")),
-
-    # Include Django REST API
     path('api/', include('api.urls')),
-
-    path('logs/', include('logs.urls')),
 ]
 
-urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
-urlpatterns += static(settings.STATIC_URL,document_root=settings.STATIC_ROOT)
+urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
+
+
+if "cas_server" in settings.INSTALLED_APPS:
+    urlpatterns += [
+        # 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 = [
+        path('__debug__/', include(debug_toolbar.urls)),
+    ] + urlpatterns
diff --git a/requirements/api.txt b/requirements/api.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8dd9f5f2e0a50d42949b2647a6184f9bd77598a8
--- /dev/null
+++ b/requirements/api.txt
@@ -0,0 +1,3 @@
+djangorestframework==3.9.0
+django-rest-polymorphic==0.1.8
+
diff --git a/requirements.txt b/requirements/base.txt
similarity index 73%
rename from requirements.txt
rename to requirements/base.txt
index 9a5eaa22ac63b20ff4021533bdc6ed668679d3ea..e9dc7635ed8444cc48f0ce6c615cbe812622a9ba 100644
--- a/requirements.txt
+++ b/requirements/base.txt
@@ -4,18 +4,12 @@ defusedxml==0.6.0
 Django~=2.2
 django-allauth==0.39.1
 django-autocomplete-light==3.5.1
-django-cas-client==1.5.3
-django-cas-server==1.1.0
 django-crispy-forms==1.7.2
 django-extensions==2.1.9
 django-filter==2.2.0
 django-polymorphic==2.0.3
-djangorestframework==3.9.0
-django-rest-polymorphic==0.1.8
-django-reversion==3.0.3
 django-tables2==2.1.0
 docutils==0.14
-psycopg2==2.8.4
 idna==2.8
 oauthlib==3.1.0
 Pillow==6.1.0
diff --git a/requirements/cas.txt b/requirements/cas.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d468d2d5077580bf004e625a136f31a1734dcef0
--- /dev/null
+++ b/requirements/cas.txt
@@ -0,0 +1,2 @@
+django-cas-client==1.5.3
+django-cas-server==1.1.0
diff --git a/requirements/production.txt b/requirements/production.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f0b5222826649e71d629934444582238919500d9
--- /dev/null
+++ b/requirements/production.txt
@@ -0,0 +1 @@
+psycopg2==2.8.4
diff --git a/static/js/base.js b/static/js/base.js
new file mode 100644
index 0000000000000000000000000000000000000000..2362375bf7c6f89fc7995f5764f47f7ef9113003
--- /dev/null
+++ b/static/js/base.js
@@ -0,0 +1,281 @@
+// Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+
+/**
+ * Convert balance in cents to a human readable amount
+ * @param value the balance, in cents
+ * @returns {string}
+ */
+function pretty_money(value) {
+    if (value % 100 === 0)
+        return (value < 0 ? "- " : "") + Math.floor(Math.abs(value) / 100) + " €";
+    else
+        return (value < 0 ? "- " : "") + Math.floor(Math.abs(value) / 100) + "."
+            + (Math.abs(value) % 100 < 10 ? "0" : "") + (Math.abs(value) % 100) + " €";
+}
+
+/**
+ * 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
+ */
+function addMsg(msg, alert_type) {
+    let msgDiv = $("#messages");
+    let html = msgDiv.html();
+    html += "<div class=\"alert alert-" + alert_type + " alert-dismissible\">" +
+        "<button class=\"close\" data-dismiss=\"alert\" href=\"#\"><span aria-hidden=\"true\">×</span></button>"
+        + msg + "</div>\n";
+    msgDiv.html(html);
+}
+
+/**
+ * Reload the balance of the user on the right top corner
+ */
+function refreshBalance() {
+    $("#user_balance").load("/ #user_balance");
+}
+
+/**
+ * Query the 20 first matched notes with a given pattern
+ * @param pattern The pattern that is queried
+ * @param fun For each found note with the matched alias `alias`, fun(note, alias) is called.
+ */
+function getMatchedNotes(pattern, fun) {
+    $.getJSON("/api/note/alias/?format=json&alias=" + pattern + "&search=user|club&ordering=normalized_name", fun);
+}
+
+/**
+ * Generate a <li> entry with a given id and text
+ */
+function li(id, text) {
+    return "<li class=\"list-group-item py-1 d-flex justify-content-between align-items-center\"" +
+                " id=\"" + id + "\">" + text + "</li>\n";
+}
+
+/**
+ * Render note name and picture
+ * @param note The note to render
+ * @param alias The alias to be displayed
+ * @param user_note_field
+ * @param profile_pic_field
+ */
+function displayNote(note, alias, user_note_field=null, profile_pic_field=null) {
+    let img = note == null ? null : note.display_image;
+    if (img == null)
+        img = '/media/pic/default.png';
+    if (note !== null && alias !== note.name)
+        alias += " (aka. " + note.name + ")";
+    if (note !== null && user_note_field !== null)
+        $("#" + user_note_field).text(alias + " : " + pretty_money(note.balance));
+    if (profile_pic_field != null)
+        $("#" + profile_pic_field).attr('src', img);
+}
+
+/**
+ * Remove a note from the emitters.
+ * @param d The note to remove
+ * @param note_prefix The prefix of the identifiers of the <li> blocks of the emitters
+ * @param notes_display An array containing the infos of the buyers: [alias, note id, note object, quantity]
+ * @param note_list_id The div block identifier where the notes of the buyers are displayed
+ * @param user_note_field The identifier of the field that display the note of the hovered note (useful in
+ *                        consumptions, put null if not used)
+ * @param profile_pic_field The identifier of the field that display the profile picture of the hovered note
+ *                          (useful in consumptions, put null if not used)
+ * @returns an anonymous function to be compatible with jQuery events
+ */
+function removeNote(d, note_prefix="note", notes_display, note_list_id, user_note_field=null, profile_pic_field=null) {
+    return (function() {
+        let new_notes_display = [];
+        let html = "";
+        notes_display.forEach(function (disp) {
+            if (disp.quantity > 1 || disp.id !== d.id) {
+                disp.quantity -= disp.id === d.id ? 1 : 0;
+                new_notes_display.push(disp);
+                html += li(note_prefix + "_" + disp.id, disp.name
+                    + "<span class=\"badge badge-dark badge-pill\">" + disp.quantity + "</span>");
+            }
+        });
+
+        notes_display.length = 0;
+        new_notes_display.forEach(function(disp) {
+            notes_display.push(disp);
+        });
+
+        $("#" + note_list_id).html(html);
+        notes_display.forEach(function (disp) {
+            let obj = $("#" + note_prefix + "_" + disp.id);
+            obj.click(removeNote(disp, note_prefix, notes_display, note_list_id, user_note_field, profile_pic_field));
+            obj.hover(function() {
+                if (disp.note)
+                    displayNote(disp.note, disp.name, user_note_field, profile_pic_field);
+            });
+        });
+    });
+}
+
+/**
+ * Generate an auto-complete field to query a note with its alias
+ * @param field_id The identifier of the text field where the alias is typed
+ * @param alias_matched_id The div block identifier where the matched aliases are displayed
+ * @param note_list_id The div block identifier where the notes of the buyers are displayed
+ * @param notes An array containing the note objects of the buyers
+ * @param notes_display An array containing the infos of the buyers: [alias, note id, note object, quantity]
+ * @param alias_prefix The prefix of the <li> blocks for the matched aliases
+ * @param note_prefix The prefix of the <li> blocks for the notes of the buyers
+ * @param user_note_field The identifier of the field that display the note of the hovered note (useful in
+ *                        consumptions, put null if not used)
+ * @param profile_pic_field The identifier of the field that display the profile picture of the hovered note
+ *                          (useful in consumptions, put null if not used)
+ * @param alias_click Function that is called when an alias is clicked. If this method exists and doesn't return true,
+ *                    the associated note is not displayed.
+ *                    Useful for a consumption if the item is selected before.
+ */
+function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes_display, alias_prefix="alias",
+                          note_prefix="note", user_note_field=null, profile_pic_field=null, alias_click=null) {
+    let field = $("#" + field_id);
+    // When the user clicks on the search field, it is immediately cleared
+    field.click(function() {
+        field.val("");
+    });
+
+    let old_pattern = null;
+
+    // When the user type "Enter", the first alias is clicked
+    field.keypress(function(event) {
+        if (event.originalEvent.charCode === 13)
+            $("#" + alias_matched_id + " li").first().trigger("click");
+    });
+
+    // When the user type something, the matched aliases are refreshed
+    field.keyup(function(e) {
+        if (e.originalEvent.charCode === 13)
+            return;
+
+        let pattern = field.val();
+        // If the pattern is not modified, we don't query the API
+        if (pattern === old_pattern || pattern === "")
+            return;
+
+        old_pattern = pattern;
+
+        // Clear old matched notes
+        notes.length = 0;
+
+        let aliases_matched_obj = $("#" + alias_matched_id);
+        let aliases_matched_html = "";
+
+        // Get matched notes with the given pattern
+        getMatchedNotes(pattern, function(aliases) {
+            // The response arrived too late, we stop the request
+            if (pattern !== $("#" + field_id).val())
+                return;
+
+            aliases.results.forEach(function (alias) {
+                let note = alias.note;
+                aliases_matched_html += li(alias_prefix + "_" + alias.id, alias.name);
+                note.alias = alias;
+                notes.push(note);
+            });
+
+            // Display the list of matched aliases
+            aliases_matched_obj.html(aliases_matched_html);
+
+            notes.forEach(function (note) {
+                let alias = note.alias;
+                let alias_obj = $("#" + alias_prefix + "_" + alias.id);
+                // When an alias is hovered, the profile picture and the balance are displayed at the right place
+                alias_obj.hover(function () {
+                    displayNote(note, alias.name, user_note_field, profile_pic_field);
+                });
+
+                // When the user click on an alias, the associated note is added to the emitters
+                alias_obj.click(function () {
+                    field.val("");
+                    // If the note is already an emitter, we increase the quantity
+                    var disp = null;
+                    notes_display.forEach(function (d) {
+                        // We compare the note ids
+                        if (d.id === note.id) {
+                            d.quantity += 1;
+                            disp = d;
+                        }
+                    });
+                    // In the other case, we add a new emitter
+                    if (disp == null) {
+                        disp = {
+                            name: alias.name,
+                            id: note.id,
+                            note: note,
+                            quantity: 1
+                        };
+                        notes_display.push(disp);
+                    }
+
+                    // If the function alias_click exists, it is called. If it doesn't return true, then the notes are
+                    // note displayed. Useful for a consumption when a button is already clicked
+                    if (alias_click && !alias_click())
+                        return;
+
+                    let note_list = $("#" + note_list_id);
+                    let html = "";
+                    notes_display.forEach(function (disp) {
+                        html += li(note_prefix + "_" + disp.id, disp.name
+                            + "<span class=\"badge badge-dark badge-pill\">" + disp.quantity + "</span>");
+                    });
+
+                    // Emitters are displayed
+                    note_list.html(html);
+
+                    notes_display.forEach(function (disp) {
+                        let line_obj = $("#" + note_prefix + "_" + disp.id);
+                        // Hover an emitter display also the profile picture
+                        line_obj.hover(function () {
+                            displayNote(disp.note, disp.name, user_note_field, profile_pic_field);
+                        });
+
+                        // When an emitter is clicked, it is removed
+                        line_obj.click(removeNote(disp, note_prefix, notes_display, note_list_id, user_note_field,
+                            profile_pic_field));
+                    });
+                });
+            });
+        });
+    });
+}
+
+// When a validate button is clicked, we switch the validation status
+function de_validate(id, validated) {
+    $("#validate_" + id).html("<strong style=\"font-size: 16pt;\">⟳ ...</strong>");
+
+    // Perform a PATCH request to the API in order to update the transaction
+    // If the user has insuffisent rights, an error message will appear
+    $.ajax({
+        "url": "/api/note/transaction/transaction/" + id + "/",
+        type: "PATCH",
+        dataType: "json",
+        headers: {
+            "X-CSRFTOKEN": CSRF_TOKEN
+        },
+        data: {
+            "resourcetype": "TemplateTransaction",
+            valid: !validated
+        },
+        success: function () {
+            // Refresh jQuery objects
+            $(".validate").click(de_validate);
+
+            refreshBalance();
+            // error if this method doesn't exist. Please define it.
+            refreshHistory();
+        },
+        error: function(err) {
+            addMsg("Une erreur est survenue lors de la validation/dévalidation " +
+                "de cette transaction : " + err.responseText, "danger");
+
+            refreshBalance();
+            // error if this method doesn't exist. Please define it.
+            refreshHistory();
+        }
+    });
+}
diff --git a/static/js/consos.js b/static/js/consos.js
new file mode 100644
index 0000000000000000000000000000000000000000..5f7a314a949cc600ff02b15e14da9a9fdb91de81
--- /dev/null
+++ b/static/js/consos.js
@@ -0,0 +1,205 @@
+// Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+/**
+ * Refresh the history table on the consumptions page.
+ */
+function refreshHistory() {
+    $("#history").load("/note/consos/ #history");
+    $("#most_used").load("/note/consos/ #most_used");
+}
+
+$(document).ready(function() {
+    // If hash of a category in the URL, then select this category
+    // else select the first one
+    if (location.hash) {
+        $("a[href='" + location.hash + "']").tab("show");
+    } else {
+        $("a[data-toggle='tab']").first().tab("show");
+    }
+
+    // When selecting a category, change URL
+    $(document.body).on("click", "a[data-toggle='tab']", function() {
+        location.hash = this.getAttribute("href");
+    });
+
+    // Switching in double consumptions mode should update the layout
+    let double_conso_obj = $("#double_conso");
+    double_conso_obj.click(function() {
+        $("#consos_list_div").show();
+        $("#infos_div").attr('class', 'col-sm-5 col-xl-6');
+        $("#note_infos_div").attr('class', 'col-xl-3');
+        $("#user_select_div").attr('class', 'col-xl-4');
+        $("#buttons_div").attr('class', 'col-sm-7 col-xl-6');
+
+        let note_list_obj = $("#note_list");
+        if (buttons.length > 0 && note_list_obj.text().length > 0) {
+            $("#consos_list").html(note_list_obj.html());
+            note_list_obj.html("");
+
+            buttons.forEach(function(button) {
+                $("#conso_button_" + button.id).click(removeNote(button, "conso_button", buttons,
+                    "consos_list"));
+            });
+        }
+    });
+
+    let single_conso_obj = $("#single_conso");
+    single_conso_obj.click(function() {
+        $("#consos_list_div").hide();
+        $("#infos_div").attr('class', 'col-sm-5 col-md-4');
+        $("#note_infos_div").attr('class', 'col-xl-5');
+        $("#user_select_div").attr('class', 'col-xl-7');
+        $("#buttons_div").attr('class', 'col-sm-7 col-md-8');
+
+        let consos_list_obj = $("#consos_list");
+        if (buttons.length > 0) {
+            if (notes_display.length === 0 && consos_list_obj.text().length > 0) {
+                $("#note_list").html(consos_list_obj.html());
+                consos_list_obj.html("");
+                buttons.forEach(function(button) {
+                    $("#conso_button_" + button.id).click(removeNote(button, "conso_button", buttons,
+                        "note_list"));
+                });
+            }
+            else {
+                buttons.length = 0;
+                consos_list_obj.html("");
+            }
+        }
+    });
+
+    // Ensure we begin in single consumption. Removing these lines may cause problems when reloading.
+    single_conso_obj.prop('checked', 'true');
+    double_conso_obj.removeAttr('checked');
+    $("label[for='double_conso']").attr('class', 'btn btn-sm btn-outline-primary');
+
+    $("#consos_list_div").hide();
+
+    $("#consume_all").click(consumeAll);
+});
+
+notes = [];
+notes_display = [];
+buttons = [];
+
+// When the user searches an alias, we update the auto-completion
+autoCompleteNote("note", "alias_matched", "note_list", notes, notes_display,
+    "alias", "note", "user_note", "profile_pic", function() {
+        if (buttons.length > 0 && $("#single_conso").is(":checked")) {
+            consumeAll();
+            return false;
+        }
+        return true;
+    });
+
+/**
+ * Add a transaction from a button.
+ * @param dest Where the money goes
+ * @param amount The price of the item
+ * @param type The type of the transaction (content type id for TemplateTransaction)
+ * @param category_id The category identifier
+ * @param category_name The category name
+ * @param template_id The identifier of the button
+ * @param template_name The name of  the button
+ */
+function addConso(dest, amount, type, category_id, category_name, template_id, template_name) {
+    var button = null;
+    buttons.forEach(function(b) {
+        if (b.id === template_id) {
+            b.quantity += 1;
+            button = b;
+        }
+    });
+    if (button == null) {
+        button = {
+            id: template_id,
+            name: template_name,
+            dest: dest,
+            quantity: 1,
+            amount: amount,
+            type: type,
+            category_id: category_id,
+            category_name: category_name
+        };
+        buttons.push(button);
+    }
+
+    let dc_obj = $("#double_conso");
+    if (dc_obj.is(":checked") || notes_display.length === 0) {
+        let list = dc_obj.is(":checked") ? "consos_list" : "note_list";
+        let html = "";
+        buttons.forEach(function(button) {
+            html += li("conso_button_" + button.id, button.name
+                + "<span class=\"badge badge-dark badge-pill\">" + button.quantity + "</span>");
+        });
+
+        $("#" + list).html(html);
+
+        buttons.forEach(function(button) {
+            $("#conso_button_" + button.id).click(removeNote(button, "conso_button", buttons, list));
+        });
+    }
+    else
+        consumeAll();
+}
+
+/**
+ * Reset the page as its initial state.
+ */
+function reset() {
+    notes_display.length = 0;
+    notes.length = 0;
+    buttons.length = 0;
+    $("#note_list").html("");
+    $("#alias_matched").html("");
+    $("#consos_list").html("");
+    displayNote(null, "");
+    refreshHistory();
+    refreshBalance();
+}
+
+
+/**
+ * Apply all transactions: all notes in `notes` buy each item in `buttons`
+ */
+function consumeAll() {
+    notes_display.forEach(function(note_display) {
+        buttons.forEach(function(button) {
+            consume(note_display.id, button.dest, button.quantity * note_display.quantity, button.amount,
+                button.name + " (" + button.category_name + ")", button.type, button.category_id, button.id);
+       });
+    });
+}
+
+/**
+ * Create a new transaction from a button through the API.
+ * @param source The note that paid the item (type: int)
+ * @param dest The note that sold the item (type: int)
+ * @param quantity The quantity sold (type: int)
+ * @param amount The price of one item, in cents (type: int)
+ * @param reason The transaction details (type: str)
+ * @param type The type of the transaction (content type id for TemplateTransaction)
+ * @param category The category id of the button (type: int)
+ * @param template The button id (type: int)
+ */
+function consume(source, dest, quantity, amount, reason, type, category, template) {
+    $.post("/api/note/transaction/transaction/",
+        {
+            "csrfmiddlewaretoken": CSRF_TOKEN,
+            "quantity": quantity,
+            "amount": amount,
+            "reason": reason,
+            "valid": true,
+            "polymorphic_ctype": type,
+            "resourcetype": "TemplateTransaction",
+            "source": source,
+            "destination": dest,
+            "category": category,
+            "template": template
+        }, reset).fail(function (e) {
+            reset();
+
+            addMsg("Une erreur est survenue lors de la transaction : " + e.responseText, "danger");
+    });
+}
diff --git a/static/js/transfer.js b/static/js/transfer.js
new file mode 100644
index 0000000000000000000000000000000000000000..a0c2d88ae2bf65e545df8b53b45b1d5f2f0188e8
--- /dev/null
+++ b/static/js/transfer.js
@@ -0,0 +1,157 @@
+sources = [];
+sources_notes_display = [];
+dests = [];
+dests_notes_display = [];
+
+function refreshHistory() {
+    $("#history").load("/note/transfer/ #history");
+}
+
+function reset() {
+    sources_notes_display.length = 0;
+    sources.length = 0;
+    dests_notes_display.length = 0;
+    dests.length = 0;
+    $("#source_note_list").html("");
+    $("#dest_note_list").html("");
+    $("#source_alias_matched").html("");
+    $("#dest_alias_matched").html("");
+    $("#amount").val("");
+    $("#reason").val("");
+    $("#last_name").val("");
+    $("#first_name").val("");
+    $("#bank").val("");
+    refreshBalance();
+    refreshHistory();
+}
+
+$(document).ready(function() {
+    autoCompleteNote("source_note", "source_alias_matched", "source_note_list", sources, sources_notes_display,
+        "source_alias", "source_note", "user_note", "profile_pic");
+    autoCompleteNote("dest_note", "dest_alias_matched", "dest_note_list", dests, dests_notes_display,
+        "dest_alias", "dest_note", "user_note", "profile_pic", function() {
+            let last = dests_notes_display[dests_notes_display.length - 1];
+            dests_notes_display.length = 0;
+            dests_notes_display.push(last);
+
+            last.quantity = 1;
+
+            $.getJSON("/api/user/" + last.note.user + "/", function(user) {
+                $("#last_name").val(user.last_name);
+                $("#first_name").val(user.first_name);
+            });
+
+            return true;
+       });
+
+
+    // Ensure we begin in gift mode. Removing these lines may cause problems when reloading.
+    $("#type_gift").prop('checked', 'true');
+    $("#type_transfer").removeAttr('checked');
+    $("#type_credit").removeAttr('checked');
+    $("#type_debit").removeAttr('checked');
+    $("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');
+});
+
+$("#transfer").click(function() {
+    if ($("#type_gift").is(':checked')) {
+        dests_notes_display.forEach(function (dest) {
+            $.post("/api/note/transaction/transaction/",
+                {
+                    "csrfmiddlewaretoken": CSRF_TOKEN,
+                    "quantity": dest.quantity,
+                    "amount": 100 * $("#amount").val(),
+                    "reason": $("#reason").val(),
+                    "valid": true,
+                    "polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE,
+                    "resourcetype": "Transaction",
+                    "source": user_id,
+                    "destination": dest.id
+                }, function () {
+                    addMsg("Le transfert de "
+                        + pretty_money(dest.quantity * 100 * $("#amount").val()) + " de votre note "
+                        + " vers la note " + dest.name + " a été fait avec succès !", "success");
+
+                    reset();
+                }).fail(function (err) {
+                    addMsg("Le transfert de "
+                        + pretty_money(dest.quantity * 100 * $("#amount").val()) + " de votre note "
+                        + " vers la note " + dest.name + " a échoué : " + err.responseText, "danger");
+
+                reset();
+            });
+        });
+    }
+    else if ($("#type_transfer").is(':checked')) {
+        sources_notes_display.forEach(function (source) {
+            dests_notes_display.forEach(function (dest) {
+                $.post("/api/note/transaction/transaction/",
+                    {
+                        "csrfmiddlewaretoken": CSRF_TOKEN,
+                        "quantity": source.quantity * dest.quantity,
+                        "amount": 100 * $("#amount").val(),
+                        "reason": $("#reason").val(),
+                        "valid": true,
+                        "polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE,
+                        "resourcetype": "Transaction",
+                        "source": source.id,
+                        "destination": dest.id
+                    }, function () {
+                        addMsg("Le transfert de "
+                            + pretty_money(source.quantity * dest.quantity * 100 * $("#amount").val()) + " de la note " + source.name
+                            + " vers la note " + dest.name + " a été fait avec succès !", "success");
+
+                        reset();
+                    }).fail(function (err) {
+                        addMsg("Le transfert de "
+                            + pretty_money(source.quantity * dest.quantity * 100 * $("#amount").val()) + " de la note " + source.name
+                            + " vers la note " + dest.name + " a échoué : " + err.responseText, "danger");
+
+                        reset();
+                });
+            });
+        });
+    } else if ($("#type_credit").is(':checked') || $("#type_debit").is(':checked')) {
+        let special_note = $("#credit_type").val();
+        let user_note = dests_notes_display[0].id;
+        let given_reason = $("#reason").val();
+        let source, dest, reason;
+        if ($("#type_credit").is(':checked')) {
+            source = special_note;
+            dest = user_note;
+            reason = "Crédit " + $("#credit_type option:selected").text().toLowerCase();
+            if (given_reason.length > 0)
+                reason += " (" + given_reason + ")";
+        }
+        else {
+            source = user_note;
+            dest = special_note;
+            reason = "Retrait " + $("#credit_type option:selected").text().toLowerCase();
+            if (given_reason.length > 0)
+                reason += " (" + given_reason + ")";
+        }
+        $.post("/api/note/transaction/transaction/",
+            {
+                "csrfmiddlewaretoken": CSRF_TOKEN,
+                "quantity": 1,
+                "amount": 100 * $("#amount").val(),
+                "reason": reason,
+                "valid": true,
+                "polymorphic_ctype": SPECIAL_TRANSFER_POLYMORPHIC_CTYPE,
+                "resourcetype": "SpecialTransaction",
+                "source": source,
+                "destination": dest,
+                "last_name": $("#last_name").val(),
+                "first_name": $("#first_name").val(),
+                "bank": $("#bank").val()
+            }, function () {
+                addMsg("Le crédit/retrait a bien été effectué !", "success");
+                reset();
+            }).fail(function (err) {
+                addMsg("Le crédit/transfert a échoué : " + err.responseText, "danger");
+                reset();
+        });
+    }
+});
\ No newline at end of file
diff --git a/templates/base.html b/templates/base.html
index 6814bedfba9b3783255ffe7df3fd3b1a826889db..e61937021c6f566f9b3125cc00dac9de9e66634c 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -1,4 +1,4 @@
-{% load static i18n pretty_money static %}
+{% load static i18n pretty_money static getenv %}
 {% comment %}
 SPDX-License-Identifier: GPL-3.0-or-later
 {% endcomment %}
@@ -46,12 +46,20 @@ SPDX-License-Identifier: GPL-3.0-or-later
             crossorigin="anonymous"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/turbolinks/5.2.0/turbolinks.js"
             crossorigin="anonymous"></script>
+    <script src="/static/js/base.js"></script>
 
     {# Si un formulaire requiert des données supplémentaires (notamment JS), les données sont chargées #}
     {% if form.media %}
         {{ form.media }}
     {% endif %}
 
+    <style>
+        .validate:hover {
+            cursor: pointer;
+            text-decoration: underline;
+        }
+    </style>
+
     {% block extracss %}{% endblock %}
 </head>
 <body class="d-flex w-100 h-100 flex-column">
@@ -67,23 +75,27 @@ SPDX-License-Identifier: GPL-3.0-or-later
         <div class="collapse navbar-collapse" id="navbarNavDropdown">
             <ul class="navbar-nav">
                 <li class="nav-item active">
-                    <a class="nav-link" href="{% url 'note:consos' %}"><i class="fa fa-coffee"></i> Consos</a>
+                    <a class="nav-link" href="{% url 'note:consos' %}"><i class="fa fa-coffee"></i> {% trans 'Consumptions' %}</a>
+                </li>
+                <li class="nav-item active">
+                    <a class="nav-link" href="{% url 'member:club_list' %}"><i class="fa fa-users"></i> {% trans 'Clubs' %}</a>
                 </li>
                 <li class="nav-item active">
-                    <a class="nav-link" href="{% url 'member:club_list' %}"><i class="fa fa-users"></i> Clubs</a>
+                    <a class="nav-link" href="#"><i class="fa fa-calendar"></i> {% trans 'Activities' %}</a>
                 </li>
                 <li class="nav-item active">
-                    <a class="nav-link" href="#"><i class="fa fa-calendar"></i> Activités</a>
+                    <a class="nav-link" href="{% url 'note:template_list' %}"><i class="fa fa-coffee"></i> {% trans 'Buttons' %}</a>
                 </li>
                 <li class="nav-item active">
-                    <a class="nav-link" href="{% url 'note:template_list' %}"><i class="fa fa-coffee"></i> Bouton</a>
+                    <a class="nav-link" href="{% url 'note:transfer' %}"><i class="fa fa-exchange"></i>{% trans 'Transfer' %} </a>
                 </li>
             </ul>
             <ul class="navbar-nav ml-auto">
                 {% if user.is_authenticated %}
                     <li class="dropdown">
                         <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-                            <i class="fa fa-user"></i> {{ user.username }} ({{ user.note.balance | pretty_money }})
+                            <i class="fa fa-user"></i>
+                            <span id="user_balance">{{ user.username }} ({{ user.note.balance | pretty_money }})</span>
                         </a>
                         <div class="dropdown-menu dropdown-menu-right"
                              aria-labelledby="navbarDropdownMenuLink">
@@ -112,6 +124,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
     </nav>
     <div class="container-fluid my-3" style="max-width: 1600px;">
         {% block contenttitle %}<h1>{{ title }}</h1>{% endblock %}
+        <div id="messages"></div>
         {% block content %}
             <p>Default content...</p>
         {% endblock content %}
@@ -125,7 +138,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
                       class="form-inline">
                     <span class="text-muted mr-1">
                         NoteKfet2020 &mdash;
-                        <a href="mailto:tresorie.bde@lists.crans.org"
+                        <a href="mailto:{{ "CONTACT_EMAIL" | getenv }}"
                            class="text-muted">Nous contacter</a> &mdash;
                     </span>
                     {% csrf_token %}
@@ -155,6 +168,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
     </div>
 </footer>
 
+<script>
+    CSRF_TOKEN = "{{ csrf_token }}";
+</script>
+
 {% block extrajavascript %}
 {% endblock extrajavascript %}
 </body>
diff --git a/templates/cas_server/base.html b/templates/cas_server/base.html
new file mode 100644
index 0000000000000000000000000000000000000000..4e93cee08f82590ca0f08977a24bf33efd260fe1
--- /dev/null
+++ b/templates/cas_server/base.html
@@ -0,0 +1,99 @@
+{% load i18n %}{% load static %}{% get_current_language as LANGUAGE_CODE %}<!DOCTYPE html>
+<html{% if LANGUAGE_CODE %} lang="{{LANGUAGE_CODE}}"{% endif %}>
+    <head>
+        <meta charset="utf-8">
+        <!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge" /><![endif]-->
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <title>{% block title %}{% trans "Central Authentication Service"  %}{% endblock %}</title>
+        <link href="{{settings.CAS_COMPONENT_URLS.bootstrap3_css}}" rel="stylesheet">
+        <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
+        <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+        <!--[if lt IE 9]>
+        <script src="{{settings.CAS_COMPONENT_URLS.html5shiv}}"></script>
+        <script src="{{settings.CAS_COMPONENT_URLS.respond}}"></script>
+        <![endif]-->
+        {% if settings.CAS_FAVICON_URL %}<link rel="shortcut icon" href="{{settings.CAS_FAVICON_URL}}" />{% endif %}
+        <link href="{% static "cas_server/styles.css" %}" rel="stylesheet">
+    </head>
+    <body>
+      <div id="wrap">
+        <div class="container">
+            {% if auto_submit %}<noscript>{% endif %}
+            <div class="row">
+              <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
+                <h1 id="app-name">
+                    {% if settings.CAS_LOGO_URL %}<img src="{{settings.CAS_LOGO_URL}}" alt="cas-logo" />{% endif %}
+                    Authentification Note Kfet 2020</h1>
+              </div>
+            </div>
+            {% if auto_submit %}</noscript>{% endif %}
+            <div class="row">
+            <div class="col-lg-3 col-md-3 col-sm-2 col-xs-12"></div>
+            <div class="col-lg-6 col-md-6 col-sm-8 col-xs-12">
+            {% if auto_submit %}<noscript>{% endif %}
+            {% for msg in CAS_INFO_RENDER %}
+              <div class="alert alert-{{msg.type}}{% if msg.discardable %} alert-dismissable{% endif %}">
+                {% if msg.discardable %}<button type="button" class="close" data-dismiss="alert" aria-hidden="true" id="info-{{msg.name}}">&#215;</button>{% endif %}
+                <p>{{msg.message}}</p>
+              </div>
+            {% endfor %}
+            {% if settings.CAS_NEW_VERSION_HTML_WARNING and upgrade_available %}
+              <div class="alert alert-info alert-dismissable">
+                <button type="button" class="close" data-dismiss="alert" aria-hidden="true" id="alert-version">&#215;</button>
+                <p>{% blocktrans %}A new version of the application is available. This instance runs {{VERSION}} and the last version is {{LAST_VERSION}}. Please consider upgrading.{% endblocktrans %}</p>
+              </div>
+            {% endif %}
+            {% block ante_messages %}{% endblock %}
+            {% for message in messages %}
+                <div {% spaceless %}
+                    {% if message.level == message_levels.DEBUG %}
+                        class="alert alert-warning"
+                    {% elif message.level == message_levels.INFO %}
+                        class="alert alert-info"
+                    {% elif message.level == message_levels.SUCCESS %}
+                        class="alert alert-success"
+                    {% elif message.level == message_levels.WARNING %}
+                        class="alert alert-warning"
+                    {% else %}
+                        class="alert alert-danger"
+                    {% endif %}
+                {% endspaceless %}>
+                    <p>{{message}}</p>
+                </div>
+            {% endfor %}
+            {% if auto_submit %}</noscript>{% endif %}
+            {% block content %}{% endblock %}
+            </div>
+            <div class="col-lg-3 col-md-3 col-sm-2 col-xs-0"></div>
+            </div>
+        </div> <!-- /container -->
+      </div>
+      <div style="clear: both;"></div>
+      {% if settings.CAS_SHOW_POWERED %}
+      <div id="footer">
+          <p><a class="text-muted" href="https://pypi.org/project/django-cas-server/">django-cas-server powered</a></p>
+      </div>
+      {% endif %}
+      <script src="{{settings.CAS_COMPONENT_URLS.jquery}}"></script>
+      <script src="{{settings.CAS_COMPONENT_URLS.bootstrap3_js}}"></script>
+      <script src="{% static "cas_server/functions.js" %}"></script>
+      <script type="text/javascript">
+{% if settings.CAS_NEW_VERSION_HTML_WARNING and upgrade_available %}
+discard_and_remember("#alert-version", "cas-alert-version", "{{LAST_VERSION}}");
+{% endif %}
+{% for msg in CAS_INFO_RENDER %}
+{% if msg.discardable %}
+discard_and_remember("#info-{{msg.name}}", "cas-info-{{msg.name}}", "{{msg.hash}}");
+{% endif %}
+{% endfor %}
+{% block javascript_inline %}{% endblock %}
+</script>
+      {% block javascript %}{% endblock %}
+    </body>
+</html>
+<!--
+Powered by django-cas-server version {{VERSION}}
+
+Pypi: https://pypi.org/project/django-cas-server/
+github: https://github.com/nitmir/django-cas-server
+-->
diff --git a/templates/cas_server/form.html b/templates/cas_server/form.html
new file mode 100644
index 0000000000000000000000000000000000000000..405dedd12fc1c35ca35827cc456aea95b0d23f17
--- /dev/null
+++ b/templates/cas_server/form.html
@@ -0,0 +1,26 @@
+{% load cas_server %}
+{% for error in form.non_field_errors %}
+<div class="alert alert-danger alert-dismissable">
+  <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&#215;</button>
+  {{error}}
+</div>
+{% endfor %}
+{% for field in form %}{% if not field|is_hidden %}
+<div class="form-group
+  {% if not form.non_field_errors %}
+    {% if field.errors %} has-error
+    {% elif form.cleaned_data %} has-success
+    {% endif %}
+  {% endif %}"
+>{% spaceless %}
+  {% if field|is_checkbox %}
+    <div class="checkbox"><label for="{{field.auto_id}}">{{field}}{{field.label}}</label></div>
+  {% else %}
+    <label class="control-label" for="{{field.auto_id}}">{{field.label}}</label>
+    {{field}}
+  {% endif %}
+  {% for error in field.errors %}
+    <span class="help-block">{{error}}</span>
+  {% endfor %}
+{% endspaceless %}</div>
+{% else %}{{field}}{% endif %}{% endfor %}
diff --git a/templates/cas_server/logged.html b/templates/cas_server/logged.html
new file mode 100644
index 0000000000000000000000000000000000000000..46e1c9a8824ba03c0796cb1f263a58b6a8f99e38
--- /dev/null
+++ b/templates/cas_server/logged.html
@@ -0,0 +1,21 @@
+{% extends "cas_server/base.html" %}
+{% load i18n %}
+{% block content %}
+<div class="alert alert-success" role="alert">{% blocktrans %}<h3>Log In Successful</h3>You have successfully logged into the Central Authentication Service.<br/>For security reasons, please Log Out and Exit your web browser when you are done accessing services that require authentication!{% endblocktrans %}</div>
+<form class="form-signin" method="get" action="logout">
+  <div class="checkbox">
+    <label>
+      <input type="checkbox" name="all" value="1">{% trans "Log me out from all my sessions" %}
+    </label>
+  </div>
+  {% if settings.CAS_FEDERATE and request.COOKIES.remember_provider %}
+  <div class="checkbox">
+    <label>
+      <input type="checkbox" name="forget_provider" value="1">{% trans "Forget the identity provider" %}
+    </label>
+  </div>
+  {% endif %}
+  <button class="btn btn-danger btn-block btn-lg" type="submit">{% trans "Logout" %}</button>
+</form>
+{% endblock %}
+
diff --git a/templates/cas_server/login.html b/templates/cas_server/login.html
new file mode 100644
index 0000000000000000000000000000000000000000..ddc2eb32aa80727c714e0f7b87c8fd912de0bc39
--- /dev/null
+++ b/templates/cas_server/login.html
@@ -0,0 +1,33 @@
+{% extends "cas_server/base.html" %}
+{% load i18n %}
+
+{% block ante_messages %}
+{% if auto_submit %}<noscript>{% endif %}
+<h2 class="form-signin-heading">{% trans "Please log in" %}</h2>
+{% if auto_submit %}</noscript>{% endif %}
+{% endblock %}
+{% block content %}
+    <div class="alert alert-warning">
+        {% trans "If you don't have any Note Kfet account, please follow <a href='/accounts/signup'>this link to sign up</a>." %}
+    </div>
+<form class="form-signin" method="post" id="login_form"{% if post_url %} action="{{post_url}}"{% endif %}>
+  {% csrf_token %}
+  {% include "cas_server/form.html" %}
+  {% if auto_submit %}<noscript>{% endif %}
+  <button class="btn btn-primary btn-block btn-lg" type="submit">{% trans "Login" %}</button>
+  {% if auto_submit %}</noscript>{% endif %}
+</form>
+{% endblock %}
+{% block javascript_inline %}
+jQuery(function( $ ){
+    $("#id_warn").click(function(e){
+        if($("#id_warn").is(':checked')){
+            createCookie("warn", "on", 10 * 365);
+        } else {
+            eraseCookie("warn");
+        }
+    });
+});{% if auto_submit %}
+document.getElementById('login_form').submit(); // SUBMIT FORM{% endif %}
+{% endblock %}
+
diff --git a/templates/cas_server/logout.html b/templates/cas_server/logout.html
new file mode 100644
index 0000000000000000000000000000000000000000..8069337678aa7272d2d10ab066647774f52a9720
--- /dev/null
+++ b/templates/cas_server/logout.html
@@ -0,0 +1,7 @@
+{% extends "cas_server/base.html" %}
+{% load static %}
+{% load i18n %}
+{% block content %}
+<div class="alert alert-success" role="alert">{{logout_msg}}</div>
+{% endblock %}
+
diff --git a/templates/cas_server/proxy.xml b/templates/cas_server/proxy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ab51d89a89c9f59f9a57d7ecf421db644d5dc0b3
--- /dev/null
+++ b/templates/cas_server/proxy.xml
@@ -0,0 +1,5 @@
+<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas">
+      <cas:proxySuccess>
+          <cas:proxyTicket>{{ticket}}</cas:proxyTicket>
+      </cas:proxySuccess>
+  </cas:serviceResponse>
diff --git a/templates/cas_server/samlValidate.xml b/templates/cas_server/samlValidate.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3b130fd2c7cf2d47c62e1793dcc55d8292d6f040
--- /dev/null
+++ b/templates/cas_server/samlValidate.xml
@@ -0,0 +1,59 @@
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+  <SOAP-ENV:Header />
+  <SOAP-ENV:Body>
+      <Response xmlns="urn:oasis:names:tc:SAML:1.0:protocol"
+                xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol"
+                IssueInstant="{{ IssueInstant }}"
+                MajorVersion="1" MinorVersion="1" Recipient="{{ Recipient }}"
+                ResponseID="{{ ResponseID }}">
+      <Status>
+        <StatusCode Value="samlp:Success">
+        </StatusCode>
+      </Status>
+      <Assertion xmlns="urn:oasis:names:tc:SAML:1.0:assertion" AssertionID="{{ResponseID}}"
+      IssueInstant="{{IssueInstant}}" Issuer="localhost" MajorVersion="1"
+      MinorVersion="1">
+        <Conditions NotBefore="{{IssueInstant}}" NotOnOrAfter="{{expireInstant}}">
+          <AudienceRestrictionCondition>
+            <Audience>
+              {{Recipient}}
+            </Audience>
+          </AudienceRestrictionCondition>
+        </Conditions>
+        <AttributeStatement>
+          <Subject>
+            <NameIdentifier>{{username}}</NameIdentifier>
+            <SubjectConfirmation>
+              <ConfirmationMethod>
+                urn:oasis:names:tc:SAML:1.0:cm:artifact
+              </ConfirmationMethod>
+            </SubjectConfirmation>
+          </Subject>
+          <Attribute AttributeName="authenticationDate" AttributeNamespace="http://www.ja-sig.org/products/cas/">
+            <AttributeValue>{{auth_date}}</AttributeValue>
+          </Attribute>
+          <Attribute AttributeName="longTermAuthenticationRequestTokenUsed" AttributeNamespace="http://www.ja-sig.org/products/cas/">
+            <AttributeValue>false</AttributeValue>{# we do not support long-term (Remember-Me) auth #}
+          </Attribute>
+          <Attribute AttributeName="isFromNewLogin" AttributeNamespace="http://www.ja-sig.org/products/cas/">
+            <AttributeValue>{{is_new_login}}</AttributeValue>
+          </Attribute>
+{% for name, value in attributes %}          <Attribute AttributeName="{{name}}" AttributeNamespace="http://www.ja-sig.org/products/cas/">
+            <AttributeValue>{{value}}</AttributeValue>
+          </Attribute>
+{% endfor %}        </AttributeStatement>
+        <AuthenticationStatement AuthenticationInstant="{{IssueInstant}}"
+        AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:password">
+          <Subject>
+            <NameIdentifier>{{username}}</NameIdentifier>
+            <SubjectConfirmation>
+              <ConfirmationMethod>
+                urn:oasis:names:tc:SAML:1.0:cm:artifact
+              </ConfirmationMethod>
+            </SubjectConfirmation>
+          </Subject>
+        </AuthenticationStatement>
+      </Assertion>
+    </Response>
+  </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
diff --git a/templates/cas_server/samlValidateError.xml b/templates/cas_server/samlValidateError.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c72daba1d01329b2f72d7f4a39c2ca1c70d88bda
--- /dev/null
+++ b/templates/cas_server/samlValidateError.xml
@@ -0,0 +1,14 @@
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+  <SOAP-ENV:Header />
+  <SOAP-ENV:Body>
+      <Response xmlns="urn:oasis:names:tc:SAML:1.0:protocol"
+                xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol"
+                IssueInstant="{{ IssueInstant }}"
+                MajorVersion="1" MinorVersion="1" Recipient="{{ Recipient }}"
+                ResponseID="{{ ResponseID }}">
+      <Status>
+        <StatusCode Value="samlp:{{code}}">{{msg}}</StatusCode>
+      </Status>
+    </Response>
+  </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
diff --git a/templates/cas_server/serviceValidate.xml b/templates/cas_server/serviceValidate.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f583dbeace3f43da2a04a89b40e0cadaf5868d32
--- /dev/null
+++ b/templates/cas_server/serviceValidate.xml
@@ -0,0 +1,19 @@
+<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas">
+  <cas:authenticationSuccess>
+    <cas:user>{{username}}</cas:user>
+    <cas:attributes>
+      <cas:authenticationDate>{{auth_date}}</cas:authenticationDate>
+      <cas:longTermAuthenticationRequestTokenUsed>false</cas:longTermAuthenticationRequestTokenUsed>{# we do not support long-term (Remember-Me) auth #}
+      <cas:isFromNewLogin>{{is_new_login}}</cas:isFromNewLogin>
+{% for key, value in attributes %}      <cas:{{key}}>{{value}}</cas:{{key}}>
+{% endfor %}    </cas:attributes>
+    <cas:attribute name="authenticationDate" value="{{auth_date}}"/>
+    <cas:attribute name="longTermAuthenticationRequestTokenUsed" value="false"/>
+    <cas:attribute name="isFromNewLogin" value="{{is_new_login}}"/>
+{% for key, value in attributes %}    <cas:attribute name="{{key}}" value="{{value}}"/>
+{% endfor %}{% if proxyGrantingTicket %}    <cas:proxyGrantingTicket>{{proxyGrantingTicket}}</cas:proxyGrantingTicket>
+{% endif %}{% if  proxies %}    <cas:proxies>
+{% for proxy in proxies %}      <cas:proxy>{{proxy}}</cas:proxy>
+{% endfor %}    </cas:proxies>
+{% endif %}  </cas:authenticationSuccess>
+</cas:serviceResponse>
diff --git a/templates/cas_server/serviceValidateError.xml b/templates/cas_server/serviceValidateError.xml
new file mode 100644
index 0000000000000000000000000000000000000000..cab8d9bdd82b288b6a79f44acafff24c6c4f0d1c
--- /dev/null
+++ b/templates/cas_server/serviceValidateError.xml
@@ -0,0 +1,3 @@
+<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas">
+ <cas:authenticationFailure code="{{code}}">{{msg}}</cas:authenticationFailure>
+</cas:serviceResponse>
diff --git a/templates/cas_server/warn.html b/templates/cas_server/warn.html
new file mode 100644
index 0000000000000000000000000000000000000000..4f80b15a2c460304034f877b565986681eb530a1
--- /dev/null
+++ b/templates/cas_server/warn.html
@@ -0,0 +1,11 @@
+{% extends "cas_server/base.html" %}
+{% load static %}
+{% load i18n %}
+
+{% block content %}
+      <form class="form-signin" method="post">
+{% csrf_token %}
+{% include "cas_server/form.html" %}
+<button class="btn btn-primary btn-block btn-lg" type="submit">{% trans "Connect to the service" %}</button>
+      </form>
+{% endblock %}
diff --git a/templates/django_filters/rest_framework/crispy_form.html b/templates/django_filters/rest_framework/crispy_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..171767c086cc5fe7f96816d6b0a2c2bfc50b1d08
--- /dev/null
+++ b/templates/django_filters/rest_framework/crispy_form.html
@@ -0,0 +1,5 @@
+{% load crispy_forms_tags %}
+{% load i18n %}
+
+<h2>{% trans "Field filters" %}</h2>
+{% crispy filter.form %}
diff --git a/templates/django_filters/rest_framework/form.html b/templates/django_filters/rest_framework/form.html
new file mode 100644
index 0000000000000000000000000000000000000000..b116e35317537ecf43046b79a4ca7525d1dc80c0
--- /dev/null
+++ b/templates/django_filters/rest_framework/form.html
@@ -0,0 +1,6 @@
+{% load i18n %}
+<h2>{% trans "Field filters" %}</h2>
+<form class="form" action="" method="get">
+    {{ filter.form.as_p }}
+    <button type="submit" class="btn btn-primary">{% trans "Submit" %}</button>
+</form>
diff --git a/templates/django_filters/widgets/multiwidget.html b/templates/django_filters/widgets/multiwidget.html
new file mode 100644
index 0000000000000000000000000000000000000000..089ddb20c9fccebb7562d4cb0c400ba0a6f3020c
--- /dev/null
+++ b/templates/django_filters/widgets/multiwidget.html
@@ -0,0 +1 @@
+{% for widget in widget.subwidgets %}{% include widget.template_name %}{% if forloop.first %}-{% endif %}{% endfor %}
diff --git a/templates/member/club_form.html b/templates/member/club_form.html
index 3fc2dd8be1aa4916e6425659c2b79decad67e3b9..577297bbc12eb39b0f0457ae8c65945ffc71750f 100644
--- a/templates/member/club_form.html
+++ b/templates/member/club_form.html
@@ -1,11 +1,12 @@
 {% extends "base.html" %}
 {% load static %}
+{% load i18n %}
 {% load crispy_forms_tags %}
 {% block content %}
-<p><a class="btn btn-default" href="{% url 'note:template_list' %}">Template Listing</a></p>
+<p><a class="btn btn-default" href="{% url 'note:template_list' %}">{% trans "Clubs list" %}</a></p>
 <form method="post">
 {% csrf_token %}
 {{form|crispy}}
-<button class="btn btn-primary" type="submit">Submit</button>
+<button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
 </form>
 {% endblock %}
diff --git a/templates/member/club_list.html b/templates/member/club_list.html
index f807c25ce8fd252ecf0a06b3d93e5312b351a6a3..165711136329c1a8ccbdf12880c33499541c0436 100644
--- a/templates/member/club_list.html
+++ b/templates/member/club_list.html
@@ -1,10 +1,11 @@
 {% extends "base.html" %}
 {% load render_table from django_tables2 %}
+{% load i18n %}
 {% block content %}
 
 {% render_table  table %}
 
-<a class="btn btn-primary" href="{% url 'member:club_create' %}">New Club</a>
+<a class="btn btn-primary" href="{% url 'member:club_create' %}">{% trans "New club" %}</a>
 
 {% endblock %}
 {% block extrajavascript %}
diff --git a/templates/member/profile_detail.html b/templates/member/profile_detail.html
index e997b333005d051cd33b371666ff5c6b5fd4d773..31510acfb8f8d71511880fa3126cb62f0665dcf5 100644
--- a/templates/member/profile_detail.html
+++ b/templates/member/profile_detail.html
@@ -10,7 +10,7 @@
                     <img src="{{ object.note.display_image.url }}" class="img-thumbnail mt-2" >
                 </a>
             </div>
-            <div class="card-body">
+            <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>
@@ -76,7 +76,9 @@
                     </a>
                 </div>
                 <div id="historyListCollapse" class="collapse" style="overflow:auto hidden" aria-labelledby="historyListHeading" data-parent="#accordionProfile">
-                    {% render_table history_list %}
+                    <div id="history_list">
+                        {% render_table history_list %}
+                    </div>
                 </div>
             </div>
         </div>
@@ -84,3 +86,12 @@
     </div>
 </div>
 {% 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");
+    }
+    </script>
+{% endblock %}
diff --git a/templates/member/signup.html b/templates/member/signup.html
index e682bd9b8be5c6c327f8b17cdf4d7a7f07a3da96..d7b3c23ef8bb0fba2baebc04e178ecf1bc59ab78 100644
--- a/templates/member/signup.html
+++ b/templates/member/signup.html
@@ -2,16 +2,16 @@
 {% extends 'base.html' %}
 {% load crispy_forms_tags %}
 {% load i18n %}
-{% block title %}Sign Up{% endblock %}
+{% block title %}{% trans "Sign up" %}{% endblock %}
 
 {% block content %}
-  <h2>Sign up</h2>
+  <h2>{% trans "Sign up" %}</h2>
   <form method="post">
       {% csrf_token %}
       {{ form|crispy }}
       {{ profile_form|crispy }}
       <button class="btn btn-success" type="submit">
-          {% trans "Sign Up" %}
+          {% trans "Sign up" %}
       </button>
   </form>
 {% endblock %}
diff --git a/templates/note/conso_form.html b/templates/note/conso_form.html
index 10b06589cfd4434aa6bc79100307901d017b1547..b108a96f83f81df9588c1c992c719b152bc7a0d8 100644
--- a/templates/note/conso_form.html
+++ b/templates/note/conso_form.html
@@ -1,97 +1,171 @@
 {% extends "base.html" %}
 
-{% load i18n static pretty_money %}
+{% load i18n static pretty_money django_tables2 %}
 
 {# Remove page title #}
 {% block contenttitle %}{% endblock %}
 
 {% block content %}
-    {# Regroup buttons under categories #}
-    {% regroup transaction_templates by category as categories %}
+    <div class="row mt-4">
+        <div class="col-sm-5 col-md-4" id="infos_div">
+            <div class="row">
+                {# User details column #}
+                <div class="col-xl-5" id="note_infos_div">
+                    <div class="card border-success shadow mb-4">
+                        <img src="/media/pic/default.png"
+                            id="profile_pic" alt="" class="img-fluid rounded mx-auto d-block">
+                        <div class="card-body text-center">
+                            <span id="user_note"></span>
+                        </div>
+                    </div>
+                </div>
 
-    <form method="post" onsubmit="window.onbeforeunload=null">
-        {% csrf_token %}
+                {# User selection column #}
+                <div class="col-xl-7" id="user_select_div">
+                    <div class="card border-success shadow mb-4">
+                        <div class="card-header">
+                            <p class="card-text font-weight-bold">
+                                {% trans "Select emitters" %}
+                            </p>
+                        </div>
+                        <ul class="list-group list-group-flush" id="note_list">
+                        </ul>
+                        <div class="card-body">
+                            <input class="form-control mx-auto d-block" type="text" id="note" />
+                            <ul class="list-group list-group-flush" id="alias_matched">
+                            </ul>
+                        </div>
+                    </div>
+                </div>
 
-        <div class="row">
-            <div class="col-sm-5 mb-4">
-                {% if form.non_field_errors %}
-                    <p class="errornote">
-                        {% for error in form.non_field_errors %}
-                            {{ error }}
-                        {% endfor %}
-                    </p>
-                {% endif %}
-                {% for field in form %}
-                    <div class="form-row{% if field.errors %} errors{% endif %}">
-                        {{ field.errors }}
-                        <div>
-                            {{ field.label_tag }}
-                            {% if field.is_readonly %}
-                                <div class="readonly">{{ field.contents }}</div>
-                            {% else %}
-                                {{ field }}
-                            {% endif %}
-                            {% if field.field.help_text %}
-                                <div class="help">{{ field.field.help_text|safe }}</div>
-                            {% endif %}
+                <div class="col-xl-5" id="consos_list_div">
+                    <div class="card border-info shadow mb-4">
+                        <div class="card-header">
+                            <p class="card-text font-weight-bold">
+                                {% trans "Select consumptions" %}
+                            </p>
                         </div>
+                        <ul class="list-group list-group-flush" id="consos_list">
+                        </ul>
+                        <button id="consume_all" class="form-control btn btn-primary">
+                            {% trans "Consume!" %}
+                        </button>
                     </div>
-                {% endfor %}
+                </div>
             </div>
+        </div>
 
-            <div class="col-sm-7">
-                <div class="card text-center shadow">
-                    {# Tabs for button categories #}
-                    <div class="card-header">
-                        <ul class="nav nav-tabs nav-fill card-header-tabs">
-                            {% for category in categories %}
-                                <li class="nav-item">
-                                    <a class="nav-link" data-toggle="tab" href="#{{ category.grouper|slugify }}">
-                                        {{ category.grouper }}
-                                    </a>
-                                </li>
-                            {% endfor %}
-                        </ul>
+        {# Buttons column #}
+        <div class="col-sm-7 col-md-8" id="buttons_div">
+            {# Show last used buttons #}
+            <div class="card shadow mb-4">
+                <div class="card-header">
+                    <p class="card-text font-weight-bold">
+                        {% trans "Most used buttons" %}
+                    </p>
+                </div>
+                <div class="card-body text-nowrap" style="overflow:auto hidden">
+                    <div class="d-inline-flex flex-wrap justify-content-center" id="most_used">
+                        {% for button in most_used %}
+                            {% if button.display %}
+                                <button class="btn btn-outline-dark rounded-0 flex-fill"
+                                        id="most_used_button{{ button.id }}" name="button" value="{{ button.name }}">
+                                    {{ button.name }} ({{ button.amount | pretty_money }})
+                                </button>
+                            {% endif %}
+                        {% endfor %}
                     </div>
+                </div>
+            </div>
+
+            {# Regroup buttons under categories #}
+            {% regroup transaction_templates by category as categories %}
+
+            <div class="card border-primary text-center shadow mb-4">
+                {# Tabs for button categories #}
+                <div class="card-header">
+                    <ul class="nav nav-tabs nav-fill card-header-tabs">
+                        {% for category in categories %}
+                            <li class="nav-item">
+                                <a class="nav-link font-weight-bold" data-toggle="tab" href="#{{ category.grouper|slugify }}">
+                                    {{ category.grouper }}
+                                </a>
+                            </li>
+                        {% endfor %}
+                    </ul>
+                </div>
 
-                    {# Tabs content #}
-                    <div class="card-body">
-                        <div class="tab-content">
-                            {% for category in categories %}
-                                <div class="tab-pane" id="{{ category.grouper|slugify }}">
-                                    <div class="d-inline-flex flex-wrap justify-content-center">
-                                        {% for button in category.list %}
+                {# Tabs content #}
+                <div class="card-body">
+                    <div class="tab-content">
+                        {% for category in categories %}
+                            <div class="tab-pane" id="{{ category.grouper|slugify }}">
+                                <div class="d-inline-flex flex-wrap justify-content-center">
+                                    {% for button in category.list %}
+                                        {% if button.display %}
                                             <button class="btn btn-outline-dark rounded-0 flex-fill"
-                                                    name="button" value="{{ button.name }}">
+                                                    id="button{{ button.id }}" name="button" value="{{ button.name }}">
                                                 {{ button.name }} ({{ button.amount | pretty_money }})
                                             </button>
-                                        {% endfor %}
-                                    </div>
+                                        {% endif %}
+                                    {% endfor %}
                                 </div>
-                            {% endfor %}
-                        </div>
+                            </div>
+                        {% endfor %}
+                    </div>
+                </div>
+
+                {# Mode switch #}
+                <div class="card-footer border-primary">
+                    <a class="btn btn-sm btn-secondary float-left" href="{% url 'note:template_list' %}">
+                        <i class="fa fa-edit"></i> {% trans "Edit" %}
+                    </a>
+                    <div class="btn-group btn-group-toggle float-right" data-toggle="buttons">
+                        <label for="single_conso" class="btn btn-sm btn-outline-primary active">
+                            <input type="radio" name="conso_type" id="single_conso" checked>
+                            {% trans "Single consumptions" %}
+                        </label>
+                        <label for="double_conso" class="btn btn-sm btn-outline-primary">
+                            <input type="radio" name="conso_type" id="double_conso">
+                            {% trans "Double consumptions" %}
+                        </label>
                     </div>
                 </div>
             </div>
         </div>
-    </form>
+    </div>
+
+    <div class="card shadow mb-4" id="history">
+        <div class="card-header">
+            <p class="card-text font-weight-bold">
+                {% trans "Recent transactions history" %}
+            </p>
+        </div>
+        {% render_table table %}
+    </div>
 {% endblock %}
 
 {% block extrajavascript %}
+    <script type="text/javascript" src="/static/js/consos.js"></script>
     <script type="text/javascript">
-        $(document).ready(function() {
-            // If hash of a category in the URL, then select this category
-            // else select the first one
-            if (location.hash) {
-                $("a[href='" + location.hash + "']").tab("show");
-            } else {
-                $("a[data-toggle='tab']").first().tab("show");
-            }
+        {% for button in most_used %}
+            {% if button.display %}
+                $("#most_used_button{{ button.id }}").click(function() {
+                    addConso({{ button.destination.id }}, {{ button.amount }},
+                        {{ polymorphic_ctype }}, {{ button.category.id }}, "{{ button.category.name }}",
+                        {{ button.id }}, "{{ button.name }}");
+                });
+            {% endif %}
+        {% endfor %}
 
-            // When selecting a category, change URL
-            $(document.body).on("click", "a[data-toggle='tab']", function(event) {
-                location.hash = this.getAttribute("href");
-            });
-        });
+        {% for button in transaction_templates %}
+            {% if button.display %}
+                $("#button{{ button.id }}").click(function() {
+                    addConso({{ button.destination.id }}, {{ button.amount }},
+                        {{ polymorphic_ctype }}, {{ button.category.id }}, "{{ button.category.name }}",
+                        {{ button.id }}, "{{ button.name }}");
+                });
+            {% endif %}
+        {% endfor %}
     </script>
 {% endblock %}
diff --git a/templates/note/transaction_form.html b/templates/note/transaction_form.html
index ff8504bc157666e021a4698c430db81163ebcd28..f320083e08dca5c1151d7bd0036a185328349746 100644
--- a/templates/note/transaction_form.html
+++ b/templates/note/transaction_form.html
@@ -3,35 +3,188 @@
 SPDX-License-Identifier: GPL-2.0-or-later
 {% endcomment %}
 
-{% load i18n static %}
+{% load i18n static django_tables2 %}
 
 {% block content %}
-    <form method="post" onsubmit="window.onbeforeunload=null">{% csrf_token %}
-        {% if form.non_field_errors %}
-            <p class="errornote">
-                {% for error in form.non_field_errors %}
-                    {{ error }}
-                {% endfor %}
-            </p>
-        {% endif %}
-        <fieldset class="module aligned">
-            {% for field in form %}
-                <div class="form-row{% if field.errors %} errors{% endif %}">
-                    {{ field.errors }}
-                    <div>
-                        {{ field.label_tag }}
-                        {% if field.is_readonly %}
-                            <div class="readonly">{{ field.contents }}</div>
-                        {% else %}
-                            {{ field }}
-                        {% endif %}
-                        {% if field.field.help_text %}
-                            <div class="help">{{ field.field.help_text|safe }}</div>
-                        {% endif %}
+
+    <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">
+                <label for="type_gift" class="btn btn-sm btn-outline-primary active">
+                    <input type="radio" name="transaction_type" id="type_gift" checked>
+                    {% trans "Gift" %}
+                </label>
+                <label for="type_transfer" class="btn btn-sm btn-outline-primary">
+                    <input type="radio" name="transaction_type" id="type_transfer">
+                    {% trans "Transfer" %}
+                </label>
+                <label for="type_credit" class="btn btn-sm btn-outline-primary">
+                    <input type="radio" name="transaction_type" id="type_credit">
+                    {% trans "Credit" %}
+                </label>
+                <label type="type_debit" class="btn btn-sm btn-outline-primary">
+                    <input type="radio" name="transaction_type" id="type_debit">
+                    {% trans "Debit" %}
+                </label>
+            </div>
+        </div>
+    </div>
+
+    <div class="row">
+        <div class="col-md-4" id="emitters_div" style="display: none;">
+            <div class="card border-success shadow mb-4">
+                <div class="card-header">
+                    <p class="card-text font-weight-bold">
+                        {% trans "Select emitters" %}
+                    </p>
+                </div>
+                <ul class="list-group list-group-flush" id="source_note_list">
+                </ul>
+                <div class="card-body">
+                    <input class="form-control mx-auto d-block" type="text" id="source_note" />
+                    <ul class="list-group list-group-flush" id="source_alias_matched">
+                    </ul>
+                </div>
+            </div>
+        </div>
+
+        <div class="col-xl-4" id="note_infos_div">
+            <div class="card border-success shadow mb-4">
+                <img src="/media/pic/default.png"
+                    id="profile_pic" alt="" class="img-fluid rounded mx-auto d-block">
+                <div class="card-body text-center">
+                    <span id="user_note"></span>
+                </div>
+            </div>
+        </div>
+
+        <div class="col-md-4" id="external_div" style="display: none;">
+            <div class="card border-success shadow mb-4">
+                <div class="card-header">
+                    <p class="card-text font-weight-bold">
+                        {% trans "External payment" %}
+                    </p>
+                </div>
+                <ul class="list-group list-group-flush" id="source_note_list">
+                </ul>
+                <div class="card-body">
+                    <div class="form-row">
+                        <div class="col-md-12">
+                            <label for="credit_type">{% trans "Transfer type" %} :</label>
+                            <select id="credit_type" class="custom-select">
+                                {% for special_type in special_types %}
+                                    <option value="{{ special_type.id }}">{{ special_type.special_type }}</option>
+                                {% endfor %}
+                            </select>
+                        </div>
+                    </div>
+                    <div class="form-row">
+                        <div class="col-md-12">
+                            <label for="last_name">{% trans "Name" %} :</label>
+                            <input type="text" id="last_name" class="form-control" />
+                        </div>
+                    </div>
+                    <div class="form-row">
+                        <div class="col-md-12">
+                            <label for="first_name">{% trans "First name" %} :</label>
+                            <input type="text" id="first_name" class="form-control" />
+                        </div>
                     </div>
+                    <div class="form-row">
+                        <div class="col-md-12">
+                            <label for="bank">{% trans "Bank" %} :</label>
+                            <input type="text" id="bank" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <div class="col-md-8" id="dests_div">
+            <div class="card border-info shadow mb-4">
+                <div class="card-header">
+                    <p class="card-text font-weight-bold" id="dest_title">
+                        {% trans "Select receivers" %}
+                    </p>
                 </div>
-            {% endfor %}
-        </fieldset>
-        <input type="submit" value="{% trans 'Transfer' %}">
-    </form>
+                <ul class="list-group list-group-flush" id="dest_note_list">
+                </ul>
+                <div class="card-body">
+                    <input class="form-control mx-auto d-block" type="text" id="dest_note" />
+                    <ul class="list-group list-group-flush" id="dest_alias_matched">
+                    </ul>
+                </div>
+            </div>
+        </div>
+    </div>
+
+
+    <div class="form-row">
+        <div class="form-group col-md-6">
+            <label for="amount">{% trans "Amount" %} :</label>
+            <div class="input-group">
+                <input class="form-control mx-auto d-block" type="number" min="0" step="0.01" id="amount" />
+                <div class="input-group-append">
+                    <span class="input-group-text">€</span>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group col-md-6">
+            <label for="reason">{% trans "Reason" %} :</label>
+            <input class="form-control mx-auto d-block" type="text" id="reason" required />
+        </div>
+    </div>
+
+    <div class="form-row">
+        <div class="col-md-12">
+            <button id="transfer" class="form-control btn btn-primary">{% trans 'Transfer' %}</button>
+        </div>
+    </div>
+
+    <div class="card shadow mb-4" id="history">
+        <div class="card-header">
+            <p class="card-text font-weight-bold">
+                {% trans "Recent transactions history" %}
+            </p>
+        </div>
+        {% render_table table %}
+    </div>
+{% endblock %}
+
+{% block extrajavascript %}
+    <script>
+        TRANSFER_POLYMORPHIC_CTYPE = {{ polymorphic_ctype }};
+        SPECIAL_TRANSFER_POLYMORPHIC_CTYPE = {{ special_polymorphic_ctype }};
+        user_id = {{ user.note.pk }};
+
+        $("#type_gift").click(function() {
+            $("#emitters_div").hide();
+            $("#external_div").hide();
+            $("#dests_div").attr('class', 'col-md-8');
+            $("#dest_title").text("{% trans "Select receivers" %}");
+        });
+
+        $("#type_transfer").click(function() {
+            $("#emitters_div").show();
+            $("#external_div").hide();
+            $("#dests_div").attr('class', 'col-md-4');
+            $("#dest_title").text("{% trans "Select receivers" %}");
+        });
+
+        $("#type_credit").click(function() {
+            $("#emitters_div").hide();
+            $("#external_div").show();
+            $("#dests_div").attr('class', 'col-md-4');
+            $("#dest_title").text("{% trans "Credit note" %}");
+        });
+
+        $("#type_debit").click(function() {
+            $("#emitters_div").hide();
+            $("#external_div").show();
+            $("#dests_div").attr('class', 'col-md-4');
+            $("#dest_title").text("{% trans "Debit note" %}");
+        });
+    </script>
+    <script src="/static/js/transfer.js"></script>
 {% endblock %}
diff --git a/templates/note/transactiontemplate_form.html b/templates/note/transactiontemplate_form.html
index 3fc2dd8be1aa4916e6425659c2b79decad67e3b9..1f9a574a050ff49056f3a6931a00636cb951b747 100644
--- a/templates/note/transactiontemplate_form.html
+++ b/templates/note/transactiontemplate_form.html
@@ -1,8 +1,9 @@
 {% extends "base.html" %}
 {% load static %}
+{% load i18n %}
 {% load crispy_forms_tags %}
 {% block content %}
-<p><a class="btn btn-default" href="{% url 'note:template_list' %}">Template Listing</a></p>
+<p><a class="btn btn-default" href="{% url 'note:template_list' %}">{% trans "Buttons list" %}</a></p>
 <form method="post">
 {% csrf_token %}
 {{form|crispy}}
diff --git a/templates/note/transactiontemplate_list.html b/templates/note/transactiontemplate_list.html
index 62e4d164794c9995153c7aeedb8def8a381b8e74..4960023694b30179bc3423c30c4a451fa3642a8b 100644
--- a/templates/note/transactiontemplate_list.html
+++ b/templates/note/transactiontemplate_list.html
@@ -15,7 +15,7 @@
     <td><a href="{{object.get_absolute_url}}">{{ object.name }}</a></td>
     <td>{{ object.destination }}</td>
     <td>{{ object.amount | pretty_money }}</td>
-    <td>{{ object.template_type }}</td>
+    <td>{{ object.category }}</td>
 </tr>
 {% endfor %}
 </table>
diff --git a/templates/registration/login.html b/templates/registration/login.html
index 04ef8d7deb25d939ef1ca6f1eb6ab9995416ed69..5a4322d138ea110f8089d6c9a928d579740f476c 100644
--- a/templates/registration/login.html
+++ b/templates/registration/login.html
@@ -17,6 +17,10 @@ SPDX-License-Identifier: GPL-2.0-or-later
         </p>
     {% endif %}
 
+    <div class="alert alert-info">
+        Vous pouvez aussi vous connecter via l'authentification centralisée <a href="{% url 'cas_login' %}">en suivant ce lien.</a>
+    </div>
+
     <form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
         {{ form | crispy }}
         <input type="submit" value="{% trans 'Log in' %}" class="btn btn-primary">
diff --git a/tox.ini b/tox.ini
index c4e88c786dc93b3d03e10e9b775644415267a2a1..2217b6bfbf407fc4056ac13140ae604e32bfd87f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -9,7 +9,10 @@ skipsdist = True
 setenv =
 	PYTHONWARNINGS = all
 deps =
-    -r{toxinidir}/requirements.txt
+    -r{toxinidir}/requirements/base.txt
+    -r{toxinidir}/requirements/api.txt
+    -r{toxinidir}/requirements/cas.txt
+    -r{toxinidir}/requirements/production.txt
     coverage
 commands =
     ./manage.py makemigrations
@@ -18,7 +21,10 @@ commands =
 
 [testenv:linters]
 deps =
-    -r{toxinidir}/requirements.txt
+    -r{toxinidir}/requirements/base.txt
+    -r{toxinidir}/requirements/api.txt
+    -r{toxinidir}/requirements/cas.txt
+    -r{toxinidir}/requirements/production.txt
     flake8
     flake8-colors
     flake8-import-order
@@ -26,7 +32,7 @@ deps =
     pep8-naming
     pyflakes
 commands =
-    flake8 apps/activity apps/api apps/member apps/note
+    flake8 apps/activity apps/api apps/logs apps/member apps/note
 
 [flake8]
 # Ignore too many errors, should be reduced in the future