diff --git a/api.py b/api.py index c533c5a..ab46c3c 100644 --- a/api.py +++ b/api.py @@ -1420,7 +1420,7 @@ async def universign_envoyer_avec_email( pdf_bytes: bytes, email: str, nom: str, - doc_data: Dict, # Données du document (type, montant, date, etc.) + doc_data: Dict, session: AsyncSession, ) -> Dict: import requests @@ -1430,20 +1430,36 @@ async def universign_envoyer_avec_email( api_url = settings.universign_api_url auth = (api_key, "") + # ======================================== + # ÉTAPE 1 : Créer la transaction + # ======================================== + logger.info(f"🔐 Création transaction Universign pour {email}") + response = requests.post( f"{api_url}/transactions", auth=auth, json={ "name": f"{doc_data.get('type_label', 'Document')} {doc_id}", "language": "fr", + "profile": "default", # ✅ Ajout du profil }, timeout=30, ) + + if response.status_code != 200: + logger.error(f"❌ Erreur création transaction: {response.status_code} - {response.text}") + raise Exception(f"Erreur création transaction: {response.status_code}") + response.raise_for_status() transaction_id = response.json().get("id") - logger.info(f"✅ Transaction Universign créée: {transaction_id}") + logger.info(f"✅ Transaction créée: {transaction_id}") + # ======================================== + # ÉTAPE 2 : Upload du fichier PDF + # ======================================== + logger.info(f"📄 Upload PDF ({len(pdf_bytes)} octets)") + files = { "file": ( f"{doc_data.get('type_label', 'Document')}_{doc_id}.pdf", @@ -1451,56 +1467,138 @@ async def universign_envoyer_avec_email( "application/pdf", ) } - response = requests.post(f"{api_url}/files", auth=auth, files=files, timeout=30) + response = requests.post( + f"{api_url}/files", + auth=auth, + files=files, + timeout=30 + ) + + if response.status_code != 200: + logger.error(f"❌ Erreur upload fichier: {response.status_code} - {response.text}") + raise Exception(f"Erreur upload fichier: {response.status_code}") + response.raise_for_status() file_id = response.json().get("id") + logger.info(f"✅ Fichier uploadé: {file_id}") + + # ======================================== + # ÉTAPE 3 : Créer le document dans la transaction + # ======================================== + logger.info(f"📋 Ajout document à la transaction") + response = requests.post( f"{api_url}/transactions/{transaction_id}/documents", auth=auth, - data={"document": file_id}, + json={"file": file_id}, # ✅ Utiliser 'file' au lieu de 'document' timeout=30, ) + + if response.status_code != 200: + logger.error(f"❌ Erreur ajout document: {response.status_code} - {response.text}") + raise Exception(f"Erreur ajout document: {response.status_code}") + response.raise_for_status() document_id = response.json().get("id") + logger.info(f"✅ Document ajouté: {document_id}") + + # ======================================== + # ÉTAPE 4 : Ajouter un champ de signature + # ======================================== + logger.info(f"✍️ Ajout champ signature") + response = requests.post( f"{api_url}/transactions/{transaction_id}/documents/{document_id}/fields", auth=auth, - data={"type": "signature"}, + json={ + "type": "signature", + "page": 1, # ✅ Préciser la page + # Position optionnelle - Universign peut la placer automatiquement + # "x": 100, + # "y": 600, + }, timeout=30, ) + + if response.status_code != 200: + logger.error(f"❌ Erreur ajout champ: {response.status_code} - {response.text}") + raise Exception(f"Erreur ajout champ signature: {response.status_code}") + response.raise_for_status() field_id = response.json().get("id") + logger.info(f"✅ Champ signature créé: {field_id}") + + # ======================================== + # ÉTAPE 5 : Ajouter le signataire + # ======================================== + logger.info(f"👤 Ajout signataire: {nom} ({email})") + response = requests.post( - f"{api_url}/transactions/{transaction_id}/signatures", + f"{api_url}/transactions/{transaction_id}/signers", auth=auth, - data={"signer": email, "field": field_id}, + json={ + "email": email, + "firstName": nom.split()[0] if ' ' in nom else nom, # ✅ Prénom + "lastName": nom.split()[-1] if ' ' in nom else "", # ✅ Nom + # ✅ Lier le signataire au champ de signature + "fields": [field_id], + }, timeout=30, ) + + if response.status_code != 200: + logger.error(f"❌ Erreur ajout signataire: {response.status_code} - {response.text}") + raise Exception(f"Erreur ajout signataire: {response.status_code}") + response.raise_for_status() + signer_id = response.json().get("id") + logger.info(f"✅ Signataire ajouté: {signer_id}") + + # ======================================== + # ÉTAPE 6 : Démarrer la transaction + # ======================================== + logger.info(f"🚀 Démarrage de la transaction") + response = requests.post( - f"{api_url}/transactions/{transaction_id}/start", auth=auth, timeout=30 + f"{api_url}/transactions/{transaction_id}/start", + auth=auth, + json={}, # ✅ Body vide mais présent + timeout=30 ) + + if response.status_code != 200: + logger.error(f"❌ Erreur démarrage transaction: {response.status_code}") + logger.error(f"Réponse complète: {response.text}") + raise Exception(f"Erreur démarrage transaction: {response.status_code} - {response.text}") + response.raise_for_status() - final_data = response.json() - signer_url = ( - final_data.get("actions", [{}])[0].get("url", "") - if final_data.get("actions") - else "" - ) + + # ======================================== + # ÉTAPE 7 : Récupérer l'URL de signature + # ======================================== + signer_url = "" + if final_data.get("signers"): + for signer in final_data["signers"]: + if signer.get("email") == email: + signer_url = signer.get("url", "") + break if not signer_url: + logger.warning("⚠️ URL de signature non trouvée dans la réponse") raise ValueError("URL de signature non retournée par Universign") - logger.info(f"✅ Signature Universign démarrée: {transaction_id}") + logger.info(f"✅ Signature Universign prête: {transaction_id}") + # ======================================== + # ÉTAPE 8 : Créer l'email de notification + # ======================================== template = templates_signature_email["demande_signature"] - # Préparer les variables type_labels = { 0: "Devis", 10: "Commande", @@ -1519,7 +1617,6 @@ async def universign_envoyer_avec_email( "CONTACT_EMAIL": settings.smtp_from, } - # Remplacer les variables dans le template sujet = template["sujet"] corps = template["corps_html"] @@ -1556,11 +1653,18 @@ async def universign_envoyer_avec_email( "email_sent": True, } + except requests.exceptions.HTTPError as e: + logger.error(f"❌ Erreur HTTP Universign: {e}") + logger.error(f"Réponse: {e.response.text if e.response else 'N/A'}") + return { + "error": f"Erreur Universign: {e.response.status_code} - {e.response.text if e.response else str(e)}", + "statut": "ERREUR", + "email_sent": False, + } except Exception as e: - logger.error(f"❌ Erreur Universign+Email: {e}") + logger.error(f"❌ Erreur Universign+Email: {e}", exc_info=True) return {"error": str(e), "statut": "ERREUR", "email_sent": False} - async def universign_statut(transaction_id: str) -> Dict: """Récupération statut signature""" import requests