672 lines
23 KiB
Python
672 lines
23 KiB
Python
from reportlab.pdfgen.canvas import Canvas
|
|
from reportlab.lib.pagesizes import A4
|
|
from reportlab.lib.units import cm
|
|
from reportlab.platypus import Table, TableStyle, Paragraph
|
|
from reportlab.lib.styles import getSampleStyleSheet
|
|
from reportlab.lib import colors
|
|
|
|
from django.conf import settings
|
|
|
|
from datetime import date, datetime, timedelta
|
|
from django.db.models import Q, Sum
|
|
import os
|
|
|
|
import locale
|
|
|
|
from PIL import Image
|
|
|
|
from textwrap import wrap
|
|
|
|
from comptabilite.models import (
|
|
Transaction,
|
|
TransactionType,
|
|
EvaluationRules,
|
|
EvaluationRulesAdaptation,
|
|
Annuality,
|
|
ComplementaryInformations,
|
|
)
|
|
|
|
from comptabilite.tools import (
|
|
get_transactions_and_sums_for_year_and_type,
|
|
get_transactiontypes_and_total_amount_transactions,
|
|
get_transactiontype_and_sum_for_spf_export,
|
|
get_right_engagement_and_sump_spf,
|
|
get_asset_liability_and_sump_spf,
|
|
)
|
|
|
|
import os
|
|
import yaml
|
|
import re
|
|
|
|
EXPENSES = 0
|
|
RECETTES = 1
|
|
|
|
X = 35
|
|
Y = 841.89
|
|
INDENT = 5
|
|
RIGHT_X = 595.27 - X
|
|
TITLED_X = 125
|
|
INDENTED_X = X + INDENT
|
|
INDENTED_RIGHT_X = RIGHT_X - INDENT
|
|
MIDDLE = (RIGHT_X - X) / 2
|
|
|
|
COMMON_LINE_HEIGHT = -15
|
|
SMALL_LINE_HEIGHT = -10
|
|
LARGE_LINE_HEIGHT = -20
|
|
DOUBLE_LINE_HEIGHT = COMMON_LINE_HEIGHT * 2
|
|
BIG_LINE_HEIGHT = COMMON_LINE_HEIGHT * 3
|
|
HUGE_LINE_HEIGHT = COMMON_LINE_HEIGHT * 4
|
|
|
|
|
|
class PDFDocument(object):
|
|
""" Create the PDF object, using the response object as its "file" """
|
|
|
|
def __init__(self, response):
|
|
self.document = Canvas(response, pagesize=A4)
|
|
self.y = Y - X
|
|
self.__load_config()
|
|
|
|
def __load_config(self):
|
|
""" Charge le contenu du fichier SITE_CONFIG.YAML qui contient les données relatives à
|
|
l'ASBL.
|
|
"""
|
|
current_path = os.path.dirname(os.path.realpath(__file__))
|
|
self.club_infos = None
|
|
with open(os.path.join(current_path, "site_config.yaml"), "r") as stream:
|
|
try:
|
|
self.club_infos = yaml.load(stream, Loader=yaml.FullLoader)
|
|
except yaml.YAMLError as exc:
|
|
print(exc)
|
|
|
|
def add_vspace(self, height=COMMON_LINE_HEIGHT):
|
|
""" Passe à la ligne avec une hauteur de ligne passée en paramètre.
|
|
|
|
Args:
|
|
height (int): hauteur de la ligne.
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
|
|
self.y += height
|
|
|
|
# if y < 120;
|
|
# document.PageBreak()
|
|
# y = 790
|
|
|
|
def add_string(
|
|
self, x, string, font_family="Helvetica", font_decoration=None, font_size=10
|
|
):
|
|
if font_decoration:
|
|
font_family += "-" + font_decoration
|
|
self.document.setFont(font_family, font_size)
|
|
self.document.drawString(x, self.y, string)
|
|
|
|
def add_new_line(
|
|
self,
|
|
x,
|
|
string,
|
|
height=COMMON_LINE_HEIGHT,
|
|
font_family="Helvetica",
|
|
font_decoration=None,
|
|
font_size=10,
|
|
):
|
|
self.add_vspace(height)
|
|
self.add_string(x, string, font_family, font_decoration, font_size)
|
|
|
|
def add_header(self, contract=None):
|
|
""" Génère le header du document.
|
|
|
|
Args:
|
|
contract (contract): instance de la class Contract.
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
rect_height = 40
|
|
self.document.rect(X, self.y, RIGHT_X - X, -rect_height, fill=0)
|
|
self.add_new_line(INDENTED_X, self.club_infos["NAME"])
|
|
self.document.drawRightString(
|
|
INDENTED_RIGHT_X, self.y, "N° Entreprise : " + self.club_infos["BCE_NUMBER"]
|
|
)
|
|
self.add_new_line(
|
|
INDENTED_X,
|
|
"Siège social : "
|
|
+ self.club_infos["ADDRESS"]
|
|
+ " - "
|
|
+ self.club_infos["CITY"]
|
|
+ " "
|
|
+ self.club_infos["ZIP"],
|
|
)
|
|
if contract is not None:
|
|
self.document.drawRightString(
|
|
INDENTED_RIGHT_X, self.y, "N° de Référence : " + str(contract.reference)
|
|
)
|
|
self.add_vspace(BIG_LINE_HEIGHT)
|
|
|
|
def download(self):
|
|
""" Close the PDF object cleanly, and send it to download. """
|
|
self.document.showPage()
|
|
self.document.save()
|
|
|
|
|
|
class SpfDocument(PDFDocument):
|
|
def generate(self, accounting_year):
|
|
""" Genère un document aux normes du SPF Finance.
|
|
|
|
Args:
|
|
accounting_year (int): année comptable.
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
self.add_header()
|
|
self.add_title(accounting_year)
|
|
self.add_recette_expenses(accounting_year)
|
|
self.add_annexes(accounting_year)
|
|
|
|
def add_title(self, accounting_year):
|
|
""" Ajoute un titre au document.
|
|
|
|
Args:
|
|
accounting_year (int): année comptable.
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
self.add_string(
|
|
130,
|
|
"Comptes simplifiés de l'exercice " + accounting_year,
|
|
font_decoration="Bold",
|
|
font_size=20,
|
|
)
|
|
self.add_vspace(DOUBLE_LINE_HEIGHT)
|
|
|
|
def add_recette_expenses(self, accounting_year):
|
|
""" Ajoute l'état des recettes et dépense pour une année comptable passée en paramètre.
|
|
|
|
Args:
|
|
accounting_year (int): année comptable.
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
self.add_string(
|
|
X, "Etat recettes/dépenses (en €)", font_decoration="Oblique", font_size=14
|
|
)
|
|
self.__display_transactiontypes_table(accounting_year)
|
|
self.add_vspace()
|
|
|
|
def add_annexes(self, accounting_year):
|
|
""" Ajoute les annexes au document PDF
|
|
|
|
Args:
|
|
accounting_year (int): année comptable.
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
self.add_new_line(X, "Annexes", font_decoration="Oblique", font_size=14)
|
|
self.add_evaluation_rules(accounting_year)
|
|
self.add_modification_evaluation_rules(accounting_year)
|
|
self.add_additional_information(accounting_year)
|
|
self.add_patrimony(accounting_year)
|
|
self.add_right_engagement(accounting_year)
|
|
|
|
def add_evaluation_rules(self, accounting_year):
|
|
""" Ajoute les règles d'évaluation au PDF
|
|
|
|
Args:
|
|
accounting_year (int): année comptable.
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
self.add_new_line(X, "1. Résumé des règles d'évaluation")
|
|
rules_list = EvaluationRules.objects.filter(
|
|
Q(stop_date__year__lte=accounting_year) | Q(stop_date__isnull=True)
|
|
).exclude(start_date__year__gt=accounting_year)
|
|
if rules_list:
|
|
for rule in rules_list:
|
|
self.add_new_line(
|
|
INDENTED_X + INDENT * 2,
|
|
rule.label + " : " + rule.explanation,
|
|
font_size=9,
|
|
)
|
|
else:
|
|
self.add_new_line(
|
|
INDENTED_X + INDENT * 2, "Pas de règle d'évaluation.", font_size=9
|
|
)
|
|
self.add_vspace()
|
|
|
|
def add_modification_evaluation_rules(self, accounting_year):
|
|
""" Ajoute les modifications d'évaluation au PDF
|
|
|
|
Args:
|
|
accounting_year (int): année comptable.
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
self.add_new_line(X, "2. Adaptation des règles d'évaluation")
|
|
rules_adaptation_list = EvaluationRulesAdaptation.objects.filter(
|
|
start_date__year=accounting_year
|
|
)
|
|
if rules_adaptation_list:
|
|
for line in rules_adaptation_list:
|
|
self.add_new_line(
|
|
INDENTED_X + INDENT * 2,
|
|
line.label + " : " + line.information,
|
|
font_size=9,
|
|
)
|
|
else:
|
|
self.add_new_line(
|
|
INDENTED_X + INDENT * 2,
|
|
"Pas d'adaptation des règles d'évaluation.",
|
|
font_size=9,
|
|
)
|
|
self.add_vspace()
|
|
|
|
def add_additional_information(self, accounting_year):
|
|
""" Ajoute les informations complémentaires au PDF
|
|
|
|
Args:
|
|
accounting_year (int): année comptable.
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
self.add_new_line(X, "3. Informations complémentaires")
|
|
annuality = Annuality.objects.filter(year__year=accounting_year)
|
|
complementary_informations = ComplementaryInformations.objects.filter(
|
|
annuality=annuality[0]
|
|
)
|
|
if complementary_informations:
|
|
for line in complementary_informations:
|
|
self.add_new_line(
|
|
INDENTED_X + INDENT * 2,
|
|
line.label + " : " + line.information,
|
|
font_size=9,
|
|
)
|
|
else:
|
|
self.add_new_line(
|
|
INDENTED_X + INDENT * 2,
|
|
"Pas d'informations complémentaires.",
|
|
font_size=9,
|
|
)
|
|
self.add_vspace()
|
|
|
|
def add_patrimony(self, accounting_year):
|
|
""" Ajoute les informations du patrimoine au PDF
|
|
|
|
Args:
|
|
accounting_year (int): année comptable.
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
self.add_new_line(X, "4. Etat du patrimoine")
|
|
|
|
annuality = Annuality.objects.filter(year__year=accounting_year)
|
|
tmp_compta_expenses = get_transactiontype_and_sum_for_spf_export(
|
|
accounting_year, EXPENSES
|
|
)
|
|
tmp_compta_recettes = get_transactiontype_and_sum_for_spf_export(
|
|
accounting_year, RECETTES
|
|
)
|
|
|
|
assets_list = get_asset_liability_and_sump_spf(accounting_year, category=0)
|
|
for item in assets_list:
|
|
if item[0] == "Liquidité":
|
|
item[1] += (
|
|
annuality[0].opening_balance
|
|
+ tmp_compta_recettes["sum_total_transaction"]
|
|
- tmp_compta_expenses["sum_total_transaction"]
|
|
)
|
|
|
|
liability_list = get_asset_liability_and_sump_spf(accounting_year, category=1)
|
|
|
|
self.__display_table_header("AVOIRS", "DETTES")
|
|
save = self.y
|
|
self.__display_table_two_column(1, assets_list)
|
|
longest_y = self.y
|
|
self.y = save
|
|
self.__display_table_two_column(2, liability_list)
|
|
|
|
if self.y > longest_y:
|
|
self.y = longest_y
|
|
|
|
self.add_vspace()
|
|
|
|
def add_right_engagement(self, accounting_year):
|
|
""" Ajoute les droits & engagements au PDF
|
|
|
|
Args:
|
|
accounting_year (int): année comptable.
|
|
|
|
Returns:
|
|
ne retourne rien
|
|
"""
|
|
self.add_new_line(
|
|
X,
|
|
"5. Droits et engagements importants qui ne sont pas susceptibles d'être quantifiés",
|
|
)
|
|
# self.__display_table_header("DROITS", "ENGAGEMENT")
|
|
self.add_new_line(
|
|
INDENTED_X + INDENT * 2, "Pas de droits ou d'engagements.", font_size=9
|
|
)
|
|
self.add_vspace()
|
|
def __display_transactiontypes_table(self, accounting_year):
|
|
""" Ajoute le table pour les recettes & dépenses d'une année comptable.
|
|
|
|
Args:
|
|
accounting_year (int): année comptable
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
expenses = get_transactiontype_and_sum_for_spf_export(accounting_year, EXPENSES)
|
|
recettes = get_transactiontype_and_sum_for_spf_export(accounting_year, RECETTES)
|
|
|
|
self.__display_table_header("DEPENSES", "RECETTES")
|
|
self.__display_transactiontype_table_body(
|
|
expenses,
|
|
recettes,
|
|
int(expenses["sum_total_transaction"]),
|
|
int(recettes["sum_total_transaction"]),
|
|
)
|
|
|
|
def __display_table_header(self, title_left, title_right):
|
|
self.add_vspace()
|
|
self.document.rect(X, self.y - 5, RIGHT_X - X, -COMMON_LINE_HEIGHT, fill=0)
|
|
self.add_string(INDENTED_X, title_left, font_decoration="Bold", font_size=9)
|
|
self.add_string(
|
|
MIDDLE + INDENTED_X, title_right, font_decoration="Bold", font_size=9
|
|
)
|
|
|
|
def __display_transactiontype_table_body(
|
|
self, expenses, recettes, totalexpenses, totalrecettes
|
|
):
|
|
for i in range(4):
|
|
self.__display_table_line(
|
|
expenses["transaction_type_info"][i]["label"],
|
|
int(expenses["transaction_type_info"][i]["sum_total_amount"]),
|
|
recettes["transaction_type_info"][i]["label"],
|
|
int(recettes["transaction_type_info"][i]["sum_total_amount"]),
|
|
)
|
|
self.add_vspace()
|
|
self.document.rect(
|
|
X, self.y - 5, MIDDLE, 5 * -COMMON_LINE_HEIGHT
|
|
) # Séparation en deux
|
|
self.document.rect(
|
|
MIDDLE, self.y - 5, MIDDLE, 5 * -COMMON_LINE_HEIGHT
|
|
) # Séparation descriptif et montant recettes
|
|
self.y -= COMMON_LINE_HEIGHT
|
|
self.__display_table_line(
|
|
"Total des dépenses",
|
|
totalexpenses,
|
|
"Total des recettes",
|
|
totalrecettes,
|
|
font_decoration="Bold",
|
|
)
|
|
|
|
def __display_key_part(self, column_number, key, font_decoration=None):
|
|
""" Ajoute dans la colonne d'un tableau (à deux colonnes) la clef d'un couple clef/valeur.
|
|
|
|
Args:
|
|
column_number (int): numéro de la colonne du tableau
|
|
key (str): la clef à afficher
|
|
font_decoration (str): décoration de la police de carectères
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
if column_number == 1:
|
|
space = INDENTED_X
|
|
else:
|
|
space = MIDDLE + INDENTED_X
|
|
self.add_string(space, key, font_decoration=font_decoration, font_size=9)
|
|
|
|
def __display_value_part(self, column_number, value):
|
|
""" Ajoute dans la colonne d'un tableau (à deux colonnes) la valeur d'un couple clef/valeur
|
|
|
|
Args:
|
|
column_number (int): numéro de la colonne du tableau
|
|
value (str): la valeur à afficher
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
if column_number == 1:
|
|
space = MIDDLE + X - INDENT
|
|
else:
|
|
space = INDENTED_RIGHT_X
|
|
|
|
self.document.drawRightString(
|
|
space, self.y, str(locale.format("%d", int(value), grouping=True)),
|
|
)
|
|
|
|
def __display_column(self, column_number, key, value, font_decoration):
|
|
self.__display_key_part(column_number, key, font_decoration)
|
|
self.__display_value_part(column_number, value)
|
|
|
|
def __display_table_line(self, key1, value1, key2, value2, font_decoration=None):
|
|
self.document.rect(X, self.y - 5, RIGHT_X - X, COMMON_LINE_HEIGHT, fill=0)
|
|
self.add_vspace()
|
|
self.__display_column(1, key1, value1, font_decoration)
|
|
self.__display_column(2, key2, value2, font_decoration)
|
|
|
|
def __display_table_two_column(self, column_number, list):
|
|
if column_number == 1:
|
|
begin_rect_line = X
|
|
begin_rect_res = MIDDLE
|
|
begin_text = INDENTED_X
|
|
begin_res = MIDDLE + X - INDENT
|
|
else:
|
|
begin_rect_line = MIDDLE + X
|
|
begin_rect_res = MIDDLE * 2
|
|
begin_text = MIDDLE + INDENTED_X
|
|
begin_res = INDENTED_RIGHT_X
|
|
|
|
total = 0
|
|
# locale.setlocale(locale.LC_ALL, "pt_br.utf-8")
|
|
|
|
for line in list:
|
|
self.document.rect(
|
|
begin_rect_line, self.y - 5, MIDDLE, COMMON_LINE_HEIGHT, fill=0
|
|
)
|
|
self.document.rect(
|
|
begin_rect_res, self.y - 5, X, COMMON_LINE_HEIGHT, fill=0
|
|
)
|
|
self.add_new_line(begin_text, line[0], font_size=9)
|
|
self.document.drawRightString(
|
|
begin_res, self.y, str(locale.format("%d", int(line[1]), grouping=True))
|
|
)
|
|
|
|
total += int(line[1])
|
|
|
|
return total
|
|
|
|
|
|
class BillPaper(PDFDocument):
|
|
|
|
def generate(self, contract):
|
|
""" Génère une facture pour un contrat
|
|
|
|
Args:
|
|
contract (contract): instance de la class Contract.
|
|
|
|
Returns:
|
|
ne retourne rien
|
|
"""
|
|
self.amount = 0
|
|
self.styles = getSampleStyleSheet()
|
|
self.style = self.styles["BodyText"]
|
|
|
|
self.add_header(contract)
|
|
self.add_bill_title(contract)
|
|
self.add_prestations(contract)
|
|
self.add_conclusion(contract)
|
|
self.add_signature()
|
|
self.add_terms_of_sales()
|
|
|
|
def add_bill_title(self, contract):
|
|
""" Génère le titre de la facture.
|
|
|
|
Args:
|
|
contract (contract): instance de la class Contract.
|
|
|
|
Returns:
|
|
ne retourne rien
|
|
"""
|
|
self.y = 650
|
|
text = (("<b>" + contract.client.name + "</b>") if contract.client.is_company else "") + "<br />" + "A l'attention de <b>" + contract.client.contact + "</b><br />" + contract.client.address + "<br />" + str(contract.client.postal_code) + " " + contract.client.city + "<br /><br />Concernant la/le <b>" + contract.title + "</b>"
|
|
|
|
paragraph = Paragraph(text, self.style)
|
|
width, height = paragraph.wrap(10*cm, 10*cm)
|
|
paragraph.drawOn(self.document, TITLED_X, self.y)
|
|
self.add_vspace(BIG_LINE_HEIGHT)
|
|
|
|
def __add_section_title(self, title_text):
|
|
""" Ajout le titre d'une section """
|
|
text = "<b>" + title_text + "</b>"
|
|
paragraph = Paragraph(text, self.style)
|
|
width, height = paragraph.wrap(8*cm, 8*cm)
|
|
paragraph.drawOn(self.document, INDENTED_X, self.y)
|
|
self.add_vspace(-height)
|
|
|
|
def add_prestations(self, contract):
|
|
""" Génère l'affichage des prestations : tableau, liste des prestations, …
|
|
|
|
Args:
|
|
contract (contract): instance de la class Contract.
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
self.__add_section_title("Prestations")
|
|
|
|
total = 0
|
|
elements = []
|
|
|
|
prestations_list = contract.get_prestation.all()
|
|
for prestation in prestations_list:
|
|
total += prestation.total_amount
|
|
|
|
data = [['Date', 'Libellé', 'Nbre', 'Prix Unit.', 'Total']]
|
|
for prestation in prestations_list:
|
|
data.append(
|
|
[
|
|
str(prestation.date),
|
|
prestation.label,
|
|
str(prestation.unit),
|
|
str(prestation.unit_price),
|
|
str(prestation.total_amount)
|
|
]
|
|
)
|
|
data.append([' ', ' ', ' ', 'Total', str(total)])
|
|
style = TableStyle(
|
|
[
|
|
('BACKGROUND', (0,0), (-1,0), '#317BB5'), # première ligne bleue
|
|
('TEXTCOLOR', (0,0), (-1,0), '#FFFFFF'),
|
|
('ALIGN', (2,0), (-1, 0), 'CENTER'),
|
|
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
|
|
('BACKGROUND', (0,-1), (-1,-1), '#317BB5'), # dernière ligne bleue
|
|
('ALIGN', (2,1), (-1, -1), 'RIGHT'),
|
|
('FONTNAME', (0,-1), (-1,-1), 'Helvetica-Bold'),
|
|
('TEXTCOLOR', (0,-1), (-1,-1), '#FFFFFF'),
|
|
]
|
|
)
|
|
table = Table(data, [2.6*cm, 8.2*cm, 2.6*cm, 2.6*cm, 2.6*cm])
|
|
table.setStyle(style)
|
|
width, height = table.wrapOn(self.document, 19*cm, 15*cm)
|
|
self.add_vspace(-(9 * height / 10))
|
|
table.drawOn(self.document, X, self.y)
|
|
self.add_vspace(-height / 3)
|
|
|
|
self.document.drawRightString(INDENTED_X + 442, self.y, "Acompte")
|
|
self.document.drawRightString(INDENTED_RIGHT_X, self.y, str(contract.advance))
|
|
self.add_vspace()
|
|
self.document.setFont("Helvetica-Bold", 10)
|
|
self.document.drawRightString(INDENTED_X + 442, self.y, "Solde à payer")
|
|
self.amount = total - contract.advance
|
|
self.document.drawRightString(INDENTED_RIGHT_X, self.y, str(self.amount))
|
|
|
|
def add_conclusion(self, contract):
|
|
""" Affiche la add_add_conclusion de la facture.
|
|
|
|
Args:
|
|
contract (contract): instance de la class Contract.
|
|
|
|
Returns:
|
|
ne retourne rien.
|
|
"""
|
|
self.add_vspace(HUGE_LINE_HEIGHT)
|
|
text = "Merci de bien vouloir payer la somme de "
|
|
self.add_new_line(INDENTED_X, text)
|
|
space = len(text)
|
|
self.add_string(INDENTED_X + (space * 4.55), str(self.amount), font_decoration="Bold")
|
|
space += len(str(self.amount) + " ")
|
|
self.add_string(INDENTED_X + (space * 4.55), "€ sur le compte ")
|
|
space += len("€ sur le compte")
|
|
self.add_string(
|
|
INDENTED_X + (space * 4.55), self.club_infos["IBAN"], font_decoration="Bold"
|
|
)
|
|
space += len(" " + self.club_infos["IBAN"] + " ")
|
|
self.add_string(
|
|
INDENTED_X + (space * 4.55),
|
|
" (" + self.club_infos["BIC"] + " - " + self.club_infos["BANK"] + ")",
|
|
)
|
|
self.add_vspace(COMMON_LINE_HEIGHT)
|
|
|
|
if not contract.is_paid:
|
|
the_date = datetime.now()
|
|
pay_date = the_date + timedelta(days=25)
|
|
self.add_string(INDENTED_X, "Pour le ")
|
|
space = len("Pour le ")
|
|
date = str(pay_date.day) + "/" + str(pay_date.month) + "/" + str(pay_date.year)
|
|
self.add_string(INDENTED_X + (space * 4.55), date, font_decoration="Bold")
|
|
space += len(date + " ")
|
|
self.add_string(INDENTED_X + (space * 4.55), " au plus tard, avec la référence :")
|
|
space += len("au plus tard, avec la référence:")
|
|
self.add_string(
|
|
INDENTED_X + (space * 4.55),
|
|
'"' + str(contract.reference) + '"',
|
|
font_decoration="Bold",
|
|
)
|
|
|
|
def add_signature(self):
|
|
""" Génère la signature. """
|
|
self.add_vspace(HUGE_LINE_HEIGHT)
|
|
self.document.drawString(
|
|
INDENTED_X,
|
|
self.y,
|
|
self.club_infos["CITY"] + ", le " + date.today().strftime("%d-%m-%Y"),
|
|
)
|
|
self.document.drawRightString(RIGHT_X, self.y, "Président")
|
|
url = os.path.join(settings.STATICFILES_DIRS[0], "img/signature.png")
|
|
self.add_vspace(DOUBLE_LINE_HEIGHT)
|
|
self.document.drawImage(url, INDENTED_X + 340, self.y, width=180, height=39)
|
|
|
|
def add_terms_of_sales(self):
|
|
""" Ajoute les conditions générales de payement au bas de la facture """
|
|
self.y = 125
|
|
self.__add_section_title("Conditions générales de paiement")
|
|
|
|
lines = [
|
|
"Facture payable au comptant.",
|
|
"En cas de défaut de paiement à l'échéance, il est dû de plein droit et sans mise en demeure, un interêt fixé au taux de 15% l'an.",
|
|
"Tout réclamation, pour être admise, doit être faite dans les huit jours de la réception de la facture.",
|
|
"En cas de litige concernant la présente facture, seuls les tribunaux de Mons seront compétents."
|
|
]
|
|
|
|
text_object = self.document.beginText()
|
|
text_object.setTextOrigin(INDENTED_X, self.y)
|
|
text_object.setFont("Helvetica", 11)
|
|
|
|
for line in lines:
|
|
wraped_text = "\n".join(wrap(line, 108))
|
|
text_object.textLines(wraped_text)
|
|
|
|
self.document.drawText(text_object)
|