diff --git a/cotisations/forms.py b/cotisations/forms.py index 7ad9e413..ccf9d5d6 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -46,7 +46,7 @@ from django.shortcuts import get_object_or_404 from re2o.field_permissions import FieldPermissionFormMixin from re2o.mixins import FormRevMixin -from .models import Article, Paiement, Facture, Banque +from .models import Article, Paiement, Facture, Banque, CustomInvoice from .payment_methods import balance @@ -131,24 +131,13 @@ class SelectClubArticleForm(Form): self.fields['article'].queryset = Article.find_allowed_articles(user) -# TODO : change Facture to Invoice -class NewFactureFormPdf(Form): +class CustomInvoiceForm(FormRevMixin, ModelForm): """ - Form used to create a custom PDF invoice. + Form used to create a custom invoice. """ - paid = forms.BooleanField(label=_l("Paid"), required=False) - # TODO : change dest field to recipient - dest = forms.CharField( - required=True, - max_length=255, - label=_l("Recipient") - ) - # TODO : change chambre field to address - chambre = forms.CharField( - required=False, - max_length=10, - label=_l("Address") - ) + class Meta: + model = CustomInvoice + fields = '__all__' class ArticleForm(FormRevMixin, ModelForm): diff --git a/cotisations/migrations/0031_custom_invoice.py b/cotisations/migrations/0031_custom_invoice.py new file mode 100644 index 00000000..52921739 --- /dev/null +++ b/cotisations/migrations/0031_custom_invoice.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-07-21 20:01 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import re2o.field_permissions +import re2o.mixins + + +def reattribute_ids(apps, schema_editor): + Facture = apps.get_model('cotisations', 'Facture') + BaseInvoice = apps.get_model('cotisations', 'BaseInvoice') + + for f in Facture.objects.all(): + base = BaseInvoice.objects.create(id=f.pk, date=f.date) + f.baseinvoice_ptr = base + f.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('cotisations', '0030_custom_payment'), + ] + + operations = [ + migrations.CreateModel( + name='BaseInvoice', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateTimeField(auto_now_add=True, verbose_name='Date')), + ], + bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, re2o.field_permissions.FieldPermissionModelMixin, models.Model), + ), + migrations.CreateModel( + name='CustomInvoice', + fields=[ + ('baseinvoice_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cotisations.BaseInvoice')), + ('recipient', models.CharField(max_length=255, verbose_name='Recipient')), + ('payment', models.CharField(max_length=255, verbose_name='Payment type')), + ('address', models.CharField(max_length=255, verbose_name='Address')), + ('paid', models.BooleanField(verbose_name='Paid')), + ], + bases=('cotisations.baseinvoice',), + options={'permissions': (('view_custom_invoice', 'Can view a custom invoice'),)}, + ), + migrations.AddField( + model_name='facture', + name='baseinvoice_ptr', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='cotisations.BaseInvoice', null=True), + preserve_default=False, + ), + migrations.RunPython(reattribute_ids), + migrations.AlterField( + model_name='vente', + name='facture', + field=models.ForeignKey(on_delete=models.CASCADE, verbose_name='Invoice', to='cotisations.BaseInvoice') + ), + migrations.RemoveField( + model_name='facture', + name='id', + ), + migrations.RemoveField( + model_name='facture', + name='date', + ), + migrations.AlterField( + model_name='facture', + name='baseinvoice_ptr', + field=models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cotisations.BaseInvoice'), + ) + ] diff --git a/cotisations/models.py b/cotisations/models.py index 52d71b58..c4515cc7 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -55,80 +55,11 @@ from cotisations.utils import find_payment_method from cotisations.validators import check_no_balance -# TODO : change facture to invoice -class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): - """ - The model for an invoice. It reprensents the fact that a user paid for - something (it can be multiple article paid at once). - - An invoice is linked to : - * one or more purchases (one for each article sold that time) - * a user (the one who bought those articles) - * a payment method (the one used by the user) - * (if applicable) a bank - * (if applicable) a cheque number. - Every invoice is dated throught the 'date' value. - An invoice has a 'controlled' value (default : False) which means that - someone with high enough rights has controlled that invoice and taken it - into account. It also has a 'valid' value (default : True) which means - that someone with high enough rights has decided that this invoice was not - valid (thus it's like the user never paid for his articles). It may be - necessary in case of non-payment. - """ - - user = models.ForeignKey('users.User', on_delete=models.PROTECT) - # TODO : change paiement to payment - paiement = models.ForeignKey('Paiement', on_delete=models.PROTECT) - # TODO : change banque to bank - banque = models.ForeignKey( - 'Banque', - on_delete=models.PROTECT, - blank=True, - null=True - ) - # TODO : maybe change to cheque nummber because not evident - cheque = models.CharField( - max_length=255, - blank=True, - verbose_name=_l("Cheque number") - ) +class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): date = models.DateTimeField( auto_now_add=True, verbose_name=_l("Date") ) - # TODO : change name to validity for clarity - valid = models.BooleanField( - default=True, - verbose_name=_l("Validated") - ) - # TODO : changed name to controlled for clarity - control = models.BooleanField( - default=False, - verbose_name=_l("Controlled") - ) - - class Meta: - abstract = False - permissions = ( - # TODO : change facture to invoice - ('change_facture_control', - _l("Can change the \"controlled\" state")), - # TODO : seems more likely to be call create_facture_pdf - # or create_invoice_pdf - ('change_facture_pdf', - _l("Can create a custom PDF invoice")), - ('view_facture', - _l("Can see an invoice's details")), - ('change_all_facture', - _l("Can edit all the previous invoices")), - ) - verbose_name = _l("Invoice") - verbose_name_plural = _l("Invoices") - - def linked_objects(self): - """Return linked objects : machine and domain. - Usefull in history display""" - return self.vente_set.all() # TODO : change prix to price def prix(self): @@ -167,6 +98,78 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): ).values_list('name', flat=True)) return name + +# TODO : change facture to invoice +class Facture(BaseInvoice): + """ + The model for an invoice. It reprensents the fact that a user paid for + something (it can be multiple article paid at once). + + An invoice is linked to : + * one or more purchases (one for each article sold that time) + * a user (the one who bought those articles) + * a payment method (the one used by the user) + * (if applicable) a bank + * (if applicable) a cheque number. + Every invoice is dated throught the 'date' value. + An invoice has a 'controlled' value (default : False) which means that + someone with high enough rights has controlled that invoice and taken it + into account. It also has a 'valid' value (default : True) which means + that someone with high enough rights has decided that this invoice was not + valid (thus it's like the user never paid for his articles). It may be + necessary in case of non-payment. + """ + + user = models.ForeignKey('users.User', on_delete=models.PROTECT) + # TODO : change paiement to payment + paiement = models.ForeignKey('Paiement', on_delete=models.PROTECT) + # TODO : change banque to bank + banque = models.ForeignKey( + 'Banque', + on_delete=models.PROTECT, + blank=True, + null=True + ) + # TODO : maybe change to cheque nummber because not evident + cheque = models.CharField( + max_length=255, + blank=True, + verbose_name=_l("Cheque number") + ) + # TODO : change name to validity for clarity + valid = models.BooleanField( + default=True, + verbose_name=_l("Validated") + ) + # TODO : changed name to controlled for clarity + control = models.BooleanField( + default=False, + verbose_name=_l("Controlled") + ) + + class Meta: + abstract = False + permissions = ( + # TODO : change facture to invoice + ('change_facture_control', + _l("Can change the \"controlled\" state")), + # TODO : seems more likely to be call create_facture_pdf + # or create_invoice_pdf + ('change_facture_pdf', + _l("Can create a custom PDF invoice")), + ('view_facture', + _l("Can see an invoice's details")), + ('change_all_facture', + _l("Can edit all the previous invoices")), + ) + verbose_name = _l("Invoice") + verbose_name_plural = _l("Invoices") + + def linked_objects(self): + """Return linked objects : machine and domain. + Usefull in history display""" + return self.vente_set.all() + def can_edit(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.change_facture'): return False, _("You don't have the right to edit an invoice.") @@ -265,6 +268,28 @@ def facture_post_delete(**kwargs): user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) +class CustomInvoice(BaseInvoice): + class Meta: + permissions = ( + ('view_custom_invoice', _l("Can view a custom invoice")), + ) + recipient = models.CharField( + max_length=255, + verbose_name=_l("Recipient") + ) + payment = models.CharField( + max_length=255, + verbose_name=_l("Payment type") + ) + address = models.CharField( + max_length=255, + verbose_name=_l("Address") + ) + paid = models.BooleanField( + verbose_name="Paid" + ) + + # TODO : change Vente to Purchase class Vente(RevMixin, AclMixin, models.Model): """ @@ -288,7 +313,7 @@ class Vente(RevMixin, AclMixin, models.Model): # TODO : change facture to invoice facture = models.ForeignKey( - 'Facture', + 'BaseInvoice', on_delete=models.CASCADE, verbose_name=_l("Invoice") ) @@ -355,6 +380,10 @@ class Vente(RevMixin, AclMixin, models.Model): cotisation_type defined (which means the article sold represents a cotisation) """ + try: + invoice = self.facture.facture + except Facture.DoesNotExist: + return if not hasattr(self, 'cotisation') and self.type_cotisation: cotisation = Cotisation(vente=self) cotisation.type_cotisation = self.type_cotisation @@ -362,7 +391,7 @@ class Vente(RevMixin, AclMixin, models.Model): end_cotisation = Cotisation.objects.filter( vente__in=Vente.objects.filter( facture__in=Facture.objects.filter( - user=self.facture.user + user=invoice.user ).exclude(valid=False)) ).filter( Q(type_cotisation='All') | @@ -371,9 +400,9 @@ class Vente(RevMixin, AclMixin, models.Model): date_start__lt=date_start ).aggregate(Max('date_end'))['date_end__max'] elif self.type_cotisation == "Adhesion": - end_cotisation = self.facture.user.end_adhesion() + end_cotisation = invoice.user.end_adhesion() else: - end_cotisation = self.facture.user.end_connexion() + end_cotisation = invoice.user.end_connexion() date_start = date_start or timezone.now() end_cotisation = end_cotisation or date_start date_max = max(end_cotisation, date_start) @@ -445,6 +474,10 @@ def vente_post_save(**kwargs): LDAP user when a purchase has been saved. """ purchase = kwargs['instance'] + try: + purchase.facture.facture + except Facture.DoesNotExist: + return if hasattr(purchase, 'cotisation'): purchase.cotisation.vente = purchase purchase.cotisation.save() @@ -462,8 +495,12 @@ def vente_post_delete(**kwargs): Synchronise the LDAP user after a purchase has been deleted. """ purchase = kwargs['instance'] + try: + invoice = purchase.facture.facture + except Facture.DoesNotExist: + return if purchase.type_cotisation: - user = purchase.facture.user + user = invoice.user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) diff --git a/cotisations/templates/cotisations/aff_custom_invoice.html b/cotisations/templates/cotisations/aff_custom_invoice.html new file mode 100644 index 00000000..1a477613 --- /dev/null +++ b/cotisations/templates/cotisations/aff_custom_invoice.html @@ -0,0 +1,108 @@ +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2017 Gabriel Détraz +Copyright © 2017 Goulven Kermarec +Copyright © 2017 Augustin Lemesle +Copyright © 2018 Hugo Levy-Falk + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} +{% load i18n %} +{% load acl %} + +
+ {% if custom_invoice_list.paginator %} + {% include 'pagination.html' with list=custom_invoice_list %} + {% endif %} + + + + + + + + + + + + + + + + {% for invoice in custom_invoice_list %} + + + + + + + + + + + + {% endfor %} +
+ {% trans "Recipient" as tr_recip %} + {% include 'buttons/sort.html' with prefix='invoice' col='user' text=tr_user %} + {% trans "Designation" %}{% trans "Total price" %} + {% trans "Payment method" as tr_payment_method %} + {% include 'buttons/sort.html' with prefix='invoice' col='payement' text=tr_payment_method %} + + {% trans "Date" as tr_date %} + {% include 'buttons/sort.html' with prefix='invoice' col='date' text=tr_date %} + + {% trans "Invoice id" as tr_invoice_id %} + {% include 'buttons/sort.html' with prefix='invoice' col='id' text=tr_invoice_id %} + {% trans "Paid" %}
{{ invoice.recipient }}{{ invoice.name }}{{ invoice.prix_total }}{{ invoice.payment }}{{ invoice.date }}{{ invoice.id }}{{ invoice.paid }} + + + + {% trans "PDF" %} + +
+ + {% if custom_invoice_list.paginator %} + {% include 'pagination.html' with list=custom_invoice_list %} + {% endif %} +
diff --git a/cotisations/templates/cotisations/index_custom_invoice.html b/cotisations/templates/cotisations/index_custom_invoice.html new file mode 100644 index 00000000..67d00126 --- /dev/null +++ b/cotisations/templates/cotisations/index_custom_invoice.html @@ -0,0 +1,36 @@ +{% extends "cotisations/sidebar.html" %} +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2017 Gabriel Détraz +Copyright © 2017 Goulven Kermarec +Copyright © 2017 Augustin Lemesle + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} +{% load acl %} +{% load i18n %} + +{% block title %}{% trans "Custom invoices" %}{% endblock %} + +{% block content %} +

{% trans "Custom invoices list" %}

+{% can_create CustomInvoice %} +{% include "buttons/add.html" with href='cotisations:new-custom-invoice'%} +{% acl_end %} +{% include 'cotisations/aff_custom_invoice.html' with custom_invoice_list=custom_invoice_list %} +{% endblock %} diff --git a/cotisations/templates/cotisations/sidebar.html b/cotisations/templates/cotisations/sidebar.html index 296730f2..8d37bb6a 100644 --- a/cotisations/templates/cotisations/sidebar.html +++ b/cotisations/templates/cotisations/sidebar.html @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block sidebar %} {% can_change Facture pdf %} - + {% trans "Create an invoice" %} @@ -40,6 +40,11 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Invoices" %} {% acl_end %} + {% can_view_all CustomInvoice %} + + {% trans "Custom invoices" %} + + {% acl_end %} {% can_view_all Article %} {% trans "Available articles" %} diff --git a/cotisations/urls.py b/cotisations/urls.py index 470ccbfa..edc448fe 100644 --- a/cotisations/urls.py +++ b/cotisations/urls.py @@ -52,9 +52,29 @@ urlpatterns = [ name='facture-pdf' ), url( - r'^new_facture_pdf/$', - views.new_facture_pdf, - name='new-facture-pdf' + r'^index_custom_invoice/$', + views.index_custom_invoice, + name='index-custom-invoice' + ), + url( + r'^new_custom_invoice/$', + views.new_custom_invoice, + name='new-custom-invoice' + ), + url( + r'^edit_custom_invoice/(?P[0-9]+)$', + views.edit_custom_invoice, + name='edit-custom-invoice' + ), + url( + r'^custom_invoice_pdf/(?P[0-9]+)$', + views.custom_invoice_pdf, + name='custom-invoice-pdf', + ), + url( + r'^del_custom_invoice/(?P[0-9]+)$', + views.del_custom_invoice, + name='del-custom-invoice' ), url( r'^credit_solde/(?P[0-9]+)$', diff --git a/cotisations/views.py b/cotisations/views.py index 66eb66f5..90bc3632 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -58,7 +58,15 @@ from re2o.acl import ( can_change, ) from preferences.models import AssoOption, GeneralOption -from .models import Facture, Article, Vente, Paiement, Banque +from .models import ( + Facture, + Article, + Vente, + Paiement, + Banque, + CustomInvoice, + BaseInvoice +) from .forms import ( FactureForm, ArticleForm, @@ -67,10 +75,10 @@ from .forms import ( DelPaiementForm, BanqueForm, DelBanqueForm, - NewFactureFormPdf, SelectUserArticleForm, SelectClubArticleForm, - RechargeForm + RechargeForm, + CustomInvoiceForm ) from .tex import render_invoice from .payment_methods.forms import payment_method_factory @@ -178,10 +186,10 @@ def new_facture(request, user, userid): # TODO : change facture to invoice @login_required -@can_change(Facture, 'pdf') -def new_facture_pdf(request): +@can_create(CustomInvoice) +def new_custom_invoice(request): """ - View used to generate a custom PDF invoice. It's mainly used to + View used to generate a custom invoice. It's mainly used to get invoices that are not taken into account, for the administrative point of view. """ @@ -190,7 +198,7 @@ def new_facture_pdf(request): Q(type_user='All') | Q(type_user=request.user.class_name) ) # Building the invocie form and the article formset - invoice_form = NewFactureFormPdf(request.POST or None) + invoice_form = CustomInvoiceForm(request.POST or None) if request.user.is_class_club: articles_formset = formset_factory(SelectClubArticleForm)( request.POST or None, @@ -202,44 +210,31 @@ def new_facture_pdf(request): form_kwargs={'user': request.user} ) if invoice_form.is_valid() and articles_formset.is_valid(): - # Get the article list and build an list out of it - # contiaining (article_name, article_price, quantity, total_price) - articles_info = [] - for articles_form in articles_formset: - if articles_form.cleaned_data: - article = articles_form.cleaned_data['article'] - quantity = articles_form.cleaned_data['quantity'] - articles_info.append({ - 'name': article.name, - 'price': article.prix, - 'quantity': quantity, - 'total_price': article.prix * quantity - }) - paid = invoice_form.cleaned_data['paid'] - recipient = invoice_form.cleaned_data['dest'] - address = invoice_form.cleaned_data['chambre'] - total_price = sum(a['total_price'] for a in articles_info) + new_invoice_instance = invoice_form.save() + for art_item in articles_formset: + if art_item.cleaned_data: + article = art_item.cleaned_data['article'] + quantity = art_item.cleaned_data['quantity'] + Vente.objects.create( + facture=new_invoice_instance, + name=article.name, + prix=article.prix, + type_cotisation=article.type_cotisation, + duration=article.duration, + number=quantity + ) + messages.success( + request, + _('The custom invoice was successfully created.') + ) + return redirect(reverse('cotisations:index-custom-invoice')) + - return render_invoice(request, { - 'DATE': timezone.now(), - 'recipient_name': recipient, - 'address': address, - 'article': articles_info, - 'total': total_price, - 'paid': paid, - 'asso_name': AssoOption.get_cached_value('name'), - 'line1': AssoOption.get_cached_value('adresse1'), - 'line2': AssoOption.get_cached_value('adresse2'), - 'siret': AssoOption.get_cached_value('siret'), - 'email': AssoOption.get_cached_value('contact'), - 'phone': AssoOption.get_cached_value('telephone'), - 'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH) - }) return form({ 'factureform': invoice_form, 'action_name': _("Create"), 'articlesformset': articles_formset, - 'articles': articles + 'articlelist': articles }, 'cotisations/facture.html', request) @@ -292,7 +287,7 @@ def facture_pdf(request, facture, **_kwargs): def edit_facture(request, facture, **_kwargs): """ View used to edit an existing invoice. - Articles can be added or remove to the invoice and quantity + Articles can be added or removed to the invoice and quantity can be set as desired. This is also the view used to invalidate an invoice. """ @@ -347,6 +342,100 @@ def del_facture(request, facture, **_kwargs): }, 'cotisations/delete.html', request) +@login_required +@can_edit(CustomInvoice) +def edit_custom_invoice(request, invoice, **kwargs): + # Building the invocie form and the article formset + invoice_form = CustomInvoiceForm( + request.POST or None, + instance=invoice + ) + purchases_objects = Vente.objects.filter(facture=invoice) + purchase_form_set = modelformset_factory( + Vente, + fields=('name', 'number'), + extra=0, + max_num=len(purchases_objects) + ) + purchase_form = purchase_form_set( + request.POST or None, + queryset=purchases_objects + ) + if invoice_form.is_valid() and purchase_form.is_valid(): + if invoice_form.changed_data: + invoice_form.save() + purchase_form.save() + messages.success( + request, + _("The invoice has been successfully edited.") + ) + return redirect(reverse('cotisations:index-custom-invoice')) + + return form({ + 'factureform': invoice_form, + 'venteform': purchase_form + }, 'cotisations/edit_facture.html', request) + + +@login_required +@can_view(CustomInvoice) +def custom_invoice_pdf(request, invoice, **_kwargs): + """ + View used to generate a PDF file from an existing invoice in database + Creates a line for each Purchase (thus article sold) and generate the + invoice with the total price, the payment method, the address and the + legal information for the user. + """ + # TODO : change vente to purchase + purchases_objects = Vente.objects.all().filter(facture=invoice) + # Get the article list and build an list out of it + # contiaining (article_name, article_price, quantity, total_price) + purchases_info = [] + for purchase in purchases_objects: + purchases_info.append({ + 'name': purchase.name, + 'price': purchase.prix, + 'quantity': purchase.number, + 'total_price': purchase.prix_total + }) + return render_invoice(request, { + 'paid': invoice.paid, + 'fid': invoice.id, + 'DATE': invoice.date, + 'recipient_name': invoice.recipient, + 'address': invoice.address, + 'article': purchases_info, + 'total': invoice.prix_total(), + 'asso_name': AssoOption.get_cached_value('name'), + 'line1': AssoOption.get_cached_value('adresse1'), + 'line2': AssoOption.get_cached_value('adresse2'), + 'siret': AssoOption.get_cached_value('siret'), + 'email': AssoOption.get_cached_value('contact'), + 'phone': AssoOption.get_cached_value('telephone'), + 'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH) + }) + + +# TODO : change facture to invoice +@login_required +@can_delete(CustomInvoice) +def del_custom_invoice(request, invoice, **_kwargs): + """ + View used to delete an existing invocie. + """ + if request.method == "POST": + invoice.delete() + messages.success( + request, + _("The invoice has been successfully deleted.") + ) + return redirect(reverse('cotisations:index-custom-invoice')) + return form({ + 'objet': invoice, + 'objet_name': _("Invoice") + }, 'cotisations/delete.html', request) + + @login_required @can_create(Article) def add_article(request): @@ -681,8 +770,31 @@ def index_banque(request): }) +@login_required +@can_view_all(CustomInvoice) +def index_custom_invoice(request): + """View used to display every custom invoice.""" + pagination_number = GeneralOption.get_cached_value('pagination_number') + custom_invoice_list = CustomInvoice.objects.prefetch_related('vente_set') + custom_invoice_list = SortTable.sort( + custom_invoice_list, + request.GET.get('col'), + request.GET.get('order'), + SortTable.COTISATIONS_CUSTOM + ) + custom_invoice_list = re2o_paginator( + request, + custom_invoice_list, + pagination_number, + ) + return render(request, 'cotisations/index_custom_invoice.html', { + 'custom_invoice_list': custom_invoice_list + }) + + @login_required @can_view_all(Facture) +@can_view_all(CustomInvoice) def index(request): """ View used to display the list of all exisitng invoices. @@ -698,7 +810,7 @@ def index(request): ) invoice_list = re2o_paginator(request, invoice_list, pagination_number) return render(request, 'cotisations/index.html', { - 'facture_list': invoice_list + 'facture_list': invoice_list, }) diff --git a/re2o/utils.py b/re2o/utils.py index 75304369..6f7870f0 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -250,6 +250,14 @@ class SortTable: 'cotis_id': ['id'], 'default': ['-date'] } + COTISATIONS_CUSTOM = { + 'invoice_date': ['date'], + 'invoice_id': ['id'], + 'invoice_recipient': ['recipient'], + 'invoice_address': ['address'], + 'invoice_payment': ['payment'], + 'default': ['-date'] + } COTISATIONS_CONTROL = { 'control_name': ['user__adherent__name'], 'control_surname': ['user__surname'], diff --git a/templates/buttons/add.html b/templates/buttons/add.html index 17058b89..33148a7b 100644 --- a/templates/buttons/add.html +++ b/templates/buttons/add.html @@ -21,6 +21,6 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. {% endcomment %} - +