8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-25 22:22:26 +00:00

Add discount for custom invoices.

This commit is contained in:
Hugo LEVY-FALK 2018-12-29 14:01:22 +01:00 committed by chirac
parent 81b7a7c4af
commit e7a7e81a2c
4 changed files with 172 additions and 98 deletions

View file

@ -46,7 +46,7 @@ from django.shortcuts import get_object_or_404
from re2o.field_permissions import FieldPermissionFormMixin from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin 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 from .payment_methods import balance
@ -104,7 +104,44 @@ class SelectArticleForm(FormRevMixin, Form):
user = kwargs.pop('user') user = kwargs.pop('user')
target_user = kwargs.pop('target_user', None) target_user = kwargs.pop('target_user', None)
super(SelectArticleForm, self).__init__(*args, **kwargs) 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): class CustomInvoiceForm(FormRevMixin, ModelForm):
@ -248,7 +285,8 @@ class RechargeForm(FormRevMixin, Form):
super(RechargeForm, self).__init__(*args, **kwargs) super(RechargeForm, self).__init__(*args, **kwargs)
self.fields['payment'].empty_label = \ self.fields['payment'].empty_label = \
_("Select a payment method") _("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): def clean(self):
""" """
@ -266,4 +304,3 @@ class RechargeForm(FormRevMixin, Form):
} }
) )
return self.cleaned_data return self.cleaned_data

View file

@ -44,6 +44,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% blocktrans %}Current balance: {{ balance }} €{% endblocktrans %} {% blocktrans %}Current balance: {{ balance }} €{% endblocktrans %}
</p> </p>
{% endif %} {% endif %}
{% bootstrap_form_errors factureform %}
{% bootstrap_form_errors discount_form %}
<form class="form" method="post"> <form class="form" method="post">
{% csrf_token %} {% csrf_token %}
@ -68,8 +70,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endfor %} {% endfor %}
</div> </div>
<input class="btn btn-primary btn-block" role="button" value="{% trans "Add an extra article"%}" id="add_one"> <input class="btn btn-primary btn-block" role="button" value="{% trans "Add an extra article"%}" id="add_one">
<h3>{% trans "Discount" %}</h3>
{% if discount_form %}
{% bootstrap_form discount_form %}
{% endif %}
<p> <p>
{% blocktrans %}Total price: <span id="total_price">0,00</span> €{% endblocktrans %} {% blocktrans %}Total price: <span id="total_price">0,00</span> €{% endblocktrans %}
</p> </p>
{% endif %} {% endif %}
{% bootstrap_button action_name button_type='submit' icon='ok' button_class='btn-success' %} {% 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%} {% if articlesformset or payment_method%}
<script type="text/javascript"> <script type="text/javascript">
{% if articlesformset %} {% if articlesformset %}
var prices = {}; var prices = {};
{% for article in articlelist %} {% for article in articlelist %}
prices[{{ article.id|escapejs }}] = {{ article.prix }}; prices[{{ article.id|escapejs }}] = {{ article.prix }};
{% endfor %} {% endfor %}
var template = `Article : &nbsp; var template = `Article : &nbsp;
{% bootstrap_form articlesformset.empty_form label_class='sr-only' %} {% bootstrap_form articlesformset.empty_form label_class='sr-only' %}
&nbsp; &nbsp;
<button class="btn btn-danger btn-sm" <button class="btn btn-danger btn-sm"
id="id_form-__prefix__-article-remove" type="button"> id="id_form-__prefix__-article-remove" type="button">
<span class="fa fa-times"></span> <span class="fa fa-times"></span>
</button>` </button>`
function add_article(){ function add_article(){
// Index start at 0 => new_index = number of items // Index start at 0 => new_index = number of items
var new_index = var new_index =
document.getElementsByClassName('product_to_sell').length; document.getElementsByClassName('product_to_sell').length;
document.getElementById('id_form-TOTAL_FORMS').value ++; document.getElementById('id_form-TOTAL_FORMS').value ++;
var new_article = document.createElement('div'); var new_article = document.createElement('div');
new_article.className = 'product_to_sell form-inline'; new_article.className = 'product_to_sell form-inline';
new_article.innerHTML = template.replace(/__prefix__/g, new_index); new_article.innerHTML = template.replace(/__prefix__/g, new_index);
document.getElementById('form_set').appendChild(new_article); document.getElementById('form_set').appendChild(new_article);
add_listenner_for_id(new_index); add_listenner_for_id(new_index);
} }
function update_price(){ function update_price(){
var price = 0; var price = 0;
var product_count = var product_count =
document.getElementsByClassName('product_to_sell').length; document.getElementsByClassName('product_to_sell').length;
var article, article_price, quantity; var article, article_price, quantity;
for (i = 0; i < product_count; ++i){ for (i = 0; i < product_count; ++i){
article = document.getElementById( article = document.getElementById(
'id_form-' + i.toString() + '-article').value; 'id_form-' + i.toString() + '-article').value;
if (article == '') { if (article == '') {
continue; continue;
} }
article_price = prices[article]; article_price = prices[article];
quantity = document.getElementById( quantity = document.getElementById(
'id_form-' + i.toString() + '-quantity').value; 'id_form-' + i.toString() + '-quantity').value;
price += article_price * quantity; price += article_price * quantity;
}
document.getElementById('total_price').innerHTML =
price.toFixed(2).toString().replace('.', ',');
} }
{% if discount_form %}
function add_listenner_for_id(i){ var relative_discount = document.getElementById('id_is_relative').checked;
document.getElementById('id_form-' + i.toString() + '-article') var discount = document.getElementById('id_discount').value;
.addEventListener("change", update_price, true); if(relative_discount) {
document.getElementById('id_form-' + i.toString() + '-article') discount = discount/100 * price;
.addEventListener("onkeypress", update_price, true);
document.getElementById('id_form-' + i.toString() + '-quantity')
.addEventListener("change", update_price, true);
document.getElementById('id_form-' + i.toString() + '-article-remove')
.addEventListener("click", function(event) {
var article = event.target.parentNode;
article.parentNode.removeChild(article);
document.getElementById('id_form-TOTAL_FORMS').value --;
update_price();
})
} }
price -= discount;
{% endif %}
document.getElementById('total_price').innerHTML =
price.toFixed(2).toString().replace('.', ',');
}
// Add events manager when DOM is fully loaded function add_listenner_for_id(i){
document.addEventListener("DOMContentLoaded", function() { document.getElementById('id_form-' + i.toString() + '-article')
document.getElementById("add_one") .addEventListener("change", update_price, true);
.addEventListener("click", add_article, true); document.getElementById('id_form-' + i.toString() + '-article')
var product_count = .addEventListener("onkeypress", update_price, true);
document.getElementsByClassName('product_to_sell').length; document.getElementById('id_form-' + i.toString() + '-quantity')
for (i = 0; i < product_count; ++i){ .addEventListener("change", update_price, true);
add_listenner_for_id(i); document.getElementById('id_form-' + i.toString() + '-article-remove')
} .addEventListener("click", function(event) {
update_price(); var article = event.target.parentNode;
}); article.parentNode.removeChild(article);
document.getElementById('id_form-TOTAL_FORMS').value --;
update_price();
})
}
// Add events manager when DOM is fully loaded
document.addEventListener("DOMContentLoaded", function() {
document.getElementById("add_one")
.addEventListener("click", add_article, true);
var product_count =
document.getElementsByClassName('product_to_sell').length;
for (i = 0; i < product_count; ++i){
add_listenner_for_id(i);
}
document.getElementById('id_discount')
.addEventListener('change', update_price, true);
document.getElementById('id_is_relative')
.addEventListener('click', update_price, true);
update_price();
});
{% endif %} {% endif %}
{% if payment_method.templates %} {% if payment_method.templates %}
var TEMPLATES = [ var TEMPLATES = [
"", "",
{% for t in payment_method.templates %} {% for t in payment_method.templates %}
{% if t %} {% if t %}
`{% bootstrap_form t %}`, `{% bootstrap_form t %}`,
{% else %} {% else %}
"", "",
{% endif %} {% endif %}
{% endfor %} {% endfor %}
]; ];
function update_payment_method_form(){ function update_payment_method_form(){
var method = document.getElementById('paymentMethodSelect').value; var method = document.getElementById('paymentMethodSelect').value;
if(method==""){ if(method==""){
method=0; method=0;
}
else{
method = Number(method);
method += 1;
}
console.log(method);
var html = TEMPLATES[method];
document.getElementById('paymentMethod').innerHTML = html;
} }
document.getElementById("paymentMethodSelect").addEventListener("change", update_payment_method_form); else{
method = Number(method);
method += 1;
}
console.log(method);
var html = TEMPLATES[method];
document.getElementById('paymentMethod').innerHTML = html;
}
document.getElementById("paymentMethodSelect").addEventListener("change", update_payment_method_form);
{% endif %} {% endif %}
</script> </script>
{% endif %} {% endif %}

View file

@ -36,6 +36,7 @@ from django.template import Context
from django.http import HttpResponse from django.http import HttpResponse
from django.conf import settings from django.conf import settings
from django.utils.text import slugify from django.utils.text import slugify
import logging
TEMP_PREFIX = getattr(settings, 'TEX_TEMP_PREFIX', 'render_tex-') TEMP_PREFIX = getattr(settings, 'TEX_TEMP_PREFIX', 'render_tex-')
@ -93,6 +94,20 @@ def create_pdf(template, ctx={}):
return pdf 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={}): def render_tex(_request, template, ctx={}):
"""Creates a PDF from a LaTex templates using pdflatex. """Creates a PDF from a LaTex templates using pdflatex.

View file

@ -80,9 +80,10 @@ from .forms import (
DelBanqueForm, DelBanqueForm,
SelectArticleForm, SelectArticleForm,
RechargeForm, 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 .payment_methods.forms import payment_method_factory
from .utils import find_payment_method from .utils import find_payment_method
@ -198,8 +199,9 @@ def new_custom_invoice(request):
request.POST or None, request.POST or None,
form_kwargs={'user': request.user} 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() new_invoice_instance = invoice_form.save()
for art_item in articles_formset: for art_item in articles_formset:
if art_item.cleaned_data: if art_item.cleaned_data:
@ -213,6 +215,7 @@ def new_custom_invoice(request):
duration=article.duration, duration=article.duration,
number=quantity number=quantity
) )
discount_form.apply_to_invoice(new_invoice_instance)
messages.success( messages.success(
request, request,
_("The custom invoice was created.") _("The custom invoice was created.")
@ -223,7 +226,8 @@ def new_custom_invoice(request):
'factureform': invoice_form, 'factureform': invoice_form,
'action_name': _("Confirm"), 'action_name': _("Confirm"),
'articlesformset': articles_formset, 'articlesformset': articles_formset,
'articlelist': articles 'articlelist': articles,
'discount_form': discount_form
}, 'cotisations/facture.html', request) }, 'cotisations/facture.html', request)
@ -382,7 +386,7 @@ def custom_invoice_pdf(request, invoice, **_kwargs):
purchases_info = [] purchases_info = []
for purchase in purchases_objects: for purchase in purchases_objects:
purchases_info.append({ purchases_info.append({
'name': purchase.name, 'name': escape_chars(purchase.name),
'price': purchase.prix, 'price': purchase.prix,
'quantity': purchase.number, 'quantity': purchase.number,
'total_price': purchase.prix_total 'total_price': purchase.prix_total