8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-12-23 07:23:46 +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.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

View file

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

View file

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

View file

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