Merge branch 'fix/update_pdf_structure' into feat/update_pdf_structure

This commit is contained in:
Fanilo-Nantenaina 2026-01-08 10:48:26 +03:00
commit f3957dddcf

View file

@ -12,9 +12,11 @@ from config.config import settings
import logging import logging
from reportlab.lib.pagesizes import A4 from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas from reportlab.pdfgen import canvas
from reportlab.lib.units import cm
from io import BytesIO from io import BytesIO
from reportlab.lib.units import mm
from reportlab.lib.colors import HexColor
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -207,102 +209,243 @@ class EmailQueue:
pdf = canvas.Canvas(buffer, pagesize=A4) pdf = canvas.Canvas(buffer, pagesize=A4)
width, height = A4 width, height = A4
# Couleurs
green_color = HexColor("#2A6F4F")
gray_400 = HexColor("#9CA3AF")
gray_600 = HexColor("#4B5563")
gray_800 = HexColor("#1F2937")
# Marges
margin = 8 * mm
content_width = width - 2 * margin
y = height - margin
# ===== HEADER =====
y -= 20 * mm
# Logo/Nom entreprise à gauche
pdf.setFont("Helvetica-Bold", 18)
pdf.setFillColor(green_color)
pdf.drawString(margin, y, "Bijou S.A.S")
# Informations document à droite
pdf.setFillColor(gray_800)
pdf.setFont("Helvetica-Bold", 20) pdf.setFont("Helvetica-Bold", 20)
pdf.drawString(2 * cm, height - 3 * cm, f"Document N° {doc_id}") numero = doc.get("numero") or "BROUILLON"
pdf.drawRightString(width - margin, y, numero.upper())
type_labels = { y -= 7 * mm
0: "DEVIS",
1: "BON DE LIVRAISON",
2: "BON DE RETOUR",
3: "COMMANDE",
4: "PRÉPARATION",
5: "FACTURE",
}
type_label = type_labels.get(type_doc, "DOCUMENT")
pdf.setFont("Helvetica", 12)
pdf.drawString(2 * cm, height - 4 * cm, f"Type: {type_label}")
y = height - 5 * cm
pdf.setFont("Helvetica-Bold", 14)
pdf.drawString(2 * cm, y, "CLIENT")
y -= 0.8 * cm
pdf.setFont("Helvetica", 11)
pdf.drawString(2 * cm, y, f"Code: {doc.get('client_code') or ''}")
y -= 0.6 * cm
pdf.drawString(2 * cm, y, f"Nom: {doc.get('client_intitule') or ''}")
y -= 0.6 * cm
pdf.drawString(2 * cm, y, f"Date: {doc.get('date') or ''}")
y -= 1.5 * cm
pdf.setFont("Helvetica-Bold", 14)
pdf.drawString(2 * cm, y, "ARTICLES")
y -= 1 * cm
pdf.setFont("Helvetica-Bold", 10)
pdf.drawString(2 * cm, y, "Désignation")
pdf.drawString(10 * cm, y, "Qté")
pdf.drawString(12 * cm, y, "Prix Unit.")
pdf.drawString(15 * cm, y, "Total HT")
y -= 0.5 * cm
pdf.line(2 * cm, y, width - 2 * cm, y)
y -= 0.7 * cm
pdf.setFont("Helvetica", 9) pdf.setFont("Helvetica", 9)
pdf.setFillColor(gray_600)
for ligne in doc.get("lignes", []): date_str = doc.get("date") or datetime.now().strftime("%d/%m/%Y")
if y < 3 * cm: pdf.drawRightString(width - margin, y, f"Date : {date_str}")
y -= 5 * mm
date_livraison = (
doc.get("date_livraison") or doc.get("date_echeance") or date_str
)
pdf.drawRightString(width - margin, y, f"Validité : {date_livraison}")
y -= 5 * mm
reference = doc.get("reference") or ""
pdf.drawRightString(width - margin, y, f"Réf : {reference}")
# ===== ADDRESSES =====
y -= 20 * mm
# Émetteur (gauche)
col1_x = margin
col2_x = margin + content_width / 2 + 6 * mm
col_width = content_width / 2 - 6 * mm
pdf.setFont("Helvetica-Bold", 8)
pdf.setFillColor(gray_400)
pdf.drawString(col1_x, y, "ÉMETTEUR")
y_emetteur = y - 5 * mm
pdf.setFont("Helvetica-Bold", 10)
pdf.setFillColor(gray_800)
pdf.drawString(col1_x, y_emetteur, "Bijou S.A.S")
y_emetteur -= 5 * mm
pdf.setFont("Helvetica", 9)
pdf.setFillColor(gray_600)
pdf.drawString(col1_x, y_emetteur, "123 Avenue de la République")
y_emetteur -= 4 * mm
pdf.drawString(col1_x, y_emetteur, "75011 Paris, France")
y_emetteur -= 5 * mm
pdf.drawString(col1_x, y_emetteur, "contact@bijou.com")
# Destinataire (droite, avec fond gris)
box_y = y - 4 * mm
box_height = 28 * mm
pdf.setFillColorRGB(0.97, 0.97, 0.97) # bg-gray-50
pdf.roundRect(
col2_x, box_y - box_height, col_width, box_height, 3 * mm, fill=1, stroke=0
)
pdf.setFillColor(gray_400)
pdf.setFont("Helvetica-Bold", 8)
pdf.drawString(col2_x + 4 * mm, y, "DESTINATAIRE")
y_dest = y - 5 * mm
pdf.setFont("Helvetica-Bold", 10)
pdf.setFillColor(gray_800)
client_name = doc.get("client_intitule") or "Client"
pdf.drawString(col2_x + 4 * mm, y_dest, client_name)
y_dest -= 5 * mm
pdf.setFont("Helvetica", 9)
pdf.setFillColor(gray_600)
pdf.drawString(col2_x + 4 * mm, y_dest, "10 rue des Clients")
y_dest -= 4 * mm
pdf.drawString(col2_x + 4 * mm, y_dest, "75001 Paris")
# ===== LIGNES D'ARTICLES =====
y = min(y_emetteur, y_dest) - 20 * mm
# En-têtes des colonnes
col_designation = margin
col_quantite = width - margin - 80 * mm
col_prix_unit = width - margin - 64 * mm
col_taux_taxe = width - margin - 40 * mm
col_montant = width - margin - 24 * mm
pdf.setFont("Helvetica-Bold", 9)
pdf.setFillColor(gray_800)
pdf.drawString(col_designation, y, "Désignation")
pdf.drawRightString(col_quantite, y, "Qté")
pdf.drawRightString(col_prix_unit, y, "Prix Unit. HT")
pdf.drawRightString(col_taux_taxe, y, "TVA")
pdf.drawRightString(col_montant, y, "Montant HT")
y -= 7 * mm
# Lignes d'articles
pdf.setFont("Helvetica", 8)
lignes = doc.get("lignes", [])
for ligne in lignes:
if y < 60 * mm: # Nouvelle page si nécessaire
pdf.showPage() pdf.showPage()
y = height - 3 * cm y = height - margin - 20 * mm
pdf.setFont("Helvetica", 9) pdf.setFont("Helvetica", 8)
designation = ( designation = (
ligne.get("designation") or ligne.get("designation_article") or "" ligne.get("designation") or ligne.get("designation_article") or ""
) )
if designation: if len(designation) > 60:
designation = str(designation)[:50] designation = designation[:57] + "..."
else:
designation = ""
pdf.drawString(2 * cm, y, designation) pdf.setFillColor(gray_800)
pdf.drawString(10 * cm, y, str(ligne.get("quantite") or 0)) pdf.setFont("Helvetica-Bold", 8)
pdf.drawString( pdf.drawString(col_designation, y, designation)
12 * cm,
y,
f"{ligne.get('prix_unitaire_ht') or ligne.get('prix_unitaire', 0):.2f}",
)
pdf.drawString(
15 * cm,
y,
f"{ligne.get('montant_ligne_ht') or ligne.get('montant_ht', 0):.2f}",
)
y -= 0.6 * cm
y -= 1 * cm y -= 4 * mm
pdf.line(12 * cm, y, width - 2 * cm, y)
y -= 0.8 * cm # Description (si différente)
pdf.setFont("Helvetica-Bold", 11) description = ligne.get("description", "")
pdf.drawString(12 * cm, y, "Total HT NET:") if description and description != designation:
pdf.drawString(15 * cm, y, f"{doc.get('total_ht_net') or 0:.2f}") pdf.setFont("Helvetica", 7)
pdf.setFillColor(gray_600)
if len(description) > 70:
description = description[:67] + "..."
pdf.drawString(col_designation, y, description)
y -= 4 * mm
y -= 0.6 * cm # Valeurs
pdf.drawString(12 * cm, y, "TVA (20%):") y += 4 * mm # Remonter pour aligner avec la désignation
tva = (doc.get("total_ttc") or 0) - (doc.get("total_ht_net") or 0) pdf.setFont("Helvetica", 8)
pdf.drawString(15 * cm, y, f"{tva:.2f}") pdf.setFillColor(gray_800)
y -= 0.6 * cm quantite = ligne.get("quantite") or 0
pdf.setFont("Helvetica-Bold", 14) pdf.drawRightString(col_quantite, y, str(quantite))
pdf.drawString(12 * cm, y, "Total TTC:")
pdf.drawString(15 * cm, y, f"{doc.get('total_ttc') or 0:.2f}")
pdf.setFont("Helvetica", 8) prix_unit = ligne.get("prix_unitaire_ht") or ligne.get("prix_unitaire", 0)
pdf.drawString( pdf.drawRightString(col_prix_unit, y, f"{prix_unit:.2f}")
2 * cm, 2 * cm, f"Généré le {datetime.now().strftime('%d/%m/%Y %H:%M')}"
) taux_taxe = ligne.get("taux_taxe1") or 20
pdf.drawString(2 * cm, 1.5 * cm, "Sage 100c - API Dataven") pdf.drawRightString(col_taux_taxe, y, f"{taux_taxe}%")
montant = ligne.get("montant_ligne_ht") or ligne.get("montant_ht", 0)
pdf.setFont("Helvetica-Bold", 8)
pdf.drawRightString(col_montant, y, f"{montant:.2f}")
y -= 8 * mm
# Si aucune ligne
if not lignes:
pdf.setFont("Helvetica-Oblique", 9)
pdf.setFillColor(gray_400)
pdf.drawCentredString(width / 2, y, "Aucune ligne")
y -= 15 * mm
# ===== TOTAUX =====
y -= 10 * mm
totals_x = width - margin - 64 * mm
totals_label_width = 40 * mm
pdf.setFont("Helvetica", 9)
pdf.setFillColor(gray_600)
# Total HT
pdf.drawString(totals_x, y, "Total HT")
total_ht = doc.get("total_ht_net") or doc.get("total_ht") or 0
pdf.drawRightString(width - margin, y, f"{total_ht:.2f}")
y -= 6 * mm
# TVA
pdf.drawString(totals_x, y, "TVA")
total_ttc = doc.get("total_ttc") or 0
tva = total_ttc - total_ht
pdf.drawRightString(width - margin, y, f"{tva:.2f}")
y -= 8 * mm
# Ligne de séparation
pdf.setStrokeColor(gray_400)
pdf.line(totals_x, y + 2 * mm, width - margin, y + 2 * mm)
# Net à payer
pdf.setFont("Helvetica-Bold", 12)
pdf.setFillColor(green_color)
pdf.drawString(totals_x, y, "Net à payer")
pdf.drawRightString(width - margin, y, f"{total_ttc:.2f}")
# ===== NOTES =====
notes = doc.get("notes_publique") or doc.get("notes")
if notes:
y -= 15 * mm
pdf.setStrokeColor(gray_400)
pdf.line(margin, y + 5 * mm, width - margin, y + 5 * mm)
y -= 5 * mm
pdf.setFont("Helvetica-Bold", 8)
pdf.setFillColor(gray_400)
pdf.drawString(margin, y, "NOTES & CONDITIONS")
y -= 5 * mm
pdf.setFont("Helvetica", 8)
pdf.setFillColor(gray_600)
# Gérer les sauts de ligne dans les notes
for line in notes.split("\n"):
if y < 25 * mm:
break
pdf.drawString(margin, y, line[:100])
y -= 4 * mm
# ===== FOOTER =====
pdf.setFont("Helvetica", 7)
pdf.setFillColor(gray_400)
pdf.drawCentredString(width / 2, 15 * mm, "Page 1 / 1")
pdf.save() pdf.save()
buffer.seek(0) buffer.seek(0)