Manage remittance types

This commit is contained in:
Yohann D'ANELLO 2020-03-24 17:06:50 +01:00
parent 1b03eac95b
commit 4cf1047faa
11 changed files with 153 additions and 66 deletions

View File

@ -1,16 +1,27 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-lateré
from django.contrib import admin from django.contrib import admin
from .models import Invoice, Product from .models import RemittanceType, Remittance
@admin.register(Invoice) @admin.register(RemittanceType)
class InvoiceAdmin(admin.ModelAdmin): class RemittanceTypeAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'object', 'acquitted', ) """
Admin customisation for RemiitanceType
"""
list_display = ('note', )
@admin.register(Product) @admin.register(Remittance)
class ProductAdmin(admin.ModelAdmin): class RemittanceAdmin(admin.ModelAdmin):
list_display = ('designation', 'quantity', 'amount', ) """
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)

View File

@ -2,8 +2,9 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from rest_framework import serializers from rest_framework import serializers
from note.api.serializers import SpecialTransactionSerializer
from ..models import Invoice, Product from ..models import Invoice, Product, RemittanceType, Remittance
class ProductSerializer(serializers.ModelSerializer): class ProductSerializer(serializers.ModelSerializer):
@ -32,3 +33,30 @@ class InvoiceSerializer(serializers.ModelSerializer):
def get_products(self, obj): def get_products(self, obj):
return serializers.ListSerializer(child=ProductSerializer())\ return serializers.ListSerializer(child=ProductSerializer())\
.to_representation(Product.objects.filter(invoice=obj).all()) .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)

View File

@ -1,7 +1,7 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from .views import InvoiceViewSet, ProductViewSet from .views import InvoiceViewSet, ProductViewSet, RemittanceViewSet, RemittanceTypeViewSet
def register_treasury_urls(router, path): def register_treasury_urls(router, path):
@ -10,3 +10,5 @@ def register_treasury_urls(router, path):
""" """
router.register(path + '/invoice', InvoiceViewSet) router.register(path + '/invoice', InvoiceViewSet)
router.register(path + '/product', ProductViewSet) router.register(path + '/product', ProductViewSet)
router.register(path + '/remittance_type', RemittanceTypeViewSet)
router.register(path + '/remittance', RemittanceViewSet)

View File

@ -5,8 +5,8 @@ from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter from rest_framework.filters import SearchFilter
from api.viewsets import ReadProtectedModelViewSet from api.viewsets import ReadProtectedModelViewSet
from .serializers import InvoiceSerializer, ProductSerializer from .serializers import InvoiceSerializer, ProductSerializer, RemittanceTypeSerializer, RemittanceSerializer
from ..models import Invoice, Product from ..models import Invoice, Product, RemittanceType, Remittance
class InvoiceViewSet(ReadProtectedModelViewSet): class InvoiceViewSet(ReadProtectedModelViewSet):
@ -31,3 +31,23 @@ class ProductViewSet(ReadProtectedModelViewSet):
serializer_class = ProductSerializer serializer_class = ProductSerializer
filter_backends = [SearchFilter] filter_backends = [SearchFilter]
search_fields = ['$designation', ] 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

View File

@ -0,0 +1,9 @@
[
{
"model": "treasury.remittancetype",
"pk": 1,
"fields": {
"note": 3
}
}
]

View File

@ -49,8 +49,8 @@ class RemittanceForm(forms.ModelForm):
self.helper = FormHelper() self.helper = FormHelper()
if self.instance.pk: if self.instance.pk:
self.fields["type"].disabled = True self.fields["remittance_type"].disabled = True
self.fields["type"].required = False self.fields["remittance_type"].required = False
if not self.instance.closed: if not self.instance.closed:
self.helper.add_input(Submit('submit', _("Submit"), attr={'class': 'btn btn-block btn-primary'})) self.helper.add_input(Submit('submit', _("Submit"), attr={'class': 'btn btn-block btn-primary'}))
@ -66,8 +66,8 @@ class RemittanceForm(forms.ModelForm):
cleaned_data = super().clean() cleaned_data = super().clean()
if "type" in self.changed_data: if self.instance.pk and cleaned_data.get("remittance_type") != self.instance.remittance_type:
self.add_error("type", _("You can't change the type of the remittance.")) self.add_error("remittance_type", _("You can't change the type of the remittance."))
if "close" in self.data: if "close" in self.data:
self.instance.closed = True self.instance.closed = True
@ -77,7 +77,7 @@ class RemittanceForm(forms.ModelForm):
class Meta: class Meta:
model = Remittance model = Remittance
fields = ('type', 'comment', ) fields = ('remittance_type', 'comment',)
class LinkTransactionToRemittanceForm(forms.ModelForm): class LinkTransactionToRemittanceForm(forms.ModelForm):

View File

@ -96,6 +96,20 @@ class Product(models.Model):
return self.total / 100 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): class Remittance(models.Model):
""" """
Treasurers want to regroup checks or bank transfers in bank remittances. Treasurers want to regroup checks or bank transfers in bank remittances.
@ -106,8 +120,8 @@ class Remittance(models.Model):
verbose_name=_("Date"), verbose_name=_("Date"),
) )
type = models.ForeignKey( remittance_type = models.ForeignKey(
NoteSpecial, RemittanceType,
on_delete=models.PROTECT, on_delete=models.PROTECT,
verbose_name=_("Type"), verbose_name=_("Type"),
) )
@ -133,13 +147,13 @@ class Remittance(models.Model):
def amount(self): def amount(self):
return sum(transaction.total for transaction in self.transactions.all()) return sum(transaction.total for transaction in self.transactions.all())
def full_clean(self, exclude=None, validate_unique=True): def save(self, force_insert=False, force_update=False, using=None,
ret = super().full_clean(exclude, validate_unique) update_fields=None):
if self.transactions.filter(~Q(source=self.type)).exists(): if self.transactions.filter(~Q(source=self.remittance_type.note)).exists():
raise ValidationError("All transactions in a remittance must have the same type") raise ValidationError("All transactions in a remittance must have the same type")
return ret return super().save(force_insert, force_update, using, update_fields)
def __str__(self): def __str__(self):
return _("Remittance #{:d}: {}").format(self.id, self.comment, ) return _("Remittance #{:d}: {}").format(self.id, self.comment, )

View File

@ -56,7 +56,7 @@ class RemittanceTable(tables.Table):
} }
model = Remittance model = Remittance
template_name = 'django_tables2/bootstrap4.html' template_name = 'django_tables2/bootstrap4.html'
fields = ('id', 'date', 'type', 'comment', 'count', 'amount', 'view',) fields = ('id', 'date', 'remittance_type', 'comment', 'count', 'amount', 'view',)
class SpecialTransactionTable(tables.Table): class SpecialTransactionTable(tables.Table):

View File

@ -17,11 +17,11 @@ from django.urls import reverse_lazy
from django.views.generic import CreateView, UpdateView from django.views.generic import CreateView, UpdateView
from django.views.generic.base import View, TemplateView from django.views.generic.base import View, TemplateView
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from note.models import SpecialTransaction from note.models import SpecialTransaction, NoteSpecial
from note_kfet.settings.base import BASE_DIR from note_kfet.settings.base import BASE_DIR
from .forms import InvoiceForm, ProductFormSet, ProductFormSetHelper, RemittanceForm, LinkTransactionToRemittanceForm from .forms import InvoiceForm, ProductFormSet, ProductFormSetHelper, RemittanceForm, LinkTransactionToRemittanceForm
from .models import Invoice, Product, Remittance, SpecialTransactionProxy from .models import Invoice, Product, Remittance, SpecialTransactionProxy, RemittanceType
from .tables import InvoiceTable, RemittanceTable, SpecialTransactionTable from .tables import InvoiceTable, RemittanceTable, SpecialTransactionTable
@ -212,11 +212,11 @@ class RemittanceListView(LoginRequiredMixin, TemplateView):
ctx["opened_remittances"] = RemittanceTable(data=Remittance.objects.filter(closed=False).all()) ctx["opened_remittances"] = RemittanceTable(data=Remittance.objects.filter(closed=False).all())
ctx["closed_remittances"] = RemittanceTable(data=Remittance.objects.filter(closed=True).reverse().all()) ctx["closed_remittances"] = RemittanceTable(data=Remittance.objects.filter(closed=True).reverse().all())
ctx["special_transactions_no_remittance"] = SpecialTransactionTable( ctx["special_transactions_no_remittance"] = SpecialTransactionTable(
data=SpecialTransaction.objects.filter(source__polymorphic_ctype__model="notespecial", data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)),
specialtransactionproxy__remittance=None).all(), specialtransactionproxy__remittance=None).all(),
exclude=('remittance_remove', )) exclude=('remittance_remove', ))
ctx["special_transactions_with_remittance"] = SpecialTransactionTable( ctx["special_transactions_with_remittance"] = SpecialTransactionTable(
data=SpecialTransaction.objects.filter(source__polymorphic_ctype__model="notespecial", data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)),
specialtransactionproxy__remittance__closed=False).all(), specialtransactionproxy__remittance__closed=False).all(),
exclude=('remittance_add', )) exclude=('remittance_add', ))
@ -236,7 +236,6 @@ class RemittanceUpdateView(LoginRequiredMixin, UpdateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs) ctx = super().get_context_data(**kwargs)
form = ctx["form"]
ctx["table"] = RemittanceTable(data=Remittance.objects.all()) ctx["table"] = RemittanceTable(data=Remittance.objects.all())
data = SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self.object).all() data = SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self.object).all()
ctx["special_transactions"] = SpecialTransactionTable( ctx["special_transactions"] = SpecialTransactionTable(
@ -262,7 +261,7 @@ class LinkTransactionToRemittanceView(LoginRequiredMixin, UpdateView):
form.fields["bank"].initial = self.object.transaction.bank form.fields["bank"].initial = self.object.transaction.bank
form.fields["amount"].initial = self.object.transaction.amount form.fields["amount"].initial = self.object.transaction.amount
form.fields["remittance"].queryset = form.fields["remittance"] \ form.fields["remittance"].queryset = form.fields["remittance"] \
.queryset.filter(type=self.object.transaction.source) .queryset.filter(remittance_type__note=self.object.transaction.source)
return ctx return ctx

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-24 01:00+0100\n" "POT-Creation-Date: 2020-03-24 15:49+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -576,28 +576,29 @@ msgstr ""
msgid "Unit price" msgid "Unit price"
msgstr "" msgstr ""
#: apps/treasury/models.py:106 #: apps/treasury/models.py:120
msgid "Date" msgid "Date"
msgstr "" msgstr ""
#: apps/treasury/models.py:112 #: apps/treasury/models.py:126
msgid "Type" msgid "Type"
msgstr "" msgstr ""
#: apps/treasury/models.py:117 #: apps/treasury/models.py:131
msgid "Comment" msgid "Comment"
msgstr "" msgstr ""
#: apps/treasury/models.py:122 #: apps/treasury/models.py:136
msgid "Closed" msgid "Closed"
msgstr "" msgstr ""
#: apps/treasury/models.py:145 #: apps/treasury/models.py:159
msgid "Remittance #{:d}: {}" msgid "Remittance #{:d}: {}"
msgstr "" msgstr ""
#: apps/treasury/models.py:164 apps/treasury/tables.py:64 #: apps/treasury/models.py:178 apps/treasury/tables.py:64
#: apps/treasury/tables.py:72 #: apps/treasury/tables.py:72 templates/treasury/invoice_list.html:13
#: templates/treasury/remittance_list.html:13
msgid "Remittance" msgid "Remittance"
msgstr "" msgstr ""
@ -605,7 +606,8 @@ msgstr ""
msgid "Invoice #{:d}" msgid "Invoice #{:d}"
msgstr "" msgstr ""
#: apps/treasury/tables.py:19 #: apps/treasury/tables.py:19 templates/treasury/invoice_list.html:10
#: templates/treasury/remittance_list.html:10
msgid "Invoice" msgid "Invoice"
msgstr "" msgstr ""
@ -952,7 +954,7 @@ msgstr ""
msgid "Remove product" msgid "Remove product"
msgstr "" msgstr ""
#: templates/treasury/invoice_list.html:8 #: templates/treasury/invoice_list.html:21
msgid "New invoice" msgid "New invoice"
msgstr "" msgstr ""
@ -977,34 +979,34 @@ msgstr ""
msgid "There is no transaction linked with this remittance." msgid "There is no transaction linked with this remittance."
msgstr "" msgstr ""
#: templates/treasury/remittance_list.html:6 #: templates/treasury/remittance_list.html:19
msgid "Opened remittances" msgid "Opened remittances"
msgstr "" msgstr ""
#: templates/treasury/remittance_list.html:11 #: templates/treasury/remittance_list.html:24
msgid "There is no opened remittance." msgid "There is no opened remittance."
msgstr "" msgstr ""
#: templates/treasury/remittance_list.html:15 #: templates/treasury/remittance_list.html:28
msgid "New remittance" msgid "New remittance"
msgstr "" msgstr ""
#: templates/treasury/remittance_list.html:19 #: templates/treasury/remittance_list.html:32
msgid "Transfers without remittances" msgid "Transfers without remittances"
msgstr "" msgstr ""
#: templates/treasury/remittance_list.html:24 #: templates/treasury/remittance_list.html:37
msgid "There is no transaction without any linked remittance." msgid "There is no transaction without any linked remittance."
msgstr "" msgstr ""
#: templates/treasury/remittance_list.html:30 #: templates/treasury/remittance_list.html:43
msgid "Transfers with opened remittances" msgid "Transfers with opened remittances"
msgstr "" msgstr ""
#: templates/treasury/remittance_list.html:35 #: templates/treasury/remittance_list.html:48
msgid "There is no transaction without an opened linked remittance." msgid "There is no transaction without an opened linked remittance."
msgstr "" msgstr ""
#: templates/treasury/remittance_list.html:41 #: templates/treasury/remittance_list.html:54
msgid "Closed remittances" msgid "Closed remittances"
msgstr "" msgstr ""

View File

@ -3,7 +3,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-24 01:00+0100\n" "POT-Creation-Date: 2020-03-24 15:49+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -576,28 +576,29 @@ msgstr "Quantité"
msgid "Unit price" msgid "Unit price"
msgstr "Prix unitaire" msgstr "Prix unitaire"
#: apps/treasury/models.py:106 #: apps/treasury/models.py:120
msgid "Date" msgid "Date"
msgstr "Date" msgstr "Date"
#: apps/treasury/models.py:112 #: apps/treasury/models.py:126
msgid "Type" msgid "Type"
msgstr "Type" msgstr "Type"
#: apps/treasury/models.py:117 #: apps/treasury/models.py:131
msgid "Comment" msgid "Comment"
msgstr "Commentaire" msgstr "Commentaire"
#: apps/treasury/models.py:122 #: apps/treasury/models.py:136
msgid "Closed" msgid "Closed"
msgstr "Fermée" msgstr "Fermée"
#: apps/treasury/models.py:145 #: apps/treasury/models.py:159
msgid "Remittance #{:d}: {}" msgid "Remittance #{:d}: {}"
msgstr "Remise n°{:d} : {}" msgstr "Remise n°{:d} : {}"
#: apps/treasury/models.py:164 apps/treasury/tables.py:64 #: apps/treasury/models.py:178 apps/treasury/tables.py:64
#: apps/treasury/tables.py:72 #: apps/treasury/tables.py:72 templates/treasury/invoice_list.html:13
#: templates/treasury/remittance_list.html:13
msgid "Remittance" msgid "Remittance"
msgstr "Remise" msgstr "Remise"
@ -605,7 +606,8 @@ msgstr "Remise"
msgid "Invoice #{:d}" msgid "Invoice #{:d}"
msgstr "Facture n°{:d}" msgstr "Facture n°{:d}"
#: apps/treasury/tables.py:19 #: apps/treasury/tables.py:19 templates/treasury/invoice_list.html:10
#: templates/treasury/remittance_list.html:10
msgid "Invoice" msgid "Invoice"
msgstr "Facture" msgstr "Facture"
@ -954,7 +956,7 @@ msgstr "Ajouter produit"
msgid "Remove product" msgid "Remove product"
msgstr "Retirer produit" msgstr "Retirer produit"
#: templates/treasury/invoice_list.html:8 #: templates/treasury/invoice_list.html:21
msgid "New invoice" msgid "New invoice"
msgstr "Nouvelle facture" msgstr "Nouvelle facture"
@ -979,34 +981,34 @@ msgstr "Transactions liées"
msgid "There is no transaction linked with this remittance." msgid "There is no transaction linked with this remittance."
msgstr "Il n'y a pas de transaction liée à cette remise." msgstr "Il n'y a pas de transaction liée à cette remise."
#: templates/treasury/remittance_list.html:6 #: templates/treasury/remittance_list.html:19
msgid "Opened remittances" msgid "Opened remittances"
msgstr "Remises ouvertes" msgstr "Remises ouvertes"
#: templates/treasury/remittance_list.html:11 #: templates/treasury/remittance_list.html:24
msgid "There is no opened remittance." msgid "There is no opened remittance."
msgstr "Il n'y a pas de remise ouverte." msgstr "Il n'y a pas de remise ouverte."
#: templates/treasury/remittance_list.html:15 #: templates/treasury/remittance_list.html:28
msgid "New remittance" msgid "New remittance"
msgstr "Nouvelle remise" msgstr "Nouvelle remise"
#: templates/treasury/remittance_list.html:19 #: templates/treasury/remittance_list.html:32
msgid "Transfers without remittances" msgid "Transfers without remittances"
msgstr "Transactions sans remise associée" msgstr "Transactions sans remise associée"
#: templates/treasury/remittance_list.html:24 #: templates/treasury/remittance_list.html:37
msgid "There is no transaction without any linked remittance." msgid "There is no transaction without any linked remittance."
msgstr "Il n'y a pas de transactions sans remise associée." msgstr "Il n'y a pas de transactions sans remise associée."
#: templates/treasury/remittance_list.html:30 #: templates/treasury/remittance_list.html:43
msgid "Transfers with opened remittances" msgid "Transfers with opened remittances"
msgstr "Transactions avec remise associée ouverte" msgstr "Transactions avec remise associée ouverte"
#: templates/treasury/remittance_list.html:35 #: templates/treasury/remittance_list.html:48
msgid "There is no transaction without an opened linked remittance." msgid "There is no transaction without an opened linked remittance."
msgstr "Il n'y a pas de transaction sans remise ouverte associée." msgstr "Il n'y a pas de transaction sans remise ouverte associée."
#: templates/treasury/remittance_list.html:41 #: templates/treasury/remittance_list.html:54
msgid "Closed remittances" msgid "Closed remittances"
msgstr "Remises fermées" msgstr "Remises fermées"