mirror of
https://github.com/nanoy42/coope
synced 2024-11-21 19:03:12 +00:00
Initial commit pas initial
This commit is contained in:
parent
9042adfd31
commit
15e13f978c
54 changed files with 2199 additions and 188 deletions
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"python.pythonPath": "/home/nanoy/.virtualenvs/coopeV3/bin/python"
|
||||
}
|
|
@ -40,6 +40,9 @@ INSTALLED_APPS = [
|
|||
'gestion',
|
||||
'users',
|
||||
'preferences',
|
||||
'coopeV3',
|
||||
'dal',
|
||||
'dal_select2',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
|
0
coopeV3/templatetags/__init__.py
Normal file
0
coopeV3/templatetags/__init__.py
Normal file
40
coopeV3/templatetags/vip.py
Normal file
40
coopeV3/templatetags/vip.py
Normal file
|
@ -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
|
|
@ -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')),
|
||||
]
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
37
gestion/forms.py
Normal file
37
gestion/forms.py
Normal file
|
@ -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")
|
|
@ -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]),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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
|
||||
|
|
212
gestion/templates/gestion/manage.html
Normal file
212
gestion/templates/gestion/manage.html
Normal file
|
@ -0,0 +1,212 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{%block entete%}<h1>Gestion de la Coopé™</h1>{%endblock%}
|
||||
|
||||
{% block navbar %}
|
||||
<ul>
|
||||
{% if perms.gestion.can_add_consumption_history %}<li><a href="#first">Commande</a></li>{% endif %}
|
||||
{% if perms.gestion.can_add_reload %}<li><a href="#second">Rechargement Client</a></li>{% endif %}
|
||||
{% if perms.gestion.can_add_refund %}<li><a href="#third">Remboursement client</a><li>{% endif %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<a class="up_button" href="#intro">
|
||||
UP
|
||||
</a>
|
||||
<style>
|
||||
.up_button{
|
||||
display:block;
|
||||
background-color:white;
|
||||
position:fixed;
|
||||
border-radius:100%;
|
||||
width:50px;
|
||||
height:50px;
|
||||
color:black;
|
||||
text-align:center;
|
||||
line-height:50px;
|
||||
right:1em;
|
||||
bottom : 1em;
|
||||
}
|
||||
</style>
|
||||
<section id="intro" class="main">
|
||||
<div class="spotlight">
|
||||
<div class="content">
|
||||
<header class="major">
|
||||
<h2>Transaction</h2>
|
||||
</header>
|
||||
<div class="row uniform">
|
||||
<div class="12u$">
|
||||
{{gestion_form}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row uniform">
|
||||
<h3>Récapitulatif</h3>
|
||||
</div>
|
||||
<div class="row uniform">
|
||||
<div class="12u$">
|
||||
<table id="sumUpTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Solde</th>
|
||||
<th>Montant total de la commande</th>
|
||||
<th>Solde après la commande</th>
|
||||
<th>Payer</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td id="balance">0€</td>
|
||||
<td id="totalAmount">0€</td>
|
||||
<td id="totalAfter">0€</td>
|
||||
<td><button class="btn small">Payer</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row uniform">
|
||||
<h3>Produits</h3>
|
||||
</div>
|
||||
<div class="row uniform">
|
||||
<div class="12u$">
|
||||
<table id="productTable" type="input" name="tableau" class="alt">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>CodeBarre</th>
|
||||
<th>Nom Produit</th>
|
||||
<th>Prix Unitaire</th>
|
||||
<th>Quantité</th>
|
||||
<th>Sous-total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="items">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row uniform">
|
||||
<div class="12u$">
|
||||
<div class="boutonProduit">
|
||||
<table>
|
||||
<tbody class="actions" id="bouton Produit">
|
||||
<tr style="text-align:center; font-weight:bold;"><td colspan="4">Bières pression</td></tr>
|
||||
{% for produit in bieresPression %}
|
||||
{% if forloop.counter0|divisibleby:4 %}
|
||||
<tr style="text-align:center">
|
||||
{% endif %}
|
||||
<td><button class="product" target="{{produit.barcode}}">{{produit.name}}</button></td>
|
||||
{% if forloop.counter|divisibleby:4 %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not bieresPression|divisibleby:4 %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr style="text-align:center; font-weight:bold;"><td colspan="4">Bières bouteilles</td></tr>
|
||||
{% for produit in bieresBouteille %}
|
||||
{% if forloop.counter0|divisibleby:4 %}
|
||||
<tr style="text-align:center">
|
||||
{% endif %}
|
||||
<td><button class="boutonsProduit" disabled target="{{produit.codeBarre}}" type="{{produit.typeSaisie}}">{{produit.nom}}</button></td>
|
||||
{% if forloop.counter|divisibleby:4 %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not bieresBouteille|divisibleby:4 %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr style="text-align:center; font-weight:bold;"><td colspan="4">Paninis</td></tr>
|
||||
{% for produit in panini %}
|
||||
{% if forloop.counter0|divisibleby:4 %}
|
||||
<tr style="text-align:center">
|
||||
{% endif %}
|
||||
<td><button class="boutonsProduit" disabled target="{{produit.codeBarre}}" type="{{produit.typeSaisie}}">{{produit.nom}}</button></td>
|
||||
{% if forloop.counter|divisibleby:4 %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not panini|divisibleby:4 %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr style="text-align:center; font-weight:bold;"><td colspan="4">Softs</td></tr>
|
||||
{% for produit in soft %}
|
||||
{% if forloop.counter0|divisibleby:4 %}
|
||||
<tr style="text-align:center">
|
||||
{% endif %}
|
||||
<td><button class="boutonsProduit" disabled target="{{produit.codeBarre}}" type="{{produit.typeSaisie}}">{{produit.nom}}</button></td>
|
||||
{% if forloop.counter|divisibleby:4 %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not soft|divisibleby:4 %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
<tr style="text-align:center; font-weight:bold;"><td colspan="4">Bouffe</td></tr>
|
||||
{% for produit in autreBouffe %}
|
||||
{% if forloop.counter0|divisibleby:4 %}
|
||||
<tr style="text-align:center">
|
||||
{% endif %}
|
||||
<td><button class="boutonsProduit" disabled target="{{produit.codeBarre}}" type="{{produit.typeSaisie}}">{{produit.nom}}</button></td>
|
||||
{% if forloop.counter|divisibleby:4 %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not autreBouffe|divisibleby:4 %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if menus %}
|
||||
<tr style="text-align:center; font-weight:bold;"><td colspan="4">Menus</td></tr>
|
||||
{% for produit in menus %}
|
||||
{% if forloop.counter0|divisibleby:4 %}
|
||||
<tr style="text-align:center">
|
||||
{% endif %}
|
||||
<td><button class="boutonsProduit" disabled target="{{produit.codeBarre}}" type="MN">{{produit.nom}}</button></td>
|
||||
{% if forloop.counter|divisibleby:4 %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not menus|divisibleby:4 %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% if perms.gestion.cand_add_reload %}
|
||||
<section id="second" class="main">
|
||||
<header class="major">
|
||||
<h2>Rechargement client</h2>
|
||||
</header>
|
||||
<form method="post" action="{% url 'gestion:reload' %}">
|
||||
{% csrf_token %}
|
||||
{{reload_form}}
|
||||
<br>
|
||||
<button type="submit">Recharger</button>
|
||||
</form>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% if perms.gestion.can_refund %}
|
||||
<section id="third" class="main">
|
||||
<header class="major">
|
||||
<h2>Remboursement client</h2>
|
||||
</header>
|
||||
<form method="post" action="{% url 'gestion:refund' %}">
|
||||
{% csrf_token %}
|
||||
{{refund_form}}
|
||||
<br>
|
||||
<button type="submit">Rembourser</button>
|
||||
</form>
|
||||
</section>
|
||||
{% endif %}
|
||||
{{gestion_form.media}}
|
||||
<script src="{% static 'manage.js' %}"></script>
|
||||
{%endblock%}
|
55
gestion/templates/gestion/products_index.html
Normal file
55
gestion/templates/gestion/products_index.html
Normal file
|
@ -0,0 +1,55 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block entete %}<h1>Gestion des produits</h1>{% endblock %}
|
||||
{% block navbar%}
|
||||
<ul>
|
||||
<li><a href="#first">Produits</a></li>
|
||||
<li><a href="#second">Futs</a></li>
|
||||
<li><a href="#third">Menus</a></li>
|
||||
<li><a href="#fourth">Stocks</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<section id="first" class="main">
|
||||
<header class="major">
|
||||
<h2>Produits</h2>
|
||||
</header>
|
||||
Actions possibles :
|
||||
<ul>
|
||||
<li><a href="{% url 'gestion:addProduct' %}">Créer un produit</a></li>
|
||||
<li><a href="{% url 'users:searchUser' %}">Rechercher un produit</a></li>
|
||||
<li><a href="{% url 'users:usersIndex' %}">Lister tous les produits</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="second" class="main">
|
||||
<header class="major">
|
||||
<h2>Futs</h2>
|
||||
</header>
|
||||
Actions possibles :
|
||||
<ul>
|
||||
<li><a href="{% url 'gestion:addBarrel' %}">Créer un fut</a></li>
|
||||
<li><a href="">Percuter un fut</a></li>
|
||||
<li><a href="">Lister les futs</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="third" class="main">
|
||||
<header class="major">
|
||||
<h2>Menus</h2>
|
||||
</header>
|
||||
Actions possibles :
|
||||
<ul>
|
||||
<li><a href="{% url 'gestion:addMenu' %}">Créer un menu</a></li>
|
||||
<li><a href="">Rechercher un menu</a></li>
|
||||
<li><a href="{% url 'users:adminsIndex' %}">Lister les menus</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="fourth" class="main">
|
||||
<header class="major">
|
||||
<h2>Stocks</h2>
|
||||
</header>
|
||||
Actions possibles :
|
||||
<ul>
|
||||
<li><a href="{% url 'users:addSuperuser' %}">Voir les Stocks</a></li>
|
||||
<li><a href="{% url 'users:superusersIndex' %}">Classement sur un produit</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
{% endblock %}
|
55
gestion/templates/gestion/products_list.html
Normal file
55
gestion/templates/gestion/products_list.html
Normal file
|
@ -0,0 +1,55 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block entete %}<h1>Gestion des produits</h1>{% endblock %}
|
||||
{% block navbar%}
|
||||
<ul>
|
||||
<li><a href="#first">Produits</a></li>
|
||||
<li><a href="#second">Futs</a></li>
|
||||
<li><a href="#third">Menus</a></li>
|
||||
<li><a href="#fourth">Stocks</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<section id="first" class="main">
|
||||
<header class="major">
|
||||
<h2>Liste des produits</h2>
|
||||
</header>
|
||||
Actions possibles :
|
||||
<ul>
|
||||
<li><a href="{% url 'gestion:addProduct' %}">Créer un produit</a></li>
|
||||
<li><a href="{% url 'users:searchUser' %}">Rechercher un produit</a></li>
|
||||
<li><a href="{% url 'users:usersIndex' %}">Lister tous les produits</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="second" class="main">
|
||||
<header class="major">
|
||||
<h2>Futs</h2>
|
||||
</header>
|
||||
Actions possibles :
|
||||
<ul>
|
||||
<li><a href="{% url 'users:createGroup' %}">Créer un fut</a></li>
|
||||
<li><a href="">Percuter un fut</a></li>
|
||||
<li><a href="">Lister les futs</a><li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="third" class="main">
|
||||
<header class="major">
|
||||
<h2>Menus</h2>
|
||||
</header>
|
||||
Actions possibles :
|
||||
<ul>
|
||||
<li><a href="{% url 'users:addAdmin' %}">Créer un menu</a></li>
|
||||
<li><a href="">Rechercher un menu</a></li>
|
||||
<li><a href="{% url 'users:adminsIndex' %}">Lister les menus</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="fourth" class="main">
|
||||
<header class="major">
|
||||
<h2>Stocks</h2>
|
||||
</header>
|
||||
Actions possibles :
|
||||
<ul>
|
||||
<li><a href="{% url 'users:addSuperuser' %}">Voir les Stocks</a></li>
|
||||
<li><a href="{% url 'users:superusersIndex' %}">Classement sur un produit</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
{% endblock %}
|
|
@ -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/<str:barcode>', views.getProduct, name="getProduct"),
|
||||
]
|
||||
|
|
108
gestion/views.py
108
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})
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
30
preferences/forms.py
Normal file
30
preferences/forms.py
Normal file
|
@ -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'}),
|
||||
}
|
||||
|
|
@ -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)),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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) + "€"
|
||||
|
|
35
preferences/templates/preferences/cotisations_index.html
Normal file
35
preferences/templates/preferences/cotisations_index.html
Normal file
|
@ -0,0 +1,35 @@
|
|||
{% extends "base.html" %}
|
||||
{% block entete %}<h1>Gestion des cotisations</h1>{% endblock %}
|
||||
{% block navbar %}
|
||||
<ul>
|
||||
<li><a href="#first">Liste des cotisations</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<section id="first" class="main">
|
||||
<header class="major">
|
||||
<h2>Liste des cotisations</h2>
|
||||
</header>
|
||||
<a class="button" href="{% url 'preferences:addCotisation' %}">Créer une cotisation</a><br><br>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Durée de cotisation</th>
|
||||
<th>Prix</th>
|
||||
<th>Administration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for cotisation in cotisations %}
|
||||
<tr>
|
||||
<td>{{ cotisation.duration }} jours</td>
|
||||
<td>{{ cotisation.amount }} €</td>
|
||||
<td><a class="button small" href="{% url 'preferences:editCotisation' cotisation.pk %}">Modifier</a> <a class="button small" href="{% url 'preferences:deleteCotisation' cotisation.pk %}">Supprimer</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
89
preferences/templates/preferences/general_preferences.html
Normal file
89
preferences/templates/preferences/general_preferences.html
Normal file
|
@ -0,0 +1,89 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block entete %}
|
||||
<h1>Administration</h1>
|
||||
{% endblock %}
|
||||
{% block nav %}
|
||||
<ul>
|
||||
<li><a href="#first" class="active">Message global</a></li>
|
||||
<li><a href="#second">Site actif</a></li>
|
||||
<li><a href="#third">Bureau</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form class="main" method="post" action="">
|
||||
{% csrf_token %}
|
||||
<section id="first" class="main">
|
||||
<div class="spotlight">
|
||||
<div class="content">
|
||||
<header class="major">
|
||||
<h2>Message global</h2>
|
||||
</header>
|
||||
<div class="row">
|
||||
<div class="12u">
|
||||
{{form.global_message}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="second" class="main">
|
||||
<div class="spotlight">
|
||||
<div class="content">
|
||||
<header class="major">
|
||||
<h2>Site actif</h2>
|
||||
</header>
|
||||
<div class="row uniform">
|
||||
<div class="12u">
|
||||
{{form.is_active}}
|
||||
<label for="{{form.is_active.id_for_label}}">Site actif ?</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row uniform">
|
||||
<div class="12u">
|
||||
{{form.active_message}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="third" class="main">
|
||||
<div class="spotlight">
|
||||
<div class="content">
|
||||
<header class="major">
|
||||
<h2>Bureau</h2>
|
||||
</header>
|
||||
<div class="row uniform">
|
||||
<div class="6u">
|
||||
{{form.president}}
|
||||
</div>
|
||||
<div class="6u">
|
||||
{{form.vice_president}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row uniform">
|
||||
<div class="6u">
|
||||
{{form.secretary}}
|
||||
</div>
|
||||
<div class="6u">
|
||||
{{form.treasurer}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row uniform">
|
||||
<div class="6u">
|
||||
{{form.grocer}}
|
||||
</div>
|
||||
<div class="6u">
|
||||
{{form.brewer}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row uniform">
|
||||
<div class="12u">
|
||||
<button type="submit">Enregistrer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
{% endblock %}
|
39
preferences/templates/preferences/payment_methods_index.html
Normal file
39
preferences/templates/preferences/payment_methods_index.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
{% extends "base.html" %}
|
||||
{% block entete %}<h1>Gestion des moyens de paiement</h1>{% endblock %}
|
||||
{% block navbar %}
|
||||
<ul>
|
||||
<li><a href="#first">Liste des moyens de paiement</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<section id="first" class="main">
|
||||
<header class="major">
|
||||
<h2>Liste des moyens de paiement</h2>
|
||||
</header>
|
||||
<a class="button" href="{% url 'preferences:addPaymentMethod' %}">Créer un moyen de paiement</a><br><br>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nom</th>
|
||||
<th>Actif ?</th>
|
||||
<th>Utilisable dans les cotisations</th>
|
||||
<th>Affecte le solde</th>
|
||||
<th>Administration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for pm in paymentMethods %}
|
||||
<tr>
|
||||
<td>{{ pm.name }} </td>
|
||||
<td>{{ pm.is_active | yesno:"Oui, Non"}}</td>
|
||||
<td>{{ pm.is_usable_in_cotisation | yesno:"Oui, Non" }}</td>
|
||||
<td>{{ pm.affect_balance | yesno:"Oui, Non" }}</td>
|
||||
<td><a class="button small" href="{% url 'preferences:editPaymentMethod' pm.pk %}">Modifier</a> <a class="button small" href="{% url 'preferences:deletePaymentMethod' pm.pk %}">Supprimer</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
16
preferences/urls.py
Normal file
16
preferences/urls.py
Normal file
|
@ -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/<int:pk>', views.editCotisation, name="editCotisation"),
|
||||
path('deleteCotisation/<int:pk>', views.deleteCotisation, name="deleteCotisation"),
|
||||
path('paymentMethodsIndex', views.paymentMethodsIndex, name="paymentMethodsIndex"),
|
||||
path('addPaymentMethod', views.addPaymentMethod, name="addPaymentMethod"),
|
||||
path('editPaymentMethod/<int:pk>', views.editPaymentMethod, name="editPaymentMethod"),
|
||||
path('deletePaymentMethod/<int:pk>', views.deletePaymentMethod, name="deletePaymentMethod"),
|
||||
]
|
|
@ -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'))
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
Django==2.1
|
||||
django-autocomplete-light==3.3.2
|
||||
pytz==2018.5
|
||||
|
|
162
static/autocomplete_light/autocomplete.init.js
Normal file
162
static/autocomplete_light/autocomplete.init.js
Normal file
|
@ -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));
|
183
static/autocomplete_light/forward.js
Normal file
183
static/autocomplete_light/forward.js
Normal file
|
@ -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);
|
36
static/autocomplete_light/jquery.init.js
Normal file
36
static/autocomplete_light/jquery.init.js
Normal file
|
@ -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;
|
||||
}
|
7
static/autocomplete_light/jquery.post-setup.js
Normal file
7
static/autocomplete_light/jquery.post-setup.js
Normal file
|
@ -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;
|
||||
}
|
9
static/autocomplete_light/select2.css
Normal file
9
static/autocomplete_light/select2.css
Normal file
|
@ -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;
|
||||
}
|
116
static/autocomplete_light/select2.js
Normal file
116
static/autocomplete_light/select2.js
Normal file
|
@ -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 = $('<span>');
|
||||
$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(
|
||||
$('<option>', {value: data.id, text: data.text, selected: true})
|
||||
);
|
||||
select.trigger('change');
|
||||
select.select2('close');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
window.__dal__initListenerIsSet = true;
|
||||
$('[data-autocomplete-light-function=select2]:not([id*="__prefix__"])').each(function() {
|
||||
window.__dal__initialize(this);
|
||||
});
|
||||
})(yl.jQuery);
|
|
@ -1,17 +1,3 @@
|
|||
#listePseudoTransaction {
|
||||
}
|
||||
|
||||
#listePseudoTransaction .item {
|
||||
padding: 3px;
|
||||
font-family: Helvetica;
|
||||
border: 1px solid #c0c0c0;
|
||||
}
|
||||
|
||||
#istePseudoTransaction .item:hover {
|
||||
background-color: #f2f2f2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.itemSelected {
|
||||
background-color: #dcdcdc;
|
||||
.select2-container{
|
||||
color: !black;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
@import url(font-awesome.min.css);
|
||||
@import url(font.css);
|
||||
|
||||
|
@ -6,6 +7,12 @@
|
|||
html5up.net | @ajlkn
|
||||
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
||||
*/
|
||||
.select2-container{
|
||||
color: black;
|
||||
}
|
||||
.select2-search__field{
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* Reset */
|
||||
|
||||
|
@ -2072,6 +2079,7 @@
|
|||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
input[type="number"],
|
||||
select,
|
||||
textarea {
|
||||
-moz-appearance: none;
|
||||
|
@ -2091,6 +2099,7 @@
|
|||
input[type="text"]:invalid,
|
||||
input[type="password"]:invalid,
|
||||
input[type="email"]:invalid,
|
||||
input[type="number"]:invalid
|
||||
select:invalid,
|
||||
textarea:invalid {
|
||||
box-shadow: none;
|
||||
|
@ -2131,6 +2140,7 @@
|
|||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
input[type="number"],
|
||||
select {
|
||||
height: 2.75em;
|
||||
}
|
||||
|
@ -2139,7 +2149,7 @@
|
|||
padding: 0.75em 1em;
|
||||
}
|
||||
|
||||
input[type="checkbox"],
|
||||
/*input[type="checkbox"],*/
|
||||
input[type="radio"] {
|
||||
-webkit-appearance: none;
|
||||
-ms-appearance: none;
|
||||
|
@ -2151,7 +2161,7 @@
|
|||
z-index: -1;
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label,
|
||||
/*input[type="checkbox"] + label,*/
|
||||
input[type="radio"] + label {
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
|
@ -2163,7 +2173,7 @@
|
|||
position: relative;
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label:before,
|
||||
/*input[type="checkbox"] + label:before,*/
|
||||
input[type="radio"] + label:before {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
@ -2173,7 +2183,7 @@
|
|||
text-transform: none !important;
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label:before,
|
||||
/*input[type="checkbox"] + label:before,*/
|
||||
input[type="radio"] + label:before {
|
||||
border-radius: 8px;
|
||||
border: solid 1px;
|
||||
|
@ -2188,14 +2198,14 @@
|
|||
width: 1.65em;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked + label:before,
|
||||
/*input[type="checkbox"]:checked + label:before,*/
|
||||
input[type="radio"]:checked + label:before {
|
||||
content: '\f00c';
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label:before {
|
||||
/*input[type="checkbox"] + label:before {
|
||||
border-radius: 8px;
|
||||
}
|
||||
}*/
|
||||
|
||||
input[type="radio"] + label:before {
|
||||
border-radius: 100%;
|
||||
|
@ -2228,6 +2238,7 @@
|
|||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
input[type="number"],
|
||||
select,
|
||||
textarea {
|
||||
background: rgba(255, 255, 255, 0.075);
|
||||
|
@ -2237,6 +2248,7 @@
|
|||
input[type="text"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="email"]:focus,
|
||||
input[type="number"]:focus,
|
||||
select:focus,
|
||||
textarea:focus {
|
||||
border-color: #8cc9f0;
|
||||
|
@ -2247,25 +2259,25 @@
|
|||
color: rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label,
|
||||
/*input[type="checkbox"] + label,*/
|
||||
input[type="radio"] + label {
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label:before,
|
||||
/*input[type="checkbox"] + label:before,*/
|
||||
input[type="radio"] + label:before {
|
||||
background: rgba(255, 255, 255, 0.075);
|
||||
border-color: rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked + label:before,
|
||||
/*input[type="checkbox"]:checked + label:before,*/
|
||||
input[type="radio"]:checked + label:before {
|
||||
background-color: #ffffff;
|
||||
border-color: #ffffff;
|
||||
color: #935d8c;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:focus + label:before,
|
||||
/*input[type="checkbox"]:focus + label:before,*/
|
||||
input[type="radio"]:focus + label:before {
|
||||
border-color: #8cc9f0;
|
||||
box-shadow: 0 0 0 1px #8cc9f0;
|
||||
|
@ -3475,6 +3487,7 @@
|
|||
#main input[type="text"],
|
||||
#main input[type="password"],
|
||||
#main input[type="email"],
|
||||
#main input[type="number"],
|
||||
#main select,
|
||||
#main textarea {
|
||||
background: rgba(222, 222, 222, 0.25);
|
||||
|
@ -3484,6 +3497,7 @@
|
|||
#main input[type="text"]:focus,
|
||||
#main input[type="password"]:focus,
|
||||
#main input[type="email"]:focus,
|
||||
#main input[type="number"]:focus,
|
||||
#main select:focus,
|
||||
#main textarea:focus {
|
||||
border-color: #8cc9f0;
|
||||
|
@ -3494,25 +3508,25 @@
|
|||
color: #dddddd;
|
||||
}
|
||||
|
||||
#main input[type="checkbox"] + label,
|
||||
/*#main input[type="checkbox"] + label,*/
|
||||
#main input[type="radio"] + label {
|
||||
color: #636363;
|
||||
}
|
||||
|
||||
#main input[type="checkbox"] + label:before,
|
||||
/*#main input[type="checkbox"] + label:before,*/
|
||||
#main input[type="radio"] + label:before {
|
||||
background: rgba(222, 222, 222, 0.25);
|
||||
border-color: #dddddd;
|
||||
}
|
||||
|
||||
#main input[type="checkbox"]:checked + label:before,
|
||||
/*#main input[type="checkbox"]:checked + label:before,*/
|
||||
#main input[type="radio"]:checked + label:before {
|
||||
background-color: #636363;
|
||||
border-color: #636363;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#main input[type="checkbox"]:focus + label:before,
|
||||
/*#main input[type="checkbox"]:focus + label:before,*/
|
||||
#main input[type="radio"]:focus + label:before {
|
||||
border-color: #8cc9f0;
|
||||
box-shadow: 0 0 0 1px #8cc9f0;
|
||||
|
|
2
static/jquery.js
vendored
Normal file
2
static/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
58
static/manage.js
Normal file
58
static/manage.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
totalAmount = 0
|
||||
products = []
|
||||
paymentMethod = null
|
||||
solde = 0
|
||||
|
||||
function get_product(barcode){
|
||||
res = $.get("getProduct/" + barcode, function(data){
|
||||
add_product(data.pk, data.barcode, data.name, data.amount);
|
||||
});
|
||||
}
|
||||
|
||||
function add_product(pk, barcode, name, amount){
|
||||
exist = false
|
||||
index = -1
|
||||
for(k=0;k < products.length; k++){
|
||||
if(products[k].pk == pk){
|
||||
exist = true
|
||||
index = k
|
||||
}
|
||||
}
|
||||
if(exist){
|
||||
products[index].quantity += 1;
|
||||
}else{
|
||||
products.push({"pk": pk, "barcode": barcode, "name": name, "amount": amount, "quantity": 1});
|
||||
}
|
||||
generate_html()
|
||||
}
|
||||
|
||||
function generate_html(){
|
||||
html =""
|
||||
for(k=0;k<products.length;k++){
|
||||
product = products[k]
|
||||
html += "<tr><td>" + product.barcode + "</td><td>" + product.name + "</td><td>" + String(product.amount) + "</td><td>" + String(product.quantity) + "</td><td>" + String(product.quantity * product.amount) + "</td></tr>"
|
||||
}
|
||||
$("#items").html(html)
|
||||
updateTotal()
|
||||
}
|
||||
|
||||
function updateTotal(){
|
||||
total = 0
|
||||
for(k=0;k<products.length;k++){
|
||||
total += products[k].quantity * products[k].amount
|
||||
}
|
||||
$("#totalAmount").text(String(total) + "€")
|
||||
if(paymentMethod == "compte"){
|
||||
totalAfter = solde - total
|
||||
$("#totalAfter").text(totalAfter + "€")
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
$(".product").click(function(){
|
||||
product = get_product($(this).attr('target'));
|
||||
});
|
||||
$("#id_paymentMethod").on('change', function(){
|
||||
alert('lol')
|
||||
});
|
||||
});
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
@import url(font-awesome.min.css);
|
||||
@import url(font.css);
|
||||
|
||||
|
@ -6,6 +7,12 @@
|
|||
html5up.net | @ajlkn
|
||||
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
||||
*/
|
||||
.select2-container{
|
||||
color: black;
|
||||
}
|
||||
.select2-search__field{
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* Reset */
|
||||
|
||||
|
@ -2072,6 +2079,7 @@
|
|||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
input[type="number"],
|
||||
select,
|
||||
textarea {
|
||||
-moz-appearance: none;
|
||||
|
@ -2091,6 +2099,7 @@
|
|||
input[type="text"]:invalid,
|
||||
input[type="password"]:invalid,
|
||||
input[type="email"]:invalid,
|
||||
input[type="number"]:invalid
|
||||
select:invalid,
|
||||
textarea:invalid {
|
||||
box-shadow: none;
|
||||
|
@ -2131,6 +2140,7 @@
|
|||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
input[type="number"],
|
||||
select {
|
||||
height: 2.75em;
|
||||
}
|
||||
|
@ -2139,7 +2149,7 @@
|
|||
padding: 0.75em 1em;
|
||||
}
|
||||
|
||||
input[type="checkbox"],
|
||||
/*input[type="checkbox"],*/
|
||||
input[type="radio"] {
|
||||
-webkit-appearance: none;
|
||||
-ms-appearance: none;
|
||||
|
@ -2151,7 +2161,7 @@
|
|||
z-index: -1;
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label,
|
||||
/*input[type="checkbox"] + label,*/
|
||||
input[type="radio"] + label {
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
|
@ -2163,7 +2173,7 @@
|
|||
position: relative;
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label:before,
|
||||
/*input[type="checkbox"] + label:before,*/
|
||||
input[type="radio"] + label:before {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
@ -2173,7 +2183,7 @@
|
|||
text-transform: none !important;
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label:before,
|
||||
/*input[type="checkbox"] + label:before,*/
|
||||
input[type="radio"] + label:before {
|
||||
border-radius: 8px;
|
||||
border: solid 1px;
|
||||
|
@ -2188,14 +2198,14 @@
|
|||
width: 1.65em;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked + label:before,
|
||||
/*input[type="checkbox"]:checked + label:before,*/
|
||||
input[type="radio"]:checked + label:before {
|
||||
content: '\f00c';
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label:before {
|
||||
/*input[type="checkbox"] + label:before {
|
||||
border-radius: 8px;
|
||||
}
|
||||
}*/
|
||||
|
||||
input[type="radio"] + label:before {
|
||||
border-radius: 100%;
|
||||
|
@ -2228,6 +2238,7 @@
|
|||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
input[type="number"],
|
||||
select,
|
||||
textarea {
|
||||
background: rgba(255, 255, 255, 0.075);
|
||||
|
@ -2237,6 +2248,7 @@
|
|||
input[type="text"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="email"]:focus,
|
||||
input[type="number"]:focus,
|
||||
select:focus,
|
||||
textarea:focus {
|
||||
border-color: #8cc9f0;
|
||||
|
@ -2247,25 +2259,25 @@
|
|||
color: rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label,
|
||||
/*input[type="checkbox"] + label,*/
|
||||
input[type="radio"] + label {
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label:before,
|
||||
/*input[type="checkbox"] + label:before,*/
|
||||
input[type="radio"] + label:before {
|
||||
background: rgba(255, 255, 255, 0.075);
|
||||
border-color: rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked + label:before,
|
||||
/*input[type="checkbox"]:checked + label:before,*/
|
||||
input[type="radio"]:checked + label:before {
|
||||
background-color: #ffffff;
|
||||
border-color: #ffffff;
|
||||
color: #935d8c;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:focus + label:before,
|
||||
/*input[type="checkbox"]:focus + label:before,*/
|
||||
input[type="radio"]:focus + label:before {
|
||||
border-color: #8cc9f0;
|
||||
box-shadow: 0 0 0 1px #8cc9f0;
|
||||
|
@ -3475,6 +3487,7 @@
|
|||
#main input[type="text"],
|
||||
#main input[type="password"],
|
||||
#main input[type="email"],
|
||||
#main input[type="number"],
|
||||
#main select,
|
||||
#main textarea {
|
||||
background: rgba(222, 222, 222, 0.25);
|
||||
|
@ -3484,6 +3497,7 @@
|
|||
#main input[type="text"]:focus,
|
||||
#main input[type="password"]:focus,
|
||||
#main input[type="email"]:focus,
|
||||
#main input[type="number"]:focus,
|
||||
#main select:focus,
|
||||
#main textarea:focus {
|
||||
border-color: #8cc9f0;
|
||||
|
@ -3494,25 +3508,25 @@
|
|||
color: #dddddd;
|
||||
}
|
||||
|
||||
#main input[type="checkbox"] + label,
|
||||
/*#main input[type="checkbox"] + label,*/
|
||||
#main input[type="radio"] + label {
|
||||
color: #636363;
|
||||
}
|
||||
|
||||
#main input[type="checkbox"] + label:before,
|
||||
/*#main input[type="checkbox"] + label:before,*/
|
||||
#main input[type="radio"] + label:before {
|
||||
background: rgba(222, 222, 222, 0.25);
|
||||
border-color: #dddddd;
|
||||
}
|
||||
|
||||
#main input[type="checkbox"]:checked + label:before,
|
||||
/*#main input[type="checkbox"]:checked + label:before,*/
|
||||
#main input[type="radio"]:checked + label:before {
|
||||
background-color: #636363;
|
||||
border-color: #636363;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#main input[type="checkbox"]:focus + label:before,
|
||||
/*#main input[type="checkbox"]:focus + label:before,*/
|
||||
#main input[type="radio"]:focus + label:before {
|
||||
border-color: #8cc9f0;
|
||||
box-shadow: 0 0 0 1px #8cc9f0;
|
||||
|
|
2
staticfiles/jquery.js
vendored
Normal file
2
staticfiles/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
67
staticfiles/manage.js
Normal file
67
staticfiles/manage.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
totalAmount = 0
|
||||
products = []
|
||||
paymentMethod = null
|
||||
balance = 0
|
||||
username = ""
|
||||
id = 0
|
||||
|
||||
function get_product(barcode){
|
||||
res = $.get("getProduct/" + barcode, function(data){
|
||||
add_product(data.pk, data.barcode, data.name, data.amount);
|
||||
});
|
||||
}
|
||||
|
||||
function add_product(pk, barcode, name, amount){
|
||||
exist = false
|
||||
index = -1
|
||||
for(k=0;k < products.length; k++){
|
||||
if(products[k].pk == pk){
|
||||
exist = true
|
||||
index = k
|
||||
}
|
||||
}
|
||||
if(exist){
|
||||
products[index].quantity += 1;
|
||||
}else{
|
||||
products.push({"pk": pk, "barcode": barcode, "name": name, "amount": amount, "quantity": 1});
|
||||
}
|
||||
generate_html()
|
||||
}
|
||||
|
||||
function generate_html(){
|
||||
html =""
|
||||
for(k=0;k<products.length;k++){
|
||||
product = products[k]
|
||||
html += "<tr><td>" + product.barcode + "</td><td>" + product.name + "</td><td>" + String(product.amount) + "</td><td>" + String(product.quantity) + "</td><td>" + String(product.quantity * product.amount) + "</td></tr>"
|
||||
}
|
||||
$("#items").html(html)
|
||||
updateTotal()
|
||||
}
|
||||
|
||||
function updateTotal(){
|
||||
total = 0
|
||||
for(k=0;k<products.length;k++){
|
||||
total += products[k].quantity * products[k].amount
|
||||
}
|
||||
$("#totalAmount").text(String(total) + "€")
|
||||
totalAfter = balance - total
|
||||
$("#totalAfter").text(totalAfter + "€")
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
$(".product").click(function(){
|
||||
product = get_product($(this).attr('target'));
|
||||
});
|
||||
$("#id_client").on('change', function(){
|
||||
id = $("#id_client").val();
|
||||
$.get("/users/getUser/" + id, function(data){
|
||||
balance = data.balance;
|
||||
username = data.username;
|
||||
$("#balance").html(balance + "€");
|
||||
updateTotal();
|
||||
}).fail(function(){
|
||||
alert("Une erreur inconnue est survenue");
|
||||
window.location.reload()
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,4 +1,5 @@
|
|||
{% load static %}
|
||||
{% load vip %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
@ -16,7 +17,7 @@
|
|||
<header id="header" class="alt">
|
||||
<span class="logo"><img src="{%static 'Images/coope.png' %}" alt="" /></span>
|
||||
<h1>{% block entete %}{% endblock %}</h1>
|
||||
<h3></h3>
|
||||
<h3>{% global_message %}</h3>
|
||||
<nav>
|
||||
{% include 'nav.html' %}
|
||||
</nav>
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
{% load vip %}
|
||||
<section>
|
||||
<h2>A propos</h2>
|
||||
<p>{% lorem %}</p>
|
||||
<ul class="actions">
|
||||
<li>
|
||||
<a class="button" href="">En savoir plus</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Contacts</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Email</td>
|
||||
<td>coopemetz@gmail.com</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Prez</td>
|
||||
<td>{% president %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>V-Prez</td>
|
||||
<td>{% vice_president %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Trésorier</td>
|
||||
<td>{% treasurer %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Secrétaire</td>
|
||||
<td>{% secretary %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Maitre brasseur</td>
|
||||
<td>{% brewer %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Epic Epicier</td>
|
||||
<td>{% grocer %}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<ul class="icons">
|
||||
<li><a href="https://www.facebook.com/coopesmetz/" class="icon fa-facebook alt"><span class="label">Facebook</span></a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<p class="copyright">coopeV3 © 2016 - 2018. Remi Delannoy - Guillaume Goessel - Yoann Pietri. Design: <a href="https://html5up.net">HTML5 UP</a>.</p>
|
||||
|
|
@ -25,4 +25,5 @@
|
|||
{{extra_css}}
|
||||
</style>
|
||||
{% endif %}
|
||||
{{form.media}}
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
{% if request.user.is_authenticated %}
|
||||
<span class="tabulation2">
|
||||
<a href="{% url 'users:profile' request.user.pk %}">Mon profil</a>
|
||||
</span>
|
||||
<span class="tabulation2">
|
||||
<a href="{% url 'gestion:manage' %}">Caisse</a>
|
||||
</span>
|
||||
<span class="tabulation2">
|
||||
<a href="{% url 'users:index' %}">Gestion des clients</a>
|
||||
</span>
|
||||
<span class="tabulation2">
|
||||
<a href="{% url 'gestion:productsIndex' %}">Gestion des produits</a>
|
||||
</span>
|
||||
<span class="tabulation2">
|
||||
<a href="">Comptabilité</a>
|
||||
</span>
|
||||
<span class="tabulation2">
|
||||
<a href="">Classement</a>
|
||||
</span>
|
||||
<span class="tabulation2">
|
||||
<a href="">Classement sur l'année</a>
|
||||
</span>
|
||||
<span class="tabulation2">
|
||||
<a href="{% url 'preferences:generalPreferences' %}">Admin</a>
|
||||
</span>
|
||||
<span class="tabulation2">
|
||||
<a href="{% url 'preferences:cotisationsIndex' %}">Cotisations</a>
|
||||
</span>
|
||||
<span class="tabulation2">
|
||||
<a href="{% url 'preferences:paymentMethodsIndex' %}">Moyens de paiement</a>
|
||||
</span>
|
||||
<span class="tabulation2">
|
||||
<a href="{% url 'users:logout' %}">Deconnexion</a>
|
||||
</span>
|
||||
{% else %}
|
||||
<a href="{% url 'users:login' %}">Connexion</a>
|
||||
{% endif %}
|
8
templates/search_field.html
Normal file
8
templates/search_field.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
<input {{attrs}} name="{{name}}" type="text" class="form-control" placeholder="{{placeholder}}"/>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$("#" + {{attrs.id}}).click(function(){
|
||||
alert('lol')
|
||||
});
|
||||
};
|
||||
</script>
|
|
@ -1,7 +1,8 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import School, Profile
|
||||
from .models import School, Profile, CotisationHistory
|
||||
|
||||
admin.site.register(School)
|
||||
admin.site.register(Profile)
|
||||
admin.site.register(CotisationHistory)
|
||||
# Register your models here.
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from django import forms
|
||||
from django.contrib.auth.models import User, Group
|
||||
from dal import autocomplete
|
||||
from .models import School, CotisationHistory, WhiteListHistory
|
||||
|
||||
from .models import School
|
||||
class LoginForm(forms.Form):
|
||||
username = forms.CharField(max_length=255, label="Nom d'utitisateur")
|
||||
password = forms.CharField(max_length=255, widget=forms.PasswordInput, label="Mot de passe")
|
||||
|
@ -24,14 +25,43 @@ class EditGroupForm(forms.ModelForm):
|
|||
fields = "__all__"
|
||||
|
||||
class SelectUserForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
restrictTo = kwargs.pop("restrictTo") or None
|
||||
if(restrictTo == "non-superusers"):
|
||||
self.queryset = User.objects.filter(is_superuser=False)
|
||||
elif(restrictTo == "non-admins"):
|
||||
self.queryset = User.objects.filter(is_staff=False)
|
||||
else:
|
||||
self.queryset = User.objects.all()
|
||||
super(SelectUserForm, self).__init__(*args, **kwargs)
|
||||
self.fields['user'].queryset = self.queryset
|
||||
user = forms.ModelChoiceField(queryset=User.objects.all(), label="Utilisateur")
|
||||
|
||||
class SelectNonSuperUserForm(forms.Form):
|
||||
user = forms.ModelChoiceField(queryset=User.objects.filter(is_active=True), required=True, label="Utilisateur", widget=autocomplete.ModelSelect2(url='users:non-super-users-autocomplete', attrs={'data-minimum-input-length':2}))
|
||||
|
||||
class SelectNonAdminUserForm(forms.Form):
|
||||
user = forms.ModelChoiceField(queryset=User.objects.filter(is_active=True), required=True, label="Utilisateur", widget=autocomplete.ModelSelect2(url='users:active-users-autocomplete', attrs={'data-minimum-input-length':2}))
|
||||
|
||||
|
||||
class GroupsEditForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ("groups", )
|
||||
|
||||
class EditPasswordForm(forms.Form):
|
||||
password = forms.CharField(max_length=255, widget=forms.PasswordInput, label="Mot de passe actuel")
|
||||
password1 = forms.CharField(max_length=255, widget=forms.PasswordInput, label="Nouveau mot de passe")
|
||||
password2 = forms.CharField(max_length=255, widget=forms.PasswordInput, label="Nouveau mot de passe (répétez)")
|
||||
|
||||
def clean_password2(self):
|
||||
password1 = self.cleaned_data.get("password1")
|
||||
password2 = self.cleaned_data.get("password2")
|
||||
if password1 and password2 and password1 != password2:
|
||||
raise forms.ValidationError("Les mots de passe ne sont pas identiques")
|
||||
return password2
|
||||
|
||||
class addCotisationHistoryForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = CotisationHistory
|
||||
fields = ("cotisation", "paymentMethod")
|
||||
|
||||
class addWhiteListHistoryForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = WhiteListHistory
|
||||
fields = ("duration", )
|
||||
|
||||
class SchoolForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = School
|
||||
fields = "__all__"
|
|
@ -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
|
||||
|
@ -10,20 +10,24 @@ 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='Cotisation',
|
||||
name='CotisationHistory',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('amount', models.DecimalField(decimal_places=2, max_digits=5)),
|
||||
('paymentDate', models.DateTimeField(auto_now_add=True)),
|
||||
('endDate', models.DateTimeField()),
|
||||
('paymentMethod', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='preferences.PaymentMethod')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||
('amount', models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Montant')),
|
||||
('duration', models.PositiveIntegerField(verbose_name='Durée')),
|
||||
('paymentDate', models.DateTimeField(auto_now_add=True, verbose_name='Date du paiement')),
|
||||
('endDate', models.DateTimeField(verbose_name='Fin de la cotisation')),
|
||||
('valid', models.IntegerField(choices=[(0, 'En attente de validation'), (1, 'Validée'), (2, 'Invalidée')], default=0)),
|
||||
('coopeman', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='cotisation_made', to=settings.AUTH_USER_MODEL)),
|
||||
('cotisation', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='preferences.Cotisation', verbose_name='Type de cotisation')),
|
||||
('paymentMethod', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='preferences.PaymentMethod', verbose_name='Moyen de paiement')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='Client')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
|
@ -42,6 +46,17 @@ class Migration(migrations.Migration):
|
|||
('name', models.CharField(max_length=255)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='WhiteListHistory',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('paymentDate', models.DateTimeField(auto_now_add=True)),
|
||||
('endDate', models.DateTimeField()),
|
||||
('duration', models.PositiveIntegerField(help_text="Durée de l'accès gracieux en jour", verbose_name='Durée')),
|
||||
('coopeman', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='whitelist_made', to=settings.AUTH_USER_MODEL)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='school',
|
||||
|
|
|
@ -3,20 +3,39 @@ from django.contrib.auth.models import User
|
|||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
from preferences.models import PaymentMethod
|
||||
from preferences.models import PaymentMethod, Cotisation
|
||||
|
||||
class School(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
name = models.CharField(max_length=255, verbose_name="Nom")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Cotisation(models.Model):
|
||||
class CotisationHistory(models.Model):
|
||||
WAITING = 0
|
||||
VALID = 1
|
||||
INVALID = 2
|
||||
VALIDATION_CHOICES = (
|
||||
(WAITING, 'En attente de validation'),
|
||||
(VALID, 'Validée'),
|
||||
(INVALID, 'Invalidée'),
|
||||
)
|
||||
user = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name="Client")
|
||||
amount = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Montant")
|
||||
duration = models.PositiveIntegerField(verbose_name="Durée")
|
||||
paymentDate = models.DateTimeField(auto_now_add=True, verbose_name="Date du paiement")
|
||||
endDate = models.DateTimeField(verbose_name="Fin de la cotisation")
|
||||
paymentMethod = models.ForeignKey(PaymentMethod, on_delete=models.PROTECT, verbose_name="Moyen de paiement")
|
||||
cotisation = models.ForeignKey(Cotisation, on_delete=models.PROTECT, verbose_name="Type de cotisation")
|
||||
coopeman = models.ForeignKey(User, on_delete=models.PROTECT, related_name="cotisation_made")
|
||||
valid = models.IntegerField(choices=VALIDATION_CHOICES, default=WAITING)
|
||||
|
||||
class WhiteListHistory(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||
amount = models.DecimalField(max_digits=5, decimal_places=2)
|
||||
paymentDate = models.DateTimeField(auto_now_add=True)
|
||||
endDate = models.DateTimeField()
|
||||
paymentMethod = models.ForeignKey(PaymentMethod, on_delete=models.PROTECT)
|
||||
duration = models.PositiveIntegerField(verbose_name="Durée", help_text="Durée de l'accès gracieux en jour")
|
||||
coopeman = models.ForeignKey(User, on_delete=models.PROTECT, related_name="whitelist_made")
|
||||
|
||||
class Profile(models.Model):
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||
|
@ -30,7 +49,7 @@ class Profile(models.Model):
|
|||
return self.credit - self.debit
|
||||
|
||||
def positiveBalance(self):
|
||||
return self.solde() >= 0
|
||||
return self.balance >= 0
|
||||
|
||||
@property
|
||||
def rank(self):
|
||||
|
@ -45,6 +64,9 @@ class Profile(models.Model):
|
|||
#alcool += conso.nombre * float(produit.deg) * produit.volume * 0.79 /10 /1000
|
||||
return 0
|
||||
|
||||
def __str__(self):
|
||||
return str(self.user)
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def create_user_profile(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
|
@ -52,4 +74,4 @@ def create_user_profile(sender, instance, created, **kwargs):
|
|||
|
||||
@receiver(post_save, sender=User)
|
||||
def save_user_profile(sender, instance, **kwargs):
|
||||
instance.profile.save()
|
||||
instance.profile.save()
|
|
@ -6,6 +6,7 @@
|
|||
<li><a href="#second">Groupes</a></li>
|
||||
<li><a href="#third">Admins</a></li>
|
||||
<li><a href="#fourth">Superusers</a></li>
|
||||
<li><a href="#fifth">Écoles</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
@ -16,8 +17,8 @@
|
|||
Actions possibles :
|
||||
<ul>
|
||||
<li><a href="{% url 'users:createUser' %}">Ajouter un utilisateur</a></li>
|
||||
<li><a href="">Rechercher un utilisateur</a></li>
|
||||
<li><a href="">Lister tous les utilisateurs</a></li>
|
||||
<li><a href="{% url 'users:searchUser' %}">Rechercher un utilisateur</a></li>
|
||||
<li><a href="{% url 'users:usersIndex' %}">Lister tous les utilisateurs</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="second" class="main">
|
||||
|
@ -52,4 +53,14 @@
|
|||
<li><a href="{% url 'users:superusersIndex' %}">Lister les superuser</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="fifth" class="main">
|
||||
<header class="major">
|
||||
<h2>Écoles</h2>
|
||||
</header>
|
||||
Actions possibles :
|
||||
<ul>
|
||||
<li><a href="{% url 'users:createSchool' %}">Ajouter une école</a></li>
|
||||
<li><a href="{% url 'users:schoolsIndex' %}">Lister les écoles</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<li><a href="#third">{% if self %}Mes derniers rechargements{% else %}Derniers rechargements {% endif %}</a>
|
||||
</li>
|
||||
<li><a href="#fourth">{% if self %} Mes cotisations {% else %} Cotisations {% endif %}</a></li>
|
||||
<li><a href="#fifth">{% if self %} Mes accès gracieux {% else %} Accès gracieux {% endif %}</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
@ -24,7 +25,8 @@
|
|||
<ul class="alt" id="informationsClient">
|
||||
<li>
|
||||
<b>Nom : </b>{{user.last_name}}<span class="tabulation">
|
||||
<b>Prénom : </b>{{user.first_name}}</span>
|
||||
<b>Prénom : </b>{{user.first_name}}</span><span class="tabulation">
|
||||
<b>École : </b>{{user.profile.school}}</span>
|
||||
</li>
|
||||
<li><b>Pseudo : </b>{{user.username}}<span class="tabulation">
|
||||
<b>Mail: </b> {{user.email}}</span>
|
||||
|
@ -37,7 +39,7 @@
|
|||
<b>Crédit : </b>{{user.profile.credit}}</span><span class="tabulation">
|
||||
<b>Débit : </b>{{user.profile.debit}}</span>
|
||||
</li>
|
||||
<li><b>Groupes : </b>{{user.group_set|join:", "}}</li>
|
||||
<li><b>Groupe(s) : </b>{{user.groups.all|join:", "}}</li>
|
||||
<li>
|
||||
<b>Position au classement : </b>{{user.profile.rank}}<span class="tabulation">
|
||||
<b>Quantité d'alcool ingérée : </b>{{user.profile.alcohol}} kg</span>
|
||||
|
@ -48,14 +50,30 @@
|
|||
<ul class="alt">
|
||||
<li>
|
||||
{% if self or perms.users.can_change_user %}
|
||||
<span><a href="">Modifier {{self | yesno:"mes,les"}} informations</a></span>
|
||||
<span class="tabulation"><a href="">Changer {{self | yesno:"mon,le"}} mot de passe</a></span>
|
||||
<span><a href="{% url 'users:editUser' user.pk %}">Modifier {{self | yesno:"mes,les"}} informations</a></span>
|
||||
{% endif %}
|
||||
{% if self %}
|
||||
<span class="tabulation"><a href="{% url 'users:editPassword' user.pk %}">Changer mon mot de passe</a></span>
|
||||
{% endif %}
|
||||
{% if perms.users.can_reset_password %}
|
||||
<span class="tabulation"><a href="">Réinitialiser le mot de passe</a></span>
|
||||
<span class="tabulation"><a href="{% url 'users:resetPassword' user.pk %}">Réinitialiser le mot de passe</a></span>
|
||||
{% endif %}
|
||||
{% if perms.users.can_change_user_perm %}
|
||||
<span class="tabulation"><a href="">Changer les groupes</a></span>
|
||||
<span class="tabulation"><a href="{% url 'users:editGroups' user.pk %}">Changer les groupes</a></span>
|
||||
{% endif %}
|
||||
{% if request.user.is_staff %}
|
||||
{% if user.is_staff %}
|
||||
<span class="tabulation"><a href="">Retirer des admins</a></span>
|
||||
{% else %}
|
||||
<span class="tabulation"><a href="">Ajouter aux admins</a></span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if request.user.is_superuser %}
|
||||
{% if user.is_superuser %}
|
||||
<span class="tabulation"><a href="">Retirer des superusers</a></span>
|
||||
{% else %}
|
||||
<span class="tabulation"><a href="">Ajouter aux superusers</a></span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -65,7 +83,7 @@
|
|||
<section class="row uniform">
|
||||
</section>
|
||||
</section>
|
||||
<section id="second" class="main special">
|
||||
<section id="second" class="main">
|
||||
<header class="major">
|
||||
<h2>{{self | yesno:"Mes dernières,Dernières"}} consommations</h2>
|
||||
<p>(Affichage des 10 dernières entrées)</p>
|
||||
|
@ -96,7 +114,7 @@
|
|||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="third" class="main special">
|
||||
<section id="third" class="main">
|
||||
<header class="major">
|
||||
<h2>{{self | yesno:"Mes derniers,Derniers"}} rechargements</h2>
|
||||
<p>(Affichage des 5 dernières entrées)</p>
|
||||
|
@ -125,31 +143,67 @@
|
|||
</section>
|
||||
</section>
|
||||
|
||||
<section id="fourth" class="main special">
|
||||
<section id="fourth" class="main">
|
||||
<header class="major">
|
||||
<h2>{{ self | yesno:"Mes cotisations,Cotisations"}}</h2>
|
||||
</header>
|
||||
<section>
|
||||
<a class="button" href="{% url 'users:addCotisationHistory' user.pk %}">Ajouter une cotisation</a><br><br>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Montant</th>
|
||||
<th>Durée</th>
|
||||
<th>Date de paiement</th>
|
||||
<th>Moyen de paimement</th>
|
||||
<th>Moyen de paiement</th>
|
||||
<th>Date de fin</th>
|
||||
<th>Etat</th>
|
||||
<th>Modération</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for cotisation in cotisations %}
|
||||
<tr>
|
||||
<td>{{cotisation.amount}}</td>
|
||||
<td>{{cotisation.amount}}€</td>
|
||||
<td>{{cotisation.duration}} jours</td>
|
||||
<td>{{cotisation.paymentDate}}</td>
|
||||
<td>{{cotisation.paymentMethod}}</td>
|
||||
<td>{{cotisation.endDate}}</td>
|
||||
<td>{{cotisation.valid}}</td>
|
||||
<td><a class="button small" href="{% url 'users:validateCotisationHistory' cotisation.pk %}">Valider</a> <a class="button small" href="{% url 'users:invalidateCotisationHistory' cotisation.pk %}">Invalider</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbdoy>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="fifth" class="main">
|
||||
<header class="major">
|
||||
<h2>{{ self | yesno:"Mes accès gracieux,Accès gracieux"}}</h2>
|
||||
</header>
|
||||
<section>
|
||||
<a class="button" href="{% url 'users:addWhiteListHistory' user.pk %}">Ajouter un accès à titre gracieux</a><br><br>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date de l'ajout</th>
|
||||
<th>Date de fin</th>
|
||||
<th>Durée</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for whitelist in whitelists %}
|
||||
<tr>
|
||||
<td>{{whitelist.paymentDate}}</td>
|
||||
<td>{{whitelist.endDate}}</td>
|
||||
<td>{{whitelist.duration}} jours</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
|
33
users/templates/users/schools_index.html
Normal file
33
users/templates/users/schools_index.html
Normal file
|
@ -0,0 +1,33 @@
|
|||
{% extends "base.html" %}
|
||||
{% block entete %}<h1>Gestion des écoles</h1>{% endblock %}
|
||||
{% block navbar %}
|
||||
<ul>
|
||||
<li><a href="#first">Liste des écoles</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<section id="first" class="main">
|
||||
<header class="major">
|
||||
<h2>Liste des écoles</h2>
|
||||
</header>
|
||||
<a class="button" href="{% url 'users:createSchool' %}">Créer une école</a><br><br>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Ecole</th>
|
||||
<th>Administration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for school in schools %}
|
||||
<tr>
|
||||
<td>{{ school }}</td>
|
||||
<td><a class="button small" href="{% url 'users:editSchool' school.pk %}">Modifier</a> <a class="button small" href="{% url 'users:deleteSchool' school.pk %}">Supprimer</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
33
users/templates/users/users_index.html
Normal file
33
users/templates/users/users_index.html
Normal file
|
@ -0,0 +1,33 @@
|
|||
{% extends "base.html" %}
|
||||
{% block entete %}<h1>Gestion des utilisateurs</h1>{% endblock %}
|
||||
{% block navbar %}
|
||||
<ul>
|
||||
<li><a href="#first">Liste des utilisateurs</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<section id="first" class="main">
|
||||
<header class="major">
|
||||
<h2>Liste des utilisateurs</h2>
|
||||
</header>
|
||||
<a class="button" href="{% url 'users:createUser' %}">Créer un utilisateur</a><br><br>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Utilisateur</th>
|
||||
<th>Profil</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<td>{{ user }}</td>
|
||||
<td><a class="button small" href="{% url 'users:profile' user.pk %}">Profil</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
|
@ -8,6 +8,12 @@ urlpatterns = [
|
|||
path('index', views.index, name="index"),
|
||||
path('profile/<int:pk>', views.profile, name="profile"),
|
||||
path('createUser', views.createUser, name="createUser"),
|
||||
path('searchUser', views.searchUser, name="searchUser"),
|
||||
path('usersIndex', views.usersIndex, name="usersIndex"),
|
||||
path('editGroups/<int:pk>', views.editGroups, name="editGroups"),
|
||||
path('editPassword/<int:pk>', views.editPassword, name="editPassword"),
|
||||
path('editUser/<int:pk>', views.editUser, name="editUser"),
|
||||
path('resetPassword/<int:pk>', views.resetPassword, name="resetPassword"),
|
||||
path('groupsIndex', views.groupsIndex, name="groupsIndex"),
|
||||
path('groupProfile/<int:pk>', 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/<int:pk>', 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/<int:pk>', views.getUser, name="getUser"),
|
||||
path('addCotisationHistory/<int:pk>', views.addCotisationHistory, name="addCotisationHistory"),
|
||||
path('validateCotisationHistory/<int:pk>', views.validateCotisationHistory, name="validateCotisationHistory"),
|
||||
path('invalidateCotisationHistory/<int:pk>', views.invalidateCotisationHistory, name="invalidateCotisationHistory"),
|
||||
path('addWhiteListHistory/<int:pk>', views.addWhiteListHistory, name="addWhiteListHistory"),
|
||||
path('schoolsIndex', views.schoolsIndex, name="schoolsIndex"),
|
||||
path('createSchool', views.createSchool, name="createSchool"),
|
||||
path('editSchool/<int:pk>', views.editSchool, name="editSchool"),
|
||||
path('deleteSchool/<int:pk>', views.deleteSchool, name="deleteSchool"),
|
||||
]
|
||||
|
|
201
users/views.py
201
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
|
Loading…
Reference in a new issue