diff --git a/accounting/templates/accounting/journal_details.jinja b/accounting/templates/accounting/journal_details.jinja
index 3e499dda..4c5c3bb3 100644
--- a/accounting/templates/accounting/journal_details.jinja
+++ b/accounting/templates/accounting/journal_details.jinja
@@ -37,6 +37,7 @@
{% trans %}Comment{% endtrans %} |
{% trans %}File{% endtrans %} |
{% trans %}Actions{% endtrans %} |
+ {% trans %}PDF{% endtrans %} |
@@ -70,6 +71,7 @@
{% trans %}Edit{% endtrans %}
{% endif %}
+ {% trans %}Generate{% endtrans %} |
{% endfor %}
diff --git a/accounting/urls.py b/accounting/urls.py
index 15626460..fcd4a5f8 100644
--- a/accounting/urls.py
+++ b/accounting/urls.py
@@ -29,6 +29,7 @@ urlpatterns = [
# Operations
url(r'^operation/create/(?P[0-9]+)$', OperationCreateView.as_view(), name='op_new'),
url(r'^operation/(?P[0-9]+)$', OperationEditView.as_view(), name='op_edit'),
+ url(r'^operation/(?P[0-9]+)/pdf$', OperationPDFView.as_view(), name='op_pdf'),
# Companies
url(r'^company/create$', CompanyCreateView.as_view(), name='co_new'),
url(r'^company/(?P[0-9]+)$', CompanyEditView.as_view(), name='co_edit'),
diff --git a/accounting/views.py b/accounting/views.py
index de424400..10b667fe 100644
--- a/accounting/views.py
+++ b/accounting/views.py
@@ -5,6 +5,10 @@ from django.core.urlresolvers import reverse_lazy
from django.forms.models import modelform_factory
from django.forms import HiddenInput
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
@@ -190,6 +194,7 @@ class JournalEditView(CanEditMixin, UpdateView):
fields = ['name', 'start_date', 'end_date', 'club_account', 'closed']
template_name = 'core/edit.jinja'
+
# Operation views
class OperationForm(forms.ModelForm):
@@ -303,6 +308,125 @@ class OperationEditView(CanEditMixin, UpdateView):
kwargs['object'] = self.object.journal
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
class CompanyCreateView(CanCreateMixin, CreateView):
@@ -357,4 +481,3 @@ class LabelDeleteView(CanEditMixin, DeleteView):
def get_success_url(self):
return self.object.get_absolute_url()
-
diff --git a/core/static/core/img/logo.jpg b/core/static/core/img/logo.jpg
new file mode 100644
index 00000000..8a273377
Binary files /dev/null and b/core/static/core/img/logo.jpg differ
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index f2106df7..6ec2c920 100644
--- a/locale/fr/LC_MESSAGES/django.po
+++ b/locale/fr/LC_MESSAGES/django.po
@@ -6,7 +6,7 @@
msgid ""
msgstr ""
"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"
"Last-Translator: Skia \n"
"Language-Team: AE info \n"
@@ -244,11 +244,11 @@ msgstr "Un code comptable ne contient que des numéros"
msgid "movement type"
msgstr "type de mouvement"
-#: accounting/models.py:300
+#: accounting/models.py:300 accounting/views.py:383
msgid "Credit"
msgstr "Crédit"
-#: accounting/models.py:300
+#: accounting/models.py:300 accounting/views.py:383
msgid "Debit"
msgstr "Débit"
@@ -337,7 +337,7 @@ msgstr "Nouveau compte club"
#: accounting/templates/accounting/bank_account_details.jinja:26
#: accounting/templates/accounting/bank_account_list.jinja:21
#: 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/user_tools.jinja:36 core/views/user.py:152
#: counter/templates/counter/cash_summary_list.jinja:53
@@ -403,9 +403,11 @@ msgstr "Nom"
#: accounting/templates/accounting/club_account_details.jinja:29
msgid "Start"
-msgstr "Début"
+msgstr ""
#: accounting/templates/accounting/club_account_details.jinja:30
+#, fuzzy
+#| msgid "End"
msgid "End"
msgstr "Fin"
@@ -431,12 +433,12 @@ msgid "Actions"
msgstr "Actions"
#: accounting/templates/accounting/club_account_details.jinja:50
-#: accounting/templates/accounting/journal_details.jinja:58
+#: accounting/templates/accounting/journal_details.jinja:59
msgid "Yes"
msgstr "Oui"
#: accounting/templates/accounting/club_account_details.jinja:52
-#: accounting/templates/accounting/journal_details.jinja:60
+#: accounting/templates/accounting/journal_details.jinja:61
msgid "No"
msgstr "Non"
@@ -455,7 +457,7 @@ msgstr "Classeur : "
#: core/templates/core/user_account_detail.jinja:10
#: counter/templates/counter/counter_click.jinja:32
msgid "Amount: "
-msgstr "Montant: "
+msgstr "Montant : "
#: accounting/templates/accounting/journal_details.jinja:19
msgid "Effective amount: "
@@ -521,6 +523,14 @@ msgstr "Commentaire"
msgid "File"
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
msgid "Back to club account"
msgstr "Retour au compte club"
@@ -559,6 +569,45 @@ msgstr "Types simplifiés"
msgid "New simplified type"
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
msgid "unix name"
msgstr "nom unix"
@@ -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
#: 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"
msgstr "nom d'utilisateur"
@@ -646,8 +695,10 @@ msgstr "Rôle"
#: club/templates/club/club_old_members.jinja:10
#: core/templates/core/user_clubs.jinja:17
#: core/templates/core/user_clubs.jinja:43
+#, fuzzy
+#| msgid "description"
msgid "Description"
-msgstr "Description"
+msgstr "description"
#: club/templates/club/club_members.jinja:11
#: core/templates/core/user_clubs.jinja:18
@@ -1234,6 +1285,8 @@ msgid "page content"
msgstr "contenu de la page"
#: core/templates/core/403.jinja:5
+#, fuzzy
+#| msgid "403, Forbidden"
msgid "403, Forbidden"
msgstr "403. Non autorisé"
@@ -1366,8 +1419,10 @@ msgid "Edit %(obj)s"
msgstr "Éditer %(obj)s"
#: core/templates/core/file.jinja:7 core/templates/core/file_list.jinja:6
+#, fuzzy
+#| msgid "Files"
msgid "File list"
-msgstr "Liste des fichiers"
+msgstr "Fichiers"
#: core/templates/core/file.jinja:9
msgid "New file"
@@ -1575,8 +1630,10 @@ msgid "History"
msgstr "Historique"
#: core/templates/core/page.jinja:45
+#, fuzzy
+#| msgid "Target does not exists"
msgid "Page does not exist"
-msgstr "La page n'existe pas."
+msgstr "La cible n'existe pas."
#: core/templates/core/page.jinja:47
msgid "Create it?"
@@ -1725,6 +1782,8 @@ msgid "Year"
msgstr "Année"
#: core/templates/core/user_account.jinja:9
+#, fuzzy
+#| msgid "Month"
msgid "Month"
msgstr "Mois"
@@ -1784,8 +1843,10 @@ msgid "Club(s)"
msgstr "Clubs"
#: core/templates/core/user_clubs.jinja:10
+#, fuzzy
+#| msgid "Current scrub: "
msgid "Current club(s) :"
-msgstr "Clubs actuels : "
+msgstr "Blouse actuelle : "
#: core/templates/core/user_clubs.jinja:36
msgid "Old club(s) :"
@@ -2428,6 +2489,8 @@ msgid "Choose another month: "
msgstr "Choisir un autre mois : "
#: counter/templates/counter/invoices_call.jinja:21
+#, fuzzy
+#| msgid "Sum"
msgid "Sum"
msgstr "Somme"
@@ -2804,7 +2867,7 @@ msgstr "Utilisateur qui sera conservé"
msgid "User that will be deleted"
msgstr "Utilisateur qui sera supprimé"
-#: sas/models.py:97
+#: sas/models.py:99
msgid "picture"
msgstr "photo"
@@ -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"
msgstr ""
"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"
+#~ msgid "Enter a valid time."
+#~ msgstr "adresse email secondaire"
+
+#, fuzzy
+#~| msgid "second email address"
+#~ msgid "Enter a valid date/time."
+#~ msgstr "adresse email secondaire"
+
+#, fuzzy
+#~| msgid "second email address"
+#~ msgid "Enter a valid duration."
+#~ msgstr "adresse email secondaire"
+
+#, fuzzy
+#~| msgid "second email address"
+#~ msgid "Enter a list of values."
+#~ msgstr "adresse email secondaire"
+
+#, fuzzy
+#~| msgid "second email address"
+#~ msgid "Enter a valid UUID."
+#~ msgstr "adresse email secondaire"
+
+#, fuzzy
+#~| msgid "Current club(s) :"
+#~ msgid "Currently"
+#~ msgstr "Clubs actuels : "
+
+#, fuzzy
+#~| msgid "Unknown event"
+#~ msgid "Unknown"
+#~ msgstr "Événement inconnu"
+
+#, fuzzy
+#~| msgid "M"
+#~ msgid "PM"
+#~ msgstr "M"
+
+#, fuzzy
+#~| msgid "M"
+#~ msgid "AM"
+#~ msgstr "M"
+
+#, fuzzy
+#~| msgid "Month"
+#~ msgid "Mon"
+#~ msgstr "Mois"
+
+#, fuzzy
+#~| msgid "Start"
+#~ msgid "Sat"
+#~ msgstr "Début"
+
+#, fuzzy
+#~| msgid "Search"
+#~ msgid "March"
+#~ msgstr "Recherche"
+
+#, fuzzy
+#~| msgid "Man"
+#~ msgid "May"
+#~ msgstr "Homme"
+
+#, fuzzy
+#~| msgid "past member"
+#~ msgid "September"
+#~ msgstr "Anciens membres"
+
+#, fuzzy
+#~| msgid "Members"
+#~ msgid "November"
+#~ msgstr "Membres"
+
+#, fuzzy
+#~| msgid "Members"
+#~ msgid "December"
+#~ msgstr "Membres"
+
+#, fuzzy
+#~| msgid "Man"
+#~ msgid "jan"
+#~ msgstr "Homme"
+
+#, fuzzy
+#~| msgid "Bar"
+#~ msgid "mar"
+#~ msgstr "Bar"
+
+#, fuzzy
+#~| msgid "Bar"
+#~ msgid "apr"
+#~ msgstr "Bar"
+
+#, fuzzy
+#~| msgid "company"
+#~ msgid "may"
+#~ msgstr "entreprise"
+
+#, fuzzy
+#~| msgid "journal"
+#~ msgid "jun"
+#~ msgstr "classeur"
+
+#, fuzzy
+#~| msgid "journal"
+#~ msgid "jul"
+#~ msgstr "classeur"
+
+#, fuzzy
+#~| msgid "sex"
+#~ msgid "sep"
+#~ msgstr "sexe"
+
+#, fuzzy
+#~| msgid "Doctor"
+#~ msgid "oct"
+#~ msgstr "Doctorant"
+
+#, fuzzy
+#~| msgid "Search"
+#~ msgctxt "abbrev. month"
+#~ msgid "March"
+#~ msgstr "Recherche"
+
+#, fuzzy
+#~| msgid "Man"
+#~ msgctxt "abbrev. month"
+#~ msgid "May"
+#~ msgstr "Homme"
+
+#, fuzzy
+#~| msgid "Search"
+#~ msgctxt "alt. month"
+#~ msgid "March"
+#~ msgstr "Recherche"
+
+#, fuzzy
+#~| msgid "Man"
+#~ msgctxt "alt. month"
+#~ msgid "May"
+#~ msgstr "Homme"
+
+#, fuzzy
+#~| msgid "past member"
+#~ msgctxt "alt. month"
+#~ msgid "September"
+#~ msgstr "Anciens membres"
+
+#, fuzzy
+#~| msgid "Members"
+#~ msgctxt "alt. month"
+#~ msgid "November"
+#~ msgstr "Membres"
+
+#, fuzzy
+#~| msgid "Members"
+#~ msgctxt "alt. month"
+#~ msgid "December"
+#~ msgstr "Membres"
+
+#, fuzzy
+#~| msgid "second email address"
+#~ msgid "This is not a valid IPv6 address."
+#~ msgstr "adresse email secondaire"
+
+#, fuzzy
+#~| msgid "Page does not exist"
+#~ msgid "\"%(path)s\" does not exist"
+#~ msgstr "La page n'existe pas."
+
+#, fuzzy
+#~| msgid "Club"
+#~ msgid "Club:"
+#~ msgstr "Club"
+
+#, fuzzy
+#~| msgid "Debit"
+#~ msgid "Debitor"
+#~ msgstr "Débiteur"