mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-11-03 17:08:47 +01:00 
			
		
		
		
	Manage remittance types
This commit is contained in:
		@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								apps/treasury/fixtures/initial.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								apps/treasury/fixtures/initial.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    "model": "treasury.remittancetype",
 | 
			
		||||
    "pk": 1,
 | 
			
		||||
    "fields": {
 | 
			
		||||
      "note": 3
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
@@ -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):
 | 
			
		||||
 
 | 
			
		||||
@@ -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, )
 | 
			
		||||
 
 | 
			
		||||
@@ -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):
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user