From 74b628d8317b4b5099b4ee2b8e659c1c1459edd8 Mon Sep 17 00:00:00 2001 From: nanoy Date: Sun, 2 Dec 2018 16:28:40 +0100 Subject: [PATCH] Docs, permissions --- coopeV3/settings.py | 1 + gestion/migrations/0009_auto_20181202_1628.py | 87 ++++ gestion/models.py | 60 ++- gestion/templates/gestion/annual_ranking.html | 24 - gestion/templates/gestion/manage.html | 1 + gestion/urls.py | 1 - gestion/views.py | 461 ++++++++++++++++-- preferences/forms.py | 9 + preferences/models.py | 9 + preferences/views.py | 130 ++++- templates/nav.html | 3 - users/forms.py | 39 ++ users/migrations/0005_auto_20181202_1628.py | 45 ++ users/models.py | 52 +- users/views.py | 208 +++++++- 15 files changed, 1052 insertions(+), 78 deletions(-) create mode 100644 gestion/migrations/0009_auto_20181202_1628.py delete mode 100644 gestion/templates/gestion/annual_ranking.html create mode 100644 users/migrations/0005_auto_20181202_1628.py diff --git a/coopeV3/settings.py b/coopeV3/settings.py index 62250b1..30f5d45 100644 --- a/coopeV3/settings.py +++ b/coopeV3/settings.py @@ -56,6 +56,7 @@ MIDDLEWARE = [ 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'simple_history.middleware.HistoryRequestMiddleware', + 'django.contrib.admindocs.middleware.XViewMiddleware', ] ROOT_URLCONF = 'coopeV3.urls' diff --git a/gestion/migrations/0009_auto_20181202_1628.py b/gestion/migrations/0009_auto_20181202_1628.py new file mode 100644 index 0000000..f4ef280 --- /dev/null +++ b/gestion/migrations/0009_auto_20181202_1628.py @@ -0,0 +1,87 @@ +# Generated by Django 2.1 on 2018-12-02 15:28 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('gestion', '0008_auto_20181130_1904'), + ] + + operations = [ + migrations.RemoveField( + model_name='historicalstocking', + name='history_user', + ), + migrations.DeleteModel( + name='Stocking', + ), + migrations.AlterModelOptions( + name='consumption', + options={'verbose_name': 'Consommation totale'}, + ), + migrations.AlterModelOptions( + name='consumptionhistory', + options={'verbose_name': 'Consommation'}, + ), + migrations.AlterModelOptions( + name='historicalconsumption', + options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Consommation totale'}, + ), + migrations.AlterModelOptions( + name='historicalconsumptionhistory', + options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Consommation'}, + ), + migrations.AlterModelOptions( + name='historicalkeg', + options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Fût'}, + ), + migrations.AlterModelOptions( + name='historicalkeghistory', + options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Historique de fût'}, + ), + migrations.AlterModelOptions( + name='historicalmenuhistory', + options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Historique de menu'}, + ), + migrations.AlterModelOptions( + name='historicalproduct', + options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Produit'}, + ), + migrations.AlterModelOptions( + name='historicalrefund', + options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Remboursement'}, + ), + migrations.AlterModelOptions( + name='historicalreload', + options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Rechargement'}, + ), + migrations.AlterModelOptions( + name='keg', + options={'permissions': (('open_keg', 'Peut percuter les fûts'), ('close_keg', 'Peut fermer les fûts')), 'verbose_name': 'Fût'}, + ), + migrations.AlterModelOptions( + name='keghistory', + options={'verbose_name': 'Historique de fût'}, + ), + migrations.AlterModelOptions( + name='menuhistory', + options={'verbose_name': 'Historique de menu'}, + ), + migrations.AlterModelOptions( + name='product', + options={'verbose_name': 'Produit'}, + ), + migrations.AlterModelOptions( + name='refund', + options={'verbose_name': 'Remboursement'}, + ), + migrations.AlterModelOptions( + name='reload', + options={'verbose_name': 'Rechargement'}, + ), + migrations.DeleteModel( + name='HistoricalStocking', + ), + ] diff --git a/gestion/models.py b/gestion/models.py index a553bde..b83ddef 100644 --- a/gestion/models.py +++ b/gestion/models.py @@ -1,12 +1,15 @@ from django.db import models from django.contrib.auth.models import User -from preferences.models import PaymentMethod -from django.core.exceptions import ValidationError from simple_history.models import HistoricalRecords from django.core.validators import MinValueValidator +from preferences.models import PaymentMethod +from django.core.exceptions import ValidationError class Product(models.Model): + """ + Stores a product + """ P_PRESSION = 'PP' D_PRESSION = 'DP' G_PRESSION = 'GP' @@ -23,6 +26,8 @@ class Product(models.Model): (FOOD, "Bouffe autre que panini"), (PANINI, "Bouffe pour panini"), ) + class Meta: + verbose_name = "Produit" 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", validators=[MinValueValidator(0)]) stockHold = models.IntegerField(default=0, verbose_name="Stock en soute") @@ -66,7 +71,11 @@ def isGalopin(id): ) class Keg(models.Model): + """ + Stores a keg + """ class Meta: + verbose_name = "Fût" permissions = ( ("open_keg", "Peut percuter les fûts"), ("close_keg", "Peut fermer les fûts") @@ -87,6 +96,12 @@ class Keg(models.Model): return self.name class KegHistory(models.Model): + """ + Stores a keg history, related to :model:`gestion.Keg` + """ + class Meta: + verbose_name = "Historique de fût" + keg = models.ForeignKey(Keg, on_delete=models.PROTECT) openingDate = models.DateTimeField(auto_now_add=True) quantitySold = models.DecimalField(decimal_places=2, max_digits=5, default=0) @@ -104,6 +119,12 @@ class KegHistory(models.Model): return res class Reload(models.Model): + """ + Stores reloads + """ + class Meta: + verbose_name = "Rechargement" + 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", validators=[MinValueValidator(0)]) PaymentMethod = models.ForeignKey(PaymentMethod, on_delete=models.PROTECT, verbose_name="Moyen de paiement") @@ -124,15 +145,13 @@ class Raming(models.Model): def __str__(self): 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) - history = HistoricalRecords() - - def __str__(self): - return "Inventaire fait le {0}".format(self.date) - - class Refund(models.Model): + """ + Stores refunds + """ + class Meta: + verbose_name = "Remboursement" + date = models.DateTimeField(auto_now_add=True) 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", validators=[MinValueValidator(0)]) @@ -144,6 +163,9 @@ class Refund(models.Model): class Menu(models.Model): + """ + Stores menus + """ name = models.CharField(max_length=255, verbose_name="Nom") amount = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Montant", validators=[MinValueValidator(0)]) barcode = models.CharField(max_length=20, unique=True, verbose_name="Code barre") @@ -162,6 +184,12 @@ class Menu(models.Model): return res class MenuHistory(models.Model): + """ + Stores MenuHistory related to :model:`gestion.Menu` + """ + class Meta: + verbose_name = "Historique de menu" + customer = models.ForeignKey(User, on_delete=models.PROTECT, related_name="menu_taken") quantity = models.PositiveIntegerField(default=0) paymentMethod = models.ForeignKey(PaymentMethod, on_delete=models.PROTECT) @@ -175,6 +203,12 @@ class MenuHistory(models.Model): return "{2} a consommé {0} {1}".format(self.quantity, self.menu, self.customer) class ConsumptionHistory(models.Model): + """ + Stores consumption history related to :model:`gestion.Product` + """ + class Meta: + verbose_name = "Consommation" + customer = models.ForeignKey(User, on_delete=models.PROTECT, related_name="consumption_taken") quantity = models.PositiveIntegerField(default=0) paymentMethod = models.ForeignKey(PaymentMethod, on_delete=models.PROTECT) @@ -188,6 +222,12 @@ class ConsumptionHistory(models.Model): return "{0} {1} consommé par {2} le {3} (encaissé par {4})".format(self.quantity, self.product, self.customer, self.date, self.coopeman) class Consumption(models.Model): + """ + Stores total consumptions + """ + class Meta: + verbose_name = "Consommation totale" + customer = models.ForeignKey(User, on_delete=models.PROTECT, related_name="consumption_global_taken") product = models.ForeignKey(Product, on_delete=models.PROTECT) quantity = models.PositiveIntegerField(default=0) diff --git a/gestion/templates/gestion/annual_ranking.html b/gestion/templates/gestion/annual_ranking.html deleted file mode 100644 index e3ea586..0000000 --- a/gestion/templates/gestion/annual_ranking.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "base.html" %} -{%load static %} -{%block entete%}

Classement

{%endblock%} -{% block nav %} - -{% endblock %} -{% block content %} -
-
-
-
-

Dezo pas dezo

-
-
-
- Dû à des problèmes techniques, cet onglet n'est actuellement pas disponible. -
-
-
-
-
-{%endblock%} \ No newline at end of file diff --git a/gestion/templates/gestion/manage.html b/gestion/templates/gestion/manage.html index 3b107d6..b329d2a 100644 --- a/gestion/templates/gestion/manage.html +++ b/gestion/templates/gestion/manage.html @@ -39,6 +39,7 @@
+ Annuler

{{gestion_form}}
diff --git a/gestion/urls.py b/gestion/urls.py index e4828cd..c05a330 100644 --- a/gestion/urls.py +++ b/gestion/urls.py @@ -29,7 +29,6 @@ urlpatterns = [ 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('cancelConsumption/', views.cancel_consumption, name="cancelConsumption"), path('cancelMenu/', views.cancel_menu, name="cancelMenu"), diff --git a/gestion/views.py b/gestion/views.py index 0964297..4f0826d 100644 --- a/gestion/views.py +++ b/gestion/views.py @@ -21,6 +21,45 @@ from preferences.models import PaymentMethod @login_required @acl_or('gestion.add_consumptionhistory', 'gestion.add_reload', 'gestion.add_refund') def manage(request): + """ + Display the manage page + + **Context** + + ``gestion_form`` + The manage form + + ``reload_form`` + The :model:`gestion.Reload` form + + ``refund_form`` + The :model:`gestion.Refund` form + + ``bieresPression`` + A list of active :model:`gestion.Product` corresponding to draft beers + + ``bieresBouteille`` + A list of active :model:`gestion.Product` corresponding to bottle beers + + ``panini`` + A list of active :model:`gestion.Product` corresponding to panini items + + ``food`` + A list of active :model:`gestion.Product` corresponding to non-panini items + + ``soft`` + A list of active :model:`gestion.Product` correspond to non alcoholic beverage + + ``menus`` + The list of active :model:`gestion.Menu` + + ``pay_buttons`` + List of :model:`paymentMethod` + + **Template** + + :template:`gestion/manage.html` + """ pay_buttons = PaymentMethod.objects.filter(is_active=True) gestion_form = GestionForm(request.POST or None) reload_form = ReloadForm(request.POST or None) @@ -39,12 +78,27 @@ 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, "pay_buttons": pay_buttons}) + 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 + }) +@active_required @login_required @permission_required('gestion.add_consumptionhistory') @csrf_exempt def order(request): + """ + Process the given order. Called by a js/JQuery script. + """ if("user" not in request.POST or "paymentMethod" not in request.POST or "amount" not in request.POST or "order" not in request.POST): return HttpResponse("Erreur du POST") else: @@ -120,34 +174,42 @@ def order(request): article.save() return HttpResponse("La commande a bien été effectuée") +@active_required @login_required @permission_required('gestion.add_reload') def reload(request): + """ + Process a 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() + if reload_form.is_valid(): + reload_entry = reload_form.save(commit=False) + reload_entry.coopeman = request.user + reload_entry.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) + "€") + 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')) +@active_required @login_required @permission_required('gestion.add_refund') def refund(request): + """ + Process a refund request + """ refund_form = RefundForm(request.POST or None) - if(refund_form.is_valid()): + 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() + if amount <= user.profile.balance: + refund_entry = refund_form.save(commit = False) + refund_entry.coopeman = request.user + refund_entry.save() user.profile.credit -= amount user.save() messages.success(request, "Le compte de " + user.username + " a bien été remboursé de " + str(amount) + "€") @@ -157,9 +219,16 @@ def refund(request): messages.error(request, "Le remboursement a échoué") return redirect(reverse('gestion:manage')) +@active_required @login_required @permission_required('gestion.delete_consumptionhistory') def cancel_consumption(request, pk): + """ + Cancel a :model:`gestion.ConsumptionHistory` + + ``pk`` + The primary key of the :model:`gestion.ConsumptionHistory` that have to be cancelled + """ consumption = get_object_or_404(ConsumptionHistory, pk=pk) user = consumption.customer user.profile.debit -= consumption.amount @@ -168,9 +237,16 @@ def cancel_consumption(request, pk): messages.success(request, "La consommation a bien été annulée") return redirect(reverse('users:profile', kwargs={'pk': user.pk})) +@active_required @login_required @permission_required('gestion.delete_menuhistory') def cancel_menu(request, pk): + """ + Cancel a :model:`gestion.MenuHistory` + + ``pk`` + The primary key of the :model:`gestion.MenuHistory` that have to be cancelled + """ menu_history = get_object_or_404(MenuHistory, pk=pk) user = menu_history.customer user.profile.debit -= menu_history.amount @@ -180,15 +256,41 @@ def cancel_menu(request, pk): return redirect(reverse('users:profile', kwargs={'pk': user.pk})) ########## Products ########## - +@active_required @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): + """ + Display the products manage static page + + **Template** + + :template:`gestion/products_index.html` + """ return render(request, "gestion/products_index.html") +@active_required @login_required @permission_required('gestion.add_product') def addProduct(request): + """ + Form to add a :model:`gestion.Product` + + **Context** + + ``form`` + The ProductForm instance + + ``form_title`` + The title for the form template + + ``form_button`` + The text of the button for the form template + + **Template** + + :template:`form.html` + """ form = ProductForm(request.POST or None) if(form.is_valid()): form.save() @@ -196,9 +298,31 @@ def addProduct(request): return redirect(reverse('gestion:productsList')) return render(request, "form.html", {"form": form, "form_title": "Ajout d'un produit", "form_button": "Ajouter"}) +@active_required @login_required -@permission_required('gestion.edit_product') +@permission_required('gestion.change_product') def editProduct(request, pk): + """ + Form to edit a :model:`gestion.Product` + + ``pk`` + The primary key of the requested :model:`gestion.Product` + + **Context** + + ``form`` + The ProductForm instance + + ``form_title`` + The title for the form template + + ``form_button`` + The text of the button for the form template + + **Template** + + :template:`form.html` + """ product = get_object_or_404(Product, pk=pk) form = ProductForm(request.POST or None, instance=product) if(form.is_valid()): @@ -207,38 +331,96 @@ def editProduct(request, pk): return redirect(reverse('gestion:productsList')) return render(request, "form.html", {"form": form, "form_title": "Modification d'un produit", "form_button": "Modifier"}) +@active_required @login_required @permission_required('gestion.view_product') def productsList(request): + """ + Display the list of :model:`gestion.Product` + + **Context** + + ``products`` + The list of :model:`gestion.Product` + + **Template** + + :template:`gestion/products_list.html` + """ products = Product.objects.all() return render(request, "gestion/products_list.html", {"products": products}) +@active_required @login_required @permission_required('gestion.view_product') def searchProduct(request): + """ + Form to search a :model:`gestion.Product` + + **Context** + + ``form`` + The SearchProductForm instance + + ``form_title`` + The title for the form template + + ``form_button`` + The text of the button for the form template + + **Template** + + :template:`form.html` + """ 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"}) +@active_required @login_required @permission_required('gestion.view_product') def productProfile(request, pk): + """ + Display the profile of a :model:`gestion.Product` + + ``pk`` + The primary key of the requested :model:`gestion.Product` + + **Context** + + ``product`` + The :model:`gestion.Product` instance + + **Template** + + :model:`gestion/product_profile.html` + """ product = get_object_or_404(Product, pk=pk) return render(request, "gestion/product_profile.html", {"product": product}) - + +@active_required @login_required def getProduct(request, barcode): + """ + Get :model:`gestion.Product` by barcode. Called by a js/JQuery script + + ``barcode`` + The requested barcode + """ product = Product.objects.get(barcode=barcode) data = json.dumps({"pk": product.pk, "barcode" : product.barcode, "name": product.name, "amount" : product.amount}) return HttpResponse(data, content_type='application/json') +@active_required @login_required -@permission_required('gestion.edit_product') +@permission_required('gestion.change_product') def switch_activate(request, pk): """ - If the product is active, switch to not active. - If the product is not active, switch to active. + Switch the active status of the requested :model:`gestion.Product` + + ``pk`` + The primary key of the :model:`gestion.Product` """ product = get_object_or_404(Product, pk=pk) product.is_active = 1 - product.is_active @@ -247,6 +429,9 @@ def switch_activate(request, pk): return redirect(reverse('gestion:productsList')) class ProductsAutocomplete(autocomplete.Select2QuerySetView): + """ + Autocomplete view for all :model:`gestion.Product` + """ def get_queryset(self): qs = Product.objects.all() if self.q: @@ -255,9 +440,28 @@ class ProductsAutocomplete(autocomplete.Select2QuerySetView): ########## Kegs ########## +@active_required @login_required @permission_required('gestion.add_keg') def addKeg(request): + """ + Display a form to add a :model:`gestion.Keg` + + **Context** + + ``form`` + The KegForm instance + + ``form_title`` + The title for the :template:`form.html` template + + ``form_button`` + The text for the button in :template:`form.html` template + + **Template** + + :template:`form.html` + """ form = KegForm(request.POST or None) if(form.is_valid()): keg = form.save() @@ -265,9 +469,31 @@ def addKeg(request): return redirect(reverse('gestion:kegsList')) return render(request, "form.html", {"form":form, "form_title": "Ajout d'un fût", "form_button": "Ajouter"}) +@active_required @login_required -@permission_required('gestion.edit_keg') +@permission_required('gestion.change_keg') def editKeg(request, pk): + """ + Display a form to edit a :model:`gestion.Keg` + + ``pk`` + The primary key of the requested :model:`gestion.Keg` + + **Context** + + ``form`` + The KegForm instance + + ``form_title`` + The title for the :template:`form.html` template + + ``form_button`` + The text for the button in :template:`form.html` template + + **Template** + + :template:`form.html` + """ keg = get_object_or_404(Keg, pk=pk) form = KegForm(request.POST or None, instance=keg) if(form.is_valid()): @@ -276,9 +502,28 @@ def editKeg(request, pk): return redirect(reverse('gestion:kegsList')) return render(request, "form.html", {"form": form, "form_title": "Modification d'un fût", "form_button": "Modifier"}) +@active_required @login_required @permission_required('gestion.open_keg') def openKeg(request): + """ + Display a form to open a :model:`gestion.Keg` + + **Context** + + ``form`` + The SelectPositiveKegForm instance + + ``form_title`` + The title for the :template:`form.html` template + + ``form_button`` + The text for the button in :template:`form.html` template + + **Template** + + :template:`form.html` + """ form = SelectPositiveKegForm(request.POST or None) if(form.is_valid()): keg = form.cleaned_data['keg'] @@ -296,9 +541,16 @@ def openKeg(request): return redirect(reverse('gestion:kegsList')) return render(request, "form.html", {"form": form, "form_title":"Percutage d'un fût", "form_button":"Percuter"}) +@active_required @login_required @permission_required('gestion.open_keg') def openDirectKeg(request, pk): + """ + Open the requested :model:`gestion.Keg` + + ``pk`` + The primary key of the :model:`gestion.Keg` + """ keg = get_object_or_404(Keg, pk=pk) if(keg.stockHold > 0): previousKegHistory = KegHistory.objects.filter(keg=keg).filter(isCurrentKegHistory=True) @@ -316,9 +568,28 @@ def openDirectKeg(request, pk): messages.error(request, "Il n'y a pas de fût en stock") return redirect(reverse('gestion:kegsList')) +@active_required @login_required @permission_required('gestion.close_keg') def closeKeg(request): + """ + Display a form to close a :model:`gestion.Keg` + + **Context** + + ``form`` + The SelectActiveKegForm instance + + ``form_title`` + The title for the :template:`form.html` template + + ``form_button`` + The text for the button in :template:`form.html` template + + **Template** + + :template:`form.html` + """ form = SelectActiveKegForm(request.POST or None) if(form.is_valid()): keg = form.cleaned_data['keg'] @@ -332,11 +603,18 @@ def closeKeg(request): return redirect(reverse('gestion:kegsList')) return render(request, "form.html", {"form": form, "form_title":"Fermeture d'un fût", "form_button":"Fermer le fût"}) +@active_required @login_required -@permission_required('gestion:close_keg') +@permission_required('gestion.close_keg') def closeDirectKeg(request, pk): + """ + Close the requested :model:`gestion.Keg` + + ``pk`` + The pk of the active :model:`gestion.Keg` + """ keg = get_object_or_404(Keg, pk=pk) - if(keg.is_active): + if keg.is_active: kegHistory = get_object_or_404(KegHistory, keg=keg, isCurrentKegHistory=True) kegHistory.isCurrentKegHistory = False kegHistory.closingDate = timezone.now() @@ -348,22 +626,60 @@ def closeDirectKeg(request, pk): messages.error(request, "Le fût n'est pas ouvert") return redirect(reverse('gestion:kegsList')) +@active_required @login_required @permission_required('gestion.view_keg') def kegsList(request): + """ + Display the list of :model:`gestion.Keg` + + **Context** + + ``kegs_active`` + List of active :model:`gestion.Keg` + + ``kegs_inactive`` + List of inactive :model:`gestion.Keg` + + **Template** + + :template:`gestion/kegs_list.html` + """ kegs_active = KegHistory.objects.filter(isCurrentKegHistory=True) ids_actives = kegs_active.values('id') kegs_inactive = Keg.objects.exclude(id__in = ids_actives) return render(request, "gestion/kegs_list.html", {"kegs_active": kegs_active, "kegs_inactive": kegs_inactive}) +@active_required @login_required @permission_required('gestion.view_keghistory') def kegH(request, pk): + """ + Display the history of requested :model:`gestion.Keg` + + ``pk`` + The primary key of the requested :model:`gestion.Keg` + + **Context** + + ``keg`` + The :model:`gestion.Keg` instance + + ``kegHistory`` + List of :model:`gestion.KegHistory` attached to keg + + **Template** + + :template:`gestion/kegh.html` + """ keg = get_object_or_404(Keg, pk=pk) kegHistory = KegHistory.objects.filter(keg=keg).order_by('-openingDate') return render(request, "gestion/kegh.html", {"keg": keg, "kegHistory": kegHistory}) class KegActiveAutocomplete(autocomplete.Select2QuerySetView): + """ + Autocomplete view for active :model:`gestion.Keg` + """ def get_queryset(self): qs = Keg.objects.filter(is_active = True) if self.q: @@ -371,6 +687,9 @@ class KegActiveAutocomplete(autocomplete.Select2QuerySetView): return qs class KegPositiveAutocomplete(autocomplete.Select2QuerySetView): + """ + Autocomplete view for :model:`gestion.Keg` with positive stockHold + """ def get_queryset(self): qs = Keg.objects.filter(stockHold__gt = 0) if self.q: @@ -379,9 +698,28 @@ class KegPositiveAutocomplete(autocomplete.Select2QuerySetView): ########## Menus ########## +@active_required @login_required @permission_required('gestion.add_menu') def addMenu(request): + """ + Display a form to add a :model:`gestion.Menu` + + **Context** + + ``form`` + The MenuForm instance + + ``form_title`` + The title for the :template:`form.html` template + + ``form_button`` + The text for the button in :template:`form.html` template + + **Template** + + :template:`form.html` + """ form = MenuForm(request.POST or None) extra_css = "#id_articles{height:200px;}" if(form.is_valid()): @@ -390,9 +728,31 @@ def addMenu(request): return redirect(reverse('gestion:menusList')) return render(request, "form.html", {"form":form, "form_title": "Ajout d'un menu", "form_button": "Ajouter", "extra_css": extra_css}) +@active_required @login_required -@permission_required('gestion.edit_menu') +@permission_required('gestion.change_menu') def edit_menu(request, pk): + """ + Display a form to edit a :model:`gestion.Menu` + + ``pk`` + The primary key of requested :model:`gestion.Menu` + + **Context** + + ``form`` + The MenuForm instance + + ``form_title`` + The title for the :template:`form.html` template + + ``form_button`` + The text for the button in :template:`form.html` template + + **Template** + + :template:`form.html` + """ menu = get_object_or_404(Menu, pk=pk) form = MenuForm(request.POST or None, instance=menu) extra_css = "#id_articles{height:200px;}" @@ -402,11 +762,12 @@ def edit_menu(request, pk): return redirect(reverse('gestion:menusList')) return render(request, "form.html", {"form": form, "form_title": "Modification d'un menu", "form_button": "Modifier", "extra_css": extra_css}) +@active_required @login_required @permission_required('gestion.view_menu') def searchMenu(request): """ - Search a menu via SearchMenuForm instance + Search a :model:`gestion.Menu` via SearchMenuForm instance **Context** @@ -429,18 +790,34 @@ def searchMenu(request): return redirect(reverse('gestion:editMenu', kwargs={'pk':menu.pk})) return render(request, "form.html", {"form": form, "form_title": "Recherche d'un menu", "form_button": "Modifier"}) +@active_required @login_required @permission_required('gestion.view_menu') def menus_list(request): + """ + Display the :model:`gestion.Menu` list + + **Context** + + ``menus`` + The list of :model:`gestion.Menu` instances + + **Template** + + :template:`gestion/menus_list.html` + """ menus = Menu.objects.all() return render(request, "gestion/menus_list.html", {"menus": menus}) +@active_required @login_required -@permission_required('gestion.edit_menu') +@permission_required('gestion.change_menu') def switch_activate_menu(request, pk): """ - If the menu is active, switch to not active. - If the menu is not active, switch to active. + Switch active status of a :model:`gestion.Menu` + + ``pk`` + The pk of the :model:`gestion.Menu` """ menu = get_object_or_404(Menu, pk=pk) menu.is_active = 1 - menu.is_active @@ -448,22 +825,50 @@ def switch_activate_menu(request, pk): messages.success(request, "La disponibilité du menu a bien été changée") return redirect(reverse('gestion:menusList')) +@active_required @login_required +@permission_required('gestion.view_menu') def get_menu(request, barcode): + """ + Search :model:`gestion.Menu` by barcode + + ``barcode`` + The requested barcode + """ menu = get_object_or_404(Menu, barcode=barcode) data = json.dumps({"pk": menu.pk, "barcode" : menu.barcode, "name": menu.name, "amount" : menu.amount}) return HttpResponse(data, content_type='application/json') class MenusAutocomplete(autocomplete.Select2QuerySetView): + """ + Used as autcomplete for all :model:`gestion.Menu` + """ def get_queryset(self): qs = Menu.objects.all() if self.q: qs = qs.filter(name__istartswith=self.q) return qs + ########## Ranking ########## +@active_required @login_required def ranking(request): + """ + Display the ranking page + + **Context** + + ``bestBuyers`` + List of the 25 best buyers + + ``bestDrinkers`` + List of the 25 best drinkers + + **Template** + + :template: `gestion/ranking.html` + """ bestBuyers = User.objects.order_by('-profile__debit')[:25] customers = User.objects.all() list = [] @@ -471,8 +876,4 @@ def ranking(request): 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") + return render(request, "gestion/ranking.html", {"bestBuyers": bestBuyers, "bestDrinkers": bestDrinkers}) \ No newline at end of file diff --git a/preferences/forms.py b/preferences/forms.py index bfcb2f0..d6224c6 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -4,17 +4,26 @@ from django.core.exceptions import ValidationError from .models import Cotisation, PaymentMethod, GeneralPreferences class CotisationForm(forms.ModelForm): + """ + Form to add and edit cotisations + """ class Meta: model = Cotisation fields = "__all__" class PaymentMethodForm(forms.ModelForm): + """ + Form to add and edit payment methods + """ class Meta: model = PaymentMethod fields = "__all__" class GeneralPreferencesForm(forms.ModelForm): + """ + Form to edit the general preferences + """ class Meta: model = GeneralPreferences fields = "__all__" diff --git a/preferences/models.py b/preferences/models.py index 13781b1..8f41f74 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -4,6 +4,9 @@ from django.core.validators import MinValueValidator class PaymentMethod(models.Model): + """ + Stores payment methods + """ 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="Cotisations ?") @@ -15,6 +18,9 @@ class PaymentMethod(models.Model): return self.name class GeneralPreferences(models.Model): + """ + Stores a unique line of general preferences + """ is_active = models.BooleanField(default=True) active_message = models.TextField(blank=True) global_message = models.TextField(blank=True) @@ -27,6 +33,9 @@ class GeneralPreferences(models.Model): history = HistoricalRecords() class Cotisation(models.Model): + """ + Stores cotisations + """ amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, verbose_name="Montant", validators=[MinValueValidator(0)]) duration = models.PositiveIntegerField(verbose_name="Durée de la cotisation (jours)") history = HistoricalRecords() diff --git a/preferences/views.py b/preferences/views.py index 58ff90b..a832db2 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -11,8 +11,20 @@ from .forms import CotisationForm, PaymentMethodForm, GeneralPreferencesForm @active_required @login_required -@permission_required('preferences.add_generalpreferences') +@permission_required('preferences.change_generalpreferences') def generalPreferences(request): + """ + Display form to edit the general preferences + + **Context** + + ``form`` + The GeneralPreferences form instance + + **Template** + + :template:`preferences/general_preferences.html` + """ gp,_ = GeneralPreferences.objects.get_or_create(pk=1) form = GeneralPreferencesForm(request.POST or None, instance=gp) if(form.is_valid()): @@ -25,6 +37,18 @@ def generalPreferences(request): @login_required @permission_required('preferences.view_cotisation') def cotisationsIndex(request): + """ + Lists the cotisations + + **Context** + + ``cotisations`` + List of cotisations + + **Template** + + :template:`preferences/cotisations_index.html` + """ cotisations = Cotisation.objects.all() return render(request, "preferences/cotisations_index.html", {"cotisations": cotisations}) @@ -32,6 +56,24 @@ def cotisationsIndex(request): @login_required @permission_required('preferences.add_cotisation') def addCotisation(request): + """ + Form to add a cotisation + + **Context** + + ``form`` + The CotisationForm form instance + + ``form_title`` + The title of the form + + ``form_button`` + The text of the form button + + **Template** + + :template:`form.html` + """ form = CotisationForm(request.POST or None) if(form.is_valid()): cotisation = form.save() @@ -43,6 +85,27 @@ def addCotisation(request): @login_required @permission_required('preferences.change_cotisation') def editCotisation(request, pk): + """ + Form to edit a cotisation + + ``pk`` + The primary key of the cotisation + + **Context** + + ``form`` + The CotisationForm form instance + + ``form_title`` + The title of the form + + ``form_button`` + The text of the form button + + **Template** + + :template:`form.html` + """ cotisation = get_object_or_404(Cotisation, pk=pk) form = CotisationForm(request.POST or None, instance=cotisation) if(form.is_valid()): @@ -55,6 +118,12 @@ def editCotisation(request, pk): @login_required @permission_required('preferences.delete_cotisation') def deleteCotisation(request,pk): + """ + Delete a cotisation + + ``pk`` + The primary key of the cotisation to delete + """ 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() @@ -68,6 +137,18 @@ def deleteCotisation(request,pk): @login_required @permission_required('preferences.view_paymentmethod') def paymentMethodsIndex(request): + """ + Lists the paymentMethods + + **Context** + + ``paymentMethods`` + List of paymentMethods + + **Template** + + :template:`preferences/payment_methods_index.html` + """ paymentMethods = PaymentMethod.objects.all() return render(request, "preferences/payment_methods_index.html", {"paymentMethods": paymentMethods}) @@ -75,6 +156,24 @@ def paymentMethodsIndex(request): @login_required @permission_required('preferences.add_paymentmethod') def addPaymentMethod(request): + """ + Form to add a paymentMethod + + **Context** + + ``form`` + The CotisationForm form paymentMethod + + ``form_title`` + The title of the form + + ``form_button`` + The text of the form button + + **Template** + + :template:`form.html` + """ form = PaymentMethodForm(request.POST or None) if(form.is_valid()): paymentMethod = form.save() @@ -86,6 +185,27 @@ def addPaymentMethod(request): @login_required @permission_required('preferences.change_paymentmethod') def editPaymentMethod(request, pk): + """ + Form to edit a paymentMethod + + ``pk`` + The primary key of the paymentMethod + + **Context** + + ``form`` + The PaymentMethodForm form instance + + ``form_title`` + The title of the form + + ``form_button`` + The text of the form button + + **Template** + + :template:`form.html` + """ paymentMethod = get_object_or_404(PaymentMethod, pk=pk) form = PaymentMethodForm(request.POST or None, instance=paymentMethod) if(form.is_valid()): @@ -98,8 +218,14 @@ def editPaymentMethod(request, pk): @login_required @permission_required('preferences.delete_paymentmethod') def deletePaymentMethod(request,pk): + """ + Delete a paymentMethod + + ``pk`` + The primary key of the paymentMethod to delete + """ 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')) \ No newline at end of file + return redirect(reverse('preferences:paymentMethodsIndex')) diff --git a/templates/nav.html b/templates/nav.html index 42f4af2..7244264 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -14,9 +14,6 @@ Classement - - Classement sur l'année - Admin diff --git a/users/forms.py b/users/forms.py index a8f0fd7..4fea80f 100644 --- a/users/forms.py +++ b/users/forms.py @@ -4,10 +4,16 @@ from dal import autocomplete from .models import School, CotisationHistory, WhiteListHistory class LoginForm(forms.Form): + """ + Form to log in + """ username = forms.CharField(max_length=255, label="Nom d'utitisateur") password = forms.CharField(max_length=255, widget=forms.PasswordInput, label="Mot de passe") class CreateUserForm(forms.ModelForm): + """ + Form to create a new user + """ class Meta: model = User fields = ("username", "last_name", "first_name", "email") @@ -15,35 +21,59 @@ class CreateUserForm(forms.ModelForm): school = forms.ModelChoiceField(queryset=School.objects.all(), label="École") class CreateGroupForm(forms.ModelForm): + """ + Form to create a new group + """ class Meta: model = Group fields = ("name", ) class EditGroupForm(forms.ModelForm): + """ + Form to edit a group + """ class Meta: model = Group fields = "__all__" class SelectUserForm(forms.Form): + """ + Form to select a user from all users + """ 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): + """ + Form to select a user from all non-superuser users + """ 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): + """ + Form to select a user from all non-staff users + """ 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): + """ + Form to edit a user's list of groups + """ class Meta: model = User fields = ("groups", ) class EditPasswordForm(forms.Form): + """ + Form to change the password of a user + """ 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): + """ + Verify if the two new passwords are identical + """ password1 = self.cleaned_data.get("password1") password2 = self.cleaned_data.get("password2") if password1 and password2 and password1 != password2: @@ -51,16 +81,25 @@ class EditPasswordForm(forms.Form): return password2 class addCotisationHistoryForm(forms.ModelForm): + """ + Form to add a cotisation to user + """ class Meta: model = CotisationHistory fields = ("cotisation", "paymentMethod") class addWhiteListHistoryForm(forms.ModelForm): + """ + Form to add a whitelist to user + """ class Meta: model = WhiteListHistory fields = ("duration", ) class SchoolForm(forms.ModelForm): + """ + Form to add and edit a school + """ class Meta: model = School fields = "__all__" \ No newline at end of file diff --git a/users/migrations/0005_auto_20181202_1628.py b/users/migrations/0005_auto_20181202_1628.py new file mode 100644 index 0000000..99a1031 --- /dev/null +++ b/users/migrations/0005_auto_20181202_1628.py @@ -0,0 +1,45 @@ +# Generated by Django 2.1 on 2018-12-02 15:28 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0004_historicalcotisationhistory_historicalprofile_historicalschool_historicalwhitelisthistory'), + ] + + operations = [ + migrations.AlterModelOptions( + name='cotisationhistory', + options={'permissions': (('validate_cotisationhistory', 'Peut (in)valider les cotisations'),), 'verbose_name': 'Historique cotisation'}, + ), + migrations.AlterModelOptions( + name='historicalcotisationhistory', + options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Historique cotisation'}, + ), + migrations.AlterModelOptions( + name='historicalprofile', + options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Profil'}, + ), + migrations.AlterModelOptions( + name='historicalschool', + options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical École'}, + ), + migrations.AlterModelOptions( + name='historicalwhitelisthistory', + options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Historique accès gracieux'}, + ), + migrations.AlterModelOptions( + name='profile', + options={'verbose_name': 'Profil'}, + ), + migrations.AlterModelOptions( + name='school', + options={'verbose_name': 'École'}, + ), + migrations.AlterModelOptions( + name='whitelisthistory', + options={'verbose_name': 'Historique accès gracieux'}, + ), + ] diff --git a/users/models.py b/users/models.py index 7799e07..72c97aa 100644 --- a/users/models.py +++ b/users/models.py @@ -8,6 +8,12 @@ from preferences.models import PaymentMethod, Cotisation from gestion.models import ConsumptionHistory class School(models.Model): + """ + Stores school + """ + class Meta: + verbose_name = "École" + name = models.CharField(max_length=255, verbose_name="Nom") history = HistoricalRecords() @@ -15,9 +21,13 @@ class School(models.Model): return self.name class CotisationHistory(models.Model): + """ + Stores cotisations history, related to :model:`preferences.Cotisation` + """ class Meta: + verbose_name = "Historique cotisation" permissions = ( - ("validate_consumptionhistory", "Peut (in)valider les cotisations"), + ("validate_cotisationhistory", "Peut (in)valider les cotisations"), ) WAITING = 0 @@ -40,6 +50,12 @@ class CotisationHistory(models.Model): history = HistoricalRecords() class WhiteListHistory(models.Model): + """ + Stores whitelist history + """ + class Meta: + verbose_name = "Historique accès gracieux" + user = models.ForeignKey(User, on_delete=models.PROTECT) paymentDate = models.DateTimeField(auto_now_add=True) endDate = models.DateTimeField() @@ -48,6 +64,12 @@ class WhiteListHistory(models.Model): history = HistoricalRecords() class Profile(models.Model): + """ + Stores user profile + """ + class Meta: + verbose_name = "Profil" + user = models.OneToOneField(User, on_delete=models.CASCADE) credit = models.DecimalField(max_digits=5, decimal_places=2, default=0) debit = models.DecimalField(max_digits=5, decimal_places=2, default=0) @@ -57,6 +79,9 @@ class Profile(models.Model): @property def is_adherent(self): + """ + Test if a user is adherent + """ if(self.cotisationEnd and self.cotisationEnd > timezone.now()): return True else: @@ -64,17 +89,29 @@ class Profile(models.Model): @property def balance(self): + """ + Computes user balance + """ return self.credit - self.debit def positiveBalance(self): + """ + Test if the user balance is positive or null + """ return self.balance >= 0 @property def rank(self): + """ + Computes the rank (by debit) of the user + """ return Profile.objects.filter(debit__gte=self.debit).count() @property def alcohol(self): + """ + Computes ingerated alcohol + """ consumptions = ConsumptionHistory.objects.filter(customer=self.user).select_related('product') alcohol = 0 for consumption in consumptions: @@ -87,18 +124,27 @@ class Profile(models.Model): @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): + """ + Create profile when user is created + """ if created: Profile.objects.create(user=instance) @receiver(post_save, sender=User) def save_user_profile(sender, instance, **kwargs): + """ + Save profile when user is saved + """ instance.profile.save() def str_user(self): - if(self.profile.is_adherent): + """ + Rewrite str method for user + """ + if self.profile.is_adherent: fin = "Adhérent" else: fin = "Non adhérent" return self.username + " (" + self.first_name + " " + self.last_name + ", " + str(self.profile.balance) + "€, " + fin + ")" -User.add_to_class("__str__", str_user) \ No newline at end of file +User.add_to_class("__str__", str_user) diff --git a/users/views.py b/users/views.py index 08a85f6..fe9e6cf 100644 --- a/users/views.py +++ b/users/views.py @@ -623,6 +623,18 @@ def removeUser(request, groupPk, userPk): @login_required @admin_required def adminsIndex(request): + """ + Lists the staff + + **Context** + + ``admins`` + List of staff + + **Template** + + :template:`users/admins_index.html` + """ admins = User.objects.filter(is_staff=True) return render(request, "users/admins_index.html", {"admins": admins}) @@ -630,6 +642,24 @@ def adminsIndex(request): @login_required @admin_required def addAdmin(request): + """ + Form to add a member to staff + + **Context** + + ``form`` + The SelectNonAdminUserForm form instance + + ``form_title`` + The title of the form + + ``form_button`` + The text of the button + + **Template** + + :template:`form.html` + """ form = SelectNonAdminUserForm(request.POST or None) if(form.is_valid()): user = form.cleaned_data['user'] @@ -637,12 +667,18 @@ def addAdmin(request): user.save() messages.success(request, "L'utilisateur " + user.username + " a bien été rajouté aux admins") 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"}) + return render(request, "form.html", {"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): + """ + Remove an user form staff + + ``pk`` + The primary key of the user + """ user = get_object_or_404(User, pk=pk) if user.is_staff: if user.is_superuser: @@ -664,6 +700,18 @@ def removeAdmin(request, pk): @login_required @superuser_required def superusersIndex(request): + """ + Lists the superusers + + **Context** + + ``superusers`` + List of superusers + + **Template** + + :template:`users/superusers_index.html` + """ superusers = User.objects.filter(is_superuser=True) return render(request, "users/superusers_index.html", {"superusers": superusers}) @@ -671,8 +719,26 @@ def superusersIndex(request): @login_required @superuser_required def addSuperuser(request): + """ + Displays a form to add a superuser + + **Context** + + ``form`` + The SelectNonSuperUserForm form instance + + ``form_title`` + The title of the form + + ``form_button`` + The text of the button + + **Template** + + :template:`form.html` + """ form = SelectNonSuperUserForm(request.POST or None) - if(form.is_valid()): + if form.is_valid(): user = form.cleaned_data['user'] user.is_admin = True user.is_superuser = True @@ -685,6 +751,12 @@ def addSuperuser(request): @login_required @superuser_required def removeSuperuser(request, pk): + """ + Removes a user from superusers + + ``pk`` + The primary key of the user + """ user = get_object_or_404(User, pk=pk) if user.is_superuser: if User.objects.filter(is_superuser=True).count() > 1: @@ -703,6 +775,27 @@ def removeSuperuser(request, pk): @login_required @permission_required('users.add_cotisationhistory') def addCotisationHistory(request, pk): + """ + Add a cotisation to the requested user + + ``pk`` + The primary key of the user + + **Context** + + ``form`` + The addCotisationHistoryForm form instance + + ``form_title`` + The title of the form + + ``form_button`` + The text of the button + + **Template** + + :template:`form.html` + """ user = get_object_or_404(User, pk=pk) form = addCotisationHistoryForm(request.POST or None) if(form.is_valid()): @@ -730,8 +823,14 @@ def addCotisationHistory(request, pk): @active_required @login_required -@permission_required('users.validate_consumptionhistory') +@permission_required('users.validate_cotisationhistory') def validateCotisationHistory(request, pk): + """ + Validate the requested :model:`users.CotisationHistory` + + ``pk`` + The primary key of the :model:`users.CotisationHistory` + """ cotisationHistory = get_object_or_404(CotisationHistory, pk=pk) cotisationHistory.valid = CotisationHistory.VALID cotisationHistory.save() @@ -740,8 +839,14 @@ def validateCotisationHistory(request, pk): @active_required @login_required -@permission_required('users.validate_consumptionhistory') +@permission_required('users.validate_cotisationhistory') def invalidateCotisationHistory(request, pk): + """ + Invalidate the requested :model:`users.CotisationHistory` + + ``pk`` + The primary key of the :model:`users.CotisationHistory` + """ cotisationHistory = get_object_or_404(CotisationHistory, pk=pk) cotisationHistory.valid = CotisationHistory.INVALID cotisationHistory.save() @@ -759,6 +864,27 @@ def invalidateCotisationHistory(request, pk): @login_required @permission_required('users.add_whitelisthistory') def addWhiteListHistory(request, pk): + """ + Add a :model:`users.WhitelistHistory` to the requested user + + ``pk`` + The primary key of the user + + **Context** + + ``form`` + The addWhiteListHistoryForm form instance + + ``form_title`` + The title of the form + + ``form_button`` + The text of the button + + **Template** + + :template:`form.html` + """ user = get_object_or_404(User, pk=pk) form = addWhiteListHistoryForm(request.POST or None) if(form.is_valid()): @@ -782,6 +908,18 @@ def addWhiteListHistory(request, pk): @login_required @permission_required('users.view_school') def schoolsIndex(request): + """ + Lists the :model:`users.School` + + **Context** + + ``schools`` + List of the :model:`users.School` + + **Template** + + :template:`users/schools_index.html` + """ schools = School.objects.all() return render(request, "users/schools_index.html", {"schools": schools}) @@ -789,8 +927,26 @@ def schoolsIndex(request): @login_required @permission_required('users.add_school') def createSchool(request): + """ + Displays form to create :model:`users.School` + + **Context** + + ``form`` + The SchoolForm form instance + + ``form_title`` + The title of the form + + ``form_button`` + The text of the button + + **Template** + + :template:`form.html` + """ form = SchoolForm(request.POST or None) - if(form.is_valid()): + if form.is_valid(): form.save() messages.success(request, "L'école a bien été créée") return redirect(reverse('users:schoolsIndex')) @@ -800,6 +956,27 @@ def createSchool(request): @login_required @permission_required('users.change_school') def editSchool(request, pk): + """ + Displays form to create :model:`users.School` + + ``pk`` + The primary key of :model:`users.School` + + **Context** + + ``form`` + The SchoolForm form instance + + ``form_title`` + The title of the form + + ``form_button`` + The text of the button + + **Template** + + :template:`form.html` + """ school = get_object_or_404(School, pk=pk) form = SchoolForm(request.POST or None, instance=school) if(form.is_valid()): @@ -812,6 +989,12 @@ def editSchool(request, pk): @login_required @permission_required('users.delete_school') def deleteSchool(request, pk): + """ + Delete a :model:`users.School` + + ``pk`` + The primary key of the school to delete + """ school = get_object_or_404(School, pk=pk) message = "L'école " + str(school) + " a bien été supprimée" school.delete() @@ -821,6 +1004,9 @@ def deleteSchool(request, pk): ########## Autocomplete searchs ########## class AllUsersAutocomplete(autocomplete.Select2QuerySetView): + """ + Autcomplete for all users + """ def get_queryset(self): qs = User.objects.all() if self.q: @@ -828,6 +1014,9 @@ class AllUsersAutocomplete(autocomplete.Select2QuerySetView): return qs class ActiveUsersAutocomplete(autocomplete.Select2QuerySetView): + """ + Autocomplete for active users + """ def get_queryset(self): qs = User.objects.filter(is_active=True) if self.q: @@ -835,11 +1024,17 @@ class ActiveUsersAutocomplete(autocomplete.Select2QuerySetView): return qs class AdherentAutocomplete(autocomplete.Select2QuerySetView): + """ + Autocomplete for adherents + """ def get_queryset(self): qs = User.objects.all() return qs class NonSuperUserAutocomplete(autocomplete.Select2QuerySetView): + """ + Autocomplete for non-superuser users + """ def get_queryset(self): qs = User.objects.filter(is_superuser=False) if self.q: @@ -847,6 +1042,9 @@ class NonSuperUserAutocomplete(autocomplete.Select2QuerySetView): return qs class NonAdminUserAutocomplete(autocomplete.Select2QuerySetView): + """ + Autocomplete for non-admin users + """ def get_queryset(self): qs = User.objects.filter(is_staff=False) if self.q: