Commit 00feca44 authored by Skia's avatar Skia

Merge branch 'krophil' into 'master'

pdf generation for operations

See merge request !21
parents a60063f0 0b4f8265
Pipeline #443 passed with stage
in 2 minutes and 19 seconds
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
<td>{% trans %}Comment{% endtrans %}</td> <td>{% trans %}Comment{% endtrans %}</td>
<td>{% trans %}File{% endtrans %}</td> <td>{% trans %}File{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td> <td>{% trans %}Actions{% endtrans %}</td>
<td>{% trans %}PDF{% endtrans %}</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
...@@ -70,6 +71,7 @@ ...@@ -70,6 +71,7 @@
<a href="{{ url('accounting:op_edit', op_id=o.id) }}">{% trans %}Edit{% endtrans %}</a> <a href="{{ url('accounting:op_edit', op_id=o.id) }}">{% trans %}Edit{% endtrans %}</a>
{% endif %} {% endif %}
</td> </td>
<td><a href="{{ url('accounting:op_pdf', op_id=o.id) }}">{% trans %}Generate{% endtrans %}</a></td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
......
...@@ -29,6 +29,7 @@ urlpatterns = [ ...@@ -29,6 +29,7 @@ urlpatterns = [
# Operations # Operations
url(r'^operation/create/(?P<j_id>[0-9]+)$', OperationCreateView.as_view(), name='op_new'), url(r'^operation/create/(?P<j_id>[0-9]+)$', OperationCreateView.as_view(), name='op_new'),
url(r'^operation/(?P<op_id>[0-9]+)$', OperationEditView.as_view(), name='op_edit'), url(r'^operation/(?P<op_id>[0-9]+)$', OperationEditView.as_view(), name='op_edit'),
url(r'^operation/(?P<op_id>[0-9]+)/pdf$', OperationPDFView.as_view(), name='op_pdf'),
# Companies # Companies
url(r'^company/create$', CompanyCreateView.as_view(), name='co_new'), url(r'^company/create$', CompanyCreateView.as_view(), name='co_new'),
url(r'^company/(?P<co_id>[0-9]+)$', CompanyEditView.as_view(), name='co_edit'), url(r'^company/(?P<co_id>[0-9]+)$', CompanyEditView.as_view(), name='co_edit'),
......
...@@ -5,6 +5,10 @@ from django.core.urlresolvers import reverse_lazy ...@@ -5,6 +5,10 @@ from django.core.urlresolvers import reverse_lazy
from django.forms.models import modelform_factory from django.forms.models import modelform_factory
from django.forms import HiddenInput from django.forms import HiddenInput
from django import forms from django import forms
from django.http import HttpResponseRedirect, HttpResponse
from django.utils.translation import ugettext as _
from django.conf import settings
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
...@@ -190,6 +194,7 @@ class JournalEditView(CanEditMixin, UpdateView): ...@@ -190,6 +194,7 @@ class JournalEditView(CanEditMixin, UpdateView):
fields = ['name', 'start_date', 'end_date', 'club_account', 'closed'] fields = ['name', 'start_date', 'end_date', 'club_account', 'closed']
template_name = 'core/edit.jinja' template_name = 'core/edit.jinja'
# Operation views # Operation views
class OperationForm(forms.ModelForm): class OperationForm(forms.ModelForm):
...@@ -303,6 +308,125 @@ class OperationEditView(CanEditMixin, UpdateView): ...@@ -303,6 +308,125 @@ class OperationEditView(CanEditMixin, UpdateView):
kwargs['object'] = self.object.journal kwargs['object'] = self.object.journal
return kwargs return kwargs
class OperationPDFView(CanViewMixin, DetailView):
"""
Display the PDF of a given operation
"""
model = Operation
pk_url_kwarg = "op_id"
def get(self, request, *args, **kwargs):
from reportlab.pdfgen import canvas
from reportlab.lib.units import cm
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter
from reportlab.lib.utils import ImageReader
from reportlab.graphics.shapes import Drawing
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase import pdfmetrics
pdfmetrics.registerFont(TTFont('DejaVu', 'DejaVuSerif.ttf'))
self.object = self.get_object()
amount = self.object.amount
remark = self.object.remark
nature = self.object.accounting_type.movement_type
num = self.object.number
date = self.object.date
mode = self.object.mode
cheque_number = self.object.cheque_number
club_name = self.object.journal.club_account.name
ti = self.object.journal.name
op_label = self.object.label
club_address = self.object.journal.club_account.club.address
id_op = self.object.id
if self.object.target_type == "OTHER":
target = self.object.target_label
else:
target = self.object.target.get_display_name()
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="op-%d(%s_on_%s).pdf"' %(num, ti, club_name)
p = canvas.Canvas(response)
p.setFont('DejaVu', 12)
p.setTitle("%s %d" % (_("Operation"), num))
width, height = letter
im = ImageReader("core/static/core/img/logo.jpg")
iw, ih = im.getSize()
p.drawImage(im, 40, height - 50, width=iw/2, height=ih/2)
labelStr = [["%s %s - %s %s" % (_("Journal"), ti, _("Operation"), num)]]
label = Table(labelStr, colWidths=[150], rowHeights=[20])
label.setStyle(TableStyle([
('ALIGN',(0,0),(-1,-1),'CENTER'),
('BOX', (0,0), (-1,-1), 0.25, colors.black),
]))
w, h = label.wrapOn(label, 0, 0)
label.drawOn(p, width-180, height)
p.drawString(90, height - 100, _("Financial proof: ") + "OP%010d" % (id_op)) #Justificatif du libellé
p.drawString(90, height - 130, _("Club: %(club_name)s") % ({"club_name": club_name}))
p.drawString(90, height - 160, _("Label: %(op_label)s") % {"op_label": op_label if op_label != None else ""})
data = []
data += [["%s" % (_("Credit").upper() if nature == 'CREDIT' else _("Debit").upper())]]
data += [[_("Amount: %(amount).2f €") % {"amount": amount}]]
payment_mode = ""
for m in settings.SITH_ACCOUNTING_PAYMENT_METHOD:
if m[0] == mode:
payment_mode += "[\u00D7]"
else:
payment_mode += "[ ]"
payment_mode += " %s\n" %(m[1])
data += [[payment_mode]]
data += [["%s : %s" % (_("Debtor") if nature == 'CREDIT' else _("Creditor"), target), ""]]
data += [["%s \n%s" % (_("Comment:"), remark)]]
t = Table(data, colWidths=[(width-90*2)/2]*2, rowHeights=[20, 20, 70, 20, 80])
t.setStyle(TableStyle([
('ALIGN',(0,0),(-1,-1),'CENTER'),
('VALIGN',(-2,-1),(-1,-1),'TOP'),
('VALIGN',(0,0),(-1,-2),'MIDDLE'),
('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
('SPAN', (0, 0), (1, 0)), # line DEBIT/CREDIT
('SPAN', (0, 1), (1, 1)), # line amount
('SPAN',(-2, -1), (-1,-1)), # line comment
('SPAN', (0, -2), (-1, -2)), # line creditor/debtor
('SPAN', (0, 2), (1, 2)), # line payment_mode
('ALIGN',(0, 2), (1, 2),'LEFT'), # line payment_mode
('ALIGN', (-2, -1), (-1, -1), 'LEFT'),
('BOX', (0,0), (-1,-1), 0.25, colors.black),
]))
w, h = t.wrapOn(p, 0, 0)
t.drawOn(p, 90, 350)
p.drawCentredString(10.5 * cm, 2 * cm, club_name)
p.drawCentredString(10.5 * cm, 1 * cm, club_address)
p.showPage()
p.save()
return response
# Company views # Company views
class CompanyCreateView(CanCreateMixin, CreateView): class CompanyCreateView(CanCreateMixin, CreateView):
...@@ -357,4 +481,3 @@ class LabelDeleteView(CanEditMixin, DeleteView): ...@@ -357,4 +481,3 @@ class LabelDeleteView(CanEditMixin, DeleteView):
def get_success_url(self): def get_success_url(self):
return self.object.get_absolute_url() return self.object.get_absolute_url()
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-11-29 11:25+0100\n" "POT-Creation-Date: 2016-11-29 12:34+0100\n"
"PO-Revision-Date: 2016-07-18\n" "PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Skia <skia@libskia.so>\n" "Last-Translator: Skia <skia@libskia.so>\n"
"Language-Team: AE info <ae.info@utbm.fr>\n" "Language-Team: AE info <ae.info@utbm.fr>\n"
...@@ -244,11 +244,11 @@ msgstr "Un code comptable ne contient que des numéros" ...@@ -244,11 +244,11 @@ msgstr "Un code comptable ne contient que des numéros"
msgid "movement type" msgid "movement type"
msgstr "type de mouvement" msgstr "type de mouvement"
#: accounting/models.py:300 #: accounting/models.py:300 accounting/views.py:383
msgid "Credit" msgid "Credit"
msgstr "Crédit" msgstr "Crédit"
#: accounting/models.py:300 #: accounting/models.py:300 accounting/views.py:383
msgid "Debit" msgid "Debit"
msgstr "Débit" msgstr "Débit"
...@@ -337,7 +337,7 @@ msgstr "Nouveau compte club" ...@@ -337,7 +337,7 @@ msgstr "Nouveau compte club"
#: accounting/templates/accounting/bank_account_details.jinja:26 #: accounting/templates/accounting/bank_account_details.jinja:26
#: accounting/templates/accounting/bank_account_list.jinja:21 #: accounting/templates/accounting/bank_account_list.jinja:21
#: accounting/templates/accounting/club_account_details.jinja:55 #: accounting/templates/accounting/club_account_details.jinja:55
#: accounting/templates/accounting/journal_details.jinja:70 club/views.py:54 #: accounting/templates/accounting/journal_details.jinja:71 club/views.py:54
#: core/templates/core/file.jinja:38 core/templates/core/page.jinja:31 #: core/templates/core/file.jinja:38 core/templates/core/page.jinja:31
#: core/templates/core/user_tools.jinja:36 core/views/user.py:152 #: core/templates/core/user_tools.jinja:36 core/views/user.py:152
#: counter/templates/counter/cash_summary_list.jinja:53 #: counter/templates/counter/cash_summary_list.jinja:53
...@@ -403,9 +403,11 @@ msgstr "Nom" ...@@ -403,9 +403,11 @@ msgstr "Nom"
#: accounting/templates/accounting/club_account_details.jinja:29 #: accounting/templates/accounting/club_account_details.jinja:29
msgid "Start" msgid "Start"
msgstr "Début" msgstr ""
#: accounting/templates/accounting/club_account_details.jinja:30 #: accounting/templates/accounting/club_account_details.jinja:30
#, fuzzy
#| msgid "End"
msgid "End" msgid "End"
msgstr "Fin" msgstr "Fin"
...@@ -431,12 +433,12 @@ msgid "Actions" ...@@ -431,12 +433,12 @@ msgid "Actions"
msgstr "Actions" msgstr "Actions"
#: accounting/templates/accounting/club_account_details.jinja:50 #: accounting/templates/accounting/club_account_details.jinja:50
#: accounting/templates/accounting/journal_details.jinja:58 #: accounting/templates/accounting/journal_details.jinja:59
msgid "Yes" msgid "Yes"
msgstr "Oui" msgstr "Oui"
#: accounting/templates/accounting/club_account_details.jinja:52 #: accounting/templates/accounting/club_account_details.jinja:52
#: accounting/templates/accounting/journal_details.jinja:60 #: accounting/templates/accounting/journal_details.jinja:61
msgid "No" msgid "No"
msgstr "Non" msgstr "Non"
...@@ -455,7 +457,7 @@ msgstr "Classeur : " ...@@ -455,7 +457,7 @@ msgstr "Classeur : "
#: core/templates/core/user_account_detail.jinja:10 #: core/templates/core/user_account_detail.jinja:10
#: counter/templates/counter/counter_click.jinja:32 #: counter/templates/counter/counter_click.jinja:32
msgid "Amount: " msgid "Amount: "
msgstr "Montant: " msgstr "Montant : "
#: accounting/templates/accounting/journal_details.jinja:19 #: accounting/templates/accounting/journal_details.jinja:19
msgid "Effective amount: " msgid "Effective amount: "
...@@ -521,6 +523,14 @@ msgstr "Commentaire" ...@@ -521,6 +523,14 @@ msgstr "Commentaire"
msgid "File" msgid "File"
msgstr "Fichier" msgstr "Fichier"
#: accounting/templates/accounting/journal_details.jinja:40
msgid "PDF"
msgstr ""
#: accounting/templates/accounting/journal_details.jinja:74
msgid "Generate"
msgstr "Générer"
#: accounting/templates/accounting/label_list.jinja:14 #: accounting/templates/accounting/label_list.jinja:14
msgid "Back to club account" msgid "Back to club account"
msgstr "Retour au compte club" msgstr "Retour au compte club"
...@@ -559,6 +569,45 @@ msgstr "Types simplifiés" ...@@ -559,6 +569,45 @@ msgstr "Types simplifiés"
msgid "New simplified type" msgid "New simplified type"
msgstr "Nouveau type simplifié" msgstr "Nouveau type simplifié"
#: accounting/views.py:360 accounting/views.py:366
msgid "Operation"
msgstr "Opération"
#: accounting/views.py:366
msgid "Journal"
msgstr "Classeur"
#: accounting/views.py:377
msgid "Financial proof: "
msgstr "Justificatif de libellé : "
#: accounting/views.py:378
#, python-format
msgid "Club: %(club_name)s"
msgstr "Club : %(club_name)s"
#: accounting/views.py:379
#, python-format
msgid "Label: %(op_label)s"
msgstr "Libellé : %(op_label)s"
#: accounting/views.py:385
#, python-format
msgid "Amount: %(amount).2f €"
msgstr "Montant : %(amount).2f €"
#: accounting/views.py:397
msgid "Debtor"
msgstr "Débiteur"
#: accounting/views.py:397
msgid "Creditor"
msgstr "Créditeur"
#: accounting/views.py:399
msgid "Comment:"
msgstr "Commentaire :"
#: club/models.py:21 #: club/models.py:21
msgid "unix name" msgid "unix name"
msgstr "nom unix" msgstr "nom unix"
...@@ -593,7 +642,7 @@ msgstr "Un club avec ce nom UNIX existe déjà." ...@@ -593,7 +642,7 @@ msgstr "Un club avec ce nom UNIX existe déjà."
#: club/models.py:145 counter/models.py:386 counter/models.py:403 #: club/models.py:145 counter/models.py:386 counter/models.py:403
#: eboutic/models.py:14 eboutic/models.py:47 launderette/models.py:89 #: eboutic/models.py:14 eboutic/models.py:47 launderette/models.py:89
#: launderette/models.py:126 sas/models.py:96 #: launderette/models.py:126 sas/models.py:98
msgid "user" msgid "user"
msgstr "nom d'utilisateur" msgstr "nom d'utilisateur"
...@@ -646,8 +695,10 @@ msgstr "Rôle" ...@@ -646,8 +695,10 @@ msgstr "Rôle"
#: club/templates/club/club_old_members.jinja:10 #: club/templates/club/club_old_members.jinja:10
#: core/templates/core/user_clubs.jinja:17 #: core/templates/core/user_clubs.jinja:17
#: core/templates/core/user_clubs.jinja:43 #: core/templates/core/user_clubs.jinja:43
#, fuzzy
#| msgid "description"
msgid "Description" msgid "Description"
msgstr "Description" msgstr "description"
#: club/templates/club/club_members.jinja:11 #: club/templates/club/club_members.jinja:11
#: core/templates/core/user_clubs.jinja:18 #: core/templates/core/user_clubs.jinja:18
...@@ -1234,6 +1285,8 @@ msgid "page content" ...@@ -1234,6 +1285,8 @@ msgid "page content"
msgstr "contenu de la page" msgstr "contenu de la page"
#: core/templates/core/403.jinja:5 #: core/templates/core/403.jinja:5
#, fuzzy
#| msgid "403, Forbidden"
msgid "403, Forbidden" msgid "403, Forbidden"
msgstr "403. Non autorisé" msgstr "403. Non autorisé"
...@@ -1366,8 +1419,10 @@ msgid "Edit %(obj)s" ...@@ -1366,8 +1419,10 @@ msgid "Edit %(obj)s"
msgstr "Éditer %(obj)s" msgstr "Éditer %(obj)s"
#: core/templates/core/file.jinja:7 core/templates/core/file_list.jinja:6 #: core/templates/core/file.jinja:7 core/templates/core/file_list.jinja:6
#, fuzzy
#| msgid "Files"
msgid "File list" msgid "File list"
msgstr "Liste des fichiers" msgstr "Fichiers"
#: core/templates/core/file.jinja:9 #: core/templates/core/file.jinja:9
msgid "New file" msgid "New file"
...@@ -1575,8 +1630,10 @@ msgid "History" ...@@ -1575,8 +1630,10 @@ msgid "History"
msgstr "Historique" msgstr "Historique"
#: core/templates/core/page.jinja:45 #: core/templates/core/page.jinja:45
#, fuzzy
#| msgid "Target does not exists"
msgid "Page does not exist" msgid "Page does not exist"
msgstr "La page n'existe pas." msgstr "La cible n'existe pas."
#: core/templates/core/page.jinja:47 #: core/templates/core/page.jinja:47
msgid "Create it?" msgid "Create it?"
...@@ -1725,6 +1782,8 @@ msgid "Year" ...@@ -1725,6 +1782,8 @@ msgid "Year"
msgstr "Année" msgstr "Année"
#: core/templates/core/user_account.jinja:9 #: core/templates/core/user_account.jinja:9
#, fuzzy
#| msgid "Month"
msgid "Month" msgid "Month"
msgstr "Mois" msgstr "Mois"
...@@ -1784,8 +1843,10 @@ msgid "Club(s)" ...@@ -1784,8 +1843,10 @@ msgid "Club(s)"
msgstr "Clubs" msgstr "Clubs"
#: core/templates/core/user_clubs.jinja:10 #: core/templates/core/user_clubs.jinja:10
#, fuzzy
#| msgid "Current scrub: "
msgid "Current club(s) :" msgid "Current club(s) :"
msgstr "Clubs actuels : " msgstr "Blouse actuelle : "
#: core/templates/core/user_clubs.jinja:36 #: core/templates/core/user_clubs.jinja:36
msgid "Old club(s) :" msgid "Old club(s) :"
...@@ -2428,6 +2489,8 @@ msgid "Choose another month: " ...@@ -2428,6 +2489,8 @@ msgid "Choose another month: "
msgstr "Choisir un autre mois : " msgstr "Choisir un autre mois : "
#: counter/templates/counter/invoices_call.jinja:21 #: counter/templates/counter/invoices_call.jinja:21
#, fuzzy
#| msgid "Sum"
msgid "Sum" msgid "Sum"
msgstr "Somme" msgstr "Somme"
...@@ -2804,7 +2867,7 @@ msgstr "Utilisateur qui sera conservé" ...@@ -2804,7 +2867,7 @@ msgstr "Utilisateur qui sera conservé"
msgid "User that will be deleted" msgid "User that will be deleted"
msgstr "Utilisateur qui sera supprimé" msgstr "Utilisateur qui sera supprimé"
#: sas/models.py:97 #: sas/models.py:99
msgid "picture" msgid "picture"
msgstr "photo" msgstr "photo"
...@@ -3018,3 +3081,321 @@ msgstr "Un utilisateur avec cette adresse email existe déjà" ...@@ -3018,3 +3081,321 @@ msgstr "Un utilisateur avec cette adresse email existe déjà"
msgid "You must either choose an existing user or create a new one properly" msgid "You must either choose an existing user or create a new one properly"
msgstr "" msgstr ""
"Vous devez soit choisir un utilisateur existant, ou en créer un proprement." "Vous devez soit choisir un utilisateur existant, ou en créer un proprement."
#, fuzzy
#~| msgid "Nature"
#~ msgid "Nature bilan: "
#~ msgstr "Nature"
#, fuzzy
#~| msgid "linked operation"
#~ msgid "Nature of operation"
#~ msgstr "opération liée"
#, fuzzy
#~| msgid "location"
#~ msgid "Syndication"
#~ msgstr "lieu"
#, fuzzy
#~| msgid "second email address"
#~ msgid "Enter a valid value."
#~ msgstr "adresse email secondaire"
#, fuzzy
#~| msgid "second email address"
#~ msgid "Enter a valid URL."
#~ msgstr "adresse email secondaire"
#, fuzzy
#~| msgid "second email address"
#~ msgid "Enter a valid integer."
#~ msgstr "adresse email secondaire"
#, fuzzy
#~| msgid "second email address"
#~ msgid "Enter a valid email address."
#~ msgstr "adresse email secondaire"
#, fuzzy
#~| msgid ""
#~| "Enter a valid username. This value may contain only letters, numbers "
#~| "and ./+/-/_ characters."
#~ msgid ""
#~ "Enter a valid 'slug' consisting of letters, numbers, underscores or "
#~ "hyphens."
#~ msgstr ""
#~ "Entrez un nom d'utilisateur correct. Uniquement des lettres, numéros, "
#~ "et ./+/-/_"
#, fuzzy
#~| msgid "second email address"
#~ msgid "Enter a valid IPv4 address."
#~ msgstr "adresse email secondaire"
#, fuzzy
#~| msgid "second email address"
#~ msgid "Enter a valid IPv6 address."
#~ msgstr "adresse email secondaire"
#, fuzzy
#~| msgid "second email address"
#~ msgid "Enter a valid IPv4 or IPv6 address."
#~ msgstr "adresse email secondaire"
#, fuzzy
#~| msgid "A user with that email address already exists"
#~ msgid "%(model_name)s with this %(field_labels)s already exists."
#~ msgstr "Un utilisateur avec cette adresse email existe déjà"
#, fuzzy
#~| msgid "Token name can not be blank"
#~ msgid "This field cannot be null."
#~ msgstr "Le nom du jeton ne peut pas être vide"
#, fuzzy
#~| msgid "Token name can not be blank"
#~ msgid "This field cannot be blank."
#~ msgstr "Le nom du jeton ne peut pas être vide"
#, fuzzy
#~| msgid "A user with that email address already exists"
#~ msgid "%(model_name)s with this %(field_label)s already exists."
#~ msgstr "Un utilisateur avec cette adresse email existe déjà"
#, fuzzy
#~| msgid "number"
#~ msgid "Decimal number"
#~ msgstr "numéro"
#, fuzzy
#~| msgid "Description"
#~ msgid "Duration"
#~ msgstr "Description"
#, fuzzy
#~| msgid "email address"
#~ msgid "Email address"
#~ msgstr "adresse email"
#, fuzzy
#~| msgid "File list"
#~ msgid "File path"
#~ msgstr "Liste des fichiers"
#, fuzzy
#~| msgid "account number"
#~ msgid "Floating point number"
#~ msgstr "numero de compte"
#, fuzzy
#~| msgid "address"
#~ msgid "IPv4 address"
#~ msgstr "Adresse"
#, fuzzy
#~| msgid "address"
#~ msgid "IP address"
#~ msgstr "Adresse"
#, fuzzy
#~| msgid "A user with that email address already exists"
#~ msgid "%(model)s instance with %(field)s %(value)r does not exist."
#~ msgstr "Un utilisateur avec cette adresse email existe déjà"
#, fuzzy
#~| msgid "account number"
#~ msgid "Enter a whole number."
#~ msgstr "numero de compte"
#, fuzzy
#~| msgid "account number"
#~ msgid "Enter a number."
#~ msgstr "numero de compte"
#, fuzzy
#~| msgid "second email address"
#~ msgid "Enter a valid date."
#~ msgstr "adresse email secondaire"
#, fuzzy
#~| msgid "second email address"