From cc0e1cb3ab59385371ff3a4b96502c958c8c4240 Mon Sep 17 00:00:00 2001 From: Dalahro Date: Sat, 9 Jul 2016 23:26:17 +0200 Subject: [PATCH] =?UTF-8?q?Cr=C3=A9ation=20basique=20de=20facture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cotisations/forms.py | 10 +- .../templates/cotisations/factures.tex | 49 ++++---- .../templates/cotisations/factures_old.tex | 116 ------------------ cotisations/tex.py | 75 ++++------- cotisations/views.py | 18 ++- re2o/settings.py | 8 ++ 6 files changed, 78 insertions(+), 198 deletions(-) delete mode 100644 cotisations/templates/cotisations/factures_old.tex diff --git a/cotisations/forms.py b/cotisations/forms.py index d2a1e69e..f2065cad 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -1,5 +1,5 @@ from django import forms -from django.forms import ModelForm +from django.forms import ModelForm, Form from .models import Article, Paiement, Facture, Banque class NewFactureForm(ModelForm): @@ -27,6 +27,14 @@ class NewFactureForm(ModelForm): raise forms.ValidationError("Le numero de chèque et la banque sont obligatoires") return cleaned_data +class NewFactureFormPdf(Form): + article = forms.ModelMultipleChoiceField(queryset=Article.objects.all(), label="Article") + number = forms.IntegerField(label="Quantité") + paid = forms.BooleanField(label="Payé", required=False) + dest = forms.CharField(required=True, max_length=255, label="Destinataire") + obj = forms.CharField(required=False, label="Objet") + detail = forms.CharField(required=False, max_length=255, label="Détails") + class EditFactureForm(NewFactureForm): class Meta(NewFactureForm.Meta): fields = '__all__' diff --git a/cotisations/templates/cotisations/factures.tex b/cotisations/templates/cotisations/factures.tex index 690f9af9..aaaf42e8 100644 --- a/cotisations/templates/cotisations/factures.tex +++ b/cotisations/templates/cotisations/factures.tex @@ -1,7 +1,6 @@ {% load i18n %} {% language 'fr' %} -\nonstopmode %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Invoice Template % LaTeX Template @@ -29,19 +28,19 @@ \documentclass[12pt]{article} % Use the custom invoice class (invoice.cls) \usepackage[utf8]{inputenc} \usepackage[frenchb]{babel} -\usepackage{tabularx} \usepackage[letterpaper,hmargin=0.79in,vmargin=0.79in]{geometry} \usepackage{calc} % Counters for totaling hours and cost \usepackage{longtable} \usepackage{graphicx} \usepackage{calc} +\usepackage{tabularx} \pagestyle{empty} % No page numbers \linespread{1.5} % Line spacing \setlength{\doublerulesep}{\arrayrulewidth} % Double rules look like one thick one \def \tab {\hspace*{3ex}} % Define \tab to create some horizontal white space - +\setlength{\parindent}{0cm} @@ -49,43 +48,43 @@ %\newcommand{\product}[5][0][0][0][0][0]{ %\setlength{ptotal}{#3*\real{#4}} %\addtolength{total}{#3*\real{#4}} -\DeclareDocumentCommand{\product}{ O{0} O{0} O{0} O{0} O{0} }{ -#1 & #2 & #3 & #4 & #5 \\ -\hline -} %---------------------------------------------------------------------------------------- % HEADING SECTION %---------------------------------------------------------------------------------------- \begin{titlepage} -\begin{textblock*}{4cm}(20mm,5mm) +%\begin{textblock}{4cm}(20mm,5mm) %\includegraphics[scale=0.3]{% templatetag openbrace %}{{tpl_path}}/logo.png} -\end{textblock*} +%\end{textblock} \end{titlepage} -\hfil{\Huge\bf ReZo Metz}\hfil % Company providing the invoice +\hfil{\Huge\bf {{asso_name}} }\hfil % Company providing the invoice \bigskip\break % Whitespace -\hrule % Horizontal line -2 rue Edouard Belin \\ % Your address and contact information -57070 Metz \hfill augustin.lemesle@supelec.fr \\ -Siret : +\hrule % Horizontal line \\ +\vspace{0.5cm} +{{line1}} \hfill {{phone}} \\ % Your address and contact information +{{line2}} \hfill {{email}} \\ +Siret : {{siret}} \\ \\ -{\bf À :} \tab {{f.user.name}} {{f.user.surname}} \\ % Invoice recipient +{\bf À :} \tab {{dest}} \\ % Invoice recipient {\bf Date:} \tab {{DATE}} \\ % Invoice date -{\bf Facture \no:} \tab {{fid}} \\ % Invoice number - +{\bf Objet:} \tab {{obj}} \\ % Objet +\tab \tab {{detail}} \\ % Details %---------------------------------------------------------------------------------------- % TABLE OF EXPENSES %---------------------------------------------------------------------------------------- -\begin{tabularx}{\textwidth}{|l|X|r|r|r|} +\begin{tabularx}{\textwidth}{|X|r|r|r|} \hline - \textbf{Code} & \textbf{Désignation} & \textbf{Qté.} & \textbf{Prix Unit.} \euro & \textbf{Prix Tot.} \euro\\ + \textbf{Désignation} & \textbf{Prix Unit.} & \textbf{Quantité} & \textbf{Prix total} \\ \hline -{% for a in f.article %} -\product[{{a.code}}][{{a.designation}}][{{a.nombre}}][{{a.pu|floatformat:2}}][{{a.ptotal|floatformat:2}}] + +{% for a in article %} +\hline + {{a.0.name}} & {{a.0.prix}} & {{a.1}} & {{a.2}}\\ +\hline {% endfor %} \hline @@ -93,14 +92,14 @@ Siret : %\setcounter{paid}{0} %\setcounter{topay}{\real{\value{total}}-\value{paid}} - +\vspace{1cm} \hfill \begin{tabular}{|l|r|} \hline -\textbf{Total} & {{total|floatformat:2}}\euro \\ -\textbf{Votre règlement} & {{paid|floatformat:2}}\euro \\ +\textbf{Total} & {{total|floatformat:2}} \\ +\textbf{Votre règlement} & {% if paid %}{{total|floatformat:2}}{% else %} 00,00 {% endif %} \\ \hline -\textbf{À PAYER} & {{topay|floatformat:2}}\euro \\ +\textbf{À PAYER} & {% if not paid %}{{total|floatformat:2}}{% else %} 00,00 {% endif %}\\ \hline \hline diff --git a/cotisations/templates/cotisations/factures_old.tex b/cotisations/templates/cotisations/factures_old.tex deleted file mode 100644 index 690f9af9..00000000 --- a/cotisations/templates/cotisations/factures_old.tex +++ /dev/null @@ -1,116 +0,0 @@ -{% load i18n %} -{% language 'fr' %} - -\nonstopmode -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Invoice Template -% LaTeX Template -% Version 1.0 (3/11/12) -%% This template has been downloaded from: -% http://www.LaTeXTemplates.com -% -% Original author: -% Trey Hunner (http://www.treyhunner.com/) -% -% License: -% CC BY-NC-SA 3.0 (http://creativecommons.org/licenses/by-nc-sa/3.0/) -% -% Important note: -% This template requires the invoice.cls file to be in the same directory as -% the .tex file. The invoice.cls file provides the style used for structuring the -% document. -% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%---------------------------------------------------------------------------------------- -% DOCUMENT CONFIGURATION -%---------------------------------------------------------------------------------------- - -\documentclass[12pt]{article} % Use the custom invoice class (invoice.cls) -\usepackage[utf8]{inputenc} -\usepackage[frenchb]{babel} -\usepackage{tabularx} -\usepackage[letterpaper,hmargin=0.79in,vmargin=0.79in]{geometry} -\usepackage{calc} % Counters for totaling hours and cost -\usepackage{longtable} -\usepackage{graphicx} -\usepackage{calc} - -\pagestyle{empty} % No page numbers -\linespread{1.5} % Line spacing - -\setlength{\doublerulesep}{\arrayrulewidth} % Double rules look like one thick one -\def \tab {\hspace*{3ex}} % Define \tab to create some horizontal white space - - - - -\begin{document} -%\newcommand{\product}[5][0][0][0][0][0]{ -%\setlength{ptotal}{#3*\real{#4}} -%\addtolength{total}{#3*\real{#4}} -\DeclareDocumentCommand{\product}{ O{0} O{0} O{0} O{0} O{0} }{ -#1 & #2 & #3 & #4 & #5 \\ -\hline -} - -%---------------------------------------------------------------------------------------- -% HEADING SECTION -%---------------------------------------------------------------------------------------- -\begin{titlepage} -\begin{textblock*}{4cm}(20mm,5mm) -%\includegraphics[scale=0.3]{% templatetag openbrace %}{{tpl_path}}/logo.png} -\end{textblock*} -\end{titlepage} -\hfil{\Huge\bf ReZo Metz}\hfil % Company providing the invoice -\bigskip\break % Whitespace -\hrule % Horizontal line -2 rue Edouard Belin \\ % Your address and contact information -57070 Metz \hfill augustin.lemesle@supelec.fr \\ -Siret : -\\ \\ -{\bf À :} \tab {{f.user.name}} {{f.user.surname}} \\ % Invoice recipient - -{\bf Date:} \tab {{DATE}} \\ % Invoice date - -{\bf Facture \no:} \tab {{fid}} \\ % Invoice number - -%---------------------------------------------------------------------------------------- -% TABLE OF EXPENSES -%---------------------------------------------------------------------------------------- - -\begin{tabularx}{\textwidth}{|l|X|r|r|r|} -\hline - \textbf{Code} & \textbf{Désignation} & \textbf{Qté.} & \textbf{Prix Unit.} \euro & \textbf{Prix Tot.} \euro\\ -\hline - -{% for a in f.article %} -\product[{{a.code}}][{{a.designation}}][{{a.nombre}}][{{a.pu|floatformat:2}}][{{a.ptotal|floatformat:2}}] -{% endfor %} - - \hline -\end{tabularx} - -%\setcounter{paid}{0} -%\setcounter{topay}{\real{\value{total}}-\value{paid}} - -\hfill -\begin{tabular}{|l|r|} -\hline -\textbf{Total} & {{total|floatformat:2}}\euro \\ -\textbf{Votre règlement} & {{paid|floatformat:2}}\euro \\ -\hline -\textbf{À PAYER} & {{topay|floatformat:2}}\euro \\ -\hline -\hline - -\end{tabular} - -\vspace{1.5cm} % Whitespace -\hrule % Horizontal line -\footnotesize{TVA non applicable, art. 293 B du CGI} - -{% endlanguage %} -%---------------------------------------------------------------------------------------- - -\end{document} diff --git a/cotisations/tex.py b/cotisations/tex.py index a06b73ce..41dac6ee 100644 --- a/cotisations/tex.py +++ b/cotisations/tex.py @@ -5,8 +5,8 @@ from django.core.cache import cache from django.conf import settings from django.shortcuts import redirect -from tempfile import mkdtemp -import subprocess +import tempfile +from subprocess import Popen, PIPE import os import shutil from hashlib import md5 @@ -17,55 +17,22 @@ CACHE_PREFIX = getattr(settings, 'TEX_CACHE_PREFIX', 'render-tex') CACHE_TIMEOUT = getattr(settings, 'TEX_CACHE_TIMEOUT', 86400) # 1 day -def render_tex(request, template, ctx={}): - doc = template.rsplit('/', 1)[-1].rsplit('.', 1)[0] - - # Utile ? Parfois il faut le chemin absolu pour retrouver les images - #ctx.setdefault('tpl_path', os.path.join(settings.BASE_DIR, 'factures/templates/factures')) - - try: - body = get_template(template).render(Context(ctx)).encode('utf-8') - except TemplateDoesNotExist: - raise Http404() - - etag = md5(body).hexdigest() - if request.META.get('HTTP_IF_NONE_MATCH', '') == etag: - return HttpResponseNotModified() - - cache_key = "%s:%s:%s" % (CACHE_PREFIX, template, etag) - pdf = cache.get(cache_key) - if pdf is None: - if b'\\nonstopmode' not in body: - raise ValueError("\\nonstopmode not present in document, cowardly refusing to process.") - - tmp = mkdtemp(prefix=TEMP_PREFIX) - try: - with open("%s/%s.tex" % (tmp, doc), "w") as f: - f.write(str(body)) - del body - - error = subprocess.Popen( - ["pdflatex", "%s.tex" % doc], - cwd=tmp, - stdin=open(os.devnull, "r"), - stderr=open(os.devnull, "wb"), - stdout=open(os.devnull, "wb") - ).wait() - - if error: - log = open("%s/%s.log" % (tmp, doc)).read() - return HttpResponse(log, content_type="text/plain") - - pdf = open("%s/%s.pdf" % (tmp, doc)).read() - finally: - shutil.rmtree(tmp) - pass - - if pdf: - cache.set(cache_key, pdf, CACHE_TIMEOUT) - - res = HttpResponse(pdf, content_type="application/pdf") - res['ETag'] = etag - return res - - +def render_tex(request,tmp, ctx={}): + context = Context(ctx) + template = get_template('cotisations/factures.tex') + rendered_tpl = template.render(context).encode('utf-8') + + with tempfile.TemporaryDirectory() as tempdir: + for i in range(2): + process = Popen( + ['pdflatex', '-output-directory', tempdir], + stdin = PIPE, + stdout = PIPE, + ) + process.communicate(rendered_tpl) + with open(os.path.join(tempdir, 'texput.pdf'), 'rb') as f: + pdf = f.read() + r = HttpResponse(content_type='application/pdf') + #r['Content-Disposition'] = 'attachement; filename=texput.pdf' + r.write(pdf) + return r diff --git a/cotisations/views.py b/cotisations/views.py index 5371d8bc..aa71d918 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -10,9 +10,10 @@ from django.contrib import messages from django.db.models import Max, ProtectedError from .models import Facture, Article, Cotisation, Paiement, Banque -from .forms import NewFactureForm, EditFactureForm, ArticleForm, DelArticleForm, PaiementForm, DelPaiementForm, BanqueForm, DelBanqueForm +from .forms import NewFactureForm, EditFactureForm, ArticleForm, DelArticleForm, PaiementForm, DelPaiementForm, BanqueForm, DelBanqueForm, NewFactureFormPdf from users.models import User from .tex import render_tex +from re2o.settings import ASSO_NAME, ASSO_ADDRESS_LINE1, ASSO_ADDRESS_LINE2, ASSO_SIRET, ASSO_EMAIL, ASSO_PHONE from dateutil.relativedelta import relativedelta from django.utils import timezone @@ -75,7 +76,20 @@ def new_facture(request, userid): @login_required def new_facture_pdf(request): - return render_tex(request, 'cotisations/factures.tex', {'DATE':None}) + facture_form = NewFactureFormPdf(request.POST or None) + if facture_form.is_valid(): + tbl = [] + article = facture_form.cleaned_data['article'] + quantite = facture_form.cleaned_data['number'] + paid = facture_form.cleaned_data['paid'] + destinataire = facture_form.cleaned_data['dest'] + objet = facture_form.cleaned_data['obj'] + detail = facture_form.cleaned_data['detail'] + for a in article: + tbl.append([a, quantite, a.prix * quantite]) + prix_total = sum(a[2] for a in tbl) + return render_tex(request, 'cotisations/factures.tex', {'DATE' : timezone.now(),'dest':destinataire, 'obj':objet, 'detail':detail, 'article':tbl, 'total':prix_total, 'paid':paid, 'asso_name':ASSO_NAME, 'line1':ASSO_ADDRESS_LINE1, 'line2':ASSO_ADDRESS_LINE2, 'siret':ASSO_SIRET, 'email':ASSO_EMAIL, 'phone':ASSO_PHONE}) + return form({'factureform': facture_form}, 'cotisations/facture.html', request) @login_required @permission_required('cableur') diff --git a/re2o/settings.py b/re2o/settings.py index 8788c33e..805b935c 100644 --- a/re2o/settings.py +++ b/re2o/settings.py @@ -20,6 +20,14 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ +# Association information + +ASSO_NAME = "ReZo Metz" +ASSO_ADDRESS_LINE1 = "2, rue Edouard Belin" +ASSO_ADDRESS_LINE2 = "57070 Metz" +ASSO_SIRET = "" +ASSO_EMAIL = "tresorier@ecole.fr" +ASSO_PHONE = "01 02 03 04 05" # Auth definition