diff --git a/.gitignore b/.gitignore
index 2e8c3f4..8e15950 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,3 +39,4 @@ tags
# End of https://www.gitignore.io/api/vim,git,django
+.vscode
\ No newline at end of file
diff --git a/coopeV3/settings.py b/coopeV3/settings.py
index 8b51238..0cb6d72 100644
--- a/coopeV3/settings.py
+++ b/coopeV3/settings.py
@@ -37,12 +37,13 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
+ 'django.contrib.admindocs',
'gestion',
'users',
'preferences',
'coopeV3',
'dal',
- 'dal_select2',
+ 'dal_select2',
]
MIDDLEWARE = [
@@ -129,4 +130,4 @@ STATICFILES_DIRS = [
]
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
-
+LOGIN_URL = '/users/login'
\ No newline at end of file
diff --git a/coopeV3/urls.py b/coopeV3/urls.py
index 62c9708..83de9b1 100644
--- a/coopeV3/urls.py
+++ b/coopeV3/urls.py
@@ -20,8 +20,9 @@ from . import views
urlpatterns = [
path('', views.home, name="home"),
+ path('admin/doc/', include('django.contrib.admindocs.urls')),
path('admin/', admin.site.urls),
path('users/', include('users.urls')),
path('gestion/', include('gestion.urls')),
- path('preferences/', include('preferences.urls')),
+ path('preferences/', include('preferences.urls')),
]
diff --git a/gestion/admin.py b/gestion/admin.py
index ff8c544..65d67ea 100644
--- a/gestion/admin.py
+++ b/gestion/admin.py
@@ -1,8 +1,9 @@
from django.contrib import admin
-from .models import Reload, Refund, Product, Keg
+from .models import Reload, Refund, Product, Keg, ConsumptionHistory
admin.site.register(Reload)
admin.site.register(Refund)
admin.site.register(Product)
admin.site.register(Keg)
+admin.site.register(ConsumptionHistory)
\ No newline at end of file
diff --git a/gestion/forms.py b/gestion/forms.py
index ebdd877..8d3250c 100644
--- a/gestion/forms.py
+++ b/gestion/forms.py
@@ -1,4 +1,5 @@
from django import forms
+from django.core.exceptions import ValidationError
from django.contrib.auth.models import User
from dal import autocomplete
@@ -8,14 +9,38 @@ from preferences.models import PaymentMethod
from coopeV3.widgets import SearchField
class ReloadForm(forms.ModelForm):
+ def __init__(self, *args, **kwargs):
+ super(ReloadForm, self).__init__(*args, **kwargs)
+ self.fields['PaymentMethod'].queryset = PaymentMethod.objects.filter(is_usable_in_reload=True)
+
class Meta:
model = Reload
fields = ("customer", "amount", "PaymentMethod")
+ widgets = {'customer': autocomplete.ModelSelect2(url='users:active-users-autocomplete', attrs={'data-minimum-input-length':2})}
+
+ def clean_amount(self):
+ if self.cleaned_data['amount'] <= 0:
+ raise ValidationError(
+ "Le montant doit être strictement positif"
+ )
+ else:
+ return self.cleaned_data['amount']
+
class RefundForm(forms.ModelForm):
class Meta:
model = Refund
fields = ("customer", "amount")
+ widgets = {'customer': autocomplete.ModelSelect2(url='users:active-users-autocomplete', attrs={'data-minimum-input-length':2})}
+
+ def clean_amount(self):
+ if self.cleaned_data['amount'] <= 0:
+ raise ValidationError(
+ "Le montant doit être strictement positif"
+ )
+ else:
+ return self.cleaned_data['amount']
+
class ProductForm(forms.ModelForm):
class Meta:
@@ -32,6 +57,11 @@ class MenuForm(forms.ModelForm):
model = Menu
fields = "__all__"
+class SearchProductForm(forms.Form):
+ product = forms.ModelChoiceField(queryset=Product.objects.all(), required=True, label="Produit", widget=autocomplete.ModelSelect2(url='gestion:products-autocomplete', attrs={'data-minimum-input-length':2}))
+
+class SearchMenuForm(forms.Form):
+ menu = forms.ModelChoiceField(queryset=Menu.objects.all(), required=True, label="Menu", widget=autocomplete.ModelSelect2(url='gestion:menus-autocomplete', attrs={'data-minimum-input-length':2}))
+
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")
+ 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}))
\ No newline at end of file
diff --git a/gestion/models.py b/gestion/models.py
index b4fffc6..94da081 100644
--- a/gestion/models.py
+++ b/gestion/models.py
@@ -138,7 +138,7 @@ class MenuHistory(models.Model):
coopeman = models.ForeignKey(User, on_delete=models.PROTECT, related_name="menu_selled")
def __str__(self):
- return "{2} a consommé {0} {1}".format(self.quantite, self.menu, self.client)
+ return "{2} a consommé {0} {1}".format(self.quantity, self.menu, self.customer)
class ConsumptionHistory(models.Model):
customer = models.ForeignKey(User, on_delete=models.PROTECT, related_name="consumption_taken")
diff --git a/gestion/templates/gestion/manage.html b/gestion/templates/gestion/manage.html
index ca522d7..8f1eec4 100644
--- a/gestion/templates/gestion/manage.html
+++ b/gestion/templates/gestion/manage.html
@@ -61,7 +61,7 @@
0€
0€
0€
- Payer
+ {% for pm in pay_buttons %}{{pm.name}} {% endfor %}
@@ -93,11 +93,11 @@
Bières pression
- {% for produit in bieresPression %}
+ {% for product in bieresPression %}
{% if forloop.counter0|divisibleby:4 %}
{% endif %}
- {{produit.name}}
+ {{product.name}}
{% if forloop.counter|divisibleby:4 %}
{% endif %}
@@ -106,11 +106,11 @@
{% endif %}
Bières bouteilles
- {% for produit in bieresBouteille %}
+ {% for product in bieresBouteille %}
{% if forloop.counter0|divisibleby:4 %}
{% endif %}
- {{produit.nom}}
+ {{product.name}}
{% if forloop.counter|divisibleby:4 %}
{% endif %}
@@ -119,11 +119,11 @@
{% endif %}
Paninis
- {% for produit in panini %}
+ {% for product in panini %}
{% if forloop.counter0|divisibleby:4 %}
{% endif %}
- {{produit.nom}}
+ {{product.nom}}
{% if forloop.counter|divisibleby:4 %}
{% endif %}
@@ -132,11 +132,11 @@
{% endif %}
Softs
- {% for produit in soft %}
+ {% for product in soft %}
{% if forloop.counter0|divisibleby:4 %}
{% endif %}
- {{produit.nom}}
+ {{product.nom}}
{% if forloop.counter|divisibleby:4 %}
{% endif %}
@@ -146,11 +146,11 @@
{% endif %}
Bouffe
- {% for produit in autreBouffe %}
+ {% for product in autreBouffe %}
{% if forloop.counter0|divisibleby:4 %}
{% endif %}
- {{produit.nom}}
+ {{product.nom}}
{% if forloop.counter|divisibleby:4 %}
{% endif %}
@@ -160,11 +160,11 @@
{% endif %}
{% if menus %}
Menus
- {% for produit in menus %}
+ {% for product in menus %}
{% if forloop.counter0|divisibleby:4 %}
{% endif %}
- {{produit.nom}}
+ {{product.nom}}
{% if forloop.counter|divisibleby:4 %}
{% endif %}
@@ -208,5 +208,7 @@
{% endif %}
{{gestion_form.media}}
+{{reload_form.media}}
+{{refund_form.media}}
{%endblock%}
diff --git a/gestion/templates/gestion/products_index.html b/gestion/templates/gestion/products_index.html
index 629dbe8..049d3cf 100644
--- a/gestion/templates/gestion/products_index.html
+++ b/gestion/templates/gestion/products_index.html
@@ -16,8 +16,8 @@
Actions possibles :
@@ -26,9 +26,10 @@
Actions possibles :
diff --git a/gestion/templates/gestion/products_list.html b/gestion/templates/gestion/products_list.html
index 0c26c7f..eb96d41 100644
--- a/gestion/templates/gestion/products_list.html
+++ b/gestion/templates/gestion/products_list.html
@@ -13,43 +13,40 @@
- Actions possibles :
-
-
-
-
- Actions possibles :
-
-
-
-
- Actions possibles :
-
-
-
-
- Actions possibles :
-
+ Créer un produit
+
+
+
+
+ Nom
+ Prix
+ Stock (soute)
+ Stock (bar)
+ Code barre
+ Catégorie
+ Actif
+ Degré
+ Volume
+ Administrer
+
+
+
+ {% for product in products %}
+
+ {{ product.name }}
+ {{ product.amount}}
+ {{ product.stockHold }}
+ {{ product.stockBar }}
+ {{ product.barcode }}
+ {{ product.category }}
+ {{ product.is_active }}
+ {{ product.degree }}
+ {{ product.volume }}
+
+
+ {% endfor %}
+
+
+
{% endblock %}
diff --git a/gestion/urls.py b/gestion/urls.py
index 3e0e17b..a400287 100644
--- a/gestion/urls.py
+++ b/gestion/urls.py
@@ -8,8 +8,15 @@ urlpatterns = [
path('reload', views.reload, name="reload"),
path('refund', views.refund, name="refund"),
path('productsIndex', views.productsIndex, name="productsIndex"),
+ path('productsList', views.productsList, name="productsList"),
path('addProduct', views.addProduct, name="addProduct"),
path('addKeg', views.addKeg, name="addKeg"),
path('addMenu', views.addMenu, name="addMenu"),
path('getProduct/', views.getProduct, name="getProduct"),
-]
+ path('order', views.order, name="order"),
+ path('ranking', views.ranking, name="ranking"),
+ path('annualRanking', views.annualRanking, name="annualRanking"),
+ path('searchProduct', views.searchProduct, name="searchProduct"),
+ path('productProfile/', views.productProfile, name="productProfile"),
+ path('products-autocomplete', views.ProductsAutocomplete.as_view(), name="products-autocomplete"),
+]
\ No newline at end of file
diff --git a/gestion/views.py b/gestion/views.py
index 59e8b6d..bd4d23b 100644
--- a/gestion/views.py
+++ b/gestion/views.py
@@ -1,16 +1,26 @@
-from django.shortcuts import render, redirect
+from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.urls import reverse
-from django.http import HttpResponse
+from django.http import HttpResponse, Http404
from django.contrib.auth.models import User
+from django.views.decorators.csrf import csrf_exempt
+from django.contrib.auth.decorators import login_required, permission_required
-import json
+from coopeV3.acl import active_required, acl_or
+
+import simplejson as json
from dal import autocomplete
+from decimal import *
-from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm
-from .models import Product, Menu, Keg
+from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm, SearchMenuForm, SearchProductForm
+from .models import Product, Menu, Keg, ConsumptionHistory
+from preferences.models import PaymentMethod
+@active_required
+@login_required
+@acl_or('gestion.add_consumptionhistory', 'gestion.add_reload', 'gestion.add_refund')
def manage(request):
+ pay_buttons = PaymentMethod.objects.filter(is_active=True)
gestion_form = GestionForm(request.POST or None)
reload_form = ReloadForm(request.POST or None)
refund_form = RefundForm(request.POST or None)
@@ -28,8 +38,37 @@ def manage(request):
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})
+ 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, "pay_buttons": pay_buttons})
+@login_required
+@permission_required('gestion.add_consumptionhistory')
+@csrf_exempt
+def order(request):
+ print(request.POST)
+ if("user" not in request.POST or "paymentMethod" not in request.POST or "amount" not in request.POST or "order" not in request.POST):
+ raise Http404("Erreur du POST")
+ else:
+ user = get_object_or_404(User, pk=request.POST['user'])
+ paymentMethod = get_object_or_404(PaymentMethod, pk=request.POST['paymentMethod'])
+ amount = Decimal(request.POST['amount'])
+ order = json.loads(request.POST["order"])
+ if(len(order) == 0 or amount == 0):
+ raise Http404("Pas de commande")
+ if(paymentMethod.affect_balance):
+ if(user.profile.balance < amount):
+ raise Http404("Solde inférieur au prix de la commande")
+ else:
+ user.profile.debit += amount
+ user.save()
+ for o in order:
+ print(o)
+ product = get_object_or_404(Product, pk=o["pk"])
+ ch = ConsumptionHistory(customer = user, quantity = int(o["quantity"]), paymentMethod=paymentMethod, product=product, amount=int(o["quantity"])*product.amount, coopeman=request.user)
+ ch.save()
+ return HttpResponse("La commande a bien été effectuée")
+
+@login_required
+@permission_required('gestion.add_reload')
def reload(request):
reload_form = ReloadForm(request.POST or None)
if(reload_form.is_valid()):
@@ -45,6 +84,8 @@ def reload(request):
messages.error(request, "Le rechargement a échoué")
return redirect(reverse('gestion:manage'))
+@login_required
+@permission_required('gestion.add_refund')
def refund(request):
refund_form = RefundForm(request.POST or None)
if(refund_form.is_valid()):
@@ -63,9 +104,15 @@ def refund(request):
messages.error(request, "Le remboursement a échoué")
return redirect(reverse('gestion:manage'))
+########## Products ##########
+
+@login_required
+@acl_or('gestion.add_product', 'gestion.view_product', 'gestion.add_keg', 'gestion.view_keg', 'gestion.change_keg', 'gestion.view_menu', 'gestion.add_menu')
def productsIndex(request):
return render(request, "gestion/products_index.html")
+@login_required
+@permission_required('gestion.add_product')
def addProduct(request):
form = ProductForm(request.POST or None)
if(form.is_valid()):
@@ -74,18 +121,43 @@ def addProduct(request):
return redirect(reverse('gestion:productsIndex'))
return render(request, "form.html", {"form": form, "form_title": "Ajout d'un produit", "form_button": "Ajouter"})
+@login_required
+@permission_required('gestion.view_product')
def productsList(request):
products = Product.objects.all()
return render(request, "gestion/products_list.html", {"products": products})
+@login_required
+@permission_required('gestion.view_product')
+def searchProduct(request):
+ form = SearchProductForm(request.POST or None)
+ if(form.is_valid()):
+ return redirect(reverse('gestion:productProfile', kwargs={'pk': form.cleaned_data['product'].pk }))
+ return render(request, "form.html", {"form": form, "form_title":"Rechercher un produit", "form_button": "Rechercher"})
+
+@login_required
+@permission_required('gestion.view_product')
+def productProfile(request, pk):
+ product = get_object_or_404(Product, pk=pk)
+ return render(request, "gestion/product_profile.html", {"product": product})
+
+@login_required
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)})
+ data = json.dumps({"pk": product.pk, "barcode" : product.barcode, "name": product.name, "amount" : product.amount})
return HttpResponse(data, content_type='application/json')
+class ProductsAutocomplete(autocomplete.Select2QuerySetView):
+ def get_queryset(self):
+ qs = Product.objects.all()
+ if self.q:
+ qs = qs.filter(name__istartswith=self.q)
+ return qs
########## Kegs ##########
+@login_required
+@permission_required('gestion.add_keg')
def addKeg(request):
form = KegForm(request.POST or None)
if(form.is_valid()):
@@ -97,6 +169,8 @@ def addKeg(request):
########## Menus ##########
+@login_required
+@permission_required('gestion.add_menu')
def addMenu(request):
form = MenuForm(request.POST or None)
extra_css = "#id_articles{height:200px;}"
@@ -106,3 +180,53 @@ def addMenu(request):
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})
+
+@login_required
+@permission_required('gestion.view_menu')
+def searchMenu(request):
+ """
+ Search a menu via SearchMenuForm instance
+
+ **Context**
+
+ ``form_entete``
+ The form title.
+
+ ``form``
+ The SearchMenuForm instance.
+
+ ``form_button``
+ The content of the form button.
+
+ **Template**
+
+ :template:`form.html`
+ """
+ form = SearchMenuForm(request.POST or None)
+ if(form.is_valid()):
+ menu = form.menu
+ return redirect(reverse('gestion:changeMenu', kwargs={'pk':menu.pk}))
+ return render(request, "form.html", {"form": form, "form_title": "Recherche d'un menu", "form_button": "Modifier"})
+
+class MenusAutocomplete(autocomplete.Select2QuerySetView):
+ def get_queryset(self):
+ qs = Menu.objects.all()
+ if self.q:
+ qs = qs.filter(name__istartswith=self.q)
+ return qs
+########## Ranking ##########
+
+@login_required
+def ranking(request):
+ bestBuyers = User.objects.order_by('-profile__debit')[:25]
+ customers = User.objects.all()
+ list = []
+ for customer in customers:
+ alcohol = customer.profile.alcohol
+ list.append([customer, alcohol])
+ bestDrinkers = sorted(list, key=lambda x: x[1], reverse=True)[:25]
+ return render(request, "gestion/ranking.html", {"bestBuyers": bestBuyers, "bestDrinkers": bestDrinkers})
+
+@login_required
+def annualRanking(request):
+ return render(request, "gestion/annual_ranking.html")
\ No newline at end of file
diff --git a/preferences/forms.py b/preferences/forms.py
index 6271cab..52a197e 100644
--- a/preferences/forms.py
+++ b/preferences/forms.py
@@ -1,4 +1,5 @@
from django import forms
+from django.core.exceptions import ValidationError
from .models import Cotisation, PaymentMethod, GeneralPreferences
@@ -6,6 +7,14 @@ class CotisationForm(forms.ModelForm):
class Meta:
model = Cotisation
fields = "__all__"
+
+ def clean_amount(self):
+ if self.cleaned_data['amount'] <= 0:
+ raise ValidationError(
+ "Le montant doit être strictement positif"
+ )
+ else:
+ return self.cleaned_data['amount']
class PaymentMethodForm(forms.ModelForm):
class Meta:
diff --git a/preferences/models.py b/preferences/models.py
index 0c5dbb7..4502f93 100644
--- a/preferences/models.py
+++ b/preferences/models.py
@@ -3,7 +3,8 @@ from django.db import models
class PaymentMethod(models.Model):
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")
+ is_usable_in_cotisation = models.BooleanField(default=True, verbose_name="Cotisations ?")
+ is_usable_in_reload = models.BooleanField(default=True, verbose_name="Rechargements ?")
affect_balance = models.BooleanField(default=False, verbose_name="Affecte le solde")
def __str__(self):
diff --git a/preferences/templates/preferences/payment_methods_index.html b/preferences/templates/preferences/payment_methods_index.html
index f5ef170..8bd9bcc 100644
--- a/preferences/templates/preferences/payment_methods_index.html
+++ b/preferences/templates/preferences/payment_methods_index.html
@@ -17,7 +17,8 @@
Nom
Actif ?
- Utilisable dans les cotisations
+ Cotisations ?
+ Rechargements ?
Affecte le solde
Administration
@@ -28,6 +29,7 @@
{{ pm.name }}
{{ pm.is_active | yesno:"Oui, Non"}}
{{ pm.is_usable_in_cotisation | yesno:"Oui, Non" }}
+ {{ pm.is_usable_in_reload | yesno:"Oui, Non" }}
{{ pm.affect_balance | yesno:"Oui, Non" }}
Modifier Supprimer
diff --git a/preferences/views.py b/preferences/views.py
index c16aa75..58ff90b 100644
--- a/preferences/views.py
+++ b/preferences/views.py
@@ -1,11 +1,17 @@
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.urls import reverse
+from django.contrib.auth.decorators import login_required, permission_required
+
+from coopeV3.acl import active_required
from .models import GeneralPreferences, Cotisation, PaymentMethod
from .forms import CotisationForm, PaymentMethodForm, GeneralPreferencesForm
+@active_required
+@login_required
+@permission_required('preferences.add_generalpreferences')
def generalPreferences(request):
gp,_ = GeneralPreferences.objects.get_or_create(pk=1)
form = GeneralPreferencesForm(request.POST or None, instance=gp)
@@ -15,10 +21,16 @@ def generalPreferences(request):
########## Cotisations ##########
+@active_required
+@login_required
+@permission_required('preferences.view_cotisation')
def cotisationsIndex(request):
cotisations = Cotisation.objects.all()
return render(request, "preferences/cotisations_index.html", {"cotisations": cotisations})
+@active_required
+@login_required
+@permission_required('preferences.add_cotisation')
def addCotisation(request):
form = CotisationForm(request.POST or None)
if(form.is_valid()):
@@ -27,6 +39,9 @@ def addCotisation(request):
return redirect(reverse('preferences:cotisationsIndex'))
return render(request, "form.html", {"form": form, "form_title": "Création d'une cotisation", "form_button": "Créer"})
+@active_required
+@login_required
+@permission_required('preferences.change_cotisation')
def editCotisation(request, pk):
cotisation = get_object_or_404(Cotisation, pk=pk)
form = CotisationForm(request.POST or None, instance=cotisation)
@@ -36,6 +51,9 @@ def editCotisation(request, pk):
return redirect(reverse('preferences:cotisationsIndex'))
return render(request, "form.html", {"form": form, "form_title": "Modification d'une cotisation", "form_button": "Modifier"})
+@active_required
+@login_required
+@permission_required('preferences.delete_cotisation')
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"
@@ -46,10 +64,16 @@ def deleteCotisation(request,pk):
########## Payment Methods ##########
+@active_required
+@login_required
+@permission_required('preferences.view_paymentmethod')
def paymentMethodsIndex(request):
paymentMethods = PaymentMethod.objects.all()
return render(request, "preferences/payment_methods_index.html", {"paymentMethods": paymentMethods})
+@active_required
+@login_required
+@permission_required('preferences.add_paymentmethod')
def addPaymentMethod(request):
form = PaymentMethodForm(request.POST or None)
if(form.is_valid()):
@@ -58,6 +82,9 @@ def addPaymentMethod(request):
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"})
+@active_required
+@login_required
+@permission_required('preferences.change_paymentmethod')
def editPaymentMethod(request, pk):
paymentMethod = get_object_or_404(PaymentMethod, pk=pk)
form = PaymentMethodForm(request.POST or None, instance=paymentMethod)
@@ -67,9 +94,12 @@ def editPaymentMethod(request, pk):
return redirect(reverse('preferences:paymentMethodsIndex'))
return render(request, "form.html", {"form": form, "form_title": "Modification d'un moyen de paiement", "form_button": "Modifier"})
+@active_required
+@login_required
+@permission_required('preferences.delete_paymentmethod')
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'))
+ return redirect(reverse('preferences:paymentMethodsIndex'))
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 226d374..46ba27e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,5 @@
Django==2.1
django-autocomplete-light==3.3.2
pytz==2018.5
+simplejson==3.16.0
+docutils==0.14
\ No newline at end of file
diff --git a/staticfiles/manage.js b/staticfiles/manage.js
index dbd3ce3..5d1c896 100644
--- a/staticfiles/manage.js
+++ b/staticfiles/manage.js
@@ -1,4 +1,4 @@
-totalAmount = 0
+total = 0
products = []
paymentMethod = null
balance = 0
@@ -64,4 +64,16 @@ $(document).ready(function(){
window.location.reload()
});
});
+ $(".pay_button").click(function(){
+ alert('Tentative de paiment avec le moyen ' + $(this).attr('data-payment'));
+ console.log(products)
+ $.post("order", {"user":id, "paymentMethod": $(this).attr('data-payment'), "order_length": products.length, "order": JSON.stringify(products), "amount": total}, function(data){
+ alert(data);
+ location.reload();
+ }).fail(function(data){
+ alert("Impossible d'effectuer la transaction");
+ location.reload();
+ });
+
+ });
});
diff --git a/templates/footer.html b/templates/footer.html
index dbaf833..64d1593 100644
--- a/templates/footer.html
+++ b/templates/footer.html
@@ -1,12 +1,7 @@
{% load vip %}
A propos
- {% lorem %}
-
+ L'association Coopé Technopole Metz est une association de droit local dont le siège social est établi à la résidence Edouard Branly. Son but est d'entretenir un lieu convivial sous la forme d'un bar associatif. Les membres actifs sont les coopemen (ou coopewomen).
Contacts
diff --git a/templates/nav.html b/templates/nav.html
index 136250a..f982a0b 100644
--- a/templates/nav.html
+++ b/templates/nav.html
@@ -12,13 +12,13 @@
Gestion des produits
- Comptabilité
+ Comptabilité
- Classement
+ Classement
- Classement sur l'année
+ Classement sur l'année
Admin
diff --git a/users/admin.py b/users/admin.py
index 67e5fc6..8547939 100644
--- a/users/admin.py
+++ b/users/admin.py
@@ -1,8 +1,9 @@
from django.contrib import admin
+from django.contrib.auth.models import Permission
from .models import School, Profile, CotisationHistory
+admin.site.register(Permission)
admin.site.register(School)
admin.site.register(Profile)
-admin.site.register(CotisationHistory)
-# Register your models here.
+admin.site.register(CotisationHistory)
\ No newline at end of file
diff --git a/users/forms.py b/users/forms.py
index 2fb2a44..a8f0fd7 100644
--- a/users/forms.py
+++ b/users/forms.py
@@ -25,14 +25,13 @@ class EditGroupForm(forms.ModelForm):
fields = "__all__"
class SelectUserForm(forms.Form):
- user = forms.ModelChoiceField(queryset=User.objects.all(), label="Utilisateur")
+ user = forms.ModelChoiceField(queryset=User.objects.all(), required=True, label="Utilisateur", widget=autocomplete.ModelSelect2(url='users:all-users-autocomplete', attrs={'data-minimum-input-length':2}))
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}))
-
+ user = forms.ModelChoiceField(queryset=User.objects.filter(is_active=True), required=True, label="Utilisateur", widget=autocomplete.ModelSelect2(url='users:non-admin-users-autocomplete', attrs={'data-minimum-input-length':2}))
class GroupsEditForm(forms.ModelForm):
class Meta:
diff --git a/users/models.py b/users/models.py
index 1508f7d..58e87d4 100644
--- a/users/models.py
+++ b/users/models.py
@@ -4,6 +4,7 @@ from django.db.models.signals import post_save
from django.dispatch import receiver
from preferences.models import PaymentMethod, Cotisation
+from gestion.models import ConsumptionHistory
class School(models.Model):
name = models.CharField(max_length=255, verbose_name="Nom")
@@ -12,6 +13,11 @@ class School(models.Model):
return self.name
class CotisationHistory(models.Model):
+ class Meta:
+ permissions = (
+ ("validate_consumptionhistory", "Peut (in)valider les cotisations"),
+ )
+
WAITING = 0
VALID = 1
INVALID = 2
@@ -57,12 +63,12 @@ class Profile(models.Model):
@property
def alcohol(self):
- #consos = Consommation.objects.filter(client=self).select_related('produit')
- #alcool = 0
- #for conso in consos:
- #produit = conso.produit
- #alcool += conso.nombre * float(produit.deg) * produit.volume * 0.79 /10 /1000
- return 0
+ consumptions = ConsumptionHistory.objects.filter(customer=self.user).select_related('product')
+ alcohol = 0
+ for consumption in consumptions:
+ product = consumption.product
+ alcohol += consumption.quantity * float(product.deg) * product.volume * 0.79 /10 /1000
+ return alcohol
def __str__(self):
return str(self.user)
@@ -74,4 +80,9 @@ def create_user_profile(sender, instance, created, **kwargs):
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
- instance.profile.save()
\ No newline at end of file
+ instance.profile.save()
+
+def str_user(self):
+ return self.username + " (" + self.first_name + " " + self.last_name + ", " + str(self.profile.balance) + "€)"
+
+User.add_to_class("__str__", str_user)
\ No newline at end of file
diff --git a/users/templates/users/groups_index.html b/users/templates/users/groups_index.html
index b5a4f23..c9feac0 100644
--- a/users/templates/users/groups_index.html
+++ b/users/templates/users/groups_index.html
@@ -10,6 +10,7 @@
Liste des groupes de droit
+ Ajouter un groupe de droit
diff --git a/users/templates/users/profile.html b/users/templates/users/profile.html
index 0e68c56..2ea0534 100644
--- a/users/templates/users/profile.html
+++ b/users/templates/users/profile.html
@@ -130,13 +130,13 @@
- {%for reload in lastReloads%}
+ {% for reload in reloads %}
- {{reload.amount}}
- {{reload.paymentMethod}}
+ {{reload.amount}}€
+ {{reload.PaymentMethod}}
{{reload.date}}
- {%endfor%}
+ {% endfor %}
@@ -208,11 +208,4 @@
-
-
-
-{%endblock%}
-{%block addScript %}
-
-
-{%endblock%}
+{%endblock%}
\ No newline at end of file
diff --git a/users/urls.py b/users/urls.py
index 12a2dbc..8637409 100644
--- a/users/urls.py
+++ b/users/urls.py
@@ -30,6 +30,7 @@ urlpatterns = [
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('non-admin-users-autocomplete', views.NonAdminUserAutocomplete.as_view(), name="non-admin-users-autocomplete"),
path('getUser/', views.getUser, name="getUser"),
path('addCotisationHistory/', views.addCotisationHistory, name="addCotisationHistory"),
path('validateCotisationHistory/', views.validateCotisationHistory, name="validateCotisationHistory"),
@@ -39,4 +40,5 @@ urlpatterns = [
path('createSchool', views.createSchool, name="createSchool"),
path('editSchool/', views.editSchool, name="editSchool"),
path('deleteSchool/', views.deleteSchool, name="deleteSchool"),
+ path('allReloads//', views.allReloads, name="allReloads"),
]
diff --git a/users/views.py b/users/views.py
index d01cce8..2d36ee5 100644
--- a/users/views.py
+++ b/users/views.py
@@ -5,16 +5,38 @@ 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 django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
+from django.contrib.auth.decorators import login_required, permission_required
-import json
+import simplejson as json
from datetime import datetime, timedelta
-
from dal import autocomplete
+from coopeV3.acl import admin_required, superuser_required, self_or_has_perm, active_required
from .models import CotisationHistory, WhiteListHistory, School
from .forms import CreateUserForm, LoginForm, CreateGroupForm, EditGroupForm, SelectUserForm, GroupsEditForm, EditPasswordForm, addCotisationHistoryForm, addCotisationHistoryForm, addWhiteListHistoryForm, SelectNonAdminUserForm, SelectNonSuperUserForm, SchoolForm
+from gestion.models import Reload
+@active_required
def loginView(request):
+ """
+ Display the login form for :model:`User`.
+
+ **Context**
+
+ ``form_entete``
+ Title of the form.
+
+ ``form``
+ The login form.
+
+ ``form_button``
+ Content of the form button.
+
+ **Template**
+
+ :template:`form.html`
+ """
form = LoginForm(request.POST or None)
if(form.is_valid()):
user = authenticate(username=form.cleaned_data['username'], password=form.cleaned_data['password'])
@@ -29,26 +51,91 @@ def loginView(request):
messages.error(request, "Nom d'utilisateur et/ou mot de passe invalide")
return render(request, "form.html", {"form_entete": "Connexion", "form": form, "form_title": "Connexion", "form_button": "Se connecter"})
+@active_required
+@login_required
def logoutView(request):
+ """
+ Logout the logged user
+ """
logout(request)
messages.success(request, "Vous êtes à présent déconnecté")
return redirect(reverse('home'))
+@active_required
+@login_required
+@permission_required('auth.view_user')
def index(request):
- return render(request, "users/index.html")
+ """
+ Display the index for user related actions
-########## schools ##########
+ **Template**
+
+ :template:`users/index.html`
+ """
+ return render(request, "users/index.html")
########## users ##########
+@active_required
+@login_required
+@self_or_has_perm('pk', 'auth.view_user')
def profile(request, pk):
+ """
+ Display the profile for the requested user
+
+ ``pk``
+ The primary key for user
+
+ **Context**
+
+ ``user``
+ The instance of User
+
+ ``self``
+ Boolean value wich indicates if the current logged user and the request user are the same
+
+ ``cotisations``
+ List of the user's cotisations
+
+ ``whitelists``
+ List of the user's whitelists
+
+ ``reloads``
+ List of the last 5 reloads of the user
+
+ **Template**
+
+ :template:`users/profile.html`
+ """
user = get_object_or_404(User, pk=pk)
self = request.user == user
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})
+ reloads = Reload.objects.filter(customer=user).order_by('-date')
+ return render(request, "users/profile.html", {"user":user, "self":self, "cotisations":cotisations, "whitelists": whitelists, "reloads": reloads})
+@active_required
+@login_required
+@permission_required('auth.add_user')
def createUser(request):
+ """
+ Display a CreateUserForm instance.
+
+ **Context**
+
+ ``form_entete``
+ The form title.
+
+ ``form``
+ The CreateUserForm instance.
+
+ ``form_button``
+ The content of the form button.
+
+ **Template**
+
+ :template:`form.html`
+ """
form = CreateUserForm(request.POST or None)
if(form.is_valid()):
user = form.save(commit=False)
@@ -58,17 +145,77 @@ 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"})
+@active_required
+@login_required
+@permission_required('auth.view_user')
def searchUser(request):
+ """
+ Display a simple searchForm for User.
+
+ **Context**
+
+ ``form_entete``
+ The form title.
+
+ ``form``
+ The searchForm instance.
+
+ ``form_button``
+ The content of the form button.
+
+ **Template**
+
+ :template:`form.html`
+ """
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"})
+@active_required
+@login_required
+@permission_required('auth.view_user')
def usersIndex(request):
+ """
+ Display the list of all users.
+
+ **Context**
+
+ ``users``
+ The list of all users
+
+ **Template**
+
+ :template:`users/users_index.html`
+ """
users = User.objects.all()
return render(request, "users/users_index.html", {"users":users})
+@active_required
+@login_required
+@permission_required('auth.change_user')
def editGroups(request, pk):
+ """
+ Edit the groups of a user.
+
+ ``pk``
+ The pk of the user.
+
+ **Context**
+
+ ``form_entete``
+ The form title.
+
+ ``form``
+ The GroupsEditForm instance.
+
+ ``form_button``
+ The content of the form button.
+
+ **Template**
+
+ :template:`form.html`
+ """
user = get_object_or_404(User, pk=pk)
form = GroupsEditForm(request.POST or None, instance=user)
if(form.is_valid()):
@@ -78,7 +225,30 @@ def editGroups(request, 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})
+@active_required
+@login_required
+@permission_required('auth.change_user')
def editPassword(request, pk):
+ """
+ Change the password of a user.
+
+ ``pk``
+ The pk of the user.
+
+ **Context**
+ ``form_entete``
+ The form title.
+
+ ``form``
+ The EditPasswordForm instance.
+
+ ``form_button``
+ The content of the form button.
+
+ **Template**
+
+ :template:`form.html`
+ """
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")
@@ -95,7 +265,31 @@ def editPassword(request, pk):
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"})
+@active_required
+@login_required
+@permission_required('auth.change_user')
def editUser(request, pk):
+ """
+ Edit a user and user profile
+
+ ``pk``
+ The pk of the user.
+
+ **Context**
+
+ ``form_entete``
+ The form title.
+
+ ``form``
+ The CreateUserForm instance.
+
+ ``form_button``
+ The content of the form button.
+
+ **Template**
+
+ :template:`form.html`
+ """
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()):
@@ -105,7 +299,17 @@ def editUser(request, pk):
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"})
+@active_required
+@login_required
+@permission_required('auth.change_user')
def resetPassword(request, pk):
+ """
+ Reset the password of a user.
+
+ ``pk``
+ The pk of the user
+
+ """
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.")
@@ -116,22 +320,114 @@ def resetPassword(request, pk):
messages.success(request, "Le mot de passe de " + user.username + " a bien été réinitialisé.")
return redirect(reverse('users:profile', kwargs={'pk': pk}))
+@active_required
+@login_required
+@permission_required('auth.view_user')
def getUser(request, pk):
+ """
+ Return username and balance of the requested user (pk)
+
+ ``pk``
+ The pk of the user
+ """
user = get_object_or_404(User, pk=pk)
- data = json.dumps({"username": user.username, "balance": float(user.profile.balance)})
+ data = json.dumps({"username": user.username, "balance": user.profile.balance})
return HttpResponse(data, content_type='application/json')
+@active_required
+@login_required
+@self_or_has_perm('pk', 'auth.view_user')
+def allReloads(request, pk, page):
+ """
+ Display all the reloads of the requested user.
+
+ ``pk``
+ The pk of the user.
+ ``page``
+ The page number.
+
+ **Context**
+
+ ``reloads``
+ The reloads of the page.
+ ``user``
+ The requested user
+
+ **Template**
+
+ :template:`users/allReloads.html`
+ """
+ user = get_object_or_404(User, pk=pk)
+ allReloads = Reload.objects.filter(customer=user).order_by('-date')
+ paginator = Paginator(allReloads, 2)
+ reloads = paginator.get_page(page)
+ return render(request, "users/allReloads.html", {"reloads": reloads, "user":user})
+
########## Groups ##########
+@active_required
+@login_required
+@permission_required('auth.view_group')
def groupsIndex(request):
+ """
+ Display all the groups.
+
+ **Context**
+
+ ``groups``
+ List of all groups.
+
+ **Template**
+
+ :template:`users/groups_index.html`
+ """
groups = Group.objects.all()
return render(request, "users/groups_index.html", {"groups": groups})
+@active_required
+@login_required
+@permission_required('auth.view_group')
def groupProfile(request, pk):
+ """
+ Display the profile of a group.
+
+ ``pk``
+ The pk of the group.
+
+ **Context**
+
+ ``group``
+ The requested group.
+
+ **Template**
+
+ :template:`users/group_profile.html`
+ """
group = get_object_or_404(Group, pk=pk)
return render(request, "users/group_profile.html", {"group": group})
+@active_required
+@login_required
+@permission_required('auth.add_group')
def createGroup(request):
+ """
+ Create a group with a CreateGroupForm instance.
+
+ **Context**
+
+ ``form_entete``
+ The form title.
+
+ ``form``
+ The CreateGroupForm instance.
+
+ ``form_button``
+ The content of the form button.
+
+ **Template**
+
+ :template:`form.html`
+ """
form = CreateGroupForm(request.POST or None)
if(form.is_valid()):
group = form.save()
@@ -139,7 +435,31 @@ def createGroup(request):
return redirect(reverse('users:groupProfile', kwargs={'pk': group.pk}))
return render(request, "form.html", {"form_entete": "Gestion des utilisateurs", "form":form, "form_title": "Création d'un groupe de droit", "form_button": "Créer le groupe de droit"})
+@active_required
+@login_required
+@permission_required('auth.change_group')
def editGroup(request, pk):
+ """
+ Edit a group with a EditGroupForm instance.
+
+ ``pk``
+ The pk of the group.
+
+ **Context**
+
+ ``form_entete``
+ The form title.
+
+ ``form``
+ The EditGroupForm instance.
+
+ ``form_button``
+ The content of the form button.
+
+ **Template**
+
+ :template:`form.html`
+ """
group = get_object_or_404(Group, pk=pk)
form = EditGroupForm(request.POST or None, instance=group)
extra_css = "#id_permissions{height:200px;}"
@@ -149,7 +469,17 @@ def editGroup(request, pk):
return redirect(reverse('users:groupProfile', kwargs={'pk': group.pk}))
return render(request, "form.html", {"form_entete": "Gestion des utilisateurs", "form": form, "form_title": "Modification du groupe de droit " + group.name, "form_button": "Modifier le groupe de droit", "extra_css":extra_css})
+@active_required
+@login_required
+@permission_required('auth.delete_group')
def deleteGroup(request, pk):
+ """
+ Delete the requested group.
+
+ ``pk``
+ The pk of the group
+
+ """
group = get_object_or_404(Group, pk=pk)
if group.user_set.count() == 0:
name = group.name
@@ -160,7 +490,20 @@ def deleteGroup(request, pk):
messages.error(request, "Impossible de supprimer le groupe " + group.name + " : il y a encore des utilisateurs")
return redirect(reverse('users:groupProfile', kwargs={'pk': group.pk}))
+@active_required
+@login_required
+@permission_required('auth.change_group')
def removeRight(request, groupPk, permissionPk):
+ """
+ Remove a right from a given group.
+
+ ``groupPk``
+ The pk of the group.
+
+ ``permissionPk``
+ The pk of the right.
+
+ """
group = get_object_or_404(Group, pk=groupPk)
perm = get_object_or_404(Permission, pk=permissionPk)
if perm in group.permissions.all():
@@ -170,7 +513,20 @@ def removeRight(request, groupPk, permissionPk):
messages.error(request, "Impossible de retirer la permission " + perm.codename + " du groupe " + group.name)
return redirect(reverse('users:groupProfile', kwargs={'pk': groupPk}) + "#second")
+@active_required
+@login_required
+@permission_required('auth.change_user')
def removeUser(request, groupPk, userPk):
+ """
+ Remove a user from a given group.
+
+ ``groupPk``
+ The pk of the group.
+
+ ``userPk``
+ The pk of the user.
+
+ """
group = get_object_or_404(Group, pk=groupPk)
user = get_object_or_404(User, pk=userPk)
if(group in user.groups.all()):
@@ -182,10 +538,16 @@ def removeUser(request, groupPk, userPk):
########## admins ##########
+@active_required
+@login_required
+@admin_required
def adminsIndex(request):
admins = User.objects.filter(is_staff=True)
return render(request, "users/admins_index.html", {"admins": admins})
+@active_required
+@login_required
+@admin_required
def addAdmin(request):
form = SelectNonAdminUserForm(request.POST or None)
if(form.is_valid()):
@@ -196,6 +558,9 @@ def addAdmin(request):
return redirect(reverse('users:adminsIndex'))
return render(request, "form.html", {"form_entete": "Gestion des admins", "form": form, "form_title": "Ajout d'un admin", "form_button":"Ajouter l'utilisateur aux admins"})
+@active_required
+@login_required
+@admin_required
def removeAdmin(request, pk):
user = get_object_or_404(User, pk=pk)
if user.is_staff:
@@ -214,10 +579,16 @@ def removeAdmin(request, pk):
########## superusers ##########
+@active_required
+@login_required
+@superuser_required
def superusersIndex(request):
superusers = User.objects.filter(is_superuser=True)
return render(request, "users/superusers_index.html", {"superusers": superusers})
+@active_required
+@login_required
+@superuser_required
def addSuperuser(request):
form = SelectNonSuperUserForm(request.POST or None)
if(form.is_valid()):
@@ -229,6 +600,9 @@ def addSuperuser(request):
return redirect(reverse('users:superusersIndex'))
return render(request, "form.html", {"form_entete": "Gestion des superusers", "form": form, "form_title": "Ajout d'un superuser", "form_button":"Ajouter l'utilisateur aux superusers"})
+@active_required
+@login_required
+@superuser_required
def removeSuperuser(request, pk):
user = get_object_or_404(User, pk=pk)
if user.is_superuser:
@@ -244,11 +618,20 @@ def removeSuperuser(request, pk):
########## Cotisations ##########
+@active_required
+@login_required
+@permission_required('users.add_cotisationhistory')
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)
+ if(cotisation.paymentMethod.affect_balance):
+ if(user.profile.balance >= cotisation.amount):
+ user.profile.balance -= cotisation.amount
+ else:
+ cotisation.delete()
+ messages.error(request, "Solde insuffisant")
cotisation.user = user
cotisation.coopeman = request.user
cotisation.amount = cotisation.cotisation.amount
@@ -264,6 +647,9 @@ def addCotisationHistory(request, pk):
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"})
+@active_required
+@login_required
+@permission_required('users.validate_consumptionhistory')
def validateCotisationHistory(request, pk):
cotisationHistory = get_object_or_404(CotisationHistory, pk=pk)
cotisationHistory.valid = CotisationHistory.VALID
@@ -271,18 +657,26 @@ def validateCotisationHistory(request, pk):
messages.success(request, "La cotisation a bien été validée")
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+@active_required
+@login_required
+@permission_required('users.validate_consumptionhistory')
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)
+ if(cotisationHistory.paymentMethod.affect_balance):
+ user.profile.balance += cotisation.amount
user.save()
messages.success(request, "La cotisation a bien été invalidée")
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
########## Whitelist ##########
+@active_required
+@login_required
+@permission_required('users.add_whitelisthistory')
def addWhiteListHistory(request, pk):
user = get_object_or_404(User, pk=pk)
form = addWhiteListHistoryForm(request.POST or None)
@@ -303,10 +697,16 @@ def addWhiteListHistory(request, pk):
########## Schools ##########
+@active_required
+@login_required
+@permission_required('users.view_school')
def schoolsIndex(request):
schools = School.objects.all()
return render(request, "users/schools_index.html", {"schools": schools})
+@active_required
+@login_required
+@permission_required('users.add_school')
def createSchool(request):
form = SchoolForm(request.POST or None)
if(form.is_valid()):
@@ -315,6 +715,9 @@ def createSchool(request):
return redirect(reverse('users:schoolsIndex'))
return render(request, "form.html", {"form": form, "form_title": "Création d'une école", "form_button": "Créer"})
+@active_required
+@login_required
+@permission_required('users.change_school')
def editSchool(request, pk):
school = get_object_or_404(School, pk=pk)
form = SchoolForm(request.POST or None, instance=school)
@@ -324,6 +727,9 @@ def editSchool(request, pk):
return redirect(reverse('users:schoolsIndex'))
return render(request, "form.html", {"form": form, "form_title": "Modification de l'école " + str(school), "form_button": "Modifier"})
+@active_required
+@login_required
+@permission_required('users.delete_school')
def deleteSchool(request, pk):
school = get_object_or_404(School, pk=pk)
message = "L'école " + str(school) + " a bien été supprimée"
@@ -355,6 +761,13 @@ class AdherentAutocomplete(autocomplete.Select2QuerySetView):
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
+
+class NonAdminUserAutocomplete(autocomplete.Select2QuerySetView):
+ def get_queryset(self):
+ qs = User.objects.filter(is_staff=False)
if self.q:
qs = qs.filter(Q(username__istartswith=self.q) | Q(first_name__istartswith=self.q) | Q(last_name__istartswith=self.q))
return qs
\ No newline at end of file