refactor: improve societe info handling and add sqlite lock retry docs

This commit is contained in:
Fanilo-Nantenaina 2026-01-13 18:03:33 +03:00
parent 3f1dce918d
commit 30ffc7a493

View file

@ -74,6 +74,11 @@ def _register_sage_font():
return False return False
# ============================================================================
# HELPERS POUR GESTION DES LOCKS SQLite
# ============================================================================
async def execute_with_retry( async def execute_with_retry(
session, session,
operation, operation,
@ -81,6 +86,17 @@ async def execute_with_retry(
base_delay: float = 0.1, base_delay: float = 0.1,
max_delay: float = 2.0, max_delay: float = 2.0,
): ):
"""
Exécute une opération async avec retry en cas de lock SQLite.
Args:
session: Session SQLAlchemy async
operation: Coroutine à exécuter
max_retries: Nombre max de tentatives
base_delay: Délai initial entre les tentatives (secondes)
max_delay: Délai maximum entre les tentatives
"""
import sqlite3
from sqlalchemy.exc import OperationalError from sqlalchemy.exc import OperationalError
last_exception = None last_exception = None
@ -107,7 +123,7 @@ async def execute_with_retry(
else: else:
# Autre erreur OperationalError, ne pas retry # Autre erreur OperationalError, ne pas retry
raise raise
except Exception: except Exception as e:
# Autres exceptions, ne pas retry # Autres exceptions, ne pas retry
raise raise
@ -148,6 +164,13 @@ class EmailQueue:
pass pass
def enqueue(self, email_log_id: str, delay_seconds: float = 0): def enqueue(self, email_log_id: str, delay_seconds: float = 0):
"""
Ajoute un email à la queue avec un délai optionnel.
Args:
email_log_id: ID de l'email log
delay_seconds: Délai avant traitement (pour éviter les conflits)
"""
if delay_seconds > 0: if delay_seconds > 0:
timer = threading.Timer(delay_seconds, lambda: self.queue.put(email_log_id)) timer = threading.Timer(delay_seconds, lambda: self.queue.put(email_log_id))
timer.daemon = True timer.daemon = True
@ -377,9 +400,13 @@ class EmailQueue:
societe_info = None societe_info = None
try: try:
societe_info = self.sage_client.lire_informations_societe() societe_info = self.sage_client.lire_informations_societe()
logger.debug( # Log selon le type (dict ou objet)
f"Infos société récupérées: {societe_info.raison_sociale if societe_info else 'N/A'}" if societe_info:
) if isinstance(societe_info, dict):
raison = societe_info.get("raison_sociale", "N/A")
else:
raison = getattr(societe_info, "raison_sociale", "N/A")
logger.debug(f"Infos société récupérées: {raison}")
except Exception as e: except Exception as e:
logger.warning(f"Impossible de récupérer les infos société: {e}") logger.warning(f"Impossible de récupérer les infos société: {e}")
@ -441,10 +468,17 @@ class SagePDFGenerator:
self.use_sage_font = _sage_font_registered self.use_sage_font = _sage_font_registered
def _get_societe_field(self, field: str, default: str = "") -> str: def _get_societe_field(self, field: str, default: str = "") -> str:
"""Récupère un champ de la société avec fallback.""" """Récupère un champ de la société avec fallback (supporte dict et objet)."""
if self.societe_info is None: if self.societe_info is None:
return default return default
return getattr(self.societe_info, field, default) or default
# Support dict ou objet Pydantic
if isinstance(self.societe_info, dict):
value = self.societe_info.get(field)
else:
value = getattr(self.societe_info, field, None)
return value if value is not None else default
def _get_societe_name(self) -> str: def _get_societe_name(self) -> str:
"""Retourne la raison sociale de la société.""" """Retourne la raison sociale de la société."""
@ -1029,10 +1063,16 @@ class SagePDFGenerator:
# Forme juridique et capital si disponibles # Forme juridique et capital si disponibles
if self.societe_info: if self.societe_info:
forme = self._get_societe_field("forme_juridique", "") forme = self._get_societe_field("forme_juridique", "")
capital = getattr(self.societe_info, "capital", 0) capital = self._get_societe_field("capital", 0)
if forme and capital > 0: # Convertir en float si c'est une string
if isinstance(capital, str):
try:
capital = float(capital)
except (ValueError, TypeError):
capital = 0
if forme and capital and float(capital) > 0:
legal_parts.append( legal_parts.append(
f"{forme} au capital de {capital:,.0f}".replace(",", " ") f"{forme} au capital de {float(capital):,.0f}".replace(",", " ")
) )
# Dessiner les informations légales # Dessiner les informations légales