Creating new contact successful
This commit is contained in:
parent
08686a7b2f
commit
3e617d070a
1 changed files with 420 additions and 322 deletions
|
|
@ -6663,29 +6663,10 @@ class SageConnector:
|
||||||
return self._lire_document_sql(numero, type_doc=30)
|
return self._lire_document_sql(numero, type_doc=30)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def creer_contact(self, contact_data: Dict) -> Dict:
|
def creer_contact(self, contact_data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
Crée un nouveau contact pour un client via COM
|
Crée un nouveau contact dans F_CONTACTT via COM
|
||||||
VERSION COMPLÈTE avec gestion du contact par défaut
|
VERSION CORRIGÉE - Utilise IBOTiersContact3
|
||||||
|
|
||||||
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)
|
|
||||||
"""
|
"""
|
||||||
if not self.cial:
|
if not self.cial:
|
||||||
raise RuntimeError("Connexion Sage non établie")
|
raise RuntimeError("Connexion Sage non établie")
|
||||||
|
|
@ -6693,130 +6674,206 @@ class SageConnector:
|
||||||
try:
|
try:
|
||||||
with self._com_context(), self._lock_com:
|
with self._com_context(), self._lock_com:
|
||||||
logger.info("=" * 80)
|
logger.info("=" * 80)
|
||||||
logger.info("[CRÉATION CONTACT SAGE]")
|
logger.info("[CREATION CONTACT F_CONTACTT]")
|
||||||
logger.info("=" * 80)
|
logger.info("=" * 80)
|
||||||
|
|
||||||
# Validation des champs obligatoires
|
# Validation
|
||||||
if not contact_data.get("numero"):
|
if not contact_data.get("numero"):
|
||||||
raise ValueError("numero (code client) obligatoire")
|
raise ValueError("numero (code client) obligatoire")
|
||||||
|
|
||||||
if not contact_data.get("nom"):
|
if not contact_data.get("nom"):
|
||||||
raise ValueError("nom obligatoire")
|
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)
|
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" CLIENT: {numero_client}")
|
||||||
logger.info(f"[1] Vérification client: {numero}")
|
logger.info(f" CONTACT: {prenom} {nom}")
|
||||||
|
|
||||||
|
# Charger le client
|
||||||
|
logger.info(f"[1] Chargement du client: {numero_client}")
|
||||||
factory_client = self.cial.CptaApplication.FactoryClient
|
factory_client = self.cial.CptaApplication.FactoryClient
|
||||||
try:
|
try:
|
||||||
persist_client = factory_client.ReadNumero(numero)
|
persist_client = factory_client.ReadNumero(numero_client)
|
||||||
if not persist_client:
|
if not persist_client:
|
||||||
raise ValueError(f"Client {numero} non trouvé")
|
raise ValueError(f"Client {numero_client} non trouve")
|
||||||
logger.info(f" ✓ Client {numero} existe")
|
|
||||||
|
client_obj = win32com.client.CastTo(persist_client, "IBOClient3")
|
||||||
|
client_obj.Read()
|
||||||
|
logger.info(f" OK Client charge")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"Client {numero} introuvable: {e}")
|
raise ValueError(f"Client {numero_client} introuvable: {e}")
|
||||||
|
|
||||||
# Créer l'objet contact
|
# Via FactoryTiersContact du client
|
||||||
logger.info("[2] Création objet Contact")
|
logger.info("[2] Creation via FactoryTiersContact")
|
||||||
factory_contact = self.cial.CptaApplication.FactoryContactT
|
|
||||||
|
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()
|
persist = factory_contact.Create()
|
||||||
contact = win32com.client.CastTo(persist, "IBOContactT3")
|
logger.info(f" Objet cree: {type(persist).__name__}")
|
||||||
logger.info(" ✓ Objet IBOContactT3 créé")
|
|
||||||
|
|
||||||
# Configuration obligatoire
|
# Cast vers IBOTiersContact3 (qui a Nom, Prenom, etc.)
|
||||||
logger.info("[3] Configuration obligatoire")
|
contact = None
|
||||||
contact.CT_Num = numero
|
interfaces_a_tester = [
|
||||||
contact.CT_Nom = nom
|
"IBOTiersContact3",
|
||||||
logger.info(f" CT_Num = {numero}")
|
"IBOTiersContact",
|
||||||
logger.info(f" CT_Nom = {nom}")
|
"IBOContactT3",
|
||||||
|
"IBOContactT",
|
||||||
|
]
|
||||||
|
|
||||||
# SetDefault pour initialiser les valeurs par défaut
|
for interface_name in interfaces_a_tester:
|
||||||
contact.SetDefault()
|
try:
|
||||||
logger.info(" ✓ SetDefault() appliqué")
|
temp = win32com.client.CastTo(persist, interface_name)
|
||||||
|
|
||||||
|
# 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]}")
|
||||||
|
|
||||||
# Civilité
|
if not contact:
|
||||||
logger.info("[4] Identité")
|
logger.error(" ERROR Aucun cast ne fonctionne")
|
||||||
civilite_input = contact_data.get("civilite")
|
raise RuntimeError("Impossible de caster vers une interface contact valide")
|
||||||
if civilite_input:
|
|
||||||
civilite_map = {
|
# Configuration du contact
|
||||||
"M.": 0,
|
logger.info("[3] Configuration du contact")
|
||||||
"Mme": 1,
|
|
||||||
"Mlle": 2,
|
# Vérifier les propriétés disponibles
|
||||||
"Société": 3
|
if hasattr(contact, '_prop_map_put_'):
|
||||||
}
|
props = list(contact._prop_map_put_.keys())
|
||||||
civilite_code = civilite_map.get(civilite_input)
|
logger.info(f" Proprietes disponibles: {props}")
|
||||||
if civilite_code is not None:
|
|
||||||
self._try_set_attribute(contact, "CT_Civilite", civilite_code)
|
# Nom (obligatoire)
|
||||||
logger.info(f" CT_Civilite = {civilite_code} ({civilite_input})")
|
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
|
# Prénom
|
||||||
if contact_data.get("prenom"):
|
if prenom:
|
||||||
prenom = self._clean_str(contact_data["prenom"], 35)
|
try:
|
||||||
self._try_set_attribute(contact, "CT_Prenom", prenom)
|
contact.Prenom = prenom
|
||||||
logger.info(f" CT_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
|
# Fonction
|
||||||
if contact_data.get("fonction"):
|
if contact_data.get("fonction"):
|
||||||
fonction = self._clean_str(contact_data["fonction"], 35)
|
fonction = self._clean_str(contact_data["fonction"], 35)
|
||||||
self._try_set_attribute(contact, "CT_Fonction", fonction)
|
try:
|
||||||
logger.info(f" CT_Fonction = {fonction}")
|
contact.Fonction = fonction
|
||||||
|
logger.info(f" OK Fonction = {fonction}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f" WARN Fonction: {e}")
|
||||||
|
|
||||||
# Service
|
# Service
|
||||||
logger.info("[5] Organisation")
|
|
||||||
if contact_data.get("service_code") is not None:
|
if contact_data.get("service_code") is not None:
|
||||||
service = self._safe_int(contact_data["service_code"])
|
try:
|
||||||
if service is not None:
|
service = self._safe_int(contact_data["service_code"])
|
||||||
self._try_set_attribute(contact, "N_Service", service)
|
if service is not None and hasattr(contact, 'ServiceContact'):
|
||||||
logger.info(f" N_Service = {service}")
|
contact.ServiceContact = service
|
||||||
|
logger.info(f" OK ServiceContact = {service}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f" WARN ServiceContact: {e}")
|
||||||
|
|
||||||
# Coordonnées
|
# Telecom
|
||||||
logger.info("[6] Coordonnées")
|
logger.info("[4] Coordonnees (Telecom)")
|
||||||
|
|
||||||
if contact_data.get("telephone"):
|
if hasattr(contact, 'Telecom'):
|
||||||
telephone = self._clean_str(contact_data["telephone"], 21)
|
try:
|
||||||
self._try_set_attribute(contact, "CT_Telephone", telephone)
|
telecom = contact.Telecom
|
||||||
logger.info(f" CT_Telephone = {telephone}")
|
logger.info(f" Type Telecom: {type(telecom).__name__}")
|
||||||
|
|
||||||
if contact_data.get("portable"):
|
if contact_data.get("telephone"):
|
||||||
portable = self._clean_str(contact_data["portable"], 21)
|
telephone = self._clean_str(contact_data["telephone"], 21)
|
||||||
self._try_set_attribute(contact, "CT_TelPortable", portable)
|
if self._try_set_attribute(telecom, "Telephone", telephone):
|
||||||
logger.info(f" CT_TelPortable = {portable}")
|
logger.info(f" Telephone = {telephone}")
|
||||||
|
|
||||||
if contact_data.get("telecopie"):
|
if contact_data.get("portable"):
|
||||||
fax = self._clean_str(contact_data["telecopie"], 21)
|
portable = self._clean_str(contact_data["portable"], 21)
|
||||||
self._try_set_attribute(contact, "CT_Telecopie", fax)
|
if self._try_set_attribute(telecom, "Portable", portable):
|
||||||
logger.info(f" CT_Telecopie = {fax}")
|
logger.info(f" Portable = {portable}")
|
||||||
|
|
||||||
if contact_data.get("email"):
|
if contact_data.get("email"):
|
||||||
email = self._clean_str(contact_data["email"], 69)
|
email = self._clean_str(contact_data["email"], 69)
|
||||||
self._try_set_attribute(contact, "CT_EMail", email)
|
if self._try_set_attribute(telecom, "EMail", email):
|
||||||
logger.info(f" CT_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
|
# Réseaux sociaux
|
||||||
logger.info("[7] Réseaux sociaux")
|
logger.info("[5] Reseaux sociaux")
|
||||||
|
|
||||||
if contact_data.get("facebook"):
|
if contact_data.get("facebook"):
|
||||||
facebook = self._clean_str(contact_data["facebook"], 69)
|
facebook = self._clean_str(contact_data["facebook"], 69)
|
||||||
self._try_set_attribute(contact, "CT_Facebook", facebook)
|
try:
|
||||||
logger.info(f" CT_Facebook = {facebook}")
|
contact.Facebook = facebook
|
||||||
|
logger.info(f" Facebook = {facebook}")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
if contact_data.get("linkedin"):
|
if contact_data.get("linkedin"):
|
||||||
linkedin = self._clean_str(contact_data["linkedin"], 69)
|
linkedin = self._clean_str(contact_data["linkedin"], 69)
|
||||||
self._try_set_attribute(contact, "CT_LinkedIn", linkedin)
|
try:
|
||||||
logger.info(f" CT_LinkedIn = {linkedin}")
|
contact.LinkedIn = linkedin
|
||||||
|
logger.info(f" LinkedIn = {linkedin}")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
if contact_data.get("skype"):
|
if contact_data.get("skype"):
|
||||||
skype = self._clean_str(contact_data["skype"], 69)
|
skype = self._clean_str(contact_data["skype"], 69)
|
||||||
self._try_set_attribute(contact, "CT_Skype", skype)
|
try:
|
||||||
logger.info(f" CT_Skype = {skype}")
|
contact.Skype = skype
|
||||||
|
logger.info(f" Skype = {skype}")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
# Enregistrement du contact
|
# SetDefault
|
||||||
logger.info("[8] WRITE CONTACT")
|
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:
|
try:
|
||||||
contact.Write()
|
contact.Write()
|
||||||
|
logger.info(" OK Write() reussi")
|
||||||
|
|
||||||
contact.Read()
|
contact.Read()
|
||||||
logger.info(" ✓ Write() réussi")
|
logger.info(" OK Read() reussi")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_detail = str(e)
|
error_detail = str(e)
|
||||||
try:
|
try:
|
||||||
|
|
@ -6825,59 +6882,72 @@ class SageConnector:
|
||||||
error_detail = f"{sage_error.Description} (Code: {sage_error.Number})"
|
error_detail = f"{sage_error.Description} (Code: {sage_error.Number})"
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
logger.error(f" ✗ Erreur Write: {error_detail}")
|
logger.error(f" ERROR Write: {error_detail}")
|
||||||
raise RuntimeError(f"Échec création contact: {error_detail}")
|
raise RuntimeError(f"Echec enregistrement: {error_detail}")
|
||||||
|
|
||||||
# Récupération des données finales
|
# Récupérer les IDs
|
||||||
contact_numero = getattr(contact, "CT_No", None)
|
contact_no = None
|
||||||
n_contact = getattr(contact, "N_Contact", 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}")
|
# Contact par défaut
|
||||||
|
|
||||||
# Gestion du contact par défaut
|
|
||||||
est_defaut = contact_data.get("est_defaut", False)
|
est_defaut = contact_data.get("est_defaut", False)
|
||||||
|
if est_defaut and (contact_no or n_contact):
|
||||||
if est_defaut:
|
logger.info("[7] Definition comme contact par defaut")
|
||||||
logger.info("[9] Définition comme contact par défaut")
|
|
||||||
try:
|
try:
|
||||||
if contact_numero:
|
nom_complet = f"{prenom} {nom}".strip() if prenom else nom
|
||||||
# Construire le nom complet pour CT_Contact
|
|
||||||
prenom = self._clean_str(contact_data.get("prenom", ""), 35)
|
persist_client = factory_client.ReadNumero(numero_client)
|
||||||
nom_complet = f"{prenom} {nom}".strip() if prenom else nom
|
client_obj = win32com.client.CastTo(persist_client, "IBOClient3")
|
||||||
|
client_obj.Read()
|
||||||
# Charger le client pour mise à jour
|
|
||||||
persist_client = factory_client.ReadNumero(numero)
|
client_obj.CT_Contact = nom_complet
|
||||||
client_obj = win32com.client.CastTo(persist_client, "IBOClient3")
|
logger.info(f" CT_Contact = '{nom_complet}'")
|
||||||
client_obj.Read()
|
|
||||||
|
if contact_no and hasattr(client_obj, 'CT_NoContact'):
|
||||||
# Définir CT_Contact
|
try:
|
||||||
ancien_contact = getattr(client_obj, "CT_Contact", "")
|
client_obj.CT_NoContact = contact_no
|
||||||
client_obj.CT_Contact = nom_complet
|
logger.info(f" CT_NoContact = {contact_no}")
|
||||||
logger.info(f" CT_Contact: '{ancien_contact}' → '{nom_complet}'")
|
except:
|
||||||
|
pass
|
||||||
# Essayer CT_NoContact si disponible
|
|
||||||
if self._try_set_attribute(client_obj, "CT_NoContact", contact_numero):
|
client_obj.Write()
|
||||||
logger.info(f" CT_NoContact = {contact_numero}")
|
client_obj.Read()
|
||||||
|
logger.info(" OK Contact par defaut defini")
|
||||||
# Enregistrer le client
|
|
||||||
client_obj.Write()
|
|
||||||
client_obj.Read()
|
|
||||||
logger.info(f" ✓ Contact défini comme par défaut")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f" ⚠ Échec définition par défaut: {e}")
|
logger.warning(f" WARN Echec: {e}")
|
||||||
# On ne fait pas échouer la création pour autant
|
est_defaut = False
|
||||||
est_defaut = False # On indique que ça n'a pas marché
|
|
||||||
|
|
||||||
logger.info("=" * 80)
|
logger.info("=" * 80)
|
||||||
logger.info(f"[SUCCÈS] Contact créé: CT_No={contact_numero}, N_Contact={n_contact}")
|
logger.info(f"[SUCCES] Contact cree: {prenom} {nom}")
|
||||||
if est_defaut:
|
logger.info(f" Lie au client {numero_client}")
|
||||||
logger.info(f" Défini comme contact par défaut")
|
if contact_no:
|
||||||
|
logger.info(f" CT_No={contact_no}")
|
||||||
logger.info("=" * 80)
|
logger.info("=" * 80)
|
||||||
|
|
||||||
# Retourner les données complètes
|
# Retour
|
||||||
contact_dict = self._contact_to_dict(contact)
|
contact_dict = {
|
||||||
contact_dict["est_defaut"] = est_defaut
|
"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
|
return contact_dict
|
||||||
|
|
||||||
|
|
@ -6892,7 +6962,7 @@ class SageConnector:
|
||||||
def modifier_contact(self, numero: str, contact_numero: int, updates: Dict) -> Dict:
|
def modifier_contact(self, numero: str, contact_numero: int, updates: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
Modifie un contact existant via COM
|
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:
|
if not self.cial:
|
||||||
raise RuntimeError("Connexion Sage non établie")
|
raise RuntimeError("Connexion Sage non établie")
|
||||||
|
|
@ -6903,18 +6973,53 @@ class SageConnector:
|
||||||
logger.info(f"[MODIFICATION CONTACT] CT_No={contact_numero}")
|
logger.info(f"[MODIFICATION CONTACT] CT_No={contact_numero}")
|
||||||
logger.info("=" * 80)
|
logger.info("=" * 80)
|
||||||
|
|
||||||
# Lire le contact existant
|
# Lire le contact existant - CORRECTION ICI
|
||||||
logger.info("[1] Lecture contact existant")
|
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:
|
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:
|
if not persist:
|
||||||
raise ValueError(f"Contact CT_No={contact_numero} non trouvé pour client {numero}")
|
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()
|
contact.Read()
|
||||||
logger.info(f" ✓ Contact chargé: {contact.CT_Nom}")
|
logger.info(f" ✓ Contact chargé: {getattr(contact, 'CT_Nom', '?')}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"Contact introuvable: {e}")
|
raise ValueError(f"Contact introuvable: {e}")
|
||||||
|
|
@ -6934,7 +7039,7 @@ class SageConnector:
|
||||||
|
|
||||||
if "nom" in updates:
|
if "nom" in updates:
|
||||||
nom = self._clean_str(updates["nom"], 35)
|
nom = self._clean_str(updates["nom"], 35)
|
||||||
if nom: # Ne pas autoriser nom vide
|
if nom:
|
||||||
self._try_set_attribute(contact, "CT_Nom", nom)
|
self._try_set_attribute(contact, "CT_Nom", nom)
|
||||||
logger.info(f" CT_Nom = {nom}")
|
logger.info(f" CT_Nom = {nom}")
|
||||||
modifications_appliquees.append("nom")
|
modifications_appliquees.append("nom")
|
||||||
|
|
@ -6992,7 +7097,10 @@ class SageConnector:
|
||||||
logger.info("[3] WRITE CONTACT")
|
logger.info("[3] WRITE CONTACT")
|
||||||
try:
|
try:
|
||||||
contact.Write()
|
contact.Write()
|
||||||
contact.Read()
|
try:
|
||||||
|
contact.Read()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
logger.info(" ✓ Write() réussi")
|
logger.info(" ✓ Write() réussi")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_detail = str(e)
|
error_detail = str(e)
|
||||||
|
|
@ -7011,46 +7119,37 @@ class SageConnector:
|
||||||
est_defaut_demande = updates.get("est_defaut")
|
est_defaut_demande = updates.get("est_defaut")
|
||||||
est_actuellement_defaut = False
|
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")
|
logger.info("[4] Gestion contact par défaut")
|
||||||
|
try:
|
||||||
if est_defaut_demande:
|
# Construire le nom complet
|
||||||
# Définir comme contact par défaut
|
nom_final = getattr(contact, "CT_Nom", "")
|
||||||
try:
|
prenom_final = getattr(contact, "CT_Prenom", "")
|
||||||
# Construire le nom complet
|
nom_complet = f"{prenom_final} {nom_final}".strip() if prenom_final else nom_final
|
||||||
nom_final = getattr(contact, "CT_Nom", "")
|
|
||||||
prenom_final = getattr(contact, "CT_Prenom", "")
|
# Charger le client
|
||||||
nom_complet = f"{prenom_final} {nom_final}".strip() if prenom_final else nom_final
|
factory_client = self.cial.CptaApplication.FactoryClient
|
||||||
|
persist_client = factory_client.ReadNumero(numero)
|
||||||
# Charger le client
|
client_obj = win32com.client.CastTo(persist_client, "IBOClient3")
|
||||||
factory_client = self.cial.CptaApplication.FactoryClient
|
client_obj.Read()
|
||||||
persist_client = factory_client.ReadNumero(numero)
|
|
||||||
client_obj = win32com.client.CastTo(persist_client, "IBOClient3")
|
# Mettre à jour CT_Contact
|
||||||
client_obj.Read()
|
ancien_contact = getattr(client_obj, "CT_Contact", "")
|
||||||
|
client_obj.CT_Contact = nom_complet
|
||||||
# Mettre à jour CT_Contact
|
logger.info(f" CT_Contact: '{ancien_contact}' → '{nom_complet}'")
|
||||||
ancien_contact = getattr(client_obj, "CT_Contact", "")
|
|
||||||
client_obj.CT_Contact = nom_complet
|
# Essayer CT_NoContact si disponible
|
||||||
logger.info(f" CT_Contact: '{ancien_contact}' → '{nom_complet}'")
|
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):
|
# Enregistrer le client
|
||||||
logger.info(f" CT_NoContact = {contact_numero}")
|
client_obj.Write()
|
||||||
|
client_obj.Read()
|
||||||
# Enregistrer le client
|
logger.info(" ✓ Contact défini comme par défaut")
|
||||||
client_obj.Write()
|
est_actuellement_defaut = True
|
||||||
client_obj.Read()
|
|
||||||
logger.info(" ✓ Contact défini comme par défaut")
|
except Exception as e:
|
||||||
est_actuellement_defaut = True
|
logger.warning(f" ⚠ Échec définition par défaut: {e}")
|
||||||
|
|
||||||
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")
|
|
||||||
else:
|
else:
|
||||||
# Vérifier si c'est déjà le contact par défaut
|
# Vérifier si c'est déjà le contact par défaut
|
||||||
try:
|
try:
|
||||||
|
|
@ -7065,9 +7164,8 @@ class SageConnector:
|
||||||
nom_complet = f"{prenom_final} {nom_final}".strip() if prenom_final else nom_final
|
nom_complet = f"{prenom_final} {nom_final}".strip() if prenom_final else nom_final
|
||||||
|
|
||||||
est_actuellement_defaut = (ct_contact == nom_complet)
|
est_actuellement_defaut = (ct_contact == nom_complet)
|
||||||
|
except:
|
||||||
except Exception as e:
|
pass
|
||||||
logger.debug(f"Impossible de vérifier statut par défaut: {e}")
|
|
||||||
|
|
||||||
logger.info("=" * 80)
|
logger.info("=" * 80)
|
||||||
logger.info(f"[SUCCÈS] Contact modifié: CT_No={contact_numero}")
|
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(f" Contact par défaut")
|
||||||
logger.info("=" * 80)
|
logger.info("=" * 80)
|
||||||
|
|
||||||
# Retourner les données mises à jour
|
|
||||||
contact_dict = self._contact_to_dict(contact)
|
contact_dict = self._contact_to_dict(contact)
|
||||||
contact_dict["est_defaut"] = est_actuellement_defaut
|
contact_dict["est_defaut"] = est_actuellement_defaut
|
||||||
|
|
||||||
|
|
@ -7087,6 +7184,133 @@ class SageConnector:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[ERREUR] {e}", exc_info=True)
|
logger.error(f"[ERREUR] {e}", exc_info=True)
|
||||||
raise RuntimeError(f"Erreur technique: {e}")
|
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]:
|
def lister_contacts(self, numero: str) -> List[Dict]:
|
||||||
|
|
@ -7257,132 +7481,6 @@ class SageConnector:
|
||||||
raise RuntimeError(f"Erreur technique: {e}")
|
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:
|
def _contact_to_dict(self, contact) -> Dict:
|
||||||
"""Convertit un objet COM Contact en dictionnaire"""
|
"""Convertit un objet COM Contact en dictionnaire"""
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue