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
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Container Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Levy--Falk Hugo
Re2o
Commits
d6091d11
Commit
d6091d11
authored
Jul 22, 2018
by
Levy--Falk Hugo
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
forms.py
cotisations/forms.py
+6
-17
0031_custom_invoice.py
cotisations/migrations/0031_custom_invoice.py
+72
-0
models.py
cotisations/models.py
+84
-47
aff_custom_invoice.html
cotisations/templates/cotisations/aff_custom_invoice.html
+108
-0
index_custom_invoice.html
cotisations/templates/cotisations/index_custom_invoice.html
+36
-0
sidebar.html
cotisations/templates/cotisations/sidebar.html
+6
-1
urls.py
cotisations/urls.py
+23
-3
views.py
cotisations/views.py
+155
-43
utils.py
re2o/utils.py
+8
-0
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
...
@@ -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
from
.models
import
Article
,
Paiement
,
Facture
,
Banque
,
CustomInvoice
from
.payment_methods
import
balance
from
.payment_methods
import
balance
...
@@ -131,24 +131,13 @@ class SelectClubArticleForm(Form):
...
@@ -131,24 +131,13 @@ class SelectClubArticleForm(Form):
self
.
fields
[
'article'
]
.
queryset
=
Article
.
find_allowed_articles
(
user
)
self
.
fields
[
'article'
]
.
queryset
=
Article
.
find_allowed_articles
(
user
)
# TODO : change Facture to Invoice
class
CustomInvoiceForm
(
FormRevMixin
,
ModelForm
):
class
NewFactureFormPdf
(
Form
):
"""
"""
Form used to create a custom
PDF
invoice.
Form used to create a custom invoice.
"""
"""
paid
=
forms
.
BooleanField
(
label
=
_l
(
"Paid"
),
required
=
False
)
class
Meta
:
# TODO : change dest field to recipient
model
=
CustomInvoice
dest
=
forms
.
CharField
(
fields
=
'__all__'
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
ArticleForm
(
FormRevMixin
,
ModelForm
):
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
...
@@ -55,8 +55,52 @@ from cotisations.utils import find_payment_method
from
cotisations.validators
import
check_no_balance
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
# 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
The model for an invoice. It reprensents the fact that a user paid for
something (it can be multiple article paid at once).
something (it can be multiple article paid at once).
...
@@ -92,10 +136,6 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
...
@@ -92,10 +136,6 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
blank
=
True
,
blank
=
True
,
verbose_name
=
_l
(
"Cheque number"
)
verbose_name
=
_l
(
"Cheque number"
)
)
)
date
=
models
.
DateTimeField
(
auto_now_add
=
True
,
verbose_name
=
_l
(
"Date"
)
)
# TODO : change name to validity for clarity
# TODO : change name to validity for clarity
valid
=
models
.
BooleanField
(
valid
=
models
.
BooleanField
(
default
=
True
,
default
=
True
,
...
@@ -130,43 +170,6 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
...
@@ -130,43 +170,6 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
Usefull in history display"""
Usefull in history display"""
return
self
.
vente_set
.
all
()
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
):
def
can_edit
(
self
,
user_request
,
*
args
,
**
kwargs
):
if
not
user_request
.
has_perm
(
'cotisations.change_facture'
):
if
not
user_request
.
has_perm
(
'cotisations.change_facture'
):
return
False
,
_
(
"You don't have the right to edit an invoice."
)
return
False
,
_
(
"You don't have the right to edit an invoice."
)
...
@@ -265,6 +268,28 @@ def facture_post_delete(**kwargs):
...
@@ -265,6 +268,28 @@ def facture_post_delete(**kwargs):
user
.
ldap_sync
(
base
=
False
,
access_refresh
=
True
,
mac_refresh
=
False
)
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
# TODO : change Vente to Purchase
class
Vente
(
RevMixin
,
AclMixin
,
models
.
Model
):
class
Vente
(
RevMixin
,
AclMixin
,
models
.
Model
):
"""
"""
...
@@ -288,7 +313,7 @@ class Vente(RevMixin, AclMixin, models.Model):
...
@@ -288,7 +313,7 @@ class Vente(RevMixin, AclMixin, models.Model):
# TODO : change facture to invoice
# TODO : change facture to invoice
facture
=
models
.
ForeignKey
(
facture
=
models
.
ForeignKey
(
'
Factur
e'
,
'
BaseInvoic
e'
,
on_delete
=
models
.
CASCADE
,
on_delete
=
models
.
CASCADE
,
verbose_name
=
_l
(
"Invoice"
)
verbose_name
=
_l
(
"Invoice"
)
)
)
...
@@ -355,6 +380,10 @@ class Vente(RevMixin, AclMixin, models.Model):
...
@@ -355,6 +380,10 @@ class Vente(RevMixin, AclMixin, models.Model):
cotisation_type defined (which means the article sold represents
cotisation_type defined (which means the article sold represents
a cotisation)
a cotisation)
"""
"""
try
:
invoice
=
self
.
facture
.
facture
except
Facture
.
DoesNotExist
:
return
if
not
hasattr
(
self
,
'cotisation'
)
and
self
.
type_cotisation
:
if
not
hasattr
(
self
,
'cotisation'
)
and
self
.
type_cotisation
:
cotisation
=
Cotisation
(
vente
=
self
)
cotisation
=
Cotisation
(
vente
=
self
)
cotisation
.
type_cotisation
=
self
.
type_cotisation
cotisation
.
type_cotisation
=
self
.
type_cotisation
...
@@ -362,7 +391,7 @@ class Vente(RevMixin, AclMixin, models.Model):
...
@@ -362,7 +391,7 @@ class Vente(RevMixin, AclMixin, models.Model):
end_cotisation
=
Cotisation
.
objects
.
filter
(
end_cotisation
=
Cotisation
.
objects
.
filter
(
vente__in
=
Vente
.
objects
.
filter
(
vente__in
=
Vente
.
objects
.
filter
(
facture__in
=
Facture
.
objects
.
filter
(
facture__in
=
Facture
.
objects
.
filter
(
user
=
self
.
factur
e
.
user
user
=
invoic
e
.
user
)
.
exclude
(
valid
=
False
))
)
.
exclude
(
valid
=
False
))
)
.
filter
(
)
.
filter
(
Q
(
type_cotisation
=
'All'
)
|
Q
(
type_cotisation
=
'All'
)
|
...
@@ -371,9 +400,9 @@ class Vente(RevMixin, AclMixin, models.Model):
...
@@ -371,9 +400,9 @@ class Vente(RevMixin, AclMixin, models.Model):
date_start__lt
=
date_start
date_start__lt
=
date_start
)
.
aggregate
(
Max
(
'date_end'
))[
'date_end__max'
]
)
.
aggregate
(
Max
(
'date_end'
))[
'date_end__max'
]
elif
self
.
type_cotisation
==
"Adhesion"
:
elif
self
.
type_cotisation
==
"Adhesion"
:
end_cotisation
=
self
.
factur
e
.
user
.
end_adhesion
()
end_cotisation
=
invoic
e
.
user
.
end_adhesion
()
else
:
else
:
end_cotisation
=
self
.
factur
e
.
user
.
end_connexion
()
end_cotisation
=
invoic
e
.
user
.
end_connexion
()
date_start
=
date_start
or
timezone
.
now
()
date_start
=
date_start
or
timezone
.
now
()
end_cotisation
=
end_cotisation
or
date_start
end_cotisation
=
end_cotisation
or
date_start
date_max
=
max
(
end_cotisation
,
date_start
)
date_max
=
max
(
end_cotisation
,
date_start
)
...
@@ -445,6 +474,10 @@ def vente_post_save(**kwargs):
...
@@ -445,6 +474,10 @@ def vente_post_save(**kwargs):
LDAP user when a purchase has been saved.
LDAP user when a purchase has been saved.
"""
"""
purchase
=
kwargs
[
'instance'
]
purchase
=
kwargs
[
'instance'
]
try
:
purchase
.
facture
.
facture
except
Facture
.
DoesNotExist
:
return
if
hasattr
(
purchase
,
'cotisation'
):
if
hasattr
(
purchase
,
'cotisation'
):
purchase
.
cotisation
.
vente
=
purchase
purchase
.
cotisation
.
vente
=
purchase
purchase
.
cotisation
.
save
()
purchase
.
cotisation
.
save
()
...
@@ -462,8 +495,12 @@ def vente_post_delete(**kwargs):
...
@@ -462,8 +495,12 @@ def vente_post_delete(**kwargs):
Synchronise the LDAP user after a purchase has been deleted.
Synchronise the LDAP user after a purchase has been deleted.
"""
"""
purchase
=
kwargs
[
'instance'
]
purchase
=
kwargs
[
'instance'
]
try
:
invoice
=
purchase
.
facture
.
facture
except
Facture
.
DoesNotExist
:
return
if
purchase
.
type_cotisation
:
if
purchase
.
type_cotisation
:
user
=
purchase
.
factur
e
.
user
user
=
invoic
e
.
user
user
.
ldap_sync
(
base
=
False
,
access_refresh
=
True
,
mac_refresh
=
False
)
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.,
...
@@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% block sidebar %}
{% block sidebar %}
{% can_change Facture pdf %}
{% 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" %}
<i
class=
"fa fa-plus"
></i>
{% trans "Create an invoice" %}
</a>
</a>
<a
class=
"list-group-item list-group-item-warning"
href=
"{% url "
cotisations:control
"
%}"
>
<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.,
...
@@ -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" %}
<i
class=
"fa fa-list-ul"
></i>
{% trans "Invoices" %}
</a>
</a>
{% acl_end %}
{% 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 %}
{% can_view_all Article %}
<a
class=
"list-group-item list-group-item-info"
href=
"{% url "
cotisations:index-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" %}
<i
class=
"fa fa-list-ul"
></i>
{% trans "Available articles" %}
...
...
cotisations/urls.py
View file @
d6091d11
...
@@ -52,9 +52,29 @@ urlpatterns = [
...
@@ -52,9 +52,29 @@ urlpatterns = [
name
=
'facture-pdf'
name
=
'facture-pdf'
),
),
url
(
url
(
r'^new_facture_pdf/$'
,
r'^index_custom_invoice/$'
,
views
.
new_facture_pdf
,
views
.
index_custom_invoice
,
name
=
'new-facture-pdf'
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
(
url
(
r'^credit_solde/(?P<userid>[0-9]+)$'
,
r'^credit_solde/(?P<userid>[0-9]+)$'
,
...
...
cotisations/views.py
View file @
d6091d11
...
@@ -58,7 +58,15 @@ from re2o.acl import (
...
@@ -58,7 +58,15 @@ from re2o.acl import (
can_change
,
can_change
,
)
)
from
preferences.models
import
AssoOption
,
GeneralOption
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
(
from
.forms
import
(
FactureForm
,
FactureForm
,
ArticleForm
,
ArticleForm
,
...
@@ -67,10 +75,10 @@ from .forms import (
...
@@ -67,10 +75,10 @@ from .forms import (
DelPaiementForm
,
DelPaiementForm
,
BanqueForm
,
BanqueForm
,
DelBanqueForm
,
DelBanqueForm
,
NewFactureFormPdf
,
SelectUserArticleForm
,
SelectUserArticleForm
,
SelectClubArticleForm
,
SelectClubArticleForm
,
RechargeForm
RechargeForm
,
CustomInvoiceForm
)
)
from
.tex
import
render_invoice
from
.tex
import
render_invoice
from
.payment_methods.forms
import
payment_method_factory
from
.payment_methods.forms
import
payment_method_factory
...
@@ -178,10 +186,10 @@ def new_facture(request, user, userid):
...
@@ -178,10 +186,10 @@ def new_facture(request, user, userid):
# TODO : change facture to invoice
# TODO : change facture to invoice
@
login_required
@
login_required
@
can_c
hange
(
Facture
,
'pdf'
)
@
can_c
reate
(
CustomInvoice
)
def
new_
facture_pdf
(
request
):
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
get invoices that are not taken into account, for the administrative
point of view.
point of view.
"""
"""
...
@@ -190,7 +198,7 @@ def new_facture_pdf(request):
...
@@ -190,7 +198,7 @@ def new_facture_pdf(request):
Q
(
type_user
=
'All'
)
|
Q
(
type_user
=
request
.
user
.
class_name
)
Q
(
type_user
=
'All'
)
|
Q
(
type_user
=
request
.
user
.
class_name
)
)
)
# Building the invocie form and the article formset
# 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
:
if
request
.
user
.
is_class_club
:
articles_formset
=
formset_factory
(
SelectClubArticleForm
)(
articles_formset
=
formset_factory
(
SelectClubArticleForm
)(
request
.
POST
or
None
,
request
.
POST
or
None
,
...
@@ -202,44 +210,31 @@ def new_facture_pdf(request):
...
@@ -202,44 +210,31 @@ def new_facture_pdf(request):
form_kwargs
=
{
'user'
:
request
.
user
}
form_kwargs
=
{
'user'
:
request
.
user
}
)
)
if
invoice_form
.
is_valid
()
and
articles_formset
.
is_valid
():
if
invoice_form
.
is_valid
()
and
articles_formset
.
is_valid
():
# Get the article list and build an list out of it
new_invoice_instance
=
invoice_form
.
save
()
# contiaining (article_name, article_price, quantity, total_price)
for
art_item
in
articles_formset
:
articles_info
=
[]
if
art_item
.
cleaned_data
:
for
articles_form
in
articles_formset
:
article
=
art_item
.
cleaned_data
[
'article'
]
if
articles_form
.
cleaned_data
:
quantity
=
art_item
.
cleaned_data
[
'quantity'
]
article
=
articles_form
.
cleaned_data
[
'article'
]
Vente
.
objects
.
create
(
quantity
=
articles_form
.
cleaned_data
[
'quantity'
]
facture
=
new_invoice_instance
,
articles_info
.
append
({
name
=
article
.
name
,
'name'
:
article
.
name
,
prix
=
article
.
prix
,
'price'
:
article
.
prix
,
type_cotisation
=
article
.
type_cotisation
,
'quantity'
:
quantity
,
duration
=
article
.
duration
,
'total_price'
:
article
.
prix
*
quantity
number
=
quantity
})
)
paid
=
invoice_form
.
cleaned_data
[
'paid'
]
messages
.
success
(
recipient
=
invoice_form
.
cleaned_data
[
'dest'
]
request
,
address
=
invoice_form
.
cleaned_data
[
'chambre'
]
_
(
'The custom invoice was successfully created.'
)
total_price
=
sum
(
a
[
'total_price'
]
for
a
in
articles_info
)
)
return
redirect
(
reverse
(
'cotisations:index-custom-invoice'
))
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
)
})
return
form
({
return
form
({
'factureform'
:
invoice_form
,
'factureform'
:
invoice_form
,
'action_name'
:
_
(
"Create"
),
'action_name'
:
_
(
"Create"
),
'articlesformset'
:
articles_formset
,
'articlesformset'
:
articles_formset
,
'article
s
'
:
articles
'article
list
'
:
articles
},
'cotisations/facture.html'
,
request
)
},
'cotisations/facture.html'
,
request
)
...
@@ -292,7 +287,7 @@ def facture_pdf(request, facture, **_kwargs):
...
@@ -292,7 +287,7 @@ def facture_pdf(request, facture, **_kwargs):
def
edit_facture
(
request
,
facture
,
**
_kwargs
):
def
edit_facture
(
request
,
facture
,
**
_kwargs
):
"""
"""
View used to edit an existing invoice.
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
can be set as desired. This is also the view used to invalidate