Compare commits

...

2 Commits

Author SHA1 Message Date
Trullemans Gregory 0172ec1d6a Refactoring and code lisibility improvement. 2020-02-26 16:20:39 +01:00
Trullemans Gregory 8e9dc50f26 [WIP] Code refactoring. 2020-02-26 15:18:19 +01:00
12 changed files with 733 additions and 826 deletions

View File

@ -1,17 +0,0 @@
import os
import yaml
def loadBillingConfig(request):
"""
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__))
club_informations = None
with open(os.path.join(current_path, "billing_config.yaml"), "r") as stream:
try:
club_informations = yaml.load(stream)
except yaml.YAMLError as exc:
print(exc)
return club_informations

View File

@ -1,12 +0,0 @@
SITE_TITLE: 'Flying Acrobatics Trampoline'
CLUB_NAME: 'Flying Acrobatics Trampoline Club'
NAME: "Flying Acrobatics Trampoline Club A.S.B.L."
ADDRESS: "Champ de l'Epine, 7"
CITY: "Hennuyères"
ZIP: "7090"
GSM: "0484/14.25.28"
BCE_NUMBER: "0660.737.571"
TVA: "N.A."
BANK: "CRELAN"
IBAN: "BE49 1030 4549 3371"
BIC: "NICABEBB"

View File

@ -2,24 +2,19 @@ from django.shortcuts import render
from django.http import HttpResponse from django.http import HttpResponse
from django.db.models import Sum from django.db.models import Sum
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from PIL import Image
import os import os
from django.conf import settings from django.conf import settings
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
from .billing_config import loadBillingConfig
from .models import ( from .models import (
Client, Client,
Contract, Contract,
Prestation, Prestation,
) )
from tools.pdf_generator import PDFDocument
def contract_listing(request): def contract_listing(request):
""" Récupère la liste de tous les contrats. """ """ Récupère la liste de tous les contrats. """
@ -65,327 +60,6 @@ def prestation_listing(request):
context = {"prestation_list": prestation_list} context = {"prestation_list": prestation_list}
return render(request, "prestation_listing.html", context) return render(request, "prestation_listing.html", context)
# Est-ce que ces variables ne devraient pas être dans un fichier de configuration à part ?
X = 35
Y = 841.89
RIGHT_X = 595.27 - X
TITLED_X = 125
INDENTED_X = X + 5
INDENTED_RIGHT_X = RIGHT_X - 5
PRESTATION_COLUMN_2 = INDENTED_X + 65
PRESTATION_COLUMN_3 = INDENTED_X + 400
PRESTATION_COLUMN_4 = INDENTED_X + 455
COMMON_LINE_HEIGHT = -15
SMALL_LINE_HEIGHT = COMMON_LINE_HEIGHT + 5
LARGE_LINE_HEIGHT = COMMON_LINE_HEIGHT - 5
DOUBLE_LINE_HEIGHT = COMMON_LINE_HEIGHT * 2
BIG_LINE_HEIGHT = COMMON_LINE_HEIGHT * 3
HUGE_LINE_HEIGHT = COMMON_LINE_HEIGHT * 4
class MyDocument(object):
# Create the PDF object, using the response object as its "file."
# http://www.reportlab.com/docs/reportlab-userguide.pdf
# canvas.rect(x, y, width, height, stroke=1, fill=0)
# localhost:8000/billing/contract/pdf/2
def __init__(self, response):
# Create the PDF object, using the response object as its "file."
self.document = canvas.Canvas(response, pagesize=A4)
self.y = Y - X
self.amount = 0
def newline(self, height=None):
""" Passe à la ligne, la hauteur de la ligne étant passée en paramètre.
Args:
height (int): hauteur de la ligne
"""
if height == DOUBLE_LINE_HEIGHT:
self.y += DOUBLE_LINE_HEIGHT
elif height == BIG_LINE_HEIGHT:
self.y += BIG_LINE_HEIGHT
elif height == HUGE_LINE_HEIGHT:
self.y += HUGE_LINE_HEIGHT
else:
self.y += COMMON_LINE_HEIGHT
# if y < 120;
# document.PageBreak()
# y = 790
def drawString(
self, x, string, font_family="Helvetica", font_decoration=None, font_size=10
):
font = font_family
if font_decoration is not None:
font += "-" + font_decoration
self.document.setFont(font, font_size)
self.document.drawString(x, self.y, string)
def drawNewLine(
self,
x,
string,
height=None,
font_family="Helvetica",
font_decoration=None,
font_size=10,
):
self.newline(height)
self.drawString(x, string, font_family, font_decoration, font_size)
def header(self, club_infos, contract):
""" Génère le header de la facture.
Args:
club_infos (array<string>): informations du club.
contract (contract): instance de la class Contract.
"""
# self.document.rect(X, 735, 525, 70, fill=0)
self.drawNewLine(INDENTED_X, club_infos["NAME"])
self.document.drawRightString(
RIGHT_X, self.y, "N° de Référence : " + str(contract.reference)
)
self.drawNewLine(INDENTED_X, club_infos["ADDRESS"])
self.drawNewLine(INDENTED_X, club_infos["CITY"] + " " + club_infos["ZIP"])
self.drawNewLine(INDENTED_X, club_infos["GSM"])
self.newline(DOUBLE_LINE_HEIGHT)
def title(self, contract):
""" Génère le "titre" de la facture.
Args:
contract (contract): instance de la class Contract.
"""
res = contract.client.contact.count("i")
res += contract.client.contact.count("t")
res += contract.client.contact.count("l")
res += contract.client.contact.count("j")
res += contract.client.contact.count("f")
# res += contract.client.contact.count('r')
self.drawString(TITLED_X, "A l'attention de")
self.drawString(194.25, contract.client.contact, font_decoration="Bold")
if contract.client.is_company:
self.drawString(
194.25 + ((len(contract.client.contact) - res / 1.5) * 5.8), " pour la"
)
self.drawNewLine(TITLED_X, contract.client.name, font_decoration="Bold")
self.drawNewLine(TITLED_X, contract.client.address)
self.drawNewLine(
TITLED_X, str(contract.client.postal_code) + " " + contract.client.city
)
self.newline()
self.drawNewLine(TITLED_X, "Concernant la/le")
self.drawString(200, contract.name, font_decoration="Bold")
self.newline()
def payementInformation(self, club_infos, contract):
""" Génère les informations de payement.
Args:
club_infos (array<string>): informations du club.
contract (contract): instance de la class Contract.
"""
self.newline()
height = 40
self.document.rect(X, self.y - height, RIGHT_X - X, height, fill=0)
self.drawNewLine(INDENTED_X, "N° Entreprise : " + club_infos["BCE_NUMBER"])
if contract.client.company_number:
self.document.drawRightString(
INDENTED_RIGHT_X,
self.y,
"Votre N° Entreprise : " + contract.client.company_number,
)
self.drawNewLine(
INDENTED_X,
"IBAN : "
+ club_infos["IBAN"]
+ " (BIC : "
+ club_infos["BIC"]
+ " - "
+ club_infos["BANK"]
+ ")",
)
self.newline(DOUBLE_LINE_HEIGHT)
def prestations(self, contract):
""" Génère l'affichage des prestations : tableau, liste des prestations, …
Args:
contract (contract): instance de la class Contract.
"""
self.drawNewLine(X, "Prestations", font_decoration="Bold")
total = self.drawPrestationsTable(contract.get_prestation.all())
self.newline(DOUBLE_LINE_HEIGHT)
self.document.setFont("Helvetica", 10)
self.document.drawRightString(INDENTED_X + 445, self.y, "Acompte")
self.document.drawRightString(INDENTED_RIGHT_X, self.y, str(contract.advance))
self.newline()
self.document.setFont("Helvetica-Bold", 10)
self.document.drawRightString(INDENTED_X + 445, self.y, "Solde à payer")
self.amount = total - contract.advance
self.document.drawRightString(INDENTED_RIGHT_X, self.y, str(self.amount))
def drawColoredRow(self):
""" Génère une ligne colorée. """
self.document.setFillColorCMYK(0.43, 0.2, 0, 0)
self.document.rect(
X, self.y - 4, RIGHT_X - X, COMMON_LINE_HEIGHT, fill=True, stroke=False
)
self.document.setFillColorCMYK(0, 0, 0, 1)
def drawHeaderPrestationsTable(self):
""" Génère le header de la table des prestations. """
self.drawColoredRow()
self.newline()
self.document.setFillColorCMYK(0, 0, 0, 1)
self.drawString(INDENTED_X, "Date")
self.drawString(PRESTATION_COLUMN_2, "Libellé")
self.drawString(INDENTED_X + 365, "Nbre")
self.drawString(INDENTED_X + 420, "Prix Unit.")
self.document.setFont("Helvetica-Bold", 10)
self.document.drawRightString(INDENTED_X + 510, self.y, "Total")
def drawFooterPrestationTable(self, total):
""" Génère le footer de la table des prestations.
Args:
total (int): somme totale des prestations.
"""
self.drawColoredRow()
self.newline()
self.document.setFont("Helvetica-Bold", 10)
self.document.drawRightString(INDENTED_X + 445, self.y, "Total")
self.document.drawRightString(INDENTED_RIGHT_X, self.y, str(total))
def displayPrestation(self, prestation, total):
""" Affiche une ligne de prestation dans le tableau.
Args:
prestation (prestation): instance de la classe Prestation.
total (int): somme totale des prestations.
Returns:
total (int): somme totale des prestations.
"""
total += prestation.total_amount
self.drawNewLine(INDENTED_X, str(prestation.date))
self.drawString(PRESTATION_COLUMN_2, prestation.label)
self.document.drawRightString(PRESTATION_COLUMN_3, self.y, str(prestation.unit))
self.document.drawRightString(
PRESTATION_COLUMN_4, self.y, str(prestation.unit_price)
)
self.document.drawRightString(
INDENTED_RIGHT_X, self.y, str(prestation.total_amount)
)
return total
def drawPrestationsTable(self, prestations_list):
""" Génère le tableau des prestations.
Args:
prestations_list (list): liste des prestations d'un contrat
"""
self.drawHeaderPrestationsTable()
total = 0
for prestation in prestations_list:
total = self.displayPrestation(prestation, total)
self.drawFooterPrestationTable(total)
return total
def conclusion(self, club_infos, contract):
""" Affiche la conclusion de la facture.
Args:
club_infos (array<string>): informations du club.
contract (contract): instance de la class Contract.
"""
self.newline(DOUBLE_LINE_HEIGHT)
self.document.rect(X, self.y, RIGHT_X - X, BIG_LINE_HEIGHT, fill=0)
self.drawNewLine(INDENTED_X, "Merci de bien vouloir payer la somme de ")
self.drawString(INDENTED_X + 184, str(self.amount), font_decoration="Bold")
self.drawString(INDENTED_X + 215, "€ sur le compte ")
self.drawString(INDENTED_X + 290, club_infos["IBAN"], font_decoration="Bold")
self.newline(COMMON_LINE_HEIGHT)
if not contract.is_paid:
the_date = datetime.now()
pay_date = the_date + timedelta(days=15)
self.drawString(INDENTED_X, "Pour le ")
self.drawString(
INDENTED_X + 35,
str(pay_date.day)
+ "/"
+ str(pay_date.month)
+ "/"
+ str(pay_date.year),
font_decoration="Bold",
)
self.drawString(INDENTED_X + 85, " au plus tard, avec la référence :")
self.drawString(
INDENTED_X + 230,
'"' + str(contract.reference) + '"',
font_decoration="Bold",
)
def addSignature(self, club_infos):
""" Génère la signature.
Args:
club_infos (array<string>): informations du club.
"""
self.newline(BIG_LINE_HEIGHT)
self.document.drawString(
INDENTED_X,
self.y,
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.newline(DOUBLE_LINE_HEIGHT)
self.document.drawImage(url, INDENTED_X + 340, self.y, width=180, height=39)
def footer(self):
""" Ajoute les conditions générales de payement. """
self.y = 175
self.newline(DOUBLE_LINE_HEIGHT)
self.drawNewLine(
INDENTED_X, "CONDITIONS GENERALES DE PAIEMENT", font_decoration="Bold"
)
self.drawNewLine(INDENTED_X, "Facture payable au comptant.")
self.drawNewLine(
INDENTED_X,
"""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""",
)
self.drawNewLine(INDENTED_X, "15% l'an.")
self.drawNewLine(
INDENTED_X,
"""Tout réclamation, pour être admise, doit être faite dans les huit jours de la
réception de la facture.""",
)
self.drawNewLine(
INDENTED_X,
"""En cas de litige concernant la présente facture, seuls les tribunaux de MONS seront
compétents.""",
)
def download(self):
# Close the PDF object cleanly, and we're done.
self.document.showPage()
self.document.save()
def contract_export(request, contract_id): def contract_export(request, contract_id):
""" Génere un fichier PDF pour fournir au client. """ Génere un fichier PDF pour fournir au client.
@ -393,7 +67,6 @@ def contract_export(request, contract_id):
contract_id (int): identifiant d'un contract. contract_id (int): identifiant d'un contract.
""" """
club_infos = loadBillingConfig(request)
contract = Contract.objects.get(pk=contract_id) contract = Contract.objects.get(pk=contract_id)
# Create the HttpResponse object with the appropriate PDF headers. # Create the HttpResponse object with the appropriate PDF headers.
@ -401,15 +74,16 @@ def contract_export(request, contract_id):
response["Content-Disposition"] = ( response["Content-Disposition"] = (
'attachment; filename="facture_' + str(contract.reference) + '.pdf"' 'attachment; filename="facture_' + str(contract.reference) + '.pdf"'
) )
document = MyDocument(response) document = PDFDocument(response)
document.generate_bill_paper(contract)
document.header(club_infos, contract) # document.header(club_infos, contract)
document.title(contract) # document.title(contract)
document.payementInformation(club_infos, contract) # document.payementInformation(club_infos, contract)
document.prestations(contract) # document.prestations(contract)
document.conclusion(club_infos, contract) # document.conclusion(club_infos, contract)
document.addSignature(club_infos) # document.addSignature(club_infos)
document.footer() # document.footer()
document.download() document.download()
return response return response

View File

@ -46,11 +46,11 @@ ROOT_URLCONF = "comptaClub.urls"
TEMPLATES = [ TEMPLATES = [
{ {
"BACKEND": "django.template.backends.django.DjangoTemplates", "BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [os.path.join(BASE_DIR, "templates"),], "DIRS": [os.path.join(BASE_DIR, "templates"), ],
"APP_DIRS": True, "APP_DIRS": True,
"OPTIONS": { "OPTIONS": {
"context_processors": [ "context_processors": [
"comptaClub.site_config.load", # "comptaClub.site_config.load",
"django.template.context_processors.debug", "django.template.context_processors.debug",
"django.template.context_processors.request", "django.template.context_processors.request",
"django.contrib.auth.context_processors.auth", "django.contrib.auth.context_processors.auth",

View File

@ -1,17 +0,0 @@
import os
import yaml
def load(request):
"""
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__))
informations = None
with open(os.path.join(current_path, "site_config.yaml"), "r") as stream:
try:
informations = yaml.load(stream)
except yaml.YAMLError as exc:
print(exc)
return informations

View File

@ -6,6 +6,8 @@ from django.utils import timezone
from eventCompta.models import Event from eventCompta.models import Event
from .utils import zero_or_value
class RuleOfEvaluation(models.Model): class RuleOfEvaluation(models.Model):
""" """
@ -144,9 +146,7 @@ class ComplementaryInformations(models.Model):
class TransactionType(models.Model): class TransactionType(models.Model):
""" """ Représente les types de transaction possible. """
Représente les types de transaction possible.
"""
class Meta: class Meta:
verbose_name = "Type de transaction" verbose_name = "Type de transaction"
@ -243,7 +243,7 @@ class Transaction(Comptability):
self.totalAmount = zero_or_value(self.bkAmount) + zero_or_value(self.bxAmount) self.totalAmount = zero_or_value(self.bkAmount) + zero_or_value(self.bxAmount)
def __compute_amount(self): def __compute_amount(self):
"""Calcule le montant de la transaction s'il n'est pas déjà fourni. """ """ Calcule le montant de la transaction s'il n'est pas déjà fourni. """
if self.amount is None: if self.amount is None:
self.amount = self.totalAmount self.amount = self.totalAmount

View File

@ -1,5 +1,6 @@
from decimal import Decimal from decimal import Decimal
from django.db.models import Sum from django.db.models import Sum
from .models import ( from .models import (
Transaction, Transaction,
TransactionType, TransactionType,
@ -9,10 +10,7 @@ from .models import (
RightEngagement, RightEngagement,
) )
from .utils import zero_or_value
def zero_or_value(value):
"""Retourne zéro si la valeur est nulle."""
return value if value else Decimal(0.00)
def get_transactions_and_sums_for_year_and_type(accounting_year, transaction_type_id): def get_transactions_and_sums_for_year_and_type(accounting_year, transaction_type_id):
@ -190,31 +188,31 @@ def get_transactiontype_and_sum_for_spf_export(accounting_year, transaction_type
""" """
sum_total_transaction = 0 sum_total_transaction = 0
transaction_type_info = [] transactiontypes_info = []
transaction_type_list = TransactionType.objects.filter( transactiontypes_list = TransactionType.objects.filter(
transaction_type=transaction_type_id, parent=None transaction_type=transaction_type_id, parent=None
).order_by("order") ).order_by("order")
for transaction_type in transaction_type_list: for transactiontype in transactiontypes_list:
sum_total_amount = get_transactions_totalamount_sum_value_for_type( sum_total_amount = get_transactions_totalamount_sum_value_for_type(
accounting_year, transaction_type accounting_year, transactiontype
) + get_transactions_totalamount_sum_value_for_parenttype( ) + get_transactions_totalamount_sum_value_for_parenttype(
accounting_year, transaction_type accounting_year, transactiontype
) )
sum_total_transaction += sum_total_amount sum_total_transaction += sum_total_amount
transaction_type_info.append( transactiontypes_info.append(
{ {
"label": transaction_type.label, "label": transactiontype.label,
"sum_total_amount": sum_total_amount "sum_total_amount": sum_total_amount
if sum_total_amount != 0 if sum_total_amount != 0
else Decimal(0.00), else Decimal(0.00),
"category": transaction_type.category, "category": transactiontype.category,
} }
) )
return { return {
"transaction_type_info": transaction_type_info, "transaction_type_info": transactiontypes_info,
"sum_total_transaction": sum_total_transaction, "sum_total_transaction": sum_total_transaction,
} }

View File

@ -0,0 +1,6 @@
from decimal import Decimal
def zero_or_value(value):
""" Retourne zéro si la valeur est nulle. """
return value if value else Decimal(0.00)

View File

@ -1,8 +1,5 @@
from itertools import zip_longest from itertools import zip_longest
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import render from django.shortcuts import render
from django.db.models import Q from django.db.models import Q
@ -26,7 +23,7 @@ from .tools import (
get_asset_liability_and_sump_spf, get_asset_liability_and_sump_spf,
) )
from comptaClub.site_config import load from tools.pdf_generator import PDFDocument
import locale import locale
@ -121,7 +118,7 @@ def export_year_spf_finance(request, accounting_year):
transactiontypes_list_recettes = get_transactiontype_and_sum_for_spf_export( transactiontypes_list_recettes = get_transactiontype_and_sum_for_spf_export(
accounting_year, RECETTES accounting_year, RECETTES
) )
list_sum = [ transactiontypes_list = [
x x
for x in zip_longest( for x in zip_longest(
transactiontypes_list_recettes["transaction_type_info"], transactiontypes_list_recettes["transaction_type_info"],
@ -166,7 +163,7 @@ def export_year_spf_finance(request, accounting_year):
annuality[0].save() annuality[0].save()
context = { context = {
"list_sum": list_sum, "transactiontypes_list": transactiontypes_list,
"accounting_year": accounting_year, "accounting_year": accounting_year,
"total_expenses": transactiontypes_list_expenses["sum_total_transaction"], "total_expenses": transactiontypes_list_expenses["sum_total_transaction"],
"total_recette": transactiontypes_list_recettes["sum_total_transaction"], "total_recette": transactiontypes_list_recettes["sum_total_transaction"],
@ -180,412 +177,6 @@ def export_year_spf_finance(request, accounting_year):
return render(request, "year_transaction_export_spf.html", context) return render(request, "year_transaction_export_spf.html", context)
# Est-ce que ces variables ne devraient pas être dans un fichier de configuration à part ?
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
PRESTATION_COLUMN_2 = INDENTED_X + 65
PRESTATION_COLUMN_3 = INDENTED_X + 400
PRESTATION_COLUMN_4 = INDENTED_X + 455
COMMON_LINE_HEIGHT = -15
SMALL_LINE_HEIGHT = COMMON_LINE_HEIGHT + 5
LARGE_LINE_HEIGHT = COMMON_LINE_HEIGHT - 5
DOUBLE_LINE_HEIGHT = COMMON_LINE_HEIGHT * 2
BIG_LINE_HEIGHT = COMMON_LINE_HEIGHT * 3
HUGE_LINE_HEIGHT = COMMON_LINE_HEIGHT * 4
class MyDocument(object):
# Create the PDF object, using the response object as its "file."
# http://www.reportlab.com/docs/reportlab-userguide.pdf
# canvas.rect(x, y, width, height, stroke=1, fill=0)
# localhost:8000/billing/contract/pdf/2
def __init__(self, response):
# Create the PDF object, using the response object as its "file."
self.document = canvas.Canvas(response, pagesize=A4)
self.y = Y - X
def newline(self, height=None):
""" Passe à la ligne, la hauteur de la ligne étant passée en paramètre.
Args:
height (int): hauteur de la ligne
"""
if height == DOUBLE_LINE_HEIGHT:
self.y += DOUBLE_LINE_HEIGHT
elif height == BIG_LINE_HEIGHT:
self.y += BIG_LINE_HEIGHT
elif height == HUGE_LINE_HEIGHT:
self.y += HUGE_LINE_HEIGHT
else:
self.y += COMMON_LINE_HEIGHT
# if y < 120;
# document.PageBreak()
# y = 790
def drawString(
self, x, string, font_family="Helvetica", font_decoration=None, font_size=10
):
font = font_family
if font_decoration is not None:
font += "-" + font_decoration
self.document.setFont(font, font_size)
self.document.drawString(x, self.y, string)
def drawNewLine(
self,
x,
string,
height=None,
font_family="Helvetica",
font_decoration=None,
font_size=10,
):
self.newline(height)
self.drawString(x, string, font_family, font_decoration, font_size)
def header(self, club_infos):
""" Génère le header de la facture.
Args:
club_infos (array<string>): informations du club.
"""
rect_height = 40
self.document.rect(X, self.y, RIGHT_X - X, -rect_height, fill=0)
self.drawNewLine(INDENTED_X, "A.S.B.L. : " + club_infos["CLUB_NAME"])
self.document.drawRightString(
INDENTED_RIGHT_X, self.y, "N° Entreprise : " + club_infos["BCE_NUMBER"]
)
self.drawNewLine(
INDENTED_X,
"Siège social : "
+ club_infos["ADDRESS"]
+ " - "
+ club_infos["CITY"]
+ " "
+ club_infos["ZIP"],
)
self.newline(BIG_LINE_HEIGHT)
def title(self, accounting_year):
self.drawString(
130,
"Comptes simplifiés de l'exercice " + accounting_year,
font_decoration="Bold",
font_size=20,
)
self.newline(DOUBLE_LINE_HEIGHT)
def recette_expenses(self, accounting_year):
self.drawString(
X, "Etat recettes/dépenses (en €)", font_decoration="Oblique", font_size=14
)
self.__display_table_transaction(accounting_year)
self.newline()
def __display_table_transaction(self, accounting_year):
self.__display_table_header("DEPENSES", "RECETTES")
transactions_extenses = get_transactiontype_and_sum_for_spf_export(
accounting_year, EXTENSES
)
transactions_recettes = get_transactiontype_and_sum_for_spf_export(
accounting_year, RECETTES
)
self.__display_table_transaction_body(
transactions_extenses, transactions_recettes
)
self.__display_table_footer(
int(transactions_extenses["sum_total_transaction"]),
int(transactions_recettes["sum_total_transaction"]),
)
def __display_table_header(self, title_left, title_right):
self.newline()
self.document.rect(X, self.y - 5, RIGHT_X - X, -COMMON_LINE_HEIGHT, fill=0)
self.drawString(INDENTED_X, title_left, font_decoration="Bold", font_size=9)
self.drawString(
MIDDLE + INDENTED_X, title_right, font_decoration="Bold", font_size=9
)
# TODO : à revoir !!!!!!
def __display_table_transaction_body(
self, transactions_extenses, transactions_recettes
):
for i in range(4):
self.__display_table_line(
transactions_extenses["sum"][i][0],
int(transactions_extenses["sum"][i][1]),
transactions_recettes["sum"][i][0],
int(transactions_recettes["sum"][i][1]),
)
self.newline()
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
def __display_table_footer(self, totaldepense, totalrecette):
locale.setlocale(locale.LC_ALL, "pt_br.utf-8")
self.document.rect(X, self.y - 5, RIGHT_X - X, COMMON_LINE_HEIGHT, fill=0)
self.newline()
# first column
self.drawString(
INDENTED_X, "Total des dépenses ", font_decoration="Bold", font_size=9
)
self.document.drawRightString(
MIDDLE + X - INDENT,
self.y,
str(locale.format("%d", int(totaldepense), grouping=True)),
)
# second column
self.drawString(
(MIDDLE + INDENTED_X),
"Total des recettes ",
font_decoration="Bold",
font_size=9,
)
self.document.drawRightString(
INDENTED_RIGHT_X,
self.y,
str(locale.format("%d", int(totalrecette), grouping=True)),
)
def __display_table_line(self, key1, value1, key2, value2):
locale.setlocale(locale.LC_ALL, "pt_br.utf-8")
self.newline()
self.document.rect(X, self.y - 5, RIGHT_X - X, 15, fill=0) # ligne entière
# first column
self.drawString(INDENTED_X, key1, font_size=9)
self.document.drawRightString(
MIDDLE + X - INDENT,
self.y,
str(locale.format("%d", int(value1), grouping=True)),
)
# second column
self.drawString((MIDDLE + INDENTED_X), key2, font_size=9)
self.document.drawRightString(
INDENTED_RIGHT_X,
self.y,
str(locale.format("%d", int(value2), grouping=True)),
)
def annexes(self, accounting_year):
""" Ajoute les annexes du document au PDF
Args:
accounting_year (int): année comptable
Returns:
Ne retourne rien
"""
self.drawNewLine(X, "Annexes", font_decoration="Oblique", font_size=14)
self.display_evaluation_rules(accounting_year)
self.display_modification_evaluation_rules(accounting_year)
self.display_additional_information(accounting_year)
self.display_patrimony(accounting_year)
self.display_right_engagement(accounting_year)
def display_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.drawNewLine(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.drawNewLine(
INDENTED_X + INDENT * 2,
rule.label + " : " + rule.explanation,
font_size=9,
)
else:
self.drawNewLine(
INDENTED_X + INDENT * 2, "Pas de règle d'évaluation.", font_size=9
)
self.newline()
def display_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.drawNewLine(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.drawNewLine(
INDENTED_X + INDENT * 2,
line.label + " : " + line.information,
font_size=9,
)
else:
self.drawNewLine(
INDENTED_X + INDENT * 2,
"Pas d'adaptation des règles d'évaluation.",
font_size=9,
)
self.newline()
def display_additional_information(self, accounting_year):
""" Ajoute les informations complémentaires au PDF
Args:
accounting_year (int): année comptable
Returns:
Ne retourne rien
"""
self.drawNewLine(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.drawNewLine(
INDENTED_X + INDENT * 2,
line.label + " : " + line.information,
font_size=9,
)
else:
self.drawNewLine(
INDENTED_X + INDENT * 2,
"Pas d'informations complémentaires.",
font_size=9,
)
self.newline()
def display_patrimony(self, accounting_year):
""" Ajoute les informations du patrimoine au PDF
Args:
accounting_year (int): année comptable
Returns:
Ne retourne rien
"""
self.drawNewLine(X, "4. Etat du patrimoine")
annuality = Annuality.objects.filter(year__year=accounting_year)
tmp_compta_extenses = get_transactiontype_and_sum_for_spf_export(
accounting_year, EXTENSES
)
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_extenses["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(assets_list, 1)
longest_y = self.y
self.y = save
self.__display_table_two_column(liability_list, 2)
if self.y > longest_y:
self.y = longest_y
self.newline()
def __display_table_two_column(self, list, column=1):
if column == 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.drawNewLine(begin_text, line[0])
self.document.drawRightString(
begin_res, self.y, str(locale.format("%d", int(line[1]), grouping=True))
)
total += int(line[1])
return total
def display_right_engagement(self, accounting_year):
""" Ajoute les droits & engagements au PDF
Args:
accounting_year (int): année comptable
Returns:
Ne retourne rien
"""
self.drawNewLine(
X,
"5. Droits et engagements importants qui ne sont pas susceptibles d'être quantifiés",
)
# self.__display_table_header("DROITS", "ENGAGEMENT")
self.drawNewLine(
INDENTED_X + INDENT * 2, "Pas de droits ou d'engagements.", font_size=9
)
self.newline()
def download(self):
# Close the PDF object cleanly, and we're done.
self.document.showPage()
self.document.save()
def generate_pdf_for_spf(request, accounting_year): def generate_pdf_for_spf(request, accounting_year):
""" Génère un fichier PDF suivant les contraintes du SPF Finances """ Génère un fichier PDF suivant les contraintes du SPF Finances
@ -599,13 +190,8 @@ def generate_pdf_for_spf(request, accounting_year):
response["Content-Disposition"] = ( response["Content-Disposition"] = (
'attachment; filename="comptes_simplifies_annuels_' + accounting_year + '.pdf"' 'attachment; filename="comptes_simplifies_annuels_' + accounting_year + '.pdf"'
) )
document = MyDocument(response) document = PDFDocument(response)
info = load(request) document.generate_spf_export(accounting_year)
document.header(info)
document.title(accounting_year)
document.recette_expenses(accounting_year)
document.annexes(accounting_year)
document.download() document.download()
return response return response

View File

@ -32,19 +32,19 @@
<th class="col-md-2 centered" colspan="2">Recettes</th> <th class="col-md-2 centered" colspan="2">Recettes</th>
</tr> </tr>
</thead> </thead>
{% for line in list_sum %} {% for transactiontype in transactiontypes_list %}
<tr> <tr>
<td> <td>
{{ line.1.0 }} {{ transactiontype.1.label }}
</td> </td>
<td class="push-right"> <td class="push-right">
{% if line.1.1 == None %}0,00{% else %}{{ line.1.1|floatformat:0|intdot }}{% endif %} {% if transactiontype.1.sum_total_amount == None %}0,00{% else %}{{ transactiontype.1.sum_total_amount|floatformat:0|intdot }}{% endif %}
</td> </td>
<td> <td>
{{ line.0.0 }} {{ transactiontype.0.label }}
</td> </td>
<td class="push-right"> <td class="push-right">
{% if line.0.1 == None %}0,00{% else %}{{ line.0.1|floatformat:0|intdot }}{% endif %} {% if transactiontype.0.sum_total_amount == None %}0,00{% else %}{{ transactiontype.0.sum_total_amount|floatformat:0|intdot }}{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

689
src/tools/pdf_generator.py Normal file
View File

@ -0,0 +1,689 @@
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from django.conf import settings
from datetime import date, datetime, timedelta
from django.db.models import Q
import os
import locale
from PIL import Image
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
EXTENSES = 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
PRESTATION_COLUMN_2 = INDENTED_X + 65
PRESTATION_COLUMN_3 = INDENTED_X + 400
PRESTATION_COLUMN_4 = INDENTED_X + 455
COMMON_LINE_HEIGHT = -15
SMALL_LINE_HEIGHT = COMMON_LINE_HEIGHT + 5
LARGE_LINE_HEIGHT = COMMON_LINE_HEIGHT - 5
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."
# http://www.reportlab.com/docs/reportlab-userguide.pdf
# canvas.rect(x, y, width, height, stroke=1, fill=0)
# localhost:8000/billing/contract/pdf/2
def __init__(self, response):
# Create the PDF object, using the response object as its "file."
self.document = canvas.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)
except yaml.YAMLError as exc:
print(exc)
def generate_spf_export(self, accounting_year):
""" Genère un document aux normes du SPF Finance """
self.header()
self.title_spf(accounting_year)
self.recette_expenses(accounting_year)
self.annexes(accounting_year)
def generate_bill_paper(self, contract):
""" Génère une facture pour un contrat """
self.amount = 0
self.header(contract)
self.title_billing(contract)
self.prestations(contract)
self.conclusion(contract)
self.addSignature()
self.footer()
def newline(self, height=None):
""" Passe à la ligne, la hauteur de la ligne étant passée en paramètre.
Args:
height (int): hauteur de la ligne
"""
if height == DOUBLE_LINE_HEIGHT:
self.y += DOUBLE_LINE_HEIGHT
elif height == BIG_LINE_HEIGHT:
self.y += BIG_LINE_HEIGHT
elif height == HUGE_LINE_HEIGHT:
self.y += HUGE_LINE_HEIGHT
else:
self.y += COMMON_LINE_HEIGHT
# if y < 120;
# document.PageBreak()
# y = 790
def drawString(
self, x, string, font_family="Helvetica", font_decoration=None, font_size=10
):
font = font_family
if font_decoration is not None:
font += "-" + font_decoration
self.document.setFont(font, font_size)
self.document.drawString(x, self.y, string)
def drawNewLine(
self,
x,
string,
height=None,
font_family="Helvetica",
font_decoration=None,
font_size=10,
):
self.newline(height)
self.drawString(x, string, font_family, font_decoration, font_size)
def header(self, contract=None):
""" Génère le header de la facture.
Args:
contract (contract): instance de la class Contract.
"""
rect_height = 40
self.document.rect(X, self.y, RIGHT_X - X, -rect_height, fill=0)
self.drawNewLine(INDENTED_X, self.club_infos["NAME"])
self.document.drawRightString(
INDENTED_RIGHT_X, self.y, "N° Entreprise : " + self.club_infos["BCE_NUMBER"]
)
self.drawNewLine(
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.newline(BIG_LINE_HEIGHT)
def title_billing(self, contract):
""" Génère le "titre" de la facture.
Args:
contract (contract): instance de la class Contract.
"""
res = contract.client.contact.count("i")
res += contract.client.contact.count("t")
res += contract.client.contact.count("l")
res += contract.client.contact.count("j")
res += contract.client.contact.count("f")
# res += contract.client.contact.count('r')
self.drawString(TITLED_X, "A l'attention de")
self.drawString(194.25, contract.client.contact, font_decoration="Bold")
if contract.client.is_company:
self.drawString(
194.25 + ((len(contract.client.contact) - res / 1.5) * 5.8), " pour la"
)
self.drawNewLine(TITLED_X, contract.client.name, font_decoration="Bold")
self.drawNewLine(TITLED_X, contract.client.address)
self.drawNewLine(
TITLED_X, str(contract.client.postal_code) + " " + contract.client.city
)
self.newline()
self.drawNewLine(TITLED_X, "Concernant la/le")
self.drawString(200, contract.name, font_decoration="Bold")
self.newline()
def title_spf(self, accounting_year):
self.drawString(
130,
"Comptes simplifiés de l'exercice " + accounting_year,
font_decoration="Bold",
font_size=20,
)
self.newline(DOUBLE_LINE_HEIGHT)
def payementInformation(self, contract):
""" Génère les informations de payement.
Args:
contract (contract): instance de la class Contract.
"""
self.newline()
height = 40
self.document.rect(X, self.y - height, RIGHT_X - X, height, fill=0)
self.drawNewLine(INDENTED_X, "N° Entreprise : " + self.club_infos["BCE_NUMBER"])
if contract.client.company_number:
self.document.drawRightString(
INDENTED_RIGHT_X,
self.y,
"Votre N° Entreprise : " + contract.client.company_number,
)
self.drawNewLine(
INDENTED_X,
"IBAN : "
+ self.club_infos["IBAN"]
+ " (BIC : "
+ self.club_infos["BIC"]
+ " - "
+ self.club_infos["BANK"]
+ ")",
)
self.newline(DOUBLE_LINE_HEIGHT)
def prestations(self, contract):
""" Génère l'affichage des prestations : tableau, liste des prestations, …
Args:
contract (contract): instance de la class Contract.
"""
self.drawNewLine(X, "Prestations", font_decoration="Bold")
total = self.drawPrestationsTable(contract.get_prestation.all())
self.newline(DOUBLE_LINE_HEIGHT)
self.document.setFont("Helvetica", 10)
self.document.drawRightString(INDENTED_X + 445, self.y, "Acompte")
self.document.drawRightString(INDENTED_RIGHT_X, self.y, str(contract.advance))
self.newline()
self.document.setFont("Helvetica-Bold", 10)
self.document.drawRightString(INDENTED_X + 445, self.y, "Solde à payer")
self.amount = total - contract.advance
self.document.drawRightString(INDENTED_RIGHT_X, self.y, str(self.amount))
def drawColoredRow(self):
""" Génère une ligne colorée. """
self.document.setFillColorCMYK(0.43, 0.2, 0, 0)
self.document.rect(
X, self.y - 4, RIGHT_X - X, COMMON_LINE_HEIGHT, fill=True, stroke=False
)
self.document.setFillColorCMYK(0, 0, 0, 1)
def drawHeaderPrestationsTable(self):
""" Génère le header de la table des prestations. """
self.drawColoredRow()
self.newline()
self.document.setFillColorCMYK(0, 0, 0, 1)
self.drawString(INDENTED_X, "Date")
self.drawString(PRESTATION_COLUMN_2, "Libellé")
self.drawString(INDENTED_X + 365, "Nbre")
self.drawString(INDENTED_X + 420, "Prix Unit.")
self.document.setFont("Helvetica-Bold", 10)
self.document.drawRightString(INDENTED_X + 510, self.y, "Total")
def drawFooterPrestationTable(self, total):
""" Génère le footer de la table des prestations.
Args:
total (int): somme totale des prestations.
"""
self.drawColoredRow()
self.newline()
self.document.setFont("Helvetica-Bold", 10)
self.document.drawRightString(INDENTED_X + 445, self.y, "Total")
self.document.drawRightString(INDENTED_RIGHT_X, self.y, str(total))
def displayPrestation(self, prestation, total):
""" Affiche une ligne de prestation dans le tableau.
Args:
prestation (prestation): instance de la classe Prestation.
total (int): somme totale des prestations.
Returns:
total (int): somme totale des prestations.
"""
total += prestation.total_amount
self.drawNewLine(INDENTED_X, str(prestation.date))
self.drawString(PRESTATION_COLUMN_2, prestation.label)
self.document.drawRightString(PRESTATION_COLUMN_3, self.y, str(prestation.unit))
self.document.drawRightString(
PRESTATION_COLUMN_4, self.y, str(prestation.unit_price)
)
self.document.drawRightString(
INDENTED_RIGHT_X, self.y, str(prestation.total_amount)
)
return total
def drawPrestationsTable(self, prestations_list):
""" Génère le tableau des prestations.
Args:
prestations_list (list): liste des prestations d'un contrat
"""
self.drawHeaderPrestationsTable()
total = 0
for prestation in prestations_list:
total = self.displayPrestation(prestation, total)
self.drawFooterPrestationTable(total)
return total
def conclusion(self, contract):
""" Affiche la conclusion de la facture.
Args:
contract (contract): instance de la class Contract.
"""
self.newline(DOUBLE_LINE_HEIGHT)
self.document.rect(X, self.y, RIGHT_X - X, BIG_LINE_HEIGHT, fill=0)
self.drawNewLine(INDENTED_X, "Merci de bien vouloir payer la somme de ")
self.drawString(INDENTED_X + 184, str(self.amount), font_decoration="Bold")
self.drawString(INDENTED_X + 215, "€ sur le compte ")
self.drawString(INDENTED_X + 290, self.club_infos["IBAN"], font_decoration="Bold")
self.drawString(INDENTED_X + 390, " (" + self.club_infos["BIC"] + " - " + self.club_infos["BANK"] + ")")
self.newline(COMMON_LINE_HEIGHT)
if not contract.is_paid:
the_date = datetime.now()
pay_date = the_date + timedelta(days=15)
self.drawString(INDENTED_X, "Pour le ")
self.drawString(
INDENTED_X + 35,
str(pay_date.day)
+ "/"
+ str(pay_date.month)
+ "/"
+ str(pay_date.year),
font_decoration="Bold",
)
self.drawString(INDENTED_X + 85, " au plus tard, avec la référence :")
self.drawString(
INDENTED_X + 230,
'"' + str(contract.reference) + '"',
font_decoration="Bold",
)
def addSignature(self):
""" Génère la signature. """
self.newline(BIG_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.newline(DOUBLE_LINE_HEIGHT)
self.document.drawImage(url, INDENTED_X + 340, self.y, width=180, height=39)
def footer(self):
""" Ajoute les conditions générales de payement. """
self.y = 175
self.newline(DOUBLE_LINE_HEIGHT)
self.drawNewLine(
INDENTED_X, "CONDITIONS GENERALES DE PAIEMENT", font_decoration="Bold"
)
self.drawNewLine(INDENTED_X, "Facture payable au comptant.")
self.drawNewLine(
INDENTED_X,
"""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""",
)
self.drawNewLine(INDENTED_X, "15% l'an.")
self.drawNewLine(
INDENTED_X,
"""Tout réclamation, pour être admise, doit être faite dans les huit jours de la réception de la facture.""",
)
self.drawNewLine(
INDENTED_X,
"""En cas de litige concernant la présente facture, seuls les tribunaux de MONS seront compétents.""",
)
def recette_expenses(self, accounting_year):
self.drawString(
X, "Etat recettes/dépenses (en €)", font_decoration="Oblique", font_size=14
)
self.__display_transactions_table(accounting_year)
self.newline()
def __display_transactions_table(self, accounting_year):
self.__display_table_header("DEPENSES", "RECETTES")
transactiontypes_extenses = get_transactiontype_and_sum_for_spf_export(
accounting_year, EXTENSES
)
transactiontypes_recettes = get_transactiontype_and_sum_for_spf_export(
accounting_year, RECETTES
)
self.__display_transactiontype_table_body(
transactiontypes_extenses, transactiontypes_recettes
)
self.__display_transactiontype_table_footer(
int(transactiontypes_extenses["sum_total_transaction"]),
int(transactiontypes_recettes["sum_total_transaction"]),
)
def __display_table_header(self, title_left, title_right):
self.newline()
self.document.rect(X, self.y - 5, RIGHT_X - X, -COMMON_LINE_HEIGHT, fill=0)
self.drawString(INDENTED_X, title_left, font_decoration="Bold", font_size=9)
self.drawString(
MIDDLE + INDENTED_X, title_right, font_decoration="Bold", font_size=9
)
# TODO : à revoir !!!!!!
def __display_transactiontype_table_body(
self, transactions_extenses, transactions_recettes
):
for i in range(4):
self.__display_table_line(
transactions_extenses["transaction_type_info"][i]["label"],
int(transactions_extenses["transaction_type_info"][i]["sum_total_amount"]),
transactions_recettes["transaction_type_info"][i]["label"],
int(transactions_recettes["transaction_type_info"][i]["sum_total_amount"]),
)
self.newline()
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
def __display_transactiontype_table_footer(self, totalexpenses, totalrecette):
locale.setlocale(locale.LC_ALL, "pt_br.utf-8")
self.document.rect(X, self.y - 5, RIGHT_X - X, COMMON_LINE_HEIGHT, fill=0)
self.newline()
# first column
self.drawString(
INDENTED_X, "Total des dépenses ", font_decoration="Bold", font_size=9
)
self.document.drawRightString(
MIDDLE + X - INDENT,
self.y,
str(locale.format("%d", int(totalexpenses), grouping=True)),
)
# second column
self.drawString(
(MIDDLE + INDENTED_X),
"Total des recettes ",
font_decoration="Bold",
font_size=9,
)
self.document.drawRightString(
INDENTED_RIGHT_X,
self.y,
str(locale.format("%d", int(totalrecette), grouping=True)),
)
def __display_table_line(self, key1, value1, key2, value2):
locale.setlocale(locale.LC_ALL, "pt_br.utf-8")
self.newline()
self.document.rect(X, self.y - 5, RIGHT_X - X, 15, fill=0) # ligne entière
# first column
self.drawString(INDENTED_X, key1, font_size=9)
self.document.drawRightString(
MIDDLE + X - INDENT,
self.y,
str(locale.format("%d", int(value1), grouping=True)),
)
# second column
self.drawString((MIDDLE + INDENTED_X), key2, font_size=9)
self.document.drawRightString(
INDENTED_RIGHT_X,
self.y,
str(locale.format("%d", int(value2), grouping=True)),
)
def annexes(self, accounting_year):
""" Ajoute les annexes du document au PDF
Args:
accounting_year (int): année comptable
Returns:
Ne retourne rien
"""
self.drawNewLine(X, "Annexes", font_decoration="Oblique", font_size=14)
self.display_evaluation_rules(accounting_year)
self.display_modification_evaluation_rules(accounting_year)
self.display_additional_information(accounting_year)
self.display_patrimony(accounting_year)
self.display_right_engagement(accounting_year)
def display_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.drawNewLine(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.drawNewLine(
INDENTED_X + INDENT * 2,
rule.label + " : " + rule.explanation,
font_size=9,
)
else:
self.drawNewLine(
INDENTED_X + INDENT * 2, "Pas de règle d'évaluation.", font_size=9
)
self.newline()
def display_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.drawNewLine(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.drawNewLine(
INDENTED_X + INDENT * 2,
line.label + " : " + line.information,
font_size=9,
)
else:
self.drawNewLine(
INDENTED_X + INDENT * 2,
"Pas d'adaptation des règles d'évaluation.",
font_size=9,
)
self.newline()
def display_additional_information(self, accounting_year):
""" Ajoute les informations complémentaires au PDF
Args:
accounting_year (int): année comptable
Returns:
Ne retourne rien
"""
self.drawNewLine(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.drawNewLine(
INDENTED_X + INDENT * 2,
line.label + " : " + line.information,
font_size=9,
)
else:
self.drawNewLine(
INDENTED_X + INDENT * 2,
"Pas d'informations complémentaires.",
font_size=9,
)
self.newline()
def display_patrimony(self, accounting_year):
""" Ajoute les informations du patrimoine au PDF
Args:
accounting_year (int): année comptable
Returns:
Ne retourne rien
"""
self.drawNewLine(X, "4. Etat du patrimoine")
annuality = Annuality.objects.filter(year__year=accounting_year)
tmp_compta_extenses = get_transactiontype_and_sum_for_spf_export(
accounting_year, EXTENSES
)
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_extenses["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(assets_list, 1)
longest_y = self.y
self.y = save
self.__display_table_two_column(liability_list, 2)
if self.y > longest_y:
self.y = longest_y
self.newline()
def __display_table_two_column(self, list, column=1):
if column == 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.drawNewLine(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
def display_right_engagement(self, accounting_year):
""" Ajoute les droits & engagements au PDF
Args:
accounting_year (int): année comptable
Returns:
Ne retourne rien
"""
self.drawNewLine(
X,
"5. Droits et engagements importants qui ne sont pas susceptibles d'être quantifiés",
)
# self.__display_table_header("DROITS", "ENGAGEMENT")
self.drawNewLine(
INDENTED_X + INDENT * 2, "Pas de droits ou d'engagements.", font_size=9
)
self.newline()
def download(self):
# Close the PDF object cleanly, and we're done.
self.document.showPage()
self.document.save()