feat(pdf): add dynamic company info to PDF generator

This commit is contained in:
Fanilo-Nantenaina 2026-01-13 17:37:23 +03:00
parent 08665f15dd
commit 3f1dce918d

View file

@ -20,7 +20,6 @@ from io import BytesIO
from reportlab.lib.units import mm
from reportlab.lib.colors import HexColor, Color
from PIL import Image
from sqlalchemy.exc import OperationalError
logger = logging.getLogger(__name__)
@ -82,6 +81,8 @@ async def execute_with_retry(
base_delay: float = 0.1,
max_delay: float = 2.0,
):
from sqlalchemy.exc import OperationalError
last_exception = None
for attempt in range(max_retries):
@ -106,7 +107,7 @@ async def execute_with_retry(
else:
# Autre erreur OperationalError, ne pas retry
raise
except Exception as e:
except Exception:
# Autres exceptions, ne pas retry
raise
@ -372,8 +373,18 @@ class EmailQueue:
if not doc:
raise Exception(f"Document {doc_id} introuvable")
# Récupérer les informations de la société émettrice
societe_info = None
try:
societe_info = self.sage_client.lire_informations_societe()
logger.debug(
f"Infos société récupérées: {societe_info.raison_sociale if societe_info else 'N/A'}"
)
except Exception as e:
logger.warning(f"Impossible de récupérer les infos société: {e}")
# Générer le PDF avec le nouveau générateur
generator = SagePDFGenerator(doc, type_doc)
generator = SagePDFGenerator(doc, type_doc, societe_info=societe_info)
return generator.generate()
@ -392,10 +403,11 @@ class SagePDFGenerator:
GRAY_800 = HexColor("#1F2937")
GRAY_900 = HexColor("#111827")
COMPANY_NAME = "Bijou S.A.S"
COMPANY_ADDRESS_1 = "123 Avenue de la République"
COMPANY_ADDRESS_2 = "75011 Paris, France"
COMPANY_EMAIL = "contact@bijou.com"
# Valeurs par défaut (fallback si société non disponible)
DEFAULT_COMPANY_NAME = "Entreprise"
DEFAULT_COMPANY_ADDRESS = ""
DEFAULT_COMPANY_CITY = ""
DEFAULT_COMPANY_EMAIL = ""
# Labels des types de documents
TYPE_LABELS = {
@ -406,10 +418,11 @@ class SagePDFGenerator:
60: "Facture",
}
def __init__(self, doc_data: dict, type_doc: int):
def __init__(self, doc_data: dict, type_doc: int, societe_info=None):
self.doc = doc_data
self.type_doc = type_doc
self.type_label = self.TYPE_LABELS.get(type_doc, "Document")
self.societe_info = societe_info
# Configuration de la page
self.page_width, self.page_height = A4
@ -427,6 +440,64 @@ class SagePDFGenerator:
_register_sage_font()
self.use_sage_font = _sage_font_registered
def _get_societe_field(self, field: str, default: str = "") -> str:
"""Récupère un champ de la société avec fallback."""
if self.societe_info is None:
return default
return getattr(self.societe_info, field, default) or default
def _get_societe_name(self) -> str:
"""Retourne la raison sociale de la société."""
return self._get_societe_field("raison_sociale", self.DEFAULT_COMPANY_NAME)
def _get_societe_address_line1(self) -> str:
"""Retourne la première ligne d'adresse."""
adresse = self._get_societe_field("adresse", "")
complement = self._get_societe_field("complement_adresse", "")
if adresse and complement:
return f"{adresse}, {complement}"
return adresse or complement or self.DEFAULT_COMPANY_ADDRESS
def _get_societe_address_line2(self) -> str:
"""Retourne la deuxième ligne d'adresse (CP + Ville + Pays)."""
cp = self._get_societe_field("code_postal", "")
ville = self._get_societe_field("ville", "")
pays = self._get_societe_field("pays", "")
parts = []
if cp:
parts.append(cp)
if ville:
parts.append(ville)
city_line = " ".join(parts)
if pays and pays.lower() not in ["france", "fr"]:
city_line = f"{city_line}, {pays}" if city_line else pays
return city_line or self.DEFAULT_COMPANY_CITY
def _get_societe_email(self) -> str:
"""Retourne l'email de la société."""
# Priorité à email_societe, puis email
email = self._get_societe_field("email_societe", "")
if not email:
email = self._get_societe_field("email", "")
return email or self.DEFAULT_COMPANY_EMAIL
def _get_societe_phone(self) -> str:
"""Retourne le téléphone de la société."""
return self._get_societe_field("telephone", "")
def _get_societe_siret(self) -> str:
"""Retourne le SIRET de la société."""
return self._get_societe_field("siret", "")
def _get_societe_tva(self) -> str:
"""Retourne le numéro de TVA de la société."""
return self._get_societe_field("numero_tva", "")
def _get_font(self, bold: bool = False) -> str:
"""Retourne le nom de la police à utiliser."""
if self.use_sage_font:
@ -563,31 +634,57 @@ class SagePDFGenerator:
col1_x, y, "ÉMETTEUR", font_size=8, bold=True, color=self.GRAY_400
)
# === INFORMATIONS SOCIÉTÉ (dynamiques) ===
y_emetteur = y - 6 * mm
self._draw_text(
col1_x,
y_emetteur,
self.COMPANY_NAME,
self._get_societe_name(),
font_size=10,
bold=True,
color=self.GRAY_800,
)
y_emetteur -= 5 * mm
self._draw_text(
col1_x, y_emetteur, self.COMPANY_ADDRESS_1, font_size=9, color=self.GRAY_600
)
# Adresse ligne 1
address_line1 = self._get_societe_address_line1()
if address_line1:
y_emetteur -= 5 * mm
# Tronquer si trop long
if len(address_line1) > 45:
address_line1 = address_line1[:42] + "..."
self._draw_text(
col1_x, y_emetteur, address_line1, font_size=9, color=self.GRAY_600
)
y_emetteur -= 4 * mm
self._draw_text(
col1_x, y_emetteur, self.COMPANY_ADDRESS_2, font_size=9, color=self.GRAY_600
)
# Adresse ligne 2 (CP + Ville)
address_line2 = self._get_societe_address_line2()
if address_line2:
y_emetteur -= 4 * mm
self._draw_text(
col1_x, y_emetteur, address_line2, font_size=9, color=self.GRAY_600
)
y_emetteur -= 5 * mm
self._draw_text(
col1_x, y_emetteur, self.COMPANY_EMAIL, font_size=9, color=self.GRAY_600
)
# Email
societe_email = self._get_societe_email()
if societe_email:
y_emetteur -= 5 * mm
self._draw_text(
col1_x, y_emetteur, societe_email, font_size=9, color=self.GRAY_600
)
# Téléphone (optionnel)
societe_phone = self._get_societe_phone()
if societe_phone:
y_emetteur -= 4 * mm
self._draw_text(
col1_x,
y_emetteur,
f"Tél: {societe_phone}",
font_size=8,
color=self.GRAY_500,
)
# === DESTINATAIRE ===
box_padding = 4 * mm
box_height = 26 * mm
box_y = y - 3 * mm
@ -912,8 +1009,48 @@ class SagePDFGenerator:
return y
def _draw_footer(self):
footer_y = 12 * mm
footer_y = 15 * mm
# Informations légales de la société
legal_parts = []
societe_name = self._get_societe_name()
if societe_name and societe_name != self.DEFAULT_COMPANY_NAME:
legal_parts.append(societe_name)
siret = self._get_societe_siret()
if siret:
legal_parts.append(f"SIRET: {siret}")
tva = self._get_societe_tva()
if tva:
legal_parts.append(f"TVA: {tva}")
# Forme juridique et capital si disponibles
if self.societe_info:
forme = self._get_societe_field("forme_juridique", "")
capital = getattr(self.societe_info, "capital", 0)
if forme and capital > 0:
legal_parts.append(
f"{forme} au capital de {capital:,.0f}".replace(",", " ")
)
# Dessiner les informations légales
if legal_parts:
legal_text = "".join(legal_parts)
# Tronquer si trop long
if len(legal_text) > 100:
legal_text = legal_text[:97] + "..."
self._draw_text(
self.page_width / 2,
footer_y + 4 * mm,
legal_text,
font_size=7,
color=self.GRAY_400,
align="center",
)
# Pagination
page_text = f"Page {self.page_number} / {self.total_pages}"
self._draw_text(
self.page_width / 2,