Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Nounous
re2o
Commits
e968f2b1
Commit
e968f2b1
authored
Jun 26, 2017
by
chibrac
Browse files
Gestion du solde en option
parent
6f9932ad
Changes
9
Hide whitespace changes
Inline
Side-by-side
cotisations/forms.py
View file @
e968f2b1
...
...
@@ -46,10 +46,22 @@ class NewFactureForm(ModelForm):
banque
=
cleaned_data
.
get
(
"banque"
)
if
not
paiement
:
raise
forms
.
ValidationError
(
"Le moyen de paiement est obligatoire"
)
elif
paiement
.
moyen
==
"chè
que"
and
not
(
cheque
and
banque
):
elif
paiement
.
moyen
.
lower
()
==
"chèque"
or
paiement
.
moyen
.
lower
()
==
"che
que"
and
not
(
cheque
and
banque
):
raise
forms
.
ValidationError
(
"Le numero de chèque et la banque sont obligatoires"
)
return
cleaned_data
class
CreditSoldeForm
(
NewFactureForm
):
class
Meta
(
NewFactureForm
.
Meta
):
model
=
Facture
fields
=
[
'paiement'
,
'banque'
,
'cheque'
]
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
CreditSoldeForm
,
self
).
__init__
(
*
args
,
**
kwargs
)
self
.
fields
[
'paiement'
].
queryset
=
Paiement
.
objects
.
exclude
(
moyen
=
'solde'
).
exclude
(
moyen
=
"Solde"
)
montant
=
forms
.
DecimalField
(
max_digits
=
5
,
decimal_places
=
2
,
required
=
True
)
class
SelectArticleForm
(
Form
):
article
=
forms
.
ModelChoiceField
(
queryset
=
Article
.
objects
.
all
(),
label
=
"Article"
,
required
=
True
)
quantity
=
forms
.
IntegerField
(
label
=
"Quantité"
,
validators
=
[
MinValueValidator
(
1
)],
required
=
True
)
...
...
cotisations/models.py
View file @
e968f2b1
...
...
@@ -25,8 +25,10 @@ from django.db import models
from
django.db.models.signals
import
post_save
,
post_delete
from
django.dispatch
import
receiver
from
dateutil.relativedelta
import
relativedelta
from
django.forms
import
ValidationError
from
django.core.validators
import
MinValueValidator
class
Facture
(
models
.
Model
):
PRETTY_NAME
=
"Factures émises"
...
...
@@ -107,6 +109,11 @@ class Article(models.Model):
iscotisation
=
models
.
BooleanField
()
duration
=
models
.
IntegerField
(
help_text
=
"Durée exprimée en mois entiers"
,
blank
=
True
,
null
=
True
)
def
clean
(
self
):
if
self
.
name
.
lower
()
==
"solde"
:
raise
ValidationError
(
"Solde est un nom d'article invalide"
)
def
__str__
(
self
):
return
self
.
name
...
...
@@ -126,6 +133,9 @@ class Paiement(models.Model):
def
__str__
(
self
):
return
self
.
moyen
def
clean
(
self
):
self
.
moyen
=
self
.
moyen
.
title
()
class
Cotisation
(
models
.
Model
):
PRETTY_NAME
=
"Cotisations"
...
...
cotisations/urls.py
View file @
e968f2b1
...
...
@@ -30,6 +30,7 @@ urlpatterns = [
url
(
r
'^del_facture/(?P<factureid>[0-9]+)$'
,
views
.
del_facture
,
name
=
'del-facture'
),
url
(
r
'^facture_pdf/(?P<factureid>[0-9]+)$'
,
views
.
facture_pdf
,
name
=
'facture-pdf'
),
url
(
r
'^new_facture_pdf/$'
,
views
.
new_facture_pdf
,
name
=
'new-facture-pdf'
),
url
(
r
'^credit_solde/(?P<userid>[0-9]+)$'
,
views
.
credit_solde
,
name
=
'credit-solde'
),
url
(
r
'^add_article/$'
,
views
.
add_article
,
name
=
'add-article'
),
url
(
r
'^edit_article/(?P<articleid>[0-9]+)$'
,
views
.
edit_article
,
name
=
'edit-article'
),
url
(
r
'^del_article/$'
,
views
.
del_article
,
name
=
'del-article'
),
...
...
cotisations/views.py
View file @
e968f2b1
...
...
@@ -38,12 +38,12 @@ from reversion import revisions as reversion
from
reversion.models
import
Version
from
.models
import
Facture
,
Article
,
Vente
,
Cotisation
,
Paiement
,
Banque
from
.forms
import
NewFactureForm
,
TrezEditFactureForm
,
EditFactureForm
,
ArticleForm
,
DelArticleForm
,
PaiementForm
,
DelPaiementForm
,
BanqueForm
,
DelBanqueForm
,
NewFactureFormPdf
,
SelectArticleForm
from
.forms
import
NewFactureForm
,
TrezEditFactureForm
,
EditFactureForm
,
ArticleForm
,
DelArticleForm
,
PaiementForm
,
DelPaiementForm
,
BanqueForm
,
DelBanqueForm
,
NewFactureFormPdf
,
CreditSoldeForm
,
SelectArticleForm
from
users.models
import
User
from
.tex
import
render_tex
from
re2o.settings
import
ASSO_NAME
,
ASSO_ADDRESS_LINE1
,
ASSO_ADDRESS_LINE2
,
ASSO_SIRET
,
ASSO_EMAIL
,
ASSO_PHONE
,
LOGO_PATH
from
re2o
import
settings
from
preferences.models
import
GeneralOption
from
preferences.models
import
OptionalUser
,
GeneralOption
from
dateutil.relativedelta
import
relativedelta
from
django.utils
import
timezone
...
...
@@ -87,6 +87,19 @@ def new_facture(request, userid):
articles
=
article_formset
# Si au moins un article est rempli
if
any
(
art
.
cleaned_data
for
art
in
articles
):
options
,
created
=
OptionalUser
.
objects
.
get_or_create
()
user_solde
=
options
.
user_solde
solde_negatif
=
options
.
solde_negatif
# Si on paye par solde, que l'option est activée, on vérifie que le négatif n'est pas atteint
if
user_solde
:
if
new_facture
.
paiement
==
Paiement
.
objects
.
get_or_create
(
moyen
=
'solde'
)[
0
]:
prix_total
=
0
for
art_item
in
articles
:
if
art_item
.
cleaned_data
:
prix_total
+=
art_item
.
cleaned_data
[
'article'
].
prix
*
art_item
.
cleaned_data
[
'quantity'
]
if
float
(
user
.
solde
)
-
float
(
prix_total
)
<
solde_negatif
:
messages
.
error
(
request
,
"Le solde est insuffisant pour effectuer l'opération"
)
return
redirect
(
"/users/profil/"
+
userid
)
with
transaction
.
atomic
(),
reversion
.
create_revision
():
new_facture
.
save
()
reversion
.
set_user
(
request
.
user
)
...
...
@@ -195,6 +208,33 @@ def del_facture(request, factureid):
return
redirect
(
"/cotisations/"
)
return
form
({
'objet'
:
facture
,
'objet_name'
:
'facture'
},
'cotisations/delete.html'
,
request
)
@
login_required
@
permission_required
(
'cableur'
)
def
credit_solde
(
request
,
userid
):
""" Credit ou débit de solde """
try
:
user
=
User
.
objects
.
get
(
pk
=
userid
)
except
User
.
DoesNotExist
:
messages
.
error
(
request
,
u
"Utilisateur inexistant"
)
return
redirect
(
"/cotisations/"
)
facture
=
CreditSoldeForm
(
request
.
POST
or
None
)
if
facture
.
is_valid
():
facture_instance
=
facture
.
save
(
commit
=
False
)
with
transaction
.
atomic
(),
reversion
.
create_revision
():
facture_instance
.
user
=
user
facture_instance
.
save
()
reversion
.
set_user
(
request
.
user
)
reversion
.
set_comment
(
"Création"
)
new_vente
=
Vente
.
objects
.
create
(
facture
=
facture_instance
,
name
=
"solde"
,
prix
=
facture
.
cleaned_data
[
'montant'
],
iscotisation
=
False
,
duration
=
0
,
number
=
1
)
with
transaction
.
atomic
(),
reversion
.
create_revision
():
new_vente
.
save
()
reversion
.
set_user
(
request
.
user
)
reversion
.
set_comment
(
"Création"
)
messages
.
success
(
request
,
"Solde modifié"
)
return
redirect
(
"/cotisations/"
)
return
form
({
'factureform'
:
facture
},
'cotisations/facture.html'
,
request
)
@
login_required
@
permission_required
(
'trésorier'
)
def
add_article
(
request
):
...
...
preferences/migrations/0003_optionaluser_solde_negatif.py
0 → 100644
View file @
e968f2b1
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-06-26 01:33
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'preferences'
,
'0002_auto_20170625_1923'
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
'optionaluser'
,
name
=
'solde_negatif'
,
field
=
models
.
DecimalField
(
decimal_places
=
2
,
default
=
0
,
max_digits
=
5
),
),
]
preferences/models.py
View file @
e968f2b1
...
...
@@ -22,13 +22,18 @@
from
django.db
import
models
from
cotisations.models
import
Paiement
class
OptionalUser
(
models
.
Model
):
is_tel_mandatory
=
models
.
BooleanField
(
default
=
True
)
user_solde
=
models
.
BooleanField
(
default
=
False
)
solde_negatif
=
models
.
DecimalField
(
max_digits
=
5
,
decimal_places
=
2
,
default
=
0
)
gpg_fingerprint
=
models
.
BooleanField
(
default
=
True
)
def
clean
(
self
):
if
self
.
user_solde
:
Paiement
.
objects
.
get_or_create
(
moyen
=
"Solde"
)
class
OptionalMachine
(
models
.
Model
):
password_machine
=
models
.
BooleanField
(
default
=
False
)
max_lambdauser_interfaces
=
models
.
IntegerField
(
default
=
10
)
...
...
users/models.py
View file @
e968f2b1
...
...
@@ -40,7 +40,7 @@ from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from
django.core.validators
import
MinLengthValidator
from
topologie.models
import
Room
from
cotisations.models
import
Cotisation
,
Facture
,
Vente
from
cotisations.models
import
Cotisation
,
Facture
,
Paiement
,
Vente
from
machines.models
import
Interface
,
Machine
from
preferences.models
import
OptionalUser
...
...
@@ -312,6 +312,18 @@ class User(AbstractBaseUser):
else
:
return
max
(
self
.
end_adhesion
,
self
.
end_whitelist
)
@
cached_property
def
solde
(
self
):
options
,
created
=
OptionalUser
.
objects
.
get_or_create
()
user_solde
=
options
.
user_solde
if
user_solde
:
solde_object
,
created
=
Paiement
.
objects
.
get_or_create
(
moyen
=
'Solde'
)
somme_debit
=
Vente
.
objects
.
filter
(
facture__in
=
Facture
.
objects
.
filter
(
user
=
self
,
paiement
=
solde_object
)).
aggregate
(
total
=
models
.
Sum
(
models
.
F
(
'prix'
)
*
models
.
F
(
'number'
),
output_field
=
models
.
FloatField
()))[
'total'
]
or
0
somme_credit
=
Vente
.
objects
.
filter
(
facture__in
=
Facture
.
objects
.
filter
(
user
=
self
),
name
=
"solde"
).
aggregate
(
total
=
models
.
Sum
(
models
.
F
(
'prix'
)
*
models
.
F
(
'number'
),
output_field
=
models
.
FloatField
()))[
'total'
]
or
0
return
somme_credit
-
somme_debit
else
:
return
0
def
user_interfaces
(
self
):
return
Interface
.
objects
.
filter
(
machine__in
=
Machine
.
objects
.
filter
(
user
=
self
,
active
=
True
))
...
...
users/templates/users/profil.html
View file @
e968f2b1
...
...
@@ -81,7 +81,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<th>
Commentaire
</th>
<td>
{{ user.comment }}
</td>
</tr>
<tr>
<th>
Date d'inscription
</th>
<td>
{{ user.registered }}
</td>
...
...
@@ -130,6 +129,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% else %}
<td>
Aucun
</td>
{% endif %}
</tr>
{% if user_solde %}
<tr>
<th>
Solde
</th>
<td>
{{ user.solde }} €
</td>
</tr>
{% endif %}
</table>
<h2>
Machines :
</h2>
<h4><a
class=
"btn btn-primary btn-sm"
role=
"button"
href=
"{% url 'machines:new-machine' user.id %}"
><i
class=
"glyphicon glyphicon-phone"
></i>
Ajouter une machine
</a></h4>
...
...
@@ -139,7 +145,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<p>
Aucune machine
</p>
{% endif %}
<h2>
Cotisations :
</h2>
{% if is_cableur %}
<h4><a
class=
"btn btn-primary btn-sm"
role=
"button"
href=
"{% url 'cotisations:new-facture' user.id %}"
><i
class=
"glyphicon glyphicon-piggy-bank"
></i>
Ajouter une cotisation
</a></h4>
{% endif%}
{% if is_cableur %}
<h4><a
class=
"btn btn-primary btn-sm"
role=
"button"
href=
"{% url 'cotisations:new-facture' user.id %}"
><i
class=
"glyphicon glyphicon-piggy-bank"
></i>
Ajouter une cotisation
</a>
{% if user_solde %}
<a
class=
"btn btn-primary btn-sm"
role=
"button"
href=
"{% url 'cotisations:credit-solde' user.id %}"
><i
class=
"glyphicon glyphicon-piggy-bank"
></i>
Modifier le solde
</a>
{% endif%}
</h4>
{% endif%}
{% if facture_list %}
{% include "cotisations/aff_cotisations.html" with facture_list=facture_list %}
{% else %}
...
...
users/views.py
View file @
e968f2b1
...
...
@@ -45,7 +45,7 @@ from cotisations.models import Facture
from
machines.models
import
Machine
,
Interface
from
users.forms
import
MassArchiveForm
,
PassForm
,
ResetPasswordForm
from
machines.views
import
unassign_ips
,
assign_ips
from
preferences.models
import
GeneralOption
from
preferences.models
import
OptionalUser
,
GeneralOption
from
re2o.login
import
hashNT
from
re2o.settings
import
REQ_EXPIRE_STR
,
EMAIL_FROM
,
ASSO_NAME
,
ASSO_EMAIL
,
SITE_NAME
...
...
@@ -694,6 +694,8 @@ def profil(request, userid):
bans
=
Ban
.
objects
.
filter
(
user__pseudo
=
users
)
whitelists
=
Whitelist
.
objects
.
filter
(
user__pseudo
=
users
)
list_droits
=
Right
.
objects
.
filter
(
user
=
users
)
options
,
created
=
OptionalUser
.
objects
.
get_or_create
()
user_solde
=
options
.
user_solde
return
render
(
request
,
'users/profil.html'
,
...
...
@@ -704,6 +706,7 @@ def profil(request, userid):
'ban_list'
:
bans
,
'white_list'
:
whitelists
,
'list_droits'
:
list_droits
,
'user_solde'
:
user_solde
,
}
)
...
...
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