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.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__'

View file

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

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.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

View file

@ -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')

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
# 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