diff --git a/sage_connector.py b/sage_connector.py index d84a98e..7ae4c3b 100644 --- a/sage_connector.py +++ b/sage_connector.py @@ -3696,10 +3696,107 @@ class SageConnector: raise ValueError(f"Client {code} introuvable") client = win32com.client.CastTo(persist, "IBOClient3") - client.Read() - logger.info(f" Client chargé: {getattr(client, 'CT_Intitule', '')}") + # ============================================================ + # DEBUG TEMPORAIRE : Lister les méthodes disponibles + # ============================================================ + methodes_client = [m for m in dir(client) if not m.startswith("_")] + logger.info( + f" [DEBUG] Méthodes disponibles sur client: {methodes_client}" + ) + # Chercher spécifiquement les méthodes de verrouillage + lock_methods = [ + m + for m in methodes_client + if "lock" in m.lower() + or "read" in m.lower() + or "write" in m.lower() + ] + logger.info(f" [DEBUG] Méthodes lock/read/write: {lock_methods}") + + # ============================================================ + # VERROUILLAGE : Tenter plusieurs approches + # ============================================================ + import time + + max_retries = 3 + retry_delay = 1.0 + locked = False + lock_method_used = None + + for attempt in range(max_retries): + try: + # Approche 1: ReadLock (méthode préférée) + if hasattr(client, "ReadLock"): + client.ReadLock() + locked = True + lock_method_used = "ReadLock" + logger.info(f" Verrouillage via ReadLock() [OK]") + break + + # Approche 2: Lock + elif hasattr(client, "Lock"): + client.Lock() + locked = True + lock_method_used = "Lock" + logger.info(f" Verrouillage via Lock() [OK]") + break + + # Approche 3: LockRecord + elif hasattr(client, "LockRecord"): + client.LockRecord() + locked = True + lock_method_used = "LockRecord" + logger.info(f" Verrouillage via LockRecord() [OK]") + break + + # Approche 4: Read avec paramètre mode écriture + else: + try: + client.Read(1) # 1 = mode écriture + lock_method_used = "Read(1)" + logger.info(f" Verrouillage via Read(1) [OK]") + except TypeError: + client.Read() + lock_method_used = "Read()" + logger.info( + f" Read() simple (pas de verrouillage explicite)" + ) + break + + except Exception as lock_err: + err_str = str(lock_err) + is_lock_error = ( + "en cours d'utilisation" in err_str + or "-2512" in err_str + or "locked" in err_str.lower() + ) + + if is_lock_error: + if attempt < max_retries - 1: + logger.warning( + f" Tentative {attempt + 1}/{max_retries} - " + f"Client verrouillé, attente {retry_delay}s..." + ) + time.sleep(retry_delay) + retry_delay *= 2 # Backoff exponentiel + else: + raise RuntimeError( + f"Client {code} verrouillé après {max_retries} tentatives. " + "Vérifiez qu'il n'est pas ouvert dans Sage ou par un autre processus." + ) + else: + # Autre erreur, propager + raise + + logger.info( + f" Client chargé: {getattr(client, 'CT_Intitule', '')} (via {lock_method_used})" + ) + + # ============================================================ + # ETAPE 2: IDENTIFICATION + # ============================================================ logger.info("[ETAPE 2] IDENTIFICATION") if "intitule" in client_data: @@ -3757,6 +3854,9 @@ class SageConnector: ): champs_modifies.append("code_naf") + # ============================================================ + # ETAPE 3: ADRESSE + # ============================================================ adresse_keys = [ "contact", "adresse", @@ -3830,6 +3930,9 @@ class SageConnector: except Exception as e: logger.error(f" Adresse erreur: {e}") + # ============================================================ + # ETAPE 4: TELECOM + # ============================================================ telecom_keys = [ "telephone", "telecopie", @@ -3890,6 +3993,9 @@ class SageConnector: except Exception as e: logger.error(f" Telecom erreur: {e}") + # ============================================================ + # ETAPE 5: COMPTE GENERAL + # ============================================================ if "compte_general" in client_data: logger.info("[ETAPE 5] COMPTE GENERAL") @@ -3916,6 +4022,9 @@ class SageConnector: except Exception as e: logger.warning(f" CompteGPrinc erreur: {e}") + # ============================================================ + # ETAPE 6: CATEGORIES + # ============================================================ if ( "categorie_tarifaire" in client_data or "categorie_comptable" in client_data @@ -3960,6 +4069,9 @@ class SageConnector: except Exception as e: logger.warning(f" CatCompta erreur: {e}") + # ============================================================ + # ETAPE 7: TAUX + # ============================================================ taux_modifies = False for i in range(1, 5): key = f"taux{i:02d}" @@ -3972,6 +4084,9 @@ class SageConnector: if try_set_attribute(client, f"CT_Taux{i:02d}", val): champs_modifies.append(key) + # ============================================================ + # ETAPE 8: STATISTIQUES + # ============================================================ stat_modifies = False stat01 = client_data.get("statistique01") or client_data.get("secteur") @@ -3999,6 +4114,9 @@ class SageConnector: ): champs_modifies.append(key) + # ============================================================ + # ETAPE 9: COMMERCIAL + # ============================================================ commercial_keys = [ "encours_autorise", "assurance_credit", @@ -4050,6 +4168,9 @@ class SageConnector: except Exception as e: logger.warning(f" Collaborateur erreur: {e}") + # ============================================================ + # ETAPE 10: FACTURATION + # ============================================================ facturation_keys = [ "lettrage_auto", "est_actif", @@ -4112,6 +4233,9 @@ class SageConnector: ): champs_modifies.append(key) + # ============================================================ + # ETAPE 11: LOGISTIQUE + # ============================================================ logistique_keys = [ "priorite_livraison", "livraison_partielle", @@ -4136,6 +4260,9 @@ class SageConnector: ): champs_modifies.append(key) + # ============================================================ + # ETAPE 12: COMMENTAIRE + # ============================================================ if "commentaire" in client_data: logger.info("[ETAPE 12] COMMENTAIRE") if try_set_attribute( @@ -4145,6 +4272,9 @@ class SageConnector: ): champs_modifies.append("commentaire") + # ============================================================ + # ETAPE 13: ANALYTIQUE + # ============================================================ if "section_analytique" in client_data: logger.info("[ETAPE 13] ANALYTIQUE") if try_set_attribute( @@ -4154,6 +4284,9 @@ class SageConnector: ): champs_modifies.append("section_analytique") + # ============================================================ + # ETAPE 14: ORGANISATION & SURVEILLANCE + # ============================================================ organisation_keys = [ "mode_reglement_code", "surveillance_active", @@ -4261,8 +4394,20 @@ class SageConnector: ): champs_modifies.append("sv_resultat") + # ============================================================ + # VERIFICATION AVANT WRITE + # ============================================================ if not champs_modifies: logger.warning("Aucun champ à modifier") + # Déverrouiller si nécessaire + if locked: + try: + if hasattr(client, "ReadUnlock"): + client.ReadUnlock() + elif hasattr(client, "Unlock"): + client.Unlock() + except Exception: + pass return _extraire_client(client) logger.info("=" * 80) @@ -4271,9 +4416,11 @@ class SageConnector: logger.info(f" {i}. {champ}") logger.info("=" * 80) + # ============================================================ + # WRITE AVEC GESTION DU DEVERROUILLAGE + # ============================================================ try: client.Write() - client.Read() logger.info("[OK] Write réussi") except Exception as e: error_detail = str(e) @@ -4288,6 +4435,24 @@ class SageConnector: logger.error(f"[ERREUR] {error_detail}") raise RuntimeError(f"Echec Write(): {error_detail}") + finally: + # Toujours déverrouiller après Write (succès ou échec) + if locked: + try: + if hasattr(client, "ReadUnlock"): + client.ReadUnlock() + logger.debug(" ReadUnlock() [OK]") + elif hasattr(client, "Unlock"): + client.Unlock() + logger.debug(" Unlock() [OK]") + elif hasattr(client, "UnlockRecord"): + client.UnlockRecord() + logger.debug(" UnlockRecord() [OK]") + except Exception as unlock_err: + logger.warning(f" Déverrouillage ignoré: {unlock_err}") + + # Relire après Write pour retourner les données à jour + client.Read() logger.info("=" * 80) logger.info( diff --git a/utils/documents/settle.py b/utils/documents/settle.py index ae8c426..7525c91 100644 --- a/utils/documents/settle.py +++ b/utils/documents/settle.py @@ -135,68 +135,47 @@ def regler_facture( if not echeance: raise ValueError(f"Facture {numero_facture} sans échéance") - transaction_active = False - try: - self.cial.CptaApplication.BeginTrans() - transaction_active = True - except Exception: - pass + numero_reglement = _executer_reglement_com( + self, + doc, + echeance, + montant, + mode_reglement, + date_reglement, + reference, + libelle, + code_journal, + client_code, + numero_facture, + ) - try: - numero_reglement = _executer_reglement_com( - self, - doc, - echeance, - montant, - mode_reglement, - date_reglement, - reference, - libelle, - code_journal, - client_code, - numero_facture, + time.sleep(0.3) + doc.Read() + nouveau_montant_regle = float(getattr(doc, "DO_MontantRegle", 0.0)) + + if abs(nouveau_montant_regle - montant_deja_regle) < 0.01: + raise RuntimeError( + "Le règlement n'a pas été appliqué (DO_MontantRegle inchangé)" ) - if transaction_active: - try: - self.cial.CptaApplication.CommitTrans() - except Exception: - pass + nouveau_solde = total_ttc - nouveau_montant_regle + logger.info(f"Règlement effectué - Solde restant: {nouveau_solde:.2f}€") - time.sleep(0.5) - doc.Read() - nouveau_montant_regle = float(getattr(doc, "DO_MontantRegle", 0.0)) - - if abs(nouveau_montant_regle - montant_deja_regle) < 0.01: - raise RuntimeError( - "Le règlement n'a pas été appliqué (DO_MontantRegle inchangé)" - ) - - nouveau_solde = total_ttc - nouveau_montant_regle - logger.info(f"Règlement effectué - Solde restant: {nouveau_solde:.2f}€") - - return { - "numero_facture": numero_facture, - "numero_reglement": numero_reglement, - "montant_regle": montant, - "date_reglement": date_reglement.strftime("%Y-%m-%d"), - "mode_reglement": mode_reglement, - "mode_reglement_libelle": ModeReglement.get_libelle(mode_reglement), - "reference": reference, - "libelle": libelle, - "code_journal": code_journal, - "total_facture": total_ttc, - "solde_restant": nouveau_solde, - "facture_soldee": nouveau_solde < 0.01, - "client_code": client_code, - } - except Exception: - if transaction_active: - try: - self.cial.CptaApplication.RollbackTrans() - except Exception: - pass - raise + return { + "numero_facture": numero_facture, + "numero_reglement": numero_reglement, + "montant_regle": montant, + "date_reglement": date_reglement.strftime("%Y-%m-%d"), + "mode_reglement": mode_reglement, + "mode_reglement_libelle": ModeReglement.get_libelle(mode_reglement), + "reference": reference, + "libelle": libelle, + "code_journal": code_journal, + "total_facture": total_ttc, + "solde_restant": nouveau_solde, + "facture_soldee": nouveau_solde < 0.01, + "client_code": client_code, + } except ValueError: raise except Exception as e: @@ -241,255 +220,271 @@ def _executer_reglement_com( ): erreurs = [] - # Approche 1: CreateProcess_ReglerEcheances - Créer règlement puis l'assigner + # APPROCHE PRINCIPALE: Créer règlement complet, l'écrire, puis l'assigner au process try: - logger.info( - "Tentative via CreateProcess_ReglerEcheances avec règlement créé..." - ) - process = self.cial.CreateProcess_ReglerEcheances() + logger.info("Création du règlement via FactoryDocumentReglement...") - if process: - # D'abord créer un règlement via FactoryDocumentReglement - factory_reg = self.cial.FactoryDocumentReglement - reg = factory_reg.Create() - reg = win32com.client.CastTo(reg, "IBODocumentReglement") + # 1. Créer le règlement + factory_reg = self.cial.FactoryDocumentReglement + reg = factory_reg.Create() + reg = win32com.client.CastTo(reg, "IBODocumentReglement") + logger.info(" Règlement créé et casté vers IBODocumentReglement") - # Configurer le règlement - try: - journal_factory = self.cial.CptaApplication.FactoryJournal - journal_persist = journal_factory.ReadNumero(code_journal) - if journal_persist: - reg.Journal = journal_persist - logger.info(f" Journal: {code_journal}") - except Exception as e: - logger.warning(f" Journal: {e}") + # 2. Configurer le Journal (objet) + try: + journal_factory = self.cial.CptaApplication.FactoryJournal + journal_persist = journal_factory.ReadNumero(code_journal) + if journal_persist: + reg.Journal = journal_persist + logger.info(f" Journal défini: {code_journal}") + except Exception as e: + logger.warning(f" Journal: {e}") - try: - factory_client = self.cial.CptaApplication.FactoryClient - if client_code: - client_persist = factory_client.ReadNumero(client_code) - if client_persist: - reg.TiersPayeur = client_persist - logger.info(f" TiersPayeur: {client_code}") - except Exception as e: - logger.warning(f" TiersPayeur: {e}") + # 3. Configurer le TiersPayeur (objet client) + try: + factory_client = self.cial.CptaApplication.FactoryClient + if client_code: + client_persist = factory_client.ReadNumero(client_code) + if client_persist: + reg.TiersPayeur = client_persist + logger.info(f" TiersPayeur défini: {client_code}") + except Exception as e: + logger.warning(f" TiersPayeur: {e}") + # 4. Configurer les champs simples + try: + reg.RG_Date = pywintypes.Time(date_reglement) + logger.info(f" RG_Date: {date_reglement}") + except Exception as e: + logger.warning(f" RG_Date: {e}") + + try: + reg.RG_Montant = montant + logger.info(f" RG_Montant: {montant}") + except Exception as e: + logger.warning(f" RG_Montant: {e}") + + # 5. Mode de règlement via l'objet Reglement + try: + # Lire le mode de règlement depuis la base + mode_factory = getattr( + self.cial.CptaApplication, "FactoryModeReglement", None + ) + if mode_factory: + mode_obj = mode_factory.ReadNumero(mode_reglement) + if mode_obj: + reg.Reglement = mode_obj + logger.info(f" Mode règlement défini: {mode_reglement}") + except Exception as e: + logger.debug(f" Mode règlement via factory: {e}") + + if reference: try: - reg.RG_Date = pywintypes.Time(date_reglement) - except Exception: - pass - try: - reg.RG_Montant = montant - except Exception: - pass - if reference: - try: - reg.RG_Reference = reference - except Exception: - pass - if libelle: - try: - reg.RG_Libelle = libelle - except Exception: - pass - try: - reg.RG_Impute = 1 + reg.RG_Reference = reference except Exception: pass - # Assigner le règlement au process + if libelle: try: - process.Reglement = reg - logger.info(" Règlement assigné au process") - except Exception as e: - logger.warning(f" Assignation règlement: {e}") + reg.RG_Libelle = libelle + except Exception: + pass - # Ajouter l'échéance avec montant - try: - process.AddDocumentEcheanceMontant(echeance, montant) - logger.info(" Échéance ajoutée avec montant") - except Exception as e1: - logger.debug(f" AddDocumentEcheanceMontant: {e1}") + try: + reg.RG_Impute = 1 # Imputé + except Exception: + pass + + try: + reg.RG_Compta = 0 # Non comptabilisé + except Exception: + pass + + # 6. ÉCRIRE le règlement + reg.Write() + numero = getattr(reg, "RG_Piece", None) + logger.info(f" Règlement écrit avec numéro: {numero}") + + # 7. Créer le lien règlement-échéance via la factory DU RÈGLEMENT + try: + logger.info(" Création du lien règlement-échéance...") + factory_reg_ech = getattr(reg, "FactoryDocumentReglementEcheance", None) + + if factory_reg_ech: + reg_ech = factory_reg_ech.Create() + + # Cast vers IBODocumentReglementEcheance + for iface in [ + "IBODocumentReglementEcheance3", + "IBODocumentReglementEcheance", + ]: + try: + reg_ech = win32com.client.CastTo(reg_ech, iface) + logger.info(f" Cast vers {iface} réussi") + break + except Exception: + continue + + # Définir l'échéance - le Reglement est déjà lié via la factory try: - process.AddDocumentEcheance(echeance) - logger.info(" Échéance ajoutée") - except Exception as e2: - raise RuntimeError(f"AddEcheance: {e2}") + reg_ech.Echeance = echeance + logger.info(" Echeance définie") + except Exception as e: + logger.warning(f" Echeance: {e}") - can_process = getattr(process, "CanProcess", True) - logger.info(f" CanProcess: {can_process}") - - if can_process: - process.Process() - logger.info(" Process() réussi") - - numero = None + # Définir le montant try: - result = getattr(process, "ReglementResult", None) - if result: - result.Read() - numero = getattr(result, "RG_Piece", "") + reg_ech.RC_Montant = montant + logger.info(f" RC_Montant: {montant}") + except Exception as e: + logger.warning(f" RC_Montant: {e}") + + # Écrire le lien + try: + reg_ech.SetDefault() except Exception: pass + reg_ech.Write() + logger.info(" Lien règlement-échéance écrit!") + return str(numero) if numero else None - except Exception as e: - erreurs.append(f"CreateProcess avec règlement: {e}") - logger.warning(f"CreateProcess avec règlement échoué: {e}") + except Exception as e: + erreurs.append(f"Lien échéance: {e}") + logger.warning(f" Erreur création lien: {e}") - # Approche 2: Configurer le Reglement du process directement (toutes propriétés) - try: - logger.info("Tentative via configuration complète Process.Reglement...") - process = self.cial.CreateProcess_ReglerEcheances() + # Si le lien a échoué, essayer via le process + logger.info(" Tentative via CreateProcess_ReglerEcheances...") + try: + process = self.cial.CreateProcess_ReglerEcheances() - if process: - reglement = getattr(process, "Reglement", None) - if reglement: - # Lister TOUS les attributs - reg_attrs = [a for a in dir(reglement) if not a.startswith("_")] - logger.info(f" Attributs Reglement: {reg_attrs}") - - # Configurer TOUT - _set_safe( - reglement, ["RG_Date", "Date"], pywintypes.Time(date_reglement) - ) - _set_safe(reglement, ["RG_Montant", "Montant"], montant) - _set_safe(reglement, ["JO_Num", "Journal", "CodeJournal"], code_journal) - _set_safe( - reglement, - ["CT_NumPayeur", "CT_Num", "Tiers", "Client"], - client_code, - ) - _set_safe( - reglement, - ["N_Reglement", "ModeReglement", "RG_ModeReglement"], - mode_reglement, - ) - _set_safe(reglement, ["RG_Type", "Type"], 0) # 0 = Client - _set_safe(reglement, ["RG_Impute", "Impute"], 1) - _set_safe(reglement, ["RG_Compta", "Compta"], 0) - - if reference: - _set_safe(reglement, ["RG_Reference", "Reference"], reference) - if libelle: - _set_safe(reglement, ["RG_Libelle", "Libelle"], libelle) - - # Essayer SetDefault - try: - reglement.SetDefault() - logger.info(" SetDefault() appelé") - except Exception: - pass - - logger.info(" Reglement configuré") + # Assigner le règlement déjà écrit + process.Reglement = reg + logger.info(" Règlement assigné au process") # Ajouter l'échéance try: process.AddDocumentEcheanceMontant(echeance, montant) - logger.info(" Échéance ajoutée") - except Exception as e1: - try: - process.AddDocumentEcheance(echeance) - logger.info(" Échéance ajoutée (sans montant)") - except Exception as e2: - raise RuntimeError(f"AddEcheance: {e2}") + logger.info(" Échéance ajoutée avec montant") + except Exception: + process.AddDocumentEcheance(echeance) + logger.info(" Échéance ajoutée") can_process = getattr(process, "CanProcess", True) - logger.info(f" CanProcess: {can_process}") - - # Vérifier les erreurs du process - try: - errors = getattr(process, "Errors", None) - if errors: - err_count = getattr(errors, "Count", 0) - for i in range(1, err_count + 1): - err = errors.Item(i) - logger.warning(f" Erreur process [{i}]: {err}") - except Exception: - pass + logger.info(f" CanProcess: {can_process}") if can_process: process.Process() - logger.info(" Process() réussi") - - numero = None + logger.info(" Process() réussi!") + return str(numero) if numero else None + else: + # Vérifier les erreurs try: - result = getattr(process, "ReglementResult", None) - if result: - result.Read() - numero = getattr(result, "RG_Piece", "") + errors = process.Errors + if errors: + for i in range(1, errors.Count + 1): + logger.warning(f" Erreur [{i}]: {errors.Item(i)}") except Exception: pass - return str(numero) if numero else None - else: - logger.warning(" CanProcess = False!") + except Exception as e: + erreurs.append(f"Process: {e}") + logger.warning(f" Process échoué: {e}") except Exception as e: - erreurs.append(f"Config complète: {e}") - logger.warning(f"Config complète échouée: {e}") + erreurs.append(f"FactoryDocumentReglement: {e}") + logger.error(f"FactoryDocumentReglement échoué: {e}") - # Approche 3: Utiliser SetDefaultReglement sur le document + # APPROCHE ALTERNATIVE: Via le mode règlement de l'échéance try: - logger.info("Tentative via doc.SetDefaultReglement...") + logger.info("Tentative via modification directe de l'échéance...") - if hasattr(doc, "SetDefaultReglement"): - doc.SetDefaultReglement() - logger.info(" SetDefaultReglement() appelé") + # L'échéance a un attribut Reglement qui est le mode de règlement + mode_obj = getattr(echeance, "Reglement", None) + if mode_obj: + attrs = [a for a in dir(mode_obj) if not a.startswith("_")] + logger.info(f" Attributs Reglement échéance: {attrs[:15]}...") - # Configurer via le règlement par défaut - reg = getattr(doc, "Reglement", None) - if reg: - attrs = [a for a in dir(reg) if not a.startswith("_")] - logger.info(f" Attributs doc.Reglement: {attrs[:15]}...") + # Vérifier si l'échéance a FactoryDocumentReglementEcheance + factory_reg_ech = getattr(echeance, "FactoryDocumentReglementEcheance", None) + if factory_reg_ech: + logger.info(" FactoryDocumentReglementEcheance trouvée sur échéance") + + # Créer le lien depuis l'échéance + reg_ech = factory_reg_ech.Create() + + for iface in [ + "IBODocumentReglementEcheance3", + "IBODocumentReglementEcheance", + ]: + try: + reg_ech = win32com.client.CastTo(reg_ech, iface) + logger.info(f" Cast vers {iface}") + break + except Exception: + continue + + # Ici, l'échéance devrait déjà être liée + # Il faut définir le règlement + try: + # Créer un nouveau règlement pour ce lien + factory_reg = self.cial.FactoryDocumentReglement + new_reg = factory_reg.Create() + new_reg = win32com.client.CastTo(new_reg, "IBODocumentReglement") + + # Configurer minimalement + journal_factory = self.cial.CptaApplication.FactoryJournal + journal_persist = journal_factory.ReadNumero(code_journal) + if journal_persist: + new_reg.Journal = journal_persist + + factory_client = self.cial.CptaApplication.FactoryClient + if client_code: + client_persist = factory_client.ReadNumero(client_code) + if client_persist: + new_reg.TiersPayeur = client_persist + + new_reg.RG_Date = pywintypes.Time(date_reglement) + new_reg.RG_Montant = montant + new_reg.RG_Impute = 1 + + # Écrire le règlement + new_reg.Write() + logger.info( + f" Nouveau règlement créé: {getattr(new_reg, 'RG_Piece', None)}" + ) + + # Assigner au lien - ici on doit peut-être utiliser un autre attribut + # Puisque reg_ech.Reglement n'est pas settable, essayons via SetDefault + try: + reg_ech.SetDefault() + except Exception: + pass + + reg_ech.RC_Montant = montant + reg_ech.Write() + + logger.info(" Lien créé depuis échéance!") + return str(getattr(new_reg, "RG_Piece", None)) + + except Exception as e: + logger.warning(f" Erreur: {e}") except Exception as e: - erreurs.append(f"SetDefaultReglement: {e}") - logger.warning(f"SetDefaultReglement: {e}") + erreurs.append(f"Via échéance: {e}") + logger.warning(f"Via échéance échoué: {e}") raise RuntimeError(f"Aucune méthode n'a fonctionné. Erreurs: {'; '.join(erreurs)}") -def _set_safe(obj, attrs, value): - for attr in attrs: - try: - setattr(obj, attr, value) - logger.debug(f" {attr} = {value}") - return True - except Exception: - continue - return False - - def introspecter_reglement(self): if not self.cial: raise RuntimeError("Connexion Sage non établie") result = {} try: with self._com_context(), self._lock_com: - # Process et son Reglement - try: - process = self.cial.CreateProcess_ReglerEcheances() - result["Process"] = [a for a in dir(process) if not a.startswith("_")] - - reglement = getattr(process, "Reglement", None) - if reglement: - result["Process_Reglement"] = [ - a for a in dir(reglement) if not a.startswith("_") - ] - - # Essayer de lire les valeurs par défaut - for attr in ["RG_Type", "RG_Impute", "JO_Num", "CT_NumPayeur"]: - try: - val = getattr(reglement, attr, "N/A") - result[f"Reglement_{attr}"] = str(val) - except Exception: - pass - except Exception as e: - result["error_process"] = str(e) - - # IBODocumentReglement + # IBODocumentReglement et sa factory de liens try: factory = self.cial.FactoryDocumentReglement reg = factory.Create() @@ -497,10 +492,38 @@ def introspecter_reglement(self): result["IBODocumentReglement"] = [ a for a in dir(reg) if not a.startswith("_") ] + + # FactoryDocumentReglementEcheance depuis le règlement + factory_lien = getattr(reg, "FactoryDocumentReglementEcheance", None) + if factory_lien: + lien = factory_lien.Create() + result["ReglementEcheance_base"] = [ + a for a in dir(lien) if not a.startswith("_") + ] + + for iface in [ + "IBODocumentReglementEcheance3", + "IBODocumentReglementEcheance", + ]: + try: + lien_cast = win32com.client.CastTo(lien, iface) + result[f"ReglementEcheance_{iface}"] = [ + a for a in dir(lien_cast) if not a.startswith("_") + ] + except Exception as e: + result[f"cast_{iface}_error"] = str(e) + except Exception as e: result["error_reglement"] = str(e) - # Échéance + # Process + try: + process = self.cial.CreateProcess_ReglerEcheances() + result["Process"] = [a for a in dir(process) if not a.startswith("_")] + except Exception as e: + result["error_process"] = str(e) + + # Échéance et ses attributs try: factory_doc = self.cial.FactoryDocumentVente doc_list = factory_doc.List @@ -528,10 +551,46 @@ def introspecter_reglement(self): if not a.startswith("_") ] - # Mode règlement de l'échéance + # FactoryDocumentReglementEcheance depuis l'échéance + factory_lien_ech = getattr( + ech, + "FactoryDocumentReglementEcheance", + None, + ) + if factory_lien_ech: + lien_ech = factory_lien_ech.Create() + result[ + "EcheanceReglementEcheance_base" + ] = [ + a + for a in dir(lien_ech) + if not a.startswith("_") + ] + + for iface in [ + "IBODocumentReglementEcheance3", + "IBODocumentReglementEcheance", + ]: + try: + lien_ech_cast = ( + win32com.client.CastTo( + lien_ech, iface + ) + ) + result[ + f"EcheanceReglementEcheance_{iface}" + ] = [ + a + for a in dir(lien_ech_cast) + if not a.startswith("_") + ] + except Exception: + pass + + # Reglement de l'échéance (mode) mode = getattr(ech, "Reglement", None) if mode: - result["Echeance_Reglement"] = [ + result["Echeance_Reglement_mode"] = [ a for a in dir(mode) if not a.startswith("_")