mirror of
https://github.com/nanoy42/coope
synced 2024-12-25 16:33:47 +00:00
commit
cf153f9ed9
17 changed files with 446 additions and 19 deletions
|
@ -61,3 +61,8 @@ class SelectPositiveKegForm(forms.Form):
|
||||||
|
|
||||||
class SelectActiveKegForm(forms.Form):
|
class SelectActiveKegForm(forms.Form):
|
||||||
keg = forms.ModelChoiceField(queryset=Keg.objects.filter(is_active = True), required=True, label="Fût", widget=autocomplete.ModelSelect2(url='gestion:kegs-active-autocomplete'))
|
keg = forms.ModelChoiceField(queryset=Keg.objects.filter(is_active = True), required=True, label="Fût", widget=autocomplete.ModelSelect2(url='gestion:kegs-active-autocomplete'))
|
||||||
|
|
||||||
|
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)
|
||||||
|
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)
|
25
gestion/migrations/0002_pinte.py
Normal file
25
gestion/migrations/0002_pinte.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Generated by Django 2.1 on 2018-12-21 20:34
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('gestion', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Pinte',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('last_update_date', models.DateTimeField(auto_now=True)),
|
||||||
|
('current_owner', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='pinte_owned_currently', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('previous_owner', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='pinte_owned_previously', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
37
gestion/migrations/0003_historicalpinte.py
Normal file
37
gestion/migrations/0003_historicalpinte.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# Generated by Django 2.1 on 2018-12-21 20:51
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import simple_history.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('gestion', '0002_pinte'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='HistoricalPinte',
|
||||||
|
fields=[
|
||||||
|
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||||
|
('last_update_date', models.DateTimeField(blank=True, editable=False)),
|
||||||
|
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||||
|
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||||
|
('history_date', models.DateTimeField()),
|
||||||
|
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||||
|
('current_owner', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('previous_owner', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'historical pinte',
|
||||||
|
'ordering': ('-history_date', '-history_id'),
|
||||||
|
'get_latest_by': 'history_date',
|
||||||
|
},
|
||||||
|
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||||
|
),
|
||||||
|
]
|
25
gestion/migrations/0004_auto_20181223_1830.py
Normal file
25
gestion/migrations/0004_auto_20181223_1830.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Generated by Django 2.1 on 2018-12-23 17:30
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('gestion', '0003_historicalpinte'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='pinte',
|
||||||
|
name='current_owner',
|
||||||
|
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='pintes_owned_currently', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='pinte',
|
||||||
|
name='previous_owner',
|
||||||
|
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='pintes_owned_previously', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
|
@ -225,3 +225,12 @@ class Consumption(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Consommation de " + str(self.customer) + " concernant le produit " + str(self.product)
|
return "Consommation de " + str(self.customer) + " concernant le produit " + str(self.product)
|
||||||
|
|
||||||
|
class Pinte(models.Model):
|
||||||
|
"""
|
||||||
|
Stores a physical pinte
|
||||||
|
"""
|
||||||
|
current_owner = models.ForeignKey(User, on_delete=models.PROTECT, null=True, default=None, related_name="pintes_owned_currently")
|
||||||
|
previous_owner = models.ForeignKey(User, on_delete=models.PROTECT, null=True, default=None, related_name="pintes_owned_previously")
|
||||||
|
last_update_date = models.DateTimeField(auto_now=True)
|
||||||
|
history = HistoricalRecords()
|
||||||
|
|
75
gestion/templates/gestion/pintes_list.html
Normal file
75
gestion/templates/gestion/pintes_list.html
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block entete %}Gestion des produits{% endblock %}
|
||||||
|
{% block navbar%}
|
||||||
|
<ul>
|
||||||
|
<li><a href="#first">Général</a></li>
|
||||||
|
<li><a href="#second">Liste des pintes non rendues</a></li>
|
||||||
|
<li><a href="#third">Liste des pintes rendues</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<section id="first" class="main">
|
||||||
|
<header class="major">
|
||||||
|
<h2>Général</h2>
|
||||||
|
</header>
|
||||||
|
{% if perms.gestion.add_pinte %}
|
||||||
|
<a class="button" href="{% url 'gestion:addPintes' %}">Créer une ou plusieurs pintes</a><br><br>
|
||||||
|
{% endif %}
|
||||||
|
Il a y actuellement {{ taken_pintes.count|add:free_pintes.count }} pintes, parmis lesquelles <strong>{{ free_pintes.count }} sont rendues</strong> et <strong>{{ taken_pintes.count }} ne sont pas rendues</strong>.
|
||||||
|
</section>
|
||||||
|
<section id="second" class="main">
|
||||||
|
<header class="major">
|
||||||
|
<h2>Liste des pintes non rendues</h2>
|
||||||
|
</header>
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Numéro</th>
|
||||||
|
<th>Possesseur actuel</th>
|
||||||
|
<th>Possesseur précédent</th>
|
||||||
|
<th>Date du dernier changement</th>
|
||||||
|
<th>Administrer</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for pinte in taken_pintes %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ pinte.pk }}</td>
|
||||||
|
<td>{{ pinte.current_owner }}</td>
|
||||||
|
<td>{{ pinte.previous_owner }}</td>
|
||||||
|
<td>{{ pinte.last_update_date }}</td>
|
||||||
|
<td>{% if perms.gestion.change_pinte %} <a href="{% url 'gestion:release' pinte.pk %}" class="button small">Libérer</a>{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section id="third" class="main">
|
||||||
|
<header class="major">
|
||||||
|
<h2>Liste des pintes rendues</h2>
|
||||||
|
</header>
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Numéro</th>
|
||||||
|
<th>Possesseur précédent</th>
|
||||||
|
<th>Date du dernier changement</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for pinte in free_pintes %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ pinte.pk }}</td>
|
||||||
|
<td>{{ pinte.previous_owner }}</td>
|
||||||
|
<td>{{ pinte.last_update_date }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
|
@ -5,6 +5,7 @@
|
||||||
<li><a href="#first">Produits</a></li>
|
<li><a href="#first">Produits</a></li>
|
||||||
<li><a href="#second">Futs</a></li>
|
<li><a href="#second">Futs</a></li>
|
||||||
<li><a href="#third">Menus</a></li>
|
<li><a href="#third">Menus</a></li>
|
||||||
|
<li><a href="#fourth">Pintes</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
@ -58,4 +59,21 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
<section id="fourth" class="main">
|
||||||
|
<header class="major">
|
||||||
|
<h2>Pintes</h2>
|
||||||
|
</header>
|
||||||
|
Actions possibles :
|
||||||
|
<ul>
|
||||||
|
{% if perms.gestion.add_pinte %}
|
||||||
|
<li><a href="{% url 'gestion:addPintes' %}">Créer une ou plusieurs pintes</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.gestion.change_pinte %}
|
||||||
|
<li><a href="{% url 'gestion:releasePintes' %}">Libérer des pintes</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.gestion.view_pinte %}
|
||||||
|
<li><a href="{% url 'gestion:pintesList' %}">Lister les pintes</a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -33,6 +33,10 @@ urlpatterns = [
|
||||||
path('cancelConsumption/<int:pk>', views.cancel_consumption, name="cancelConsumption"),
|
path('cancelConsumption/<int:pk>', views.cancel_consumption, name="cancelConsumption"),
|
||||||
path('cancelMenu/<int:pk>', views.cancel_menu, name="cancelMenu"),
|
path('cancelMenu/<int:pk>', views.cancel_menu, name="cancelMenu"),
|
||||||
path('productProfile/<int:pk>', views.productProfile, name="productProfile"),
|
path('productProfile/<int:pk>', views.productProfile, name="productProfile"),
|
||||||
|
path('addPintes', views.add_pintes, name="addPintes"),
|
||||||
|
path('releasePintes', views.release_pintes, name="releasePintes"),
|
||||||
|
path('pintesList', views.pintes_list, name="pintesList"),
|
||||||
|
path('release/<int:pinte_pk>', views.release, name="release"),
|
||||||
path('products-autocomplete', views.ProductsAutocomplete.as_view(), name="products-autocomplete"),
|
path('products-autocomplete', views.ProductsAutocomplete.as_view(), name="products-autocomplete"),
|
||||||
path('kegs-positive-autocomplete', views.KegPositiveAutocomplete.as_view(), name="kegs-positive-autocomplete"),
|
path('kegs-positive-autocomplete', views.KegPositiveAutocomplete.as_view(), name="kegs-positive-autocomplete"),
|
||||||
path('kegs-active-autocomplete', views.KegActiveAutocomplete.as_view(), name="kegs-active-autocomplete"),
|
path('kegs-active-autocomplete', views.KegActiveAutocomplete.as_view(), name="kegs-active-autocomplete"),
|
||||||
|
|
105
gestion/views.py
105
gestion/views.py
|
@ -13,9 +13,9 @@ import simplejson as json
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
from decimal import *
|
from decimal import *
|
||||||
|
|
||||||
from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm, SearchMenuForm, SearchProductForm, SelectPositiveKegForm, SelectActiveKegForm
|
from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm, SearchMenuForm, SearchProductForm, SelectPositiveKegForm, SelectActiveKegForm, PinteForm
|
||||||
from .models import Product, Menu, Keg, ConsumptionHistory, KegHistory, Consumption, MenuHistory
|
from .models import Product, Menu, Keg, ConsumptionHistory, KegHistory, Consumption, MenuHistory, Pinte
|
||||||
from preferences.models import PaymentMethod
|
from preferences.models import PaymentMethod, GeneralPreferences
|
||||||
|
|
||||||
@active_required
|
@active_required
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -107,6 +107,8 @@ def order(request):
|
||||||
amount = Decimal(request.POST['amount'])
|
amount = Decimal(request.POST['amount'])
|
||||||
order = json.loads(request.POST["order"])
|
order = json.loads(request.POST["order"])
|
||||||
menus = json.loads(request.POST["menus"])
|
menus = json.loads(request.POST["menus"])
|
||||||
|
listPintes = json.loads(request.POST["listPintes"])
|
||||||
|
gp,_ = GeneralPreferences.objects.get_or_create(pk=1)
|
||||||
if (not order) and (not menus):
|
if (not order) and (not menus):
|
||||||
return HttpResponse("Pas de commande")
|
return HttpResponse("Pas de commande")
|
||||||
adherentRequired = False
|
adherentRequired = False
|
||||||
|
@ -118,6 +120,14 @@ def order(request):
|
||||||
adherentRequired = adherentRequired or menu.adherent_required
|
adherentRequired = adherentRequired or menu.adherent_required
|
||||||
if(adherentRequired and not user.profile.is_adherent):
|
if(adherentRequired and not user.profile.is_adherent):
|
||||||
return HttpResponse("N'est pas adhérent et devrait l'être")
|
return HttpResponse("N'est pas adhérent et devrait l'être")
|
||||||
|
# Partie un peu complexe : je libère toutes les pintes de la commande, puis je test
|
||||||
|
# s'il a trop de pintes non rendues, puis je réalloue les pintes
|
||||||
|
for pinte in listPintes:
|
||||||
|
allocate(pinte, None)
|
||||||
|
if(gp.lost_pintes_allowed and user.profile.nb_pintes >= gp.lost_pintes_allowed):
|
||||||
|
return HttpResponse("Impossible de réaliser la commande : l'utilisateur a perdu trop de pintes.")
|
||||||
|
for pinte in listPintes:
|
||||||
|
allocate(pinte, user)
|
||||||
if(paymentMethod.affect_balance):
|
if(paymentMethod.affect_balance):
|
||||||
if(user.profile.balance < amount):
|
if(user.profile.balance < amount):
|
||||||
return HttpResponse("Solde inférieur au prix de la commande")
|
return HttpResponse("Solde inférieur au prix de la commande")
|
||||||
|
@ -418,7 +428,11 @@ def getProduct(request, barcode):
|
||||||
The requested barcode
|
The requested barcode
|
||||||
"""
|
"""
|
||||||
product = Product.objects.get(barcode=barcode)
|
product = Product.objects.get(barcode=barcode)
|
||||||
data = json.dumps({"pk": product.pk, "barcode" : product.barcode, "name": product.name, "amount" : product.amount})
|
if product.category == Product.P_PRESSION:
|
||||||
|
nb_pintes = 1
|
||||||
|
else:
|
||||||
|
nb_pintes = 0
|
||||||
|
data = json.dumps({"pk": product.pk, "barcode" : product.barcode, "name": product.name, "amount": product.amount, "needQuantityButton": product.needQuantityButton, "nb_pintes": nb_pintes})
|
||||||
return HttpResponse(data, content_type='application/json')
|
return HttpResponse(data, content_type='application/json')
|
||||||
|
|
||||||
@active_required
|
@active_required
|
||||||
|
@ -845,7 +859,11 @@ def get_menu(request, barcode):
|
||||||
The requested barcode
|
The requested barcode
|
||||||
"""
|
"""
|
||||||
menu = get_object_or_404(Menu, barcode=barcode)
|
menu = get_object_or_404(Menu, barcode=barcode)
|
||||||
data = json.dumps({"pk": menu.pk, "barcode" : menu.barcode, "name": menu.name, "amount" : menu.amount})
|
nb_pintes = 0
|
||||||
|
for article in menu.articles:
|
||||||
|
if article.category == Product.P_PRESSION:
|
||||||
|
nb_pintes +=1
|
||||||
|
data = json.dumps({"pk": menu.pk, "barcode" : menu.barcode, "name": menu.name, "amount" : menu.amount, needQuantityButton: False, "nb_pintes": nb_pintes})
|
||||||
return HttpResponse(data, content_type='application/json')
|
return HttpResponse(data, content_type='application/json')
|
||||||
|
|
||||||
class MenusAutocomplete(autocomplete.Select2QuerySetView):
|
class MenusAutocomplete(autocomplete.Select2QuerySetView):
|
||||||
|
@ -886,3 +904,80 @@ def ranking(request):
|
||||||
list.append([customer, alcohol])
|
list.append([customer, alcohol])
|
||||||
bestDrinkers = sorted(list, key=lambda x: x[1], reverse=True)[:25]
|
bestDrinkers = sorted(list, key=lambda x: x[1], reverse=True)[:25]
|
||||||
return render(request, "gestion/ranking.html", {"bestBuyers": bestBuyers, "bestDrinkers": bestDrinkers})
|
return render(request, "gestion/ranking.html", {"bestBuyers": bestBuyers, "bestDrinkers": bestDrinkers})
|
||||||
|
|
||||||
|
########## Pinte monitoring ##########
|
||||||
|
|
||||||
|
def allocate(pinte_pk, user):
|
||||||
|
"""
|
||||||
|
Allocate a pinte to a user or release the pinte if user is None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
pinte = Pinte.objects.get(pk=pinte_pk)
|
||||||
|
if pinte.current_owner is not None:
|
||||||
|
pinte.previous_owner = pinte.current_owner
|
||||||
|
pinte.current_owner = user
|
||||||
|
pinte.save()
|
||||||
|
return True
|
||||||
|
except Pinte.DoesNotExist:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@active_required
|
||||||
|
@login_required
|
||||||
|
@permission_required('gestion.change_pinte')
|
||||||
|
def release(request, pinte_pk):
|
||||||
|
"""
|
||||||
|
View to release a pinte
|
||||||
|
"""
|
||||||
|
if allocate(pinte_pk, None):
|
||||||
|
messages.success(request, "La pinte a bien été libérée")
|
||||||
|
else:
|
||||||
|
messages.error(request, "Impossible de libérer la pinte")
|
||||||
|
return redirect(reverse('gestion:pintesList'))
|
||||||
|
|
||||||
|
@active_required
|
||||||
|
@login_required
|
||||||
|
@permission_required('gestion.add_pinte')
|
||||||
|
def add_pintes(request):
|
||||||
|
form = PinteForm(request.POST or None)
|
||||||
|
if form.is_valid():
|
||||||
|
ids = form.cleaned_data['ids']
|
||||||
|
if ids != "":
|
||||||
|
ids = ids.split(" ")
|
||||||
|
else:
|
||||||
|
ids = range(form.cleaned_data['begin'], form.cleaned_data['end'] + 1)
|
||||||
|
i = 0
|
||||||
|
for id in ids:
|
||||||
|
if not Pinte.objects.filter(pk=id).exists():
|
||||||
|
new_pinte = Pinte(pk=int(id))
|
||||||
|
new_pinte.save()
|
||||||
|
i += 1
|
||||||
|
messages.success(request, str(i) + " pinte(s) a(ont) été ajoutée(s)")
|
||||||
|
return redirect(reverse('gestion:productsIndex'))
|
||||||
|
return render(request, "form.html", {"form": form, "form_title": "Ajouter des pintes", "form_button": "Ajouter"})
|
||||||
|
|
||||||
|
@active_required
|
||||||
|
@login_required
|
||||||
|
@permission_required('gestion.change_pinte')
|
||||||
|
def release_pintes(request):
|
||||||
|
form = PinteForm(request.POST or None)
|
||||||
|
if form.is_valid():
|
||||||
|
ids = form.cleaned_data['ids']
|
||||||
|
if ids != "":
|
||||||
|
ids = ids.split(" ")
|
||||||
|
else:
|
||||||
|
ids = range(form.cleaned_data['begin'], form.cleaned_data['end'] + 1)
|
||||||
|
i = 0
|
||||||
|
for id in ids:
|
||||||
|
if allocate(id, None):
|
||||||
|
i += 1
|
||||||
|
messages.success(request, str(i) + " pinte(s) a(ont) été libérée(s)")
|
||||||
|
return redirect(reverse('gestion:productsIndex'))
|
||||||
|
return render(request, "form.html", {"form": form, "form_title": "Libérer des pintes", "form_button": "Libérer"})
|
||||||
|
|
||||||
|
@active_required
|
||||||
|
@login_required
|
||||||
|
@permission_required('gestion.view_pinte')
|
||||||
|
def pintes_list(request):
|
||||||
|
free_pintes = Pinte.objects.filter(current_owner=None)
|
||||||
|
taken_pintes = Pinte.objects.exclude(current_owner=None)
|
||||||
|
return render(request, "gestion/pintes_list.html", {"free_pintes": free_pintes, "taken_pintes": taken_pintes})
|
23
preferences/migrations/0002_auto_20181221_2151.py
Normal file
23
preferences/migrations/0002_auto_20181221_2151.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.1 on 2018-12-21 20:51
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('preferences', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='generalpreferences',
|
||||||
|
name='use_pinte_monitoring',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='historicalgeneralpreferences',
|
||||||
|
name='use_pinte_monitoring',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
23
preferences/migrations/0003_auto_20181223_1440.py
Normal file
23
preferences/migrations/0003_auto_20181223_1440.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.1 on 2018-12-23 13:40
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('preferences', '0002_auto_20181221_2151'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='generalpreferences',
|
||||||
|
name='lost_pintes_allowed',
|
||||||
|
field=models.PositiveIntegerField(default=0),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='historicalgeneralpreferences',
|
||||||
|
name='lost_pintes_allowed',
|
||||||
|
field=models.PositiveIntegerField(default=0),
|
||||||
|
),
|
||||||
|
]
|
|
@ -30,6 +30,8 @@ class GeneralPreferences(models.Model):
|
||||||
secretary = models.CharField(max_length=255, blank=True)
|
secretary = models.CharField(max_length=255, blank=True)
|
||||||
brewer = models.CharField(max_length=255, blank=True)
|
brewer = models.CharField(max_length=255, blank=True)
|
||||||
grocer = models.CharField(max_length=255, blank=True)
|
grocer = models.CharField(max_length=255, blank=True)
|
||||||
|
use_pinte_monitoring = models.BooleanField(default=False)
|
||||||
|
lost_pintes_allowed = models.PositiveIntegerField(default=0)
|
||||||
history = HistoricalRecords()
|
history = HistoricalRecords()
|
||||||
|
|
||||||
class Cotisation(models.Model):
|
class Cotisation(models.Model):
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block entete %}Administration{% endblock %}
|
{% block entete %}Administration{% endblock %}
|
||||||
{% block nav %}
|
{% block navbar %}
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#first" class="active">Message global</a></li>
|
<li><a href="#first">Message global</a></li>
|
||||||
<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>
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -75,6 +76,27 @@
|
||||||
{{form.brewer}}
|
{{form.brewer}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section id="fourth" class="main">
|
||||||
|
<div class="spotlight">
|
||||||
|
<div class="content">
|
||||||
|
<header class="major">
|
||||||
|
<h2>Suivi de pintes</h2>
|
||||||
|
</header>
|
||||||
|
<div class="row uniform">
|
||||||
|
<div class="12u">
|
||||||
|
{{form.use_pinte_monitoring}}
|
||||||
|
<label for="{{form.use_pinte_monitoring.id_for_label}}">Utiliser le suivi de pinte ?</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row uniform">
|
||||||
|
<div class="12u">
|
||||||
|
{{form.lost_pintes_allowed}}
|
||||||
|
<label for="{{form.lost_pintes_allowed.id_for_label}}">Nombre de pintes non rendues avant d'interdire la consommation (0 pour ne jamais l'interdire, nécessite le suivi de pinte actif)</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row uniform">
|
<div class="row uniform">
|
||||||
<div class="12u">
|
<div class="12u">
|
||||||
<button type="submit">Enregistrer</button>
|
<button type="submit">Enregistrer</button>
|
||||||
|
|
|
@ -14,4 +14,5 @@ urlpatterns = [
|
||||||
path('editPaymentMethod/<int:pk>', views.editPaymentMethod, name="editPaymentMethod"),
|
path('editPaymentMethod/<int:pk>', views.editPaymentMethod, name="editPaymentMethod"),
|
||||||
path('deletePaymentMethod/<int:pk>', views.deletePaymentMethod, name="deletePaymentMethod"),
|
path('deletePaymentMethod/<int:pk>', views.deletePaymentMethod, name="deletePaymentMethod"),
|
||||||
path('inactive', views.inactive, name="inactive"),
|
path('inactive', views.inactive, name="inactive"),
|
||||||
|
path('getConfig', views.get_config, name="getConfig"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
|
import json
|
||||||
|
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.forms.models import model_to_dict
|
||||||
|
|
||||||
from coopeV3.acl import active_required
|
from coopeV3.acl import active_required
|
||||||
|
|
||||||
|
@ -239,3 +243,13 @@ def inactive(request):
|
||||||
gp, _ = GeneralPreferences.objects.get_or_create(pk=1)
|
gp, _ = GeneralPreferences.objects.get_or_create(pk=1)
|
||||||
return render(request, 'preferences/inactive.html', {"message": gp.active_message})
|
return render(request, 'preferences/inactive.html', {"message": gp.active_message})
|
||||||
|
|
||||||
|
########## Config ##########
|
||||||
|
|
||||||
|
def get_config(request):
|
||||||
|
"""
|
||||||
|
Load the config and return it in a json format
|
||||||
|
"""
|
||||||
|
gp,_ = GeneralPreferences.objects.get_or_create(pk=1)
|
||||||
|
data = json.dumps(model_to_dict(gp))
|
||||||
|
return HttpResponse(data, content_type='application/json')
|
||||||
|
|
|
@ -5,20 +5,32 @@ paymentMethod = null
|
||||||
balance = 0
|
balance = 0
|
||||||
username = ""
|
username = ""
|
||||||
id = 0
|
id = 0
|
||||||
|
listPintes = []
|
||||||
|
nbPintes = 0;
|
||||||
|
use_pinte_monitoring = false;
|
||||||
|
|
||||||
|
function get_config(){
|
||||||
|
res = $.get("../preferences/getConfig", function(data){
|
||||||
|
console.log(data.use_pinte_monitoring)
|
||||||
|
use_pinte_monitoring = data.use_pinte_monitoring;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function get_product(barcode){
|
function get_product(barcode){
|
||||||
res = $.get("getProduct/" + barcode, function(data){
|
res = $.get("getProduct/" + barcode, function(data){
|
||||||
add_product(data.pk, data.barcode, data.name, data.amount);
|
nbPintes += data.nb_pintes;
|
||||||
|
add_product(data.pk, data.barcode, data.name, data.amount, data.needQuantityButton);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_menu(barcode){
|
function get_menu(barcode){
|
||||||
res = $.get("getMenu/" + barcode, function(data){
|
res = $.get("getMenu/" + barcode, function(data){
|
||||||
add_menu(data.pk, data.barcode, data.name, data.amount);
|
nbPintes += data.nb_pintes;
|
||||||
|
add_menu(data.pk, data.barcode, data.name, data.amount, data.needQuantityButton);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_product(pk, barcode, name, amount){
|
function add_product(pk, barcode, name, amount, needQuantityButton){
|
||||||
exist = false
|
exist = false
|
||||||
index = -1
|
index = -1
|
||||||
for(k=0;k < products.length; k++){
|
for(k=0;k < products.length; k++){
|
||||||
|
@ -27,10 +39,18 @@ function add_product(pk, barcode, name, amount){
|
||||||
index = k
|
index = k
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(exist){
|
if(needQuantityButton){
|
||||||
products[index].quantity += 1;
|
quantity = parseInt(window.prompt("Quantité ?",""));
|
||||||
}else{
|
}else{
|
||||||
products.push({"pk": pk, "barcode": barcode, "name": name, "amount": amount, "quantity": 1});
|
quantity = 1;
|
||||||
|
}
|
||||||
|
if(quantity == null || !Number.isInteger(quantity)){
|
||||||
|
quantity = 1;
|
||||||
|
}
|
||||||
|
if(exist){
|
||||||
|
products[index].quantity += quantity;
|
||||||
|
}else{
|
||||||
|
products.push({"pk": pk, "barcode": barcode, "name": name, "amount": amount, "quantity": quantity});
|
||||||
}
|
}
|
||||||
generate_html()
|
generate_html()
|
||||||
}
|
}
|
||||||
|
@ -53,7 +73,7 @@ function add_menu(pk, barcode, name, amount){
|
||||||
}
|
}
|
||||||
|
|
||||||
function generate_html(){
|
function generate_html(){
|
||||||
html =""
|
html = "";
|
||||||
for(k=0;k<products.length;k++){
|
for(k=0;k<products.length;k++){
|
||||||
product = products[k]
|
product = products[k]
|
||||||
html += '<tr><td>' + product.barcode + '</td><td>' + product.name + '</td><td>' + String(product.amount) + '</td><td><input type="number" data-target="' + String(k) + '" onChange="updateInput(this)" value="' + String(product.quantity) + '"/></td><td>' + String(Number((product.quantity * product.amount).toFixed(2))) + '</td></tr>';
|
html += '<tr><td>' + product.barcode + '</td><td>' + product.name + '</td><td>' + String(product.amount) + '</td><td><input type="number" data-target="' + String(k) + '" onChange="updateInput(this)" value="' + String(product.quantity) + '"/></td><td>' + String(Number((product.quantity * product.amount).toFixed(2))) + '</td></tr>';
|
||||||
|
@ -94,6 +114,7 @@ function updateMenuInput(a){
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
get_config();
|
||||||
$(".product").click(function(){
|
$(".product").click(function(){
|
||||||
product = get_product($(this).attr('target'));
|
product = get_product($(this).attr('target'));
|
||||||
});
|
});
|
||||||
|
@ -113,7 +134,25 @@ $(document).ready(function(){
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$(".pay_button").click(function(){
|
$(".pay_button").click(function(){
|
||||||
$.post("order", {"user":id, "paymentMethod": $(this).attr('data-payment'), "order_length": products.length + menus.length, "order": JSON.stringify(products), "amount": total, "menus": JSON.stringify(menus)}, function(data){
|
if(use_pinte_monitoring){
|
||||||
|
message = "Il reste " + nbPintes.toString() + " pintes à renseigner. Numéro de la pinte ?"
|
||||||
|
while(nbPintes > 0){
|
||||||
|
id_pinte = window.prompt(message,"");
|
||||||
|
if(id_pinte == null){
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
id_pinte = parseInt(id_pinte);
|
||||||
|
if(!Number.isInteger(id_pinte) || id_pinte < 0){
|
||||||
|
message = "Numéro incorrect. Il reste " + nbPintes.toString() + " pintes à renseigner. Numéro de la pinte ?";
|
||||||
|
}else{
|
||||||
|
listPintes.push(id_pinte)
|
||||||
|
nbPintes -= 1;
|
||||||
|
message = "Il reste " + nbPintes.toString() + " pintes à renseigner. Numéro de la pinte ?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$.post("order", {"user":id, "paymentMethod": $(this).attr('data-payment'), "order_length": products.length + menus.length, "order": JSON.stringify(products), "amount": total, "menus": JSON.stringify(menus), "listPintes": JSON.stringify(listPintes)}, function(data){
|
||||||
alert(data);
|
alert(data);
|
||||||
location.reload();
|
location.reload();
|
||||||
}).fail(function(data){
|
}).fail(function(data){
|
||||||
|
|
|
@ -119,6 +119,13 @@ class Profile(models.Model):
|
||||||
alcohol += consumption.quantity * float(product.deg) * product.volume * 0.79 /10 /1000
|
alcohol += consumption.quantity * float(product.deg) * product.volume * 0.79 /10 /1000
|
||||||
return alcohol
|
return alcohol
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nb_pintes(self):
|
||||||
|
"""
|
||||||
|
Return the number of pintes currently owned
|
||||||
|
"""
|
||||||
|
return self.user.pintes_owned_currently.count()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.user)
|
return str(self.user)
|
||||||
|
|
||||||
|
@ -127,6 +134,9 @@ class Profile(models.Model):
|
||||||
Tente de retourner l'attribut de l'instance et si l'attribut n'existe pas,
|
Tente de retourner l'attribut de l'instance et si l'attribut n'existe pas,
|
||||||
tente de retourner l'attribut de l'user associé à l'instance
|
tente de retourner l'attribut de l'user associé à l'instance
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
|
r = self.__getattribute__(name)
|
||||||
|
except AttributeError:
|
||||||
try:
|
try:
|
||||||
r = super().__getattr__(name)
|
r = super().__getattr__(name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
Loading…
Reference in a new issue