feat(documents): add generic PDF download endpoint for documents

This commit is contained in:
Fanilo-Nantenaina 2025-12-08 17:40:12 +03:00
parent 14b2758b68
commit a1794ac90f
2 changed files with 163 additions and 0 deletions

82
api.py
View file

@ -1252,6 +1252,88 @@ async def telecharger_devis_pdf(id: str):
logger.error(f"Erreur génération PDF: {e}")
raise HTTPException(500, str(e))
@app.get("/documents/{type_doc}/{numero}/pdf", tags=["Documents"])
async def telecharger_document_pdf(
type_doc: int = Query(..., description="Type de document (0=Devis, 10=Commande, 30=Livraison, 60=Facture, 50=Avoir)"),
numero: str = Query(..., description="Numéro du document")
):
"""
📄 Téléchargement PDF d'un document (route généralisée)
**Types de documents supportés:**
- `0`: Devis
- `10`: Bon de commande
- `30`: Bon de livraison
- `60`: Facture
- `50`: Bon d'avoir
**Exemple d'utilisation:**
- `GET /documents/0/DE00001/pdf` PDF du devis DE00001
- `GET /documents/60/FA00001/pdf` PDF de la facture FA00001
**Retour:**
- Fichier PDF prêt à télécharger
- Nom de fichier formaté selon le type de document
Args:
type_doc: Type de document Sage (0-60)
numero: Numéro du document
Returns:
StreamingResponse avec le PDF
"""
try:
# Mapping des types vers les libellés
types_labels = {
0: "Devis",
10: "Commande",
20: "Preparation",
30: "BonLivraison",
40: "BonRetour",
50: "Avoir",
60: "Facture"
}
# Vérifier que le type est valide
if type_doc not in types_labels:
raise HTTPException(
400,
f"Type de document invalide: {type_doc}. "
f"Types valides: {list(types_labels.keys())}"
)
label = types_labels[type_doc]
logger.info(f"📄 Génération PDF: {label} {numero} (type={type_doc})")
# Appel à sage_client pour générer le PDF
pdf_bytes = sage_client.generer_pdf_document(numero, type_doc)
if not pdf_bytes:
raise HTTPException(
500,
f"Le PDF du document {numero} est vide"
)
logger.info(f"✅ PDF généré: {len(pdf_bytes)} octets")
# Nom de fichier formaté
filename = f"{label}_{numero}.pdf"
return StreamingResponse(
iter([pdf_bytes]),
media_type="application/pdf",
headers={
"Content-Disposition": f"attachment; filename={filename}",
"Content-Length": str(len(pdf_bytes))
}
)
except HTTPException:
raise
except Exception as e:
logger.error(f"❌ Erreur génération PDF {numero} (type {type_doc}): {e}", exc_info=True)
raise HTTPException(500, f"Erreur génération PDF: {str(e)}")
@app.post("/devis/{id}/envoyer", tags=["Devis"])
async def envoyer_devis_email(

View file

@ -543,6 +543,87 @@ class SageGatewayClient:
"numero": numero,
"facture_data": facture_data
}).get("data", {})
def generer_pdf_document(self, doc_id: str, type_doc: int) -> bytes:
"""
🆕 Génère le PDF d'un document via la gateway Windows (route généralisée)
**Cette méthode remplace les appels spécifiques par type de document**
Args:
doc_id: Numéro du document (ex: "DE00001", "FA00001")
type_doc: Type de document Sage:
- 0: Devis
- 10: Bon de commande
- 30: Bon de livraison
- 60: Facture
- 50: Bon d'avoir
Returns:
bytes: Contenu du PDF (binaire)
Raises:
ValueError: Si le PDF retourné est vide
RuntimeError: Si erreur de communication avec la gateway
Example:
>>> pdf_bytes = sage_client.generer_pdf_document("DE00001", 0)
>>> with open("devis.pdf", "wb") as f:
... f.write(pdf_bytes)
"""
try:
logger.info(f"📄 Demande génération PDF: doc_id={doc_id}, type={type_doc}")
# Appel HTTP vers la gateway Windows
r = requests.post(
f"{self.url}/sage/documents/generate-pdf",
json={
"doc_id": doc_id,
"type_doc": type_doc
},
headers=self.headers,
timeout=60 # Timeout élevé pour génération PDF
)
r.raise_for_status()
import base64
response_data = r.json()
# Vérifier que la réponse contient bien le PDF
if not response_data.get("success"):
error_msg = response_data.get("error", "Erreur inconnue")
raise RuntimeError(f"Gateway a retourné une erreur: {error_msg}")
pdf_base64 = response_data.get("data", {}).get("pdf_base64", "")
if not pdf_base64:
raise ValueError(
f"PDF vide retourné par la gateway pour {doc_id} (type {type_doc})"
)
# Décoder le base64
pdf_bytes = base64.b64decode(pdf_base64)
logger.info(f"✅ PDF décodé: {len(pdf_bytes)} octets")
return pdf_bytes
except requests.exceptions.Timeout:
logger.error(f"⏱️ Timeout génération PDF pour {doc_id}")
raise RuntimeError(
f"Timeout lors de la génération du PDF (>60s). "
f"Le document {doc_id} est peut-être trop volumineux."
)
except requests.exceptions.RequestException as e:
logger.error(f"❌ Erreur HTTP génération PDF: {e}")
raise RuntimeError(f"Erreur de communication avec la gateway: {str(e)}")
except Exception as e:
logger.error(f"❌ Erreur génération PDF: {e}", exc_info=True)
raise