3
0
Fork 0
mirror of https://github.com/nanoy42/coope synced 2024-12-25 00:13:46 +00:00

Merge branch 'release-3.2.0' into 'master'

Release 3.2.0

Closes #26, #28, #24, #1, and #18

See merge request coope/coopeV3!3
This commit is contained in:
Nanoy 2019-01-06 06:20:31 +01:00
commit 350027042e
27 changed files with 388 additions and 87 deletions

View file

@ -1,3 +1,10 @@
## v3.2.0
* Ajout d'icônes (avec font awesome)
* Amélioration du diagramme sur le profil (couleurs, valeur pour les fromages et charcuts, seuil des 1%)
* Boutons flottants sur la page de transation (avec options pour activer ou désactiver)
* Ajout du module comptabilité (génération de relevé entre deux dates)
* Exportation en csv par groupe
* Liens pour ajouter/retirer des admins/superusers enlevés sur le profil
## v3.1.0 ## v3.1.0
* Tronque la quantité d'alcool ingéré sur le profil (fix #8) * Tronque la quantité d'alcool ingéré sur le profil (fix #8)
* La modification des produits retourne sur la pge de profil du produit (fix #9) * La modification des produits retourne sur la pge de profil du produit (fix #9)
@ -18,4 +25,4 @@
## v3.0.1 ## v3.0.1
* Fix page inactive * Fix page inactive
* Fix prix dans les historiques de consommations * Fix prix dans les historiques de consommations

View file

@ -38,6 +38,7 @@ INSTALLED_APPS = [
'dal', 'dal',
'dal_select2', 'dal_select2',
'simple_history', 'simple_history',
'django_tex',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -68,6 +69,14 @@ TEMPLATES = [
], ],
}, },
}, },
{
'NAME': 'tex',
'BACKEND': 'django_tex.engine.TeXEngine',
'APP_DIRS': True,
'OPTIONS': {
'environment': 'gestion.environment.my_environment',
}
},
] ]
WSGI_APPLICATION = 'coopeV3.wsgi.application' WSGI_APPLICATION = 'coopeV3.wsgi.application'

12
gestion/environment.py Normal file
View file

@ -0,0 +1,12 @@
from django_tex.environment import environment
def latex_safe(value):
return str(value).replace('_', '\_').replace('$', '\$').replace('&', '\&').replace('#', '\#').replace('{', '\{').replace('}','\}')
def my_environment(**options):
env = environment(**options)
env.filters.update({
'latex_safe': latex_safe
})
return env

View file

@ -66,4 +66,8 @@ class SelectActiveKegForm(forms.Form):
class PinteForm(forms.Form): class PinteForm(forms.Form):
ids = forms.CharField(widget=forms.Textarea, label="Numéros", help_text="Numéros séparés par un espace. Laissez vide pour utiliser le range.", required=False) ids = forms.CharField(widget=forms.Textarea, label="Numéros", help_text="Numéros séparés par un espace. Laissez vide pour utiliser le range.", required=False)
begin = forms.IntegerField(label="Début", help_text="Début du range", required=False) begin = forms.IntegerField(label="Début", help_text="Début du range", required=False)
end = forms.IntegerField(label="Fin", help_text="Fin du range", required=False) end = forms.IntegerField(label="Fin", help_text="Fin du range", required=False)
class GenerateReleveForm(forms.Form):
begin = forms.DateTimeField(label="Date de début")
end = forms.DateTimeField(label="Date de fin")

View file

@ -0,0 +1,23 @@
# Generated by Django 2.1 on 2019-01-05 23:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gestion', '0004_auto_20181223_1830'),
]
operations = [
migrations.AddField(
model_name='historicalproduct',
name='showingMultiplier',
field=models.PositiveIntegerField(default=1),
),
migrations.AddField(
model_name='product',
name='showingMultiplier',
field=models.PositiveIntegerField(default=1),
),
]

View file

@ -39,6 +39,7 @@ class Product(models.Model):
volume = models.PositiveIntegerField(default=0) volume = models.PositiveIntegerField(default=0)
deg = models.DecimalField(default=0,max_digits=5, decimal_places=2, verbose_name="Degré", validators=[MinValueValidator(0)]) deg = models.DecimalField(default=0,max_digits=5, decimal_places=2, verbose_name="Degré", validators=[MinValueValidator(0)])
adherentRequired = models.BooleanField(default=True, verbose_name="Adhérent requis") adherentRequired = models.BooleanField(default=True, verbose_name="Adhérent requis")
showingMultiplier = models.PositiveIntegerField(default=1)
history = HistoricalRecords() history = HistoricalRecords()
def __str__(self): def __str__(self):

View file

@ -13,10 +13,31 @@
{% block content %} {% block content %}
{% if floating_buttons %}
<div class="alt_payment_buttons">
{% for pm in pay_buttons %}
<button class="button small pay_button" data-payment="{{pm.pk}}"><i class="fa fa-{{pm.icon}}"></i></button><br>
{% endfor %}
</div>
{% endif %}
<a class="up_button" href="#intro"> <a class="up_button" href="#intro">
UP UP
</a> </a>
<style> <style>
.alt_payment_buttons{
display:block;
background-color:white;
position:fixed;
right:0;
padding-right: 1em;
top:50%;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
.alt_payment_buttons button{
min-width: 0 !important;
background-color: white !important;
}
.up_button{ .up_button{
display:block; display:block;
background-color:white; background-color:white;
@ -28,7 +49,7 @@
text-align:center; text-align:center;
line-height:50px; line-height:50px;
right:1em; right:1em;
bottom : 1em; bottom : 1em;
} }
</style> </style>
{% if perms.gestion.add_consumptionhistory %} {% if perms.gestion.add_consumptionhistory %}
@ -63,7 +84,7 @@
<td id="balance">0€</td> <td id="balance">0€</td>
<td id="totalAmount">0€</td> <td id="totalAmount">0€</td>
<td id="totalAfter">0€</td> <td id="totalAfter">0€</td>
<td>{% for pm in pay_buttons %}<button class="btn small pay_button" data-payment="{{pm.pk}}">{{pm.name}}</button> {% endfor %}</td> <td>{% for pm in pay_buttons %}<button class="btn small pay_button" data-payment="{{pm.pk}}"><i class="fa fa-{{pm.icon}}"></i> {{pm.name}}</button> {% endfor %}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View file

@ -0,0 +1,71 @@
\documentclass[11pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[french]{babel}
\usepackage[T1]{fontenc}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}
\usepackage{graphicx}
\usepackage{eurosym}
\usepackage[left=2cm,right=2cm,top=2cm,bottom=2cm]{geometry}
\usepackage{tabularx}
\usepackage{longtable}
\usepackage{tabu}
\author{Généré par CoopeV3}
\title{Relevé Coopé Technopôle Metz}
\begin{document}
\maketitle
\section{Informations générales}
\begin{longtabu}{|X|X|X|}
\hline
\multicolumn{2}{|c|}{Généré le } & \textbf{ {{- now | date('d/m/Y H:i:s') -}} }\\
\hline
Infos & De & \textbf{ {{- begin | date('d/m/Y H:i:s')-}} } \\
\cline{2-3} & À & \textbf{ {{- end | date('d/m/Y H:i:s') -}} }\\
\hline
Estimations & Espèces & \textbf{ {{- value_especes | latex_safe -}} \euro{}} \\
\cline{2-3} & Lydia & \textbf{ {{- value_lydia | latex_safe -}} \euro{}} \\
\cline{2-3} & Chèques & \textbf{ {{- value_cheque | latex_safe -}} \euro{}} \\
\hline
\end{longtabu}
\section{Transactions}
\begin{longtabu}{|c|X|X|X|X|X|}
\hline
\# & Date & Client & Montant & Moyen de paiement & Produit (Qté) \\
\hline
{% for consumption in consumptions %}
{{consumption.pk}} & {{consumption.date | date('d/m/Y H:i:s')}} & {{consumption.customer.first_name|latex_safe}} {{consumption.customer.last_name|latex_safe}} & {{consumption.amount}} \euro{} & {{consumption.paymentMethod}} & {{consumption.product}} (x{{consumption.quantity}})\\
\hline
{% endfor %}
\end{longtabu}
\section{Rechargements}
\begin{longtabu}{|c|X|X|X|X|}
\hline
\# & Date & Client & Montant & Moyen de paiement \\
\hline
{% for reload in reloads %}
{{reload.pk}} & {{ reload.date | date('d/m/Y H:i:s')}} & {{reload.customer.first_name | latex_safe}} {{reload.customer.last_name | latex_safe}} & {{ reload.amount }} \euro{} & {{reload.PaymentMethod}} \\
\hline
{% endfor %}
\end{longtabu}
\section{Remboursement}
\begin{longtabu}{|c|X|X|X|}
\hline
\# & Date & Client & Montant\\
\hline
{% for refund in refunds %}
{{refund.pk}} & {{ refund.date | date('d/m/Y H:i:s')}} & {{refund.customer.first_name|latex_safe}} {{refund.customer.last_name|latex_safe}} & {{ refund.amount }} \euro{}\\
\hline
{% endfor %}
\end{longtabu}
\section{Cotisations}
\begin{longtabu}{|c|X|X|X|X|X|}
\hline
\# & Date & Client & Montant & Durée & Moyen de paiement \\
\hline
{% for cot in cotisations %}
{{cot.pk}} & {{ cot.paymentDate | date('d/m/Y H:i:s')}} & {{cot.user.first_name|latex_safe}} {{cot.user.last_name|latex_safe}} & {{cot.amount}} \euro{} & {{cot.duration}} jours & {{cot.paymentMethod}} \\
\hline
{% endfor %}
\end{longtabu}
\end{document}

View file

@ -44,4 +44,5 @@ urlpatterns = [
path('kegs-active-autocomplete', views.KegActiveAutocomplete.as_view(), name="kegs-active-autocomplete"), path('kegs-active-autocomplete', views.KegActiveAutocomplete.as_view(), name="kegs-active-autocomplete"),
path('menus-autcomplete', views.MenusAutocomplete.as_view(), name="menus-autocomplete"), path('menus-autcomplete', views.MenusAutocomplete.as_view(), name="menus-autocomplete"),
path('cancelReload/<int:pk>', views.cancel_reload, name="cancelReload"), path('cancelReload/<int:pk>', views.cancel_reload, name="cancelReload"),
path('gen_releve', views.gen_releve, name="gen_releve"),
] ]

View file

@ -8,15 +8,19 @@ from django.contrib.auth.decorators import login_required, permission_required
from django.utils import timezone from django.utils import timezone
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from coopeV3.acl import active_required, acl_or from django_tex.views import render_to_pdf
from coopeV3.acl import active_required, acl_or, admin_required
import simplejson as json import simplejson as json
from dal import autocomplete from dal import autocomplete
from decimal import * from decimal import *
import datetime
from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm, SearchMenuForm, SearchProductForm, SelectPositiveKegForm, SelectActiveKegForm, PinteForm from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm, SearchMenuForm, SearchProductForm, SelectPositiveKegForm, SelectActiveKegForm, PinteForm, GenerateReleveForm
from .models import Product, Menu, Keg, ConsumptionHistory, KegHistory, Consumption, MenuHistory, Pinte, Reload from .models import Product, Menu, Keg, ConsumptionHistory, KegHistory, Consumption, MenuHistory, Pinte, Reload, Refund
from preferences.models import PaymentMethod, GeneralPreferences from preferences.models import PaymentMethod, GeneralPreferences
from users.models import CotisationHistory
@active_required @active_required
@login_required @login_required
@ -26,10 +30,10 @@ def manage(request):
Display the manage page Display the manage page
**Context** **Context**
``gestion_form`` ``gestion_form``
The manage form The manage form
``reload_form`` ``reload_form``
The :model:`gestion.Reload` form The :model:`gestion.Reload` form
@ -44,13 +48,13 @@ def manage(request):
``panini`` ``panini``
A list of active :model:`gestion.Product` corresponding to panini items A list of active :model:`gestion.Product` corresponding to panini items
``food`` ``food``
A list of active :model:`gestion.Product` corresponding to non-panini items A list of active :model:`gestion.Product` corresponding to non-panini items
``soft`` ``soft``
A list of active :model:`gestion.Product` correspond to non alcoholic beverage A list of active :model:`gestion.Product` correspond to non alcoholic beverage
``menus`` ``menus``
The list of active :model:`gestion.Menu` The list of active :model:`gestion.Menu`
@ -72,6 +76,8 @@ def manage(request):
soft = Product.objects.filter(category=Product.SOFT).filter(is_active=True) soft = Product.objects.filter(category=Product.SOFT).filter(is_active=True)
menus = Menu.objects.filter(is_active=True) menus = Menu.objects.filter(is_active=True)
kegs = Keg.objects.filter(is_active=True) kegs = Keg.objects.filter(is_active=True)
gp, _ = GeneralPreferences.objects.get_or_create(pk=1)
floating_buttons = gp.floating_buttons
for keg in kegs: for keg in kegs:
if(keg.pinte): if(keg.pinte):
bieresPression.append(keg.pinte) bieresPression.append(keg.pinte)
@ -89,7 +95,8 @@ def manage(request):
"food": food, "food": food,
"soft": soft, "soft": soft,
"menus": menus, "menus": menus,
"pay_buttons": pay_buttons "pay_buttons": pay_buttons,
"floating_buttons": floating_buttons,
}) })
@csrf_exempt @csrf_exempt
@ -288,7 +295,7 @@ def cancel_menu(request, pk):
for product in manu_history.menu.articles: for product in manu_history.menu.articles:
consumptionT = Consumption.objects.get(customer=user, product=product) consumptionT = Consumption.objects.get(customer=user, product=product)
consumptionT -= menu_history.quantity consumptionT -= menu_history.quantity
consumptionT.save() consumptionT.save()
menu_history.delete() menu_history.delete()
messages.success(request, "La consommation du menu a bien été annulée") messages.success(request, "La consommation du menu a bien été annulée")
return redirect(reverse('users:profile', kwargs={'pk': user.pk})) return redirect(reverse('users:profile', kwargs={'pk': user.pk}))
@ -318,7 +325,7 @@ def addProduct(request):
``form`` ``form``
The ProductForm instance The ProductForm instance
``form_title`` ``form_title``
The title for the form template The title for the form template
@ -350,7 +357,7 @@ def editProduct(request, pk):
``form`` ``form``
The ProductForm instance The ProductForm instance
``form_title`` ``form_title``
The title for the form template The title for the form template
@ -399,7 +406,7 @@ def searchProduct(request):
``form`` ``form``
The SearchProductForm instance The SearchProductForm instance
``form_title`` ``form_title``
The title for the form template The title for the form template
@ -426,7 +433,7 @@ def productProfile(request, pk):
The primary key of the requested :model:`gestion.Product` The primary key of the requested :model:`gestion.Product`
**Context** **Context**
``product`` ``product``
The :model:`gestion.Product` instance The :model:`gestion.Product` instance
@ -500,10 +507,10 @@ def addKeg(request):
Display a form to add a :model:`gestion.Keg` Display a form to add a :model:`gestion.Keg`
**Context** **Context**
``form`` ``form``
The KegForm instance The KegForm instance
``form_title`` ``form_title``
The title for the :template:`form.html` template The title for the :template:`form.html` template
@ -532,10 +539,10 @@ def editKeg(request, pk):
The primary key of the requested :model:`gestion.Keg` The primary key of the requested :model:`gestion.Keg`
**Context** **Context**
``form`` ``form``
The KegForm instance The KegForm instance
``form_title`` ``form_title``
The title for the :template:`form.html` template The title for the :template:`form.html` template
@ -562,10 +569,10 @@ def openKeg(request):
Display a form to open a :model:`gestion.Keg` Display a form to open a :model:`gestion.Keg`
**Context** **Context**
``form`` ``form``
The SelectPositiveKegForm instance The SelectPositiveKegForm instance
``form_title`` ``form_title``
The title for the :template:`form.html` template The title for the :template:`form.html` template
@ -628,10 +635,10 @@ def closeKeg(request):
Display a form to close a :model:`gestion.Keg` Display a form to close a :model:`gestion.Keg`
**Context** **Context**
``form`` ``form``
The SelectActiveKegForm instance The SelectActiveKegForm instance
``form_title`` ``form_title``
The title for the :template:`form.html` template The title for the :template:`form.html` template
@ -716,7 +723,7 @@ def kegH(request, pk):
``keg`` ``keg``
The :model:`gestion.Keg` instance The :model:`gestion.Keg` instance
``kegHistory`` ``kegHistory``
List of :model:`gestion.KegHistory` attached to keg List of :model:`gestion.KegHistory` attached to keg
@ -758,10 +765,10 @@ def addMenu(request):
Display a form to add a :model:`gestion.Menu` Display a form to add a :model:`gestion.Menu`
**Context** **Context**
``form`` ``form``
The MenuForm instance The MenuForm instance
``form_title`` ``form_title``
The title for the :template:`form.html` template The title for the :template:`form.html` template
@ -791,10 +798,10 @@ def edit_menu(request, pk):
The primary key of requested :model:`gestion.Menu` The primary key of requested :model:`gestion.Menu`
**Context** **Context**
``form`` ``form``
The MenuForm instance The MenuForm instance
``form_title`` ``form_title``
The title for the :template:`form.html` template The title for the :template:`form.html` template
@ -962,7 +969,7 @@ def release(request, pinte_pk):
else: else:
messages.error(request, "Impossible de libérer la pinte") messages.error(request, "Impossible de libérer la pinte")
return redirect(reverse('gestion:pintesList')) return redirect(reverse('gestion:pintesList'))
@active_required @active_required
@login_required @login_required
@permission_required('gestion.add_pinte') @permission_required('gestion.add_pinte')
@ -1017,4 +1024,52 @@ def pintes_list(request):
def pintes_user_list(request): def pintes_user_list(request):
pks = [x.pk for x in User.objects.all() if x.profile.nb_pintes > 0] pks = [x.pk for x in User.objects.all() if x.profile.nb_pintes > 0]
users = User.objects.filter(pk__in=pks) users = User.objects.filter(pk__in=pks)
return render(request, "gestion/pintes_user_list.html", {"users": users}) return render(request, "gestion/pintes_user_list.html", {"users": users})
@active_required
@login_required
@admin_required
def gen_releve(request):
form = GenerateReleveForm(request.POST or None)
if form.is_valid():
begin, end = form.cleaned_data['begin'], form.cleaned_data['end']
consumptions = ConsumptionHistory.objects.filter(date__gte=begin).filter(date__lte=end).order_by('-date')
reloads = Reload.objects.filter(date__gt=begin).filter(date__lt=end).order_by('-date')
refunds = Refund.objects.filter(date__gt=begin).filter(date__lt=end).order_by('-date')
cotisations = CotisationHistory.objects.filter(paymentDate__gt=begin).filter(paymentDate__lt=end).order_by('-paymentDate')
especes = PaymentMethod.objects.get(name="Espèces")
lydia = PaymentMethod.objects.get(name="Lydia")
cheque = PaymentMethod.objects.get(name="Chèque")
value_especes = 0
value_lydia = 0
value_cheque = 0
for consumption in consumptions:
pm = consumption.paymentMethod
if pm == especes:
value_especes += consumption.amount
elif pm == lydia:
value_lydia += consumption.amount
elif pm == cheque:
value_cheque += consumption.amount
for reload in reloads:
pm = reload.PaymentMethod
if pm == especes:
value_especes += reload.amount
elif pm == lydia:
value_lydia += reload.amount
elif pm == cheque:
value_cheque += reload.amount
for refund in refunds:
value_especes -= refund.amount
for cot in cotisations:
pm = cot.paymentMethod
if pm == especes:
value_especes += cot.amount
elif pm == lydia:
value_lydia += cot.amount
elif pm == cheque:
value_cheque += cot.amount
now = datetime.datetime.now()
return render_to_pdf(request, 'gestion/releve.tex', {"consumptions": consumptions, "reloads": reloads, "refunds": refunds, "cotisations": cotisations, "begin": begin, "end": end, "now": now, "value_especes": value_especes, "value_lydia": value_lydia, "value_cheque": value_cheque}, filename="releve.pdf")
else:
return render(request, "form.html", {"form": form, "form_title": "Génération d'un relevé", "form_button": "Générer"})

View file

@ -0,0 +1,23 @@
# Generated by Django 2.1 on 2019-01-06 03:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('preferences', '0003_auto_20181223_1440'),
]
operations = [
migrations.AddField(
model_name='historicalpaymentmethod',
name='icon',
field=models.CharField(blank=True, max_length=255, verbose_name='Icône'),
),
migrations.AddField(
model_name='paymentmethod',
name='icon',
field=models.CharField(blank=True, max_length=255, verbose_name='Icône'),
),
]

View file

@ -0,0 +1,23 @@
# Generated by Django 2.1 on 2019-01-06 04:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('preferences', '0004_auto_20190106_0452'),
]
operations = [
migrations.AddField(
model_name='generalpreferences',
name='floating_buttons',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='historicalgeneralpreferences',
name='floating_buttons',
field=models.BooleanField(default=False),
),
]

View file

@ -12,6 +12,7 @@ class PaymentMethod(models.Model):
is_usable_in_cotisation = models.BooleanField(default=True, verbose_name="Cotisations ?") is_usable_in_cotisation = models.BooleanField(default=True, verbose_name="Cotisations ?")
is_usable_in_reload = models.BooleanField(default=True, verbose_name="Rechargements ?") is_usable_in_reload = models.BooleanField(default=True, verbose_name="Rechargements ?")
affect_balance = models.BooleanField(default=False, verbose_name="Affecte le solde") affect_balance = models.BooleanField(default=False, verbose_name="Affecte le solde")
icon = models.CharField(max_length=255, verbose_name="Icône", blank=True)
history = HistoricalRecords() history = HistoricalRecords()
def __str__(self): def __str__(self):
@ -32,6 +33,7 @@ class GeneralPreferences(models.Model):
grocer = models.CharField(max_length=255, blank=True) grocer = models.CharField(max_length=255, blank=True)
use_pinte_monitoring = models.BooleanField(default=False) use_pinte_monitoring = models.BooleanField(default=False)
lost_pintes_allowed = models.PositiveIntegerField(default=0) lost_pintes_allowed = models.PositiveIntegerField(default=0)
floating_buttons = models.BooleanField(default=False)
history = HistoricalRecords() history = HistoricalRecords()
class Cotisation(models.Model): class Cotisation(models.Model):

View file

@ -6,6 +6,7 @@
<li><a href="#second">Site actif</a></li> <li><a href="#second">Site actif</a></li>
<li><a href="#third">Bureau</a></li> <li><a href="#third">Bureau</a></li>
<li><a href="#fourth">Suivi de pintes</a></li> <li><a href="#fourth">Suivi de pintes</a></li>
<li><a href="fifth">Autre</a></li>
</ul> </ul>
{% endblock %} {% endblock %}
@ -23,6 +24,11 @@
{{form.global_message}} {{form.global_message}}
</div> </div>
</div> </div>
<div class="row uniform">
<div class="12u">
<button type="submit">Enregistrer</button>
</div>
</div>
</div> </div>
</div> </div>
</section> </section>
@ -43,6 +49,11 @@
{{form.active_message}} {{form.active_message}}
</div> </div>
</div> </div>
<div class="row uniform">
<div class="12u">
<button type="submit">Enregistrer</button>
</div>
</div>
</div> </div>
</div> </div>
</section> </section>
@ -76,6 +87,11 @@
{{form.brewer}} {{form.brewer}}
</div> </div>
</div> </div>
<div class="row uniform">
<div class="12u">
<button type="submit">Enregistrer</button>
</div>
</div>
</div> </div>
</div> </div>
</section> </section>
@ -105,5 +121,25 @@
</div> </div>
</div> </div>
</section> </section>
<section id="fifth" class="main">
<div class="spotlight">
<div class="content">
<header class="major">
<h2>Autre</h2>
</header>
<div class="row uniform">
<div class="12u">
{{form.floating_buttons}}
<label for="{{form.floating_buttons.id_for_label}}">Utiliser les boutons de paiement flottants ?</label>
</div>
</div>
<div class="row uniform">
<div class="12u">
<button type="submit">Enregistrer</button>
</div>
</div>
</div>
</div>
</section>
</form> </form>
{% endblock %} {% endblock %}

View file

@ -22,6 +22,7 @@
<th>Cotisations ?</th> <th>Cotisations ?</th>
<th>Rechargements ?</th> <th>Rechargements ?</th>
<th>Affecte le solde</th> <th>Affecte le solde</th>
<th>Icône</th>
<th>Administration</th> <th>Administration</th>
</tr> </tr>
</thead> </thead>
@ -33,6 +34,7 @@
<td>{{ pm.is_usable_in_cotisation | yesno:"Oui, Non" }}</td> <td>{{ pm.is_usable_in_cotisation | yesno:"Oui, Non" }}</td>
<td>{{ pm.is_usable_in_reload | yesno:"Oui, Non" }}</td> <td>{{ pm.is_usable_in_reload | yesno:"Oui, Non" }}</td>
<td>{{ pm.affect_balance | yesno:"Oui, Non" }}</td> <td>{{ pm.affect_balance | yesno:"Oui, Non" }}</td>
<td><i class="fa fa-{{ pm.icon }}"></i></td>
<td>{% if perms.preferences.change_paymentmethod %}<a class="button small" href="{% url 'preferences:editPaymentMethod' pm.pk %}">Modifier</a> {% endif %}{% if perms.preferences.delete_paymentmethod %}<a class="button small" href="{% url 'preferences:deletePaymentMethod' pm.pk %}">Supprimer</a>{% endif %}</td> <td>{% if perms.preferences.change_paymentmethod %}<a class="button small" href="{% url 'preferences:editPaymentMethod' pm.pk %}">Modifier</a> {% endif %}{% if perms.preferences.delete_paymentmethod %}<a class="button small" href="{% url 'preferences:deletePaymentMethod' pm.pk %}">Supprimer</a>{% endif %}</td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -33,6 +33,7 @@ def generalPreferences(request):
form = GeneralPreferencesForm(request.POST or None, instance=gp) form = GeneralPreferencesForm(request.POST or None, instance=gp)
if(form.is_valid()): if(form.is_valid()):
form.save() form.save()
messages.success(request, "Les préférences générales ont bien été mises à jour")
return render(request, "preferences/general_preferences.html", {"form": form}) return render(request, "preferences/general_preferences.html", {"form": form})
########## Cotisations ########## ########## Cotisations ##########

View file

@ -3,4 +3,5 @@ django-autocomplete-light==3.3.2
pytz==2018.5 pytz==2018.5
simplejson==3.16.0 simplejson==3.16.0
docutils==0.14 docutils==0.14
django-simple-history==2.5.1 django-simple-history==2.5.1
jinja2==2.10

View file

@ -9,6 +9,7 @@
<link rel="icon" sizes="32x32" href="{% static 'favicon32.ico' %}" type="image/x-icon"> <link rel="icon" sizes="32x32" href="{% static 'favicon32.ico' %}" type="image/x-icon">
<link rel="icon" sizes="96x96" href="{% static 'favicon96.ico' %}" type="image/x-icon"> <link rel="icon" sizes="96x96" href="{% static 'favicon96.ico' %}" type="image/x-icon">
<link rel="stylesheet" href="{%static 'css/main.css' %}" /> <link rel="stylesheet" href="{%static 'css/main.css' %}" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
</head> </head>

View file

@ -39,6 +39,6 @@
<li><a href="https://www.facebook.com/coopesmetz/" class="icon fa-facebook alt"><span class="label">Facebook</span></a></li> <li><a href="https://www.facebook.com/coopesmetz/" class="icon fa-facebook alt"><span class="label">Facebook</span></a></li>
</ul> </ul>
</section> </section>
<p class="copyright">coope.rez v3.1.0 (release stable) &copy; 2018 Yoann Pietri.</p> <p class="copyright">coope.rez v3.2.0 (release stable) &copy; 2018 Yoann Pietri.</p>

View file

@ -1,43 +1,49 @@
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<span class="tabulation2"> <span class="tabulation2">
<a href="{% url 'users:profile' request.user.pk %}">Mon profil</a> <i class="fa fa-user"></i> <a href="{% url 'users:profile' request.user.pk %}">Mon profil</a>
</span> </span>
{% if perms.gestion.add_consumptionhistory or perms.gestion.add_refund or perms.gestion.add_reload %} {% if perms.gestion.add_consumptionhistory or perms.gestion.add_refund or perms.gestion.add_reload %}
<span class="tabulation2"> <span class="tabulation2">
<a href="{% url 'gestion:manage' %}">Transactions</a> <i class="fa fa-cash-register"></i> <a href="{% url 'gestion:manage' %}">Transactions</a>
</span> </span>
{% endif %} {% endif %}
{% if perms.auth.add_user or perms.auth.view_user or perms.auth.add_group or perms.auth.view_group or perms.users.add_school or perms.users.view_school %} {% if perms.auth.add_user or perms.auth.view_user or perms.auth.add_group or perms.auth.view_group or perms.users.add_school or perms.users.view_school %}
<span class="tabulation2"> <span class="tabulation2">
<a href="{% url 'users:index' %}">Gestion des clients</a> <i class="fa fa-users"></i> <a href="{% url 'users:index' %}">Gestion des clients</a>
</span> </span>
{% endif %} {% endif %}
{% if perms.gestion.view_product or perms.gestion.add_product or perms.gestion.add_keg or perms.gestion.view_keg or perms.gestion.change_keg or perms.gestion.view_menu or perms.gestion.add_menu %} {% if perms.gestion.view_product or perms.gestion.add_product or perms.gestion.add_keg or perms.gestion.view_keg or perms.gestion.change_keg or perms.gestion.view_menu or perms.gestion.add_menu %}
<span class="tabulation2"> <span class="tabulation2">
<a href="{% url 'gestion:productsIndex' %}">Gestion des produits</a> <i class="fa fa-dolly-flatbed"></i> <a href="{% url 'gestion:productsIndex' %}">Gestion des produits</a>
</span> </span>
{% endif %} {% endif %}
<span class="tabulation2"> <span class="tabulation2">
<a href="{% url 'gestion:ranking' %}">Classement</a> <i class="fa fa-list-ol"></i> <a href="{% url 'gestion:ranking' %}">Classement</a>
</span> </span>
{% if perms.preferences.change_generalpreferences %} {% if perms.preferences.change_generalpreferences %}
<span class="tabulation2"> <span class="tabulation2">
<a href="{% url 'preferences:generalPreferences' %}">Admin</a> <br>
<i class="fa fa-tools"></i> <a href="{% url 'preferences:generalPreferences' %}">Admin</a>
</span>
{% endif %}
{% if request.user.is_staff %}
<span class="tabulation2">
<i class="fa fa-business-time"></i> <a href="{% url 'gestion:gen_releve' %}">Comptabilité</a>
</span> </span>
{% endif %} {% endif %}
{% if perms.preferences.view_cotisation %} {% if perms.preferences.view_cotisation %}
<span class="tabulation2"> <span class="tabulation2">
<a href="{% url 'preferences:cotisationsIndex' %}">Cotisations</a> <i class="fa fa-calendar-check"></i> <a href="{% url 'preferences:cotisationsIndex' %}">Cotisations</a>
</span> </span>
{% endif %} {% endif %}
{% if perms.preferences.view_cotisation %} {% if perms.preferences.view_cotisation %}
<span class="tabulation2"> <span class="tabulation2">
<a href="{% url 'preferences:paymentMethodsIndex' %}">Moyens de paiement</a> <i class="fa fa-comments-dollar"></i> <a href="{% url 'preferences:paymentMethodsIndex' %}">Moyens de paiement</a>
</span> </span>
{% endif %} {% endif %}
<span class="tabulation2"> <span class="tabulation2">
<a href="{% url 'users:logout' %}">Deconnexion</a> <i class="fa fa-bed"></i> <a href="{% url 'users:logout' %}">Deconnexion</a>
</span> </span>
{% else %} {% else %}
<a href="{% url 'users:login' %}">Connexion</a> <i class="fa fa-sign-in"></i> <a href="{% url 'users:login' %}">Connexion</a>
{% endif %} {% endif %}

View file

@ -128,4 +128,5 @@ class ExportForm(forms.Form):
('debit', 'Débit') ('debit', 'Débit')
) )
query_type = forms.ChoiceField(choices=QUERY_TYPE_CHOICES, label="Ensemble de la demande") query_type = forms.ChoiceField(choices=QUERY_TYPE_CHOICES, label="Ensemble de la demande")
fields = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=FIELDS_CHOICES, label="Champs") fields = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=FIELDS_CHOICES, label="Champs")
group = forms.ModelChoiceField(queryset=Group.objects.all(), empty_label="Tous les groupes", required=False, label="Groupe")

View file

@ -169,4 +169,5 @@ def str_user(self):
fin = "Non adhérent" fin = "Non adhérent"
return self.username + " (" + self.first_name + " " + self.last_name + ", " + str(self.profile.balance) + "€, " + fin + ")" return self.username + " (" + self.first_name + " " + self.last_name + ", " + str(self.profile.balance) + "€, " + fin + ")"
User.add_to_class("__str__", str_user)
User.add_to_class("__str__", str_user)

View file

@ -106,6 +106,7 @@
<form action="{% url 'users:exportCSV' %}" method="POST"> <form action="{% url 'users:exportCSV' %}" method="POST">
{% csrf_token %} {% csrf_token %}
{{export_form}} {{export_form}}
<br>
<button class="button" target="_blank">Exporter au format csv</button> <button class="button" target="_blank">Exporter au format csv</button>
</form> </form>
</section> </section>

View file

@ -1,5 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %} {% load static %}
{% load users_extra %}
{% block entete %}{% if self %}Mon Profil{% else %}Profil de {{user}}{% endif %}{%endblock%} {% block entete %}{% if self %}Mon Profil{% else %}Profil de {{user}}{% endif %}{%endblock%}
{% block navbar %} {% block navbar %}
@ -62,20 +63,6 @@
{% if perms.users.can_change_user_perm %} {% if perms.users.can_change_user_perm %}
<span class="tabulation"><a href="{% url 'users:editGroups' user.pk %}">Changer les groupes</a></span> <span class="tabulation"><a href="{% url 'users:editGroups' user.pk %}">Changer les groupes</a></span>
{% endif %} {% endif %}
{% if request.user.is_staff %}
{% if user.is_staff %}
<span class="tabulation"><a href="">Retirer des admins</a></span>
{% else %}
<span class="tabulation"><a href="">Ajouter aux admins</a></span>
{% endif %}
{% endif %}
{% if request.user.is_superuser %}
{% if user.is_superuser %}
<span class="tabulation"><a href="">Retirer des superusers</a></span>
{% else %}
<span class="tabulation"><a href="">Ajouter aux superusers</a></span>
{% endif %}
{% endif %}
{% if perms.auth.change_user %} {% if perms.auth.change_user %}
<span class="tabulation"><a href="{% url 'users:switchActivateUser' user.pk %}">{{ user.is_active | yesno:"Désa,A"}}ctiver</a></span> <span class="tabulation"><a href="{% url 'users:switchActivateUser' user.pk %}">{{ user.is_active | yesno:"Désa,A"}}ctiver</a></span>
{% endif %} {% endif %}
@ -97,22 +84,10 @@
label: '# of Votes', label: '# of Votes',
data: [{% for q in quantities %}{{q}}, {% endfor %}], data: [{% for q in quantities %}{{q}}, {% endfor %}],
backgroundColor: [ backgroundColor: [
'rgba(255, 99, 132, 0.2)', {% for q in products %}
'rgba(54, 162, 235, 0.2)', 'rgb({% random_filter 0 255 %}, {% random_filter 0 255 %}, {% random_filter 0 255 %})',
'rgba(255, 206, 86, 0.2)', {% endfor %}
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
], ],
borderColor: [
'rgba(255,99,132,1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}] }]
}, },
options: { options: {

View file

View file

@ -0,0 +1,9 @@
import random
from django import template
register = template.Library()
@register.simple_tag
def random_filter(a, b):
return random.randint(a, b)

View file

@ -83,19 +83,24 @@ def export_csv(request):
users = User.objects users = User.objects
qt = export_form.cleaned_data['query_type'] qt = export_form.cleaned_data['query_type']
if qt == 'all': if qt == 'all':
users = users.all() filename = "Utilisateurs-coope"
filename="Utilisateurs-coope" if not export_form.cleaned_data['group']:
users = users.all()
elif qt == 'all_active': elif qt == 'all_active':
users = users.filter(is_active=True) users = users.filter(is_active=True)
filename="Utilisateurs-actifs-coope" filename = "Utilisateurs-actifs-coope"
elif qt == 'adherent': elif qt == 'adherent':
pks = [x.pk for x in User.objects.all() if x.profile.is_adherent] pks = [x.pk for x in User.objects.all() if x.profile.is_adherent]
users = users.filter(pk__in=pks) users = users.filter(pk__in=pks)
filename="Adherents-coope" filename = "Adherents-coope"
elif qt == 'adherent_active': elif qt == 'adherent_active':
pks = [x.pk for x in User.objects.filter(is_active=True) if x.profile.is_adherent] pks = [x.pk for x in User.objects.filter(is_active=True) if x.profile.is_adherent]
users = users.filter(pk__in=pks) users = users.filter(pk__in=pks)
filename="Adherents-actifs-coope" filename = "Adherents-actifs-coope"
if export_form.cleaned_data['group']:
group = export_form.cleaned_data['group']
users = users.filter(groups=group)
filename += "(" + group.name + ")"
response = HttpResponse(content_type='text/csv') response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="'+ filename + '.csv"' response['Content-Disposition'] = 'attachment; filename="'+ filename + '.csv"'
writer = csv.writer(response) writer = csv.writer(response)
@ -154,15 +159,25 @@ def profile(request, pk):
whitelists = WhiteListHistory.objects.filter(user=user) whitelists = WhiteListHistory.objects.filter(user=user)
reloads = Reload.objects.filter(customer=user).order_by('-date')[:5] reloads = Reload.objects.filter(customer=user).order_by('-date')[:5]
consumptionsChart = Consumption.objects.filter(customer=user) consumptionsChart = Consumption.objects.filter(customer=user)
products_pre = []
quantities_pre = []
for ch in consumptionsChart:
if ch.product in products_pre:
i = products_pre.index(ch.product)
quantities_pre[i] += int(ch.quantity/ch.product.showingMultiplier)
else:
products_pre.append(ch.product)
quantities_pre.append(int(ch.quantity/ch.product.showingMultiplier))
tot = len(products_pre)
totQ = sum(quantities_pre)
products = [] products = []
quantities = [] quantities = []
for ch in consumptionsChart: for k in range(tot):
if ch.product in products: if quantities_pre[k]/totQ >= 0.01:
i = products.index(ch.product) products.append(products_pre[k])
quantities[i] += ch.quantity quantities.append(quantities_pre[k])
else: print(products)
products.append(ch.product) print(quantities)
quantities.append(ch.quantity)
lastConsumptions = ConsumptionHistory.objects.filter(customer=user).order_by('-date')[:10] lastConsumptions = ConsumptionHistory.objects.filter(customer=user).order_by('-date')[:10]
lastMenus = MenuHistory.objects.filter(customer=user).order_by('-date')[:10] lastMenus = MenuHistory.objects.filter(customer=user).order_by('-date')[:10]
return render(request, "users/profile.html", return render(request, "users/profile.html",