refactor(universign): simplify status mapping and remove redundant comments

This commit is contained in:
Fanilo-Nantenaina 2026-01-06 12:52:52 +03:00
parent f3fc32c89f
commit bac8cc6017

View file

@ -1,76 +1,46 @@
""" from typing import Dict
Mapping exhaustif : Universign Local Sage
"""
from typing import Dict, Optional
from enum import Enum
# ========================================
# MAPPING UNIVERSIGN → LOCAL
# ========================================
UNIVERSIGN_TO_LOCAL: Dict[str, str] = { UNIVERSIGN_TO_LOCAL: Dict[str, str] = {
# États initiaux "draft": "EN_ATTENTE",
"draft": "EN_ATTENTE", # Transaction créée "ready": "EN_ATTENTE",
"ready": "EN_ATTENTE", # Prête mais pas envoyée "started": "EN_COURS",
# En cours "completed": "SIGNE",
"started": "EN_COURS", # Envoyée, en attente de signature "refused": "REFUSE",
# États finaux (succès) "expired": "EXPIRE",
"completed": "SIGNE", # ✓ Signé avec succès "canceled": "REFUSE",
# États finaux (échec) "failed": "ERREUR",
"refused": "REFUSE", # ✗ Refusé par un signataire
"expired": "EXPIRE", # ⏰ Délai expiré
"canceled": "REFUSE", # ✗ Annulé manuellement
"failed": "ERREUR", # ⚠️ Erreur technique
} }
# ========================================
# MAPPING LOCAL → SAGE (CHAMP LIBRE)
# ========================================
LOCAL_TO_SAGE_STATUS: Dict[str, int] = { LOCAL_TO_SAGE_STATUS: Dict[str, int] = {
""" "EN_ATTENTE": 0,
À stocker dans un champ libre Sage (ex: CB_STATUT_SIGNATURE) "EN_COURS": 1,
""" "SIGNE": 2,
"EN_ATTENTE": 0, # Non envoyé "REFUSE": 3,
"EN_COURS": 1, # Envoyé, en attente "EXPIRE": 4,
"SIGNE": 2, # ✓ Signé (peut déclencher workflow) "ERREUR": 5,
"REFUSE": 3, # ✗ Refusé
"EXPIRE": 4, # ⏰ Expiré
"ERREUR": 5, # ⚠️ Erreur
} }
# ========================================
# ACTIONS MÉTIER PAR STATUT
# ========================================
STATUS_ACTIONS: Dict[str, Dict[str, any]] = { STATUS_ACTIONS: Dict[str, Dict[str, any]] = {
"""
Actions automatiques à déclencher selon le statut
"""
"SIGNE": { "SIGNE": {
"update_sage_status": True, # Mettre à jour Sage "update_sage_status": True,
"trigger_workflow": True, # Déclencher transformation (devis→commande) "trigger_workflow": True,
"send_notification": True, # Email de confirmation "send_notification": True,
"archive_document": True, # Archiver le PDF signé "archive_document": True,
"update_sage_field": "CB_DateSignature", # Champ libre Sage "update_sage_field": "CB_DateSignature",
}, },
"REFUSE": { "REFUSE": {
"update_sage_status": True, "update_sage_status": True,
"trigger_workflow": False, "trigger_workflow": False,
"send_notification": True, "send_notification": True,
"archive_document": False, "archive_document": False,
"alert_sales": True, # Alerter commercial "alert_sales": True,
}, },
"EXPIRE": { "EXPIRE": {
"update_sage_status": True, "update_sage_status": True,
"trigger_workflow": False, "trigger_workflow": False,
"send_notification": True, "send_notification": True,
"archive_document": False, "archive_document": False,
"schedule_reminder": True, # Programmer relance "schedule_reminder": True,
}, },
"ERREUR": { "ERREUR": {
"update_sage_status": False, "update_sage_status": False,
@ -81,141 +51,60 @@ STATUS_ACTIONS: Dict[str, Dict[str, any]] = {
}, },
} }
# ========================================
# RÈGLES DE TRANSITION
# ========================================
ALLOWED_TRANSITIONS: Dict[str, list] = { ALLOWED_TRANSITIONS: Dict[str, list] = {
"""
Transitions de statuts autorisées (validation)
"""
"EN_ATTENTE": ["EN_COURS", "ERREUR"], "EN_ATTENTE": ["EN_COURS", "ERREUR"],
"EN_COURS": ["SIGNE", "REFUSE", "EXPIRE", "ERREUR"], "EN_COURS": ["SIGNE", "REFUSE", "EXPIRE", "ERREUR"],
"SIGNE": [], # État final, pas de retour "SIGNE": [],
"REFUSE": [], # État final "REFUSE": [],
"EXPIRE": [], # État final "EXPIRE": [],
"ERREUR": ["EN_ATTENTE", "EN_COURS"], # Retry possible "ERREUR": ["EN_ATTENTE", "EN_COURS"],
} }
# ========================================
# FONCTION DE MAPPING
# ========================================
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
Args:
universign_status: Statut brut Universign
Returns:
Statut local normalisé
"""
return UNIVERSIGN_TO_LOCAL.get( return UNIVERSIGN_TO_LOCAL.get(
universign_status.lower(), universign_status.lower(),
"ERREUR", # Fallback si statut inconnu "ERREUR",
) )
def get_sage_status_code(local_status: str) -> int: def get_sage_status_code(local_status: str) -> int:
"""
Obtient le code numérique pour Sage
Args:
local_status: Statut local (EN_ATTENTE, SIGNE, etc.)
Returns:
Code numérique pour champ libre Sage
"""
return LOCAL_TO_SAGE_STATUS.get(local_status, 5) return LOCAL_TO_SAGE_STATUS.get(local_status, 5)
def is_transition_allowed(from_status: str, to_status: str) -> bool: def is_transition_allowed(from_status: str, to_status: str) -> bool:
"""
Vérifie si une transition de statut est valide
Args:
from_status: Statut actuel
to_status: Nouveau statut
Returns:
True si la transition est autorisée
"""
if from_status == to_status: if from_status == to_status:
return True # Même statut = OK (idempotence) return True
allowed = ALLOWED_TRANSITIONS.get(from_status, []) allowed = ALLOWED_TRANSITIONS.get(from_status, [])
return to_status in allowed return to_status in allowed
def get_status_actions(local_status: str) -> Dict[str, any]: def get_status_actions(local_status: str) -> Dict[str, any]:
"""
Obtient les actions à exécuter pour un statut
Args:
local_status: Statut local
Returns:
Dictionnaire des actions à exécuter
"""
return STATUS_ACTIONS.get(local_status, {}) return STATUS_ACTIONS.get(local_status, {})
def is_final_status(local_status: str) -> bool: def is_final_status(local_status: str) -> bool:
"""
Détermine si le statut est final (pas de synchronisation nécessaire)
Args:
local_status: Statut local
Returns:
True si statut final
"""
return local_status in ["SIGNE", "REFUSE", "EXPIRE"] return local_status in ["SIGNE", "REFUSE", "EXPIRE"]
# ========================================
# PRIORITÉS DE STATUTS (POUR CONFLITS)
# ========================================
STATUS_PRIORITY: Dict[str, int] = { STATUS_PRIORITY: Dict[str, int] = {
""" "ERREUR": 0,
En cas de conflit de synchronisation, prendre le statut
avec la priorité la plus élevée
"""
"ERREUR": 0, # Priorité la plus basse
"EN_ATTENTE": 1, "EN_ATTENTE": 1,
"EN_COURS": 2, "EN_COURS": 2,
"EXPIRE": 3, "EXPIRE": 3,
"REFUSE": 4, "REFUSE": 4,
"SIGNE": 5, # Priorité la plus élevée (état final souhaité) "SIGNE": 5,
} }
def resolve_status_conflict(status_a: str, status_b: str) -> str: def resolve_status_conflict(status_a: str, status_b: str) -> str:
"""
Résout un conflit entre deux statuts (prend le plus prioritaire)
Args:
status_a: Premier statut
status_b: Second statut
Returns:
Statut prioritaire
"""
priority_a = STATUS_PRIORITY.get(status_a, 0) priority_a = STATUS_PRIORITY.get(status_a, 0)
priority_b = STATUS_PRIORITY.get(status_b, 0) priority_b = STATUS_PRIORITY.get(status_b, 0)
return status_a if priority_a >= priority_b else status_b return status_a if priority_a >= priority_b else status_b
# ========================================
# MESSAGES UTILISATEUR
# ========================================
STATUS_MESSAGES: Dict[str, Dict[str, str]] = { STATUS_MESSAGES: Dict[str, Dict[str, str]] = {
"EN_ATTENTE": { "EN_ATTENTE": {
"fr": "Document en attente d'envoi", "fr": "Document en attente d'envoi",
@ -257,16 +146,6 @@ STATUS_MESSAGES: Dict[str, Dict[str, str]] = {
def get_status_message(local_status: str, lang: str = "fr") -> str: def get_status_message(local_status: str, lang: str = "fr") -> str:
"""
Obtient le message utilisateur pour un statut
Args:
local_status: Statut local
lang: Langue (fr, en)
Returns:
Message formaté
"""
status_info = STATUS_MESSAGES.get(local_status, {}) status_info = STATUS_MESSAGES.get(local_status, {})
icon = status_info.get("icon", "") icon = status_info.get("icon", "")
message = status_info.get(lang, local_status) message = status_info.get(lang, local_status)