diff --git a/Dockerfile b/Dockerfile
index d42bdd1f479c7155f33160d75271f54c1bd5ee6c..dfc49d04c604229621d73de77b553bb1fbc5a1ac 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -9,6 +9,11 @@ RUN apt update && \
     apt install -y gettext nginx uwsgi uwsgi-plugin-python3 && \
     rm -rf /var/lib/apt/lists/*
 
+# Install LaTeX requirements
+RUN apt update && \
+    apt install -y texlive-latex-extra texlive-fonts-extra texlive-lang-french && \
+    rm -rf /var/lib/apt/lists/*
+
 COPY . /code/
 
 # Comment what is not needed
diff --git a/README.md b/README.md
index 1ffe8793a27cdf3d1082a88a19df51e0662ac133..9b0c927ecb5768e50e953b4be206801c000c2b91 100644
--- a/README.md
+++ b/README.md
@@ -6,13 +6,17 @@
 
 ## Installation sur un serveur
 
-On supposera pour la suite que vous utiliser debian/ubuntu sur un serveur tout nu ou bien configuré.
+On supposera pour la suite que vous utilisez Debian/Ubuntu sur un serveur tout nu ou bien configuré.
 
 1. Paquets nécessaires
 
         $ sudo apt install nginx python3 python3-pip python3-dev uwsgi
         $ sudo apt install uwsgi-plugin-python3 python3-venv git acl
 
+    La génération des factures de l'application trésorerie nécessite une installation de LaTeX suffisante :
+
+        $ sudo apt install texlive-latex-extra texlive-fonts-extra texlive-lang-french
+
 2. Clonage du dépot
 
     on se met au bon endroit :
diff --git a/apps/api/urls.py b/apps/api/urls.py
index b275a0b87665464550f57ae7ea1dcdb943370183..67fdba309267bca584d7869b15d92341cf1ba2db 100644
--- a/apps/api/urls.py
+++ b/apps/api/urls.py
@@ -12,6 +12,7 @@ from activity.api.urls import register_activity_urls
 from api.viewsets import ReadProtectedModelViewSet
 from member.api.urls import register_members_urls
 from note.api.urls import register_note_urls
+from treasury.api.urls import register_treasury_urls
 from logs.api.urls import register_logs_urls
 from permission.api.urls import register_permission_urls
 
@@ -74,6 +75,7 @@ router.register('user', UserViewSet)
 register_members_urls(router, 'members')
 register_activity_urls(router, 'activity')
 register_note_urls(router, 'note')
+register_treasury_urls(router, 'treasury')
 register_permission_urls(router, 'permission')
 register_logs_urls(router, 'logs')
 
diff --git a/apps/note/fixtures/initial.json b/apps/note/fixtures/initial.json
index 63285e34b2c525243d3e851579db0f9ad40b10b3..a0682daef6dcf8816f05e4ff2ec8781770324fd9 100644
--- a/apps/note/fixtures/initial.json
+++ b/apps/note/fixtures/initial.json
@@ -256,4 +256,4 @@
       "name": "Alcool"
     }
   }
-]
+]
\ No newline at end of file
diff --git a/apps/note/models/__init__.py b/apps/note/models/__init__.py
index 8f1921f9e8da3f04f775d59cab20d71de4eab25d..e9c8a0a9f28d15f586e4398a0f29406bdf1aec01 100644
--- a/apps/note/models/__init__.py
+++ b/apps/note/models/__init__.py
@@ -3,12 +3,12 @@
 
 from .notes import Alias, Note, NoteClub, NoteSpecial, NoteUser
 from .transactions import MembershipTransaction, Transaction, \
-    TemplateCategory, TransactionTemplate, RecurrentTransaction
+    TemplateCategory, TransactionTemplate, RecurrentTransaction, SpecialTransaction
 
 __all__ = [
     # Notes
     'Alias', 'Note', 'NoteClub', 'NoteSpecial', 'NoteUser',
     # Transactions
     'MembershipTransaction', 'Transaction', 'TemplateCategory', 'TransactionTemplate',
-    'RecurrentTransaction',
+    'RecurrentTransaction', 'SpecialTransaction',
 ]
diff --git a/apps/note/templatetags/pretty_money.py b/apps/note/templatetags/pretty_money.py
index 265870a85aadd70d949f13cdeb990db5c0955a18..ba527f9be24c170cd0260f604e588d4f57fb8382 100644
--- a/apps/note/templatetags/pretty_money.py
+++ b/apps/note/templatetags/pretty_money.py
@@ -18,5 +18,10 @@ def pretty_money(value):
         )
 
 
+def cents_to_euros(value):
+    return "{:.02f}".format(value / 100) if value else ""
+
+
 register = template.Library()
 register.filter('pretty_money', pretty_money)
+register.filter('cents_to_euros', cents_to_euros)
diff --git a/apps/permission/admin.py b/apps/permission/admin.py
index aaa6f66142b7c1b878312029fb2a64bf291d5e5c..4312f4b0158e2bf7c2fb22d9a0894b51e161838e 100644
--- a/apps/permission/admin.py
+++ b/apps/permission/admin.py
@@ -28,4 +28,3 @@ class RolePermissionsAdmin(admin.ModelAdmin):
     Admin customisation for RolePermissions
     """
     list_display = ('role', )
-
diff --git a/apps/permission/api/views.py b/apps/permission/api/views.py
index 6087c83e774b1afca9f73fd39d24abd2d8738de4..965e82c928aa676555f4f8f47356284cbac6b0ff 100644
--- a/apps/permission/api/views.py
+++ b/apps/permission/api/views.py
@@ -2,8 +2,8 @@
 # SPDX-License-Identifier: GPL-3.0-or-later
 
 from django_filters.rest_framework import DjangoFilterBackend
-
 from api.viewsets import ReadOnlyProtectedModelViewSet
+
 from .serializers import PermissionSerializer
 from ..models import Permission
 
diff --git a/apps/permission/models.py b/apps/permission/models.py
index 109c1875adedcaa970a876da6298993badbd7851..205f5b418c7baf09254087c939a504ee919c7f0f 100644
--- a/apps/permission/models.py
+++ b/apps/permission/models.py
@@ -10,7 +10,6 @@ from django.core.exceptions import ValidationError
 from django.db import models
 from django.db.models import F, Q, Model
 from django.utils.translation import gettext_lazy as _
-
 from member.models import Role
 
 
@@ -281,4 +280,3 @@ class RolePermissions(models.Model):
 
     def __str__(self):
         return str(self.role)
-
diff --git a/apps/permission/permissions.py b/apps/permission/permissions.py
index 9f6d8cd22c6cc85f60b4c0dd005406d919283bcd..7097085f04670a33fbb65431cfdc046e7e00d832 100644
--- a/apps/permission/permissions.py
+++ b/apps/permission/permissions.py
@@ -2,6 +2,7 @@
 # SPDX-License-Identifier: GPL-3.0-or-later
 
 from rest_framework.permissions import DjangoObjectPermissions
+
 from .backends import PermissionBackend
 
 SAFE_METHODS = ('HEAD', 'OPTIONS', )
diff --git a/apps/permission/signals.py b/apps/permission/signals.py
index aebca39db243d953d611c057da8a84505c133701..1e30f56f5793296e929b195231bb4a4eb4660d26 100644
--- a/apps/permission/signals.py
+++ b/apps/permission/signals.py
@@ -3,10 +3,9 @@
 
 from django.core.exceptions import PermissionDenied
 from django.db.models.signals import pre_save, pre_delete, post_save, post_delete
-
 from logs import signals as logs_signals
-from permission.backends import PermissionBackend
 from note_kfet.middlewares import get_current_authenticated_user
+from permission.backends import PermissionBackend
 
 
 EXCLUDED = [
diff --git a/apps/permission/templatetags/perms.py b/apps/permission/templatetags/perms.py
index 8f2a0006f097601b0959cb745f7ee5d2dcc398e9..8bcd359794f1721f84b133c9a3a2402ac788bcb9 100644
--- a/apps/permission/templatetags/perms.py
+++ b/apps/permission/templatetags/perms.py
@@ -3,10 +3,8 @@
 
 from django.contrib.contenttypes.models import ContentType
 from django.template.defaultfilters import stringfilter
-
-from note_kfet.middlewares import get_current_authenticated_user, get_current_session
 from django import template
-
+from note_kfet.middlewares import get_current_authenticated_user, get_current_session
 from permission.backends import PermissionBackend
 
 
diff --git a/apps/treasury/__init__.py b/apps/treasury/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c9c6150edaa9526ab1476be7dc0e5c144ceabe1f
--- /dev/null
+++ b/apps/treasury/__init__.py
@@ -0,0 +1,4 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+default_app_config = 'treasury.apps.TreasuryConfig'
diff --git a/apps/treasury/admin.py b/apps/treasury/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..abeec3e37681997023ffbfa2b7e7a857531d9420
--- /dev/null
+++ b/apps/treasury/admin.py
@@ -0,0 +1,27 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-lateré
+
+from django.contrib import admin
+
+from .models import RemittanceType, Remittance
+
+
+@admin.register(RemittanceType)
+class RemittanceTypeAdmin(admin.ModelAdmin):
+    """
+    Admin customisation for RemiitanceType
+    """
+    list_display = ('note', )
+
+
+@admin.register(Remittance)
+class RemittanceAdmin(admin.ModelAdmin):
+    """
+    Admin customisation for Remittance
+    """
+    list_display = ('remittance_type', 'date', 'comment', 'count', 'amount', 'closed', )
+
+    def has_change_permission(self, request, obj=None):
+        if not obj:
+            return True
+        return not obj.closed and super().has_change_permission(request, obj)
diff --git a/apps/treasury/api/__init__.py b/apps/treasury/api/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/apps/treasury/api/serializers.py b/apps/treasury/api/serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..f1bbef75cfac7e2d714e7a3d83b073a5f39342a9
--- /dev/null
+++ b/apps/treasury/api/serializers.py
@@ -0,0 +1,62 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from rest_framework import serializers
+from note.api.serializers import SpecialTransactionSerializer
+
+from ..models import Invoice, Product, RemittanceType, Remittance
+
+
+class ProductSerializer(serializers.ModelSerializer):
+    """
+    REST API Serializer for Product types.
+    The djangorestframework plugin will analyse the model `Product` and parse all fields in the API.
+    """
+
+    class Meta:
+        model = Product
+        fields = '__all__'
+
+
+class InvoiceSerializer(serializers.ModelSerializer):
+    """
+    REST API Serializer for Invoice types.
+    The djangorestframework plugin will analyse the model `Invoice` and parse all fields in the API.
+    """
+    class Meta:
+        model = Invoice
+        fields = '__all__'
+        read_only_fields = ('bde',)
+
+    products = serializers.SerializerMethodField()
+
+    def get_products(self, obj):
+        return serializers.ListSerializer(child=ProductSerializer())\
+            .to_representation(Product.objects.filter(invoice=obj).all())
+
+
+class RemittanceTypeSerializer(serializers.ModelSerializer):
+    """
+    REST API Serializer for RemittanceType types.
+    The djangorestframework plugin will analyse the model `RemittanceType` and parse all fields in the API.
+    """
+
+    class Meta:
+        model = RemittanceType
+        fields = '__all__'
+
+
+class RemittanceSerializer(serializers.ModelSerializer):
+    """
+    REST API Serializer for Remittance types.
+    The djangorestframework plugin will analyse the model `Remittance` and parse all fields in the API.
+    """
+
+    transactions = serializers.SerializerMethodField()
+
+    class Meta:
+        model = Remittance
+        fields = '__all__'
+
+    def get_transactions(self, obj):
+        return serializers.ListSerializer(child=SpecialTransactionSerializer()).to_representation(obj.transactions)
diff --git a/apps/treasury/api/urls.py b/apps/treasury/api/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..30ac00e1ae3a9f598634099c217d2f5b5bafee0a
--- /dev/null
+++ b/apps/treasury/api/urls.py
@@ -0,0 +1,14 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from .views import InvoiceViewSet, ProductViewSet, RemittanceViewSet, RemittanceTypeViewSet
+
+
+def register_treasury_urls(router, path):
+    """
+    Configure router for treasury REST API.
+    """
+    router.register(path + '/invoice', InvoiceViewSet)
+    router.register(path + '/product', ProductViewSet)
+    router.register(path + '/remittance_type', RemittanceTypeViewSet)
+    router.register(path + '/remittance', RemittanceViewSet)
diff --git a/apps/treasury/api/views.py b/apps/treasury/api/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a70fd2466379461d2cff980a882b5107d39ae0e
--- /dev/null
+++ b/apps/treasury/api/views.py
@@ -0,0 +1,53 @@
+# 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.filters import SearchFilter
+from api.viewsets import ReadProtectedModelViewSet
+
+from .serializers import InvoiceSerializer, ProductSerializer, RemittanceTypeSerializer, RemittanceSerializer
+from ..models import Invoice, Product, RemittanceType, Remittance
+
+
+class InvoiceViewSet(ReadProtectedModelViewSet):
+    """
+    REST API View set.
+    The djangorestframework plugin will get all `Invoice` objects, serialize it to JSON with the given serializer,
+    then render it on /api/treasury/invoice/
+    """
+    queryset = Invoice.objects.all()
+    serializer_class = InvoiceSerializer
+    filter_backends = [DjangoFilterBackend]
+    filterset_fields = ['bde', ]
+
+
+class ProductViewSet(ReadProtectedModelViewSet):
+    """
+    REST API View set.
+    The djangorestframework plugin will get all `Product` objects, serialize it to JSON with the given serializer,
+    then render it on /api/treasury/product/
+    """
+    queryset = Product.objects.all()
+    serializer_class = ProductSerializer
+    filter_backends = [SearchFilter]
+    search_fields = ['$designation', ]
+
+
+class RemittanceTypeViewSet(ReadProtectedModelViewSet):
+    """
+    REST API View set.
+    The djangorestframework plugin will get all `RemittanceType` objects, serialize it to JSON with the given serializer
+    then render it on /api/treasury/remittance_type/
+    """
+    queryset = RemittanceType.objects.all()
+    serializer_class = RemittanceTypeSerializer
+
+
+class RemittanceViewSet(ReadProtectedModelViewSet):
+    """
+    REST API View set.
+    The djangorestframework plugin will get all `Remittance` objects, serialize it to JSON with the given serializer,
+    then render it on /api/treasury/remittance/
+    """
+    queryset = Remittance.objects.all()
+    serializer_class = RemittanceSerializer
diff --git a/apps/treasury/apps.py b/apps/treasury/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..e2873ea2d8cba8cefcb88feb19750790e639d425
--- /dev/null
+++ b/apps/treasury/apps.py
@@ -0,0 +1,33 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.apps import AppConfig
+from django.db.models import Q
+from django.db.models.signals import post_save, post_migrate
+from django.utils.translation import gettext_lazy as _
+
+
+class TreasuryConfig(AppConfig):
+    name = 'treasury'
+    verbose_name = _('Treasury')
+
+    def ready(self):
+        """
+        Define app internal signals to interact with other apps
+        """
+
+        from . import signals
+        from note.models import SpecialTransaction, NoteSpecial
+        from treasury.models import SpecialTransactionProxy
+        post_save.connect(signals.save_special_transaction, sender=SpecialTransaction)
+
+        def setup_specialtransactions_proxies(**kwargs):
+            # If the treasury app was disabled for any reason during a certain amount of time,
+            # we ensure that each special transaction is linked to a proxy
+            for transaction in SpecialTransaction.objects.filter(
+                    source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)),
+                    specialtransactionproxy=None,
+            ):
+                SpecialTransactionProxy.objects.create(transaction=transaction, remittance=None)
+
+        post_migrate.connect(setup_specialtransactions_proxies, sender=SpecialTransactionProxy)
diff --git a/apps/treasury/fixtures/initial.json b/apps/treasury/fixtures/initial.json
new file mode 100644
index 0000000000000000000000000000000000000000..143d2101bc00195201105623ce7d5d1528b7df2d
--- /dev/null
+++ b/apps/treasury/fixtures/initial.json
@@ -0,0 +1,9 @@
+[
+  {
+    "model": "treasury.remittancetype",
+    "pk": 1,
+    "fields": {
+      "note": 3
+    }
+  }
+]
diff --git a/apps/treasury/forms.py b/apps/treasury/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..caaa365fea3dfde0f7dda776f5872c34428333f7
--- /dev/null
+++ b/apps/treasury/forms.py
@@ -0,0 +1,156 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+import datetime
+
+from crispy_forms.helper import FormHelper
+from crispy_forms.layout import Submit
+from django import forms
+from django.utils.translation import gettext_lazy as _
+
+from .models import Invoice, Product, Remittance, SpecialTransactionProxy
+
+
+class InvoiceForm(forms.ModelForm):
+    """
+    Create and generate invoices.
+    """
+
+    # Django forms don't support date fields. We have to add it manually
+    date = forms.DateField(
+        initial=datetime.date.today,
+        widget=forms.TextInput(attrs={'type': 'date'})
+    )
+
+    def clean_date(self):
+        self.instance.date = self.data.get("date")
+
+    class Meta:
+        model = Invoice
+        exclude = ('bde', )
+
+
+# Add a subform per product in the invoice form, and manage correctly the link between the invoice and
+# its products. The FormSet will search automatically the ForeignKey in the Product model.
+ProductFormSet = forms.inlineformset_factory(
+    Invoice,
+    Product,
+    fields='__all__',
+    extra=1,
+)
+
+
+class ProductFormSetHelper(FormHelper):
+    """
+    Specify some template informations for the product form.
+    """
+
+    def __init__(self, form=None):
+        super().__init__(form)
+        self.form_tag = False
+        self.form_method = 'POST'
+        self.form_class = 'form-inline'
+        self.template = 'bootstrap4/table_inline_formset.html'
+
+
+class RemittanceForm(forms.ModelForm):
+    """
+    Create remittances.
+    """
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        self.helper = FormHelper()
+
+        # We can't update the type of the remittance once created.
+        if self.instance.pk:
+            self.fields["remittance_type"].disabled = True
+            self.fields["remittance_type"].required = False
+
+        # We display the submit button iff the remittance is open,
+        # the close button iff it is open and has a linked transaction
+        if not self.instance.closed:
+            self.helper.add_input(Submit('submit', _("Submit"), attr={'class': 'btn btn-block btn-primary'}))
+            if self.instance.transactions:
+                self.helper.add_input(Submit("close", _("Close"), css_class='btn btn-success'))
+        else:
+            # If the remittance is closed, we can't change anything
+            self.fields["comment"].disabled = True
+            self.fields["comment"].required = False
+
+    def clean(self):
+        # We can't update anything if the remittance is already closed.
+        if self.instance.closed:
+            self.add_error("comment", _("Remittance is already closed."))
+
+        cleaned_data = super().clean()
+
+        if self.instance.pk and cleaned_data.get("remittance_type") != self.instance.remittance_type:
+            self.add_error("remittance_type", _("You can't change the type of the remittance."))
+
+        # The close button is manually handled
+        if "close" in self.data:
+            self.instance.closed = True
+            self.cleaned_data["closed"] = True
+
+        return cleaned_data
+
+    class Meta:
+        model = Remittance
+        fields = ('remittance_type', 'comment',)
+
+
+class LinkTransactionToRemittanceForm(forms.ModelForm):
+    """
+    Attach a special transaction to a remittance.
+    """
+
+    # Since we use a proxy model for special transactions, we add manually the fields related to the transaction
+    last_name = forms.CharField(label=_("Last name"))
+
+    first_name = forms.Field(label=_("First name"))
+
+    bank = forms.Field(label=_("Bank"))
+
+    amount = forms.IntegerField(label=_("Amount"), min_value=0)
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.helper = FormHelper()
+        # Add submit button
+        self.helper.add_input(Submit('submit', _("Submit"), attr={'class': 'btn btn-block btn-primary'}))
+
+        self.fields["remittance"].queryset = Remittance.objects.filter(closed=False)
+
+    def clean_last_name(self):
+        """
+        Replace the first name in the information of the transaction.
+        """
+        self.instance.transaction.last_name = self.data.get("last_name")
+        self.instance.transaction.clean()
+
+    def clean_first_name(self):
+        """
+        Replace the last name in the information of the transaction.
+        """
+        self.instance.transaction.first_name = self.data.get("first_name")
+        self.instance.transaction.clean()
+
+    def clean_bank(self):
+        """
+        Replace the bank in the information of the transaction.
+        """
+        self.instance.transaction.bank = self.data.get("bank")
+        self.instance.transaction.clean()
+
+    def clean_amount(self):
+        """
+        Replace the amount of the transaction.
+        """
+        self.instance.transaction.amount = self.data.get("amount")
+        self.instance.transaction.clean()
+
+    class Meta:
+        model = SpecialTransactionProxy
+        fields = ('remittance', )
diff --git a/apps/treasury/migrations/__init__.py b/apps/treasury/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/apps/treasury/models.py b/apps/treasury/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..bcd89db964dbcdf9da9cdee7e30be23fd4c597eb
--- /dev/null
+++ b/apps/treasury/models.py
@@ -0,0 +1,189 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.core.exceptions import ValidationError
+from django.db import models
+from django.db.models import Q
+from django.utils.translation import gettext_lazy as _
+from note.models import NoteSpecial, SpecialTransaction
+
+
+class Invoice(models.Model):
+    """
+    An invoice model that can generates a true invoice.
+    """
+
+    id = models.PositiveIntegerField(
+        primary_key=True,
+        verbose_name=_("Invoice identifier"),
+    )
+
+    bde = models.CharField(
+        max_length=32,
+        default='Saperlistpopette.png',
+        choices=(
+            ('Saperlistpopette.png', 'Saper[list]popette'),
+            ('Finalist.png', 'Fina[list]'),
+            ('Listorique.png', '[List]orique'),
+            ('Satellist.png', 'Satel[list]'),
+            ('Monopolist.png', 'Monopo[list]'),
+            ('Kataclist.png', 'Katac[list]'),
+        ),
+        verbose_name=_("BDE"),
+    )
+
+    object = models.CharField(
+        max_length=255,
+        verbose_name=_("Object"),
+    )
+
+    description = models.TextField(
+        verbose_name=_("Description")
+    )
+
+    name = models.CharField(
+        max_length=255,
+        verbose_name=_("Name"),
+    )
+
+    address = models.TextField(
+        verbose_name=_("Address"),
+    )
+
+    date = models.DateField(
+        auto_now_add=True,
+        verbose_name=_("Place"),
+    )
+
+    acquitted = models.BooleanField(
+        verbose_name=_("Acquitted"),
+    )
+
+
+class Product(models.Model):
+    """
+    Product that appears on an invoice.
+    """
+
+    invoice = models.ForeignKey(
+        Invoice,
+        on_delete=models.PROTECT,
+    )
+
+    designation = models.CharField(
+        max_length=255,
+        verbose_name=_("Designation"),
+    )
+
+    quantity = models.PositiveIntegerField(
+        verbose_name=_("Quantity")
+    )
+
+    amount = models.IntegerField(
+        verbose_name=_("Unit price")
+    )
+
+    @property
+    def amount_euros(self):
+        return self.amount / 100
+
+    @property
+    def total(self):
+        return self.quantity * self.amount
+
+    @property
+    def total_euros(self):
+        return self.total / 100
+
+
+class RemittanceType(models.Model):
+    """
+    Store what kind of remittances can be stored.
+    """
+
+    note = models.OneToOneField(
+        NoteSpecial,
+        on_delete=models.CASCADE,
+    )
+
+    def __str__(self):
+        return str(self.note)
+
+
+class Remittance(models.Model):
+    """
+    Treasurers want to regroup checks or bank transfers in bank remittances.
+    """
+
+    date = models.DateTimeField(
+        auto_now_add=True,
+        verbose_name=_("Date"),
+    )
+
+    remittance_type = models.ForeignKey(
+        RemittanceType,
+        on_delete=models.PROTECT,
+        verbose_name=_("Type"),
+    )
+
+    comment = models.CharField(
+        max_length=255,
+        verbose_name=_("Comment"),
+    )
+
+    closed = models.BooleanField(
+        default=False,
+        verbose_name=_("Closed"),
+    )
+
+    @property
+    def transactions(self):
+        """
+        :return: Transactions linked to this remittance.
+        """
+        if not self.pk:
+            return SpecialTransaction.objects.none()
+        return SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self)
+
+    def count(self):
+        """
+        Linked transactions count.
+        """
+        return self.transactions.count()
+
+    @property
+    def amount(self):
+        """
+        Total amount of the remittance.
+        """
+        return sum(transaction.total for transaction in self.transactions.all())
+
+    def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
+        # Check if all transactions have the right type.
+        if self.transactions.filter(~Q(source=self.remittance_type.note)).exists():
+            raise ValidationError("All transactions in a remittance must have the same type")
+
+        return super().save(force_insert, force_update, using, update_fields)
+
+    def __str__(self):
+        return _("Remittance #{:d}: {}").format(self.id, self.comment, )
+
+
+class SpecialTransactionProxy(models.Model):
+    """
+    In order to keep modularity, we don't that the Note app depends on the treasury app.
+    That's why we create a proxy in this app, to link special transactions and remittances.
+    If it isn't very clean, that makes what we want.
+    """
+
+    transaction = models.OneToOneField(
+        SpecialTransaction,
+        on_delete=models.CASCADE,
+    )
+
+    remittance = models.ForeignKey(
+        Remittance,
+        on_delete=models.PROTECT,
+        null=True,
+        verbose_name=_("Remittance"),
+    )
diff --git a/apps/treasury/signals.py b/apps/treasury/signals.py
new file mode 100644
index 0000000000000000000000000000000000000000..54c19c09e0c18820435deef0505f2cf0b63be030
--- /dev/null
+++ b/apps/treasury/signals.py
@@ -0,0 +1,12 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from treasury.models import SpecialTransactionProxy, RemittanceType
+
+
+def save_special_transaction(instance, created, **kwargs):
+    """
+    When a special transaction is created, we create its linked proxy
+    """
+    if created and RemittanceType.objects.filter(note=instance.source).exists():
+        SpecialTransactionProxy.objects.create(transaction=instance, remittance=None).save()
diff --git a/apps/treasury/tables.py b/apps/treasury/tables.py
new file mode 100644
index 0000000000000000000000000000000000000000..1ecc04db196ff8e94f45f60bbd5996a49c03e9e5
--- /dev/null
+++ b/apps/treasury/tables.py
@@ -0,0 +1,103 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
+from django_tables2 import A
+from note.models import SpecialTransaction
+from note.templatetags.pretty_money import pretty_money
+
+from .models import Invoice, Remittance
+
+
+class InvoiceTable(tables.Table):
+    """
+    List all invoices.
+    """
+    id = tables.LinkColumn("treasury:invoice_update",
+                           args=[A("pk")],
+                           text=lambda record: _("Invoice #{:d}").format(record.id), )
+
+    invoice = tables.LinkColumn("treasury:invoice_render",
+                                verbose_name=_("Invoice"),
+                                args=[A("pk")],
+                                accessor="pk",
+                                text="",
+                                attrs={
+                                    'a': {'class': 'fa fa-file-pdf-o'},
+                                    'td': {'data-turbolinks': 'false'}
+                                })
+
+    class Meta:
+        attrs = {
+            'class': 'table table-condensed table-striped table-hover'
+        }
+        model = Invoice
+        template_name = 'django_tables2/bootstrap4.html'
+        fields = ('id', 'name', 'object', 'acquitted', 'invoice',)
+
+
+class RemittanceTable(tables.Table):
+    """
+    List all remittances.
+    """
+
+    count = tables.Column(verbose_name=_("Transaction count"))
+
+    amount = tables.Column(verbose_name=_("Amount"))
+
+    view = tables.LinkColumn("treasury:remittance_update",
+                             verbose_name=_("View"),
+                             args=[A("pk")],
+                             text=_("View"),
+                             attrs={
+                                 'a': {'class': 'btn btn-primary'}
+                             }, )
+
+    def render_amount(self, value):
+        return pretty_money(value)
+
+    class Meta:
+        attrs = {
+            'class': 'table table-condensed table-striped table-hover'
+        }
+        model = Remittance
+        template_name = 'django_tables2/bootstrap4.html'
+        fields = ('id', 'date', 'remittance_type', 'comment', 'count', 'amount', 'view',)
+
+
+class SpecialTransactionTable(tables.Table):
+    """
+    List special credit transactions that are (or not, following the queryset) attached to a remittance.
+    """
+
+    # Display add and remove buttons. Use the `exclude` field to select what is needed.
+    remittance_add = tables.LinkColumn("treasury:link_transaction",
+                                       verbose_name=_("Remittance"),
+                                       args=[A("specialtransactionproxy.pk")],
+                                       text=_("Add"),
+                                       attrs={
+                                           'a': {'class': 'btn btn-primary'}
+                                       }, )
+
+    remittance_remove = tables.LinkColumn("treasury:unlink_transaction",
+                                          verbose_name=_("Remittance"),
+                                          args=[A("specialtransactionproxy.pk")],
+                                          text=_("Remove"),
+                                          attrs={
+                                              'a': {'class': 'btn btn-primary btn-danger'}
+                                          }, )
+
+    def render_id(self, record):
+        return record.specialtransactionproxy.pk
+
+    def render_amount(self, value):
+        return pretty_money(value)
+
+    class Meta:
+        attrs = {
+            'class': 'table table-condensed table-striped table-hover'
+        }
+        model = SpecialTransaction
+        template_name = 'django_tables2/bootstrap4.html'
+        fields = ('id', 'source', 'destination', 'last_name', 'first_name', 'bank', 'amount', 'reason',)
diff --git a/apps/treasury/urls.py b/apps/treasury/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..d44cc4145921fd63d3c645d67c39612e2103a54c
--- /dev/null
+++ b/apps/treasury/urls.py
@@ -0,0 +1,24 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.urls import path
+
+from .views import InvoiceCreateView, InvoiceListView, InvoiceUpdateView, InvoiceRenderView, RemittanceListView,\
+    RemittanceCreateView, RemittanceUpdateView, LinkTransactionToRemittanceView, UnlinkTransactionToRemittanceView
+
+app_name = 'treasury'
+urlpatterns = [
+    # Invoice app paths
+    path('invoice/', InvoiceListView.as_view(), name='invoice_list'),
+    path('invoice/create/', InvoiceCreateView.as_view(), name='invoice_create'),
+    path('invoice/<int:pk>/', InvoiceUpdateView.as_view(), name='invoice_update'),
+    path('invoice/render/<int:pk>/', InvoiceRenderView.as_view(), name='invoice_render'),
+
+    # Remittance app paths
+    path('remittance/', RemittanceListView.as_view(), name='remittance_list'),
+    path('remittance/create/', RemittanceCreateView.as_view(), name='remittance_create'),
+    path('remittance/<int:pk>/', RemittanceUpdateView.as_view(), name='remittance_update'),
+    path('remittance/link_transaction/<int:pk>/', LinkTransactionToRemittanceView.as_view(), name='link_transaction'),
+    path('remittance/unlink_transaction/<int:pk>/', UnlinkTransactionToRemittanceView.as_view(),
+         name='unlink_transaction'),
+]
diff --git a/apps/treasury/views.py b/apps/treasury/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..904405661ed5ceb87c25737d40624a87401817a4
--- /dev/null
+++ b/apps/treasury/views.py
@@ -0,0 +1,316 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+import os
+import shutil
+import subprocess
+from tempfile import mkdtemp
+
+from crispy_forms.helper import FormHelper
+from django.contrib.auth.mixins import LoginRequiredMixin
+from django.core.exceptions import ValidationError
+from django.db.models import Q
+from django.http import HttpResponse
+from django.shortcuts import redirect
+from django.template.loader import render_to_string
+from django.urls import reverse_lazy
+from django.views.generic import CreateView, UpdateView
+from django.views.generic.base import View, TemplateView
+from django_tables2 import SingleTableView
+from note.models import SpecialTransaction, NoteSpecial
+from note_kfet.settings.base import BASE_DIR
+
+from .forms import InvoiceForm, ProductFormSet, ProductFormSetHelper, RemittanceForm, LinkTransactionToRemittanceForm
+from .models import Invoice, Product, Remittance, SpecialTransactionProxy
+from .tables import InvoiceTable, RemittanceTable, SpecialTransactionTable
+
+
+class InvoiceCreateView(LoginRequiredMixin, CreateView):
+    """
+    Create Invoice
+    """
+    model = Invoice
+    form_class = InvoiceForm
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+
+        form = context['form']
+        form.helper = FormHelper()
+        # Remove form tag on the generation of the form in the template (already present on the template)
+        form.helper.form_tag = False
+        # The formset handles the set of the products
+        form_set = ProductFormSet(instance=form.instance)
+        context['formset'] = form_set
+        context['helper'] = ProductFormSetHelper()
+        context['no_cache'] = True
+
+        return context
+
+    def form_valid(self, form):
+        ret = super().form_valid(form)
+
+        kwargs = {}
+
+        # The user type amounts in cents. We convert it in euros.
+        for key in self.request.POST:
+            value = self.request.POST[key]
+            if key.endswith("amount") and value:
+                kwargs[key] = str(int(100 * float(value)))
+            elif value:
+                kwargs[key] = value
+
+        # For each product, we save it
+        formset = ProductFormSet(kwargs, instance=form.instance)
+        if formset.is_valid():
+            for f in formset:
+                # We don't save the product if the designation is not entered, ie. if the line is empty
+                if f.is_valid() and f.instance.designation:
+                    f.save()
+                    f.instance.save()
+                else:
+                    f.instance = None
+
+        return ret
+
+    def get_success_url(self):
+        return reverse_lazy('treasury:invoice_list')
+
+
+class InvoiceListView(LoginRequiredMixin, SingleTableView):
+    """
+    List existing Invoices
+    """
+    model = Invoice
+    table_class = InvoiceTable
+
+
+class InvoiceUpdateView(LoginRequiredMixin, UpdateView):
+    """
+    Create Invoice
+    """
+    model = Invoice
+    form_class = InvoiceForm
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+
+        form = context['form']
+        form.helper = FormHelper()
+        # Remove form tag on the generation of the form in the template (already present on the template)
+        form.helper.form_tag = False
+        # Fill the intial value for the date field, with the initial date of the model instance
+        form.fields['date'].initial = form.instance.date
+        # The formset handles the set of the products
+        form_set = ProductFormSet(instance=form.instance)
+        context['formset'] = form_set
+        context['helper'] = ProductFormSetHelper()
+        context['no_cache'] = True
+
+        return context
+
+    def form_valid(self, form):
+        ret = super().form_valid(form)
+
+        kwargs = {}
+        # The user type amounts in cents. We convert it in euros.
+        for key in self.request.POST:
+            value = self.request.POST[key]
+            if key.endswith("amount") and value:
+                kwargs[key] = str(int(100 * float(value)))
+            elif value:
+                kwargs[key] = value
+
+        formset = ProductFormSet(kwargs, instance=form.instance)
+        saved = []
+        # For each product, we save it
+        if formset.is_valid():
+            for f in formset:
+                # We don't save the product if the designation is not entered, ie. if the line is empty
+                if f.is_valid() and f.instance.designation:
+                    f.save()
+                    f.instance.save()
+                    saved.append(f.instance.pk)
+                else:
+                    f.instance = None
+            # Remove old products that weren't given in the form
+            Product.objects.filter(~Q(pk__in=saved), invoice=form.instance).delete()
+
+        return ret
+
+    def get_success_url(self):
+        return reverse_lazy('treasury:invoice_list')
+
+
+class InvoiceRenderView(LoginRequiredMixin, View):
+    """
+    Render Invoice as a generated PDF with the given information and a LaTeX template
+    """
+
+    def get(self, request, **kwargs):
+        pk = kwargs["pk"]
+        invoice = Invoice.objects.get(pk=pk)
+        products = Product.objects.filter(invoice=invoice).all()
+
+        # Informations of the BDE. Should be updated when the school will move.
+        invoice.place = "Cachan"
+        invoice.my_name = "BDE ENS Cachan"
+        invoice.my_address_street = "61 avenue du Président Wilson"
+        invoice.my_city = "94230 Cachan"
+        invoice.bank_code = 30003
+        invoice.desk_code = 3894
+        invoice.account_number = 37280662
+        invoice.rib_key = 14
+        invoice.bic = "SOGEFRPP"
+
+        # Replace line breaks with the LaTeX equivalent
+        invoice.description = invoice.description.replace("\r", "").replace("\n", "\\\\ ")
+        invoice.address = invoice.address.replace("\r", "").replace("\n", "\\\\ ")
+        # Fill the template with the information
+        tex = render_to_string("treasury/invoice_sample.tex", dict(obj=invoice, products=products))
+
+        try:
+            os.mkdir(BASE_DIR + "/tmp")
+        except FileExistsError:
+            pass
+        # We render the file in a temporary directory
+        tmp_dir = mkdtemp(prefix=BASE_DIR + "/tmp/")
+
+        try:
+            with open("{}/invoice-{:d}.tex".format(tmp_dir, pk), "wb") as f:
+                f.write(tex.encode("UTF-8"))
+            del tex
+
+            # The file has to be rendered twice
+            for _ in range(2):
+                error = subprocess.Popen(
+                    ["pdflatex", "invoice-{}.tex".format(pk)],
+                    cwd=tmp_dir,
+                    stdin=open(os.devnull, "r"),
+                    stderr=open(os.devnull, "wb"),
+                    stdout=open(os.devnull, "wb"),
+                ).wait()
+
+                if error:
+                    raise IOError("An error attempted while generating a invoice (code=" + str(error) + ")")
+
+            # Display the generated pdf as a HTTP Response
+            pdf = open("{}/invoice-{}.pdf".format(tmp_dir, pk), 'rb').read()
+            response = HttpResponse(pdf, content_type="application/pdf")
+            response['Content-Disposition'] = "inline;filename=invoice-{:d}.pdf".format(pk)
+        except IOError as e:
+            raise e
+        finally:
+            # Delete all temporary files
+            shutil.rmtree(tmp_dir)
+
+        return response
+
+
+class RemittanceCreateView(LoginRequiredMixin, CreateView):
+    """
+    Create Remittance
+    """
+    model = Remittance
+    form_class = RemittanceForm
+
+    def get_success_url(self):
+        return reverse_lazy('treasury:remittance_list')
+
+    def get_context_data(self, **kwargs):
+        ctx = super().get_context_data(**kwargs)
+
+        ctx["table"] = RemittanceTable(data=Remittance.objects.all())
+        ctx["special_transactions"] = SpecialTransactionTable(data=SpecialTransaction.objects.none())
+
+        return ctx
+
+
+class RemittanceListView(LoginRequiredMixin, TemplateView):
+    """
+    List existing Remittances
+    """
+    template_name = "treasury/remittance_list.html"
+
+    def get_context_data(self, **kwargs):
+        ctx = super().get_context_data(**kwargs)
+
+        ctx["opened_remittances"] = RemittanceTable(data=Remittance.objects.filter(closed=False).all())
+        ctx["closed_remittances"] = RemittanceTable(data=Remittance.objects.filter(closed=True).reverse().all())
+
+        ctx["special_transactions_no_remittance"] = SpecialTransactionTable(
+            data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)),
+                                                   specialtransactionproxy__remittance=None).all(),
+            exclude=('remittance_remove', ))
+        ctx["special_transactions_with_remittance"] = SpecialTransactionTable(
+            data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)),
+                                                   specialtransactionproxy__remittance__closed=False).all(),
+            exclude=('remittance_add', ))
+
+        return ctx
+
+
+class RemittanceUpdateView(LoginRequiredMixin, UpdateView):
+    """
+    Update Remittance
+    """
+    model = Remittance
+    form_class = RemittanceForm
+
+    def get_success_url(self):
+        return reverse_lazy('treasury:remittance_list')
+
+    def get_context_data(self, **kwargs):
+        ctx = super().get_context_data(**kwargs)
+
+        ctx["table"] = RemittanceTable(data=Remittance.objects.all())
+        data = SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self.object).all()
+        ctx["special_transactions"] = SpecialTransactionTable(
+            data=data,
+            exclude=('remittance_add', 'remittance_remove', ) if self.object.closed else ('remittance_add', ))
+
+        return ctx
+
+
+class LinkTransactionToRemittanceView(LoginRequiredMixin, UpdateView):
+    """
+    Attach a special transaction to a remittance
+    """
+
+    model = SpecialTransactionProxy
+    form_class = LinkTransactionToRemittanceForm
+
+    def get_success_url(self):
+        return reverse_lazy('treasury:remittance_list')
+
+    def get_context_data(self, **kwargs):
+        ctx = super().get_context_data(**kwargs)
+
+        form = ctx["form"]
+        form.fields["last_name"].initial = self.object.transaction.last_name
+        form.fields["first_name"].initial = self.object.transaction.first_name
+        form.fields["bank"].initial = self.object.transaction.bank
+        form.fields["amount"].initial = self.object.transaction.amount
+        form.fields["remittance"].queryset = form.fields["remittance"] \
+            .queryset.filter(remittance_type__note=self.object.transaction.source)
+
+        return ctx
+
+
+class UnlinkTransactionToRemittanceView(LoginRequiredMixin, View):
+    """
+    Unlink a special transaction and its remittance
+    """
+
+    def get(self, *args, **kwargs):
+        pk = kwargs["pk"]
+        transaction = SpecialTransactionProxy.objects.get(pk=pk)
+
+        # The remittance must be open (or inexistant)
+        if transaction.remittance and transaction.remittance.closed:
+            raise ValidationError("Remittance is already closed.")
+
+        transaction.remittance = None
+        transaction.save()
+
+        return redirect('treasury:remittance_list')
diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po
index e61efb2a6f2e68626aefb660983d2931fba657ac..b6a8c1202f4818fc342f9423b7bcead9f6770217 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-03-16 11:53+0100\n"
+"POT-Creation-Date: 2020-03-24 15:49+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,9 @@ msgid "activity"
 msgstr ""
 
 #: apps/activity/models.py:19 apps/activity/models.py:44
-#: apps/member/models.py:61 apps/member/models.py:112
+#: apps/member/models.py:63 apps/member/models.py:114
 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
-#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:202
+#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:198
 #: templates/member/profile_detail.html:15
 msgid "name"
 msgstr ""
@@ -47,11 +47,12 @@ msgid "activity types"
 msgstr ""
 
 #: apps/activity/models.py:48 apps/note/models/transactions.py:69
+#: apps/permission/models.py:90
 msgid "description"
 msgstr ""
 
 #: apps/activity/models.py:54 apps/note/models/notes.py:164
-#: apps/note/models/transactions.py:62 apps/note/models/transactions.py:115
+#: apps/note/models/transactions.py:62
 msgid "type"
 msgstr ""
 
@@ -143,114 +144,114 @@ msgstr ""
 msgid "member"
 msgstr ""
 
-#: apps/member/models.py:23
+#: apps/member/models.py:25
 msgid "phone number"
 msgstr ""
 
-#: apps/member/models.py:29 templates/member/profile_detail.html:28
+#: apps/member/models.py:31 templates/member/profile_detail.html:28
 msgid "section"
 msgstr ""
 
-#: apps/member/models.py:30
+#: apps/member/models.py:32
 msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
 msgstr ""
 
-#: apps/member/models.py:36 templates/member/profile_detail.html:31
+#: apps/member/models.py:38 templates/member/profile_detail.html:31
 msgid "address"
 msgstr ""
 
-#: apps/member/models.py:42
+#: apps/member/models.py:44
 msgid "paid"
 msgstr ""
 
-#: apps/member/models.py:47 apps/member/models.py:48
+#: apps/member/models.py:49 apps/member/models.py:50
 msgid "user profile"
 msgstr ""
 
-#: apps/member/models.py:66
+#: apps/member/models.py:68
 msgid "email"
 msgstr ""
 
-#: apps/member/models.py:71
+#: apps/member/models.py:73
 msgid "membership fee"
 msgstr ""
 
-#: apps/member/models.py:75
+#: apps/member/models.py:77
 msgid "membership duration"
 msgstr ""
 
-#: apps/member/models.py:76
+#: apps/member/models.py:78
 msgid "The longest time a membership can last (NULL = infinite)."
 msgstr ""
 
-#: apps/member/models.py:81
+#: apps/member/models.py:83
 msgid "membership start"
 msgstr ""
 
-#: apps/member/models.py:82
+#: apps/member/models.py:84
 msgid "How long after January 1st the members can renew their membership."
 msgstr ""
 
-#: apps/member/models.py:87
+#: apps/member/models.py:89
 msgid "membership end"
 msgstr ""
 
-#: apps/member/models.py:88
+#: apps/member/models.py:90
 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:94 apps/note/models/notes.py:139
+#: apps/member/models.py:96 apps/note/models/notes.py:139
 msgid "club"
 msgstr ""
 
-#: apps/member/models.py:95
+#: apps/member/models.py:97
 msgid "clubs"
 msgstr ""
 
-#: apps/member/models.py:118
+#: apps/member/models.py:120 apps/permission/models.py:275
 msgid "role"
 msgstr ""
 
-#: apps/member/models.py:119
+#: apps/member/models.py:121
 msgid "roles"
 msgstr ""
 
-#: apps/member/models.py:143
+#: apps/member/models.py:145
 msgid "membership starts on"
 msgstr ""
 
-#: apps/member/models.py:146
+#: apps/member/models.py:148
 msgid "membership ends on"
 msgstr ""
 
-#: apps/member/models.py:150
+#: apps/member/models.py:152
 msgid "fee"
 msgstr ""
 
-#: apps/member/models.py:154
+#: apps/member/models.py:162
 msgid "membership"
 msgstr ""
 
-#: apps/member/models.py:155
+#: apps/member/models.py:163
 msgid "memberships"
 msgstr ""
 
-#: apps/member/views.py:69 templates/member/profile_detail.html:46
+#: apps/member/views.py:80 templates/member/profile_detail.html:46
 msgid "Update Profile"
 msgstr ""
 
-#: apps/member/views.py:82
+#: apps/member/views.py:93
 msgid "An alias with a similar name already exists."
 msgstr ""
 
-#: apps/member/views.py:132
+#: apps/member/views.py:146
 #, python-format
 msgid "Account #%(id)s: %(username)s"
 msgstr ""
 
-#: apps/member/views.py:202
+#: apps/member/views.py:216
 msgid "Alias successfully deleted"
 msgstr ""
 
@@ -415,84 +416,233 @@ msgstr ""
 msgid "quantity"
 msgstr ""
 
-#: apps/note/models/transactions.py:117 templates/note/transaction_form.html:15
-msgid "Gift"
+#: apps/note/models/transactions.py:115
+msgid "reason"
+msgstr ""
+
+#: apps/note/models/transactions.py:119
+msgid "valid"
+msgstr ""
+
+#: apps/note/models/transactions.py:124
+msgid "transaction"
+msgstr ""
+
+#: apps/note/models/transactions.py:125
+msgid "transactions"
 msgstr ""
 
-#: apps/note/models/transactions.py:118 templates/base.html:90
+#: apps/note/models/transactions.py:168 templates/base.html:98
 #: templates/note/transaction_form.html:19
-#: templates/note/transaction_form.html:126
+#: templates/note/transaction_form.html:145
 msgid "Transfer"
 msgstr ""
 
-#: apps/note/models/transactions.py:119
+#: apps/note/models/transactions.py:188
 msgid "Template"
 msgstr ""
 
-#: apps/note/models/transactions.py:120 templates/note/transaction_form.html:23
+#: apps/note/models/transactions.py:203
+msgid "first_name"
+msgstr ""
+
+#: apps/note/models/transactions.py:208
+msgid "bank"
+msgstr ""
+
+#: apps/note/models/transactions.py:214 templates/note/transaction_form.html:24
 msgid "Credit"
 msgstr ""
 
-#: apps/note/models/transactions.py:121 templates/note/transaction_form.html:27
+#: apps/note/models/transactions.py:214 templates/note/transaction_form.html:28
 msgid "Debit"
 msgstr ""
 
-#: apps/note/models/transactions.py:122 apps/note/models/transactions.py:230
+#: apps/note/models/transactions.py:230 apps/note/models/transactions.py:235
 msgid "membership transaction"
 msgstr ""
 
-#: apps/note/models/transactions.py:129
-msgid "reason"
+#: apps/note/models/transactions.py:231
+msgid "membership transactions"
 msgstr ""
 
-#: apps/note/models/transactions.py:133
-msgid "valid"
+#: apps/note/views.py:39
+msgid "Transfer money"
 msgstr ""
 
-#: apps/note/models/transactions.py:138
-msgid "transaction"
+#: apps/note/views.py:145 templates/base.html:79
+msgid "Consumptions"
 msgstr ""
 
-#: apps/note/models/transactions.py:139
-msgid "transactions"
+#: apps/permission/models.py:69 apps/permission/models.py:262
+#, python-brace-format
+msgid "Can {type} {model}.{field} in {query}"
 msgstr ""
 
-#: apps/note/models/transactions.py:207
-msgid "first_name"
+#: apps/permission/models.py:71 apps/permission/models.py:264
+#, python-brace-format
+msgid "Can {type} {model} in {query}"
 msgstr ""
 
-#: apps/note/models/transactions.py:212
-msgid "bank"
+#: apps/permission/models.py:84
+msgid "rank"
 msgstr ""
 
-#: apps/note/models/transactions.py:231
-msgid "membership transactions"
+#: apps/permission/models.py:147
+msgid "Specifying field applies only to view and change permission types."
 msgstr ""
 
-#: apps/note/views.py:31
-msgid "Transfer money"
+#: apps/treasury/apps.py:11 templates/base.html:102
+msgid "Treasury"
 msgstr ""
 
-#: apps/note/views.py:132 templates/base.html:78
-msgid "Consumptions"
+#: apps/treasury/forms.py:56 apps/treasury/forms.py:95
+#: templates/django_filters/rest_framework/form.html:5
+#: templates/member/club_form.html:10 templates/treasury/invoice_form.html:47
+msgid "Submit"
+msgstr ""
+
+#: apps/treasury/forms.py:58
+msgid "Close"
+msgstr ""
+
+#: apps/treasury/forms.py:65
+msgid "Remittance is already closed."
+msgstr ""
+
+#: apps/treasury/forms.py:70
+msgid "You can't change the type of the remittance."
+msgstr ""
+
+#: apps/treasury/forms.py:84
+msgid "Last name"
+msgstr ""
+
+#: apps/treasury/forms.py:86 templates/note/transaction_form.html:92
+msgid "First name"
+msgstr ""
+
+#: apps/treasury/forms.py:88 templates/note/transaction_form.html:98
+msgid "Bank"
+msgstr ""
+
+#: apps/treasury/forms.py:90 apps/treasury/tables.py:40
+#: templates/note/transaction_form.html:128
+#: templates/treasury/remittance_form.html:18
+msgid "Amount"
+msgstr ""
+
+#: apps/treasury/models.py:18
+msgid "Invoice identifier"
+msgstr ""
+
+#: apps/treasury/models.py:32
+msgid "BDE"
+msgstr ""
+
+#: apps/treasury/models.py:37
+msgid "Object"
+msgstr ""
+
+#: apps/treasury/models.py:41
+msgid "Description"
+msgstr ""
+
+#: apps/treasury/models.py:46 templates/note/transaction_form.html:86
+msgid "Name"
+msgstr ""
+
+#: apps/treasury/models.py:50
+msgid "Address"
+msgstr ""
+
+#: apps/treasury/models.py:55
+msgid "Place"
+msgstr ""
+
+#: apps/treasury/models.py:59
+msgid "Acquitted"
+msgstr ""
+
+#: apps/treasury/models.py:75
+msgid "Designation"
+msgstr ""
+
+#: apps/treasury/models.py:79
+msgid "Quantity"
+msgstr ""
+
+#: apps/treasury/models.py:83
+msgid "Unit price"
+msgstr ""
+
+#: apps/treasury/models.py:120
+msgid "Date"
 msgstr ""
 
-#: note_kfet/settings/__init__.py:61
+#: apps/treasury/models.py:126
+msgid "Type"
+msgstr ""
+
+#: apps/treasury/models.py:131
+msgid "Comment"
+msgstr ""
+
+#: apps/treasury/models.py:136
+msgid "Closed"
+msgstr ""
+
+#: apps/treasury/models.py:159
+msgid "Remittance #{:d}: {}"
+msgstr ""
+
+#: apps/treasury/models.py:178 apps/treasury/tables.py:64
+#: apps/treasury/tables.py:72 templates/treasury/invoice_list.html:13
+#: templates/treasury/remittance_list.html:13
+msgid "Remittance"
+msgstr ""
+
+#: apps/treasury/tables.py:16
+msgid "Invoice #{:d}"
+msgstr ""
+
+#: apps/treasury/tables.py:19 templates/treasury/invoice_list.html:10
+#: templates/treasury/remittance_list.html:10
+msgid "Invoice"
+msgstr ""
+
+#: apps/treasury/tables.py:38
+msgid "Transaction count"
+msgstr ""
+
+#: apps/treasury/tables.py:43 apps/treasury/tables.py:45
+msgid "View"
+msgstr ""
+
+#: apps/treasury/tables.py:66
+msgid "Add"
+msgstr ""
+
+#: apps/treasury/tables.py:74
+msgid "Remove"
+msgstr ""
+
+#: note_kfet/settings/__init__.py:63
 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
+#: note_kfet/settings/base.py:153
 msgid "German"
 msgstr ""
 
-#: note_kfet/settings/base.py:157
+#: note_kfet/settings/base.py:154
 msgid "English"
 msgstr ""
 
-#: note_kfet/settings/base.py:158
+#: note_kfet/settings/base.py:155
 msgid "French"
 msgstr ""
 
@@ -500,15 +650,15 @@ msgstr ""
 msgid "The ENS Paris-Saclay BDE note."
 msgstr ""
 
-#: templates/base.html:81
+#: templates/base.html:84
 msgid "Clubs"
 msgstr ""
 
-#: templates/base.html:84
+#: templates/base.html:89
 msgid "Activities"
 msgstr ""
 
-#: templates/base.html:87
+#: templates/base.html:94
 msgid "Buttons"
 msgstr ""
 
@@ -567,11 +717,6 @@ msgstr ""
 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 ""
@@ -653,7 +798,7 @@ msgstr ""
 msgid "Sign up"
 msgstr ""
 
-#: templates/note/conso_form.html:28 templates/note/transaction_form.html:38
+#: templates/note/conso_form.html:28 templates/note/transaction_form.html:40
 msgid "Select emitters"
 msgstr ""
 
@@ -681,49 +826,37 @@ msgstr ""
 msgid "Double consumptions"
 msgstr ""
 
-#: templates/note/conso_form.html:141
+#: templates/note/conso_form.html:141 templates/note/transaction_form.html:152
 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"
+#: templates/note/transaction_form.html:15
+msgid "Gift"
 msgstr ""
 
-#: templates/note/transaction_form.html:79
-msgid "First name"
+#: templates/note/transaction_form.html:68
+msgid "External payment"
 msgstr ""
 
-#: templates/note/transaction_form.html:85
-msgid "Bank"
+#: templates/note/transaction_form.html:76
+msgid "Transfer type"
 msgstr ""
 
-#: templates/note/transaction_form.html:97
-#: templates/note/transaction_form.html:179
-#: templates/note/transaction_form.html:186
+#: templates/note/transaction_form.html:111
+#: templates/note/transaction_form.html:169
+#: templates/note/transaction_form.html:176
 msgid "Select receivers"
 msgstr ""
 
-#: templates/note/transaction_form.html:114
-msgid "Amount"
-msgstr ""
-
-#: templates/note/transaction_form.html:119
+#: templates/note/transaction_form.html:138
 msgid "Reason"
 msgstr ""
 
-#: templates/note/transaction_form.html:193
+#: templates/note/transaction_form.html:183
 msgid "Credit note"
 msgstr ""
 
-#: templates/note/transaction_form.html:200
+#: templates/note/transaction_form.html:190
 msgid "Debit note"
 msgstr ""
 
@@ -808,3 +941,72 @@ msgstr ""
 #: templates/registration/password_reset_form.html:11
 msgid "Reset my password"
 msgstr ""
+
+#: templates/treasury/invoice_form.html:6
+msgid "Invoices list"
+msgstr ""
+
+#: templates/treasury/invoice_form.html:42
+msgid "Add product"
+msgstr ""
+
+#: templates/treasury/invoice_form.html:43
+msgid "Remove product"
+msgstr ""
+
+#: templates/treasury/invoice_list.html:21
+msgid "New invoice"
+msgstr ""
+
+#: templates/treasury/remittance_form.html:7
+msgid "Remittance #"
+msgstr ""
+
+#: templates/treasury/remittance_form.html:9
+#: templates/treasury/specialtransactionproxy_form.html:7
+msgid "Remittances list"
+msgstr ""
+
+#: templates/treasury/remittance_form.html:12
+msgid "Count"
+msgstr ""
+
+#: templates/treasury/remittance_form.html:29
+msgid "Linked transactions"
+msgstr ""
+
+#: templates/treasury/remittance_form.html:34
+msgid "There is no transaction linked with this remittance."
+msgstr ""
+
+#: templates/treasury/remittance_list.html:19
+msgid "Opened remittances"
+msgstr ""
+
+#: templates/treasury/remittance_list.html:24
+msgid "There is no opened remittance."
+msgstr ""
+
+#: templates/treasury/remittance_list.html:28
+msgid "New remittance"
+msgstr ""
+
+#: templates/treasury/remittance_list.html:32
+msgid "Transfers without remittances"
+msgstr ""
+
+#: templates/treasury/remittance_list.html:37
+msgid "There is no transaction without any linked remittance."
+msgstr ""
+
+#: templates/treasury/remittance_list.html:43
+msgid "Transfers with opened remittances"
+msgstr ""
+
+#: templates/treasury/remittance_list.html:48
+msgid "There is no transaction with an opened linked remittance."
+msgstr ""
+
+#: templates/treasury/remittance_list.html:54
+msgid "Closed remittances"
+msgstr ""
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index 5e6e94704655dc49e1fb27ae2552aab43dc21301..67af6beeac1bcf6e5c4cd6f83f6d46db953320ab 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-03-16 11:53+0100\n"
+"POT-Creation-Date: 2020-03-24 15:49+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,9 @@ msgid "activity"
 msgstr "activité"
 
 #: apps/activity/models.py:19 apps/activity/models.py:44
-#: apps/member/models.py:61 apps/member/models.py:112
+#: apps/member/models.py:63 apps/member/models.py:114
 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
-#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:202
+#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:198
 #: templates/member/profile_detail.html:15
 msgid "name"
 msgstr "nom"
@@ -42,11 +42,12 @@ msgid "activity types"
 msgstr "types d'activité"
 
 #: apps/activity/models.py:48 apps/note/models/transactions.py:69
+#: apps/permission/models.py:90
 msgid "description"
 msgstr "description"
 
 #: apps/activity/models.py:54 apps/note/models/notes.py:164
-#: apps/note/models/transactions.py:62 apps/note/models/transactions.py:115
+#: apps/note/models/transactions.py:62
 msgid "type"
 msgstr "type"
 
@@ -138,61 +139,61 @@ msgstr "Les logs ne peuvent pas être détruits."
 msgid "member"
 msgstr "adhérent"
 
-#: apps/member/models.py:23
+#: apps/member/models.py:25
 msgid "phone number"
 msgstr "numéro de téléphone"
 
-#: apps/member/models.py:29 templates/member/profile_detail.html:28
+#: apps/member/models.py:31 templates/member/profile_detail.html:28
 msgid "section"
 msgstr "section"
 
-#: apps/member/models.py:30
+#: apps/member/models.py:32
 msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
 msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
 
-#: apps/member/models.py:36 templates/member/profile_detail.html:31
+#: apps/member/models.py:38 templates/member/profile_detail.html:31
 msgid "address"
 msgstr "adresse"
 
-#: apps/member/models.py:42
+#: apps/member/models.py:44
 msgid "paid"
 msgstr "payé"
 
-#: apps/member/models.py:47 apps/member/models.py:48
+#: apps/member/models.py:49 apps/member/models.py:50
 msgid "user profile"
 msgstr "profil utilisateur"
 
-#: apps/member/models.py:66
+#: apps/member/models.py:68
 msgid "email"
 msgstr "courriel"
 
-#: apps/member/models.py:71
+#: apps/member/models.py:73
 msgid "membership fee"
 msgstr "cotisation pour adhérer"
 
-#: apps/member/models.py:75
+#: apps/member/models.py:77
 msgid "membership duration"
 msgstr "durée de l'adhésion"
 
-#: apps/member/models.py:76
+#: apps/member/models.py:78
 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:81
+#: apps/member/models.py:83
 msgid "membership start"
 msgstr "début de l'adhésion"
 
-#: apps/member/models.py:82
+#: apps/member/models.py:84
 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:87
+#: apps/member/models.py:89
 msgid "membership end"
 msgstr "fin de l'adhésion"
 
-#: apps/member/models.py:88
+#: apps/member/models.py:90
 msgid ""
 "How long the membership can last after January 1st of the next year after "
 "members can renew their membership."
@@ -200,56 +201,56 @@ 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:94 apps/note/models/notes.py:139
+#: apps/member/models.py:96 apps/note/models/notes.py:139
 msgid "club"
 msgstr "club"
 
-#: apps/member/models.py:95
+#: apps/member/models.py:97
 msgid "clubs"
 msgstr "clubs"
 
-#: apps/member/models.py:118
+#: apps/member/models.py:120 apps/permission/models.py:275
 msgid "role"
 msgstr "rôle"
 
-#: apps/member/models.py:119
+#: apps/member/models.py:121
 msgid "roles"
 msgstr "rôles"
 
-#: apps/member/models.py:143
+#: apps/member/models.py:145
 msgid "membership starts on"
 msgstr "l'adhésion commence le"
 
-#: apps/member/models.py:146
+#: apps/member/models.py:148
 msgid "membership ends on"
 msgstr "l'adhésion finie le"
 
-#: apps/member/models.py:150
+#: apps/member/models.py:152
 msgid "fee"
 msgstr "cotisation"
 
-#: apps/member/models.py:154
+#: apps/member/models.py:162
 msgid "membership"
 msgstr "adhésion"
 
-#: apps/member/models.py:155
+#: apps/member/models.py:163
 msgid "memberships"
 msgstr "adhésions"
 
-#: apps/member/views.py:69 templates/member/profile_detail.html:46
+#: apps/member/views.py:80 templates/member/profile_detail.html:46
 msgid "Update Profile"
 msgstr "Modifier le profil"
 
-#: apps/member/views.py:82
+#: apps/member/views.py:93
 msgid "An alias with a similar name already exists."
 msgstr "Un alias avec un nom similaire existe déjà."
 
-#: apps/member/views.py:132
+#: apps/member/views.py:146
 #, python-format
 msgid "Account #%(id)s: %(username)s"
 msgstr "Compte n°%(id)s : %(username)s"
 
-#: apps/member/views.py:202
+#: apps/member/views.py:216
 msgid "Alias successfully deleted"
 msgstr "L'alias a bien été supprimé"
 
@@ -415,84 +416,233 @@ msgstr "modèles de transaction"
 msgid "quantity"
 msgstr "quantité"
 
-#: apps/note/models/transactions.py:117 templates/note/transaction_form.html:15
-msgid "Gift"
-msgstr "Don"
+#: apps/note/models/transactions.py:115
+msgid "reason"
+msgstr "raison"
+
+#: apps/note/models/transactions.py:119
+msgid "valid"
+msgstr "valide"
 
-#: apps/note/models/transactions.py:118 templates/base.html:90
+#: apps/note/models/transactions.py:124
+msgid "transaction"
+msgstr "transaction"
+
+#: apps/note/models/transactions.py:125
+msgid "transactions"
+msgstr "transactions"
+
+#: apps/note/models/transactions.py:168 templates/base.html:98
 #: templates/note/transaction_form.html:19
-#: templates/note/transaction_form.html:126
+#: templates/note/transaction_form.html:145
 msgid "Transfer"
 msgstr "Virement"
 
-#: apps/note/models/transactions.py:119
+#: apps/note/models/transactions.py:188
 msgid "Template"
 msgstr "Bouton"
 
-#: apps/note/models/transactions.py:120 templates/note/transaction_form.html:23
+#: apps/note/models/transactions.py:203
+msgid "first_name"
+msgstr "prénom"
+
+#: apps/note/models/transactions.py:208
+msgid "bank"
+msgstr "banque"
+
+#: apps/note/models/transactions.py:214 templates/note/transaction_form.html:24
 msgid "Credit"
 msgstr "Crédit"
 
-#: apps/note/models/transactions.py:121 templates/note/transaction_form.html:27
+#: apps/note/models/transactions.py:214 templates/note/transaction_form.html:28
 msgid "Debit"
-msgstr "Retrait"
+msgstr "Débit"
 
-#: apps/note/models/transactions.py:122 apps/note/models/transactions.py:230
+#: apps/note/models/transactions.py:230 apps/note/models/transactions.py:235
 msgid "membership transaction"
 msgstr "transaction d'adhésion"
 
-#: apps/note/models/transactions.py:129
-msgid "reason"
-msgstr "raison"
+#: apps/note/models/transactions.py:231
+msgid "membership transactions"
+msgstr "transactions d'adhésion"
 
-#: apps/note/models/transactions.py:133
-msgid "valid"
-msgstr "valide"
+#: apps/note/views.py:39
+msgid "Transfer money"
+msgstr "Transférer de l'argent"
 
-#: apps/note/models/transactions.py:138
-msgid "transaction"
-msgstr "transaction"
+#: apps/note/views.py:145 templates/base.html:79
+msgid "Consumptions"
+msgstr "Consommations"
 
-#: apps/note/models/transactions.py:139
-msgid "transactions"
-msgstr "transactions"
+#: apps/permission/models.py:69 apps/permission/models.py:262
+#, python-brace-format
+msgid "Can {type} {model}.{field} in {query}"
+msgstr ""
 
-#: apps/note/models/transactions.py:207
-msgid "first_name"
+#: apps/permission/models.py:71 apps/permission/models.py:264
+#, python-brace-format
+msgid "Can {type} {model} in {query}"
+msgstr ""
+
+#: apps/permission/models.py:84
+msgid "rank"
+msgstr "Rang"
+
+#: apps/permission/models.py:147
+msgid "Specifying field applies only to view and change permission types."
+msgstr ""
+
+#: apps/treasury/apps.py:11 templates/base.html:102
+msgid "Treasury"
+msgstr "Trésorerie"
+
+#: apps/treasury/forms.py:56 apps/treasury/forms.py:95
+#: templates/django_filters/rest_framework/form.html:5
+#: templates/member/club_form.html:10 templates/treasury/invoice_form.html:47
+msgid "Submit"
+msgstr "Envoyer"
+
+#: apps/treasury/forms.py:58
+msgid "Close"
+msgstr "Fermer"
+
+#: apps/treasury/forms.py:65
+msgid "Remittance is already closed."
+msgstr "La remise est déjà fermée."
+
+#: apps/treasury/forms.py:70
+msgid "You can't change the type of the remittance."
+msgstr "Vous ne pouvez pas changer le type de la remise."
+
+#: apps/treasury/forms.py:84
+msgid "Last name"
+msgstr "Nom de famille"
+
+#: apps/treasury/forms.py:86 templates/note/transaction_form.html:92
+msgid "First name"
 msgstr "Prénom"
 
-#: apps/note/models/transactions.py:212
-msgid "bank"
+#: apps/treasury/forms.py:88 templates/note/transaction_form.html:98
+msgid "Bank"
 msgstr "Banque"
 
-#: apps/note/models/transactions.py:231
-msgid "membership transactions"
-msgstr "transactions d'adhésion"
+#: apps/treasury/forms.py:90 apps/treasury/tables.py:40
+#: templates/note/transaction_form.html:128
+#: templates/treasury/remittance_form.html:18
+msgid "Amount"
+msgstr "Montant"
 
-#: apps/note/views.py:31
-msgid "Transfer money"
-msgstr "Transferts d'argent"
+#: apps/treasury/models.py:18
+msgid "Invoice identifier"
+msgstr "Numéro de facture"
 
-#: apps/note/views.py:132 templates/base.html:78
-msgid "Consumptions"
-msgstr "Consommations"
+#: apps/treasury/models.py:32
+msgid "BDE"
+msgstr "BDE"
+
+#: apps/treasury/models.py:37
+msgid "Object"
+msgstr "Objet"
+
+#: apps/treasury/models.py:41
+msgid "Description"
+msgstr "Description"
+
+#: apps/treasury/models.py:46 templates/note/transaction_form.html:86
+msgid "Name"
+msgstr "Nom"
+
+#: apps/treasury/models.py:50
+msgid "Address"
+msgstr "Adresse"
+
+#: apps/treasury/models.py:55
+msgid "Place"
+msgstr "Lieu"
+
+#: apps/treasury/models.py:59
+msgid "Acquitted"
+msgstr "Acquittée"
+
+#: apps/treasury/models.py:75
+msgid "Designation"
+msgstr "Désignation"
+
+#: apps/treasury/models.py:79
+msgid "Quantity"
+msgstr "Quantité"
+
+#: apps/treasury/models.py:83
+msgid "Unit price"
+msgstr "Prix unitaire"
+
+#: apps/treasury/models.py:120
+msgid "Date"
+msgstr "Date"
+
+#: apps/treasury/models.py:126
+msgid "Type"
+msgstr "Type"
+
+#: apps/treasury/models.py:131
+msgid "Comment"
+msgstr "Commentaire"
+
+#: apps/treasury/models.py:136
+msgid "Closed"
+msgstr "Fermée"
+
+#: apps/treasury/models.py:159
+msgid "Remittance #{:d}: {}"
+msgstr "Remise n°{:d} : {}"
+
+#: apps/treasury/models.py:178 apps/treasury/tables.py:64
+#: apps/treasury/tables.py:72 templates/treasury/invoice_list.html:13
+#: templates/treasury/remittance_list.html:13
+msgid "Remittance"
+msgstr "Remise"
+
+#: apps/treasury/tables.py:16
+msgid "Invoice #{:d}"
+msgstr "Facture n°{:d}"
 
-#: note_kfet/settings/__init__.py:61
+#: apps/treasury/tables.py:19 templates/treasury/invoice_list.html:10
+#: templates/treasury/remittance_list.html:10
+msgid "Invoice"
+msgstr "Facture"
+
+#: apps/treasury/tables.py:38
+msgid "Transaction count"
+msgstr "Nombre de transactions"
+
+#: apps/treasury/tables.py:43 apps/treasury/tables.py:45
+msgid "View"
+msgstr "Voir"
+
+#: apps/treasury/tables.py:66
+msgid "Add"
+msgstr "Ajouter"
+
+#: apps/treasury/tables.py:74
+msgid "Remove"
+msgstr "supprimer"
+
+#: note_kfet/settings/__init__.py:63
 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
+#: note_kfet/settings/base.py:153
 msgid "German"
 msgstr ""
 
-#: note_kfet/settings/base.py:157
+#: note_kfet/settings/base.py:154
 msgid "English"
 msgstr ""
 
-#: note_kfet/settings/base.py:158
+#: note_kfet/settings/base.py:155
 msgid "French"
 msgstr ""
 
@@ -500,15 +650,15 @@ msgstr ""
 msgid "The ENS Paris-Saclay BDE note."
 msgstr "La note du BDE de l'ENS Paris-Saclay."
 
-#: templates/base.html:81
+#: templates/base.html:84
 msgid "Clubs"
 msgstr "Clubs"
 
-#: templates/base.html:84
+#: templates/base.html:89
 msgid "Activities"
 msgstr "Activités"
 
-#: templates/base.html:87
+#: templates/base.html:94
 msgid "Buttons"
 msgstr "Boutons"
 
@@ -569,11 +719,6 @@ msgstr ""
 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"
@@ -620,15 +765,15 @@ msgstr "Ajouter un alias"
 
 #: templates/member/profile_detail.html:15
 msgid "first name"
-msgstr ""
+msgstr "prénom"
 
 #: templates/member/profile_detail.html:18
 msgid "username"
-msgstr ""
+msgstr "pseudo"
 
 #: templates/member/profile_detail.html:21
 msgid "password"
-msgstr ""
+msgstr "mot de passe"
 
 #: templates/member/profile_detail.html:24
 msgid "Change password"
@@ -655,13 +800,13 @@ msgstr "Sauvegarder les changements"
 msgid "Sign up"
 msgstr "Inscription"
 
-#: templates/note/conso_form.html:28 templates/note/transaction_form.html:38
+#: templates/note/conso_form.html:28 templates/note/transaction_form.html:40
 msgid "Select emitters"
 msgstr "Sélection des émetteurs"
 
 #: templates/note/conso_form.html:45
 msgid "Select consumptions"
-msgstr "Consommations"
+msgstr "Sélection des consommations"
 
 #: templates/note/conso_form.html:51
 msgid "Consume!"
@@ -677,55 +822,43 @@ msgstr "Éditer"
 
 #: templates/note/conso_form.html:126
 msgid "Single consumptions"
-msgstr "Consos simples"
+msgstr "Consommations simples"
 
 #: templates/note/conso_form.html:130
 msgid "Double consumptions"
-msgstr "Consos doubles"
+msgstr "Consommations doubles"
 
-#: templates/note/conso_form.html:141
+#: templates/note/conso_form.html:141 templates/note/transaction_form.html:152
 msgid "Recent transactions history"
 msgstr "Historique des transactions récentes"
 
-#: templates/note/transaction_form.html:55
+#: templates/note/transaction_form.html:15
+msgid "Gift"
+msgstr "Don"
+
+#: templates/note/transaction_form.html:68
 msgid "External payment"
-msgstr "Paiement extérieur"
+msgstr "Paiement externe"
 
-#: templates/note/transaction_form.html:63
+#: templates/note/transaction_form.html:76
 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
+#: templates/note/transaction_form.html:111
+#: templates/note/transaction_form.html:169
+#: templates/note/transaction_form.html:176
 msgid "Select receivers"
 msgstr "Sélection des destinataires"
 
-#: templates/note/transaction_form.html:114
-msgid "Amount"
-msgstr "Montant"
-
-#: templates/note/transaction_form.html:119
+#: templates/note/transaction_form.html:138
 msgid "Reason"
 msgstr "Raison"
 
-#: templates/note/transaction_form.html:193
+#: templates/note/transaction_form.html:183
 msgid "Credit note"
-msgstr "Note à créditer"
+msgstr "Note à recharger"
 
-#: templates/note/transaction_form.html:200
+#: templates/note/transaction_form.html:190
 msgid "Debit note"
 msgstr "Note à débiter"
 
@@ -810,3 +943,72 @@ msgstr ""
 #: templates/registration/password_reset_form.html:11
 msgid "Reset my password"
 msgstr ""
+
+#: templates/treasury/invoice_form.html:6
+msgid "Invoices list"
+msgstr "Liste des factures"
+
+#: templates/treasury/invoice_form.html:42
+msgid "Add product"
+msgstr "Ajouter produit"
+
+#: templates/treasury/invoice_form.html:43
+msgid "Remove product"
+msgstr "Retirer produit"
+
+#: templates/treasury/invoice_list.html:21
+msgid "New invoice"
+msgstr "Nouvelle facture"
+
+#: templates/treasury/remittance_form.html:7
+msgid "Remittance #"
+msgstr "Remise n°"
+
+#: templates/treasury/remittance_form.html:9
+#: templates/treasury/specialtransactionproxy_form.html:7
+msgid "Remittances list"
+msgstr "Liste des remises"
+
+#: templates/treasury/remittance_form.html:12
+msgid "Count"
+msgstr "Nombre"
+
+#: templates/treasury/remittance_form.html:29
+msgid "Linked transactions"
+msgstr "Transactions liées"
+
+#: templates/treasury/remittance_form.html:34
+msgid "There is no transaction linked with this remittance."
+msgstr "Il n'y a pas de transaction liée à cette remise."
+
+#: templates/treasury/remittance_list.html:19
+msgid "Opened remittances"
+msgstr "Remises ouvertes"
+
+#: templates/treasury/remittance_list.html:24
+msgid "There is no opened remittance."
+msgstr "Il n'y a pas de remise ouverte."
+
+#: templates/treasury/remittance_list.html:28
+msgid "New remittance"
+msgstr "Nouvelle remise"
+
+#: templates/treasury/remittance_list.html:32
+msgid "Transfers without remittances"
+msgstr "Transactions sans remise associée"
+
+#: templates/treasury/remittance_list.html:37
+msgid "There is no transaction without any linked remittance."
+msgstr "Il n'y a pas de transactions sans remise associée."
+
+#: templates/treasury/remittance_list.html:43
+msgid "Transfers with opened remittances"
+msgstr "Transactions associées à une remise ouverte"
+
+#: templates/treasury/remittance_list.html:48
+msgid "There is no transaction with an opened linked remittance."
+msgstr "Il n'y a pas de transaction associée à une remise ouverte."
+
+#: templates/treasury/remittance_list.html:54
+msgid "Closed remittances"
+msgstr "Remises fermées"
diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py
index 216199deab73fe40e70ca3b065d494e18434f67d..d49b25424258dc73b5e2ae336b88727c0f50f3b9 100644
--- a/note_kfet/settings/base.py
+++ b/note_kfet/settings/base.py
@@ -59,6 +59,7 @@ INSTALLED_APPS = [
     'activity',
     'member',
     'note',
+    'treasury',
     'permission',
     'api',
     'logs',
diff --git a/note_kfet/urls.py b/note_kfet/urls.py
index 9170c62ed6c21eab048e8db366f240f194ff880f..40a9a614dee79fa0282ce43a2e2e30a3cd58027d 100644
--- a/note_kfet/urls.py
+++ b/note_kfet/urls.py
@@ -15,6 +15,7 @@ urlpatterns = [
 
     # Include project routers
     path('note/', include('note.urls')),
+    path('treasury/', include('treasury.urls')),
 
     # Include Django Contrib and Core routers
     path('i18n/', include('django.conf.urls.i18n')),
diff --git a/requirements/production.txt b/requirements/production.txt
index f0b5222826649e71d629934444582238919500d9..fe939cce41bb95dda07273e102a31bbca595af80 100644
--- a/requirements/production.txt
+++ b/requirements/production.txt
@@ -1 +1 @@
-psycopg2==2.8.4
+psycopg2-binary==2.8.4
diff --git a/static/img/Finalist.png b/static/img/Finalist.png
new file mode 100644
index 0000000000000000000000000000000000000000..1a3c41f3462ec962dd3fa9105db657f53f5052a3
Binary files /dev/null and b/static/img/Finalist.png differ
diff --git a/static/img/Kataclist.png b/static/img/Kataclist.png
new file mode 100644
index 0000000000000000000000000000000000000000..97fc411506c37a288eaef8c627ee26f06c8c2706
Binary files /dev/null and b/static/img/Kataclist.png differ
diff --git a/static/img/Listorique.png b/static/img/Listorique.png
new file mode 100644
index 0000000000000000000000000000000000000000..c515832478403494c63c030eb199f581dec56da4
Binary files /dev/null and b/static/img/Listorique.png differ
diff --git a/static/img/Monopolist.png b/static/img/Monopolist.png
new file mode 100644
index 0000000000000000000000000000000000000000..2685b21e23e44a7782d3f7c4a652a9a84f1dc86c
Binary files /dev/null and b/static/img/Monopolist.png differ
diff --git a/static/img/Satellist.png b/static/img/Satellist.png
new file mode 100644
index 0000000000000000000000000000000000000000..d2377f670bf5b18e1e2c428394c9958d2e9379f5
Binary files /dev/null and b/static/img/Satellist.png differ
diff --git a/static/js/base.js b/static/js/base.js
index f70858500cf97aa61d45fe019429999df51c9c35..b22df07710b59ec1fe25d881b3cb3def98984ee8 100644
--- a/static/js/base.js
+++ b/static/js/base.js
@@ -62,11 +62,12 @@ function li(id, text) {
  */
 function displayNote(note, alias, user_note_field=null, profile_pic_field=null) {
     if (!note.display_image) {
-        note.display_image = 'https://nk20.ynerant.fr/media/pic/default.png';
+        note.display_image = '/media/pic/default.png';
         $.getJSON("/api/note/note/" + note.id + "/?format=json", function(new_note) {
             note.display_image = new_note.display_image.replace("http:", "https:");
             note.name = new_note.name;
             note.balance = new_note.balance;
+            note.user = new_note.user;
 
             displayNote(note, alias, user_note_field, profile_pic_field);
         });
@@ -151,10 +152,13 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes
 
     let old_pattern = null;
 
-    // When the user type "Enter", the first alias is clicked
+    // When the user type "Enter", the first alias is clicked, and the informations are displayed
     field.keypress(function(event) {
-        if (event.originalEvent.charCode === 13)
-            $("#" + alias_matched_id + " li").first().trigger("click");
+        if (event.originalEvent.charCode === 13) {
+            let li_obj = $("#" + alias_matched_id + " li").first();
+            displayNote(notes[0], li_obj.text(), user_note_field, profile_pic_field);
+            li_obj.trigger("click");
+        }
     });
 
     // When the user type something, the matched aliases are refreshed
diff --git a/static/js/dynamic-formset.js b/static/js/dynamic-formset.js
index 87edfaaeb38e7ed4cd2f06fe0e9f48939b706b86..c6ff3328d5aa0923d732f8599fe929c6ccb2895c 100644
--- a/static/js/dynamic-formset.js
+++ b/static/js/dynamic-formset.js
@@ -1,5 +1,5 @@
 /**
- * jQuery Formset 1.3-pre
+ * jQuery Formset 1.5-pre
  * @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com)
  * @requires jQuery 1.2.6 or later
  *
@@ -55,19 +55,26 @@
             insertDeleteLink = function(row) {
                 var delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.'),
                     addCssSelector = $.trim(options.addCssClass).replace(/\s+/g, '.');
-                if (row.is('TR')) {
+
+                var delButtonHTML = '<a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a>';
+                if (options.deleteContainerClass) {
+                    // If we have a specific container for the remove button,
+                    // place it as the last child of that container:
+                    row.find('[class*="' + options.deleteContainerClass + '"]').append(delButtonHTML);
+                } else if (row.is('TR')) {
                     // If the forms are laid out in table rows, insert
                     // the remove button into the last table cell:
-                    row.children(':last').append('<a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + '</a>');
+                    row.children('td:last').append(delButtonHTML);
                 } else if (row.is('UL') || row.is('OL')) {
                     // If they're laid out as an ordered/unordered list,
                     // insert an <li> after the last list item:
-                    row.append('<li><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a></li>');
+                    row.append('<li>' + delButtonHTML + '</li>');
                 } else {
                     // Otherwise, just insert the remove button as the
                     // last child element of the form's container:
-                    row.append('<a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a>');
+                    row.append(delButtonHTML);
                 }
+
                 // Check if we're under the minimum number of forms - not to display delete link at rendering
                 if (!showDeleteLinks()){
                     row.find('a.' + delCssSelector).hide();
@@ -156,6 +163,7 @@
             } else {
                 // Otherwise, use the last form in the formset; this works much better if you've got
                 // extra (>= 1) forms (thnaks to justhamade for pointing this out):
+                if (options.hideLastAddForm) $('.' + options.formCssClass + ':last').hide();
                 template = $('.' + options.formCssClass + ':last').clone(true).removeAttr('id');
                 template.find('input:hidden[id $= "-DELETE"]').remove();
                 // Clear all cloned fields, except those the user wants to keep (thanks to brunogola for the suggestion):
@@ -173,21 +181,28 @@
             // FIXME: Perhaps using $.data would be a better idea?
             options.formTemplate = template;
 
-            if ($$.is('TR')) {
+            var addButtonHTML = '<a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a>';
+            if (options.addContainerClass) {
+                // If we have a specific container for the "add" button,
+                // place it as the last child of that container:
+                var addContainer = $('[class*="' + options.addContainerClass + '"');
+                addContainer.append(addButtonHTML);
+                addButton = addContainer.find('[class="' + options.addCssClass + '"]');
+            } else if ($$.is('TR')) {
                 // If forms are laid out as table rows, insert the
                 // "add" button in a new table row:
                 var numCols = $$.eq(0).children().length,   // This is a bit of an assumption :|
-                    buttonRow = $('<tr><td colspan="' + numCols + '"><a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a></tr>')
-                                .addClass(options.formCssClass + '-add');
+                    buttonRow = $('<tr><td colspan="' + numCols + '">' + addButtonHTML + '</tr>').addClass(options.formCssClass + '-add');
                 $$.parent().append(buttonRow);
-                if (hideAddButton) buttonRow.hide();
                 addButton = buttonRow.find('a');
             } else {
                 // Otherwise, insert it immediately after the last form:
-                $$.filter(':last').after('<a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a>');
+                $$.filter(':last').after(addButtonHTML);
                 addButton = $$.filter(':last').next();
-                if (hideAddButton) addButton.hide();
             }
+
+            if (hideAddButton) addButton.hide();
+
             addButton.click(function() {
                 var formCount = parseInt(totalForms.val()),
                     row = options.formTemplate.clone(true).removeClass('formset-custom-template'),
@@ -220,12 +235,15 @@
         formTemplate: null,              // The jQuery selection cloned to generate new form instances
         addText: 'add another',          // Text for the add link
         deleteText: 'remove',            // Text for the delete link
-        addCssClass: '',          // CSS class applied to the add link
-        deleteCssClass: '',    // CSS class applied to the delete link
+        addContainerClass: null,         // Container CSS class for the add link
+        deleteContainerClass: null,      // Container CSS class for the delete link
+        addCssClass: 'add-row',          // CSS class applied to the add link
+        deleteCssClass: 'delete-row',    // CSS class applied to the delete link
         formCssClass: 'dynamic-form',    // CSS class applied to each form in a formset
         extraClasses: [],                // Additional CSS classes, which will be applied to each form in turn
         keepFieldValues: '',             // jQuery selector for fields whose values should be kept when the form is cloned
         added: null,                     // Function called each time a new form is added
-        removed: null                    // Function called each time a form is deleted
+        removed: null,                   // Function called each time a form is deleted
+        hideLastAddForm: false           // When set to true, hide last empty add form (becomes visible when clicking on add button)
     };
 })(jQuery);
diff --git a/static/js/transfer.js b/static/js/transfer.js
index c615f932bb7f41091fa8d79dc5a720b0cf1d3858..a417191082ccbffbd1a1db8036e4ad040a7af5ba 100644
--- a/static/js/transfer.js
+++ b/static/js/transfer.js
@@ -39,10 +39,21 @@ $(document).ready(function() {
 
                 last.quantity = 1;
 
-                $.getJSON("/api/user/" + last.note.user + "/", function(user) {
-                    $("#last_name").val(user.last_name);
-                    $("#first_name").val(user.first_name);
-                });
+                if (!last.note.user) {
+                    $.getJSON("/api/note/note/" + last.note.id + "/?format=json", function(note) {
+                        last.note.user = note.user;
+                        $.getJSON("/api/user/" + last.note.user + "/", function(user) {
+                            $("#last_name").val(user.last_name);
+                            $("#first_name").val(user.first_name);
+                        });
+                    });
+                }
+                else {
+                    $.getJSON("/api/user/" + last.note.user + "/", function(user) {
+                        $("#last_name").val(user.last_name);
+                        $("#first_name").val(user.first_name);
+                    });
+                }
             }
 
             return true;
diff --git a/templates/base.html b/templates/base.html
index 2f07a6cc209cb330efc13b796814fd559490ce62..62fc9c58c8732a692cfa201cc2145b0eece7940c 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -92,6 +92,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
                         <a class="nav-link" href="#"><i class="fa fa-calendar"></i> {% trans 'Activities' %}</a>
                     </li>
                 {% endif %}
+                {% if "treasury.invoice"|not_empty_model_change_list %}
+                    <li class="nav-item active">
+                        <a class="nav-link" href="{% url 'treasury:invoice_list' %}"><i class="fa fa-money"></i>{% trans 'Treasury' %} </a>
+                    </li>
+                {% endif %}
             </ul>
             <ul class="navbar-nav ml-auto">
                 {% if user.is_authenticated %}
diff --git a/templates/treasury/invoice_form.html b/templates/treasury/invoice_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..0edcbdcdcbd8e55d66cc66c2a58e825664d0fd77
--- /dev/null
+++ b/templates/treasury/invoice_form.html
@@ -0,0 +1,107 @@
+{% extends "base.html" %}
+{% load static %}
+{% load i18n %}
+{% load crispy_forms_tags pretty_money %}
+{% block content %}
+    <p><a class="btn btn-default" href="{% url 'treasury:invoice_list' %}">{% trans "Invoices list" %}</a></p>
+    <form method="post" action="">
+        {% csrf_token %}
+        {# Render the invoice form #}
+        {% crispy form %}
+        {# The next part concerns the product formset #}
+        {# Generate some hidden fields that manage the number of products, and make easier the parsing #}
+        {{ formset.management_form }}
+        <table class="table table-condensed table-striped">
+            {# Fill initial data #}
+            {% for form in formset %}
+                {% if forloop.first %}
+                    <thead>
+                    <tr>
+                        <th>{{ form.designation.label }}<span class="asteriskField">*</span></th>
+                        <th>{{ form.quantity.label }}<span class="asteriskField">*</span></th>
+                        <th>{{ form.amount.label }}<span class="asteriskField">*</span></th>
+                    </tr>
+                    </thead>
+                    <tbody id="form_body">
+                {% endif %}
+                <tr class="row-formset">
+                    <td>{{ form.designation }}</td>
+                    <td>{{ form.quantity }} </td>
+                    <td>
+                        {# Use custom input for amount, with the € symbol #}
+                        <div class="input-group">
+                            <input type="number" name="product_set-{{ forloop.counter0 }}-amount" step="0.01"
+                                   id="id_product_set-{{ forloop.counter0 }}-amount"
+                                   value="{{ form.instance.amount|cents_to_euros }}">
+                            <div class="input-group-append">
+                                <span class="input-group-text">€</span>
+                            </div>
+                        </div>
+                    </td>
+                    {# These fields are hidden but handled by the formset to link the id and the invoice id #}
+                    {{ form.invoice }}
+                    {{ form.id }}
+                </tr>
+            {% endfor %}
+            </tbody>
+        </table>
+
+        {# Display buttons to add and remove products #}
+        <div class="btn-group btn-block" role="group">
+            <button type="button" id="add_more" class="btn btn-primary">{% trans "Add product" %}</button>
+            <button type="button" id="remove_one" class="btn btn-danger">{% trans "Remove product" %}</button>
+        </div>
+
+        <div class="btn-block">
+            <button type="submit" class="btn btn-block btn-primary">{% trans "Submit" %}</button>
+        </div>
+    </form>
+
+    <div id="empty_form" style="display: none;">
+        {# Hidden div that store an empty product form, to be copied into new forms #}
+        <table class='no_error'>
+            <tbody id="for_real">
+            <tr class="row-formset">
+                <td>{{ formset.empty_form.designation }}</td>
+                <td>{{ formset.empty_form.quantity }} </td>
+                <td>
+                    <div class="input-group">
+                        <input type="number" name="product_set-__prefix__-amount" step="0.01"
+                               id="id_product_set-__prefix__-amount">
+                        <div class="input-group-append">
+                            <span class="input-group-text">€</span>
+                        </div>
+                    </div>
+                </td>
+                {{ formset.empty_form.invoice }}
+                {{ formset.empty_form.id }}
+            </tr>
+            </tbody>
+        </table>
+    </div>
+{% endblock %}
+
+{% block extrajavascript %}
+    <script>
+        {# Script that handles add and remove lines #}
+        IDS = {};
+
+        $("#id_product_set-TOTAL_FORMS").val($(".row-formset").length - 1);
+
+        $('#add_more').click(function () {
+            var form_idx = $('#id_product_set-TOTAL_FORMS').val();
+            $('#form_body').append($('#for_real').html().replace(/__prefix__/g, form_idx));
+            $('#id_product_set-TOTAL_FORMS').val(parseInt(form_idx) + 1);
+            $('#id_product_set-' + parseInt(form_idx) + '-id').val(IDS[parseInt(form_idx)]);
+        });
+
+        $('#remove_one').click(function () {
+            let form_idx = $('#id_product_set-TOTAL_FORMS').val();
+            if (form_idx > 0) {
+                IDS[parseInt(form_idx) - 1] = $('#id_product_set-' + (parseInt(form_idx) - 1) + '-id').val();
+                $('#form_body tr:last-child').remove();
+                $('#id_product_set-TOTAL_FORMS').val(parseInt(form_idx) - 1);
+            }
+        });
+    </script>
+{% endblock %}
diff --git a/templates/treasury/invoice_list.html b/templates/treasury/invoice_list.html
new file mode 100644
index 0000000000000000000000000000000000000000..f14d278dd0375528512d84fb5d0e00c8dfbff214
--- /dev/null
+++ b/templates/treasury/invoice_list.html
@@ -0,0 +1,23 @@
+{% extends "base.html" %}
+{% load render_table from django_tables2 %}
+{% load i18n %}
+{% block content %}
+
+    <div class="row">
+        <div class="col-xl-12">
+            <div class="btn-group btn-group-toggle" style="width: 100%; padding: 0 0 2em 0" data-toggle="buttons">
+                <a href="#" class="btn btn-sm btn-outline-primary active">
+                    {% trans "Invoice" %}s
+                </a>
+                <a href="{% url "treasury:remittance_list" %}" class="btn btn-sm btn-outline-primary">
+                    {% trans "Remittance" %}s
+                </a>
+            </div>
+        </div>
+    </div>
+
+{% render_table table %}
+
+<a class="btn btn-primary" href="{% url 'treasury:invoice_create' %}">{% trans "New invoice" %}</a>
+
+{% endblock %}
diff --git a/templates/treasury/invoice_sample.tex b/templates/treasury/invoice_sample.tex
new file mode 100644
index 0000000000000000000000000000000000000000..3c76403e97d466f23771172a78f755ddf4c3658a
--- /dev/null
+++ b/templates/treasury/invoice_sample.tex
@@ -0,0 +1,186 @@
+\nonstopmode
+\documentclass[11pt]{article}
+
+\usepackage[french]{babel}
+\usepackage[T1]{fontenc}
+\usepackage[utf8]{inputenc}
+\usepackage[a4paper]{geometry}
+\usepackage{units}
+\usepackage{bera}
+\usepackage{graphicx}
+\usepackage{fancyhdr}
+\usepackage{fp}
+\usepackage{transparent}
+\usepackage{eso-pic}
+
+\def\TVA{0}    % Taux de la TVA
+
+\def\TotalHT{0}
+\def\TotalTVA{0}
+
+\newcommand{\AjouterProduit}[4]{%    Arguments : Désignation, quantité, prix unitaire HT, prix total HT
+    \FPround{\prix}{#3}{2}
+    \FPround{\montant}{#4}{2}
+    \FPadd{\TotalHT}{\TotalHT}{\montant}
+
+    \eaddto\ListeProduits{#1    &    \prix    &    #2    &    \montant    \cr}
+}
+
+\newcommand{\AfficheResultat}{%
+    \ListeProduits
+
+    \FPeval{\TotalTVA}{\TotalHT * \TVA / 100}
+    \FPadd{\TotalTTC}{\TotalHT}{\TotalTVA}
+    \FPround{\TotalHT}{\TotalHT}{2}
+    \FPround{\TotalTVA}{\TotalTVA}{2}
+    \FPround{\TotalTTC}{\TotalTTC}{2}
+    \global\let\TotalHT\TotalHT
+    \global\let\TotalTVA\TotalTVA
+    \global\let\TotalTTC\TotalTTC
+
+    \cr \hline
+    Total HT            & & &    \TotalHT    \cr
+    TVA \TVA~\%         & & &    \TotalTVA    \cr
+    \hline \hline
+    \textbf{Total TTC}    & & &    \TotalTTC
+}
+
+\newcommand*\eaddto[2]{% version développée de \addto
+   \edef\tmp{#2}%
+   \expandafter\addto
+   \expandafter#1%
+   \expandafter{\tmp}%
+}
+
+\newcommand	{\ListeProduits}{}
+
+% Logo du BDE
+\AddToShipoutPicture*{
+    \put(0,0){
+        \parbox[b][\paperheight]{\paperwidth}{%
+            \vfill
+            \centering
+            {\transparent{0.1}\includegraphics[width=\textwidth]{../../static/img/{{ obj.bde }}}}%
+            \vfill
+        }
+    }
+}
+
+
+%%%%%%%%%%%%%%%%%%%%% A MODIFIER DANS LA FACTURE %%%%%%%%%%%%%%%%%%%%%
+% Infos Association
+\def\MonNom{{"{"}}{{ obj.my_name }}} % Nom de l'association
+\def\MonAdresseRue{{"{"}}{{ obj.my_address_street }}} % Adresse de l'association
+\def\MonAdresseVille{{"{"}}{{ obj.my_city }}}
+
+% Informations bancaires de l'association
+\def\CodeBanque{{"{"}}{{ obj.bank_code|stringformat:".05d" }}}
+\def\CodeGuichet{{"{"}}{{ obj.desk_code|stringformat:".05d" }}}
+\def\NCompte{{"{"}}{{ obj.account_number|stringformat:".011d" }}}
+\def\CleRib{{"{"}}{{ obj.rib_key|stringformat:".02d" }}}
+\def\IBAN{FR76\CodeBanque\CodeGuichet\NCompte\CleRib}
+\def\CodeBic{{"{"}}{{ obj.bic }}}
+
+\def\FactureNum            {{"{"}}{{obj.id}}}    % Numéro de facture
+\def\FactureAcquittee    {% if obj.acquitted %} {oui} {% else %} {non} {% endif %}     % Facture acquittée : oui/non
+\def\FactureLieu    {{"{"}}{{ obj.place }}}    % Lieu de l'édition de la facture
+\def\FactureDate    {{"{"}}{{ obj.date }}}    % Date de l'édition de la facture
+\def\FactureObjet   {{"{"}}{{ obj.object|safe }} }    % Objet du document
+% Description de la facture
+\def\FactureDescr   {{"{"}}{{ obj.description|safe }}}
+
+% Infos Client
+\def\ClientNom{{"{"}}{{obj.name|safe}}}    % Nom du client
+\def\ClientAdresse{{"{"}}{{ obj.address|safe }}} % Adresse du client
+
+% Liste des produits facturés : Désignation, quantité, prix unitaire HT
+
+{% for product in products %}
+\AjouterProduit{ {{product.designation|safe}}} { {{product.quantity|safe}}} { {{product.amount_euros|safe}}} { {{product.total_euros|safe}}}
+{% endfor %}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+\geometry{verbose,tmargin=4em,bmargin=8em,lmargin=6em,rmargin=6em}
+\setlength{\parindent}{1pt}
+\setlength{\parskip}{1ex plus 0.5ex minus 0.2ex}
+
+\thispagestyle{fancy}
+\pagestyle{fancy}
+\setlength{\parindent}{0pt}
+
+\renewcommand{\headrulewidth}{0pt}
+\cfoot{
+    \small{\MonNom  ~--~ \MonAdresseRue ~ \MonAdresseVille ~--~ Téléphone : +33(0)6 89 88 56 50\newline
+     Site web : bde.ens-cachan.fr ~--~ E-mail : tresorerie.bde@lists.crans.org \newline Numéro SIRET : 399 485 838 00011
+    }
+}
+
+\begin{document}
+
+% Logo de la société
+% \includegraphics{logo.jpg}
+
+% Nom et adresse de la société
+\MonNom \\
+\MonAdresseRue \\
+\MonAdresseVille
+
+Facture n°\FactureNum
+
+
+{\addtolength{\leftskip}{10.5cm} %in ERT
+   \ClientNom    \\
+    \ClientAdresse        \\
+
+} %in ERT
+
+
+\hspace*{10.5cm}
+\FactureLieu, le \FactureDate
+
+~\\~\\
+
+\textbf{Objet : \FactureObjet \\}
+
+\textnormal{\FactureDescr}
+
+~\\
+
+\begin{center}
+    \begin{tabular}{lrrr}
+        \textbf{Désignation ~~~~~~}    & \textbf{Prix unitaire}    & \textbf{Quantité}    & \textbf{Montant (EUR)}    \\
+        \hline
+        \AfficheResultat{}
+    \end{tabular}
+\end{center}
+
+~\\
+
+\ifthenelse{\equal{\FactureAcquittee}{oui}}{
+    Facture acquittée.
+}{
+
+    À régler par chèque ou par virement bancaire :
+
+    \begin{center}
+        \begin{tabular}{|c c c c|}
+            \hline
+            \textbf{Code banque} & \textbf{Code guichet} & \textbf{N° de Compte} & \textbf{Clé RIB}\\
+                    \CodeBanque          & \CodeGuichet        & \NCompte               & \CleRib   \\
+            \hline
+            \textbf{IBAN N°}        & \multicolumn{3}{|l|} \IBAN         \\
+            \hline
+            \textbf{Code BIC}        & \multicolumn{3}{|l|}\CodeBic         \\
+            \hline
+        \end{tabular}
+    \end{center}
+
+}
+
+\begin{center}
+TVA non applicable, article 293 B du CGI.
+\end{center}
+
+\end{document}
diff --git a/templates/treasury/remittance_form.html b/templates/treasury/remittance_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..af4170f48dae1247cc137484a6e0efe06faac7b6
--- /dev/null
+++ b/templates/treasury/remittance_form.html
@@ -0,0 +1,37 @@
+{% extends "base.html" %}
+{% load static %}
+{% load i18n %}
+{% load crispy_forms_tags pretty_money %}
+{% load render_table from django_tables2 %}
+{% block content %}
+    <h1>{% trans "Remittance #" %}{{ object.pk }}</h1>
+
+    <p><a class="btn btn-default" href="{% url 'treasury:remittance_list' %}">{% trans "Remittances list" %}</a></p>
+
+    {% if object.pk %}
+        <div id="div_id_type" class="form-group"><label for="id_count" class="col-form-label">{% trans "Count" %}</label>
+            <div class="">
+                <input type="text" name="count" value="{{ object.count }}" class="textinput textInput form-control" id="id_count" disabled>
+            </div>
+        </div>
+
+        <div id="div_id_type" class="form-group"><label for="id_amount" class="col-form-label">{% trans "Amount" %}</label>
+            <div class="">
+                <input class="textinput textInput form-control" type="text" value="{{ object.amount|pretty_money }}" id="id_amount" disabled>
+            </div>
+        </div>
+    {% endif %}
+
+    {% crispy form %}
+
+    <hr>
+
+    <h2>{% trans "Linked transactions" %}</h2>
+    {% if special_transactions.data %}
+        {% render_table special_transactions %}
+    {% else %}
+        <div class="alert alert-warning">
+            {% trans "There is no transaction linked with this remittance." %}
+        </div>
+    {% endif %}
+{% endblock %}
diff --git a/templates/treasury/remittance_list.html b/templates/treasury/remittance_list.html
new file mode 100644
index 0000000000000000000000000000000000000000..8bc634e4b35e029a95d57369dd24d0c50c0aeb5e
--- /dev/null
+++ b/templates/treasury/remittance_list.html
@@ -0,0 +1,56 @@
+{% extends "base.html" %}
+{% load render_table from django_tables2 %}
+{% load i18n %}
+{% block content %}
+
+    <div class="row">
+        <div class="col-xl-12">
+            <div class="btn-group btn-group-toggle" style="width: 100%; padding: 0 0 2em 0" data-toggle="buttons">
+                <a href="{% url "treasury:invoice_list" %}" class="btn btn-sm btn-outline-primary">
+                    {% trans "Invoice" %}s
+                </a>
+                <a href="#" class="btn btn-sm btn-outline-primary active">
+                    {% trans "Remittance" %}s
+                </a>
+            </div>
+        </div>
+    </div>
+
+    <h2>{% trans "Opened remittances" %}</h2>
+    {% if opened_remittances.data %}
+        {% render_table opened_remittances %}
+    {% else %}
+        <div class="alert alert-warning">
+            {% trans "There is no opened remittance." %}
+        </div>
+    {% endif %}
+
+    <a class="btn btn-primary" href="{% url 'treasury:remittance_create' %}">{% trans "New remittance" %}</a>
+
+    <hr>
+
+    <h2>{% trans "Transfers without remittances" %}</h2>
+    {% if special_transactions_no_remittance.data %}
+        {% render_table special_transactions_no_remittance %}
+    {% else %}
+        <div class="alert alert-warning">
+            {% trans "There is no transaction without any linked remittance." %}
+        </div>
+    {% endif %}
+
+    <hr>
+
+    <h2>{% trans "Transfers with opened remittances" %}</h2>
+    {% if special_transactions_with_remittance.data %}
+        {% render_table special_transactions_with_remittance %}
+    {% else %}
+        <div class="alert alert-warning">
+            {% trans "There is no transaction with an opened linked remittance." %}
+        </div>
+    {% endif %}
+
+    <hr>
+
+    <h2>{% trans "Closed remittances" %}</h2>
+    {% render_table closed_remittances %}
+{% endblock %}
diff --git a/templates/treasury/specialtransactionproxy_form.html b/templates/treasury/specialtransactionproxy_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..4e7758ae4fb966b08be32046af19aaff9a04a1df
--- /dev/null
+++ b/templates/treasury/specialtransactionproxy_form.html
@@ -0,0 +1,9 @@
+{% extends "base.html" %}
+{% load static %}
+{% load i18n %}
+{% load crispy_forms_tags pretty_money %}
+{% load render_table from django_tables2 %}
+{% block content %}
+    <p><a class="btn btn-default" href="{% url 'treasury:remittance_list' %}">{% trans "Remittances list" %}</a></p>
+    {% crispy form %}
+{% endblock %}
diff --git a/tox.ini b/tox.ini
index 0b5c20c98b24a30131e48697742b212595fcd11a..01bf4edbd0f9f6e21622bb04b4778c30ac4add57 100644
--- a/tox.ini
+++ b/tox.ini
@@ -30,7 +30,7 @@ deps =
     pep8-naming
     pyflakes
 commands =
-    flake8 apps/activity apps/api apps/logs apps/member apps/note
+    flake8 apps/activity apps/api apps/logs apps/member apps/note apps/permission apps/treasury
 
 [flake8]
 # Ignore too many errors, should be reduced in the future