Compare commits
No commits in common. "0172ec1d6ac51ec12b709bfcb53e7181ac89257b" and "84b1136388aeccbcb787ff7765f8cfacb7611491" have entirely different histories.
0172ec1d6a
...
84b1136388
|
@ -0,0 +1,17 @@
|
|||
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
|
|
@ -2,19 +2,24 @@ from django.shortcuts import render
|
|||
from django.http import HttpResponse
|
||||
from django.db.models import Sum
|
||||
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.lib.pagesizes import A4
|
||||
|
||||
from PIL import Image
|
||||
|
||||
import os
|
||||
from django.conf import settings
|
||||
|
||||
from datetime import date, datetime, timedelta
|
||||
|
||||
from .billing_config import loadBillingConfig
|
||||
|
||||
from .models import (
|
||||
Client,
|
||||
Contract,
|
||||
Prestation,
|
||||
)
|
||||
|
||||
from tools.pdf_generator import PDFDocument
|
||||
|
||||
|
||||
def contract_listing(request):
|
||||
""" Récupère la liste de tous les contrats. """
|
||||
|
@ -60,6 +65,327 @@ def prestation_listing(request):
|
|||
context = {"prestation_list": prestation_list}
|
||||
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):
|
||||
""" Génere un fichier PDF pour fournir au client.
|
||||
|
||||
|
@ -67,6 +393,7 @@ def contract_export(request, contract_id):
|
|||
contract_id (int): identifiant d'un contract.
|
||||
"""
|
||||
|
||||
club_infos = loadBillingConfig(request)
|
||||
contract = Contract.objects.get(pk=contract_id)
|
||||
|
||||
# Create the HttpResponse object with the appropriate PDF headers.
|
||||
|
@ -74,16 +401,15 @@ def contract_export(request, contract_id):
|
|||
response["Content-Disposition"] = (
|
||||
'attachment; filename="facture_' + str(contract.reference) + '.pdf"'
|
||||
)
|
||||
document = PDFDocument(response)
|
||||
document.generate_bill_paper(contract)
|
||||
document = MyDocument(response)
|
||||
|
||||
# document.header(club_infos, contract)
|
||||
# document.title(contract)
|
||||
# document.payementInformation(club_infos, contract)
|
||||
# document.prestations(contract)
|
||||
# document.conclusion(club_infos, contract)
|
||||
# document.addSignature(club_infos)
|
||||
# document.footer()
|
||||
document.header(club_infos, contract)
|
||||
document.title(contract)
|
||||
document.payementInformation(club_infos, contract)
|
||||
document.prestations(contract)
|
||||
document.conclusion(club_infos, contract)
|
||||
document.addSignature(club_infos)
|
||||
document.footer()
|
||||
|
||||
document.download()
|
||||
return response
|
||||
|
|
|
@ -50,7 +50,7 @@ TEMPLATES = [
|
|||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
# "comptaClub.site_config.load",
|
||||
"comptaClub.site_config.load",
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
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
|
|
@ -0,0 +1,12 @@
|
|||
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"
|
|
@ -6,8 +6,6 @@ from django.utils import timezone
|
|||
|
||||
from eventCompta.models import Event
|
||||
|
||||
from .utils import zero_or_value
|
||||
|
||||
|
||||
class RuleOfEvaluation(models.Model):
|
||||
"""
|
||||
|
@ -146,7 +144,9 @@ class ComplementaryInformations(models.Model):
|
|||
|
||||
|
||||
class TransactionType(models.Model):
|
||||
""" Représente les types de transaction possible. """
|
||||
"""
|
||||
Représente les types de transaction possible.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Type de transaction"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from decimal import Decimal
|
||||
from django.db.models import Sum
|
||||
|
||||
from .models import (
|
||||
Transaction,
|
||||
TransactionType,
|
||||
|
@ -10,7 +9,10 @@ from .models import (
|
|||
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):
|
||||
|
@ -188,31 +190,31 @@ def get_transactiontype_and_sum_for_spf_export(accounting_year, transaction_type
|
|||
"""
|
||||
|
||||
sum_total_transaction = 0
|
||||
transactiontypes_info = []
|
||||
transactiontypes_list = TransactionType.objects.filter(
|
||||
transaction_type_info = []
|
||||
transaction_type_list = TransactionType.objects.filter(
|
||||
transaction_type=transaction_type_id, parent=None
|
||||
).order_by("order")
|
||||
|
||||
for transactiontype in transactiontypes_list:
|
||||
for transaction_type in transaction_type_list:
|
||||
sum_total_amount = get_transactions_totalamount_sum_value_for_type(
|
||||
accounting_year, transactiontype
|
||||
accounting_year, transaction_type
|
||||
) + get_transactions_totalamount_sum_value_for_parenttype(
|
||||
accounting_year, transactiontype
|
||||
accounting_year, transaction_type
|
||||
)
|
||||
sum_total_transaction += sum_total_amount
|
||||
|
||||
transactiontypes_info.append(
|
||||
transaction_type_info.append(
|
||||
{
|
||||
"label": transactiontype.label,
|
||||
"label": transaction_type.label,
|
||||
"sum_total_amount": sum_total_amount
|
||||
if sum_total_amount != 0
|
||||
else Decimal(0.00),
|
||||
"category": transactiontype.category,
|
||||
"category": transaction_type.category,
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"transaction_type_info": transactiontypes_info,
|
||||
"transaction_type_info": transaction_type_info,
|
||||
"sum_total_transaction": sum_total_transaction,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
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,5 +1,8 @@
|
|||
from itertools import zip_longest
|
||||
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.lib.pagesizes import A4
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render
|
||||
from django.db.models import Q
|
||||
|
@ -23,7 +26,7 @@ from .tools import (
|
|||
get_asset_liability_and_sump_spf,
|
||||
)
|
||||
|
||||
from tools.pdf_generator import PDFDocument
|
||||
from comptaClub.site_config import load
|
||||
|
||||
import locale
|
||||
|
||||
|
@ -118,7 +121,7 @@ def export_year_spf_finance(request, accounting_year):
|
|||
transactiontypes_list_recettes = get_transactiontype_and_sum_for_spf_export(
|
||||
accounting_year, RECETTES
|
||||
)
|
||||
transactiontypes_list = [
|
||||
list_sum = [
|
||||
x
|
||||
for x in zip_longest(
|
||||
transactiontypes_list_recettes["transaction_type_info"],
|
||||
|
@ -163,7 +166,7 @@ def export_year_spf_finance(request, accounting_year):
|
|||
annuality[0].save()
|
||||
|
||||
context = {
|
||||
"transactiontypes_list": transactiontypes_list,
|
||||
"list_sum": list_sum,
|
||||
"accounting_year": accounting_year,
|
||||
"total_expenses": transactiontypes_list_expenses["sum_total_transaction"],
|
||||
"total_recette": transactiontypes_list_recettes["sum_total_transaction"],
|
||||
|
@ -177,6 +180,412 @@ def export_year_spf_finance(request, accounting_year):
|
|||
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):
|
||||
""" Génère un fichier PDF suivant les contraintes du SPF Finances
|
||||
|
||||
|
@ -190,8 +599,13 @@ def generate_pdf_for_spf(request, accounting_year):
|
|||
response["Content-Disposition"] = (
|
||||
'attachment; filename="comptes_simplifies_annuels_' + accounting_year + '.pdf"'
|
||||
)
|
||||
document = PDFDocument(response)
|
||||
document.generate_spf_export(accounting_year)
|
||||
document = MyDocument(response)
|
||||
info = load(request)
|
||||
|
||||
document.header(info)
|
||||
document.title(accounting_year)
|
||||
document.recette_expenses(accounting_year)
|
||||
document.annexes(accounting_year)
|
||||
document.download()
|
||||
|
||||
return response
|
||||
|
|
|
@ -32,19 +32,19 @@
|
|||
<th class="col-md-2 centered" colspan="2">Recettes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for transactiontype in transactiontypes_list %}
|
||||
{% for line in list_sum %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ transactiontype.1.label }}
|
||||
{{ line.1.0 }}
|
||||
</td>
|
||||
<td class="push-right">
|
||||
{% if transactiontype.1.sum_total_amount == None %}0,00{% else %}{{ transactiontype.1.sum_total_amount|floatformat:0|intdot }}{% endif %}
|
||||
{% if line.1.1 == None %}0,00{% else %}{{ line.1.1|floatformat:0|intdot }}{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ transactiontype.0.label }}
|
||||
{{ line.0.0 }}
|
||||
</td>
|
||||
<td class="push-right">
|
||||
{% if transactiontype.0.sum_total_amount == None %}0,00{% else %}{{ transactiontype.0.sum_total_amount|floatformat:0|intdot }}{% endif %}
|
||||
{% if line.0.1 == None %}0,00{% else %}{{ line.0.1|floatformat:0|intdot }}{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -1,689 +0,0 @@
|
|||
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