From 4cf1047faae95dadbbd85b7f052c6de79bd45f26 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 24 Mar 2020 17:06:50 +0100 Subject: [PATCH] Manage remittance types --- apps/treasury/admin.py | 27 ++++++++++++++------ apps/treasury/api/serializers.py | 30 ++++++++++++++++++++++- apps/treasury/api/urls.py | 4 ++- apps/treasury/api/views.py | 24 ++++++++++++++++-- apps/treasury/fixtures/initial.json | 9 +++++++ apps/treasury/forms.py | 10 ++++---- apps/treasury/models.py | 26 +++++++++++++++----- apps/treasury/tables.py | 2 +- apps/treasury/views.py | 11 ++++----- locale/de/LC_MESSAGES/django.po | 38 +++++++++++++++-------------- locale/fr/LC_MESSAGES/django.po | 38 +++++++++++++++-------------- 11 files changed, 153 insertions(+), 66 deletions(-) create mode 100644 apps/treasury/fixtures/initial.json diff --git a/apps/treasury/admin.py b/apps/treasury/admin.py index cef9e37d..abeec3e3 100644 --- a/apps/treasury/admin.py +++ b/apps/treasury/admin.py @@ -1,16 +1,27 @@ # 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 .models import Invoice, Product +from .models import RemittanceType, Remittance -@admin.register(Invoice) -class InvoiceAdmin(admin.ModelAdmin): - list_display = ('id', 'name', 'object', 'acquitted', ) +@admin.register(RemittanceType) +class RemittanceTypeAdmin(admin.ModelAdmin): + """ + Admin customisation for RemiitanceType + """ + list_display = ('note', ) -@admin.register(Product) -class ProductAdmin(admin.ModelAdmin): - list_display = ('designation', 'quantity', 'amount', ) +@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/serializers.py b/apps/treasury/api/serializers.py index c835fd80..f1bbef75 100644 --- a/apps/treasury/api/serializers.py +++ b/apps/treasury/api/serializers.py @@ -2,8 +2,9 @@ # SPDX-License-Identifier: GPL-3.0-or-later 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): @@ -32,3 +33,30 @@ class InvoiceSerializer(serializers.ModelSerializer): 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 index ff3a606f..30ac00e1 100644 --- a/apps/treasury/api/urls.py +++ b/apps/treasury/api/urls.py @@ -1,7 +1,7 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # 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): @@ -10,3 +10,5 @@ def register_treasury_urls(router, path): """ 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 index 2f35b92c..7a70fd24 100644 --- a/apps/treasury/api/views.py +++ b/apps/treasury/api/views.py @@ -5,8 +5,8 @@ from django_filters.rest_framework import DjangoFilterBackend from rest_framework.filters import SearchFilter from api.viewsets import ReadProtectedModelViewSet -from .serializers import InvoiceSerializer, ProductSerializer -from ..models import Invoice, Product +from .serializers import InvoiceSerializer, ProductSerializer, RemittanceTypeSerializer, RemittanceSerializer +from ..models import Invoice, Product, RemittanceType, Remittance class InvoiceViewSet(ReadProtectedModelViewSet): @@ -31,3 +31,23 @@ class ProductViewSet(ReadProtectedModelViewSet): 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/fixtures/initial.json b/apps/treasury/fixtures/initial.json new file mode 100644 index 00000000..143d2101 --- /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 index 65fae3a4..6269138c 100644 --- a/apps/treasury/forms.py +++ b/apps/treasury/forms.py @@ -49,8 +49,8 @@ class RemittanceForm(forms.ModelForm): self.helper = FormHelper() if self.instance.pk: - self.fields["type"].disabled = True - self.fields["type"].required = False + self.fields["remittance_type"].disabled = True + self.fields["remittance_type"].required = False if not self.instance.closed: 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() - if "type" in self.changed_data: - self.add_error("type", _("You can't change the type of the remittance.")) + 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.")) if "close" in self.data: self.instance.closed = True @@ -77,7 +77,7 @@ class RemittanceForm(forms.ModelForm): class Meta: model = Remittance - fields = ('type', 'comment', ) + fields = ('remittance_type', 'comment',) class LinkTransactionToRemittanceForm(forms.ModelForm): diff --git a/apps/treasury/models.py b/apps/treasury/models.py index e2be5af7..4180d065 100644 --- a/apps/treasury/models.py +++ b/apps/treasury/models.py @@ -96,6 +96,20 @@ class Product(models.Model): 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. @@ -106,8 +120,8 @@ class Remittance(models.Model): verbose_name=_("Date"), ) - type = models.ForeignKey( - NoteSpecial, + remittance_type = models.ForeignKey( + RemittanceType, on_delete=models.PROTECT, verbose_name=_("Type"), ) @@ -133,13 +147,13 @@ class Remittance(models.Model): def amount(self): return sum(transaction.total for transaction in self.transactions.all()) - def full_clean(self, exclude=None, validate_unique=True): - ret = super().full_clean(exclude, validate_unique) + def save(self, force_insert=False, force_update=False, using=None, + 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") - return ret + return super().save(force_insert, force_update, using, update_fields) def __str__(self): return _("Remittance #{:d}: {}").format(self.id, self.comment, ) diff --git a/apps/treasury/tables.py b/apps/treasury/tables.py index f5d1a472..7c78a8a4 100644 --- a/apps/treasury/tables.py +++ b/apps/treasury/tables.py @@ -56,7 +56,7 @@ class RemittanceTable(tables.Table): } model = Remittance 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): diff --git a/apps/treasury/views.py b/apps/treasury/views.py index 71b07b0b..981dbc76 100644 --- a/apps/treasury/views.py +++ b/apps/treasury/views.py @@ -17,11 +17,11 @@ 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 +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 .models import Invoice, Product, Remittance, SpecialTransactionProxy, RemittanceType 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["closed_remittances"] = RemittanceTable(data=Remittance.objects.filter(closed=True).reverse().all()) 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(), exclude=('remittance_remove', )) 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(), exclude=('remittance_add', )) @@ -236,7 +236,6 @@ class RemittanceUpdateView(LoginRequiredMixin, UpdateView): def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) - form = ctx["form"] ctx["table"] = RemittanceTable(data=Remittance.objects.all()) data = SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self.object).all() ctx["special_transactions"] = SpecialTransactionTable( @@ -262,7 +261,7 @@ class LinkTransactionToRemittanceView(LoginRequiredMixin, UpdateView): 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(type=self.object.transaction.source) + .queryset.filter(remittance_type__note=self.object.transaction.source) return ctx diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 5b7047ae..1dc8ab2a 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-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" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -576,28 +576,29 @@ msgstr "" msgid "Unit price" msgstr "" -#: apps/treasury/models.py:106 +#: apps/treasury/models.py:120 msgid "Date" msgstr "" -#: apps/treasury/models.py:112 +#: apps/treasury/models.py:126 msgid "Type" msgstr "" -#: apps/treasury/models.py:117 +#: apps/treasury/models.py:131 msgid "Comment" msgstr "" -#: apps/treasury/models.py:122 +#: apps/treasury/models.py:136 msgid "Closed" msgstr "" -#: apps/treasury/models.py:145 +#: apps/treasury/models.py:159 msgid "Remittance #{:d}: {}" msgstr "" -#: apps/treasury/models.py:164 apps/treasury/tables.py:64 -#: apps/treasury/tables.py:72 +#: 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 "" @@ -605,7 +606,8 @@ msgstr "" msgid "Invoice #{:d}" 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" msgstr "" @@ -952,7 +954,7 @@ msgstr "" msgid "Remove product" msgstr "" -#: templates/treasury/invoice_list.html:8 +#: templates/treasury/invoice_list.html:21 msgid "New invoice" msgstr "" @@ -977,34 +979,34 @@ msgstr "" msgid "There is no transaction linked with this remittance." msgstr "" -#: templates/treasury/remittance_list.html:6 +#: templates/treasury/remittance_list.html:19 msgid "Opened remittances" msgstr "" -#: templates/treasury/remittance_list.html:11 +#: templates/treasury/remittance_list.html:24 msgid "There is no opened remittance." msgstr "" -#: templates/treasury/remittance_list.html:15 +#: templates/treasury/remittance_list.html:28 msgid "New remittance" msgstr "" -#: templates/treasury/remittance_list.html:19 +#: templates/treasury/remittance_list.html:32 msgid "Transfers without remittances" msgstr "" -#: templates/treasury/remittance_list.html:24 +#: templates/treasury/remittance_list.html:37 msgid "There is no transaction without any linked remittance." msgstr "" -#: templates/treasury/remittance_list.html:30 +#: templates/treasury/remittance_list.html:43 msgid "Transfers with opened remittances" msgstr "" -#: templates/treasury/remittance_list.html:35 +#: templates/treasury/remittance_list.html:48 msgid "There is no transaction without an opened linked remittance." msgstr "" -#: templates/treasury/remittance_list.html:41 +#: 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 b96f3f44..e2d41826 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-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" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -576,28 +576,29 @@ msgstr "Quantité" msgid "Unit price" msgstr "Prix unitaire" -#: apps/treasury/models.py:106 +#: apps/treasury/models.py:120 msgid "Date" msgstr "Date" -#: apps/treasury/models.py:112 +#: apps/treasury/models.py:126 msgid "Type" msgstr "Type" -#: apps/treasury/models.py:117 +#: apps/treasury/models.py:131 msgid "Comment" msgstr "Commentaire" -#: apps/treasury/models.py:122 +#: apps/treasury/models.py:136 msgid "Closed" msgstr "Fermée" -#: apps/treasury/models.py:145 +#: apps/treasury/models.py:159 msgid "Remittance #{:d}: {}" msgstr "Remise n°{:d} : {}" -#: apps/treasury/models.py:164 apps/treasury/tables.py:64 -#: apps/treasury/tables.py:72 +#: 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" @@ -605,7 +606,8 @@ msgstr "Remise" msgid "Invoice #{: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" msgstr "Facture" @@ -954,7 +956,7 @@ msgstr "Ajouter produit" msgid "Remove product" msgstr "Retirer produit" -#: templates/treasury/invoice_list.html:8 +#: templates/treasury/invoice_list.html:21 msgid "New invoice" msgstr "Nouvelle facture" @@ -979,34 +981,34 @@ msgstr "Transactions liées" 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:6 +#: templates/treasury/remittance_list.html:19 msgid "Opened remittances" msgstr "Remises ouvertes" -#: templates/treasury/remittance_list.html:11 +#: 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:15 +#: templates/treasury/remittance_list.html:28 msgid "New remittance" msgstr "Nouvelle remise" -#: templates/treasury/remittance_list.html:19 +#: templates/treasury/remittance_list.html:32 msgid "Transfers without remittances" 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." 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" 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." 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" msgstr "Remises fermées"