From 8c44b4edc9310de00899a32812938ede69b72efe Mon Sep 17 00:00:00 2001 From: nanoy Date: Sun, 23 Jun 2019 13:46:12 +0200 Subject: [PATCH] Generate invoice --- django_tex/core.py | 1 + gestion/forms.py | 16 ++- gestion/templates/gestion/invoice.tex | 100 ++++++++++++++++++ gestion/urls.py | 1 + gestion/views.py | 39 ++++++- .../templates/users => templates}/coope.png | Bin templates/nav.html | 5 + users/models.py | 1 + users/views.py | 2 +- 9 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 gestion/templates/gestion/invoice.tex rename {users/templates/users => templates}/coope.png (100%) diff --git a/django_tex/core.py b/django_tex/core.py index 6d67d5f..22fdacb 100644 --- a/django_tex/core.py +++ b/django_tex/core.py @@ -18,6 +18,7 @@ def run_tex(source): filename = os.path.join(tempdir, 'texput.tex') with open(filename, 'x', encoding='utf-8') as f: f.write(source) + print(source) latex_interpreter = getattr(settings, 'LATEX_INTERPRETER', DEFAULT_INTERPRETER) latex_command = 'cd "{tempdir}" && {latex_interpreter} -interaction=batchmode {path}'.format(tempdir=tempdir, latex_interpreter=latex_interpreter, path=os.path.basename(filename)) process = run(latex_command, shell=True, stdout=PIPE, stderr=PIPE) diff --git a/gestion/forms.py b/gestion/forms.py index 1d0a56f..31bfcf6 100644 --- a/gestion/forms.py +++ b/gestion/forms.py @@ -122,4 +122,18 @@ class SearchCategoryForm(forms.Form): """ A form to search a :class:`~gestion.models.Category`. """ - category = forms.ModelChoiceField(queryset=Category.objects.all(), required=True, label="Catégorie", widget=autocomplete.ModelSelect2(url='gestion:categories-autocomplete', attrs={'data-minimum-input-length':2})) \ No newline at end of file + category = forms.ModelChoiceField(queryset=Category.objects.all(), required=True, label="Catégorie", widget=autocomplete.ModelSelect2(url='gestion:categories-autocomplete', attrs={'data-minimum-input-length':2})) + +class GenerateInvoiceForm(forms.Form): + """ + A form to generate an invoice + """ + invoice_date = forms.CharField(label="Date") + invoice_number = forms.CharField(label="Numéro", help_text="Au format 19018, sans le FE") + invoice_place = forms.CharField(label="Lieu") + invoice_object = forms.CharField(label="Objet") + invoice_description = forms.CharField(label="Description", required=False) + client_name = forms.CharField(label="Nom du client") + client_address_fisrt_line = forms.CharField(label="Première ligne d'adresse") + client_address_second_line = forms.CharField(label="Deuxième ligne d'adresse") + products = forms.CharField(widget=forms.Textarea, label="Produits", help_text="Au format nom;prix;quantité avec saut de ligne") \ No newline at end of file diff --git a/gestion/templates/gestion/invoice.tex b/gestion/templates/gestion/invoice.tex new file mode 100644 index 0000000..8fd7136 --- /dev/null +++ b/gestion/templates/gestion/invoice.tex @@ -0,0 +1,100 @@ +\documentclass[french,11pt]{article} +\usepackage{babel} +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} +\usepackage[a4paper]{geometry} +\usepackage{units} +\usepackage{graphicx} +\usepackage{fancyhdr} +\usepackage{fp} +\usepackage{float} +\usepackage{eurosym} +\def\FactureDate { {{- invoice_date -}} } +\def\FactureNum { {{- invoice_number -}} } +\def\FactureAcquittee {non} +\def\FactureLieu { {{- invoice_place -}} } +\def\FactureObjet { {{- invoice_object -}} } +\def\FactureDescr { +{{- invoice_description -}} +} + +\def\ClientNom{ {{- client_name -}} } +\def\ClientAdresse{ + {{- client_address_first_line -}}\newline + {{ client_address_second_line }} +} + +\geometry{verbose,tmargin=4em,bmargin=8em,lmargin=6em,rmargin=6em} +\setlength{\parindent}{0pt} +\setlength{\parskip}{1ex plus 0.5ex minus 0.2ex} + +\thispagestyle{fancy} +\pagestyle{fancy} +\setlength{\parindent}{0pt} + +\renewcommand{\headrulewidth}{0pt} +\cfoot{ + \small{ +Coopé Technopôle Metz (CTM)\\ +Adresse mail : coopemetz@gmail.com\\} + \tiny{ +Inscrite au registre des associations du tribunal d’instance de Metz + } +} + + + +\begin{document} + +\begin{figure}[H] +\includegraphics[scale=0.3]{ {{- path -}} } +\end{figure} +Coopé Technopôle Metz\\ +4 place Édouard Branly\\ +57070 Metz + +Facture FE\FactureNum + +{\addtolength{\leftskip}{10.5cm} + \textbf{\ClientNom} \\ + \ClientAdresse \\ + +} + +\hspace*{10.5cm} +\FactureLieu, le \FactureDate + +~\\~\\ + +\textbf{Objet : \FactureObjet \\} + +\textnormal{\FactureDescr} + +\vspace{10mm} + +\begin{center} + \begin{tabular}{lrrr} + \textbf{Désignation ~~~~~~} & \textbf{Prix unitaire} & \textbf{Quantité} & \textbf{Montant (EUR)} \\ + \hline + {% for product in products %} + {{- product.0 -}} & {{- product.1 -}} \euro{} & {{- product.2 -}} & {{- product.3 -}} \euro{}\\ + {% endfor %} + \hline + \textbf{Total HT} & & & {{- total -}} \euro{} + \end{tabular} +\end{center} + +\vfill + À régler par chèque, espèces ou par virement bancaire : + \begin{center} + \begin{tabular}{|c c c c|} + \hline \textbf{Code banque} & \textbf{Code guichet}& \textbf{Nº de Compte} & \textbf{Clé RIB} \\ + 20041 & 01010 & 1074350Z031 & 48 \\ + \hline \textbf{IBAN Nº} & \multicolumn{3}{|l|}{ FR82 2004 1010 1010 7435 0Z03 148 } \\ + \hline \textbf{BIC} & \multicolumn{3}{|l|}{ PSSTFRPPNCY }\\ + \hline \textbf{Domiciliation} & \multicolumn{3}{|l|}{La Banque Postale - Centre Financier - 54900 Nancy CEDEX 9}\\ + \hline \textbf{Titulaire} & \multicolumn{3}{|l|}{ASSO COOPE TECHNOPOLE METZ}\\ + \hline + \end{tabular} + \end{center} +\end{document} diff --git a/gestion/urls.py b/gestion/urls.py index eb43a2b..1699087 100644 --- a/gestion/urls.py +++ b/gestion/urls.py @@ -54,4 +54,5 @@ urlpatterns = [ path('categories-autocomplete', views.CategoriesAutocomplete.as_view(), name="categories-autocomplete"), path('stats', views.stats, name="stats"), path('divide', views.divide, name="divide"), + path('gen_invoice', views.gen_invoice, name="gen_invoice"), ] \ No newline at end of file diff --git a/gestion/views.py b/gestion/views.py index b98471e..ffa8ef6 100644 --- a/gestion/views.py +++ b/gestion/views.py @@ -8,6 +8,7 @@ from django.contrib.auth.decorators import login_required, permission_required from django.utils import timezone from django.http import HttpResponseRedirect from django.db import transaction +from django.conf import settings from datetime import datetime, timedelta @@ -17,8 +18,9 @@ from coopeV3.acl import active_required, acl_or, admin_required import simplejson as json from dal import autocomplete from decimal import * +import os -from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm, SearchMenuForm, SearchProductForm, SelectPositiveKegForm, SelectActiveKegForm, PinteForm, GenerateReleveForm, CategoryForm, SearchCategoryForm +from .forms import ReloadForm, RefundForm, ProductForm, KegForm, MenuForm, GestionForm, SearchMenuForm, SearchProductForm, SelectPositiveKegForm, SelectActiveKegForm, PinteForm, GenerateReleveForm, CategoryForm, SearchCategoryForm, GenerateInvoiceForm from .models import Product, Menu, Keg, ConsumptionHistory, KegHistory, Consumption, MenuHistory, Pinte, Reload, Refund, Category from users.models import School from preferences.models import PaymentMethod, GeneralPreferences, Cotisation, DivideHistory @@ -826,6 +828,41 @@ def pintes_user_list(request): users = User.objects.filter(pk__in=pks) return render(request, "gestion/pintes_user_list.html", {"users": users}) +@active_required +@login_required +@permission_required('users.can_generate_invoices') +def gen_invoice(request): + """ + Displays a form to generate an invoice. + """ + form = GenerateInvoiceForm(request.POST or None) + if form.is_valid(): + products = [x.split(";") for x in form.cleaned_data["products"].split("\n")] + total = 0 + for product in products: + sub_total = Decimal(product[1]) * Decimal(product[2]) + product.append(sub_total) + total += sub_total + return render_to_pdf( + request, + 'gestion/invoice.tex', + { + "invoice_date": form.cleaned_data["invoice_date"], + "invoice_number": form.cleaned_data["invoice_number"], + "invoice_place": form.cleaned_data["invoice_place"], + "invoice_object": form.cleaned_data["invoice_object"], + "invoice_description": form.cleaned_data["invoice_description"], + "client_name": form.cleaned_data["client_name"], + "client_address_first_line": form.cleaned_data["client_address_fisrt_line"], + "client_address_second_line": form.cleaned_data["client_address_second_line"], + "products" : products, + "total": total, + "path" : os.path.join(settings.BASE_DIR, "templates/coope.png"), + }, + filename="FE" + form.cleaned_data["invoice_number"] + ".pdf") + else: + return render(request, "form.html", {"form": form, "form_title": "Génération d'une facture", "form_button": "Générer", "form_button_icon": "file-pdf"}) + @active_required @login_required @admin_required diff --git a/users/templates/users/coope.png b/templates/coope.png similarity index 100% rename from users/templates/users/coope.png rename to templates/coope.png diff --git a/templates/nav.html b/templates/nav.html index 71b38d2..fb84abf 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -42,6 +42,11 @@ Répartition {% endif %} +{% if perms.users.can_generate_invoices %} + + Facture + +{% endif %} {% if perms.preferences.view_cotisation %} Cotisations diff --git a/users/models.py b/users/models.py index 45390e1..b58c509 100644 --- a/users/models.py +++ b/users/models.py @@ -111,6 +111,7 @@ class Profile(models.Model): """ class Meta: verbose_name = "Profil" + permissions = (('can_generate_invoices', 'Can generate invocies'),) user = models.OneToOneField(User, on_delete=models.CASCADE, verbose_name="Utilisateur") """ diff --git a/users/views.py b/users/views.py index a04272a..c129b7b 100644 --- a/users/views.py +++ b/users/views.py @@ -346,7 +346,7 @@ def gen_user_infos(request, pk): user= get_object_or_404(User, pk=pk) cotisations = CotisationHistory.objects.filter(user=user).order_by('-paymentDate') now = datetime.now() - path = os.path.join(settings.BASE_DIR, "users/templates/users/coope.png") + path = os.path.join(settings.BASE_DIR, "templates/coope.png") return render_to_pdf(request, 'users/bulletin.tex', {"user": user, "now": now, "cotisations": cotisations, "path":path}, filename="bulletin_" + user.first_name + "_" + user.last_name + ".pdf") ########## Groups ##########