feat(signer-status): add new signer statuses and improve status handling

This commit is contained in:
Fanilo-Nantenaina 2026-01-06 20:24:29 +03:00
parent fbaa43e3fd
commit bcaa621432
3 changed files with 48 additions and 35 deletions

View file

@ -29,10 +29,14 @@ class UniversignTransactionStatus(str, Enum):
class UniversignSignerStatus(str, Enum): class UniversignSignerStatus(str, Enum):
WAITING = "waiting" WAITING = "waiting"
OPEN = "open"
VIEWED = "viewed" VIEWED = "viewed"
SIGNED = "signed" SIGNED = "signed"
COMPLETED = "completed"
REFUSED = "refused" REFUSED = "refused"
EXPIRED = "expired" EXPIRED = "expired"
STALLED = "stalled"
UNKNOWN = "unknown"
class LocalDocumentStatus(str, Enum): class LocalDocumentStatus(str, Enum):

View file

@ -251,46 +251,38 @@ class UniversignSyncService:
transaction: UniversignTransaction, transaction: UniversignTransaction,
universign_data: Dict, universign_data: Dict,
): ):
"""
CORRECTION : Synchronise les signataires sans perdre les données locales
"""
# Récupérer les participants depuis différents endroits possibles
signers_data = universign_data.get("participants", []) signers_data = universign_data.get("participants", [])
if not signers_data: if not signers_data:
signers_data = universign_data.get("signers", []) signers_data = universign_data.get("signers", [])
# ⚠️ IMPORTANT : Ne pas toucher aux signers si Universign n'en retourne pas
if not signers_data: if not signers_data:
logger.debug( logger.debug("Aucun signataire dans les données Universign")
"Aucun signataire dans les données Universign, conservation des données locales"
)
return return
# Créer un mapping email -> signer existant
existing_signers = {s.email: s for s in transaction.signers} existing_signers = {s.email: s for s in transaction.signers}
for idx, signer_data in enumerate(signers_data): for idx, signer_data in enumerate(signers_data):
email = signer_data.get("email", "") email = signer_data.get("email", "")
if not email: if not email:
logger.warning(f"Signataire sans email à l'index {idx}, ignoré") logger.warning(f"Signataire sans email à l'index {idx}, ignoré")
continue continue
if email in existing_signers: # ✅ PROTECTION : gérer les statuts inconnus
# ✅ Mise à jour du signer existant (ne pas écraser si None) raw_status = signer_data.get("status") or signer_data.get(
signer = existing_signers[email] "state", "waiting"
)
# Mise à jour du statut
new_status = signer_data.get("status") or signer_data.get("state")
if new_status:
try: try:
signer.status = UniversignSignerStatus(new_status) status = UniversignSignerStatus(raw_status)
except ValueError: except ValueError:
logger.warning( logger.warning(
f"Statut inconnu pour signer {email}: {new_status}" f"Statut inconnu pour signer {email}: {raw_status}, utilisation de 'unknown'"
) )
status = UniversignSignerStatus.UNKNOWN
if email in existing_signers:
signer = existing_signers[email]
signer.status = status
# Mise à jour des dates (ne pas écraser si déjà renseignées)
viewed_at = self._parse_date(signer_data.get("viewed_at")) viewed_at = self._parse_date(signer_data.get("viewed_at"))
if viewed_at and not signer.viewed_at: if viewed_at and not signer.viewed_at:
signer.viewed_at = viewed_at signer.viewed_at = viewed_at
@ -303,29 +295,26 @@ class UniversignSyncService:
if refused_at and not signer.refused_at: if refused_at and not signer.refused_at:
signer.refused_at = refused_at signer.refused_at = refused_at
# Mise à jour du nom si manquant
if signer_data.get("name") and not signer.name: if signer_data.get("name") and not signer.name:
signer.name = signer_data.get("name") signer.name = signer_data.get("name")
else: else:
# ✅ Nouveau signer # ✅ Nouveau signer avec gestion d'erreur intégrée
try: try:
status = signer_data.get("status") or signer_data.get(
"state", "waiting"
)
signer = UniversignSigner( signer = UniversignSigner(
id=f"{transaction.id}_signer_{idx}_{int(datetime.now().timestamp())}", id=f"{transaction.id}_signer_{idx}_{int(datetime.now().timestamp())}",
transaction_id=transaction.id, transaction_id=transaction.id,
email=email, email=email,
name=signer_data.get("name"), name=signer_data.get("name"),
status=UniversignSignerStatus(status), status=status,
order_index=idx, order_index=idx,
viewed_at=self._parse_date(signer_data.get("viewed_at")), viewed_at=self._parse_date(signer_data.get("viewed_at")),
signed_at=self._parse_date(signer_data.get("signed_at")), signed_at=self._parse_date(signer_data.get("signed_at")),
refused_at=self._parse_date(signer_data.get("refused_at")), refused_at=self._parse_date(signer_data.get("refused_at")),
) )
session.add(signer) session.add(signer)
logger.info(f" Nouveau signataire ajouté: {email}") logger.info(
f" Nouveau signataire ajouté: {email} (statut: {status.value})"
)
except Exception as e: except Exception as e:
logger.error(f"Erreur création signer {email}: {e}") logger.error(f"Erreur création signer {email}: {e}")
@ -534,15 +523,22 @@ class UniversignSyncService:
self, session: AsyncSession, transaction: UniversignTransaction, new_status: str self, session: AsyncSession, transaction: UniversignTransaction, new_status: str
): ):
actions = get_status_actions(new_status) actions = get_status_actions(new_status)
if not actions: if not actions:
return return
if actions.get("update_sage_status"): if actions.get("update_sage_status") and self.sage_client:
await self._update_sage_status(transaction, new_status) await self._update_sage_status(transaction, new_status)
elif actions.get("update_sage_status"):
logger.debug(
f"sage_client non configuré, skip MAJ Sage pour {transaction.sage_document_id}"
)
if actions.get("send_notification"): if actions.get("send_notification") and self.email_queue and self.settings:
await self._send_notification(session, transaction, new_status) await self._send_notification(session, transaction, new_status)
elif actions.get("send_notification"):
logger.debug(
f"email_queue/settings non configuré, skip notification pour {transaction.transaction_id}"
)
async def _update_sage_status( async def _update_sage_status(
self, transaction: UniversignTransaction, status: str self, transaction: UniversignTransaction, status: str

View file

@ -1,4 +1,8 @@
from typing import Dict, Any from typing import Dict, Any
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
UNIVERSIGN_TO_LOCAL: Dict[str, str] = { UNIVERSIGN_TO_LOCAL: Dict[str, str] = {
"draft": "EN_ATTENTE", "draft": "EN_ATTENTE",
@ -111,8 +115,17 @@ STATUS_MESSAGES: Dict[str, Dict[str, str]] = {
def map_universign_to_local(universign_status: str) -> str: def map_universign_to_local(universign_status: str) -> str:
"""Convertit un statut Universign en statut local.""" """Convertit un statut Universign en statut local avec fallback robuste."""
return UNIVERSIGN_TO_LOCAL.get(universign_status.lower(), "ERREUR") normalized = universign_status.lower().strip()
mapped = UNIVERSIGN_TO_LOCAL.get(normalized)
if not mapped:
logger.warning(
f"Statut Universign inconnu: '{universign_status}', mapping vers ERREUR"
)
return "ERREUR"
return mapped
def get_sage_status_code(local_status: str) -> int: def get_sage_status_code(local_status: str) -> int: