fix(sage_connector): improve client creation with better error handling

This commit is contained in:
Fanilo-Nantenaina 2025-12-05 22:41:33 +03:00
parent c48a3f033a
commit 53517136f2

View file

@ -2378,31 +2378,38 @@ class SageConnector:
def creer_client(self, client_data: Dict) -> Dict:
"""
Crée un nouveau client dans Sage 100c via l'API COM.
CORRECTION : Cast obligatoire après Create()
CORRECTION : CT_Type est en lecture seule, défini automatiquement par la factory
"""
if not self.cial:
raise RuntimeError("Connexion Sage non établie")
try:
with self._com_context(), self._lock_com:
# 1. Accès à la Factory Client
# 1. Accès à la Factory Client (définit automatiquement CT_Type = 0)
factory_client = self.cial.CptaApplication.FactoryClient
# Create() retourne IBIPersistObject (générique)
# Create() retourne IBIPersistObject
persist = factory_client.Create()
if not persist:
raise RuntimeError("Factory.Create() a retourné None")
logger.debug(f"📦 Objet IBIPersistObject créé : {type(persist)}")
logger.debug(f"📦 Objet IBIPersistObject créé")
# ✅ CAST OBLIGATOIRE vers IBOClient3 pour accéder aux propriétés métier
# Cast vers IBOClient3
try:
client = win32com.client.CastTo(persist, "IBOClient3")
logger.debug(f"✅ Cast réussi vers IBOClient3")
except Exception as e:
raise RuntimeError(f"Impossible de caster vers IBOClient3: {e}")
# Vérifier que CT_Type est bien défini automatiquement
try:
ct_type_auto = getattr(client, "CT_Type", None)
logger.debug(f"✅ CT_Type défini automatiquement par factory: {ct_type_auto}")
except Exception as e:
logger.warning(f"⚠️ Impossible de lire CT_Type: {e}")
# 2. Remplissage des Champs OBLIGATOIRES
logger.info(f"📝 Création client: {client_data['intitule']}")
@ -2413,12 +2420,8 @@ class SageConnector:
except Exception as e:
raise RuntimeError(f"Impossible de définir CT_Intitule: {e}")
# CT_Type - OBLIGATOIRE
try:
client.CT_Type = 0 # 0 = Client
logger.debug("✅ CT_Type = 0 (Client)")
except Exception as e:
raise RuntimeError(f"Impossible de définir CT_Type: {e}")
# ❌ CT_Type - NE PAS DÉFINIR (lecture seule, auto-défini par factory)
# La FactoryClient définit automatiquement CT_Type = 0
# CT_Num (Numéro de compte) - Optionnel si auto-numérotation
num_prop = client_data.get("num", "")
@ -2429,7 +2432,7 @@ class SageConnector:
except Exception as e:
logger.warning(f"⚠️ Impossible de définir CT_Num: {e}")
# Compte comptable général
# Compte comptable général
try:
if client_data.get("compte_collectif"):
client.CT_CompteG = client_data["compte_collectif"]
@ -2459,7 +2462,7 @@ class SageConnector:
# 3. CHAMPS OPTIONNELS
# --- Adresse principale (Accès via sous-objet Adresse) ---
# --- Adresse principale ---
try:
adresse_obj = getattr(client, "Adresse", None)
if adresse_obj:
@ -2475,7 +2478,7 @@ class SageConnector:
except Exception as e:
logger.warning(f"⚠️ Impossible de définir l'adresse: {e}")
# --- Contact / Télécom (Accès via sous-objet Telecom) ---
# --- Contact / Télécom ---
try:
telecom_obj = getattr(client, "Telecom", None)
if telecom_obj:
@ -2502,12 +2505,22 @@ class SageConnector:
logger.info("💾 Écriture du client dans Sage...")
try:
client.Write()
logger.debug("✅ Write() réussi")
except Exception as e:
raise RuntimeError(f"Échec Write(): {e}")
# Récupérer l'erreur Sage détaillée si disponible
error_detail = str(e)
try:
sage_error = self.cial.CptaApplication.LastError
if sage_error:
error_detail = f"{sage_error.Description} (Code: {sage_error.Number})"
except:
pass
raise RuntimeError(f"Échec Write(): {error_detail}")
# ✅ IMPORTANT: Relire pour récupérer le numéro auto-généré
# 5. Relire pour récupérer le numéro auto-généré
try:
client.Read()
logger.debug("✅ Read() après Write() réussi")
except Exception as e:
logger.warning(f"⚠️ Impossible de relire après Write(): {e}")
@ -2516,7 +2529,7 @@ class SageConnector:
if not num_final:
raise RuntimeError("CT_Num vide après Write() - création échouée")
# Si CT_NumPayeur était vide, le définir maintenant avec le numéro auto-généré
# Si CT_NumPayeur était vide, le définir maintenant
if not num_prop:
try:
client.CT_NumPayeur = num_final
@ -2525,15 +2538,16 @@ class SageConnector:
except Exception as e:
logger.warning(f"⚠️ Impossible de définir CT_NumPayeur après création: {e}")
logger.info(f" Client créé avec succès: {num_final} - {client.CT_Intitule}")
logger.info(f"✅✅ Client créé avec succès: {num_final} - {client.CT_Intitule} ✅✅✅")
# ✅ Forcer le refresh du cache pour que le nouveau client soit visible
# 6. Forcer le refresh du cache
self._refresh_cache_clients()
return {
"numero": num_final,
"intitule": client.CT_Intitule,
"compte_collectif": getattr(client, "CT_CompteG", ""),
"type": getattr(client, "CT_Type", 0), # Retourner le type auto-défini
"adresse": client_data.get("adresse"),
"ville": client_data.get("ville"),
"email": client_data.get("email")
@ -2542,15 +2556,17 @@ class SageConnector:
except Exception as e:
logger.error(f"❌ Erreur création client: {e}", exc_info=True)
# Gestion d'erreur remontant l'erreur COM détaillée (si disponible)
# Gestion d'erreur avec détails Sage
error_message = str(e)
if self.cial:
try:
err = self.cial.LastError
err = self.cial.CptaApplication.LastError
if err:
error_message = f"Erreur Sage: {err.Description}"
if "doublon" in err.Description.lower():
raise ValueError(f"Ce client ou ce numéro existe déjà. {error_message}")
if "doublon" in err.Description.lower() or "existe" in err.Description.lower():
raise ValueError(f"Ce client existe déjà dans Sage. {error_message}")
except ValueError:
raise # Re-lever les ValueError pour différencier erreurs métier vs techniques
except:
pass