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
# 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)

View File

@ -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)

View File

@ -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)

View File

@ -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

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()
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):

View File

@ -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, )

View File

@ -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):

View File

@ -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

View File

@ -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 <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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 ""

View File

@ -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 <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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"