Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
R
re2o
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Nounous
re2o
Commits
e7a7e81a
Commit
e7a7e81a
authored
Dec 29, 2018
by
Hugo LEVY-FALK
Committed by
chirac
Jan 01, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add discount for custom invoices.
parent
81b7a7c4
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
176 additions
and
102 deletions
+176
-102
cotisations/forms.py
cotisations/forms.py
+41
-4
cotisations/templates/cotisations/facture.html
cotisations/templates/cotisations/facture.html
+111
-93
cotisations/tex.py
cotisations/tex.py
+15
-0
cotisations/views.py
cotisations/views.py
+9
-5
No files found.
cotisations/forms.py
View file @
e7a7e81a
...
...
@@ -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
cotisations/templates/cotisations/facture.html
View file @
e7a7e81a
...
...
@@ -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
template
=
`Article :
{% bootstrap_form articlesformset.empty_form label_class='sr-only' %}
<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
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
(
var
prices
=
{};
{
%
for
article
in
articlelist
%
}
prices
[{{
article
.
id
|
escapejs
}}]
=
{{
article
.
prix
}};
{
%
endfor
%
}
var
template
=
`Article :
{% bootstrap_form articlesformset.empty_form label_class='sr-only' %}
<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
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
(
'
id_form-
'
+
i
.
toString
()
+
'
-quantity
'
).
value
;
price
+=
article_price
*
quantity
;
if
(
article
==
''
)
{
continue
;
}
document
.
getElementById
(
'
total_price
'
).
innerHTML
=
price
.
toFixed
(
2
).
toString
().
replace
(
'
.
'
,
'
,
'
);
article_price
=
prices
[
article
];
quantity
=
document
.
getElementById
(
'
id_form-
'
+
i
.
toString
()
+
'
-quantity
'
).
value
;
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
;
}
// 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
();
});
price
-=
discount
;
{
%
endif
%
}
document
.
getElementById
(
'
total_price
'
).
innerHTML
=
price
.
toFixed
(
2
).
toString
().
replace
(
'
.
'
,
'
,
'
);
}
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 %}
...
...
cotisations/tex.py
View file @
e7a7e81a
...
...
@@ -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.
...
...
cotisations/views.py
View file @
e7a7e81a
...
...
@@ -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
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment