Compare commits
2 Commits
84b1136388
...
0172ec1d6a
Author | SHA1 | Date |
---|---|---|
Trullemans Gregory | 0172ec1d6a | |
Trullemans Gregory | 8e9dc50f26 |
|
@ -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
|
|
|
@ -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"
|
|
|
@ -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
|
||||||
|
|
|
@ -50,7 +50,7 @@ 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",
|
||||||
|
|
|
@ -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
|
|
|
@ -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"
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
@ -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
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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()
|
Loading…
Reference in New Issue