diff --git a/sage_connector.py b/sage_connector.py index 49111b5..3df2ec3 100644 --- a/sage_connector.py +++ b/sage_connector.py @@ -6962,7 +6962,7 @@ class SageConnector: def modifier_contact(self, numero: str, contact_numero: int, updates: Dict) -> Dict: """ Modifie un contact existant via COM - VERSION CORRIGÉE pour Sage 100 + VERSION REFACTORISÉE - Utilise FactoryTiersContact """ if not self.cial: raise RuntimeError("Connexion Sage non établie") @@ -6973,135 +6973,192 @@ class SageConnector: logger.info(f"[MODIFICATION CONTACT] CT_No={contact_numero}") logger.info("=" * 80) - # Lire le contact existant - CORRECTION ICI - logger.info("[1] Lecture contact existant") + # Charger le client + logger.info("[1] 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 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}") - # Trouver la factory - factory_contact = None - factory_names = [ - "FactoryContact", - "FactoryContactTiers", - "FactoryTypeContacts", - ] + # Charger le contact via FactoryTiersContact + logger.info("[2] Chargement du contact") - 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 hasattr(client_obj, 'FactoryTiersContact'): + raise RuntimeError("FactoryTiersContact non trouvee sur le client") - if not factory_contact: - raise RuntimeError("Aucune factory de contacts trouvée") + factory_contact = client_obj.FactoryTiersContact try: - # ReadNumero peut accepter 1 ou 2 paramètres selon la version + # Chercher le contact par son CT_No + # Il faut lister et trouver celui avec le bon CT_No + # ou utiliser une autre méthode de lecture + + # STRATÉGIE : Lister tous les contacts et trouver le bon + contacts_collection = None + all_contacts = [] + + # Essayer de lister via SQL d'abord (plus fiable) 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) + with self._get_sql_connection() as conn: + cursor = conn.cursor() + cursor.execute( + "SELECT CT_Nom, CT_Prenom FROM F_CONTACTT WHERE CT_Num = ? AND CT_No = ?", + [numero, contact_numero] + ) + row = cursor.fetchone() + + if not row: + raise ValueError(f"Contact CT_No={contact_numero} non trouve") + + nom_recherche = row.CT_Nom.strip() if row.CT_Nom else "" + prenom_recherche = row.CT_Prenom.strip() if row.CT_Prenom else "" + + logger.info(f" Contact trouve en SQL: {prenom_recherche} {nom_recherche}") + + except Exception as e: + raise ValueError(f"Contact introuvable: {e}") + + # Charger via ReadNomPrenom + factory_dossier = self.cial.CptaApplication.FactoryDossierContact + persist = factory_dossier.ReadNomPrenom(nom_recherche, prenom_recherche) if not persist: - raise ValueError(f"Contact CT_No={contact_numero} non trouvé pour client {numero}") - - # 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 + raise ValueError(f"Contact non trouvable via ReadNomPrenom") + contact = win32com.client.CastTo(persist, "IBOTiersContact3") contact.Read() - logger.info(f" ✓ Contact chargé: {getattr(contact, 'CT_Nom', '?')}") + logger.info(f" OK Contact charge: {contact.Nom}") except Exception as e: raise ValueError(f"Contact introuvable: {e}") # Appliquer les modifications - logger.info("[2] Application des modifications") + logger.info("[3] Application des modifications") modifications_appliquees = [] # Identité if "civilite" in updates: - civilite_map = {"M.": 0, "Mme": 1, "Mlle": 2, "Société": 3} + civilite_map = {"M.": 0, "Mme": 1, "Mlle": 2, "Societe": 3} civilite_code = civilite_map.get(updates["civilite"]) if civilite_code is not None: - self._try_set_attribute(contact, "CT_Civilite", civilite_code) - logger.info(f" CT_Civilite = {civilite_code}") - modifications_appliquees.append("civilite") + try: + contact.Civilite = civilite_code + logger.info(f" Civilite = {civilite_code}") + modifications_appliquees.append("civilite") + except: + pass if "nom" in updates: nom = self._clean_str(updates["nom"], 35) if nom: - self._try_set_attribute(contact, "CT_Nom", nom) - logger.info(f" CT_Nom = {nom}") - modifications_appliquees.append("nom") + try: + contact.Nom = nom + logger.info(f" Nom = {nom}") + modifications_appliquees.append("nom") + except: + pass if "prenom" in updates: prenom = self._clean_str(updates["prenom"], 35) - self._try_set_attribute(contact, "CT_Prenom", prenom) - logger.info(f" CT_Prenom = {prenom}") - modifications_appliquees.append("prenom") + try: + contact.Prenom = prenom + logger.info(f" Prenom = {prenom}") + modifications_appliquees.append("prenom") + except: + pass if "fonction" in updates: fonction = self._clean_str(updates["fonction"], 35) - self._try_set_attribute(contact, "CT_Fonction", fonction) - logger.info(f" CT_Fonction = {fonction}") - modifications_appliquees.append("fonction") + try: + contact.Fonction = fonction + logger.info(f" Fonction = {fonction}") + modifications_appliquees.append("fonction") + except: + pass # Service if "service_code" in updates: service = self._safe_int(updates["service_code"]) - if service is not None: - self._try_set_attribute(contact, "N_Service", service) - logger.info(f" N_Service = {service}") - modifications_appliquees.append("service_code") + if service is not None and hasattr(contact, 'ServiceContact'): + try: + contact.ServiceContact = service + logger.info(f" ServiceContact = {service}") + modifications_appliquees.append("service_code") + except: + pass - # Coordonnées - coord_fields = { - "telephone": ("CT_Telephone", 21), - "portable": ("CT_TelPortable", 21), - "telecopie": ("CT_Telecopie", 21), - "email": ("CT_EMail", 69), - } - - for key, (attr, max_len) in coord_fields.items(): - if key in updates: - value = self._clean_str(updates[key], max_len) - self._try_set_attribute(contact, attr, value) - logger.info(f" {attr} = {value}") - modifications_appliquees.append(key) - - # Réseaux sociaux - social_fields = { - "facebook": ("CT_Facebook", 69), - "linkedin": ("CT_LinkedIn", 69), - "skype": ("CT_Skype", 69), - } - - for key, (attr, max_len) in social_fields.items(): - if key in updates: - value = self._clean_str(updates[key], max_len) - self._try_set_attribute(contact, attr, value) - logger.info(f" {attr} = {value}") - modifications_appliquees.append(key) - - # Enregistrement du contact - logger.info("[3] WRITE CONTACT") - try: - contact.Write() + # Coordonnées via Telecom + if hasattr(contact, 'Telecom'): try: - contact.Read() + telecom = contact.Telecom + + if "telephone" in updates: + telephone = self._clean_str(updates["telephone"], 21) + if self._try_set_attribute(telecom, "Telephone", telephone): + logger.info(f" Telephone = {telephone}") + modifications_appliquees.append("telephone") + + if "portable" in updates: + portable = self._clean_str(updates["portable"], 21) + if self._try_set_attribute(telecom, "Portable", portable): + logger.info(f" Portable = {portable}") + modifications_appliquees.append("portable") + + if "email" in updates: + email = self._clean_str(updates["email"], 69) + if self._try_set_attribute(telecom, "EMail", email): + logger.info(f" EMail = {email}") + modifications_appliquees.append("email") + + if "telecopie" in updates: + fax = self._clean_str(updates["telecopie"], 21) + if self._try_set_attribute(telecom, "Telecopie", fax): + logger.info(f" Telecopie = {fax}") + modifications_appliquees.append("telecopie") except: pass - logger.info(" ✓ Write() réussi") + + # Réseaux sociaux + if "facebook" in updates: + facebook = self._clean_str(updates["facebook"], 69) + try: + contact.Facebook = facebook + logger.info(f" Facebook = {facebook}") + modifications_appliquees.append("facebook") + except: + pass + + if "linkedin" in updates: + linkedin = self._clean_str(updates["linkedin"], 69) + try: + contact.LinkedIn = linkedin + logger.info(f" LinkedIn = {linkedin}") + modifications_appliquees.append("linkedin") + except: + pass + + if "skype" in updates: + skype = self._clean_str(updates["skype"], 69) + try: + contact.Skype = skype + logger.info(f" Skype = {skype}") + modifications_appliquees.append("skype") + except: + pass + + # Enregistrement du contact + logger.info("[4] Enregistrement") + try: + contact.Write() + contact.Read() + logger.info(" OK Write() reussi") except Exception as e: error_detail = str(e) try: @@ -7110,67 +7167,44 @@ 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 modification contact: {error_detail}") + logger.error(f" ERROR Write: {error_detail}") + raise RuntimeError(f"Echec modification contact: {error_detail}") - logger.info(f" Modifications appliquées: {', '.join(modifications_appliquees)}") + logger.info(f" Modifications appliquees: {', '.join(modifications_appliquees)}") # Gestion du contact par défaut est_defaut_demande = updates.get("est_defaut") est_actuellement_defaut = False if est_defaut_demande is not None and est_defaut_demande: - logger.info("[4] Gestion contact par défaut") + logger.info("[5] Gestion contact par defaut") 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 + nom_complet = f"{contact.Prenom} {contact.Nom}".strip() if contact.Prenom else contact.Nom - # 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}'") + 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}") + if hasattr(client_obj, 'CT_NoContact'): + try: + client_obj.CT_NoContact = contact_numero + logger.info(f" CT_NoContact = {contact_numero}") + except: + pass - # Enregistrer le client client_obj.Write() client_obj.Read() - logger.info(" ✓ Contact défini comme par défaut") + logger.info(" OK Contact par defaut defini") est_actuellement_defaut = True 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: - factory_client = self.cial.CptaApplication.FactoryClient - persist_client = factory_client.ReadNumero(numero) - client_obj = win32com.client.CastTo(persist_client, "IBOClient3") - client_obj.Read() - - ct_contact = getattr(client_obj, "CT_Contact", "") - 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 - - est_actuellement_defaut = (ct_contact == nom_complet) - except: - pass + logger.warning(f" WARN Echec: {e}") logger.info("=" * 80) - logger.info(f"[SUCCÈS] Contact modifié: CT_No={contact_numero}") - if est_actuellement_defaut: - logger.info(f" Contact par défaut") + logger.info(f"[SUCCES] Contact modifie: CT_No={contact_numero}") logger.info("=" * 80) contact_dict = self._contact_to_dict(contact) @@ -7184,7 +7218,7 @@ class SageConnector: except Exception as e: logger.error(f"[ERREUR] {e}", exc_info=True) raise RuntimeError(f"Erreur technique: {e}") - + def definir_contact_defaut(self, numero: str, contact_numero: int) -> Dict: """