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
d6091d11
Commit
d6091d11
authored
Jul 22, 2018
by
Hugo LEVY-FALK
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Custom invoices.
parent
0527206e
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
499 additions
and
112 deletions
+499
-112
cotisations/forms.py
cotisations/forms.py
+6
-17
cotisations/migrations/0031_custom_invoice.py
cotisations/migrations/0031_custom_invoice.py
+72
-0
cotisations/models.py
cotisations/models.py
+84
-47
cotisations/templates/cotisations/aff_custom_invoice.html
cotisations/templates/cotisations/aff_custom_invoice.html
+108
-0
cotisations/templates/cotisations/index_custom_invoice.html
cotisations/templates/cotisations/index_custom_invoice.html
+36
-0
cotisations/templates/cotisations/sidebar.html
cotisations/templates/cotisations/sidebar.html
+6
-1
cotisations/urls.py
cotisations/urls.py
+23
-3
cotisations/views.py
cotisations/views.py
+155
-43
re2o/utils.py
re2o/utils.py
+8
-0
templates/buttons/add.html
templates/buttons/add.html
+1
-1
No files found.
cotisations/forms.py
View file @
d6091d11
...
...
@@ -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
):
...
...
cotisations/migrations/0031_custom_invoice.py
0 → 100644
View file @
d6091d11
# -*- 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'
),
)
]
cotisations/models.py
View file @
d6091d11
...
...
@@ -55,8 +55,52 @@ from cotisations.utils import find_payment_method
from
cotisations.validators
import
check_no_balance
class
BaseInvoice
(
RevMixin
,
AclMixin
,
FieldPermissionModelMixin
,
models
.
Model
):
date
=
models
.
DateTimeField
(
auto_now_add
=
True
,
verbose_name
=
_l
(
"Date"
)
)
# TODO : change prix to price
def
prix
(
self
):
"""
Returns: the raw price without the quantities.
Deprecated, use :total_price instead.
"""
price
=
Vente
.
objects
.
filter
(
facture
=
self
)
.
aggregate
(
models
.
Sum
(
'prix'
))[
'prix__sum'
]
return
price
# TODO : change prix to price
def
prix_total
(
self
):
"""
Returns: the total price for an invoice. Sum all the articles' prices
and take the quantities into account.
"""
# TODO : change Vente to somethingelse
return
Vente
.
objects
.
filter
(
facture
=
self
)
.
aggregate
(
total
=
models
.
Sum
(
models
.
F
(
'prix'
)
*
models
.
F
(
'number'
),
output_field
=
models
.
FloatField
()
)
)[
'total'
]
or
0
def
name
(
self
):
"""
Returns : a string with the name of all the articles in the invoice.
Used for reprensenting the invoice with a string.
"""
name
=
' - '
.
join
(
Vente
.
objects
.
filter
(
facture
=
self
)
.
values_list
(
'name'
,
flat
=
True
))
return
name
# TODO : change facture to invoice
class
Facture
(
RevMixin
,
AclMixin
,
FieldPermissionModelMixin
,
models
.
Model
):
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).
...
...
@@ -92,10 +136,6 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
blank
=
True
,
verbose_name
=
_l
(
"Cheque number"
)
)
date
=
models
.
DateTimeField
(
auto_now_add
=
True
,
verbose_name
=
_l
(
"Date"
)
)
# TODO : change name to validity for clarity
valid
=
models
.
BooleanField
(
default
=
True
,
...
...
@@ -130,43 +170,6 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
Usefull in history display"""
return
self
.
vente_set
.
all
()
# TODO : change prix to price
def
prix
(
self
):
"""
Returns: the raw price without the quantities.
Deprecated, use :total_price instead.
"""
price
=
Vente
.
objects
.
filter
(
facture
=
self
)
.
aggregate
(
models
.
Sum
(
'prix'
))[
'prix__sum'
]
return
price
# TODO : change prix to price
def
prix_total
(
self
):
"""
Returns: the total price for an invoice. Sum all the articles' prices
and take the quantities into account.
"""
# TODO : change Vente to somethingelse
return
Vente
.
objects
.
filter
(
facture
=
self
)
.
aggregate
(
total
=
models
.
Sum
(
models
.
F
(
'prix'
)
*
models
.
F
(
'number'
),
output_field
=
models
.
FloatField
()
)
)[
'total'
]
or
0
def
name
(
self
):
"""
Returns : a string with the name of all the articles in the invoice.
Used for reprensenting the invoice with a string.
"""
name
=
' - '
.
join
(
Vente
.
objects
.
filter
(
facture
=
self
)
.
values_list
(
'name'
,
flat
=
True
))
return
name
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
(
'
Factur
e'
,
'
BaseInvoic
e'
,
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
.
factur
e
.
user
user
=
invoic
e
.
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
.
factur
e
.
user
.
end_adhesion
()
end_cotisation
=
invoic
e
.
user
.
end_adhesion
()
else
:
end_cotisation
=
self
.
factur
e
.
user
.
end_connexion
()
end_cotisation
=
invoic
e
.
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
.
factur
e
.
user
user
=
invoic
e
.
user
user
.
ldap_sync
(
base
=
False
,
access_refresh
=
True
,
mac_refresh
=
False
)
...
...
cotisations/templates/cotisations/aff_custom_invoice.html
0 → 100644
View file @
d6091d11
{% 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 %}
<div
class=
"table-responsive"
>
{% if custom_invoice_list.paginator %}
{% include 'pagination.html' with list=custom_invoice_list %}
{% endif %}
<table
class=
"table table-striped"
>
<thead>
<tr>
<th>
{% trans "Recipient" as tr_recip %}
{% include 'buttons/sort.html' with prefix='invoice' col='user' text=tr_user %}
</th>
<th>
{% trans "Designation" %}
</th>
<th>
{% trans "Total price" %}
</th>
<th>
{% trans "Payment method" as tr_payment_method %}
{% include 'buttons/sort.html' with prefix='invoice' col='payement' text=tr_payment_method %}
</th>
<th>
{% trans "Date" as tr_date %}
{% include 'buttons/sort.html' with prefix='invoice' col='date' text=tr_date %}
</th>
<th>
{% trans "Invoice id" as tr_invoice_id %}
{% include 'buttons/sort.html' with prefix='invoice' col='id' text=tr_invoice_id %}
</th>
<th>
{% trans "Paid" %}
</th>
<th></th>
<th></th>
</tr>
</thead>
{% for invoice in custom_invoice_list %}
<tr>
<td>
{{ invoice.recipient }}
</td>
<td>
{{ invoice.name }}
</td>
<td>
{{ invoice.prix_total }}
</td>
<td>
{{ invoice.payment }}
</td>
<td>
{{ invoice.date }}
</td>
<td>
{{ invoice.id }}
</td>
<td>
{{ invoice.paid }}
</td>
<td>
<div
class=
"dropdown"
>
<button
class=
"btn btn-default dropdown-toggle"
type=
"button"
id=
"editinvoice"
data-toggle=
"dropdown"
aria-haspopup=
"true"
aria-expanded=
"true"
>
{% trans "Edit" %}
<span
class=
"caret"
></span>
</button>
<ul
class=
"dropdown-menu"
aria-labelledby=
"editinvoice"
>
{% can_edit invoice %}
<li>
<a
href=
"{% url 'cotisations:edit-custom-invoice' invoice.id %}"
>
<i
class=
"fa fa-dollar-sign"
></i>
{% trans "Edit" %}
</a>
</li>
{% acl_end %}
{% can_delete invoice %}
<li>
<a
href=
"{% url 'cotisations:del-custom-invoice' invoice.id %}"
>
<i
class=
"fa fa-trash"
></i>
{% trans "Delete" %}
</a>
</li>
{% acl_end %}
<li>
<a
href=
"{% url 'cotisations:history' 'custominvoice' invoice.id %}"
>
<i
class=
"fa fa-history"
></i>
{% trans "Historique" %}
</a>
</li>
</ul>
</div>
</td>
<td>
<a
class=
"btn btn-primary btn-sm"
role=
"button"
href=
"{% url 'cotisations:custom-invoice-pdf' invoice.id %}"
>
<i
class=
"fa fa-file-pdf"
></i>
{% trans "PDF" %}
</a>
</td>
</tr>
{% endfor %}
</table>
{% if custom_invoice_list.paginator %}
{% include 'pagination.html' with list=custom_invoice_list %}
{% endif %}
</div>
cotisations/templates/cotisations/index_custom_invoice.html
0 → 100644
View file @
d6091d11
{% 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 %}
<h2>
{% trans "Custom invoices list" %}
</h2>
{% 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 %}
cotisations/templates/cotisations/sidebar.html
View file @
d6091d11
...
...
@@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% block sidebar %}
{% can_change Facture pdf %}
<a
class=
"list-group-item list-group-item-success"
href=
"{% url "
cotisations:new-
facture-pdf
"
%}"
>
<a
class=
"list-group-item list-group-item-success"
href=
"{% url "
cotisations:new-
custom-invoice
"
%}"
>
<i
class=
"fa fa-plus"
></i>
{% trans "Create an invoice" %}
</a>
<a
class=
"list-group-item list-group-item-warning"
href=
"{% url "
cotisations:control
"
%}"
>
...
...
@@ -40,6 +40,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<i
class=
"fa fa-list-ul"
></i>
{% trans "Invoices" %}
</a>
{% acl_end %}
{% can_view_all CustomInvoice %}
<a
class=
"list-group-item list-group-item-info"
href=
"{% url "
cotisations:index-custom-invoice
"
%}"
>
<i
class=
"fa fa-list-ul"
></i>
{% trans "Custom invoices" %}
</a>
{% acl_end %}
{% can_view_all Article %}
<a
class=
"list-group-item list-group-item-info"
href=
"{% url "
cotisations:index-article
"
%}"
>
<i
class=
"fa fa-list-ul"
></i>
{% trans "Available articles" %}
...
...
cotisations/urls.py
View file @
d6091d11
...
...
@@ -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<custominvoiceid>[0-9]+)$'
,
views
.
edit_custom_invoice
,
name
=
'edit-custom-invoice'
),
url
(
r'^custom_invoice_pdf/(?P<custominvoiceid>[0-9]+)$'
,
views
.
custom_invoice_pdf
,
name
=
'custom-invoice-pdf'
,
),
url
(
r'^del_custom_invoice/(?P<custominvoiceid>[0-9]+)$'
,
views
.
del_custom_invoice
,
name
=
'del-custom-invoice'
),
url
(
r'^credit_solde/(?P<userid>[0-9]+)$'
,
...
...
cotisations/views.py
View file @
d6091d11
...
...
@@ -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_c
hange
(
Facture
,
'pdf'
)
def
new_
facture_pdf
(
request
):
@
can_c
reate
(
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
)
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
)
})
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
form
({
'factureform'
:
invoice_form
,
'action_name'
:
_
(
"Create"
),
'articlesformset'
:
articles_formset
,
'article
s
'
:
articles
'article
list
'
:
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 remove
d
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
)