diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..12d55cf
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "python.pythonPath": "/home/nanoy/.virtualenvs/coopeV3/bin/python"
+}
\ No newline at end of file
diff --git a/coopeV3/settings.py b/coopeV3/settings.py
index e5cb22a..8b51238 100644
--- a/coopeV3/settings.py
+++ b/coopeV3/settings.py
@@ -40,6 +40,9 @@ INSTALLED_APPS = [
'gestion',
'users',
'preferences',
+ 'coopeV3',
+ 'dal',
+ 'dal_select2',
]
MIDDLEWARE = [
diff --git a/coopeV3/templatetags/__init__.py b/coopeV3/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/coopeV3/templatetags/vip.py b/coopeV3/templatetags/vip.py
new file mode 100644
index 0000000..f8be55c
--- /dev/null
+++ b/coopeV3/templatetags/vip.py
@@ -0,0 +1,40 @@
+from django import template
+
+from preferences.models import GeneralPreferences
+
+register = template.Library()
+
+@register.simple_tag
+def president():
+ gp,_ = GeneralPreferences.objects.get_or_create(pk=1)
+ return gp.president
+
+@register.simple_tag
+def vice_president():
+ gp,_ = GeneralPreferences.objects.get_or_create(pk=1)
+ return gp.vice_president
+
+@register.simple_tag
+def treasurer():
+ gp,_ = GeneralPreferences.objects.get_or_create(pk=1)
+ return gp.treasurer
+
+@register.simple_tag
+def secretary():
+ gp,_ = GeneralPreferences.objects.get_or_create(pk=1)
+ return gp.secretary
+
+@register.simple_tag
+def brewer():
+ gp,_ = GeneralPreferences.objects.get_or_create(pk=1)
+ return gp.brewer
+
+@register.simple_tag
+def grocer():
+ gp,_ = GeneralPreferences.objects.get_or_create(pk=1)
+ return gp.grocer
+
+@register.simple_tag
+def global_message():
+ gp,_ = GeneralPreferences.objects.get_or_create(pk=1)
+ return gp.global_message
diff --git a/coopeV3/urls.py b/coopeV3/urls.py
index b09d44a..62c9708 100644
--- a/coopeV3/urls.py
+++ b/coopeV3/urls.py
@@ -23,4 +23,5 @@ urlpatterns = [
path('admin/', admin.site.urls),
path('users/', include('users.urls')),
path('gestion/', include('gestion.urls')),
+ path('preferences/', include('preferences.urls')),
]
diff --git a/coopeV3/views.py b/coopeV3/views.py
index 67f8796..e1da5b8 100644
--- a/coopeV3/views.py
+++ b/coopeV3/views.py
@@ -2,7 +2,7 @@ from django.shortcuts import redirect
from django.urls import reverse
def home(request):
- if request.user is not None:
+ if request.user.is_authenticated:
if(request.user.has_perm('gestion.can_manage')):
return redirect(reverse('gestion:manage'))
else:
diff --git a/coopeV3/widgets.py b/coopeV3/widgets.py
index 986bdc0..750e2f2 100644
--- a/coopeV3/widgets.py
+++ b/coopeV3/widgets.py
@@ -1,13 +1,11 @@
-from django.forms.widgets import Input
+from django.forms.widgets import Select, Input
from django.template import Context, Template
from django.template.loader import get_template
-class SearchField:
- def __init__(self, url):
- self.url = url
+class SearchField(Input):
def render(self, name, value, attrs=None):
- super().render(name, value, attrs)
+ #super().render(name, value, attrs)
template = get_template('search_field.html')
context = Context({})
return template.render(context)
diff --git a/gestion/admin.py b/gestion/admin.py
index 8c38f3f..ff8c544 100644
--- a/gestion/admin.py
+++ b/gestion/admin.py
@@ -1,3 +1,8 @@
from django.contrib import admin
-# Register your models here.
+from .models import Reload, Refund, Product, Keg
+
+admin.site.register(Reload)
+admin.site.register(Refund)
+admin.site.register(Product)
+admin.site.register(Keg)
diff --git a/gestion/forms.py b/gestion/forms.py
new file mode 100644
index 0000000..ebdd877
--- /dev/null
+++ b/gestion/forms.py
@@ -0,0 +1,37 @@
+from django import forms
+from django.contrib.auth.models import User
+
+from dal import autocomplete
+
+from .models import Reload, Refund, Product, Keg, Menu
+from preferences.models import PaymentMethod
+from coopeV3.widgets import SearchField
+
+class ReloadForm(forms.ModelForm):
+ class Meta:
+ model = Reload
+ fields = ("customer", "amount", "PaymentMethod")
+
+class RefundForm(forms.ModelForm):
+ class Meta:
+ model = Refund
+ fields = ("customer", "amount")
+
+class ProductForm(forms.ModelForm):
+ class Meta:
+ model = Product
+ fields = "__all__"
+
+class KegForm(forms.ModelForm):
+ class Meta:
+ model = Keg
+ fields = "__all__"
+
+class MenuForm(forms.ModelForm):
+ class Meta:
+ model = Menu
+ fields = "__all__"
+
+class GestionForm(forms.Form):
+ client = forms.ModelChoiceField(queryset=User.objects.filter(is_active=True), required=True, label="Client", widget=autocomplete.ModelSelect2(url='users:active-users-autocomplete', attrs={'data-minimum-input-length':2}))
+ paymentMethod = forms.ModelChoiceField(queryset=PaymentMethod.objects.all(), required=True, label="Moyen de paiement")
diff --git a/gestion/migrations/0001_initial.py b/gestion/migrations/0001_initial.py
index 965fc23..d155d5f 100644
--- a/gestion/migrations/0001_initial.py
+++ b/gestion/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1 on 2018-08-31 12:45
+# Generated by Django 2.1 on 2018-10-04 09:32
from django.conf import settings
from django.db import migrations, models
@@ -11,23 +11,11 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('preferences', '0001_initial'),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
- migrations.CreateModel(
- name='Barrel',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=20)),
- ('stockHold', models.IntegerField(default=0)),
- ('barcode', models.CharField(max_length=20, unique=True)),
- ('amount', models.DecimalField(decimal_places=2, max_digits=5)),
- ('capacity', models.IntegerField(default=30)),
- ('active', models.BooleanField(default=False)),
- ],
- ),
migrations.CreateModel(
name='ConsumptionHistory',
fields=[
@@ -39,14 +27,38 @@ class Migration(migrations.Migration):
('customer', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='consumption_taken', to=settings.AUTH_USER_MODEL)),
],
),
+ migrations.CreateModel(
+ name='Keg',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=20, unique=True, verbose_name='Nom')),
+ ('stockHold', models.IntegerField(default=0, verbose_name='Stock en soute')),
+ ('barcode', models.CharField(max_length=20, unique=True, verbose_name='Code barre')),
+ ('amount', models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Prix du fût')),
+ ('capacity', models.IntegerField(default=30, verbose_name='Capacité (L)')),
+ ('is_active', models.BooleanField(default=False, verbose_name='Actif')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='KegHistory',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('openingDate', models.DateTimeField(auto_now_add=True)),
+ ('quantitySold', models.DecimalField(decimal_places=2, max_digits=5)),
+ ('amountSold', models.DecimalField(decimal_places=2, max_digits=5)),
+ ('closingDate', models.DateTimeField()),
+ ('isCurrentKegHistory', models.BooleanField(default=True)),
+ ('Keg', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='gestion.Keg')),
+ ],
+ ),
migrations.CreateModel(
name='Menu',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=255)),
- ('amount', models.DecimalField(decimal_places=2, max_digits=5)),
- ('barcode', models.CharField(max_length=20, unique=True)),
- ('is_active', models.BooleanField(default=False)),
+ ('name', models.CharField(max_length=255, verbose_name='Nom')),
+ ('amount', models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Montant')),
+ ('barcode', models.CharField(max_length=20, unique=True, verbose_name='Code barre')),
+ ('is_active', models.BooleanField(default=False, verbose_name='Actif')),
],
),
migrations.CreateModel(
@@ -66,17 +78,16 @@ class Migration(migrations.Migration):
name='Product',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=40)),
- ('amount', models.DecimalField(decimal_places=2, max_digits=5)),
- ('stockHold', models.IntegerField(default=0)),
- ('stockBar', models.IntegerField(default=0)),
- ('barcode', models.CharField(max_length=20, unique=True)),
- ('category', models.CharField(choices=[('PP', 'Pinte Pression'), ('DP', 'Demi Pression'), ('GP', 'Galopin pression'), ('BT', 'Bouteille'), ('SO', 'Soft'), ('FO', 'Bouffe')], default='FO', max_length=2)),
- ('needQuantityButton', models.BooleanField(default=False)),
- ('is_active', models.BooleanField(default=True)),
- ('is_beer', models.BooleanField(default=False)),
+ ('name', models.CharField(max_length=40, unique=True, verbose_name='Nom')),
+ ('amount', models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Prix de vente')),
+ ('stockHold', models.IntegerField(default=0, verbose_name='Stock en soute')),
+ ('stockBar', models.IntegerField(default=0, verbose_name='Stock en bar')),
+ ('barcode', models.CharField(max_length=20, unique=True, verbose_name='Code barre')),
+ ('category', models.CharField(choices=[('PP', 'Pinte Pression'), ('DP', 'Demi Pression'), ('GP', 'Galopin pression'), ('BT', 'Bouteille'), ('SO', 'Soft'), ('FO', 'Bouffe autre que panini'), ('PA', 'Bouffe pour panini')], default='FO', max_length=2, verbose_name='Catégorie')),
+ ('needQuantityButton', models.BooleanField(default=False, verbose_name='Bouton quantité')),
+ ('is_active', models.BooleanField(default=True, verbose_name='Actif')),
('volume', models.IntegerField(default=0)),
- ('deg', models.DecimalField(decimal_places=2, default=0, max_digits=5)),
+ ('deg', models.DecimalField(decimal_places=2, default=0, max_digits=5, verbose_name='Degré')),
],
),
migrations.CreateModel(
@@ -84,8 +95,8 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateTimeField(auto_now_add=True)),
- ('barrel', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='gestion.Barrel')),
('coopeman', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
+ ('keg', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='gestion.Keg')),
],
),
migrations.CreateModel(
@@ -93,20 +104,20 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateTimeField(auto_now_add=True)),
- ('amount', models.DecimalField(decimal_places=2, max_digits=5)),
+ ('amount', models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Montant')),
('coopeman', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='refund_realized', to=settings.AUTH_USER_MODEL)),
- ('cutsomer', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='refund_taken', to=settings.AUTH_USER_MODEL)),
+ ('customer', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='refund_taken', to=settings.AUTH_USER_MODEL, verbose_name='Client')),
],
),
migrations.CreateModel(
name='Reload',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('amount', models.DecimalField(decimal_places=2, max_digits=5)),
+ ('amount', models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Montant')),
('date', models.DateTimeField(auto_now_add=True)),
- ('PaymentMethod', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='preferences.PaymentMethod')),
+ ('PaymentMethod', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='preferences.PaymentMethod', verbose_name='Moyen de paiement')),
('coopeman', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='reload_realized', to=settings.AUTH_USER_MODEL)),
- ('customer', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='reload_taken', to=settings.AUTH_USER_MODEL)),
+ ('customer', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='reload_taken', to=settings.AUTH_USER_MODEL, verbose_name='Client')),
],
),
migrations.CreateModel(
@@ -119,7 +130,22 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='menu',
name='articles',
- field=models.ManyToManyField(to='gestion.Product'),
+ field=models.ManyToManyField(to='gestion.Product', verbose_name='Produits'),
+ ),
+ migrations.AddField(
+ model_name='keg',
+ name='demi',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='futd', to='gestion.Product', validators=[gestion.models.isDemi]),
+ ),
+ migrations.AddField(
+ model_name='keg',
+ name='galopin',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='futg', to='gestion.Product', validators=[gestion.models.isGalopin]),
+ ),
+ migrations.AddField(
+ model_name='keg',
+ name='pinte',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='futp', to='gestion.Product', validators=[gestion.models.isPinte]),
),
migrations.AddField(
model_name='consumptionhistory',
@@ -136,19 +162,4 @@ class Migration(migrations.Migration):
name='product',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='gestion.Product'),
),
- migrations.AddField(
- model_name='barrel',
- name='demi',
- field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='futd', to='gestion.Product', validators=[gestion.models.isDemi]),
- ),
- migrations.AddField(
- model_name='barrel',
- name='galopin',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='futg', to='gestion.Product', validators=[gestion.models.isGalopin]),
- ),
- migrations.AddField(
- model_name='barrel',
- name='pinte',
- field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='futp', to='gestion.Product', validators=[gestion.models.isPinte]),
- ),
]
diff --git a/gestion/models.py b/gestion/models.py
index 72961aa..b4fffc6 100644
--- a/gestion/models.py
+++ b/gestion/models.py
@@ -1,6 +1,8 @@
from django.db import models
from django.contrib.auth.models import User
from preferences.models import PaymentMethod
+from django.core.exceptions import ValidationError
+
class Product(models.Model):
P_PRESSION = 'PP'
D_PRESSION = 'DP'
@@ -8,28 +10,29 @@ class Product(models.Model):
BOTTLE = 'BT'
SOFT = 'SO'
FOOD = 'FO'
+ PANINI = 'PA'
TYPEINPUT_CHOICES_CATEGORIE = (
(P_PRESSION, "Pinte Pression"),
(D_PRESSION, "Demi Pression"),
(G_PRESSION, "Galopin pression"),
(BOTTLE, "Bouteille"),
(SOFT, "Soft"),
- (FOOD, "Bouffe"),
+ (FOOD, "Bouffe autre que panini"),
+ (PANINI, "Bouffe pour panini"),
)
- name = models.CharField(max_length=40)
- amount = models.DecimalField(max_digits=5, decimal_places=2)
- stockHold = models.IntegerField(default=0)
- stockBar = models.IntegerField(default=0)
- barcode= models.CharField(max_length=20, unique=True)
- category = models.CharField(max_length=2, choices=TYPEINPUT_CHOICES_CATEGORIE, default=FOOD)
- needQuantityButton = models.BooleanField(default=False)
- is_active = models.BooleanField(default=True)
- is_beer = models.BooleanField(default=False)
+ name = models.CharField(max_length=40, verbose_name="Nom", unique=True)
+ amount = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Prix de vente")
+ stockHold = models.IntegerField(default=0, verbose_name="Stock en soute")
+ stockBar = models.IntegerField(default=0, verbose_name="Stock en bar")
+ barcode= models.CharField(max_length=20, unique=True, verbose_name="Code barre")
+ category = models.CharField(max_length=2, choices=TYPEINPUT_CHOICES_CATEGORIE, default=FOOD, verbose_name="Catégorie")
+ needQuantityButton = models.BooleanField(default=False, verbose_name="Bouton quantité")
+ is_active = models.BooleanField(default=True, verbose_name="Actif")
volume = models.IntegerField(default=0)
- deg = models.DecimalField(default=0,max_digits=5, decimal_places=2)
+ deg = models.DecimalField(default=0,max_digits=5, decimal_places=2, verbose_name="Degré")
def __str__(self):
- return self.nom
+ return self.name
def isPinte(id):
@@ -43,7 +46,7 @@ def isPinte(id):
def isDemi(id):
product = Product.objects.get(id=id)
- if produit.category != Product.D_PRESSION:
+ if product.category != Product.D_PRESSION:
raise ValidationError(
('%(product)s n\'est pas un demi'),
params={'product': product},
@@ -57,24 +60,32 @@ def isGalopin(id):
params={'product': product},
)
-class Barrel(models.Model):
- name = models.CharField(max_length=20)
- stockHold = models.IntegerField(default=0)
- barcode = models.CharField(max_length=20, unique=True)
- amount = models.DecimalField(max_digits=5, decimal_places=2)
- capacity = models.IntegerField(default=30)
+class Keg(models.Model):
+ name = models.CharField(max_length=20, unique=True, verbose_name="Nom")
+ stockHold = models.IntegerField(default=0, verbose_name="Stock en soute")
+ barcode = models.CharField(max_length=20, unique=True, verbose_name="Code barre")
+ amount = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Prix du fût")
+ capacity = models.IntegerField(default=30, verbose_name="Capacité (L)")
pinte = models.ForeignKey(Product, on_delete=models.PROTECT, related_name="futp", validators=[isPinte])
demi = models.ForeignKey(Product, on_delete=models.PROTECT, related_name="futd", validators=[isDemi])
galopin = models.ForeignKey(Product, on_delete=models.PROTECT, related_name="futg", validators=[isGalopin],null=True, blank=True)
- active= models.BooleanField(default=False)
+ is_active = models.BooleanField(default=False, verbose_name="Actif")
def __str__(self):
return self.name
+class KegHistory(models.Model):
+ Keg = models.ForeignKey(Keg, on_delete=models.PROTECT)
+ openingDate = models.DateTimeField(auto_now_add=True)
+ quantitySold = models.DecimalField(decimal_places=2, max_digits=5)
+ amountSold = models.DecimalField(decimal_places=2, max_digits=5)
+ closingDate = models.DateTimeField()
+ isCurrentKegHistory = models.BooleanField(default=True)
+
class Reload(models.Model):
- customer = models.ForeignKey(User, on_delete=models.PROTECT, related_name="reload_taken")
- amount = models.DecimalField(max_digits=5, decimal_places=2)
- PaymentMethod = models.ForeignKey(PaymentMethod, on_delete=models.PROTECT)
+ customer = models.ForeignKey(User, on_delete=models.PROTECT, related_name="reload_taken", verbose_name="Client")
+ amount = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Montant")
+ PaymentMethod = models.ForeignKey(PaymentMethod, on_delete=models.PROTECT, verbose_name="Moyen de paiement")
coopeman = models.ForeignKey(User, on_delete=models.PROTECT, related_name="reload_realized")
date = models.DateTimeField(auto_now_add=True)
@@ -83,12 +94,12 @@ class Reload(models.Model):
class Raming(models.Model):
- barrel = models.ForeignKey(Barrel, on_delete=models.PROTECT)
+ keg = models.ForeignKey(Keg, on_delete=models.PROTECT)
coopeman = models.ForeignKey(User, on_delete=models.PROTECT)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
- return "Percussion d'un {0} effectué par {1} le {2}".format(self.barrel, self.coopeman, self.date)
+ return "Percussion d'un {0} effectué par {1} le {2}".format(self.keg, self.coopeman, self.date)
class Stocking(models.Model):
date = models.DateTimeField(auto_now_add=True)
@@ -99,8 +110,8 @@ class Stocking(models.Model):
class Refund(models.Model):
date = models.DateTimeField(auto_now_add=True)
- cutsomer = models.ForeignKey(User, on_delete=models.PROTECT, related_name="refund_taken")
- amount = models.DecimalField(max_digits=5, decimal_places=2)
+ customer = models.ForeignKey(User, on_delete=models.PROTECT, related_name="refund_taken", verbose_name="Client")
+ amount = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Montant")
coopeman = models.ForeignKey(User, on_delete=models.PROTECT, related_name="refund_realized")
def __str__(self):
@@ -108,11 +119,11 @@ class Refund(models.Model):
class Menu(models.Model):
- name = models.CharField(max_length=255)
- amount = models.DecimalField(max_digits=5, decimal_places=2)
- barcode = models.CharField(max_length=20, unique=True)
- articles = models.ManyToManyField(Product)
- is_active = models.BooleanField(default=False)
+ name = models.CharField(max_length=255, verbose_name="Nom")
+ amount = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Montant")
+ barcode = models.CharField(max_length=20, unique=True, verbose_name="Code barre")
+ articles = models.ManyToManyField(Product, verbose_name="Produits")
+ is_active = models.BooleanField(default=False, verbose_name="Actif")
def __str__(self):
return self.name
diff --git a/gestion/templates/gestion/manage.html b/gestion/templates/gestion/manage.html
new file mode 100644
index 0000000..ca522d7
--- /dev/null
+++ b/gestion/templates/gestion/manage.html
@@ -0,0 +1,212 @@
+{% extends "base.html" %}
+{% load static %}
+
+{%block entete%}
Gestion de la Coopé™
{%endblock%}
+
+{% block navbar %}
+
+{% endblock %}
+
+
+{% block content %}
+
+ UP
+
+
+
+
+
+
+
+
+
Récapitulatif
+
+
+
+
Produits
+
+
+
+
+
+
+{% if perms.gestion.cand_add_reload %}
+
+{% endif %}
+{% if perms.gestion.can_refund %}
+
+{% endif %}
+{{gestion_form.media}}
+
+{%endblock%}
diff --git a/gestion/templates/gestion/products_index.html b/gestion/templates/gestion/products_index.html
new file mode 100644
index 0000000..629dbe8
--- /dev/null
+++ b/gestion/templates/gestion/products_index.html
@@ -0,0 +1,55 @@
+{% extends 'base.html' %}
+{% block entete %}Gestion des produits
{% endblock %}
+{% block navbar%}
+
+{% endblock %}
+{% block content %}
+
+
+ Actions possibles :
+
+
+
+
+ Actions possibles :
+
+
+
+
+ Actions possibles :
+
+
+
+
+ Actions possibles :
+
+
+{% endblock %}
diff --git a/gestion/templates/gestion/products_list.html b/gestion/templates/gestion/products_list.html
new file mode 100644
index 0000000..0c26c7f
--- /dev/null
+++ b/gestion/templates/gestion/products_list.html
@@ -0,0 +1,55 @@
+{% extends 'base.html' %}
+{% block entete %}Gestion des produits
{% endblock %}
+{% block navbar%}
+
+{% endblock %}
+{% block content %}
+
+
+ Actions possibles :
+
+
+
+
+ Actions possibles :
+
+
+
+
+ Actions possibles :
+
+
+
+
+ Actions possibles :
+
+
+{% endblock %}
diff --git a/gestion/urls.py b/gestion/urls.py
index 4e9d48c..3e0e17b 100644
--- a/gestion/urls.py
+++ b/gestion/urls.py
@@ -5,4 +5,11 @@ from . import views
app_name="gestion"
urlpatterns = [
path('manage', views.manage, name="manage"),
+ path('reload', views.reload, name="reload"),
+ path('refund', views.refund, name="refund"),
+ path('productsIndex', views.productsIndex, name="productsIndex"),
+ path('addProduct', views.addProduct, name="addProduct"),
+ path('addKeg', views.addKeg, name="addKeg"),
+ path('addMenu', views.addMenu, name="addMenu"),
+ path('getProduct/', views.getProduct, name="getProduct"),
]
diff --git a/gestion/views.py b/gestion/views.py
index c8dd192..59e8b6d 100644
--- a/gestion/views.py
+++ b/gestion/views.py
@@ -1,4 +1,108 @@
-from django.shortcuts import render
+from django.shortcuts import render, redirect
+from django.contrib import messages
+from django.urls import reverse
+from django.http import HttpResponse
+from django.contrib.auth.models import User
+
+import json
+from dal import autocomplete
+
+from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm
+from .models import Product, Menu, Keg
def manage(request):
- return render(request, "base.html")
+ gestion_form = GestionForm(request.POST or None)
+ reload_form = ReloadForm(request.POST or None)
+ refund_form = RefundForm(request.POST or None)
+ bieresPression = []
+ bieresBouteille = Product.objects.filter(category=Product.BOTTLE).filter(is_active=True)
+ panini = Product.objects.filter(category=Product.PANINI).filter(is_active=True)
+ food = Product.objects.filter(category=Product.FOOD).filter(is_active=True)
+ soft = Product.objects.filter(category=Product.SOFT).filter(is_active=True)
+ menus = Menu.objects.filter(is_active=True)
+ kegs = Keg.objects.filter(is_active=True)
+ for keg in kegs:
+ if(keg.pinte):
+ bieresPression.append(keg.pinte)
+ if(keg.demi):
+ bieresPression.append(keg.demi)
+ if(keg.galopin):
+ bieresPression.append(keg.galopin)
+ return render(request, "gestion/manage.html", {"gestion_form": gestion_form, "reload_form": reload_form, "refund_form": refund_form, "bieresPression": bieresPression, "bieresBouteille": bieresBouteille, "panini": panini, "food": food, "soft": soft, "menus": menus})
+
+def reload(request):
+ reload_form = ReloadForm(request.POST or None)
+ if(reload_form.is_valid()):
+ reloadEntry = reload_form.save(commit=False)
+ reloadEntry.coopeman = request.user
+ reloadEntry.save()
+ user = reload_form.cleaned_data['customer']
+ amount = reload_form.cleaned_data['amount']
+ user.profile.credit += amount
+ user.save()
+ messages.success(request,"Le compte de " + user.username + " a bien été crédité de " + str(amount) + "€")
+ else:
+ messages.error(request, "Le rechargement a échoué")
+ return redirect(reverse('gestion:manage'))
+
+def refund(request):
+ refund_form = RefundForm(request.POST or None)
+ if(refund_form.is_valid()):
+ user = refund_form.cleaned_data['customer']
+ amount = refund_form.cleaned_data['amount']
+ if(amount <= user.profile.balance):
+ refundEntry = refund_form.save(commit = False)
+ refundEntry.coopeman = request.user
+ refundEntry.save()
+ user.profile.credit -= amount
+ user.save()
+ messages.success(request, "Le compte de " + user.username + " a bien été remboursé de " + str(amount) + "€")
+ else:
+ messages.error(request, "Impossible de rembourser l'utilisateur " + user.username + " de " + str(amount) + "€ : il n'a que " + str(user.profile.balance) + "€ sur son compte.")
+ else:
+ messages.error(request, "Le remboursement a échoué")
+ return redirect(reverse('gestion:manage'))
+
+def productsIndex(request):
+ return render(request, "gestion/products_index.html")
+
+def addProduct(request):
+ form = ProductForm(request.POST or None)
+ if(form.is_valid()):
+ form.save()
+ messages.success(request, "Le produit a bien été ajouté")
+ return redirect(reverse('gestion:productsIndex'))
+ return render(request, "form.html", {"form": form, "form_title": "Ajout d'un produit", "form_button": "Ajouter"})
+
+def productsList(request):
+ products = Product.objects.all()
+ return render(request, "gestion/products_list.html", {"products": products})
+
+def getProduct(request, barcode):
+ product = Product.objects.get(barcode=barcode)
+ data = json.dumps({"pk": product.pk, "barcode" : product.barcode, "name": product.name, "amount" : float(product.amount)})
+ return HttpResponse(data, content_type='application/json')
+
+
+########## Kegs ##########
+
+def addKeg(request):
+ form = KegForm(request.POST or None)
+ if(form.is_valid()):
+ keg = form.save()
+ messages.success(request, "Le fût " + keg.name + " a bien été ajouté")
+ return redirect(reverse('gestion:productsIndex'))
+ return render(request, "form.html", {"form":form, "form_title": "Ajout d'un fût", "form_button": "Ajouter"})
+
+
+########## Menus ##########
+
+def addMenu(request):
+ form = MenuForm(request.POST or None)
+ extra_css = "#id_articles{height:200px;}"
+ if(form.is_valid()):
+ menu = form.save()
+ messages.success(request, "Le menu " + menu.name + " a bien été ajouté")
+ return redirect(reverse('gestion:productsIndex'))
+ return render(request, "form.html", {"form":form, "form_title": "Ajout d'un menu", "form_button": "Ajouter", "extra_css": extra_css})
+
diff --git a/preferences/admin.py b/preferences/admin.py
index 8c38f3f..773c1f0 100644
--- a/preferences/admin.py
+++ b/preferences/admin.py
@@ -1,3 +1,8 @@
from django.contrib import admin
+from .models import PaymentMethod, GeneralPreferences, Cotisation
+
+admin.site.register(PaymentMethod)
+admin.site.register(GeneralPreferences)
+admin.site.register(Cotisation)
# Register your models here.
diff --git a/preferences/forms.py b/preferences/forms.py
new file mode 100644
index 0000000..6271cab
--- /dev/null
+++ b/preferences/forms.py
@@ -0,0 +1,30 @@
+from django import forms
+
+from .models import Cotisation, PaymentMethod, GeneralPreferences
+
+class CotisationForm(forms.ModelForm):
+ class Meta:
+ model = Cotisation
+ fields = "__all__"
+
+class PaymentMethodForm(forms.ModelForm):
+ class Meta:
+ model = PaymentMethod
+ fields = "__all__"
+
+
+class GeneralPreferencesForm(forms.ModelForm):
+ class Meta:
+ model = GeneralPreferences
+ fields = "__all__"
+ widgets = {
+ 'global_message': forms.Textarea(attrs={'placeholder': 'Message global à afficher sur le site'}),
+ 'active_message': forms.Textarea(attrs={'placeholder': 'Ce message s\'affichera si le site n\'est pas actif'}),
+ 'president': forms.TextInput(attrs={'placeholder': 'Président'}),
+ 'vice_president': forms.TextInput(attrs={'placeholder': 'Vice-président'}),
+ 'secretary': forms.TextInput(attrs={'placeholder': 'Secrétaire'}),
+ 'treasurer': forms.TextInput(attrs={'placeholder': 'Trésorier'}),
+ 'brewer': forms.TextInput(attrs={'placeholder': 'Maître brasseur'}),
+ 'grocer': forms.TextInput(attrs={'placeholder': 'Epic épicier'}),
+ }
+
diff --git a/preferences/migrations/0001_initial.py b/preferences/migrations/0001_initial.py
index 786a002..d587112 100644
--- a/preferences/migrations/0001_initial.py
+++ b/preferences/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1 on 2018-08-31 12:45
+# Generated by Django 2.1 on 2018-10-04 09:32
from django.db import migrations, models
@@ -11,6 +11,14 @@ class Migration(migrations.Migration):
]
operations = [
+ migrations.CreateModel(
+ name='Cotisation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('amount', models.DecimalField(decimal_places=2, max_digits=5, null=True, verbose_name='Montant')),
+ ('duration', models.PositiveIntegerField(verbose_name='Durée de la cotisation (jours)')),
+ ],
+ ),
migrations.CreateModel(
name='GeneralPreferences',
fields=[
@@ -30,7 +38,10 @@ class Migration(migrations.Migration):
name='PaymentMethod',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=255)),
+ ('name', models.CharField(max_length=255, verbose_name='Nom')),
+ ('is_active', models.BooleanField(default=True)),
+ ('is_usable_in_cotisation', models.BooleanField(default=True)),
+ ('affect_balance', models.BooleanField(default=False)),
],
),
]
diff --git a/preferences/models.py b/preferences/models.py
index fe5e8a0..0c5dbb7 100644
--- a/preferences/models.py
+++ b/preferences/models.py
@@ -1,7 +1,10 @@
from django.db import models
class PaymentMethod(models.Model):
- name = models.CharField(max_length=255)
+ name = models.CharField(max_length=255, verbose_name="Nom")
+ is_active = models.BooleanField(default=True, verbose_name="Actif")
+ is_usable_in_cotisation = models.BooleanField(default=True, verbose_name="Utilisable pour les cotisations")
+ affect_balance = models.BooleanField(default=False, verbose_name="Affecte le solde")
def __str__(self):
return self.name
@@ -16,3 +19,10 @@ class GeneralPreferences(models.Model):
secretary = models.CharField(max_length=255, blank=True)
brewer = models.CharField(max_length=255, blank=True)
grocer = models.CharField(max_length=255, blank=True)
+
+class Cotisation(models.Model):
+ amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, verbose_name="Montant")
+ duration = models.PositiveIntegerField(verbose_name="Durée de la cotisation (jours)")
+
+ def __str__(self):
+ return "Cotisation de " + str(self.duration) + " jours pour le prix de " + str(self.amount) + "€"
diff --git a/preferences/templates/preferences/cotisations_index.html b/preferences/templates/preferences/cotisations_index.html
new file mode 100644
index 0000000..aa7d130
--- /dev/null
+++ b/preferences/templates/preferences/cotisations_index.html
@@ -0,0 +1,35 @@
+{% extends "base.html" %}
+{% block entete %}Gestion des cotisations
{% endblock %}
+{% block navbar %}
+
+{% endblock %}
+{% block content %}
+
+
+ Liste des cotisations
+
+ Créer une cotisation
+
+
+
+
+ | Durée de cotisation |
+ Prix |
+ Administration |
+
+
+
+ {% for cotisation in cotisations %}
+
+ | {{ cotisation.duration }} jours |
+ {{ cotisation.amount }} € |
+ Modifier Supprimer |
+
+ {% endfor %}
+
+
+
+
+{% endblock %}
diff --git a/preferences/templates/preferences/general_preferences.html b/preferences/templates/preferences/general_preferences.html
new file mode 100644
index 0000000..94229dd
--- /dev/null
+++ b/preferences/templates/preferences/general_preferences.html
@@ -0,0 +1,89 @@
+{% extends 'base.html' %}
+{% block entete %}
+Administration
+{% endblock %}
+{% block nav %}
+
+{% endblock %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/preferences/templates/preferences/payment_methods_index.html b/preferences/templates/preferences/payment_methods_index.html
new file mode 100644
index 0000000..f5ef170
--- /dev/null
+++ b/preferences/templates/preferences/payment_methods_index.html
@@ -0,0 +1,39 @@
+{% extends "base.html" %}
+{% block entete %}Gestion des moyens de paiement
{% endblock %}
+{% block navbar %}
+
+{% endblock %}
+{% block content %}
+
+
+ Liste des moyens de paiement
+
+ Créer un moyen de paiement
+
+
+
+
+ | Nom |
+ Actif ? |
+ Utilisable dans les cotisations |
+ Affecte le solde |
+ Administration |
+
+
+
+ {% for pm in paymentMethods %}
+
+ | {{ pm.name }} |
+ {{ pm.is_active | yesno:"Oui, Non"}} |
+ {{ pm.is_usable_in_cotisation | yesno:"Oui, Non" }} |
+ {{ pm.affect_balance | yesno:"Oui, Non" }} |
+ Modifier Supprimer |
+
+ {% endfor %}
+
+
+
+
+{% endblock %}
diff --git a/preferences/urls.py b/preferences/urls.py
new file mode 100644
index 0000000..e45f5b4
--- /dev/null
+++ b/preferences/urls.py
@@ -0,0 +1,16 @@
+from django.urls import path
+
+from . import views
+
+app_name="preferences"
+urlpatterns = [
+ path('generalPreferences', views.generalPreferences, name="generalPreferences"),
+ path('cotisationsIndex', views.cotisationsIndex, name="cotisationsIndex"),
+ path('addCotisation', views.addCotisation, name="addCotisation"),
+ path('editCotisation/', views.editCotisation, name="editCotisation"),
+ path('deleteCotisation/', views.deleteCotisation, name="deleteCotisation"),
+ path('paymentMethodsIndex', views.paymentMethodsIndex, name="paymentMethodsIndex"),
+ path('addPaymentMethod', views.addPaymentMethod, name="addPaymentMethod"),
+ path('editPaymentMethod/', views.editPaymentMethod, name="editPaymentMethod"),
+ path('deletePaymentMethod/', views.deletePaymentMethod, name="deletePaymentMethod"),
+]
diff --git a/preferences/views.py b/preferences/views.py
index 91ea44a..c16aa75 100644
--- a/preferences/views.py
+++ b/preferences/views.py
@@ -1,3 +1,75 @@
-from django.shortcuts import render
+from django.shortcuts import render, redirect, get_object_or_404
+from django.contrib import messages
+from django.urls import reverse
-# Create your views here.
+from .models import GeneralPreferences, Cotisation, PaymentMethod
+
+from .forms import CotisationForm, PaymentMethodForm, GeneralPreferencesForm
+
+def generalPreferences(request):
+ gp,_ = GeneralPreferences.objects.get_or_create(pk=1)
+ form = GeneralPreferencesForm(request.POST or None, instance=gp)
+ if(form.is_valid()):
+ form.save()
+ return render(request, "preferences/general_preferences.html", {"form": form})
+
+########## Cotisations ##########
+
+def cotisationsIndex(request):
+ cotisations = Cotisation.objects.all()
+ return render(request, "preferences/cotisations_index.html", {"cotisations": cotisations})
+
+def addCotisation(request):
+ form = CotisationForm(request.POST or None)
+ if(form.is_valid()):
+ cotisation = form.save()
+ messages.success(request, "La cotisation (" + str(cotisation.duration) + " jours, " + str(cotisation.amount) + "€) a bien été créée")
+ return redirect(reverse('preferences:cotisationsIndex'))
+ return render(request, "form.html", {"form": form, "form_title": "Création d'une cotisation", "form_button": "Créer"})
+
+def editCotisation(request, pk):
+ cotisation = get_object_or_404(Cotisation, pk=pk)
+ form = CotisationForm(request.POST or None, instance=cotisation)
+ if(form.is_valid()):
+ cotisation = form.save()
+ messages.success(request, "La cotisation (" + str(cotisation.duration) + " jours, " + str(cotisation.amount) + "€) a bien été modifiée")
+ return redirect(reverse('preferences:cotisationsIndex'))
+ return render(request, "form.html", {"form": form, "form_title": "Modification d'une cotisation", "form_button": "Modifier"})
+
+def deleteCotisation(request,pk):
+ cotisation = get_object_or_404(Cotisation, pk=pk)
+ message = "La cotisation (" + str(cotisation.duration) + " jours, " + str(cotisation.amount) + "€) a bien été supprimée"
+ cotisation.delete()
+ messages.success(request, message)
+ return redirect(reverse('preferences:cotisationsIndex'))
+
+
+########## Payment Methods ##########
+
+def paymentMethodsIndex(request):
+ paymentMethods = PaymentMethod.objects.all()
+ return render(request, "preferences/payment_methods_index.html", {"paymentMethods": paymentMethods})
+
+def addPaymentMethod(request):
+ form = PaymentMethodForm(request.POST or None)
+ if(form.is_valid()):
+ paymentMethod = form.save()
+ messages.success(request, "Le moyen de paiement " + paymentMethod.name + " a bien été crée")
+ return redirect(reverse('preferences:paymentMethodsIndex'))
+ return render(request, "form.html", {"form": form, "form_title": "Création d'un moyen de paiement", "form_button": "Créer"})
+
+def editPaymentMethod(request, pk):
+ paymentMethod = get_object_or_404(PaymentMethod, pk=pk)
+ form = PaymentMethodForm(request.POST or None, instance=paymentMethod)
+ if(form.is_valid()):
+ paymentMethod = form.save()
+ messages.success(request, "Le moyen de paiment " + paymentMethod.name + " a bien été modifié")
+ return redirect(reverse('preferences:paymentMethodsIndex'))
+ return render(request, "form.html", {"form": form, "form_title": "Modification d'un moyen de paiement", "form_button": "Modifier"})
+
+def deletePaymentMethod(request,pk):
+ paymentMethod = get_object_or_404(PaymentMethod, pk=pk)
+ message = "Le moyen de paiement " + paymentMethod.name + " a bien été supprimé"
+ paymentMethod.delete()
+ messages.success(request, message)
+ return redirect(reverse('preferences:paymentMethodsIndex'))
diff --git a/requirements.txt b/requirements.txt
index 7569b6f..226d374 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
Django==2.1
+django-autocomplete-light==3.3.2
pytz==2018.5
diff --git a/static/autocomplete_light/autocomplete.init.js b/static/autocomplete_light/autocomplete.init.js
new file mode 100644
index 0000000..8f413f7
--- /dev/null
+++ b/static/autocomplete_light/autocomplete.init.js
@@ -0,0 +1,162 @@
+/*
+This script garantees that this will be called once in django admin.
+However, its the callback's responsability to clean up if the
+element was cloned with data - which should be the case.
+*/
+
+;(function ($) {
+ $.fn.getFormPrefix = function() {
+ /* Get the form prefix for a field.
+ *
+ * For example:
+ *
+ * $(':input[name$=owner]').getFormsetPrefix()
+ *
+ * Would return an empty string for an input with name 'owner' but would return
+ * 'inline_model-0-' for an input named 'inline_model-0-owner'.
+ */
+ var parts = $(this).attr('name').split('-');
+ var prefix = '';
+
+ for (var i in parts) {
+ var testPrefix = parts.slice(0, -i).join('-');
+ if (! testPrefix.length) continue;
+ testPrefix += '-';
+
+ var result = $(':input[name^=' + testPrefix + ']')
+
+ if (result.length) {
+ return testPrefix;
+ }
+ }
+
+ return '';
+ }
+
+ $.fn.getFormPrefixes = function() {
+ /*
+ * Get the form prefixes for a field, from the most specific to the least.
+ *
+ * For example:
+ *
+ * $(':input[name$=owner]').getFormPrefixes()
+ *
+ * Would return:
+ * - [''] for an input named 'owner'.
+ * - ['inline_model-0-', ''] for an input named 'inline_model-0-owner' (i.e. nested with a nested inline).
+ * - ['sections-0-items-0-', 'sections-0-', ''] for an input named 'sections-0-items-0-product'
+ * (i.e. nested multiple time with django-nested-admin).
+ */
+ var parts = $(this).attr('name').split('-').slice(0, -1);
+ var prefixes = [];
+
+ for (i = 0; i < parts.length; i += 2) {
+ var testPrefix = parts.slice(0, -i || parts.length).join('-');
+ if (!testPrefix.length)
+ continue;
+
+ testPrefix += '-';
+
+ var result = $(':input[name^=' + testPrefix + ']')
+
+ if (result.length)
+ prefixes.push(testPrefix);
+ }
+
+ prefixes.push('');
+
+ return prefixes;
+ }
+
+ var initialized = [];
+
+ function initialize(element) {
+ if (typeof element === 'undefined' || typeof element === 'number') {
+ element = this;
+ }
+
+ if (window.__dal__initListenerIsSet !== true || initialized.indexOf(element) >= 0) {
+ return;
+ }
+
+ $(element).trigger('autocompleteLightInitialize');
+ initialized.push(element);
+ }
+
+ if (!window.__dal__initialize) {
+ window.__dal__initialize = initialize;
+
+ $(document).ready(function () {
+ $('[data-autocomplete-light-function=select2]:not([id*="__prefix__"])').each(initialize);
+ });
+
+ $(document).bind('DOMNodeInserted', function (e) {
+ $(e.target).find('[data-autocomplete-light-function=select2]:not([id*="__prefix__"])').each(initialize);
+ });
+ }
+
+ // using jQuery
+ function getCookie(name) {
+ var cookieValue = null;
+ if (document.cookie && document.cookie != '') {
+ var cookies = document.cookie.split(';');
+ for (var i = 0; i < cookies.length; i++) {
+ var cookie = $.trim(cookies[i]);
+ // Does this cookie string begin with the name we want?
+ if (cookie.substring(0, name.length + 1) == (name + '=')) {
+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
+ break;
+ }
+ }
+ }
+ return cookieValue;
+ }
+
+ document.csrftoken = getCookie('csrftoken');
+ if (document.csrftoken === null) {
+ // Try to get CSRF token from DOM when cookie is missing
+ var $csrf = $('form :input[name="csrfmiddlewaretoken"]');
+ if ($csrf.length > 0) {
+ document.csrftoken = $csrf[0].value;
+ }
+ }
+})(yl.jQuery);
+
+// Does the same thing as django's admin/js/autocomplete.js, but uses yl.jQuery.
+(function($) {
+ 'use strict';
+ var init = function($element, options) {
+ var settings = $.extend({
+ ajax: {
+ data: function(params) {
+ return {
+ term: params.term,
+ page: params.page
+ };
+ }
+ }
+ }, options);
+ $element.select2(settings);
+ };
+
+ $.fn.djangoAdminSelect2 = function(options) {
+ var settings = $.extend({}, options);
+ $.each(this, function(i, element) {
+ var $element = $(element);
+ init($element, settings);
+ });
+ return this;
+ };
+
+ $(function() {
+ // Initialize all autocomplete widgets except the one in the template
+ // form used when a new formset is added.
+ $('.admin-autocomplete').not('[name*=__prefix__]').djangoAdminSelect2();
+ });
+
+ $(document).on('formset:added', (function() {
+ return function(event, $newFormset) {
+ return $newFormset.find('.admin-autocomplete').djangoAdminSelect2();
+ };
+ })(this));
+}(yl.jQuery));
diff --git a/static/autocomplete_light/forward.js b/static/autocomplete_light/forward.js
new file mode 100644
index 0000000..9fe247c
--- /dev/null
+++ b/static/autocomplete_light/forward.js
@@ -0,0 +1,183 @@
+;(function($, yl) {
+ yl.forwardHandlerRegistry = yl.forwardHandlerRegistry || {};
+
+ yl.registerForwardHandler = function(name, handler) {
+ yl.forwardHandlerRegistry[name] = handler;
+ };
+
+ yl.getForwardHandler = function(name) {
+ return yl.forwardHandlerRegistry[name];
+ };
+
+ function getForwardStrategy(element) {
+ var checkForCheckboxes = function() {
+ var all = true;
+ $.each(element, function(ix, e) {
+ if ($(e).attr("type") !== "checkbox") {
+ all = false;
+ }
+ });
+ return all;
+ };
+
+ if (element.length === 1 &&
+ element.attr("type") === "checkbox" &&
+ element.attr("value") === undefined) {
+ // Single checkbox without 'value' attribute
+ // Boolean field
+ return "exists";
+ } else if (element.length === 1 &&
+ element.attr("multiple") !== undefined) {
+ // Multiple by HTML semantics. E. g. multiple select
+ // Multiple choice field
+ return "multiple";
+ } else if (checkForCheckboxes()) {
+ // Multiple checkboxes or one checkbox with 'value' attribute.
+ // Multiple choice field represented by checkboxes
+ return "multiple";
+ } else {
+ // Other cases
+ return "single";
+ }
+ }
+
+ /**
+ * Get fields with name `name` relative to `element` with considering form
+ * prefixes.
+ * @param element the element
+ * @param name name of the field
+ * @returns jQuery object with found fields or empty jQuery object if no
+ * field was found
+ */
+ yl.getFieldRelativeTo = function(element, name) {
+ var prefixes = $(element).getFormPrefixes();
+
+ for (var i = 0; i < prefixes.length; i++) {
+ var fieldSelector = "[name=" + prefixes[i] + name + "]";
+ var field = $(fieldSelector);
+
+ if (field.length) {
+ return field;
+ }
+ }
+
+ return $();
+ };
+
+ /**
+ * Get field value which is put to forwarded dictionary
+ * @param field the field
+ * @returns forwarded value
+ */
+ yl.getValueFromField = function(field) {
+ var strategy = getForwardStrategy(field);
+ var serializedField = $(field).serializeArray();
+
+ var getSerializedFieldElementAt = function (index) {
+ // Return serializedField[index]
+ // or null if something went wrong
+ if (serializedField.length > index) {
+ return serializedField[index];
+ } else {
+ return null;
+ }
+ };
+
+ var getValueOf = function (elem) {
+ // Return elem.value
+ // or null if something went wrong
+ if (elem.hasOwnProperty("value") &&
+ elem.value !== undefined
+ ) {
+ return elem.value;
+ } else {
+ return null;
+ }
+ };
+
+ var getSerializedFieldValueAt = function (index) {
+ // Return serializedField[index].value
+ // or null if something went wrong
+ var elem = getSerializedFieldElementAt(index);
+ if (elem !== null) {
+ return getValueOf(elem);
+ } else {
+ return null;
+ }
+ };
+
+ if (strategy === "multiple") {
+ return serializedField.map(
+ function (item) {
+ return getValueOf(item);
+ }
+ );
+ } else if (strategy === "exists") {
+ return serializedField.length > 0;
+ } else {
+ return getSerializedFieldValueAt(0);
+ }
+ };
+
+ yl.getForwards = function(element) {
+ var forwardElem,
+ forwardList,
+ forwardedData,
+ divSelector,
+ form;
+ divSelector = "div.dal-forward-conf#dal-forward-conf-for-" +
+ element.attr("id");
+ form = element.length > 0 ? $(element[0].form) : $();
+
+ forwardElem =
+ form.find(divSelector).find('script');
+ if (forwardElem.length === 0) {
+ return;
+ }
+ try {
+ forwardList = JSON.parse(forwardElem.text());
+ } catch (e) {
+ return;
+ }
+
+ if (!Array.isArray(forwardList)) {
+ return;
+ }
+
+ forwardedData = {};
+
+ $.each(forwardList, function(ix, field) {
+ var srcName, dstName;
+ if (field.type === "const") {
+ forwardedData[field.dst] = field.val;
+ } else if (field.type === "self") {
+ if (field.hasOwnProperty("dst")) {
+ dstName = field.dst;
+ } else {
+ dstName = "self";
+ }
+ forwardedData[dstName] = yl.getValueFromField(element);
+ } else if (field.type === "field") {
+ srcName = field.src;
+ if (field.hasOwnProperty("dst")) {
+ dstName = field.dst;
+ } else {
+ dstName = srcName;
+ }
+ var forwardedField = yl.getFieldRelativeTo(element, srcName);
+
+ if (!forwardedField.length) {
+ return;
+ }
+
+ forwardedData[dstName] = yl.getValueFromField(forwardedField);
+ } else if (field.type === "javascript") {
+ var handler = yl.getForwardHandler(field.handler);
+ forwardedData[field.dst || field.handler] = handler(element);
+ }
+
+ });
+ return JSON.stringify(forwardedData);
+ };
+
+})(yl.jQuery, yl);
diff --git a/static/autocomplete_light/jquery.init.js b/static/autocomplete_light/jquery.init.js
new file mode 100644
index 0000000..926d8d3
--- /dev/null
+++ b/static/autocomplete_light/jquery.init.js
@@ -0,0 +1,36 @@
+var yl = yl || {};
+if (typeof django !== 'undefined' && typeof django.jQuery !== 'undefined') {
+ // If django.jQuery is already defined, use it.
+ yl.jQuery = django.jQuery;
+}
+else {
+ // We include jquery itself in our widget's media, because we need it.
+ // Normally, we expect our widget's reference to admin/js/vendor/jquery/jquery.js
+ // to be skipped, because django's own code has already included it.
+ // However, if django.jQuery is NOT defined, we know that jquery was not
+ // included before we did it ourselves. This can happen if we're not being
+ // rendered in a django admin form.
+ // However, someone ELSE'S jQuery may have been included before ours, in
+ // which case we must ensure that our jquery doesn't override theirs, since
+ // it might be a newer version that other code on the page relies on.
+ // Thus, we must run jQuery.noConflict(true) here to move our jQuery out of
+ // the way.
+ yl.jQuery = jQuery.noConflict(true);
+}
+
+// In addition to all of this, we must ensure that the global jQuery and $ are
+// defined, because Select2 requires that. jQuery will only be undefined at
+// this point if only we or django included it.
+if (typeof jQuery === 'undefined') {
+ jQuery = yl.jQuery;
+ $ = yl.jQuery;
+}
+else {
+ // jQuery IS still defined, which means someone else also included jQuery.
+ // In this situation, we need to store the old jQuery in a
+ // temp variable, set the global jQuery to our yl.jQuery, then let select2
+ // set itself up. We restore the global jQuery to its original value in
+ // jquery.post-setup.js.
+ dal_jquery_backup = jQuery.noConflict(true);
+ jQuery = yl.jQuery;
+}
diff --git a/static/autocomplete_light/jquery.post-setup.js b/static/autocomplete_light/jquery.post-setup.js
new file mode 100644
index 0000000..eac6f5a
--- /dev/null
+++ b/static/autocomplete_light/jquery.post-setup.js
@@ -0,0 +1,7 @@
+if (typeof dal_jquery_backup !== 'undefined') {
+ // We made a backup of the original global jQuery before forcing it to our
+ // yl.jQuery value. Now that select2 has been set up, we need to restore
+ // our backup to its rightful place.
+ jQuery = dal_jquery_backup;
+ $ = dal_jquery_backup;
+}
diff --git a/static/autocomplete_light/select2.css b/static/autocomplete_light/select2.css
new file mode 100644
index 0000000..471e1a1
--- /dev/null
+++ b/static/autocomplete_light/select2.css
@@ -0,0 +1,9 @@
+.select2-container {
+ min-width: 20em;
+}
+
+ul li.select2-selection__choice,
+ul li.select2-search {
+ /* Cancel out django's style */
+ list-style-type: none;
+}
diff --git a/static/autocomplete_light/select2.js b/static/autocomplete_light/select2.js
new file mode 100644
index 0000000..34d60c4
--- /dev/null
+++ b/static/autocomplete_light/select2.js
@@ -0,0 +1,116 @@
+;(function ($) {
+ if (window.__dal__initListenerIsSet)
+ return;
+
+ $(document).on('autocompleteLightInitialize', '[data-autocomplete-light-function=select2]', function() {
+ var element = $(this);
+
+ // Templating helper
+ function template(text, is_html) {
+ if (is_html) {
+ var $result = $('');
+ $result.html(text);
+ return $result;
+ } else {
+ return text;
+ }
+ }
+
+ function result_template(item) {
+ return template(item.text,
+ element.attr('data-html') !== undefined || element.attr('data-result-html') !== undefined
+ );
+ }
+
+ function selected_template(item) {
+ if (item.selected_text !== undefined) {
+ return template(item.selected_text,
+ element.attr('data-html') !== undefined || element.attr('data-selected-html') !== undefined
+ );
+ } else {
+ return result_template(item);
+ }
+ return
+ }
+
+ var ajax = null;
+ if ($(this).attr('data-autocomplete-light-url')) {
+ ajax = {
+ url: $(this).attr('data-autocomplete-light-url'),
+ dataType: 'json',
+ delay: 250,
+
+ data: function (params) {
+ var data = {
+ q: params.term, // search term
+ page: params.page,
+ create: element.attr('data-autocomplete-light-create') && !element.attr('data-tags'),
+ forward: yl.getForwards(element)
+ };
+
+ return data;
+ },
+ processResults: function (data, page) {
+ if (element.attr('data-tags')) {
+ $.each(data.results, function(index, value) {
+ value.id = value.text;
+ });
+ }
+
+ return data;
+ },
+ cache: true
+ };
+ }
+
+ $(this).select2({
+ tokenSeparators: element.attr('data-tags') ? [','] : null,
+ debug: true,
+ containerCssClass: ':all:',
+ placeholder: element.attr('data-placeholder') || '',
+ language: element.attr('data-autocomplete-light-language'),
+ minimumInputLength: element.attr('data-minimum-input-length') || 0,
+ allowClear: ! $(this).is('[required]'),
+ templateResult: result_template,
+ templateSelection: selected_template,
+ ajax: ajax,
+ tags: Boolean(element.attr('data-tags')),
+ });
+
+ $(this).on('select2:selecting', function (e) {
+ var data = e.params.args.data;
+
+ if (data.create_id !== true)
+ return;
+
+ e.preventDefault();
+
+ var select = $(this);
+
+ $.ajax({
+ url: $(this).attr('data-autocomplete-light-url'),
+ type: 'POST',
+ dataType: 'json',
+ data: {
+ text: data.id,
+ forward: yl.getForwards($(this))
+ },
+ beforeSend: function(xhr, settings) {
+ xhr.setRequestHeader("X-CSRFToken", document.csrftoken);
+ },
+ success: function(data, textStatus, jqXHR ) {
+ select.append(
+ $('
+ École : {{user.profile.school}}
Pseudo : {{user.username}}
Mail: {{user.email}}
@@ -37,7 +39,7 @@
Crédit : {{user.profile.credit}}
Débit : {{user.profile.debit}}
- Groupes : {{user.group_set|join:", "}}
+ Groupe(s) : {{user.groups.all|join:", "}}
Position au classement : {{user.profile.rank}}
Quantité d'alcool ingérée : {{user.profile.alcohol}} kg
@@ -48,14 +50,30 @@
@@ -65,7 +83,7 @@
-
-
-
+
{{ self | yesno:"Mes cotisations,Cotisations"}}
+ Ajouter une cotisation
| Montant |
+ Durée |
Date de paiement |
- Moyen de paimement |
+ Moyen de paiement |
Date de fin |
+ Etat |
+ Modération |
{% for cotisation in cotisations %}
- | {{cotisation.amount}} |
+ {{cotisation.amount}}€ |
+ {{cotisation.duration}} jours |
{{cotisation.paymentDate}} |
{{cotisation.paymentMethod}} |
{{cotisation.endDate}} |
+ {{cotisation.valid}} |
+ Valider Invalider |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+ {{ self | yesno:"Mes accès gracieux,Accès gracieux"}}
+
+
+ Ajouter un accès à titre gracieux
+
+
+
+
+ | Date de l'ajout |
+ Date de fin |
+ Durée |
+
+
+
+ {% for whitelist in whitelists %}
+
+ | {{whitelist.paymentDate}} |
+ {{whitelist.endDate}} |
+ {{whitelist.duration}} jours |
{% endfor %}
-
+
diff --git a/users/templates/users/schools_index.html b/users/templates/users/schools_index.html
new file mode 100644
index 0000000..93d5e34
--- /dev/null
+++ b/users/templates/users/schools_index.html
@@ -0,0 +1,33 @@
+{% extends "base.html" %}
+{% block entete %}Gestion des écoles
{% endblock %}
+{% block navbar %}
+
+{% endblock %}
+{% block content %}
+
+
+ Créer une école
+
+
+
+
+ | Ecole |
+ Administration |
+
+
+
+ {% for school in schools %}
+
+ | {{ school }} |
+ Modifier Supprimer |
+
+ {% endfor %}
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/users/templates/users/users_index.html b/users/templates/users/users_index.html
new file mode 100644
index 0000000..aa305d2
--- /dev/null
+++ b/users/templates/users/users_index.html
@@ -0,0 +1,33 @@
+{% extends "base.html" %}
+{% block entete %}Gestion des utilisateurs
{% endblock %}
+{% block navbar %}
+
+{% endblock %}
+{% block content %}
+
+
+ Liste des utilisateurs
+
+ Créer un utilisateur
+
+
+
+
+ | Utilisateur |
+ Profil |
+
+
+
+ {% for user in users %}
+
+ | {{ user }} |
+ Profil |
+
+ {% endfor %}
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/users/urls.py b/users/urls.py
index 80144d2..12a2dbc 100644
--- a/users/urls.py
+++ b/users/urls.py
@@ -8,6 +8,12 @@ urlpatterns = [
path('index', views.index, name="index"),
path('profile/', views.profile, name="profile"),
path('createUser', views.createUser, name="createUser"),
+ path('searchUser', views.searchUser, name="searchUser"),
+ path('usersIndex', views.usersIndex, name="usersIndex"),
+ path('editGroups/', views.editGroups, name="editGroups"),
+ path('editPassword/', views.editPassword, name="editPassword"),
+ path('editUser/', views.editUser, name="editUser"),
+ path('resetPassword/', views.resetPassword, name="resetPassword"),
path('groupsIndex', views.groupsIndex, name="groupsIndex"),
path('groupProfile/', views.groupProfile, name="groupProfile"),
path('createGroup', views.createGroup, name="createGroup"),
@@ -21,4 +27,16 @@ urlpatterns = [
path('superusersIndex', views.superusersIndex, name="superusersIndex"),
path('addSuperuser', views.addSuperuser, name="addSuperuser"),
path('removeSuperuser/', views.removeSuperuser, name="removeSuperuser"),
+ path('all-users-autocomplete', views.AllUsersAutocomplete.as_view(), name="all-users-autocomplete"),
+ path('active-users-autcocomplete', views.ActiveUsersAutocomplete.as_view(), name="active-users-autocomplete"),
+ path('non-super-users-autocomplete', views.NonSuperUserAutocomplete.as_view(), name="non-super-users-autocomplete"),
+ path('getUser/', views.getUser, name="getUser"),
+ path('addCotisationHistory/', views.addCotisationHistory, name="addCotisationHistory"),
+ path('validateCotisationHistory/', views.validateCotisationHistory, name="validateCotisationHistory"),
+ path('invalidateCotisationHistory/', views.invalidateCotisationHistory, name="invalidateCotisationHistory"),
+ path('addWhiteListHistory/', views.addWhiteListHistory, name="addWhiteListHistory"),
+ path('schoolsIndex', views.schoolsIndex, name="schoolsIndex"),
+ path('createSchool', views.createSchool, name="createSchool"),
+ path('editSchool/', views.editSchool, name="editSchool"),
+ path('deleteSchool/', views.deleteSchool, name="deleteSchool"),
]
diff --git a/users/views.py b/users/views.py
index defd20d..d01cce8 100644
--- a/users/views.py
+++ b/users/views.py
@@ -3,8 +3,16 @@ from django.urls import reverse
from django.contrib.auth.models import User, Group, Permission
from django.contrib.auth import authenticate, login, logout
from django.contrib import messages
+from django.db.models import Q
+from django.http import HttpResponse, HttpResponseRedirect
-from .forms import CreateUserForm, LoginForm, CreateGroupForm, EditGroupForm, SelectUserForm
+import json
+from datetime import datetime, timedelta
+
+from dal import autocomplete
+
+from .models import CotisationHistory, WhiteListHistory, School
+from .forms import CreateUserForm, LoginForm, CreateGroupForm, EditGroupForm, SelectUserForm, GroupsEditForm, EditPasswordForm, addCotisationHistoryForm, addCotisationHistoryForm, addWhiteListHistoryForm, SelectNonAdminUserForm, SelectNonSuperUserForm, SchoolForm
def loginView(request):
form = LoginForm(request.POST or None)
@@ -29,10 +37,16 @@ def logoutView(request):
def index(request):
return render(request, "users/index.html")
+########## schools ##########
+
+########## users ##########
+
def profile(request, pk):
user = get_object_or_404(User, pk=pk)
self = request.user == user
- return render(request, "users/profile.html", {"user":user, "self":self})
+ cotisations = CotisationHistory.objects.filter(user=user)
+ whitelists = WhiteListHistory.objects.filter(user=user)
+ return render(request, "users/profile.html", {"user":user, "self":self, "cotisations":cotisations, "whitelists": whitelists})
def createUser(request):
form = CreateUserForm(request.POST or None)
@@ -44,6 +58,68 @@ def createUser(request):
user.save()
return render(request, "form.html", {"form_entete": "Gestion des utilisateurs", "form":form, "form_title":"Création d'un nouvel utilisateur", "form_button":"Créer l'utilisateur"})
+def searchUser(request):
+ form = SelectUserForm(request.POST or None)
+ if(form.is_valid()):
+ return redirect(reverse('users:profile', kwargs={"pk":form.cleaned_data['user'].pk}))
+ return render(request, "form.html", {"form_entete": "Gestion des utilisateurs", "form": form, "form_title": "Rechercher un utilisateur", "form_button": "Afficher le profil"})
+
+def usersIndex(request):
+ users = User.objects.all()
+ return render(request, "users/users_index.html", {"users":users})
+
+def editGroups(request, pk):
+ user = get_object_or_404(User, pk=pk)
+ form = GroupsEditForm(request.POST or None, instance=user)
+ if(form.is_valid()):
+ form.save()
+ messages.success(request, "Les groupes de l'utilisateur " + user.username + " ont bien été enregistrés.")
+ return redirect(reverse('users:profile', kwargs={'pk':pk}))
+ extra_css = "#id_groups{height:200px;}"
+ return render(request, "form.html", {"form_entete": "Gestion de l'utilisateur " + user.username, "form": form, "form_title": "Modification des groupes", "form_button": "Enregistrer", "extra_css": extra_css})
+
+def editPassword(request, pk):
+ user = get_object_or_404(User, pk=pk)
+ if user != request.user:
+ messages.error(request, "Vous ne pouvez modifier le mot de passe d'un autre utilisateur")
+ return redirect(reverse('home'))
+ else:
+ form = EditPasswordForm(request.POST or None)
+ if(form.is_valid()):
+ if authenticate(username=user.username, password = form.cleaned_data['password']) is not None:
+ user.set_password(form.cleaned_data['password2'])
+ user.save()
+ messages.success(request, "Votre mot de passe a bien été mis à jour")
+ return redirect(reverse('users:profile', kwargs={'pk':pk}))
+ else:
+ messages.error(request, "Le mot de passe actuel est incorrect")
+ return render(request, "form.html", {"form_entete": "Modification de mon compte", "form": form, "form_title": "Modification de mon mot de passe", "form_button": "Modifier mon mot de passe"})
+
+def editUser(request, pk):
+ user = get_object_or_404(User, pk=pk)
+ form = CreateUserForm(request.POST or None, instance=user, initial = {'school': user.profile.school})
+ if(form.is_valid()):
+ user.profile.school = form.cleaned_data['school']
+ user.save()
+ messages.success(request, "Les modifications ont bien été enregistrées")
+ return redirect(reverse('users:profile', kwargs={'pk': pk}))
+ return render(request, "form.html", {"form_entete":"Modification du compte " + user.username, "form": form, "form_title": "Modification des informations", "form_button": "Modifier"})
+
+def resetPassword(request, pk):
+ user = get_object_or_404(User, pk=pk)
+ if user.is_superuser:
+ messages.error(request, "Impossible de réinitialiser le mot de passe de " + user.username + " : il est superuser.")
+ return redirect(reverse('users:profile', kwargs={'pk': pk}))
+ else:
+ user.set_password(user.username)
+ user.save()
+ messages.success(request, "Le mot de passe de " + user.username + " a bien été réinitialisé.")
+ return redirect(reverse('users:profile', kwargs={'pk': pk}))
+
+def getUser(request, pk):
+ user = get_object_or_404(User, pk=pk)
+ data = json.dumps({"username": user.username, "balance": float(user.profile.balance)})
+ return HttpResponse(data, content_type='application/json')
########## Groups ##########
@@ -111,7 +187,7 @@ def adminsIndex(request):
return render(request, "users/admins_index.html", {"admins": admins})
def addAdmin(request):
- form = SelectUserForm(request.POST or None, restrictTo="non-admins")
+ form = SelectNonAdminUserForm(request.POST or None)
if(form.is_valid()):
user = form.cleaned_data['user']
user.is_staff = True
@@ -143,7 +219,7 @@ def superusersIndex(request):
return render(request, "users/superusers_index.html", {"superusers": superusers})
def addSuperuser(request):
- form = SelectUserForm(request.POST or None, restrictTo="non-superusers")
+ form = SelectNonSuperUserForm(request.POST or None)
if(form.is_valid()):
user = form.cleaned_data['user']
user.is_admin = True
@@ -165,3 +241,120 @@ def removeSuperuser(request, pk):
else:
messages.error(request, "Impossible de retirer l'utilisateur " + user.username + " des superusers : il n'en fait pas partie.")
return redirect(reverse('users:superusersIndex'))
+
+########## Cotisations ##########
+
+def addCotisationHistory(request, pk):
+ user = get_object_or_404(User, pk=pk)
+ form = addCotisationHistoryForm(request.POST or None)
+ if(form.is_valid()):
+ cotisation = form.save(commit=False)
+ cotisation.user = user
+ cotisation.coopeman = request.user
+ cotisation.amount = cotisation.cotisation.amount
+ cotisation.duration = cotisation.cotisation.duration
+ if(user.profile.cotisationEnd):
+ cotisation.endDate = user.profile.cotisationEnd + timedelta(days=cotisation.cotisation.duration)
+ else:
+ cotisation.endDate = datetime.now() + timedelta(days=cotisation.cotisation.duration)
+ user.profile.cotisationEnd = cotisation.endDate
+ user.save()
+ cotisation.save()
+ messages.success(request, "La cotisation a bien été ajoutée")
+ return redirect(reverse('users:profile',kwargs={'pk':user.pk}))
+ return render(request, "form.html",{"form": form, "form_title": "Ajout d'une cotisation pour l'utilisateur " + str(user), "form_button": "Ajouter"})
+
+def validateCotisationHistory(request, pk):
+ cotisationHistory = get_object_or_404(CotisationHistory, pk=pk)
+ cotisationHistory.valid = CotisationHistory.VALID
+ cotisationHistory.save()
+ messages.success(request, "La cotisation a bien été validée")
+ return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+
+def invalidateCotisationHistory(request, pk):
+ cotisationHistory = get_object_or_404(CotisationHistory, pk=pk)
+ cotisationHistory.valid = CotisationHistory.INVALID
+ cotisationHistory.save()
+ user = cotisationHistory.user
+ user.profile.cotisationEnd = user.profile.cotisationEnd - timedelta(days=cotisationHistory.duration)
+ user.save()
+ messages.success(request, "La cotisation a bien été invalidée")
+ return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+
+########## Whitelist ##########
+
+def addWhiteListHistory(request, pk):
+ user = get_object_or_404(User, pk=pk)
+ form = addWhiteListHistoryForm(request.POST or None)
+ if(form.is_valid()):
+ whiteList = form.save(commit=False)
+ whiteList.user = user
+ whiteList.coopeman = request.user
+ if(user.profile.cotisationEnd):
+ whiteList.endDate = user.profile.cotisationEnd + timedelta(days=whiteList.duration)
+ else:
+ whiteList = datetime.now() + timedelta(days=whiteList.duration)
+ user.profile.cotisationEnd = whiteList.endDate
+ user.save()
+ whiteList.save()
+ messages.success(request, "L'accès gracieux a bien été ajouté")
+ return redirect(reverse('users:profile', kwargs={'pk':user.pk}))
+ return render(request, "form.html", {"form": form, "form_title": "Ajout d'un accès gracieux pour " + user.username, "form_button": "Ajouter"})
+
+########## Schools ##########
+
+def schoolsIndex(request):
+ schools = School.objects.all()
+ return render(request, "users/schools_index.html", {"schools": schools})
+
+def createSchool(request):
+ form = SchoolForm(request.POST or None)
+ if(form.is_valid()):
+ form.save()
+ messages.success(request, "L'école a bien été créée")
+ return redirect(reverse('users:schoolsIndex'))
+ return render(request, "form.html", {"form": form, "form_title": "Création d'une école", "form_button": "Créer"})
+
+def editSchool(request, pk):
+ school = get_object_or_404(School, pk=pk)
+ form = SchoolForm(request.POST or None, instance=school)
+ if(form.is_valid()):
+ form.save()
+ messages.success(request, "L'école a bien été modifiée")
+ return redirect(reverse('users:schoolsIndex'))
+ return render(request, "form.html", {"form": form, "form_title": "Modification de l'école " + str(school), "form_button": "Modifier"})
+
+def deleteSchool(request, pk):
+ school = get_object_or_404(School, pk=pk)
+ message = "L'école " + str(school) + " a bien été supprimée"
+ school.delete()
+ messages.success(request, message)
+ return redirect(reverse('users:schoolsIndex'))
+
+########## Autocomplete searchs ##########
+
+class AllUsersAutocomplete(autocomplete.Select2QuerySetView):
+ def get_queryset(self):
+ qs = User.objects.all()
+ if self.q:
+ qs = qs.filter(Q(username__istartswith=self.q) | Q(first_name__istartswith=self.q) | Q(last_name__istartswith=self.q))
+ return qs
+
+class ActiveUsersAutocomplete(autocomplete.Select2QuerySetView):
+ def get_queryset(self):
+ qs = User.objects.filter(is_active=True)
+ if self.q:
+ qs = qs.filter(Q(username__istartswith=self.q) | Q(first_name__istartswith=self.q) | Q(last_name__istartswith=self.q))
+ return qs
+
+class AdherentAutocomplete(autocomplete.Select2QuerySetView):
+ def get_queryset(self):
+ qs = User.objects.all()
+ return qs
+
+class NonSuperUserAutocomplete(autocomplete.Select2QuerySetView):
+ def get_queryset(self):
+ qs = User.objects.filter(is_superuser=False)
+ if self.q:
+ qs = qs.filter(Q(username__istartswith=self.q) | Q(first_name__istartswith=self.q) | Q(last_name__istartswith=self.q))
+ return qs
\ No newline at end of file