settling invoice check
This commit is contained in:
parent
b6416487c0
commit
ff8c35fcfa
2 changed files with 485 additions and 261 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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("_")
|
||||
|
|
|
|||
Loading…
Reference in a new issue