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"