mirror of
https://github.com/nanoy42/coope
synced 2024-11-25 22:22:27 +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:
commit
350027042e
27 changed files with 388 additions and 87 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
12
gestion/environment.py
Normal 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
|
|
@ -67,3 +67,7 @@ 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")
|
23
gestion/migrations/0005_auto_20190106_0018.py
Normal file
23
gestion/migrations/0005_auto_20190106_0018.py
Normal 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),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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):
|
||||||
|
|
|
@ -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>
|
||||||
|
|
71
gestion/templates/gestion/releve.tex
Normal file
71
gestion/templates/gestion/releve.tex
Normal 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}
|
|
@ -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"),
|
||||||
]
|
]
|
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -1018,3 +1025,51 @@ 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"})
|
||||||
|
|
23
preferences/migrations/0004_auto_20190106_0452.py
Normal file
23
preferences/migrations/0004_auto_20190106_0452.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
23
preferences/migrations/0005_auto_20190106_0513.py
Normal file
23
preferences/migrations/0005_auto_20190106_0513.py
Normal 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),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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):
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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 ##########
|
||||||
|
|
|
@ -4,3 +4,4 @@ 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
|
|
@ -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>
|
||||||
|
|
|
@ -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) © 2018 Yoann Pietri.</p>
|
<p class="copyright">coope.rez v3.2.0 (release stable) © 2018 Yoann Pietri.</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -129,3 +129,4 @@ class ExportForm(forms.Form):
|
||||||
)
|
)
|
||||||
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")
|
|
@ -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)
|
|
@ -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>
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
0
users/templatetags/__init__;py
Normal file
0
users/templatetags/__init__;py
Normal file
9
users/templatetags/users_extra.py
Normal file
9
users/templatetags/users_extra.py
Normal 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)
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue