Push simple
This commit is contained in:
parent
be7bc287c0
commit
7ca64e2ea6
2 changed files with 972 additions and 97 deletions
683
main.py
683
main.py
|
|
@ -2535,19 +2535,69 @@ def prospect_get(req: CodeRequest):
|
|||
# ENDPOINTS - FOURNISSEURS
|
||||
# =====================================================
|
||||
@app.post("/sage/fournisseurs/list", dependencies=[Depends(verify_token)])
|
||||
def fournisseurs_list(req: FiltreRequest):
|
||||
"""📋 Liste tous les fournisseurs (CT_Type=1)"""
|
||||
def fournisseurs_list_direct(req: FiltreRequest):
|
||||
"""
|
||||
⚡ ENDPOINT DIRECT : Liste fournisseurs SANS passer par le cache
|
||||
|
||||
Lecture directe depuis FactoryFournisseur - toujours à jour
|
||||
"""
|
||||
try:
|
||||
fournisseurs = sage.lister_tous_fournisseurs(req.filtre)
|
||||
return {"success": True, "data": fournisseurs}
|
||||
if not sage or not sage.cial:
|
||||
raise HTTPException(503, "Service Sage indisponible")
|
||||
|
||||
with sage._com_context(), sage._lock_com:
|
||||
factory = sage.cial.CptaApplication.FactoryFournisseur
|
||||
fournisseurs = []
|
||||
index = 1
|
||||
max_iterations = 10000
|
||||
erreurs_consecutives = 0
|
||||
max_erreurs = 50
|
||||
|
||||
filtre_lower = req.filtre.lower() if req.filtre else ""
|
||||
|
||||
logger.info(f"🔍 Lecture directe fournisseurs (filtre='{req.filtre}')")
|
||||
|
||||
while index < max_iterations and erreurs_consecutives < max_erreurs:
|
||||
try:
|
||||
persist = factory.List(index)
|
||||
if persist is None:
|
||||
break
|
||||
|
||||
obj = sage._cast_client(persist)
|
||||
if obj:
|
||||
data = sage._extraire_client(obj)
|
||||
|
||||
# Appliquer le filtre si nécessaire
|
||||
if not filtre_lower or \
|
||||
filtre_lower in data["numero"].lower() or \
|
||||
filtre_lower in data["intitule"].lower():
|
||||
fournisseurs.append(data)
|
||||
|
||||
erreurs_consecutives = 0
|
||||
|
||||
index += 1
|
||||
|
||||
except Exception as e:
|
||||
erreurs_consecutives += 1
|
||||
index += 1
|
||||
|
||||
if erreurs_consecutives >= max_erreurs:
|
||||
logger.warning(f"⚠️ Arrêt après {max_erreurs} erreurs consécutives")
|
||||
break
|
||||
|
||||
logger.info(f"✅ {len(fournisseurs)} fournisseurs retournés (lecture directe)")
|
||||
return {"success": True, "data": fournisseurs}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur liste fournisseurs: {e}")
|
||||
logger.error(f"❌ Erreur lecture directe fournisseurs: {e}", exc_info=True)
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@app.post("/sage/fournisseurs/get", dependencies=[Depends(verify_token)])
|
||||
def fournisseur_get(req: CodeRequest):
|
||||
"""📄 Lecture d'un fournisseur par code"""
|
||||
"""
|
||||
✅ NOUVEAU : Lecture d'un fournisseur par code
|
||||
"""
|
||||
try:
|
||||
fournisseur = sage.lire_fournisseur(req.code)
|
||||
if not fournisseur:
|
||||
|
|
@ -2981,6 +3031,627 @@ def diagnostiquer_longueurs_champs():
|
|||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
# À ajouter dans main.py (Windows Gateway)
|
||||
|
||||
@app.get("/sage/diagnostic/fournisseurs-analyse-complete", dependencies=[Depends(verify_token)])
|
||||
def analyser_fournisseurs_complet():
|
||||
"""
|
||||
🔍 DIAGNOSTIC ULTRA-COMPLET : Découverte fournisseurs
|
||||
|
||||
Teste TOUTES les méthodes possibles pour identifier les fournisseurs :
|
||||
1. CT_Type = 1 (méthode classique)
|
||||
2. CT_Qualite = 2 ou 3 (méthode moderne)
|
||||
3. FactoryFournisseur (méthode directe)
|
||||
4. CT_TypeTiers
|
||||
5. Analyse des champs disponibles
|
||||
"""
|
||||
try:
|
||||
if not sage or not sage.cial:
|
||||
raise HTTPException(503, "Service Sage indisponible")
|
||||
|
||||
with sage._com_context(), sage._lock_com:
|
||||
|
||||
diagnostic = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"methodes_testees": [],
|
||||
"fournisseurs_detectes": {},
|
||||
"tiers_analyses": [],
|
||||
"champs_disponibles": {},
|
||||
"recommendation": None
|
||||
}
|
||||
|
||||
# =============================================
|
||||
# MÉTHODE 1 : FactoryFournisseur (prioritaire)
|
||||
# =============================================
|
||||
logger.info("[DIAG] Test FactoryFournisseur...")
|
||||
|
||||
try:
|
||||
factory_fourn = sage.cial.CptaApplication.FactoryFournisseur
|
||||
fournisseurs_factory = []
|
||||
|
||||
index = 1
|
||||
while index <= 100: # Scanner 100 premiers
|
||||
try:
|
||||
persist = factory_fourn.List(index)
|
||||
if persist is None:
|
||||
break
|
||||
|
||||
fourn = sage._cast_client(persist)
|
||||
if fourn:
|
||||
fournisseurs_factory.append({
|
||||
"numero": getattr(fourn, "CT_Num", ""),
|
||||
"intitule": getattr(fourn, "CT_Intitule", ""),
|
||||
"ct_type": getattr(fourn, "CT_Type", None),
|
||||
"ct_qualite": getattr(fourn, "CT_Qualite", None),
|
||||
"methode": "FactoryFournisseur"
|
||||
})
|
||||
|
||||
index += 1
|
||||
except Exception as e:
|
||||
logger.debug(f"Erreur index {index}: {e}")
|
||||
break
|
||||
|
||||
diagnostic["methodes_testees"].append({
|
||||
"methode": "FactoryFournisseur",
|
||||
"succes": True,
|
||||
"nb_fournisseurs": len(fournisseurs_factory),
|
||||
"disponible": True
|
||||
})
|
||||
|
||||
if fournisseurs_factory:
|
||||
diagnostic["fournisseurs_detectes"]["factory"] = fournisseurs_factory[:10]
|
||||
logger.info(f"✅ {len(fournisseurs_factory)} fournisseurs via FactoryFournisseur")
|
||||
|
||||
except Exception as e:
|
||||
diagnostic["methodes_testees"].append({
|
||||
"methode": "FactoryFournisseur",
|
||||
"succes": False,
|
||||
"erreur": str(e),
|
||||
"disponible": False
|
||||
})
|
||||
logger.info(f"❌ FactoryFournisseur indisponible: {e}")
|
||||
|
||||
# =============================================
|
||||
# MÉTHODE 2 : Analyse FactoryClient avec tous les champs
|
||||
# =============================================
|
||||
logger.info("[DIAG] Analyse complète FactoryClient...")
|
||||
|
||||
factory_client = sage.cial.CptaApplication.FactoryClient
|
||||
|
||||
fournisseurs_ct_type = []
|
||||
fournisseurs_ct_qualite = []
|
||||
tous_tiers = []
|
||||
champs_vus = set()
|
||||
|
||||
index = 1
|
||||
while index <= 100: # Scanner 100 premiers tiers
|
||||
try:
|
||||
persist = factory_client.List(index)
|
||||
if persist is None:
|
||||
break
|
||||
|
||||
tiers = sage._cast_client(persist)
|
||||
if not tiers:
|
||||
index += 1
|
||||
continue
|
||||
|
||||
# Extraction COMPLÈTE de tous les champs
|
||||
tiers_info = {
|
||||
"index": index,
|
||||
"numero": getattr(tiers, "CT_Num", ""),
|
||||
"intitule": getattr(tiers, "CT_Intitule", ""),
|
||||
"champs": {}
|
||||
}
|
||||
|
||||
# Scanner TOUS les attributs CT_*
|
||||
for attr in dir(tiers):
|
||||
if attr.startswith("CT_") and not callable(getattr(tiers, attr, None)):
|
||||
try:
|
||||
valeur = getattr(tiers, attr, None)
|
||||
if valeur is not None:
|
||||
tiers_info["champs"][attr] = {
|
||||
"valeur": str(valeur),
|
||||
"type": type(valeur).__name__
|
||||
}
|
||||
champs_vus.add(attr)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Analyses spécifiques
|
||||
ct_type = getattr(tiers, "CT_Type", None)
|
||||
ct_qualite = getattr(tiers, "CT_Qualite", None)
|
||||
ct_prospect = getattr(tiers, "CT_Prospect", None)
|
||||
|
||||
tiers_info["ct_type"] = ct_type
|
||||
tiers_info["ct_qualite"] = ct_qualite
|
||||
tiers_info["ct_prospect"] = ct_prospect
|
||||
|
||||
# Identifier si c'est un fournisseur selon différentes méthodes
|
||||
tiers_info["analyses"] = {}
|
||||
|
||||
# Test CT_Type = 1
|
||||
if ct_type == 1:
|
||||
tiers_info["analyses"]["ct_type_1"] = True
|
||||
fournisseurs_ct_type.append(tiers_info)
|
||||
|
||||
# Test CT_Qualite
|
||||
if ct_qualite is not None:
|
||||
tiers_info["ct_qualite_libelle"] = {
|
||||
0: "Aucune",
|
||||
1: "Client uniquement",
|
||||
2: "Fournisseur uniquement",
|
||||
3: "Client ET Fournisseur"
|
||||
}.get(ct_qualite, f"Inconnu ({ct_qualite})")
|
||||
|
||||
if ct_qualite in [2, 3]:
|
||||
tiers_info["analyses"]["ct_qualite_2_3"] = True
|
||||
fournisseurs_ct_qualite.append(tiers_info)
|
||||
|
||||
# Test via FactoryFournisseur.ReadNumero
|
||||
try:
|
||||
factory_fourn = sage.cial.CptaApplication.FactoryFournisseur
|
||||
persist_test = factory_fourn.ReadNumero(tiers_info["numero"])
|
||||
if persist_test:
|
||||
tiers_info["analyses"]["factory_fournisseur_accessible"] = True
|
||||
except:
|
||||
tiers_info["analyses"]["factory_fournisseur_accessible"] = False
|
||||
|
||||
tous_tiers.append(tiers_info)
|
||||
index += 1
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"Erreur tiers {index}: {e}")
|
||||
index += 1
|
||||
|
||||
diagnostic["tiers_analyses"] = tous_tiers[:20] # Garder 20 exemples
|
||||
diagnostic["champs_disponibles"] = sorted(list(champs_vus))
|
||||
|
||||
# Statistiques
|
||||
diagnostic["statistiques"] = {
|
||||
"total_tiers_scannes": len(tous_tiers),
|
||||
"fournisseurs_ct_type_1": len(fournisseurs_ct_type),
|
||||
"fournisseurs_ct_qualite_2_3": len(fournisseurs_ct_qualite),
|
||||
"champs_ct_detectes": len(champs_vus)
|
||||
}
|
||||
|
||||
# Exemples de fournisseurs détectés
|
||||
if fournisseurs_ct_type:
|
||||
diagnostic["fournisseurs_detectes"]["ct_type"] = [
|
||||
{
|
||||
"numero": t["numero"],
|
||||
"intitule": t["intitule"],
|
||||
"ct_type": t["ct_type"]
|
||||
}
|
||||
for t in fournisseurs_ct_type[:10]
|
||||
]
|
||||
|
||||
if fournisseurs_ct_qualite:
|
||||
diagnostic["fournisseurs_detectes"]["ct_qualite"] = [
|
||||
{
|
||||
"numero": t["numero"],
|
||||
"intitule": t["intitule"],
|
||||
"ct_qualite": t["ct_qualite"],
|
||||
"libelle": t["ct_qualite_libelle"]
|
||||
}
|
||||
for t in fournisseurs_ct_qualite[:10]
|
||||
]
|
||||
|
||||
# =============================================
|
||||
# MÉTHODE 3 : Analyse des champs disponibles
|
||||
# =============================================
|
||||
logger.info("[DIAG] Analyse des champs disponibles...")
|
||||
|
||||
# Prendre un tiers exemple et lister TOUS ses attributs
|
||||
if tous_tiers:
|
||||
tiers_exemple = tous_tiers[0]
|
||||
|
||||
try:
|
||||
persist_ex = factory_client.ReadNumero(tiers_exemple["numero"])
|
||||
if persist_ex:
|
||||
obj_ex = sage._cast_client(persist_ex)
|
||||
|
||||
tous_attributs = {}
|
||||
for attr in dir(obj_ex):
|
||||
if not attr.startswith("_") and not callable(getattr(obj_ex, attr, None)):
|
||||
try:
|
||||
val = getattr(obj_ex, attr, None)
|
||||
if val is not None:
|
||||
tous_attributs[attr] = {
|
||||
"type": type(val).__name__,
|
||||
"valeur_exemple": str(val)[:50]
|
||||
}
|
||||
except:
|
||||
pass
|
||||
|
||||
diagnostic["attributs_complets_exemple"] = tous_attributs
|
||||
|
||||
except Exception as e:
|
||||
diagnostic["erreur_analyse_attributs"] = str(e)
|
||||
|
||||
# =============================================
|
||||
# RECOMMANDATION FINALE
|
||||
# =============================================
|
||||
logger.info("[DIAG] Génération recommandation...")
|
||||
|
||||
if "factory" in diagnostic["fournisseurs_detectes"]:
|
||||
diagnostic["recommendation"] = {
|
||||
"methode": "FactoryFournisseur",
|
||||
"code_exemple": """
|
||||
# Utiliser FactoryFournisseur directement
|
||||
factory_fourn = sage.cial.CptaApplication.FactoryFournisseur
|
||||
persist = factory_fourn.List(index) # ou .ReadNumero(code)
|
||||
""",
|
||||
"implementation": "Modifier lister_tous_fournisseurs() pour utiliser FactoryFournisseur au lieu de FactoryClient",
|
||||
"priorite": "HAUTE - Méthode la plus fiable"
|
||||
}
|
||||
|
||||
elif fournisseurs_ct_qualite:
|
||||
diagnostic["recommendation"] = {
|
||||
"methode": "CT_Qualite",
|
||||
"condition": "CT_Qualite IN (2, 3)",
|
||||
"code_exemple": """
|
||||
# Filtrer sur CT_Qualite
|
||||
qualite = getattr(tiers, "CT_Qualite", None)
|
||||
if qualite in [2, 3]: # 2=Fournisseur, 3=Client+Fournisseur
|
||||
# C'est un fournisseur
|
||||
""",
|
||||
"implementation": "Modifier _extraire_client() et le cache pour utiliser CT_Qualite",
|
||||
"priorite": "MOYENNE - Méthode moderne"
|
||||
}
|
||||
|
||||
elif fournisseurs_ct_type:
|
||||
diagnostic["recommendation"] = {
|
||||
"methode": "CT_Type",
|
||||
"condition": "CT_Type = 1",
|
||||
"code_exemple": """
|
||||
# Filtrer sur CT_Type (ancienne méthode)
|
||||
type_tiers = getattr(tiers, "CT_Type", 0)
|
||||
if type_tiers == 1: # 1=Fournisseur
|
||||
# C'est un fournisseur
|
||||
""",
|
||||
"implementation": "La méthode actuelle devrait fonctionner",
|
||||
"priorite": "BASSE - Méthode classique"
|
||||
}
|
||||
|
||||
else:
|
||||
diagnostic["recommendation"] = {
|
||||
"methode": "AUCUNE",
|
||||
"message": "Aucune méthode n'a permis d'identifier des fournisseurs",
|
||||
"actions": [
|
||||
"Vérifier si des fournisseurs existent dans Sage",
|
||||
"Augmenter le nombre de tiers scannés (actuellement 100)",
|
||||
"Vérifier les permissions de l'utilisateur Sage",
|
||||
"Consulter la documentation Sage pour votre version"
|
||||
]
|
||||
}
|
||||
|
||||
# =============================================
|
||||
# CODE DE CORRECTION SUGGÉRÉ
|
||||
# =============================================
|
||||
if "factory" in diagnostic["fournisseurs_detectes"]:
|
||||
diagnostic["code_correction"] = {
|
||||
"fichier": "sage_connector.py",
|
||||
"fonction": "lister_tous_fournisseurs",
|
||||
"code": """
|
||||
def lister_tous_fournisseurs(self, filtre=""):
|
||||
'''Liste tous les fournisseurs via FactoryFournisseur'''
|
||||
if not self.cial:
|
||||
return []
|
||||
|
||||
fournisseurs = []
|
||||
|
||||
try:
|
||||
with self._com_context(), self._lock_com:
|
||||
factory = self.cial.CptaApplication.FactoryFournisseur # ✅ CORRECTION
|
||||
index = 1
|
||||
max_iterations = 10000
|
||||
|
||||
while index < max_iterations:
|
||||
try:
|
||||
persist = factory.List(index)
|
||||
if persist is None:
|
||||
break
|
||||
|
||||
obj = self._cast_client(persist)
|
||||
if obj:
|
||||
data = self._extraire_client(obj)
|
||||
|
||||
# Filtrer si nécessaire
|
||||
if not filtre or \\
|
||||
filtre.lower() in data["numero"].lower() or \\
|
||||
filtre.lower() in data["intitule"].lower():
|
||||
fournisseurs.append(data)
|
||||
|
||||
index += 1
|
||||
|
||||
except:
|
||||
index += 1
|
||||
continue
|
||||
|
||||
logger.info(f"✅ {len(fournisseurs)} fournisseurs retournés")
|
||||
return fournisseurs
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur liste fournisseurs: {e}")
|
||||
return []
|
||||
"""
|
||||
}
|
||||
|
||||
elif fournisseurs_ct_qualite:
|
||||
diagnostic["code_correction"] = {
|
||||
"fichier": "sage_connector.py",
|
||||
"fonction": "_extraire_client + _refresh_cache_clients",
|
||||
"code": """
|
||||
# Modifier _extraire_client pour inclure CT_Qualite
|
||||
def _extraire_client(self, client_obj):
|
||||
data = {
|
||||
"numero": getattr(client_obj, "CT_Num", ""),
|
||||
"intitule": getattr(client_obj, "CT_Intitule", ""),
|
||||
"type": getattr(client_obj, "CT_Type", 0),
|
||||
"qualite": getattr(client_obj, "CT_Qualite", 0), # ✅ AJOUT
|
||||
"est_prospect": getattr(client_obj, "CT_Prospect", 0) == 1,
|
||||
"est_fournisseur": getattr(client_obj, "CT_Qualite", 0) in [2, 3] # ✅ AJOUT
|
||||
}
|
||||
# ... reste du code
|
||||
return data
|
||||
|
||||
# Modifier lister_tous_fournisseurs
|
||||
def lister_tous_fournisseurs(self, filtre=""):
|
||||
with self._lock_clients:
|
||||
if not filtre:
|
||||
return [c for c in self._cache_clients if c.get("est_fournisseur")] # ✅ MODIFICATION
|
||||
|
||||
filtre_lower = filtre.lower()
|
||||
return [
|
||||
c for c in self._cache_clients
|
||||
if c.get("est_fournisseur") and # ✅ MODIFICATION
|
||||
(filtre_lower in c["numero"].lower() or
|
||||
filtre_lower in c["intitule"].lower())
|
||||
]
|
||||
"""
|
||||
}
|
||||
|
||||
logger.info("[DIAG] ✅ Analyse complète terminée")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"diagnostic": diagnostic
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[DIAG] ❌ Erreur: {e}", exc_info=True)
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@app.get("/sage/diagnostic/fournisseurs-rapide", dependencies=[Depends(verify_token)])
|
||||
def test_fournisseurs_rapide():
|
||||
"""
|
||||
⚡ DIAGNOSTIC RAPIDE : Test des 3 méthodes principales
|
||||
"""
|
||||
try:
|
||||
if not sage or not sage.cial:
|
||||
raise HTTPException(503, "Service Sage indisponible")
|
||||
|
||||
with sage._com_context(), sage._lock_com:
|
||||
resultats = {}
|
||||
|
||||
# Test 1 : FactoryFournisseur
|
||||
try:
|
||||
factory = sage.cial.CptaApplication.FactoryFournisseur
|
||||
count = 0
|
||||
index = 1
|
||||
|
||||
while index <= 10:
|
||||
persist = factory.List(index)
|
||||
if persist is None:
|
||||
break
|
||||
count += 1
|
||||
index += 1
|
||||
|
||||
resultats["FactoryFournisseur"] = {
|
||||
"disponible": True,
|
||||
"nb_trouves": count,
|
||||
"statut": "✅ FONCTIONNEL" if count > 0 else "⚠️ Aucun fournisseur"
|
||||
}
|
||||
except Exception as e:
|
||||
resultats["FactoryFournisseur"] = {
|
||||
"disponible": False,
|
||||
"erreur": str(e),
|
||||
"statut": "❌ INDISPONIBLE"
|
||||
}
|
||||
|
||||
# Test 2 : CT_Qualite
|
||||
factory_client = sage.cial.CptaApplication.FactoryClient
|
||||
count_qualite = 0
|
||||
index = 1
|
||||
|
||||
while index <= 50:
|
||||
try:
|
||||
persist = factory_client.List(index)
|
||||
if persist is None:
|
||||
break
|
||||
|
||||
tiers = sage._cast_client(persist)
|
||||
if tiers:
|
||||
qualite = getattr(tiers, "CT_Qualite", None)
|
||||
if qualite in [2, 3]:
|
||||
count_qualite += 1
|
||||
|
||||
index += 1
|
||||
except:
|
||||
break
|
||||
|
||||
resultats["CT_Qualite"] = {
|
||||
"nb_trouves": count_qualite,
|
||||
"statut": "✅ FONCTIONNEL" if count_qualite > 0 else "⚠️ Aucun fournisseur"
|
||||
}
|
||||
|
||||
# Test 3 : CT_Type
|
||||
count_type = 0
|
||||
index = 1
|
||||
|
||||
while index <= 50:
|
||||
try:
|
||||
persist = factory_client.List(index)
|
||||
if persist is None:
|
||||
break
|
||||
|
||||
tiers = sage._cast_client(persist)
|
||||
if tiers:
|
||||
type_tiers = getattr(tiers, "CT_Type", 0)
|
||||
if type_tiers == 1:
|
||||
count_type += 1
|
||||
|
||||
index += 1
|
||||
except:
|
||||
break
|
||||
|
||||
resultats["CT_Type"] = {
|
||||
"nb_trouves": count_type,
|
||||
"statut": "✅ FONCTIONNEL" if count_type > 0 else "⚠️ Aucun fournisseur"
|
||||
}
|
||||
|
||||
# Recommandation
|
||||
if resultats["FactoryFournisseur"].get("nb_trouves", 0) > 0:
|
||||
recommandation = "Utiliser FactoryFournisseur (méthode la plus fiable)"
|
||||
elif count_qualite > 0:
|
||||
recommandation = "Utiliser CT_Qualite (méthode moderne)"
|
||||
elif count_type > 0:
|
||||
recommandation = "Utiliser CT_Type (méthode classique)"
|
||||
else:
|
||||
recommandation = "⚠️ AUCUNE MÉTHODE - Vérifier si des fournisseurs existent dans Sage"
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"resultats": resultats,
|
||||
"recommandation": recommandation,
|
||||
"actions_suivantes": [
|
||||
"Appeler /sage/diagnostic/fournisseurs-analyse-complete pour plus de détails",
|
||||
"Vérifier dans Sage si des fournisseurs existent",
|
||||
"Appliquer la correction suggérée selon la méthode fonctionnelle"
|
||||
]
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[DIAG] Erreur: {e}", exc_info=True)
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@app.get("/sage/debug/fournisseurs-direct", dependencies=[Depends(verify_token)])
|
||||
def debug_fournisseurs_direct():
|
||||
"""
|
||||
🔍 Test direct : Lecture des fournisseurs SANS passer par le cache
|
||||
"""
|
||||
try:
|
||||
if not sage or not sage.cial:
|
||||
raise HTTPException(503, "Service Sage indisponible")
|
||||
|
||||
with sage._com_context(), sage._lock_com:
|
||||
factory = sage.cial.CptaApplication.FactoryFournisseur
|
||||
|
||||
fournisseurs_direct = []
|
||||
index = 1
|
||||
|
||||
# Lire les 10 premiers directement
|
||||
while index <= 10:
|
||||
try:
|
||||
persist = factory.List(index)
|
||||
if persist is None:
|
||||
break
|
||||
|
||||
obj = sage._cast_client(persist)
|
||||
if obj:
|
||||
fournisseurs_direct.append({
|
||||
"numero": getattr(obj, "CT_Num", ""),
|
||||
"intitule": getattr(obj, "CT_Intitule", ""),
|
||||
"type": getattr(obj, "CT_Type", -1)
|
||||
})
|
||||
|
||||
index += 1
|
||||
except Exception as e:
|
||||
logger.debug(f"Erreur index {index}: {e}")
|
||||
break
|
||||
|
||||
# Vérifier l'état du cache
|
||||
cache_existe = hasattr(sage, '_cache_fournisseurs')
|
||||
cache_count = len(sage._cache_fournisseurs) if cache_existe else 0
|
||||
cache_last_update = (
|
||||
sage._cache_fournisseurs_last_update.isoformat()
|
||||
if cache_existe and sage._cache_fournisseurs_last_update
|
||||
else None
|
||||
)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"lecture_directe": {
|
||||
"nb_fournisseurs": len(fournisseurs_direct),
|
||||
"exemples": fournisseurs_direct
|
||||
},
|
||||
"etat_cache": {
|
||||
"cache_existe": cache_existe,
|
||||
"cache_count": cache_count,
|
||||
"last_update": cache_last_update,
|
||||
"attributs_sage_connector": [
|
||||
attr for attr in dir(sage)
|
||||
if 'fournisseur' in attr.lower()
|
||||
]
|
||||
},
|
||||
"diagnostic": {
|
||||
"factory_fournisseur_ok": len(fournisseurs_direct) > 0,
|
||||
"cache_initialise": cache_existe and cache_count > 0,
|
||||
"probleme": (
|
||||
"Cache non initialisé - appeler sage.connecter() ou _refresh_cache_fournisseurs()"
|
||||
if not cache_existe or cache_count == 0
|
||||
else "Tout est OK"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur debug direct: {e}", exc_info=True)
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@app.post("/sage/debug/fournisseurs-init-cache", dependencies=[Depends(verify_token)])
|
||||
def init_cache_fournisseurs_force():
|
||||
"""
|
||||
🔧 Force l'initialisation du cache fournisseurs (si la méthode existe)
|
||||
"""
|
||||
try:
|
||||
if not sage:
|
||||
raise HTTPException(503, "Service Sage indisponible")
|
||||
|
||||
# Vérifier que la méthode existe
|
||||
if not hasattr(sage, '_refresh_cache_fournisseurs'):
|
||||
return {
|
||||
"success": False,
|
||||
"erreur": "La méthode _refresh_cache_fournisseurs() n'existe pas dans sage_connector.py",
|
||||
"solution": "Vérifier que le code a bien été appliqué et redémarrer main.py"
|
||||
}
|
||||
|
||||
# Appeler la méthode
|
||||
logger.info("🔄 Initialisation forcée du cache fournisseurs...")
|
||||
sage._refresh_cache_fournisseurs()
|
||||
|
||||
# Vérifier le résultat
|
||||
cache_count = len(sage._cache_fournisseurs) if hasattr(sage, '_cache_fournisseurs') else 0
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"cache_initialise": cache_count > 0,
|
||||
"nb_fournisseurs": cache_count,
|
||||
"exemples": sage._cache_fournisseurs[:3] if cache_count > 0 else [],
|
||||
"message": (
|
||||
f"✅ Cache initialisé : {cache_count} fournisseurs"
|
||||
if cache_count > 0
|
||||
else "❌ Échec : cache toujours vide après refresh"
|
||||
)
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur init cache: {e}", exc_info=True)
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
# =====================================================
|
||||
# LANCEMENT
|
||||
# =====================================================
|
||||
|
|
|
|||
|
|
@ -29,13 +29,18 @@ class SageConnector:
|
|||
self.mot_de_passe = mot_de_passe
|
||||
self.cial = None
|
||||
|
||||
# Cache
|
||||
# Caches existants
|
||||
self._cache_clients: List[Dict] = []
|
||||
self._cache_articles: List[Dict] = []
|
||||
self._cache_clients_dict: Dict[str, Dict] = {}
|
||||
self._cache_articles_dict: Dict[str, Dict] = {}
|
||||
|
||||
# Métadonnées cache
|
||||
# ✅ NOUVEAU : Cache fournisseurs dédié
|
||||
self._cache_fournisseurs: List[Dict] = []
|
||||
self._cache_fournisseurs_dict: Dict[str, Dict] = {}
|
||||
self._cache_fournisseurs_last_update: Optional[datetime] = None
|
||||
|
||||
# Métadonnées cache existantes
|
||||
self._cache_clients_last_update: Optional[datetime] = None
|
||||
self._cache_articles_last_update: Optional[datetime] = None
|
||||
self._cache_ttl_minutes = 15
|
||||
|
|
@ -44,12 +49,12 @@ class SageConnector:
|
|||
self._refresh_thread: Optional[threading.Thread] = None
|
||||
self._stop_refresh = threading.Event()
|
||||
|
||||
# Locks thread-safe
|
||||
# Locks
|
||||
self._lock_clients = threading.RLock()
|
||||
self._lock_articles = threading.RLock()
|
||||
self._lock_com = threading.RLock() # Lock pour accès COM
|
||||
self._lock_com = threading.RLock()
|
||||
|
||||
# Thread-local storage pour COM
|
||||
# Thread-local storage pour COM
|
||||
self._thread_local = threading.local()
|
||||
|
||||
# =========================================================================
|
||||
|
|
@ -102,22 +107,24 @@ class SageConnector:
|
|||
"""Connexion initiale à Sage"""
|
||||
try:
|
||||
with self._com_context():
|
||||
self.cial = win32com.client.gencache.EnsureDispatch(
|
||||
"Objets100c.Cial.Stream"
|
||||
)
|
||||
self.cial = win32com.client.gencache.EnsureDispatch("Objets100c.Cial.Stream")
|
||||
self.cial.Name = self.chemin_base
|
||||
self.cial.Loggable.UserName = self.utilisateur
|
||||
self.cial.Loggable.UserPwd = self.mot_de_passe
|
||||
self.cial.Open()
|
||||
|
||||
logger.info(f"Connexion Sage réussie: {self.chemin_base}")
|
||||
logger.info(f"✅ Connexion Sage réussie: {self.chemin_base}")
|
||||
|
||||
# Chargement initial du cache
|
||||
logger.info("Chargement initial du cache...")
|
||||
logger.info("📦 Chargement initial du cache...")
|
||||
self._refresh_cache_clients()
|
||||
self._refresh_cache_articles()
|
||||
self._refresh_cache_fournisseurs() # ✅ CETTE LIGNE DOIT ÊTRE LÀ
|
||||
|
||||
logger.info(
|
||||
f"Cache initialisé: {len(self._cache_clients)} clients, {len(self._cache_articles)} articles"
|
||||
f"✅ Cache initialisé: {len(self._cache_clients)} clients, "
|
||||
f"{len(self._cache_articles)} articles, "
|
||||
f"{len(self._cache_fournisseurs)} fournisseurs" # ✅ AJOUT
|
||||
)
|
||||
|
||||
# Démarrage du thread d'actualisation
|
||||
|
|
@ -126,7 +133,7 @@ class SageConnector:
|
|||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur connexion Sage: {e}", exc_info=True)
|
||||
logger.error(f"❌ Erreur connexion Sage: {e}", exc_info=True)
|
||||
return False
|
||||
|
||||
def deconnecter(self):
|
||||
|
|
@ -150,36 +157,36 @@ class SageConnector:
|
|||
|
||||
def _start_refresh_thread(self):
|
||||
"""Démarre le thread d'actualisation automatique"""
|
||||
|
||||
|
||||
def refresh_loop():
|
||||
# Initialiser COM pour ce thread worker
|
||||
pythoncom.CoInitialize()
|
||||
|
||||
|
||||
try:
|
||||
while not self._stop_refresh.is_set():
|
||||
time.sleep(60) # Vérifier toutes les minutes
|
||||
|
||||
time.sleep(60)
|
||||
|
||||
# Clients
|
||||
if self._cache_clients_last_update:
|
||||
age = datetime.now() - self._cache_clients_last_update
|
||||
if age.total_seconds() > self._cache_ttl_minutes * 60:
|
||||
logger.info(
|
||||
f"Actualisation cache clients (âge: {age.seconds//60}min)"
|
||||
)
|
||||
self._refresh_cache_clients()
|
||||
|
||||
|
||||
# Articles
|
||||
if self._cache_articles_last_update:
|
||||
age = datetime.now() - self._cache_articles_last_update
|
||||
if age.total_seconds() > self._cache_ttl_minutes * 60:
|
||||
logger.info(
|
||||
f"Actualisation cache articles (âge: {age.seconds//60}min)"
|
||||
)
|
||||
self._refresh_cache_articles()
|
||||
|
||||
# ✅ AJOUT : Fournisseurs
|
||||
if hasattr(self, '_cache_fournisseurs_last_update') and self._cache_fournisseurs_last_update:
|
||||
age = datetime.now() - self._cache_fournisseurs_last_update
|
||||
if age.total_seconds() > self._cache_ttl_minutes * 60:
|
||||
logger.info(f"🔄 Actualisation cache fournisseurs (âge: {age.seconds//60}min)")
|
||||
self._refresh_cache_fournisseurs()
|
||||
|
||||
finally:
|
||||
# Nettoyer COM en fin de thread
|
||||
pythoncom.CoUninitialize()
|
||||
|
||||
|
||||
self._refresh_thread = threading.Thread(
|
||||
target=refresh_loop, daemon=True, name="SageCacheRefresh"
|
||||
)
|
||||
|
|
@ -298,6 +305,169 @@ class SageConnector:
|
|||
except Exception as e:
|
||||
logger.error(f" Erreur refresh articles: {e}", exc_info=True)
|
||||
|
||||
def _refresh_cache_fournisseurs(self):
|
||||
"""
|
||||
✅ CORRIGÉ FINAL : Actualise le cache des fournisseurs via FactoryFournisseur
|
||||
"""
|
||||
if not self.cial:
|
||||
logger.error("❌ self.cial est None")
|
||||
# ✅ INITIALISER UN CACHE VIDE même en cas d'erreur
|
||||
self._cache_fournisseurs = []
|
||||
self._cache_fournisseurs_dict = {}
|
||||
self._cache_fournisseurs_last_update = None
|
||||
return
|
||||
|
||||
fournisseurs = []
|
||||
fournisseurs_dict = {}
|
||||
|
||||
try:
|
||||
with self._com_context(), self._lock_com:
|
||||
logger.info("=" * 80)
|
||||
logger.info("🔄 DÉBUT REFRESH CACHE FOURNISSEURS")
|
||||
logger.info("=" * 80)
|
||||
|
||||
# ✅ Accéder à FactoryFournisseur
|
||||
try:
|
||||
factory = self.cial.CptaApplication.FactoryFournisseur
|
||||
logger.info("✅ FactoryFournisseur accessible")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Impossible d'accéder à FactoryFournisseur: {e}")
|
||||
# ✅ INITIALISER UN CACHE VIDE
|
||||
with self._lock_clients:
|
||||
self._cache_fournisseurs = []
|
||||
self._cache_fournisseurs_dict = {}
|
||||
self._cache_fournisseurs_last_update = None
|
||||
return
|
||||
|
||||
index = 1
|
||||
erreurs_consecutives = 0
|
||||
max_erreurs = 10 # ✅ RÉDUIT pour éviter de bloquer le démarrage
|
||||
|
||||
while index < 10000 and erreurs_consecutives < max_erreurs:
|
||||
persist = None
|
||||
|
||||
# ✅ ÉTAPE 1 : Lire l'élément (avec gestion d'erreur simple)
|
||||
try:
|
||||
persist = factory.List(index)
|
||||
except Exception as e:
|
||||
logger.debug(f"⚠️ Index {index}: factory.List() échoue - {e}")
|
||||
erreurs_consecutives += 1
|
||||
index += 1
|
||||
continue
|
||||
|
||||
if persist is None:
|
||||
logger.info(f"✅ Fin de liste à l'index {index}")
|
||||
break
|
||||
|
||||
# ✅ ÉTAPE 2 : Cast (avec gestion d'erreur simple)
|
||||
obj = None
|
||||
try:
|
||||
obj = self._cast_client(persist)
|
||||
except Exception as e:
|
||||
logger.debug(f"⚠️ Index {index}: _cast_client() échoue - {e}")
|
||||
erreurs_consecutives += 1
|
||||
index += 1
|
||||
continue
|
||||
|
||||
if obj is None:
|
||||
logger.debug(f"⚠️ Index {index}: _cast_client retourne None (skip)")
|
||||
index += 1
|
||||
continue
|
||||
|
||||
# ✅ ÉTAPE 3 : Extraire
|
||||
data = None
|
||||
try:
|
||||
data = self._extraire_client(obj)
|
||||
except Exception as e:
|
||||
logger.debug(f"⚠️ Index {index}: _extraire_client() échoue - {e}")
|
||||
erreurs_consecutives += 1
|
||||
index += 1
|
||||
continue
|
||||
|
||||
# ✅ ÉTAPE 4 : Vérifier les données
|
||||
if not data or not data.get("numero"):
|
||||
logger.debug(f"⚠️ Index {index}: données invalides (skip)")
|
||||
index += 1
|
||||
continue
|
||||
|
||||
# ✅ SUCCÈS : Marquer et stocker
|
||||
data["est_fournisseur"] = True
|
||||
fournisseurs.append(data)
|
||||
fournisseurs_dict[data["numero"]] = data
|
||||
erreurs_consecutives = 0 # ✅ Reset compteur
|
||||
|
||||
# Log progression tous les 10
|
||||
if len(fournisseurs) % 10 == 0:
|
||||
logger.info(f" ✅ {len(fournisseurs)} fournisseurs chargés...")
|
||||
|
||||
index += 1
|
||||
|
||||
# ✅ TOUJOURS stocker dans les attributs (même si vide)
|
||||
with self._lock_clients:
|
||||
self._cache_fournisseurs = fournisseurs
|
||||
self._cache_fournisseurs_dict = fournisseurs_dict
|
||||
self._cache_fournisseurs_last_update = datetime.now()
|
||||
|
||||
logger.info("=" * 80)
|
||||
logger.info(f"✅ CACHE FOURNISSEURS ACTUALISÉ: {len(fournisseurs)} fournisseurs")
|
||||
logger.info("=" * 80)
|
||||
|
||||
# ✅ Exemples
|
||||
if len(fournisseurs) > 0:
|
||||
logger.info("Exemples de fournisseurs chargés:")
|
||||
for f in fournisseurs[:3]:
|
||||
logger.info(f" - {f['numero']}: {f['intitule']}")
|
||||
else:
|
||||
logger.warning("⚠️ AUCUN FOURNISSEUR CHARGÉ")
|
||||
logger.warning(f"Erreurs consécutives finales: {erreurs_consecutives}")
|
||||
logger.warning(f"Dernier index testé: {index}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ ERREUR GLOBALE refresh fournisseurs: {e}", exc_info=True)
|
||||
# ✅ INITIALISER UN CACHE VIDE en cas d'erreur critique
|
||||
with self._lock_clients:
|
||||
self._cache_fournisseurs = []
|
||||
self._cache_fournisseurs_dict = {}
|
||||
self._cache_fournisseurs_last_update = None
|
||||
|
||||
|
||||
def lister_tous_fournisseurs(self, filtre=""):
|
||||
"""
|
||||
✅ CORRIGÉ : Liste les fournisseurs depuis le cache dédié
|
||||
"""
|
||||
# Si le cache fournisseurs n'existe pas ou est vide, le créer
|
||||
if not hasattr(self, '_cache_fournisseurs') or len(self._cache_fournisseurs) == 0:
|
||||
logger.info("Cache fournisseurs vide, chargement initial...")
|
||||
self._refresh_cache_fournisseurs()
|
||||
|
||||
with self._lock_clients:
|
||||
if not filtre:
|
||||
result = self._cache_fournisseurs.copy()
|
||||
logger.info(f"Liste fournisseurs sans filtre: {len(result)} résultats")
|
||||
return result
|
||||
|
||||
filtre_lower = filtre.lower()
|
||||
result = [
|
||||
f for f in self._cache_fournisseurs
|
||||
if filtre_lower in f["numero"].lower() or
|
||||
filtre_lower in f["intitule"].lower()
|
||||
]
|
||||
logger.info(f"Liste fournisseurs avec filtre '{filtre}': {len(result)} résultats")
|
||||
return result
|
||||
|
||||
|
||||
def lire_fournisseur(self, code_fournisseur):
|
||||
"""
|
||||
✅ CORRIGÉ : Lecture depuis le cache fournisseurs
|
||||
"""
|
||||
# Si le cache fournisseurs n'existe pas, le créer
|
||||
if not hasattr(self, '_cache_fournisseurs_dict') or not self._cache_fournisseurs_dict:
|
||||
self._refresh_cache_fournisseurs()
|
||||
|
||||
with self._lock_clients:
|
||||
return self._cache_fournisseurs_dict.get(code_fournisseur)
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# API PUBLIQUE (ultra-rapide grâce au cache)
|
||||
# =========================================================================
|
||||
|
|
@ -342,15 +512,23 @@ class SageConnector:
|
|||
|
||||
def forcer_actualisation_cache(self):
|
||||
"""Force l'actualisation immédiate du cache (endpoint admin)"""
|
||||
logger.info("Actualisation forcée du cache...")
|
||||
logger.info("🔄 Actualisation forcée du cache...")
|
||||
self._refresh_cache_clients()
|
||||
self._refresh_cache_articles()
|
||||
self._refresh_cache_fournisseurs() # ✅ AJOUT
|
||||
logger.info("✅ Cache actualisé")
|
||||
|
||||
# ✅ AJOUT
|
||||
if hasattr(self, '_refresh_cache_fournisseurs'):
|
||||
self._refresh_cache_fournisseurs()
|
||||
|
||||
logger.info("Cache actualisé")
|
||||
|
||||
|
||||
def get_cache_info(self):
|
||||
"""Retourne les infos du cache (endpoint monitoring)"""
|
||||
with self._lock_clients, self._lock_articles:
|
||||
return {
|
||||
with self._lock_clients:
|
||||
info = {
|
||||
"clients": {
|
||||
"count": len(self._cache_clients),
|
||||
"last_update": (
|
||||
|
|
@ -359,10 +537,7 @@ class SageConnector:
|
|||
else None
|
||||
),
|
||||
"age_minutes": (
|
||||
(
|
||||
datetime.now() - self._cache_clients_last_update
|
||||
).total_seconds()
|
||||
/ 60
|
||||
(datetime.now() - self._cache_clients_last_update).total_seconds() / 60
|
||||
if self._cache_clients_last_update
|
||||
else None
|
||||
),
|
||||
|
|
@ -375,17 +550,32 @@ class SageConnector:
|
|||
else None
|
||||
),
|
||||
"age_minutes": (
|
||||
(
|
||||
datetime.now() - self._cache_articles_last_update
|
||||
).total_seconds()
|
||||
/ 60
|
||||
(datetime.now() - self._cache_articles_last_update).total_seconds() / 60
|
||||
if self._cache_articles_last_update
|
||||
else None
|
||||
),
|
||||
},
|
||||
"ttl_minutes": self._cache_ttl_minutes,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# ✅ AJOUT : Info fournisseurs
|
||||
if hasattr(self, '_cache_fournisseurs'):
|
||||
info["fournisseurs"] = {
|
||||
"count": len(self._cache_fournisseurs),
|
||||
"last_update": (
|
||||
self._cache_fournisseurs_last_update.isoformat()
|
||||
if self._cache_fournisseurs_last_update
|
||||
else None
|
||||
),
|
||||
"age_minutes": (
|
||||
(datetime.now() - self._cache_fournisseurs_last_update).total_seconds() / 60
|
||||
if self._cache_fournisseurs_last_update
|
||||
else None
|
||||
),
|
||||
}
|
||||
|
||||
info["ttl_minutes"] = self._cache_ttl_minutes
|
||||
return info
|
||||
|
||||
# =========================================================================
|
||||
# CAST HELPERS
|
||||
# =========================================================================
|
||||
|
|
@ -395,7 +585,8 @@ class SageConnector:
|
|||
obj = win32com.client.CastTo(persist_obj, "IBOClient3")
|
||||
obj.Read()
|
||||
return obj
|
||||
except:
|
||||
except Exception as e:
|
||||
logger.debug(f"❌ _cast_client échoue: {e}") # ✅ AJOUTER CE LOG
|
||||
return None
|
||||
|
||||
def _cast_article(self, persist_obj):
|
||||
|
|
@ -412,31 +603,72 @@ class SageConnector:
|
|||
|
||||
def _extraire_client(self, client_obj):
|
||||
"""MISE À JOUR : Extraction avec détection prospect ET type"""
|
||||
data = {
|
||||
"numero": getattr(client_obj, "CT_Num", ""),
|
||||
"intitule": getattr(client_obj, "CT_Intitule", ""),
|
||||
"type": getattr(client_obj, "CT_Type", 0), # ✅ 0=Client/Prospect, 1=Fournisseur
|
||||
"est_prospect": getattr(client_obj, "CT_Prospect", 0) == 1, # ✅ Indicateur prospect
|
||||
}
|
||||
|
||||
|
||||
try:
|
||||
adresse = getattr(client_obj, "Adresse", None)
|
||||
if adresse:
|
||||
data["adresse"] = getattr(adresse, "Adresse", "")
|
||||
data["code_postal"] = getattr(adresse, "CodePostal", "")
|
||||
data["ville"] = getattr(adresse, "Ville", "")
|
||||
except:
|
||||
pass
|
||||
# ✅ LOGS DÉTAILLÉS
|
||||
try:
|
||||
numero = getattr(client_obj, "CT_Num", "")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur lecture CT_Num: {e}")
|
||||
raise
|
||||
|
||||
try:
|
||||
intitule = getattr(client_obj, "CT_Intitule", "")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur lecture CT_Intitule sur {numero}: {e}")
|
||||
raise
|
||||
|
||||
try:
|
||||
type_tiers = getattr(client_obj, "CT_Type", 0)
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur lecture CT_Type sur {numero}: {e}")
|
||||
type_tiers = 0
|
||||
|
||||
try:
|
||||
qualite = getattr(client_obj, "CT_Qualite", None)
|
||||
except Exception as e:
|
||||
logger.debug(f"⚠️ Erreur lecture CT_Qualite sur {numero}: {e}")
|
||||
qualite = None
|
||||
|
||||
try:
|
||||
prospect = getattr(client_obj, "CT_Prospect", 0) == 1
|
||||
except Exception as e:
|
||||
logger.debug(f"⚠️ Erreur lecture CT_Prospect sur {numero}: {e}")
|
||||
prospect = False
|
||||
|
||||
data = {
|
||||
"numero": numero,
|
||||
"intitule": intitule,
|
||||
"type": type_tiers,
|
||||
"qualite": qualite,
|
||||
"est_prospect": prospect,
|
||||
"est_fournisseur": qualite in [2, 3] if qualite is not None else False,
|
||||
}
|
||||
|
||||
try:
|
||||
telecom = getattr(client_obj, "Telecom", None)
|
||||
if telecom:
|
||||
data["telephone"] = getattr(telecom, "Telephone", "")
|
||||
data["email"] = getattr(telecom, "EMail", "")
|
||||
except:
|
||||
pass
|
||||
# Adresse (non critique)
|
||||
try:
|
||||
adresse = getattr(client_obj, "Adresse", None)
|
||||
if adresse:
|
||||
data["adresse"] = getattr(adresse, "Adresse", "")
|
||||
data["code_postal"] = getattr(adresse, "CodePostal", "")
|
||||
data["ville"] = getattr(adresse, "Ville", "")
|
||||
except Exception as e:
|
||||
logger.debug(f"⚠️ Erreur adresse sur {numero}: {e}")
|
||||
|
||||
return data
|
||||
# Telecom (non critique)
|
||||
try:
|
||||
telecom = getattr(client_obj, "Telecom", None)
|
||||
if telecom:
|
||||
data["telephone"] = getattr(telecom, "Telephone", "")
|
||||
data["email"] = getattr(telecom, "EMail", "")
|
||||
except Exception as e:
|
||||
logger.debug(f"⚠️ Erreur telecom sur {numero}: {e}")
|
||||
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ ERREUR GLOBALE _extraire_client: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
def _extraire_article(self, article_obj):
|
||||
return {
|
||||
|
|
@ -1881,34 +2113,6 @@ class SageConnector:
|
|||
return prospect
|
||||
return None
|
||||
|
||||
# =========================================================================
|
||||
# FOURNISSEURS (CT_Type = 1)
|
||||
# =========================================================================
|
||||
def lister_tous_fournisseurs(self, filtre=""):
|
||||
"""Liste tous les fournisseurs depuis le cache"""
|
||||
with self._lock_clients:
|
||||
if not filtre:
|
||||
return [c for c in self._cache_clients if c.get("type") == 1]
|
||||
|
||||
filtre_lower = filtre.lower()
|
||||
return [
|
||||
c
|
||||
for c in self._cache_clients
|
||||
if c.get("type") == 1
|
||||
and (
|
||||
filtre_lower in c["numero"].lower()
|
||||
or filtre_lower in c["intitule"].lower()
|
||||
)
|
||||
]
|
||||
|
||||
def lire_fournisseur(self, code_fournisseur):
|
||||
"""Retourne un fournisseur depuis le cache"""
|
||||
with self._lock_clients:
|
||||
fournisseur = self._cache_clients_dict.get(code_fournisseur)
|
||||
if fournisseur and fournisseur.get("type") == 1:
|
||||
return fournisseur
|
||||
return None
|
||||
|
||||
# =========================================================================
|
||||
# EXTRACTION CLIENTS (Mise à jour pour inclure prospects)
|
||||
# =========================================================================
|
||||
|
|
|
|||
Loading…
Reference in a new issue