Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
R
re2o
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Nounous
re2o
Commits
d9ebb266
Commit
d9ebb266
authored
Jan 11, 2018
by
Yoann PIETRI
Committed by
root
Jan 28, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Users can pay their own cotisation with their solde.
parent
891014ba
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
358 additions
and
16 deletions
+358
-16
cotisations/forms.py
cotisations/forms.py
+37
-4
cotisations/templates/cotisations/new_facture.html
cotisations/templates/cotisations/new_facture.html
+3
-0
cotisations/templates/cotisations/new_facture_solde.html
cotisations/templates/cotisations/new_facture_solde.html
+157
-0
cotisations/urls.py
cotisations/urls.py
+5
-0
cotisations/views.py
cotisations/views.py
+99
-1
preferences/forms.py
preferences/forms.py
+1
-0
preferences/migrations/0028_auto_20180111_1129.py
preferences/migrations/0028_auto_20180111_1129.py
+20
-0
preferences/models.py
preferences/models.py
+17
-2
preferences/templates/preferences/display_preferences.html
preferences/templates/preferences/display_preferences.html
+7
-0
users/models.py
users/models.py
+3
-5
users/templates/users/profil.html
users/templates/users/profil.html
+9
-4
No files found.
cotisations/forms.py
View file @
d9ebb266
...
...
@@ -26,9 +26,8 @@ importé par les views.
Permet de créer une nouvelle facture pour un user (NewFactureForm),
et de l'editer (soit l'user avec EditFactureForm,
soit le trésorier avec TrezEdit qui a plus de possibilités que self
notamment sur le controle trésorier)
SelectArticleForm est utilisée lors de la creation d'une facture en
notamment sur le controle trésorier SelectArticleForm est utilisée
lors de la creation d'une facture en
parrallèle de NewFacture pour le choix des articles désirés.
(la vue correspondante est unique)
...
...
@@ -40,7 +39,7 @@ from __future__ import unicode_literals
from
django
import
forms
from
django.db.models
import
Q
from
django.forms
import
ModelForm
,
Form
from
django.core.validators
import
MinValueValidator
from
django.core.validators
import
MinValueValidator
,
MaxValueValidator
from
.models
import
Article
,
Paiement
,
Facture
,
Banque
from
re2o.field_permissions
import
FieldPermissionFormMixin
...
...
@@ -246,3 +245,37 @@ class DelBanqueForm(Form):
self
.
fields
[
'banques'
].
queryset
=
instances
else
:
self
.
fields
[
'banques'
].
queryset
=
Banque
.
objects
.
all
()
class
NewFactureSoldeForm
(
NewFactureForm
):
"""Creation d'une facture, moyen de paiement, banque et numero
de cheque"""
def
__init__
(
self
,
*
args
,
**
kwargs
):
prefix
=
kwargs
.
pop
(
'prefix'
,
self
.
Meta
.
model
.
__name__
)
self
.
fields
[
'cheque'
].
required
=
False
self
.
fields
[
'banque'
].
required
=
False
self
.
fields
[
'cheque'
].
label
=
'Numero de chèque'
self
.
fields
[
'banque'
].
empty_label
=
"Non renseigné"
self
.
fields
[
'paiement'
].
empty_label
=
"Séléctionner
\
une bite de paiement"
paiement_list
=
Paiement
.
objects
.
filter
(
type_paiement
=
1
)
if
paiement_list
:
self
.
fields
[
'paiement'
].
widget
\
.
attrs
[
'data-cheque'
]
=
paiement_list
.
first
().
id
class
Meta
:
model
=
Facture
fields
=
[
'paiement'
,
'banque'
]
def
clean
(
self
):
cleaned_data
=
super
(
NewFactureSoldeForm
,
self
).
clean
()
paiement
=
cleaned_data
.
get
(
"paiement"
)
cheque
=
cleaned_data
.
get
(
"cheque"
)
banque
=
cleaned_data
.
get
(
"banque"
)
if
not
paiement
:
raise
forms
.
ValidationError
(
"Le moyen de paiement est obligatoire"
)
elif
paiement
.
type_paiement
==
"check"
and
not
(
cheque
and
banque
):
raise
forms
.
ValidationError
(
"Le numéro de chèque et
\
la banque sont obligatoires."
)
return
cleaned_data
cotisations/templates/cotisations/new_facture.html
View file @
d9ebb266
...
...
@@ -34,6 +34,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<form
class=
"form"
method=
"post"
>
{% csrf_token %}
<h3>
Nouvelle facture
</h3>
<p>
Solde de l'utilisateur : {{ user.solde }} €
</p>
{% bootstrap_form factureform %}
{{ venteform.management_form }}
<!-- TODO: FIXME to include data-type="check" for right option in id_cheque select -->
...
...
cotisations/templates/cotisations/new_facture_solde.html
0 → 100644
View file @
d9ebb266
{% 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 bootstrap3 %}
{% load staticfiles%}
{% block title %}Création et modification de factures{% endblock %}
{% block content %}
{% bootstrap_form_errors venteform.management_form %}
<form
class=
"form"
method=
"post"
>
{% csrf_token %}
<h3>
Nouvelle facture
</h3>
{{ venteform.management_form }}
<!-- TODO: FIXME to include data-type="check" for right option in id_cheque select -->
<h3>
Articles de la facture
</h3>
<div
id=
"form_set"
class=
"form-group"
>
{% for form in venteform.forms %}
<div
class=
'product_to_sell form-inline'
>
Article :
{% bootstrap_form form label_class='sr-only' %}
<button
class=
"btn btn-danger btn-sm"
id=
"id_form-0-article-remove"
type=
"button"
>
<span
class=
"glyphicon glyphicon-remove"
></span>
</button>
</div>
{% endfor %}
</div>
<input
class=
"btn btn-primary btn-sm"
role=
"button"
value=
"Ajouter un article"
id=
"add_one"
>
<p>
Prix total :
<span
id=
"total_price"
>
0,00
</span>
€
</p>
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
</form>
<script
type=
"text/javascript"
>
var
prices
=
{};
{
%
for
article
in
articlelist
%
}
prices
[{{
article
.
id
|
escapejs
}}]
=
{{
article
.
prix
}};
{
%
endfor
%
}
var
template
=
`Article :
{% bootstrap_form venteform.empty_form label_class='sr-only' %}
<button class="btn btn-danger btn-sm"
id="id_form-__prefix__-article-remove" type="button">
<span class="glyphicon glyphicon-remove"></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
;
}
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
();
}
)
}
function
set_cheque_info_visibility
()
{
var
paiement
=
document
.
getElementById
(
"
id_Facture-paiement
"
);
var
visible
=
paiement
.
value
==
paiement
.
getAttribute
(
'
data-cheque
'
);
p
=
document
.
getElementById
(
"
id_Facture-paiement
"
);
var
display
=
'
none
'
;
if
(
visible
)
{
display
=
'
block
'
;
}
document
.
getElementById
(
"
id_Facture-cheque
"
)
.
parentNode
.
style
.
display
=
display
;
document
.
getElementById
(
"
id_Facture-banque
"
)
.
parentNode
.
style
.
display
=
display
;
}
// 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_Facture-paiement
"
)
.
addEventListener
(
"
change
"
,
set_cheque_info_visibility
,
true
);
set_cheque_info_visibility
();
update_price
();
});
</script>
{% endblock %}
cotisations/urls.py
View file @
d9ebb266
...
...
@@ -26,6 +26,7 @@ from django.conf.urls import url
import
re2o
from
.
import
views
from
.
import
payment
urlpatterns
=
[
url
(
r
'^new_facture/(?P<userid>[0-9]+)$'
,
...
...
@@ -110,5 +111,9 @@ urlpatterns = [
views
.
control
,
name
=
'control'
),
url
(
r
'^new_facture_solde/(?P<userid>[0-9]+)$'
,
views
.
new_facture_solde
,
name
=
'new_facture_solde'
),
url
(
r
'^$'
,
views
.
index
,
name
=
'index'
),
]
cotisations/views.py
View file @
d9ebb266
...
...
@@ -29,6 +29,7 @@ import os
from
django.urls
import
reverse
from
django.shortcuts
import
render
,
redirect
from
django.core.paginator
import
Paginator
,
EmptyPage
,
PageNotAnInteger
from
django.core.validators
import
MaxValueValidator
from
django.contrib.auth.decorators
import
login_required
,
permission_required
from
django.contrib
import
messages
from
django.db.models
import
ProtectedError
...
...
@@ -67,7 +68,9 @@ from .forms import (
NewFactureFormPdf
,
SelectUserArticleForm
,
SelectClubArticleForm
,
CreditSoldeForm
CreditSoldeForm
,
NewFactureSoldeForm
,
RechargeForm
)
from
.tex
import
render_invoice
...
...
@@ -584,3 +587,98 @@ def index(request):
return
render
(
request
,
'cotisations/index.html'
,
{
'facture_list'
:
facture_list
})
@
login_required
def
new_facture_solde
(
request
,
userid
):
"""Creation d'une facture pour un user. Renvoie la liste des articles
et crée des factures dans un formset. Utilise un peu de js coté template
pour ajouter des articles.
Parse les article et boucle dans le formset puis save les ventes,
enfin sauve la facture parente.
TODO : simplifier cette fonction, déplacer l'intelligence coté models
Facture et Vente."""
user
=
request
.
user
facture
=
Facture
(
user
=
user
)
paiement
,
_created
=
Paiement
.
objects
.
get_or_create
(
moyen
=
'Solde'
)
facture
.
paiement
=
paiement
# Le template a besoin de connaitre les articles pour le js
article_list
=
Article
.
objects
.
filter
(
Q
(
type_user
=
'All'
)
|
Q
(
type_user
=
request
.
user
.
class_name
)
)
if
request
.
user
.
is_class_club
:
article_formset
=
formset_factory
(
SelectClubArticleForm
)(
request
.
POST
or
None
)
else
:
article_formset
=
formset_factory
(
SelectUserArticleForm
)(
request
.
POST
or
None
)
if
article_formset
.
is_valid
():
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
:
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
(
reverse
(
'users:profil'
,
kwargs
=
{
'userid'
:
userid
}
))
with
transaction
.
atomic
(),
reversion
.
create_revision
():
facture
.
save
()
reversion
.
set_user
(
request
.
user
)
reversion
.
set_comment
(
"Création"
)
for
art_item
in
articles
:
if
art_item
.
cleaned_data
:
article
=
art_item
.
cleaned_data
[
'article'
]
quantity
=
art_item
.
cleaned_data
[
'quantity'
]
new_vente
=
Vente
.
objects
.
create
(
facture
=
facture
,
name
=
article
.
name
,
prix
=
article
.
prix
,
type_cotisation
=
article
.
type_cotisation
,
duration
=
article
.
duration
,
number
=
quantity
)
with
transaction
.
atomic
(),
reversion
.
create_revision
():
new_vente
.
save
()
reversion
.
set_user
(
request
.
user
)
reversion
.
set_comment
(
"Création"
)
if
any
(
art_item
.
cleaned_data
[
'article'
].
type_cotisation
for
art_item
in
articles
if
art_item
.
cleaned_data
):
messages
.
success
(
request
,
"La cotisation a été prolongée
\
pour l'adhérent %s jusqu'au %s"
%
(
user
.
pseudo
,
user
.
end_adhesion
()
)
)
else
:
messages
.
success
(
request
,
"La facture a été crée"
)
return
redirect
(
reverse
(
'users:profil'
,
kwargs
=
{
'userid'
:
userid
}
))
messages
.
error
(
request
,
u
"Il faut au moins un article valide pour créer une facture"
)
return
redirect
(
reverse
(
'users:profil'
,
kwargs
=
{
'userid'
:
userid
}
))
return
form
({
'venteform'
:
article_formset
,
'articlelist'
:
article_list
},
'cotisations/new_facture_solde.html'
,
request
)
preferences/forms.py
View file @
d9ebb266
...
...
@@ -48,6 +48,7 @@ class EditOptionalUserForm(ModelForm):
téléphone'
self
.
fields
[
'user_solde'
].
label
=
'Activation du solde pour
\
les utilisateurs'
self
.
fields
[
'max_recharge'
].
label
=
'Rechargement max'
class
EditOptionalMachineForm
(
ModelForm
):
...
...
preferences/migrations/0028_auto_20180111_1129.py
0 → 100644
View file @
d9ebb266
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2018-01-11 10:29
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'preferences'
,
'0027_merge_20180106_2019'
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
'optionaluser'
,
name
=
'max_recharge'
,
field
=
models
.
DecimalField
(
decimal_places
=
2
,
default
=
100
,
max_digits
=
5
),
),
]
preferences/models.py
View file @
d9ebb266
...
...
@@ -41,6 +41,11 @@ class OptionalUser(models.Model):
decimal_places
=
2
,
default
=
0
)
max_recharge
=
models
.
DecimalField
(
max_digits
=
5
,
decimal_places
=
2
,
default
=
100
)
gpg_fingerprint
=
models
.
BooleanField
(
default
=
True
)
all_can_create
=
models
.
BooleanField
(
default
=
False
,
...
...
@@ -107,7 +112,10 @@ class OptionalUser(models.Model):
def
clean
(
self
):
"""Creation du mode de paiement par solde"""
if
self
.
user_solde
:
cotisations
.
models
.
Paiement
.
objects
.
get_or_create
(
moyen
=
"Solde"
)
p
=
cotisations
.
models
.
Paiement
.
objects
.
filter
(
moyen
=
"Solde"
)
if
not
len
(
p
):
c
=
cotisations
.
models
.
Paiement
(
moyen
=
"Solde"
)
c
.
save
()
class
OptionalMachine
(
models
.
Model
):
...
...
@@ -436,7 +444,14 @@ class AssoOption(models.Model):
blank
=
True
,
null
=
True
)
PAYMENT
=
(
(
'NONE'
,
'NONE'
),
(
'COMNPAY'
,
'COMNPAY'
),
)
payment
=
models
.
CharField
(
max_length
=
255
,
choices
=
PAYMENT
,
default
=
'NONE'
,
)
class
Meta
:
permissions
=
(
(
"view_assooption"
,
"Peut voir les options de l'asso"
),
...
...
preferences/templates/preferences/display_preferences.html
View file @
d9ebb266
...
...
@@ -54,6 +54,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<tr>
<th>
Creations d'users par tous
</th>
<td>
{{ useroptions.all_can_create }}
</td>
{% if useroptions.user_solde %}
<th>
Rechargement max
</th>
<td>
{{ useroptions.max_recharge }}
</td>
{% endif %}
</tr>
</table>
<h4>
Préférences machines
</h4>
...
...
@@ -159,7 +163,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<tr>
<th>
Objet utilisateur de l'association
</th>
<td>
{{ assooptions.utilisateur_asso }}
</td>
<th>
Moyen de paiement automatique
</th>
<td>
{{ assooptions.payment }}
</td>
</tr>
</table>
<h4>
Messages personalisé dans les mails
</h4>
<a
class=
"btn btn-primary btn-sm"
role=
"button"
href=
"{% url 'preferences:edit-options' 'MailMessageOption' %}"
>
...
...
users/models.py
View file @
d9ebb266
...
...
@@ -153,7 +153,7 @@ class UserManager(BaseUserManager):
user
.
set_password
(
password
)
if
su
:
user
.
is_superuser
=
True
user
.
save
(
using
=
self
.
_db
)
user
.
save
(
using
=
self
.
_db
)
return
user
def
create_user
(
self
,
pseudo
,
surname
,
email
,
password
=
None
):
...
...
@@ -409,13 +409,11 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin):
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'
)
solde_objects
=
Paiement
.
objects
.
filter
(
moyen
=
'Solde'
)
somme_debit
=
Vente
.
objects
.
filter
(
facture__in
=
Facture
.
objects
.
filter
(
user
=
self
,
paiement
=
solde_object
paiement
__in
=
solde_objects
)
).
aggregate
(
total
=
models
.
Sum
(
...
...
users/templates/users/profil.html
View file @
d9ebb266
...
...
@@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% block title %}Profil{% endblock %}
{% block content %}
<h2>
{{ users.class_name }}
</h2>
<h2>
{{ users.class_name }}
: {{ users.surname }} {{users.name}}
</h2>
<div>
<a
class=
"btn btn-primary btn-sm"
role=
"button"
href=
"{% url 'users:edit-info' users.id %}"
>
<i
class=
"glyphicon glyphicon-edit"
></i>
...
...
@@ -135,13 +135,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% if user_solde %}
<tr>
<th>
Solde
</th>
<td>
{{ users.solde }} €
</td>
</tr>
<td>
{{ users.solde }} €
<a
class=
"btn btn-primary btn-sm"
style=
'float:right'
role=
"button"
href=
"{% url 'cotisations:recharge' %}"
>
<i
class=
"glyphicon glyphicon-piggy-bank"
></i>
Recharger
</a>
</td>
{% endif %}
{% if users.shell %}
<th>
Shell
</th>
<td>
{{ users.shell }}
</td>
{% endif %}
</tr>
</table>
{% if users.is_class_club %}
<a
class=
"btn btn-primary btn-sm"
role=
"button"
href=
"{% url 'users:edit-club-admin-members' users.club.id %}"
>
...
...
@@ -191,7 +196,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<p>
Aucune machine
</p>
{% endif %}
<h2>
Cotisations
</h2>
<h4>
{% can_create Facture %}
<a
class=
"btn btn-primary btn-sm"
role=
"button"
href=
"{% url 'cotisations:new-facture' users.id %}"
><i
class=
"glyphicon glyphicon-piggy-bank"
></i>
Ajouter une cotisation
</a>
{% acl_end %} {% if user_solde %}
<a
class=
"btn btn-primary btn-sm"
role=
"button"
href=
"{% url 'cotisations:credit-solde' users.id %}"
><i
class=
"glyphicon glyphicon-piggy-bank"
></i>
Modifier le solde
</a>
{% endif
%}
</h4>
<h4>
{% can_create Facture %}
<a
class=
"btn btn-primary btn-sm"
role=
"button"
href=
"{% url 'cotisations:new-facture' users.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' users.id %}"
><i
class=
"glyphicon glyphicon-piggy-bank"
></i>
Modifier le solde
</a>
{% endif%}{% acl_else %}
<a
class=
"btn btn-primary btn-sm"
role=
"button"
href=
"{% url 'cotisations:new_facture_solde' user.id %}"
><i
class=
"glyphicon glyphicon-piggy-bank"
></i>
Ajouter une cotisation par solde
</a>
{% acl_end
%}
</h4>
{% if facture_list %}
{% include "cotisations/aff_cotisations.html" with facture_list=facture_list %}
{% else %}
...
...
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