8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-12-26 08:53:46 +00:00

Création basique de facture

This commit is contained in:
Dalahro 2016-07-09 23:26:17 +02:00
parent d363fbe35c
commit e097a5f75d
6 changed files with 78 additions and 198 deletions

View file

@ -1,5 +1,5 @@
from django import forms from django import forms
from django.forms import ModelForm from django.forms import ModelForm, Form
from .models import Article, Paiement, Facture, Banque from .models import Article, Paiement, Facture, Banque
class NewFactureForm(ModelForm): class NewFactureForm(ModelForm):
@ -27,6 +27,14 @@ class NewFactureForm(ModelForm):
raise forms.ValidationError("Le numero de chèque et la banque sont obligatoires") raise forms.ValidationError("Le numero de chèque et la banque sont obligatoires")
return cleaned_data 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 EditFactureForm(NewFactureForm):
class Meta(NewFactureForm.Meta): class Meta(NewFactureForm.Meta):
fields = '__all__' fields = '__all__'

View file

@ -1,7 +1,6 @@
{% load i18n %} {% load i18n %}
{% language 'fr' %} {% language 'fr' %}
\nonstopmode
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Invoice Template % Invoice Template
% LaTeX Template % LaTeX Template
@ -29,19 +28,19 @@
\documentclass[12pt]{article} % Use the custom invoice class (invoice.cls) \documentclass[12pt]{article} % Use the custom invoice class (invoice.cls)
\usepackage[utf8]{inputenc} \usepackage[utf8]{inputenc}
\usepackage[frenchb]{babel} \usepackage[frenchb]{babel}
\usepackage{tabularx}
\usepackage[letterpaper,hmargin=0.79in,vmargin=0.79in]{geometry} \usepackage[letterpaper,hmargin=0.79in,vmargin=0.79in]{geometry}
\usepackage{calc} % Counters for totaling hours and cost \usepackage{calc} % Counters for totaling hours and cost
\usepackage{longtable} \usepackage{longtable}
\usepackage{graphicx} \usepackage{graphicx}
\usepackage{calc} \usepackage{calc}
\usepackage{tabularx}
\pagestyle{empty} % No page numbers \pagestyle{empty} % No page numbers
\linespread{1.5} % Line spacing \linespread{1.5} % Line spacing
\setlength{\doublerulesep}{\arrayrulewidth} % Double rules look like one thick one \setlength{\doublerulesep}{\arrayrulewidth} % Double rules look like one thick one
\def \tab {\hspace*{3ex}} % Define \tab to create some horizontal white space \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]{ %\newcommand{\product}[5][0][0][0][0][0]{
%\setlength{ptotal}{#3*\real{#4}} %\setlength{ptotal}{#3*\real{#4}}
%\addtolength{total}{#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 % HEADING SECTION
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\begin{titlepage} \begin{titlepage}
\begin{textblock*}{4cm}(20mm,5mm) %\begin{textblock}{4cm}(20mm,5mm)
%\includegraphics[scale=0.3]{% templatetag openbrace %}{{tpl_path}}/logo.png} %\includegraphics[scale=0.3]{% templatetag openbrace %}{{tpl_path}}/logo.png}
\end{textblock*} %\end{textblock}
\end{titlepage} \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 \bigskip\break % Whitespace
\hrule % Horizontal line \hrule % Horizontal line \\
2 rue Edouard Belin \\ % Your address and contact information \vspace{0.5cm}
57070 Metz \hfill augustin.lemesle@supelec.fr \\ {{line1}} \hfill {{phone}} \\ % Your address and contact information
Siret : {{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 Date:} \tab {{DATE}} \\ % Invoice date
{\bf Facture \no:} \tab {{fid}} \\ % Invoice number {\bf Objet:} \tab {{obj}} \\ % Objet
\tab \tab {{detail}} \\ % Details
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
% TABLE OF EXPENSES % TABLE OF EXPENSES
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\begin{tabularx}{\textwidth}{|l|X|r|r|r|} \begin{tabularx}{\textwidth}{|X|r|r|r|}
\hline \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 \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 %} {% endfor %}
\hline \hline
@ -93,14 +92,14 @@ Siret :
%\setcounter{paid}{0} %\setcounter{paid}{0}
%\setcounter{topay}{\real{\value{total}}-\value{paid}} %\setcounter{topay}{\real{\value{total}}-\value{paid}}
\vspace{1cm}
\hfill \hfill
\begin{tabular}{|l|r|} \begin{tabular}{|l|r|}
\hline \hline
\textbf{Total} & {{total|floatformat:2}}\euro \\ \textbf{Total} & {{total|floatformat:2}} \\
\textbf{Votre règlement} & {{paid|floatformat:2}}\euro \\ \textbf{Votre règlement} & {% if paid %}{{total|floatformat:2}}{% else %} 00,00 {% endif %} \\
\hline \hline
\textbf{À PAYER} & {{topay|floatformat:2}}\euro \\ \textbf{À PAYER} & {% if not paid %}{{total|floatformat:2}}{% else %} 00,00 {% endif %}\\
\hline \hline
\hline \hline

View file

@ -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}

View file

@ -5,8 +5,8 @@ from django.core.cache import cache
from django.conf import settings from django.conf import settings
from django.shortcuts import redirect from django.shortcuts import redirect
from tempfile import mkdtemp import tempfile
import subprocess from subprocess import Popen, PIPE
import os import os
import shutil import shutil
from hashlib import md5 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 CACHE_TIMEOUT = getattr(settings, 'TEX_CACHE_TIMEOUT', 86400) # 1 day
def render_tex(request, template, ctx={}): def render_tex(request,tmp, ctx={}):
doc = template.rsplit('/', 1)[-1].rsplit('.', 1)[0] context = Context(ctx)
template = get_template('cotisations/factures.tex')
# Utile ? Parfois il faut le chemin absolu pour retrouver les images rendered_tpl = template.render(context).encode('utf-8')
#ctx.setdefault('tpl_path', os.path.join(settings.BASE_DIR, 'factures/templates/factures'))
with tempfile.TemporaryDirectory() as tempdir:
try: for i in range(2):
body = get_template(template).render(Context(ctx)).encode('utf-8') process = Popen(
except TemplateDoesNotExist: ['pdflatex', '-output-directory', tempdir],
raise Http404() stdin = PIPE,
stdout = PIPE,
etag = md5(body).hexdigest() )
if request.META.get('HTTP_IF_NONE_MATCH', '') == etag: process.communicate(rendered_tpl)
return HttpResponseNotModified() with open(os.path.join(tempdir, 'texput.pdf'), 'rb') as f:
pdf = f.read()
cache_key = "%s:%s:%s" % (CACHE_PREFIX, template, etag) r = HttpResponse(content_type='application/pdf')
pdf = cache.get(cache_key) #r['Content-Disposition'] = 'attachement; filename=texput.pdf'
if pdf is None: r.write(pdf)
if b'\\nonstopmode' not in body: return r
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

View file

@ -10,9 +10,10 @@ from django.contrib import messages
from django.db.models import Max, ProtectedError from django.db.models import Max, ProtectedError
from .models import Facture, Article, Cotisation, Paiement, Banque 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 users.models import User
from .tex import render_tex 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 dateutil.relativedelta import relativedelta
from django.utils import timezone from django.utils import timezone
@ -75,7 +76,20 @@ def new_facture(request, userid):
@login_required @login_required
def new_facture_pdf(request): 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 @login_required
@permission_required('cableur') @permission_required('cableur')

View file

@ -20,6 +20,14 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ # 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 # Auth definition