From e7a7e81a2c9916a624b3aff11935677e9b5aae80 Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Sat, 29 Dec 2018 14:01:22 +0100 Subject: [PATCH] Add discount for custom invoices. --- cotisations/forms.py | 45 +++- .../templates/cotisations/facture.html | 196 ++++++++++-------- cotisations/tex.py | 15 ++ cotisations/views.py | 14 +- 4 files changed, 172 insertions(+), 98 deletions(-) diff --git a/cotisations/forms.py b/cotisations/forms.py index 01e52756..73cb4971 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, CustomInvoice +from .models import Article, Paiement, Facture, Banque, CustomInvoice, Vente from .payment_methods import balance @@ -104,7 +104,44 @@ class SelectArticleForm(FormRevMixin, Form): user = kwargs.pop('user') target_user = kwargs.pop('target_user', None) super(SelectArticleForm, self).__init__(*args, **kwargs) - self.fields['article'].queryset = Article.find_allowed_articles(user, target_user) + self.fields['article'].queryset = Article.find_allowed_articles( + user, target_user) + + +class DiscountForm(Form): + """ + Form used in oder to create a discount on an invoice. + """ + is_relative = forms.BooleanField( + label=_("Discount is on percentage"), + required=False, + ) + discount = forms.DecimalField( + label=_("Discount"), + max_value=100, + min_value=0, + max_digits=5, + decimal_places=2, + required=False, + ) + + def apply_to_invoice(self, invoice): + invoice_price = invoice.prix_total() + discount = self.cleaned_data['discount'] + is_relative = self.cleaned_data['is_relative'] + if is_relative: + amount = discount/100 * invoice_price + else: + amount = discount + if amount > 0: + name = _("{}% discount") if is_relative else _("{}€ discount") + name = name.format(discount) + Vente.objects.create( + facture=invoice, + name=name, + prix=-amount, + number=1 + ) class CustomInvoiceForm(FormRevMixin, ModelForm): @@ -248,7 +285,8 @@ class RechargeForm(FormRevMixin, Form): super(RechargeForm, self).__init__(*args, **kwargs) self.fields['payment'].empty_label = \ _("Select a payment method") - self.fields['payment'].queryset = Paiement.find_allowed_payments(user_source).exclude(is_balance=True) + self.fields['payment'].queryset = Paiement.find_allowed_payments( + user_source).exclude(is_balance=True) def clean(self): """ @@ -266,4 +304,3 @@ class RechargeForm(FormRevMixin, Form): } ) return self.cleaned_data - diff --git a/cotisations/templates/cotisations/facture.html b/cotisations/templates/cotisations/facture.html index 1f87f579..ff9ed837 100644 --- a/cotisations/templates/cotisations/facture.html +++ b/cotisations/templates/cotisations/facture.html @@ -44,6 +44,8 @@ with this program; if not, write to the Free Software Foundation, Inc., {% blocktrans %}Current balance: {{ balance }} €{% endblocktrans %}

{% endif %} +{% bootstrap_form_errors factureform %} +{% bootstrap_form_errors discount_form %}
{% csrf_token %} @@ -68,8 +70,12 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endfor %} +

{% trans "Discount" %}

+ {% if discount_form %} + {% bootstrap_form discount_form %} + {% endif %}

- {% blocktrans %}Total price: 0,00 €{% endblocktrans %} + {% blocktrans %}Total price: 0,00 €{% endblocktrans %}

{% endif %} {% bootstrap_button action_name button_type='submit' icon='ok' button_class='btn-success' %} @@ -78,105 +84,117 @@ with this program; if not, write to the Free Software Foundation, Inc., {% if articlesformset or payment_method%} {% endif %} diff --git a/cotisations/tex.py b/cotisations/tex.py index 3f404f22..d6c0ae5f 100644 --- a/cotisations/tex.py +++ b/cotisations/tex.py @@ -36,6 +36,7 @@ from django.template import Context from django.http import HttpResponse from django.conf import settings from django.utils.text import slugify +import logging TEMP_PREFIX = getattr(settings, 'TEX_TEMP_PREFIX', 'render_tex-') @@ -93,6 +94,20 @@ def create_pdf(template, ctx={}): return pdf +def escape_chars(string): + """Escape the '%' and the '€' signs to avoid messing with LaTeX""" + if not isinstance(string, str): + return string + mapping = ( + ('€', r'\euro'), + ('%', r'\%'), + ) + r = str(string) + for k, v in mapping: + r = r.replace(k, v) + return r + + def render_tex(_request, template, ctx={}): """Creates a PDF from a LaTex templates using pdflatex. diff --git a/cotisations/views.py b/cotisations/views.py index 68118711..7d4185cc 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -80,9 +80,10 @@ from .forms import ( DelBanqueForm, SelectArticleForm, RechargeForm, - CustomInvoiceForm + CustomInvoiceForm, + DiscountForm ) -from .tex import render_invoice +from .tex import render_invoice, escape_chars from .payment_methods.forms import payment_method_factory from .utils import find_payment_method @@ -198,8 +199,9 @@ def new_custom_invoice(request): request.POST or None, form_kwargs={'user': request.user} ) + discount_form = DiscountForm(request.POST or None) - if invoice_form.is_valid() and articles_formset.is_valid(): + if invoice_form.is_valid() and articles_formset.is_valid() and discount_form.is_valid(): new_invoice_instance = invoice_form.save() for art_item in articles_formset: if art_item.cleaned_data: @@ -213,6 +215,7 @@ def new_custom_invoice(request): duration=article.duration, number=quantity ) + discount_form.apply_to_invoice(new_invoice_instance) messages.success( request, _("The custom invoice was created.") @@ -223,7 +226,8 @@ def new_custom_invoice(request): 'factureform': invoice_form, 'action_name': _("Confirm"), 'articlesformset': articles_formset, - 'articlelist': articles + 'articlelist': articles, + 'discount_form': discount_form }, 'cotisations/facture.html', request) @@ -382,7 +386,7 @@ def custom_invoice_pdf(request, invoice, **_kwargs): purchases_info = [] for purchase in purchases_objects: purchases_info.append({ - 'name': purchase.name, + 'name': escape_chars(purchase.name), 'price': purchase.prix, 'quantity': purchase.number, 'total_price': purchase.prix_total