3
0
Fork 0
mirror of https://github.com/nanoy42/coope synced 2024-11-22 03:13:12 +00:00
This commit is contained in:
Yoann Pétri 2018-11-22 22:52:15 +01:00
parent 15e13f978c
commit 23eb8776a8
26 changed files with 748 additions and 112 deletions

1
.gitignore vendored
View file

@ -39,3 +39,4 @@ tags
# End of https://www.gitignore.io/api/vim,git,django # End of https://www.gitignore.io/api/vim,git,django
.vscode

View file

@ -37,12 +37,13 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.contrib.admindocs',
'gestion', 'gestion',
'users', 'users',
'preferences', 'preferences',
'coopeV3', 'coopeV3',
'dal', 'dal',
'dal_select2', 'dal_select2',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -129,4 +130,4 @@ STATICFILES_DIRS = [
] ]
STATIC_ROOT = os.path.join(BASE_DIR, 'static') STATIC_ROOT = os.path.join(BASE_DIR, 'static')
LOGIN_URL = '/users/login'

View file

@ -20,8 +20,9 @@ from . import views
urlpatterns = [ urlpatterns = [
path('', views.home, name="home"), path('', views.home, name="home"),
path('admin/doc/', include('django.contrib.admindocs.urls')),
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('users/', include('users.urls')), path('users/', include('users.urls')),
path('gestion/', include('gestion.urls')), path('gestion/', include('gestion.urls')),
path('preferences/', include('preferences.urls')), path('preferences/', include('preferences.urls')),
] ]

View file

@ -1,8 +1,9 @@
from django.contrib import admin 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(Reload)
admin.site.register(Refund) admin.site.register(Refund)
admin.site.register(Product) admin.site.register(Product)
admin.site.register(Keg) admin.site.register(Keg)
admin.site.register(ConsumptionHistory)

View file

@ -1,4 +1,5 @@
from django import forms from django import forms
from django.core.exceptions import ValidationError
from django.contrib.auth.models import User from django.contrib.auth.models import User
from dal import autocomplete from dal import autocomplete
@ -8,14 +9,38 @@ from preferences.models import PaymentMethod
from coopeV3.widgets import SearchField from coopeV3.widgets import SearchField
class ReloadForm(forms.ModelForm): 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: class Meta:
model = Reload model = Reload
fields = ("customer", "amount", "PaymentMethod") 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 RefundForm(forms.ModelForm):
class Meta: class Meta:
model = Refund model = Refund
fields = ("customer", "amount") 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 ProductForm(forms.ModelForm):
class Meta: class Meta:
@ -32,6 +57,11 @@ class MenuForm(forms.ModelForm):
model = Menu model = Menu
fields = "__all__" 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): 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})) 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")

View file

@ -138,7 +138,7 @@ class MenuHistory(models.Model):
coopeman = models.ForeignKey(User, on_delete=models.PROTECT, related_name="menu_selled") coopeman = models.ForeignKey(User, on_delete=models.PROTECT, related_name="menu_selled")
def __str__(self): 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): class ConsumptionHistory(models.Model):
customer = models.ForeignKey(User, on_delete=models.PROTECT, related_name="consumption_taken") customer = models.ForeignKey(User, on_delete=models.PROTECT, related_name="consumption_taken")

View file

@ -61,7 +61,7 @@
<td id="balance">0€</td> <td id="balance">0€</td>
<td id="totalAmount">0€</td> <td id="totalAmount">0€</td>
<td id="totalAfter">0€</td> <td id="totalAfter">0€</td>
<td><button class="btn small">Payer</button></td> <td>{% for pm in pay_buttons %}<button class="btn small pay_button" data-payment="{{pm.pk}}">{{pm.name}}</button> {% endfor %}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -93,11 +93,11 @@
<table> <table>
<tbody class="actions" id="bouton Produit"> <tbody class="actions" id="bouton Produit">
<tr style="text-align:center; font-weight:bold;"><td colspan="4">Bières pression</td></tr> <tr style="text-align:center; font-weight:bold;"><td colspan="4">Bières pression</td></tr>
{% for produit in bieresPression %} {% for product in bieresPression %}
{% if forloop.counter0|divisibleby:4 %} {% if forloop.counter0|divisibleby:4 %}
<tr style="text-align:center"> <tr style="text-align:center">
{% endif %} {% endif %}
<td><button class="product" target="{{produit.barcode}}">{{produit.name}}</button></td> <td><button class="product" target="{{product.barcode}}">{{product.name}}</button></td>
{% if forloop.counter|divisibleby:4 %} {% if forloop.counter|divisibleby:4 %}
</tr> </tr>
{% endif %} {% endif %}
@ -106,11 +106,11 @@
</tr> </tr>
{% endif %} {% endif %}
<tr style="text-align:center; font-weight:bold;"><td colspan="4">Bières bouteilles</td></tr> <tr style="text-align:center; font-weight:bold;"><td colspan="4">Bières bouteilles</td></tr>
{% for produit in bieresBouteille %} {% for product in bieresBouteille %}
{% if forloop.counter0|divisibleby:4 %} {% if forloop.counter0|divisibleby:4 %}
<tr style="text-align:center"> <tr style="text-align:center">
{% endif %} {% endif %}
<td><button class="boutonsProduit" disabled target="{{produit.codeBarre}}" type="{{produit.typeSaisie}}">{{produit.nom}}</button></td> <td><button class="product" target="{{product.barcode}}">{{product.name}}</button></td>
{% if forloop.counter|divisibleby:4 %} {% if forloop.counter|divisibleby:4 %}
</tr> </tr>
{% endif %} {% endif %}
@ -119,11 +119,11 @@
</tr> </tr>
{% endif %} {% endif %}
<tr style="text-align:center; font-weight:bold;"><td colspan="4">Paninis</td></tr> <tr style="text-align:center; font-weight:bold;"><td colspan="4">Paninis</td></tr>
{% for produit in panini %} {% for product in panini %}
{% if forloop.counter0|divisibleby:4 %} {% if forloop.counter0|divisibleby:4 %}
<tr style="text-align:center"> <tr style="text-align:center">
{% endif %} {% endif %}
<td><button class="boutonsProduit" disabled target="{{produit.codeBarre}}" type="{{produit.typeSaisie}}">{{produit.nom}}</button></td> <td><button class="boutonsProduit" disabled target="{{product.codeBarre}}" type="{{product.typeSaisie}}">{{product.nom}}</button></td>
{% if forloop.counter|divisibleby:4 %} {% if forloop.counter|divisibleby:4 %}
</tr> </tr>
{% endif %} {% endif %}
@ -132,11 +132,11 @@
</tr> </tr>
{% endif %} {% endif %}
<tr style="text-align:center; font-weight:bold;"><td colspan="4">Softs</td></tr> <tr style="text-align:center; font-weight:bold;"><td colspan="4">Softs</td></tr>
{% for produit in soft %} {% for product in soft %}
{% if forloop.counter0|divisibleby:4 %} {% if forloop.counter0|divisibleby:4 %}
<tr style="text-align:center"> <tr style="text-align:center">
{% endif %} {% endif %}
<td><button class="boutonsProduit" disabled target="{{produit.codeBarre}}" type="{{produit.typeSaisie}}">{{produit.nom}}</button></td> <td><button class="boutonsProduit" disabled target="{{product.codeBarre}}" type="{{product.typeSaisie}}">{{product.nom}}</button></td>
{% if forloop.counter|divisibleby:4 %} {% if forloop.counter|divisibleby:4 %}
</tr> </tr>
{% endif %} {% endif %}
@ -146,11 +146,11 @@
{% endif %} {% endif %}
<tr style="text-align:center; font-weight:bold;"><td colspan="4">Bouffe</td></tr> <tr style="text-align:center; font-weight:bold;"><td colspan="4">Bouffe</td></tr>
{% for produit in autreBouffe %} {% for product in autreBouffe %}
{% if forloop.counter0|divisibleby:4 %} {% if forloop.counter0|divisibleby:4 %}
<tr style="text-align:center"> <tr style="text-align:center">
{% endif %} {% endif %}
<td><button class="boutonsProduit" disabled target="{{produit.codeBarre}}" type="{{produit.typeSaisie}}">{{produit.nom}}</button></td> <td><button class="boutonsProduit" disabled target="{{product.codeBarre}}" type="{{product.typeSaisie}}">{{product.nom}}</button></td>
{% if forloop.counter|divisibleby:4 %} {% if forloop.counter|divisibleby:4 %}
</tr> </tr>
{% endif %} {% endif %}
@ -160,11 +160,11 @@
{% endif %} {% endif %}
{% if menus %} {% if menus %}
<tr style="text-align:center; font-weight:bold;"><td colspan="4">Menus</td></tr> <tr style="text-align:center; font-weight:bold;"><td colspan="4">Menus</td></tr>
{% for produit in menus %} {% for product in menus %}
{% if forloop.counter0|divisibleby:4 %} {% if forloop.counter0|divisibleby:4 %}
<tr style="text-align:center"> <tr style="text-align:center">
{% endif %} {% endif %}
<td><button class="boutonsProduit" disabled target="{{produit.codeBarre}}" type="MN">{{produit.nom}}</button></td> <td><button class="boutonsProduit" disabled target="{{product.codeBarre}}" type="MN">{{product.nom}}</button></td>
{% if forloop.counter|divisibleby:4 %} {% if forloop.counter|divisibleby:4 %}
</tr> </tr>
{% endif %} {% endif %}
@ -208,5 +208,7 @@
</section> </section>
{% endif %} {% endif %}
{{gestion_form.media}} {{gestion_form.media}}
{{reload_form.media}}
{{refund_form.media}}
<script src="{% static 'manage.js' %}"></script> <script src="{% static 'manage.js' %}"></script>
{%endblock%} {%endblock%}

View file

@ -16,8 +16,8 @@
Actions possibles : Actions possibles :
<ul> <ul>
<li><a href="{% url 'gestion:addProduct' %}">Créer un produit</a></li> <li><a href="{% url 'gestion:addProduct' %}">Créer un produit</a></li>
<li><a href="{% url 'users:searchUser' %}">Rechercher un produit</a></li> <li><a href="{% url 'gestion:searchProduct' %}">Rechercher un produit</a></li>
<li><a href="{% url 'users:usersIndex' %}">Lister tous les produits</a></li> <li><a href="{% url 'gestion:productsList' %}">Lister tous les produits</a></li>
</ul> </ul>
</section> </section>
<section id="second" class="main"> <section id="second" class="main">
@ -26,9 +26,10 @@
</header> </header>
Actions possibles : Actions possibles :
<ul> <ul>
<li><a href="{% url 'gestion:addBarrel' %}">Créer un fut</a></li> <li><a href="{% url 'gestion:addKeg' %}">Créer un fut</a></li>
<li><a href="">Percuter un fut</a></li> <li><a href="">Percuter un fut</a></li>
<li><a href="">Lister les futs</a></li> <li><a href="">Lister les futs</a></li>
<li><a href="">Historique des futs</a></li>
</ul> </ul>
</section> </section>
<section id="third" class="main"> <section id="third" class="main">

View file

@ -13,43 +13,40 @@
<header class="major"> <header class="major">
<h2>Liste des produits</h2> <h2>Liste des produits</h2>
</header> </header>
Actions possibles : <a class="button" href="{% url 'gestion:addProduct' %}">Créer un produit</a><br><br>
<ul> <div class="table-wrapper">
<li><a href="{% url 'gestion:addProduct' %}">Créer un produit</a></li> <table>
<li><a href="{% url 'users:searchUser' %}">Rechercher un produit</a></li> <thead>
<li><a href="{% url 'users:usersIndex' %}">Lister tous les produits</a></li> <tr>
</ul> <th>Nom</th>
</section> <th>Prix</th>
<section id="second" class="main"> <th>Stock (soute)</th>
<header class="major"> <th>Stock (bar)</th>
<h2>Futs</h2> <th>Code barre</th>
</header> <th>Catégorie</th>
Actions possibles : <th>Actif</th>
<ul> <th>Degré</th>
<li><a href="{% url 'users:createGroup' %}">Créer un fut</a></li> <th>Volume</th>
<li><a href="">Percuter un fut</a></li> <th>Administrer</th>
<li><a href="">Lister les futs</a><li> </tr>
</ul> </thead>
</section> <tbody>
<section id="third" class="main"> {% for product in products %}
<header class="major"> <tr>
<h2>Menus</h2> <td>{{ product.name }}</td>
</header> <td>{{ product.amount}}</td>
Actions possibles : <td>{{ product.stockHold }}</td>
<ul> <td>{{ product.stockBar }}</td>
<li><a href="{% url 'users:addAdmin' %}">Créer un menu</a></li> <td>{{ product.barcode }}</td>
<li><a href="">Rechercher un menu</a></li> <td>{{ product.category }}</td>
<li><a href="{% url 'users:adminsIndex' %}">Lister les menus</a></li> <td>{{ product.is_active }}</td>
</ul> <td>{{ product.degree }}</td>
</section> <td>{{ product.volume }}</td>
<section id="fourth" class="main"> <td></td>
<header class="major"> </tr>
<h2>Stocks</h2> {% endfor %}
</header> </tbody>
Actions possibles : </table>
<ul> </div>
<li><a href="{% url 'users:addSuperuser' %}">Voir les Stocks</a></li>
<li><a href="{% url 'users:superusersIndex' %}">Classement sur un produit</a></li>
</ul>
</section> </section>
{% endblock %} {% endblock %}

View file

@ -8,8 +8,15 @@ urlpatterns = [
path('reload', views.reload, name="reload"), path('reload', views.reload, name="reload"),
path('refund', views.refund, name="refund"), path('refund', views.refund, name="refund"),
path('productsIndex', views.productsIndex, name="productsIndex"), path('productsIndex', views.productsIndex, name="productsIndex"),
path('productsList', views.productsList, name="productsList"),
path('addProduct', views.addProduct, name="addProduct"), path('addProduct', views.addProduct, name="addProduct"),
path('addKeg', views.addKeg, name="addKeg"), path('addKeg', views.addKeg, name="addKeg"),
path('addMenu', views.addMenu, name="addMenu"), path('addMenu', views.addMenu, name="addMenu"),
path('getProduct/<str:barcode>', views.getProduct, name="getProduct"), path('getProduct/<str:barcode>', 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/<int:pk>', views.productProfile, name="productProfile"),
path('products-autocomplete', views.ProductsAutocomplete.as_view(), name="products-autocomplete"),
]

View file

@ -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.contrib import messages
from django.urls import reverse 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.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 dal import autocomplete
from decimal import *
from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm, SearchMenuForm, SearchProductForm
from .models import Product, Menu, Keg 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): def manage(request):
pay_buttons = PaymentMethod.objects.filter(is_active=True)
gestion_form = GestionForm(request.POST or None) gestion_form = GestionForm(request.POST or None)
reload_form = ReloadForm(request.POST or None) reload_form = ReloadForm(request.POST or None)
refund_form = RefundForm(request.POST or None) refund_form = RefundForm(request.POST or None)
@ -28,8 +38,37 @@ def manage(request):
bieresPression.append(keg.demi) bieresPression.append(keg.demi)
if(keg.galopin): if(keg.galopin):
bieresPression.append(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): def reload(request):
reload_form = ReloadForm(request.POST or None) reload_form = ReloadForm(request.POST or None)
if(reload_form.is_valid()): if(reload_form.is_valid()):
@ -45,6 +84,8 @@ def reload(request):
messages.error(request, "Le rechargement a échoué") messages.error(request, "Le rechargement a échoué")
return redirect(reverse('gestion:manage')) return redirect(reverse('gestion:manage'))
@login_required
@permission_required('gestion.add_refund')
def refund(request): def refund(request):
refund_form = RefundForm(request.POST or None) refund_form = RefundForm(request.POST or None)
if(refund_form.is_valid()): if(refund_form.is_valid()):
@ -63,9 +104,15 @@ def refund(request):
messages.error(request, "Le remboursement a échoué") messages.error(request, "Le remboursement a échoué")
return redirect(reverse('gestion:manage')) 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): def productsIndex(request):
return render(request, "gestion/products_index.html") return render(request, "gestion/products_index.html")
@login_required
@permission_required('gestion.add_product')
def addProduct(request): def addProduct(request):
form = ProductForm(request.POST or None) form = ProductForm(request.POST or None)
if(form.is_valid()): if(form.is_valid()):
@ -74,18 +121,43 @@ def addProduct(request):
return redirect(reverse('gestion:productsIndex')) return redirect(reverse('gestion:productsIndex'))
return render(request, "form.html", {"form": form, "form_title": "Ajout d'un produit", "form_button": "Ajouter"}) 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): def productsList(request):
products = Product.objects.all() products = Product.objects.all()
return render(request, "gestion/products_list.html", {"products": products}) 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): def getProduct(request, barcode):
product = Product.objects.get(barcode=barcode) product = Product.objects.get(barcode=barcode)
data = json.dumps({"pk": product.pk, "barcode" : product.barcode, "name": product.name, "amount" : 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') 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 ########## ########## Kegs ##########
@login_required
@permission_required('gestion.add_keg')
def addKeg(request): def addKeg(request):
form = KegForm(request.POST or None) form = KegForm(request.POST or None)
if(form.is_valid()): if(form.is_valid()):
@ -97,6 +169,8 @@ def addKeg(request):
########## Menus ########## ########## Menus ##########
@login_required
@permission_required('gestion.add_menu')
def addMenu(request): def addMenu(request):
form = MenuForm(request.POST or None) form = MenuForm(request.POST or None)
extra_css = "#id_articles{height:200px;}" extra_css = "#id_articles{height:200px;}"
@ -106,3 +180,53 @@ def addMenu(request):
return redirect(reverse('gestion:productsIndex')) 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}) 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")

View file

@ -1,4 +1,5 @@
from django import forms from django import forms
from django.core.exceptions import ValidationError
from .models import Cotisation, PaymentMethod, GeneralPreferences from .models import Cotisation, PaymentMethod, GeneralPreferences
@ -6,6 +7,14 @@ class CotisationForm(forms.ModelForm):
class Meta: class Meta:
model = Cotisation model = Cotisation
fields = "__all__" 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 PaymentMethodForm(forms.ModelForm):
class Meta: class Meta:

View file

@ -3,7 +3,8 @@ from django.db import models
class PaymentMethod(models.Model): class PaymentMethod(models.Model):
name = models.CharField(max_length=255, verbose_name="Nom") name = models.CharField(max_length=255, verbose_name="Nom")
is_active = models.BooleanField(default=True, verbose_name="Actif") 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") affect_balance = models.BooleanField(default=False, verbose_name="Affecte le solde")
def __str__(self): def __str__(self):

View file

@ -17,7 +17,8 @@
<tr> <tr>
<th>Nom</th> <th>Nom</th>
<th>Actif ?</th> <th>Actif ?</th>
<th>Utilisable dans les cotisations</th> <th>Cotisations ?</th>
<th>Rechargements ?</th>
<th>Affecte le solde</th> <th>Affecte le solde</th>
<th>Administration</th> <th>Administration</th>
</tr> </tr>
@ -28,6 +29,7 @@
<td>{{ pm.name }} </td> <td>{{ pm.name }} </td>
<td>{{ pm.is_active | yesno:"Oui, Non"}}</td> <td>{{ pm.is_active | yesno:"Oui, Non"}}</td>
<td>{{ pm.is_usable_in_cotisation | yesno:"Oui, Non" }}</td> <td>{{ pm.is_usable_in_cotisation | yesno:"Oui, Non" }}</td>
<td>{{ pm.is_usable_in_reload | yesno:"Oui, Non" }}</td>
<td>{{ pm.affect_balance | yesno:"Oui, Non" }}</td> <td>{{ pm.affect_balance | yesno:"Oui, Non" }}</td>
<td><a class="button small" href="{% url 'preferences:editPaymentMethod' pm.pk %}">Modifier</a> <a class="button small" href="{% url 'preferences:deletePaymentMethod' pm.pk %}">Supprimer</a></td> <td><a class="button small" href="{% url 'preferences:editPaymentMethod' pm.pk %}">Modifier</a> <a class="button small" href="{% url 'preferences:deletePaymentMethod' pm.pk %}">Supprimer</a></td>
</tr> </tr>

View file

@ -1,11 +1,17 @@
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages from django.contrib import messages
from django.urls import reverse from django.urls import reverse
from django.contrib.auth.decorators import login_required, permission_required
from coopeV3.acl import active_required
from .models import GeneralPreferences, Cotisation, PaymentMethod from .models import GeneralPreferences, Cotisation, PaymentMethod
from .forms import CotisationForm, PaymentMethodForm, GeneralPreferencesForm from .forms import CotisationForm, PaymentMethodForm, GeneralPreferencesForm
@active_required
@login_required
@permission_required('preferences.add_generalpreferences')
def generalPreferences(request): def generalPreferences(request):
gp,_ = GeneralPreferences.objects.get_or_create(pk=1) gp,_ = GeneralPreferences.objects.get_or_create(pk=1)
form = GeneralPreferencesForm(request.POST or None, instance=gp) form = GeneralPreferencesForm(request.POST or None, instance=gp)
@ -15,10 +21,16 @@ def generalPreferences(request):
########## Cotisations ########## ########## Cotisations ##########
@active_required
@login_required
@permission_required('preferences.view_cotisation')
def cotisationsIndex(request): def cotisationsIndex(request):
cotisations = Cotisation.objects.all() cotisations = Cotisation.objects.all()
return render(request, "preferences/cotisations_index.html", {"cotisations": cotisations}) return render(request, "preferences/cotisations_index.html", {"cotisations": cotisations})
@active_required
@login_required
@permission_required('preferences.add_cotisation')
def addCotisation(request): def addCotisation(request):
form = CotisationForm(request.POST or None) form = CotisationForm(request.POST or None)
if(form.is_valid()): if(form.is_valid()):
@ -27,6 +39,9 @@ def addCotisation(request):
return redirect(reverse('preferences:cotisationsIndex')) return redirect(reverse('preferences:cotisationsIndex'))
return render(request, "form.html", {"form": form, "form_title": "Création d'une cotisation", "form_button": "Créer"}) 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): def editCotisation(request, pk):
cotisation = get_object_or_404(Cotisation, pk=pk) cotisation = get_object_or_404(Cotisation, pk=pk)
form = CotisationForm(request.POST or None, instance=cotisation) form = CotisationForm(request.POST or None, instance=cotisation)
@ -36,6 +51,9 @@ def editCotisation(request, pk):
return redirect(reverse('preferences:cotisationsIndex')) return redirect(reverse('preferences:cotisationsIndex'))
return render(request, "form.html", {"form": form, "form_title": "Modification d'une cotisation", "form_button": "Modifier"}) 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): def deleteCotisation(request,pk):
cotisation = get_object_or_404(Cotisation, pk=pk) cotisation = get_object_or_404(Cotisation, pk=pk)
message = "La cotisation (" + str(cotisation.duration) + " jours, " + str(cotisation.amount) + "€) a bien été supprimée" 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 ########## ########## Payment Methods ##########
@active_required
@login_required
@permission_required('preferences.view_paymentmethod')
def paymentMethodsIndex(request): def paymentMethodsIndex(request):
paymentMethods = PaymentMethod.objects.all() paymentMethods = PaymentMethod.objects.all()
return render(request, "preferences/payment_methods_index.html", {"paymentMethods": paymentMethods}) return render(request, "preferences/payment_methods_index.html", {"paymentMethods": paymentMethods})
@active_required
@login_required
@permission_required('preferences.add_paymentmethod')
def addPaymentMethod(request): def addPaymentMethod(request):
form = PaymentMethodForm(request.POST or None) form = PaymentMethodForm(request.POST or None)
if(form.is_valid()): if(form.is_valid()):
@ -58,6 +82,9 @@ def addPaymentMethod(request):
return redirect(reverse('preferences:paymentMethodsIndex')) 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"}) 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): def editPaymentMethod(request, pk):
paymentMethod = get_object_or_404(PaymentMethod, pk=pk) paymentMethod = get_object_or_404(PaymentMethod, pk=pk)
form = PaymentMethodForm(request.POST or None, instance=paymentMethod) form = PaymentMethodForm(request.POST or None, instance=paymentMethod)
@ -67,9 +94,12 @@ def editPaymentMethod(request, pk):
return redirect(reverse('preferences:paymentMethodsIndex')) return redirect(reverse('preferences:paymentMethodsIndex'))
return render(request, "form.html", {"form": form, "form_title": "Modification d'un moyen de paiement", "form_button": "Modifier"}) 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): def deletePaymentMethod(request,pk):
paymentMethod = get_object_or_404(PaymentMethod, pk=pk) paymentMethod = get_object_or_404(PaymentMethod, pk=pk)
message = "Le moyen de paiement " + paymentMethod.name + " a bien été supprimé" message = "Le moyen de paiement " + paymentMethod.name + " a bien été supprimé"
paymentMethod.delete() paymentMethod.delete()
messages.success(request, message) messages.success(request, message)
return redirect(reverse('preferences:paymentMethodsIndex')) return redirect(reverse('preferences:paymentMethodsIndex'))

View file

@ -1,3 +1,5 @@
Django==2.1 Django==2.1
django-autocomplete-light==3.3.2 django-autocomplete-light==3.3.2
pytz==2018.5 pytz==2018.5
simplejson==3.16.0
docutils==0.14

View file

@ -1,4 +1,4 @@
totalAmount = 0 total = 0
products = [] products = []
paymentMethod = null paymentMethod = null
balance = 0 balance = 0
@ -64,4 +64,16 @@ $(document).ready(function(){
window.location.reload() 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();
});
});
}); });

View file

@ -1,12 +1,7 @@
{% load vip %} {% load vip %}
<section> <section>
<h2>A propos</h2> <h2>A propos</h2>
<p>{% lorem %}</p> <p>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).</p>
<ul class="actions">
<li>
<a class="button" href="">En savoir plus</a>
</li>
</ul>
</section> </section>
<section> <section>
<h2>Contacts</h2> <h2>Contacts</h2>

View file

@ -12,13 +12,13 @@
<a href="{% url 'gestion:productsIndex' %}">Gestion des produits</a> <a href="{% url 'gestion:productsIndex' %}">Gestion des produits</a>
</span> </span>
<span class="tabulation2"> <span class="tabulation2">
<a href="">Comptabilité</a> <a href="{% url 'gestion:annualRanking' %}">Comptabilité</a>
</span> </span>
<span class="tabulation2"> <span class="tabulation2">
<a href="">Classement</a> <a href="{% url 'gestion:ranking' %}">Classement</a>
</span> </span>
<span class="tabulation2"> <span class="tabulation2">
<a href="">Classement sur l'année</a> <a href="{% url 'gestion:annualRanking' %}">Classement sur l'année</a>
</span> </span>
<span class="tabulation2"> <span class="tabulation2">
<a href="{% url 'preferences:generalPreferences' %}">Admin</a> <a href="{% url 'preferences:generalPreferences' %}">Admin</a>

View file

@ -1,8 +1,9 @@
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.models import Permission
from .models import School, Profile, CotisationHistory from .models import School, Profile, CotisationHistory
admin.site.register(Permission)
admin.site.register(School) admin.site.register(School)
admin.site.register(Profile) admin.site.register(Profile)
admin.site.register(CotisationHistory) admin.site.register(CotisationHistory)
# Register your models here.

View file

@ -25,14 +25,13 @@ class EditGroupForm(forms.ModelForm):
fields = "__all__" fields = "__all__"
class SelectUserForm(forms.Form): 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): 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})) 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): 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 GroupsEditForm(forms.ModelForm):
class Meta: class Meta:

View file

@ -4,6 +4,7 @@ from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from preferences.models import PaymentMethod, Cotisation from preferences.models import PaymentMethod, Cotisation
from gestion.models import ConsumptionHistory
class School(models.Model): class School(models.Model):
name = models.CharField(max_length=255, verbose_name="Nom") name = models.CharField(max_length=255, verbose_name="Nom")
@ -12,6 +13,11 @@ class School(models.Model):
return self.name return self.name
class CotisationHistory(models.Model): class CotisationHistory(models.Model):
class Meta:
permissions = (
("validate_consumptionhistory", "Peut (in)valider les cotisations"),
)
WAITING = 0 WAITING = 0
VALID = 1 VALID = 1
INVALID = 2 INVALID = 2
@ -57,12 +63,12 @@ class Profile(models.Model):
@property @property
def alcohol(self): def alcohol(self):
#consos = Consommation.objects.filter(client=self).select_related('produit') consumptions = ConsumptionHistory.objects.filter(customer=self.user).select_related('product')
#alcool = 0 alcohol = 0
#for conso in consos: for consumption in consumptions:
#produit = conso.produit product = consumption.product
#alcool += conso.nombre * float(produit.deg) * produit.volume * 0.79 /10 /1000 alcohol += consumption.quantity * float(product.deg) * product.volume * 0.79 /10 /1000
return 0 return alcohol
def __str__(self): def __str__(self):
return str(self.user) return str(self.user)
@ -74,4 +80,9 @@ def create_user_profile(sender, instance, created, **kwargs):
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs): def save_user_profile(sender, instance, **kwargs):
instance.profile.save() 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)

View file

@ -10,6 +10,7 @@
<header class="major"> <header class="major">
<h2>Liste des groupes de droit</h2> <h2>Liste des groupes de droit</h2>
</header> </header>
<a href="{% url 'users:createGroup' %}" class="button">Ajouter un groupe de droit</a><br><br>
<div class="table-wrapper"> <div class="table-wrapper">
<table> <table>
<thead> <thead>

View file

@ -130,13 +130,13 @@
</tr> </tr>
</thead> </thead>
<tbody id="bodyRechargement"> <tbody id="bodyRechargement">
{%for reload in lastReloads%} {% for reload in reloads %}
<tr> <tr>
<th>{{reload.amount}}</th> <th>{{reload.amount}}</th>
<th>{{reload.paymentMethod}}</th> <th>{{reload.PaymentMethod}}</th>
<th>{{reload.date}}</th> <th>{{reload.date}}</th>
</tr> </tr>
{%endfor%} {% endfor %}
</tbody> </tbody>
</table> </table>
</div> </div>
@ -208,11 +208,4 @@
</div> </div>
</section> </section>
</section> </section>
{%endblock%}
{%endblock%}
{%block addScript %}
<script src="{%static 'js/canvasjs.min.js'%}"></script>
<script src="{%static 'js/profil.js'%}"></script>
{%endblock%}

View file

@ -30,6 +30,7 @@ urlpatterns = [
path('all-users-autocomplete', views.AllUsersAutocomplete.as_view(), name="all-users-autocomplete"), 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('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-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/<int:pk>', views.getUser, name="getUser"), path('getUser/<int:pk>', views.getUser, name="getUser"),
path('addCotisationHistory/<int:pk>', views.addCotisationHistory, name="addCotisationHistory"), path('addCotisationHistory/<int:pk>', views.addCotisationHistory, name="addCotisationHistory"),
path('validateCotisationHistory/<int:pk>', views.validateCotisationHistory, name="validateCotisationHistory"), path('validateCotisationHistory/<int:pk>', views.validateCotisationHistory, name="validateCotisationHistory"),
@ -39,4 +40,5 @@ urlpatterns = [
path('createSchool', views.createSchool, name="createSchool"), path('createSchool', views.createSchool, name="createSchool"),
path('editSchool/<int:pk>', views.editSchool, name="editSchool"), path('editSchool/<int:pk>', views.editSchool, name="editSchool"),
path('deleteSchool/<int:pk>', views.deleteSchool, name="deleteSchool"), path('deleteSchool/<int:pk>', views.deleteSchool, name="deleteSchool"),
path('allReloads/<int:pk>/<int:page>', views.allReloads, name="allReloads"),
] ]

View file

@ -5,16 +5,38 @@ from django.contrib.auth import authenticate, login, logout
from django.contrib import messages from django.contrib import messages
from django.db.models import Q from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect 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 datetime import datetime, timedelta
from dal import autocomplete 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 .models import CotisationHistory, WhiteListHistory, School
from .forms import CreateUserForm, LoginForm, CreateGroupForm, EditGroupForm, SelectUserForm, GroupsEditForm, EditPasswordForm, addCotisationHistoryForm, addCotisationHistoryForm, addWhiteListHistoryForm, SelectNonAdminUserForm, SelectNonSuperUserForm, SchoolForm 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): 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) form = LoginForm(request.POST or None)
if(form.is_valid()): if(form.is_valid()):
user = authenticate(username=form.cleaned_data['username'], password=form.cleaned_data['password']) 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") 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"}) return render(request, "form.html", {"form_entete": "Connexion", "form": form, "form_title": "Connexion", "form_button": "Se connecter"})
@active_required
@login_required
def logoutView(request): def logoutView(request):
"""
Logout the logged user
"""
logout(request) logout(request)
messages.success(request, "Vous êtes à présent déconnecté") messages.success(request, "Vous êtes à présent déconnecté")
return redirect(reverse('home')) return redirect(reverse('home'))
@active_required
@login_required
@permission_required('auth.view_user')
def index(request): 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 ########## ########## users ##########
@active_required
@login_required
@self_or_has_perm('pk', 'auth.view_user')
def profile(request, pk): 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) user = get_object_or_404(User, pk=pk)
self = request.user == user self = request.user == user
cotisations = CotisationHistory.objects.filter(user=user) cotisations = CotisationHistory.objects.filter(user=user)
whitelists = WhiteListHistory.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): 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) form = CreateUserForm(request.POST or None)
if(form.is_valid()): if(form.is_valid()):
user = form.save(commit=False) user = form.save(commit=False)
@ -58,17 +145,77 @@ def createUser(request):
user.save() 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"}) 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): 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) form = SelectUserForm(request.POST or None)
if(form.is_valid()): if(form.is_valid()):
return redirect(reverse('users:profile', kwargs={"pk":form.cleaned_data['user'].pk})) 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"}) 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): 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() users = User.objects.all()
return render(request, "users/users_index.html", {"users":users}) return render(request, "users/users_index.html", {"users":users})
@active_required
@login_required
@permission_required('auth.change_user')
def editGroups(request, pk): 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) user = get_object_or_404(User, pk=pk)
form = GroupsEditForm(request.POST or None, instance=user) form = GroupsEditForm(request.POST or None, instance=user)
if(form.is_valid()): if(form.is_valid()):
@ -78,7 +225,30 @@ def editGroups(request, pk):
extra_css = "#id_groups{height:200px;}" 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}) 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): 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) user = get_object_or_404(User, pk=pk)
if user != request.user: if user != request.user:
messages.error(request, "Vous ne pouvez modifier le mot de passe d'un autre utilisateur") 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") 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"}) 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): 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) user = get_object_or_404(User, pk=pk)
form = CreateUserForm(request.POST or None, instance=user, initial = {'school': user.profile.school}) form = CreateUserForm(request.POST or None, instance=user, initial = {'school': user.profile.school})
if(form.is_valid()): if(form.is_valid()):
@ -105,7 +299,17 @@ def editUser(request, pk):
return redirect(reverse('users:profile', kwargs={'pk': 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"}) 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): def resetPassword(request, pk):
"""
Reset the password of a user.
``pk``
The pk of the user
"""
user = get_object_or_404(User, pk=pk) user = get_object_or_404(User, pk=pk)
if user.is_superuser: if user.is_superuser:
messages.error(request, "Impossible de réinitialiser le mot de passe de " + user.username + " : il est 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é.") messages.success(request, "Le mot de passe de " + user.username + " a bien été réinitialisé.")
return redirect(reverse('users:profile', kwargs={'pk': pk})) return redirect(reverse('users:profile', kwargs={'pk': pk}))
@active_required
@login_required
@permission_required('auth.view_user')
def getUser(request, pk): 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) 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') 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 ########## ########## Groups ##########
@active_required
@login_required
@permission_required('auth.view_group')
def groupsIndex(request): def groupsIndex(request):
"""
Display all the groups.
**Context**
``groups``
List of all groups.
**Template**
:template:`users/groups_index.html`
"""
groups = Group.objects.all() groups = Group.objects.all()
return render(request, "users/groups_index.html", {"groups": groups}) return render(request, "users/groups_index.html", {"groups": groups})
@active_required
@login_required
@permission_required('auth.view_group')
def groupProfile(request, pk): 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) group = get_object_or_404(Group, pk=pk)
return render(request, "users/group_profile.html", {"group": group}) return render(request, "users/group_profile.html", {"group": group})
@active_required
@login_required
@permission_required('auth.add_group')
def createGroup(request): 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) form = CreateGroupForm(request.POST or None)
if(form.is_valid()): if(form.is_valid()):
group = form.save() group = form.save()
@ -139,7 +435,31 @@ def createGroup(request):
return redirect(reverse('users:groupProfile', kwargs={'pk': group.pk})) 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"}) 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): 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) group = get_object_or_404(Group, pk=pk)
form = EditGroupForm(request.POST or None, instance=group) form = EditGroupForm(request.POST or None, instance=group)
extra_css = "#id_permissions{height:200px;}" extra_css = "#id_permissions{height:200px;}"
@ -149,7 +469,17 @@ def editGroup(request, pk):
return redirect(reverse('users:groupProfile', kwargs={'pk': group.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}) 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): def deleteGroup(request, pk):
"""
Delete the requested group.
``pk``
The pk of the group
"""
group = get_object_or_404(Group, pk=pk) group = get_object_or_404(Group, pk=pk)
if group.user_set.count() == 0: if group.user_set.count() == 0:
name = group.name 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") 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})) return redirect(reverse('users:groupProfile', kwargs={'pk': group.pk}))
@active_required
@login_required
@permission_required('auth.change_group')
def removeRight(request, groupPk, permissionPk): 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) group = get_object_or_404(Group, pk=groupPk)
perm = get_object_or_404(Permission, pk=permissionPk) perm = get_object_or_404(Permission, pk=permissionPk)
if perm in group.permissions.all(): 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) messages.error(request, "Impossible de retirer la permission " + perm.codename + " du groupe " + group.name)
return redirect(reverse('users:groupProfile', kwargs={'pk': groupPk}) + "#second") return redirect(reverse('users:groupProfile', kwargs={'pk': groupPk}) + "#second")
@active_required
@login_required
@permission_required('auth.change_user')
def removeUser(request, groupPk, userPk): 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) group = get_object_or_404(Group, pk=groupPk)
user = get_object_or_404(User, pk=userPk) user = get_object_or_404(User, pk=userPk)
if(group in user.groups.all()): if(group in user.groups.all()):
@ -182,10 +538,16 @@ def removeUser(request, groupPk, userPk):
########## admins ########## ########## admins ##########
@active_required
@login_required
@admin_required
def adminsIndex(request): def adminsIndex(request):
admins = User.objects.filter(is_staff=True) admins = User.objects.filter(is_staff=True)
return render(request, "users/admins_index.html", {"admins": admins}) return render(request, "users/admins_index.html", {"admins": admins})
@active_required
@login_required
@admin_required
def addAdmin(request): def addAdmin(request):
form = SelectNonAdminUserForm(request.POST or None) form = SelectNonAdminUserForm(request.POST or None)
if(form.is_valid()): if(form.is_valid()):
@ -196,6 +558,9 @@ def addAdmin(request):
return redirect(reverse('users:adminsIndex')) 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_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): def removeAdmin(request, pk):
user = get_object_or_404(User, pk=pk) user = get_object_or_404(User, pk=pk)
if user.is_staff: if user.is_staff:
@ -214,10 +579,16 @@ def removeAdmin(request, pk):
########## superusers ########## ########## superusers ##########
@active_required
@login_required
@superuser_required
def superusersIndex(request): def superusersIndex(request):
superusers = User.objects.filter(is_superuser=True) superusers = User.objects.filter(is_superuser=True)
return render(request, "users/superusers_index.html", {"superusers": superusers}) return render(request, "users/superusers_index.html", {"superusers": superusers})
@active_required
@login_required
@superuser_required
def addSuperuser(request): def addSuperuser(request):
form = SelectNonSuperUserForm(request.POST or None) form = SelectNonSuperUserForm(request.POST or None)
if(form.is_valid()): if(form.is_valid()):
@ -229,6 +600,9 @@ def addSuperuser(request):
return redirect(reverse('users:superusersIndex')) 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"}) 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): def removeSuperuser(request, pk):
user = get_object_or_404(User, pk=pk) user = get_object_or_404(User, pk=pk)
if user.is_superuser: if user.is_superuser:
@ -244,11 +618,20 @@ def removeSuperuser(request, pk):
########## Cotisations ########## ########## Cotisations ##########
@active_required
@login_required
@permission_required('users.add_cotisationhistory')
def addCotisationHistory(request, pk): def addCotisationHistory(request, pk):
user = get_object_or_404(User, pk=pk) user = get_object_or_404(User, pk=pk)
form = addCotisationHistoryForm(request.POST or None) form = addCotisationHistoryForm(request.POST or None)
if(form.is_valid()): if(form.is_valid()):
cotisation = form.save(commit=False) 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.user = user
cotisation.coopeman = request.user cotisation.coopeman = request.user
cotisation.amount = cotisation.cotisation.amount cotisation.amount = cotisation.cotisation.amount
@ -264,6 +647,9 @@ def addCotisationHistory(request, pk):
return redirect(reverse('users:profile',kwargs={'pk':user.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"}) 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): def validateCotisationHistory(request, pk):
cotisationHistory = get_object_or_404(CotisationHistory, pk=pk) cotisationHistory = get_object_or_404(CotisationHistory, pk=pk)
cotisationHistory.valid = CotisationHistory.VALID cotisationHistory.valid = CotisationHistory.VALID
@ -271,18 +657,26 @@ def validateCotisationHistory(request, pk):
messages.success(request, "La cotisation a bien été validée") messages.success(request, "La cotisation a bien été validée")
return HttpResponseRedirect(request.META.get('HTTP_REFERER')) return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
@active_required
@login_required
@permission_required('users.validate_consumptionhistory')
def invalidateCotisationHistory(request, pk): def invalidateCotisationHistory(request, pk):
cotisationHistory = get_object_or_404(CotisationHistory, pk=pk) cotisationHistory = get_object_or_404(CotisationHistory, pk=pk)
cotisationHistory.valid = CotisationHistory.INVALID cotisationHistory.valid = CotisationHistory.INVALID
cotisationHistory.save() cotisationHistory.save()
user = cotisationHistory.user user = cotisationHistory.user
user.profile.cotisationEnd = user.profile.cotisationEnd - timedelta(days=cotisationHistory.duration) user.profile.cotisationEnd = user.profile.cotisationEnd - timedelta(days=cotisationHistory.duration)
if(cotisationHistory.paymentMethod.affect_balance):
user.profile.balance += cotisation.amount
user.save() user.save()
messages.success(request, "La cotisation a bien été invalidée") messages.success(request, "La cotisation a bien été invalidée")
return HttpResponseRedirect(request.META.get('HTTP_REFERER')) return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
########## Whitelist ########## ########## Whitelist ##########
@active_required
@login_required
@permission_required('users.add_whitelisthistory')
def addWhiteListHistory(request, pk): def addWhiteListHistory(request, pk):
user = get_object_or_404(User, pk=pk) user = get_object_or_404(User, pk=pk)
form = addWhiteListHistoryForm(request.POST or None) form = addWhiteListHistoryForm(request.POST or None)
@ -303,10 +697,16 @@ def addWhiteListHistory(request, pk):
########## Schools ########## ########## Schools ##########
@active_required
@login_required
@permission_required('users.view_school')
def schoolsIndex(request): def schoolsIndex(request):
schools = School.objects.all() schools = School.objects.all()
return render(request, "users/schools_index.html", {"schools": schools}) return render(request, "users/schools_index.html", {"schools": schools})
@active_required
@login_required
@permission_required('users.add_school')
def createSchool(request): def createSchool(request):
form = SchoolForm(request.POST or None) form = SchoolForm(request.POST or None)
if(form.is_valid()): if(form.is_valid()):
@ -315,6 +715,9 @@ def createSchool(request):
return redirect(reverse('users:schoolsIndex')) return redirect(reverse('users:schoolsIndex'))
return render(request, "form.html", {"form": form, "form_title": "Création d'une école", "form_button": "Créer"}) 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): def editSchool(request, pk):
school = get_object_or_404(School, pk=pk) school = get_object_or_404(School, pk=pk)
form = SchoolForm(request.POST or None, instance=school) form = SchoolForm(request.POST or None, instance=school)
@ -324,6 +727,9 @@ def editSchool(request, pk):
return redirect(reverse('users:schoolsIndex')) return redirect(reverse('users:schoolsIndex'))
return render(request, "form.html", {"form": form, "form_title": "Modification de l'école " + str(school), "form_button": "Modifier"}) 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): def deleteSchool(request, pk):
school = get_object_or_404(School, pk=pk) school = get_object_or_404(School, pk=pk)
message = "L'école " + str(school) + " a bien été supprimée" message = "L'école " + str(school) + " a bien été supprimée"
@ -355,6 +761,13 @@ class AdherentAutocomplete(autocomplete.Select2QuerySetView):
class NonSuperUserAutocomplete(autocomplete.Select2QuerySetView): class NonSuperUserAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self): def get_queryset(self):
qs = User.objects.filter(is_superuser=False) 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: if self.q:
qs = qs.filter(Q(username__istartswith=self.q) | Q(first_name__istartswith=self.q) | Q(last_name__istartswith=self.q)) qs = qs.filter(Q(username__istartswith=self.q) | Q(first_name__istartswith=self.q) | Q(last_name__istartswith=self.q))
return qs return qs