From 84b7e5281c1108171052906df5963668e6227cae Mon Sep 17 00:00:00 2001 From: nanoy Date: Sun, 6 Jan 2019 00:01:30 +0100 Subject: [PATCH] =?UTF-8?q?Comptabilit=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coopeV3/settings.py | 9 +++ gestion/environment.py | 12 +++ gestion/forms.py | 6 +- gestion/templates/gestion/releve.tex | 71 ++++++++++++++++++ gestion/urls.py | 1 + gestion/views.py | 106 ++++++++++++++++++++------- requirements.txt | 3 +- templates/nav.html | 5 ++ users/models.py | 3 +- 9 files changed, 186 insertions(+), 30 deletions(-) create mode 100644 gestion/environment.py create mode 100644 gestion/templates/gestion/releve.tex diff --git a/coopeV3/settings.py b/coopeV3/settings.py index 20184c9..cea493e 100644 --- a/coopeV3/settings.py +++ b/coopeV3/settings.py @@ -38,6 +38,7 @@ INSTALLED_APPS = [ 'dal', 'dal_select2', 'simple_history', + 'django_tex', ] MIDDLEWARE = [ @@ -68,6 +69,14 @@ TEMPLATES = [ ], }, }, + { + 'NAME': 'tex', + 'BACKEND': 'django_tex.engine.TeXEngine', + 'APP_DIRS': True, + 'OPTIONS': { + 'environment': 'gestion.environment.my_environment', + } + }, ] WSGI_APPLICATION = 'coopeV3.wsgi.application' diff --git a/gestion/environment.py b/gestion/environment.py new file mode 100644 index 0000000..02dc72e --- /dev/null +++ b/gestion/environment.py @@ -0,0 +1,12 @@ +from django_tex.environment import environment + +def latex_safe(value): + return str(value).replace('_', '\_').replace('$', '\$').replace('&', '\&').replace('#', '\#').replace('{', '\{').replace('}','\}') + + +def my_environment(**options): + env = environment(**options) + env.filters.update({ + 'latex_safe': latex_safe + }) + return env \ No newline at end of file diff --git a/gestion/forms.py b/gestion/forms.py index 01079a4..34d18da 100644 --- a/gestion/forms.py +++ b/gestion/forms.py @@ -66,4 +66,8 @@ class SelectActiveKegForm(forms.Form): class PinteForm(forms.Form): ids = forms.CharField(widget=forms.Textarea, label="Numéros", help_text="Numéros séparés par un espace. Laissez vide pour utiliser le range.", required=False) begin = forms.IntegerField(label="Début", help_text="Début du range", required=False) - end = forms.IntegerField(label="Fin", help_text="Fin du range", required=False) \ No newline at end of file + end = forms.IntegerField(label="Fin", help_text="Fin du range", required=False) + +class GenerateReleveForm(forms.Form): + begin = forms.DateTimeField(label="Date de début") + end = forms.DateTimeField(label="Date de fin") \ No newline at end of file diff --git a/gestion/templates/gestion/releve.tex b/gestion/templates/gestion/releve.tex new file mode 100644 index 0000000..f759642 --- /dev/null +++ b/gestion/templates/gestion/releve.tex @@ -0,0 +1,71 @@ +\documentclass[11pt,a4paper]{article} +\usepackage[utf8]{inputenc} +\usepackage[french]{babel} +\usepackage[T1]{fontenc} +\usepackage{amsmath} +\usepackage{amsfonts} +\usepackage{amssymb} +\usepackage{graphicx} +\usepackage{eurosym} +\usepackage[left=2cm,right=2cm,top=2cm,bottom=2cm]{geometry} +\usepackage{tabularx} +\usepackage{longtable} +\usepackage{tabu} +\author{Généré par CoopeV3} +\title{Relevé Coopé Technopôle Metz} +\begin{document} +\maketitle +\section{Informations générales} +\begin{longtabu}{|X|X|X|} +\hline +\multicolumn{2}{|c|}{Généré le } & \textbf{ {{- now | date('d/m/Y H:i:s') -}} }\\ +\hline +Infos & De & \textbf{ {{- begin | date('d/m/Y H:i:s')-}} } \\ +\cline{2-3} & À & \textbf{ {{- end | date('d/m/Y H:i:s') -}} }\\ +\hline +Estimations & Espèces & \textbf{ {{- value_especes | latex_safe -}} \euro{}} \\ +\cline{2-3} & Lydia & \textbf{ {{- value_lydia | latex_safe -}} \euro{}} \\ +\cline{2-3} & Chèques & \textbf{ {{- value_cheque | latex_safe -}} \euro{}} \\ +\hline +\end{longtabu} +\section{Transactions} +\begin{longtabu}{|c|X|X|X|X|X|} +\hline +\# & Date & Client & Montant & Moyen de paiement & Produit (Qté) \\ +\hline +{% for consumption in consumptions %} +{{consumption.pk}} & {{consumption.date | date('d/m/Y H:i:s')}} & {{consumption.customer.first_name|latex_safe}} {{consumption.customer.last_name|latex_safe}} & {{consumption.amount}} \euro{} & {{consumption.paymentMethod}} & {{consumption.product}} (x{{consumption.quantity}})\\ +\hline +{% endfor %} +\end{longtabu} +\section{Rechargements} +\begin{longtabu}{|c|X|X|X|X|} +\hline +\# & Date & Client & Montant & Moyen de paiement \\ +\hline +{% for reload in reloads %} +{{reload.pk}} & {{ reload.date | date('d/m/Y H:i:s')}} & {{reload.customer.first_name | latex_safe}} {{reload.customer.last_name | latex_safe}} & {{ reload.amount }} \euro{} & {{reload.PaymentMethod}} \\ +\hline +{% endfor %} +\end{longtabu} +\section{Remboursement} +\begin{longtabu}{|c|X|X|X|} +\hline +\# & Date & Client & Montant\\ +\hline +{% for refund in refunds %} +{{refund.pk}} & {{ refund.date | date('d/m/Y H:i:s')}} & {{refund.customer.first_name|latex_safe}} {{refund.customer.last_name|latex_safe}} & {{ refund.amount }} \euro{}\\ +\hline +{% endfor %} +\end{longtabu} +\section{Cotisations} +\begin{longtabu}{|c|X|X|X|X|X|} +\hline +\# & Date & Client & Montant & Durée & Moyen de paiement \\ +\hline +{% for cot in cotisations %} +{{cot.pk}} & {{ cot.paymentDate | date('d/m/Y H:i:s')}} & {{cot.user.first_name|latex_safe}} {{cot.user.last_name|latex_safe}} & {{cot.amount}} \euro{} & {{cot.duration}} jours & {{cot.paymentMethod}} \\ +\hline +{% endfor %} +\end{longtabu} +\end{document} \ No newline at end of file diff --git a/gestion/urls.py b/gestion/urls.py index dd18cbc..538a7d4 100644 --- a/gestion/urls.py +++ b/gestion/urls.py @@ -44,4 +44,5 @@ urlpatterns = [ path('kegs-active-autocomplete', views.KegActiveAutocomplete.as_view(), name="kegs-active-autocomplete"), path('menus-autcomplete', views.MenusAutocomplete.as_view(), name="menus-autocomplete"), path('cancelReload/', views.cancel_reload, name="cancelReload"), + path('gen_releve', views.gen_releve, name="gen_releve"), ] \ No newline at end of file diff --git a/gestion/views.py b/gestion/views.py index 48a0e67..3ca3654 100644 --- a/gestion/views.py +++ b/gestion/views.py @@ -8,15 +8,19 @@ from django.contrib.auth.decorators import login_required, permission_required from django.utils import timezone from django.http import HttpResponseRedirect -from coopeV3.acl import active_required, acl_or +from django_tex.views import render_to_pdf + +from coopeV3.acl import active_required, acl_or, admin_required import simplejson as json from dal import autocomplete from decimal import * +import datetime -from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm, SearchMenuForm, SearchProductForm, SelectPositiveKegForm, SelectActiveKegForm, PinteForm -from .models import Product, Menu, Keg, ConsumptionHistory, KegHistory, Consumption, MenuHistory, Pinte, Reload +from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm, SearchMenuForm, SearchProductForm, SelectPositiveKegForm, SelectActiveKegForm, PinteForm, GenerateReleveForm +from .models import Product, Menu, Keg, ConsumptionHistory, KegHistory, Consumption, MenuHistory, Pinte, Reload, Refund from preferences.models import PaymentMethod, GeneralPreferences +from users.models import CotisationHistory @active_required @login_required @@ -26,10 +30,10 @@ def manage(request): Display the manage page **Context** - + ``gestion_form`` The manage form - + ``reload_form`` The :model:`gestion.Reload` form @@ -44,13 +48,13 @@ def manage(request): ``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` @@ -288,7 +292,7 @@ def cancel_menu(request, pk): for product in manu_history.menu.articles: consumptionT = Consumption.objects.get(customer=user, product=product) consumptionT -= menu_history.quantity - consumptionT.save() + consumptionT.save() menu_history.delete() messages.success(request, "La consommation du menu a bien été annulée") return redirect(reverse('users:profile', kwargs={'pk': user.pk})) @@ -318,7 +322,7 @@ def addProduct(request): ``form`` The ProductForm instance - + ``form_title`` The title for the form template @@ -350,7 +354,7 @@ def editProduct(request, pk): ``form`` The ProductForm instance - + ``form_title`` The title for the form template @@ -399,7 +403,7 @@ def searchProduct(request): ``form`` The SearchProductForm instance - + ``form_title`` The title for the form template @@ -426,7 +430,7 @@ def productProfile(request, pk): The primary key of the requested :model:`gestion.Product` **Context** - + ``product`` The :model:`gestion.Product` instance @@ -500,10 +504,10 @@ 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 @@ -532,10 +536,10 @@ def editKeg(request, 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 @@ -562,10 +566,10 @@ 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 @@ -628,10 +632,10 @@ 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 @@ -716,7 +720,7 @@ def kegH(request, pk): ``keg`` The :model:`gestion.Keg` instance - + ``kegHistory`` List of :model:`gestion.KegHistory` attached to keg @@ -758,10 +762,10 @@ 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 @@ -791,10 +795,10 @@ def edit_menu(request, pk): The primary key of requested :model:`gestion.Menu` **Context** - + ``form`` The MenuForm instance - + ``form_title`` The title for the :template:`form.html` template @@ -962,7 +966,7 @@ def release(request, pinte_pk): else: messages.error(request, "Impossible de libérer la pinte") return redirect(reverse('gestion:pintesList')) - + @active_required @login_required @permission_required('gestion.add_pinte') @@ -1017,4 +1021,52 @@ def pintes_list(request): def pintes_user_list(request): pks = [x.pk for x in User.objects.all() if x.profile.nb_pintes > 0] users = User.objects.filter(pk__in=pks) - return render(request, "gestion/pintes_user_list.html", {"users": users}) \ No newline at end of file + return render(request, "gestion/pintes_user_list.html", {"users": users}) + +@active_required +@login_required +@admin_required +def gen_releve(request): + form = GenerateReleveForm(request.POST or None) + if form.is_valid(): + begin, end = form.cleaned_data['begin'], form.cleaned_data['end'] + consumptions = ConsumptionHistory.objects.filter(date__gte=begin).filter(date__lte=end).order_by('-date') + reloads = Reload.objects.filter(date__gt=begin).filter(date__lt=end).order_by('-date') + refunds = Refund.objects.filter(date__gt=begin).filter(date__lt=end).order_by('-date') + cotisations = CotisationHistory.objects.filter(paymentDate__gt=begin).filter(paymentDate__lt=end).order_by('-paymentDate') + especes = PaymentMethod.objects.get(name="Espèces") + lydia = PaymentMethod.objects.get(name="Lydia") + cheque = PaymentMethod.objects.get(name="Chèque") + value_especes = 0 + value_lydia = 0 + value_cheque = 0 + for consumption in consumptions: + pm = consumption.paymentMethod + if pm == especes: + value_especes += consumption.amount + elif pm == lydia: + value_lydia += consumption.amount + elif pm == cheque: + value_cheque += consumption.amount + for reload in reloads: + pm = reload.PaymentMethod + if pm == especes: + value_especes += reload.amount + elif pm == lydia: + value_lydia += reload.amount + elif pm == cheque: + value_cheque += reload.amount + for refund in refunds: + value_especes -= refund.amount + for cot in cotisations: + pm = cot.paymentMethod + if pm == especes: + value_especes += cot.amount + elif pm == lydia: + value_lydia += cot.amount + elif pm == cheque: + value_cheque += cot.amount + now = datetime.datetime.now() + return render_to_pdf(request, 'gestion/releve.tex', {"consumptions": consumptions, "reloads": reloads, "refunds": refunds, "cotisations": cotisations, "begin": begin, "end": end, "now": now, "value_especes": value_especes, "value_lydia": value_lydia, "value_cheque": value_cheque}, filename="releve.pdf") + else: + return render(request, "form.html", {"form": form, "form_title": "Génération d'un relevé", "form_button": "Générer"}) diff --git a/requirements.txt b/requirements.txt index 543b013..31485ac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ django-autocomplete-light==3.3.2 pytz==2018.5 simplejson==3.16.0 docutils==0.14 -django-simple-history==2.5.1 \ No newline at end of file +django-simple-history==2.5.1 +jinja2==2.10 \ No newline at end of file diff --git a/templates/nav.html b/templates/nav.html index eb8ffed..6a644d2 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -25,6 +25,11 @@ Admin {% endif %} +{% if request.user.is_staff %} + + Comptabilité + +{% endif %} {% if perms.preferences.view_cotisation %} Cotisations diff --git a/users/models.py b/users/models.py index c3b0df9..b586df7 100644 --- a/users/models.py +++ b/users/models.py @@ -169,4 +169,5 @@ def str_user(self): 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) + +User.add_to_class("__str__", str_user) \ No newline at end of file