Creating new contact successful

This commit is contained in:
fanilo 2025-12-28 21:31:47 +01:00
parent 08686a7b2f
commit 3e617d070a

View file

@ -6663,29 +6663,10 @@ class SageConnector:
return self._lire_document_sql(numero, type_doc=30)
def creer_contact(self, contact_data: Dict) -> Dict:
"""
Crée un nouveau contact pour un client via COM
VERSION COMPLÈTE avec gestion du contact par défaut
Champs Sage Contact (F_CONTACTT):
- CT_Num: Code client parent (obligatoire)
- CT_Nom: Nom (obligatoire, max 35 car)
- CT_Prenom: Prénom (max 35 car)
- CT_Civilite: 0=M., 1=Mme, 2=Mlle, 3=Société
- CT_Fonction: Fonction (max 35 car)
- N_Service: Code service (int)
- CT_Telephone: Tél fixe (max 21 car)
- CT_TelPortable: Mobile (max 21 car)
- CT_Telecopie: Fax (max 21 car)
- CT_EMail: Email (max 69 car)
- CT_Facebook: Facebook (max 69 car)
- CT_LinkedIn: LinkedIn (max 69 car)
- CT_Skype: Skype (max 69 car)
Nouveau:
- est_defaut: Définir comme contact par défaut (boolean)
Crée un nouveau contact dans F_CONTACTT via COM
VERSION CORRIGÉE - Utilise IBOTiersContact3
"""
if not self.cial:
raise RuntimeError("Connexion Sage non établie")
@ -6693,130 +6674,206 @@ class SageConnector:
try:
with self._com_context(), self._lock_com:
logger.info("=" * 80)
logger.info("[CRÉATION CONTACT SAGE]")
logger.info("[CREATION CONTACT F_CONTACTT]")
logger.info("=" * 80)
# Validation des champs obligatoires
# Validation
if not contact_data.get("numero"):
raise ValueError("numero (code client) obligatoire")
if not contact_data.get("nom"):
raise ValueError("nom obligatoire")
numero = self._clean_str(contact_data["numero"], 17).upper()
numero_client = self._clean_str(contact_data["numero"], 17).upper()
nom = self._clean_str(contact_data["nom"], 35)
prenom = self._clean_str(contact_data.get("prenom", ""), 35)
# Vérifier que le client existe
logger.info(f"[1] Vérification client: {numero}")
logger.info(f" CLIENT: {numero_client}")
logger.info(f" CONTACT: {prenom} {nom}")
# Charger le client
logger.info(f"[1] Chargement du client: {numero_client}")
factory_client = self.cial.CptaApplication.FactoryClient
try:
persist_client = factory_client.ReadNumero(numero)
persist_client = factory_client.ReadNumero(numero_client)
if not persist_client:
raise ValueError(f"Client {numero} non trouvé")
logger.info(f" ✓ Client {numero} existe")
raise ValueError(f"Client {numero_client} non trouve")
client_obj = win32com.client.CastTo(persist_client, "IBOClient3")
client_obj.Read()
logger.info(f" OK Client charge")
except Exception as e:
raise ValueError(f"Client {numero} introuvable: {e}")
raise ValueError(f"Client {numero_client} introuvable: {e}")
# Créer l'objet contact
logger.info("[2] Création objet Contact")
factory_contact = self.cial.CptaApplication.FactoryContactT
# Via FactoryTiersContact du client
logger.info("[2] Creation via FactoryTiersContact")
if not hasattr(client_obj, 'FactoryTiersContact'):
raise RuntimeError("FactoryTiersContact non trouvee sur le client")
factory_contact = client_obj.FactoryTiersContact
logger.info(f" OK FactoryTiersContact: {type(factory_contact).__name__}")
# Créer l'objet
persist = factory_contact.Create()
contact = win32com.client.CastTo(persist, "IBOContactT3")
logger.info(" ✓ Objet IBOContactT3 créé")
logger.info(f" Objet cree: {type(persist).__name__}")
# Configuration obligatoire
logger.info("[3] Configuration obligatoire")
contact.CT_Num = numero
contact.CT_Nom = nom
logger.info(f" CT_Num = {numero}")
logger.info(f" CT_Nom = {nom}")
# Cast vers IBOTiersContact3 (qui a Nom, Prenom, etc.)
contact = None
interfaces_a_tester = [
"IBOTiersContact3",
"IBOTiersContact",
"IBOContactT3",
"IBOContactT",
]
# SetDefault pour initialiser les valeurs par défaut
contact.SetDefault()
logger.info(" ✓ SetDefault() appliqué")
for interface_name in interfaces_a_tester:
try:
temp = win32com.client.CastTo(persist, interface_name)
# Civilité
logger.info("[4] Identité")
civilite_input = contact_data.get("civilite")
if civilite_input:
civilite_map = {
"M.": 0,
"Mme": 1,
"Mlle": 2,
"Société": 3
}
civilite_code = civilite_map.get(civilite_input)
if civilite_code is not None:
self._try_set_attribute(contact, "CT_Civilite", civilite_code)
logger.info(f" CT_Civilite = {civilite_code} ({civilite_input})")
# Vérifier si Nom existe (pas CT_Num !)
if hasattr(temp, '_prop_map_put_'):
props = list(temp._prop_map_put_.keys())
logger.info(f" Test {interface_name}: props={props[:15]}")
# Chercher Nom ou CT_Nom
if 'Nom' in props or 'CT_Nom' in props:
contact = temp
logger.info(f" OK Cast reussi vers {interface_name}")
break
except Exception as e:
logger.debug(f" {interface_name}: {str(e)[:50]}")
if not contact:
logger.error(" ERROR Aucun cast ne fonctionne")
raise RuntimeError("Impossible de caster vers une interface contact valide")
# Configuration du contact
logger.info("[3] Configuration du contact")
# Vérifier les propriétés disponibles
if hasattr(contact, '_prop_map_put_'):
props = list(contact._prop_map_put_.keys())
logger.info(f" Proprietes disponibles: {props}")
# Nom (obligatoire)
try:
contact.Nom = nom
logger.info(f" OK Nom = {nom}")
except Exception as e:
logger.error(f" ERROR Impossible de definir Nom: {e}")
raise RuntimeError(f"Echec definition Nom: {e}")
# Prénom
if contact_data.get("prenom"):
prenom = self._clean_str(contact_data["prenom"], 35)
self._try_set_attribute(contact, "CT_Prenom", prenom)
logger.info(f" CT_Prenom = {prenom}")
if prenom:
try:
contact.Prenom = prenom
logger.info(f" OK Prenom = {prenom}")
except Exception as e:
logger.warning(f" WARN Prenom: {e}")
# Civilité
if contact_data.get("civilite"):
civilite_map = {"M.": 0, "Mme": 1, "Mlle": 2, "Societe": 3}
civilite_code = civilite_map.get(contact_data["civilite"])
if civilite_code is not None:
try:
contact.Civilite = civilite_code
logger.info(f" OK Civilite = {civilite_code}")
except Exception as e:
logger.warning(f" WARN Civilite: {e}")
# Fonction
if contact_data.get("fonction"):
fonction = self._clean_str(contact_data["fonction"], 35)
self._try_set_attribute(contact, "CT_Fonction", fonction)
logger.info(f" CT_Fonction = {fonction}")
try:
contact.Fonction = fonction
logger.info(f" OK Fonction = {fonction}")
except Exception as e:
logger.warning(f" WARN Fonction: {e}")
# Service
logger.info("[5] Organisation")
if contact_data.get("service_code") is not None:
service = self._safe_int(contact_data["service_code"])
if service is not None:
self._try_set_attribute(contact, "N_Service", service)
logger.info(f" N_Service = {service}")
try:
service = self._safe_int(contact_data["service_code"])
if service is not None and hasattr(contact, 'ServiceContact'):
contact.ServiceContact = service
logger.info(f" OK ServiceContact = {service}")
except Exception as e:
logger.warning(f" WARN ServiceContact: {e}")
# Coordonnées
logger.info("[6] Coordonnées")
# Telecom
logger.info("[4] Coordonnees (Telecom)")
if contact_data.get("telephone"):
telephone = self._clean_str(contact_data["telephone"], 21)
self._try_set_attribute(contact, "CT_Telephone", telephone)
logger.info(f" CT_Telephone = {telephone}")
if hasattr(contact, 'Telecom'):
try:
telecom = contact.Telecom
logger.info(f" Type Telecom: {type(telecom).__name__}")
if contact_data.get("portable"):
portable = self._clean_str(contact_data["portable"], 21)
self._try_set_attribute(contact, "CT_TelPortable", portable)
logger.info(f" CT_TelPortable = {portable}")
if contact_data.get("telephone"):
telephone = self._clean_str(contact_data["telephone"], 21)
if self._try_set_attribute(telecom, "Telephone", telephone):
logger.info(f" Telephone = {telephone}")
if contact_data.get("telecopie"):
fax = self._clean_str(contact_data["telecopie"], 21)
self._try_set_attribute(contact, "CT_Telecopie", fax)
logger.info(f" CT_Telecopie = {fax}")
if contact_data.get("portable"):
portable = self._clean_str(contact_data["portable"], 21)
if self._try_set_attribute(telecom, "Portable", portable):
logger.info(f" Portable = {portable}")
if contact_data.get("email"):
email = self._clean_str(contact_data["email"], 69)
self._try_set_attribute(contact, "CT_EMail", email)
logger.info(f" CT_EMail = {email}")
if contact_data.get("email"):
email = self._clean_str(contact_data["email"], 69)
if self._try_set_attribute(telecom, "EMail", email):
logger.info(f" EMail = {email}")
if contact_data.get("telecopie"):
fax = self._clean_str(contact_data["telecopie"], 21)
if self._try_set_attribute(telecom, "Telecopie", fax):
logger.info(f" Telecopie = {fax}")
except Exception as e:
logger.warning(f" WARN Erreur Telecom: {e}")
# Réseaux sociaux
logger.info("[7] Réseaux sociaux")
logger.info("[5] Reseaux sociaux")
if contact_data.get("facebook"):
facebook = self._clean_str(contact_data["facebook"], 69)
self._try_set_attribute(contact, "CT_Facebook", facebook)
logger.info(f" CT_Facebook = {facebook}")
try:
contact.Facebook = facebook
logger.info(f" Facebook = {facebook}")
except:
pass
if contact_data.get("linkedin"):
linkedin = self._clean_str(contact_data["linkedin"], 69)
self._try_set_attribute(contact, "CT_LinkedIn", linkedin)
logger.info(f" CT_LinkedIn = {linkedin}")
try:
contact.LinkedIn = linkedin
logger.info(f" LinkedIn = {linkedin}")
except:
pass
if contact_data.get("skype"):
skype = self._clean_str(contact_data["skype"], 69)
self._try_set_attribute(contact, "CT_Skype", skype)
logger.info(f" CT_Skype = {skype}")
try:
contact.Skype = skype
logger.info(f" Skype = {skype}")
except:
pass
# Enregistrement du contact
logger.info("[8] WRITE CONTACT")
# SetDefault
try:
contact.SetDefault()
logger.info(" OK SetDefault() applique")
except Exception as e:
logger.warning(f" WARN SetDefault(): {e}")
# Enregistrer
logger.info("[6] Enregistrement du contact")
try:
contact.Write()
logger.info(" OK Write() reussi")
contact.Read()
logger.info(" ✓ Write() réussi")
logger.info(" OK Read() reussi")
except Exception as e:
error_detail = str(e)
try:
@ -6825,59 +6882,72 @@ class SageConnector:
error_detail = f"{sage_error.Description} (Code: {sage_error.Number})"
except:
pass
logger.error(f" ✗ Erreur Write: {error_detail}")
raise RuntimeError(f"Échec création contact: {error_detail}")
logger.error(f" ERROR Write: {error_detail}")
raise RuntimeError(f"Echec enregistrement: {error_detail}")
# Récupération des données finales
contact_numero = getattr(contact, "CT_No", None)
n_contact = getattr(contact, "N_Contact", None)
# Récupérer les IDs
contact_no = None
n_contact = None
try:
contact_no = getattr(contact, 'CT_No', None)
n_contact = getattr(contact, 'N_Contact', None)
logger.info(f" Contact CT_No={contact_no}, N_Contact={n_contact}")
except:
pass
logger.info(f" Contact créé: CT_No={contact_numero}, N_Contact={n_contact}")
# Gestion du contact par défaut
# Contact par défaut
est_defaut = contact_data.get("est_defaut", False)
if est_defaut:
logger.info("[9] Définition comme contact par défaut")
if est_defaut and (contact_no or n_contact):
logger.info("[7] Definition comme contact par defaut")
try:
if contact_numero:
# Construire le nom complet pour CT_Contact
prenom = self._clean_str(contact_data.get("prenom", ""), 35)
nom_complet = f"{prenom} {nom}".strip() if prenom else nom
nom_complet = f"{prenom} {nom}".strip() if prenom else nom
# Charger le client pour mise à jour
persist_client = factory_client.ReadNumero(numero)
client_obj = win32com.client.CastTo(persist_client, "IBOClient3")
client_obj.Read()
persist_client = factory_client.ReadNumero(numero_client)
client_obj = win32com.client.CastTo(persist_client, "IBOClient3")
client_obj.Read()
# Définir CT_Contact
ancien_contact = getattr(client_obj, "CT_Contact", "")
client_obj.CT_Contact = nom_complet
logger.info(f" CT_Contact: '{ancien_contact}''{nom_complet}'")
client_obj.CT_Contact = nom_complet
logger.info(f" CT_Contact = '{nom_complet}'")
# Essayer CT_NoContact si disponible
if self._try_set_attribute(client_obj, "CT_NoContact", contact_numero):
logger.info(f" CT_NoContact = {contact_numero}")
# Enregistrer le client
client_obj.Write()
client_obj.Read()
logger.info(f" ✓ Contact défini comme par défaut")
if contact_no and hasattr(client_obj, 'CT_NoContact'):
try:
client_obj.CT_NoContact = contact_no
logger.info(f" CT_NoContact = {contact_no}")
except:
pass
client_obj.Write()
client_obj.Read()
logger.info(" OK Contact par defaut defini")
except Exception as e:
logger.warning(f" ⚠ Échec définition par défaut: {e}")
# On ne fait pas échouer la création pour autant
est_defaut = False # On indique que ça n'a pas marché
logger.warning(f" WARN Echec: {e}")
est_defaut = False
logger.info("=" * 80)
logger.info(f"[SUCCÈS] Contact créé: CT_No={contact_numero}, N_Contact={n_contact}")
if est_defaut:
logger.info(f" Défini comme contact par défaut")
logger.info(f"[SUCCES] Contact cree: {prenom} {nom}")
logger.info(f" Lie au client {numero_client}")
if contact_no:
logger.info(f" CT_No={contact_no}")
logger.info("=" * 80)
# Retourner les données complètes
contact_dict = self._contact_to_dict(contact)
contact_dict["est_defaut"] = est_defaut
# Retour
contact_dict = {
"numero": numero_client,
"n_contact": n_contact or contact_no,
"civilite": contact_data.get("civilite"),
"nom": nom,
"prenom": prenom,
"fonction": contact_data.get("fonction"),
"service_code": contact_data.get("service_code"),
"telephone": contact_data.get("telephone"),
"portable": contact_data.get("portable"),
"telecopie": contact_data.get("telecopie"),
"email": contact_data.get("email"),
"facebook": contact_data.get("facebook"),
"linkedin": contact_data.get("linkedin"),
"skype": contact_data.get("skype"),
"est_defaut": est_defaut,
}
return contact_dict
@ -6892,7 +6962,7 @@ class SageConnector:
def modifier_contact(self, numero: str, contact_numero: int, updates: Dict) -> Dict:
"""
Modifie un contact existant via COM
VERSION COMPLÈTE avec gestion du contact par défaut
VERSION CORRIGÉE pour Sage 100
"""
if not self.cial:
raise RuntimeError("Connexion Sage non établie")
@ -6903,18 +6973,53 @@ class SageConnector:
logger.info(f"[MODIFICATION CONTACT] CT_No={contact_numero}")
logger.info("=" * 80)
# Lire le contact existant
# Lire le contact existant - CORRECTION ICI
logger.info("[1] Lecture contact existant")
factory_contact = self.cial.CptaApplication.FactoryContactT
# Trouver la factory
factory_contact = None
factory_names = [
"FactoryContact",
"FactoryContactTiers",
"FactoryTypeContacts",
]
for factory_name in factory_names:
try:
if hasattr(self.cial.CptaApplication, factory_name):
factory_contact = getattr(self.cial.CptaApplication, factory_name)
break
except:
continue
if not factory_contact:
raise RuntimeError("Aucune factory de contacts trouvée")
try:
persist = factory_contact.ReadNumero(numero, contact_numero)
# ReadNumero peut accepter 1 ou 2 paramètres selon la version
try:
persist = factory_contact.ReadNumero(numero, contact_numero)
except TypeError:
# Si 2 paramètres ne fonctionnent pas, essayer avec CT_No seulement
persist = factory_contact.ReadNumero(contact_numero)
if not persist:
raise ValueError(f"Contact CT_No={contact_numero} non trouvé pour client {numero}")
contact = win32com.client.CastTo(persist, "IBOContactT3")
# Essayer de caster
contact = None
for interface_name in ["IBOContactT3", "IBOContact3", "IBOContactT"]:
try:
contact = win32com.client.CastTo(persist, interface_name)
break
except:
continue
if not contact:
contact = persist
contact.Read()
logger.info(f" ✓ Contact chargé: {contact.CT_Nom}")
logger.info(f" ✓ Contact chargé: {getattr(contact, 'CT_Nom', '?')}")
except Exception as e:
raise ValueError(f"Contact introuvable: {e}")
@ -6934,7 +7039,7 @@ class SageConnector:
if "nom" in updates:
nom = self._clean_str(updates["nom"], 35)
if nom: # Ne pas autoriser nom vide
if nom:
self._try_set_attribute(contact, "CT_Nom", nom)
logger.info(f" CT_Nom = {nom}")
modifications_appliquees.append("nom")
@ -6992,7 +7097,10 @@ class SageConnector:
logger.info("[3] WRITE CONTACT")
try:
contact.Write()
contact.Read()
try:
contact.Read()
except:
pass
logger.info(" ✓ Write() réussi")
except Exception as e:
error_detail = str(e)
@ -7011,46 +7119,37 @@ class SageConnector:
est_defaut_demande = updates.get("est_defaut")
est_actuellement_defaut = False
if est_defaut_demande is not None:
if est_defaut_demande is not None and est_defaut_demande:
logger.info("[4] Gestion contact par défaut")
try:
# Construire le nom complet
nom_final = getattr(contact, "CT_Nom", "")
prenom_final = getattr(contact, "CT_Prenom", "")
nom_complet = f"{prenom_final} {nom_final}".strip() if prenom_final else nom_final
if est_defaut_demande:
# Définir comme contact par défaut
try:
# Construire le nom complet
nom_final = getattr(contact, "CT_Nom", "")
prenom_final = getattr(contact, "CT_Prenom", "")
nom_complet = f"{prenom_final} {nom_final}".strip() if prenom_final else nom_final
# Charger le client
factory_client = self.cial.CptaApplication.FactoryClient
persist_client = factory_client.ReadNumero(numero)
client_obj = win32com.client.CastTo(persist_client, "IBOClient3")
client_obj.Read()
# Charger le client
factory_client = self.cial.CptaApplication.FactoryClient
persist_client = factory_client.ReadNumero(numero)
client_obj = win32com.client.CastTo(persist_client, "IBOClient3")
client_obj.Read()
# Mettre à jour CT_Contact
ancien_contact = getattr(client_obj, "CT_Contact", "")
client_obj.CT_Contact = nom_complet
logger.info(f" CT_Contact: '{ancien_contact}''{nom_complet}'")
# Mettre à jour CT_Contact
ancien_contact = getattr(client_obj, "CT_Contact", "")
client_obj.CT_Contact = nom_complet
logger.info(f" CT_Contact: '{ancien_contact}''{nom_complet}'")
# Essayer CT_NoContact si disponible
if self._try_set_attribute(client_obj, "CT_NoContact", contact_numero):
logger.info(f" CT_NoContact = {contact_numero}")
# Essayer CT_NoContact si disponible
if self._try_set_attribute(client_obj, "CT_NoContact", contact_numero):
logger.info(f" CT_NoContact = {contact_numero}")
# Enregistrer le client
client_obj.Write()
client_obj.Read()
logger.info(" ✓ Contact défini comme par défaut")
est_actuellement_defaut = True
# Enregistrer le client
client_obj.Write()
client_obj.Read()
logger.info(" ✓ Contact défini comme par défaut")
est_actuellement_defaut = True
except Exception as e:
logger.warning(f" ⚠ Échec définition par défaut: {e}")
est_actuellement_defaut = False
else:
# Retirer le statut par défaut
# Note: On ne fait rien car Sage gère via CT_Contact du client
# Si on veut vraiment retirer, il faut définir un autre contact comme par défaut
logger.info(" ⓘ Pour retirer le statut par défaut, définir un autre contact")
except Exception as e:
logger.warning(f" ⚠ Échec définition par défaut: {e}")
else:
# Vérifier si c'est déjà le contact par défaut
try:
@ -7065,9 +7164,8 @@ class SageConnector:
nom_complet = f"{prenom_final} {nom_final}".strip() if prenom_final else nom_final
est_actuellement_defaut = (ct_contact == nom_complet)
except Exception as e:
logger.debug(f"Impossible de vérifier statut par défaut: {e}")
except:
pass
logger.info("=" * 80)
logger.info(f"[SUCCÈS] Contact modifié: CT_No={contact_numero}")
@ -7075,7 +7173,6 @@ class SageConnector:
logger.info(f" Contact par défaut")
logger.info("=" * 80)
# Retourner les données mises à jour
contact_dict = self._contact_to_dict(contact)
contact_dict["est_defaut"] = est_actuellement_defaut
@ -7089,6 +7186,133 @@ class SageConnector:
raise RuntimeError(f"Erreur technique: {e}")
def definir_contact_defaut(self, numero: str, contact_numero: int) -> Dict:
"""
Définit un contact comme contact par défaut du client
VERSION CORRIGÉE pour Sage 100
"""
if not self.cial:
raise RuntimeError("Connexion Sage non établie")
try:
with self._com_context(), self._lock_com:
logger.info("=" * 80)
logger.info(f"[DÉFINIR CONTACT PAR DÉFAUT] Client={numero}, Contact={contact_numero}")
logger.info("=" * 80)
# Charger le contact - CORRECTION ICI
logger.info("[1] Chargement du contact")
# Trouver la factory
factory_contact = None
for factory_name in ["FactoryContact", "FactoryContactTiers", "FactoryTypeContacts"]:
try:
if hasattr(self.cial.CptaApplication, factory_name):
factory_contact = getattr(self.cial.CptaApplication, factory_name)
break
except:
continue
if not factory_contact:
raise RuntimeError("Factory contacts non trouvée")
try:
# ReadNumero peut accepter 1 ou 2 paramètres
try:
persist_contact = factory_contact.ReadNumero(numero, contact_numero)
except TypeError:
persist_contact = factory_contact.ReadNumero(contact_numero)
if not persist_contact:
raise ValueError(f"Contact CT_No={contact_numero} non trouvé")
# Caster
contact = None
for interface in ["IBOContactT3", "IBOContact3", "IBOContactT"]:
try:
contact = win32com.client.CastTo(persist_contact, interface)
break
except:
continue
if not contact:
contact = persist_contact
contact.Read()
nom_contact = getattr(contact, "CT_Nom", "")
prenom_contact = getattr(contact, "CT_Prenom", "")
nom_complet = f"{prenom_contact} {nom_contact}".strip() if prenom_contact else nom_contact
logger.info(f" ✓ Contact trouvé: {nom_complet}")
except Exception as e:
raise ValueError(f"Contact introuvable: {e}")
# Charger le client
logger.info("[2] Chargement du client")
factory_client = self.cial.CptaApplication.FactoryClient
try:
persist_client = factory_client.ReadNumero(numero)
if not persist_client:
raise ValueError(f"Client {numero} non trouvé")
client = win32com.client.CastTo(persist_client, "IBOClient3")
client.Read()
logger.info(f" ✓ Client chargé: {client.CT_Intitule}")
except Exception as e:
raise ValueError(f"Client introuvable: {e}")
# Définir le contact par défaut
logger.info("[3] Définition du contact par défaut")
ancien_contact = getattr(client, "CT_Contact", "")
client.CT_Contact = nom_complet
logger.info(f" CT_Contact: '{ancien_contact}''{nom_complet}'")
# Essayer CT_NoContact si disponible
if self._try_set_attribute(client, "CT_NoContact", contact_numero):
logger.info(f" CT_NoContact = {contact_numero}")
# Enregistrement
logger.info("[4] WRITE")
try:
client.Write()
client.Read()
logger.info(" ✓ Client mis à jour")
except Exception as e:
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 mise à jour: {error_detail}")
logger.info("=" * 80)
logger.info(f"[SUCCÈS] Contact par défaut: {nom_complet}")
logger.info("=" * 80)
return {
"numero": numero,
"contact_numero": contact_numero,
"contact_nom": nom_complet,
"client_intitule": client.CT_Intitule,
"est_defaut": True,
"date_modification": datetime.now().isoformat()
}
except ValueError as e:
logger.error(f"[ERREUR VALIDATION] {e}")
raise
except Exception as e:
logger.error(f"[ERREUR] {e}", exc_info=True)
raise RuntimeError(f"Erreur technique: {e}")
def lister_contacts(self, numero: str) -> List[Dict]:
"""
Liste tous les contacts d'un client
@ -7257,132 +7481,6 @@ class SageConnector:
raise RuntimeError(f"Erreur technique: {e}")
def definir_contact_defaut(self, numero: str, contact_numero: int) -> Dict:
"""
Définit un contact comme contact par défaut du client
Sage gère le contact par défaut via :
1. CT_Contact dans F_COMPTET : Nom du contact principal (35 car)
2. CT_NoContact dans F_COMPTET : Numéro du contact par défaut (si disponible)
Cette méthode :
- Vérifie que le contact existe
- Met à jour le client (F_COMPTET) pour référencer ce contact
- Retourne les infos du client et contact mis à jour
"""
if not self.cial:
raise RuntimeError("Connexion Sage non établie")
try:
with self._com_context(), self._lock_com:
logger.info("=" * 80)
logger.info(f"[DÉFINIR CONTACT PAR DÉFAUT] Client={numero}, Contact={contact_numero}")
logger.info("=" * 80)
# Étape 1: Charger le contact
logger.info("[1] Chargement du contact")
factory_contact = self.cial.CptaApplication.FactoryContactT
try:
persist_contact = factory_contact.ReadNumero(numero, contact_numero)
if not persist_contact:
raise ValueError(f"Contact CT_No={contact_numero} non trouvé pour client {numero}")
contact = win32com.client.CastTo(persist_contact, "IBOContactT3")
contact.Read()
nom_contact = getattr(contact, "CT_Nom", "")
prenom_contact = getattr(contact, "CT_Prenom", "")
# Construire le nom complet
nom_complet = f"{prenom_contact} {nom_contact}".strip() if prenom_contact else nom_contact
logger.info(f" ✓ Contact trouvé: {nom_complet}")
except Exception as e:
raise ValueError(f"Contact introuvable: {e}")
# Étape 2: Charger le client
logger.info("[2] Chargement du client")
factory_client = self.cial.CptaApplication.FactoryClient
try:
persist_client = factory_client.ReadNumero(numero)
if not persist_client:
raise ValueError(f"Client {numero} non trouvé")
client = win32com.client.CastTo(persist_client, "IBOClient3")
client.Read()
logger.info(f" ✓ Client chargé: {client.CT_Intitule}")
except Exception as e:
raise ValueError(f"Client introuvable: {e}")
# Étape 3: Définir le contact par défaut
logger.info("[3] Définition du contact par défaut")
# Méthode 1: Via CT_Contact (nom du contact)
try:
ancien_contact = getattr(client, "CT_Contact", "")
client.CT_Contact = nom_complet
logger.info(f" CT_Contact: '{ancien_contact}''{nom_complet}'")
except Exception as e:
logger.warning(f" Impossible de définir CT_Contact: {e}")
# Méthode 2: Via CT_NoContact (numéro du contact) - si disponible
# Ce champ n'existe pas dans toutes les versions de Sage
if self._try_set_attribute(client, "CT_NoContact", contact_numero):
logger.info(f" CT_NoContact = {contact_numero}")
else:
logger.info(f" CT_NoContact non disponible (normal dans certaines versions)")
# Méthode 3: Via l'objet Contact (relation) - si disponible
try:
if hasattr(client, "Contact") or hasattr(client, "ContactPrincipal"):
client.Contact = contact
logger.info(f" Contact (objet) défini")
except Exception as e:
logger.debug(f" Contact (objet) non disponible: {e}")
# Étape 4: Enregistrement
logger.info("[4] WRITE")
try:
client.Write()
client.Read()
logger.info(" ✓ Client mis à jour")
except Exception as e:
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
logger.error(f" ✗ Erreur Write: {error_detail}")
raise RuntimeError(f"Échec mise à jour client: {error_detail}")
logger.info("=" * 80)
logger.info(f"[SUCCÈS] Contact par défaut défini: {nom_complet}")
logger.info("=" * 80)
return {
"numero": numero,
"contact_numero": contact_numero,
"contact_nom": nom_complet,
"client_intitule": client.CT_Intitule,
"est_defaut": True,
"date_modification": datetime.now().isoformat()
}
except ValueError as e:
logger.error(f"[ERREUR VALIDATION] {e}")
raise
except Exception as e:
logger.error(f"[ERREUR] {e}", exc_info=True)
raise RuntimeError(f"Erreur technique: {e}")
def _contact_to_dict(self, contact) -> Dict:
"""Convertit un objet COM Contact en dictionnaire"""
try: