Made GET fournisseurs
This commit is contained in:
parent
7ca64e2ea6
commit
3aadc67abf
2 changed files with 299 additions and 1285 deletions
|
|
@ -35,11 +35,6 @@ class SageConnector:
|
|||
self._cache_clients_dict: Dict[str, Dict] = {}
|
||||
self._cache_articles_dict: Dict[str, Dict] = {}
|
||||
|
||||
# ✅ 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
|
||||
|
|
@ -119,13 +114,6 @@ class SageConnector:
|
|||
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, "
|
||||
f"{len(self._cache_articles)} articles, "
|
||||
f"{len(self._cache_fournisseurs)} fournisseurs" # ✅ AJOUT
|
||||
)
|
||||
|
||||
# Démarrage du thread d'actualisation
|
||||
self._start_refresh_thread()
|
||||
|
|
@ -177,13 +165,6 @@ class SageConnector:
|
|||
if age.total_seconds() > self._cache_ttl_minutes * 60:
|
||||
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:
|
||||
pythoncom.CoUninitialize()
|
||||
|
||||
|
|
@ -222,7 +203,7 @@ class SageConnector:
|
|||
if obj:
|
||||
data = self._extraire_client(obj)
|
||||
|
||||
# ✅ INCLURE TOUS LES TYPES (clients, prospects, fournisseurs)
|
||||
# ✅ INCLURE TOUS LES TYPES (clients, prospects)
|
||||
clients.append(data)
|
||||
clients_dict[data["numero"]] = data
|
||||
erreurs_consecutives = 0
|
||||
|
|
@ -246,11 +227,10 @@ class SageConnector:
|
|||
# 📊 Statistiques détaillées
|
||||
nb_clients = sum(1 for c in clients if c.get("type") == 0 and not c.get("est_prospect"))
|
||||
nb_prospects = sum(1 for c in clients if c.get("type") == 0 and c.get("est_prospect"))
|
||||
nb_fournisseurs = sum(1 for c in clients if c.get("type") == 1)
|
||||
|
||||
logger.info(
|
||||
f"✅ Cache actualisé: {len(clients)} tiers "
|
||||
f"({nb_clients} clients, {nb_prospects} prospects, {nb_fournisseurs} fournisseurs)"
|
||||
f"({nb_clients} clients, {nb_prospects} prospects)"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
|
|
@ -305,168 +285,183 @@ class SageConnector:
|
|||
except Exception as e:
|
||||
logger.error(f" Erreur refresh articles: {e}", exc_info=True)
|
||||
|
||||
def _refresh_cache_fournisseurs(self):
|
||||
def lister_tous_fournisseurs(self, filtre=""):
|
||||
"""
|
||||
✅ CORRIGÉ FINAL : Actualise le cache des fournisseurs via FactoryFournisseur
|
||||
✅ CORRECTION FINALE : Liste fournisseurs SANS passer par _extraire_client()
|
||||
|
||||
BYPASS TOTAL de _extraire_client() car :
|
||||
- Les objets fournisseurs n'ont pas les mêmes champs que les clients
|
||||
- _extraire_client() plante sur "CT_Qualite" (n'existe pas sur fournisseurs)
|
||||
- Le diagnostic fournisseurs-analyse-complete fonctionne SANS _extraire_client()
|
||||
|
||||
→ On fait EXACTEMENT comme le diagnostic qui marche
|
||||
"""
|
||||
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
|
||||
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
|
||||
logger.info(f"🔍 Lecture fournisseurs via FactoryFournisseur (filtre='{filtre}')")
|
||||
|
||||
factory = self.cial.CptaApplication.FactoryFournisseur
|
||||
index = 1
|
||||
max_iterations = 10000
|
||||
erreurs_consecutives = 0
|
||||
max_erreurs = 10 # ✅ RÉDUIT pour éviter de bloquer le démarrage
|
||||
max_erreurs = 50
|
||||
|
||||
while index < 10000 and erreurs_consecutives < max_erreurs:
|
||||
persist = None
|
||||
filtre_lower = filtre.lower() if filtre else ""
|
||||
|
||||
# ✅ ÉTAPE 1 : Lire l'élément (avec gestion d'erreur simple)
|
||||
while index < max_iterations and erreurs_consecutives < max_erreurs:
|
||||
try:
|
||||
persist = factory.List(index)
|
||||
|
||||
if persist is None:
|
||||
logger.debug(f"Fin de liste à l'index {index}")
|
||||
break
|
||||
|
||||
# Cast
|
||||
fourn = self._cast_client(persist)
|
||||
|
||||
if fourn:
|
||||
# ✅✅✅ EXTRACTION DIRECTE (pas de _extraire_client) ✅✅✅
|
||||
try:
|
||||
numero = getattr(fourn, "CT_Num", "").strip()
|
||||
intitule = getattr(fourn, "CT_Intitule", "").strip()
|
||||
|
||||
if not numero:
|
||||
logger.debug(f"Index {index}: CT_Num vide, skip")
|
||||
erreurs_consecutives += 1
|
||||
index += 1
|
||||
continue
|
||||
|
||||
# Construction objet minimal
|
||||
data = {
|
||||
"numero": numero,
|
||||
"intitule": intitule,
|
||||
"type": 1, # Fournisseur
|
||||
"est_fournisseur": True
|
||||
}
|
||||
|
||||
# Champs optionnels (avec gestion d'erreur)
|
||||
try:
|
||||
adresse_obj = getattr(fourn, "Adresse", None)
|
||||
if adresse_obj:
|
||||
data["adresse"] = getattr(adresse_obj, "Adresse", "").strip()
|
||||
data["code_postal"] = getattr(adresse_obj, "CodePostal", "").strip()
|
||||
data["ville"] = getattr(adresse_obj, "Ville", "").strip()
|
||||
except:
|
||||
data["adresse"] = ""
|
||||
data["code_postal"] = ""
|
||||
data["ville"] = ""
|
||||
|
||||
try:
|
||||
telecom_obj = getattr(fourn, "Telecom", None)
|
||||
if telecom_obj:
|
||||
data["telephone"] = getattr(telecom_obj, "Telephone", "").strip()
|
||||
data["email"] = getattr(telecom_obj, "EMail", "").strip()
|
||||
except:
|
||||
data["telephone"] = ""
|
||||
data["email"] = ""
|
||||
|
||||
# Filtrer si nécessaire
|
||||
if not filtre_lower or \
|
||||
filtre_lower in numero.lower() or \
|
||||
filtre_lower in intitule.lower():
|
||||
fournisseurs.append(data)
|
||||
logger.debug(f"✅ Fournisseur ajouté: {numero} - {intitule}")
|
||||
|
||||
erreurs_consecutives = 0
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"⚠️ Erreur extraction index {index}: {e}")
|
||||
erreurs_consecutives += 1
|
||||
else:
|
||||
erreurs_consecutives += 1
|
||||
|
||||
index += 1
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"⚠️ Index {index}: factory.List() échoue - {e}")
|
||||
logger.debug(f"⚠️ Erreur index {index}: {e}")
|
||||
erreurs_consecutives += 1
|
||||
index += 1
|
||||
continue
|
||||
|
||||
if persist is None:
|
||||
logger.info(f"✅ Fin de liste à l'index {index}")
|
||||
break
|
||||
if erreurs_consecutives >= max_erreurs:
|
||||
logger.warning(f"⚠️ Arrêt après {max_erreurs} erreurs consécutives")
|
||||
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}")
|
||||
logger.info(f"✅ {len(fournisseurs)} fournisseurs retournés")
|
||||
return fournisseurs
|
||||
|
||||
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
|
||||
logger.error(f"❌ Erreur liste fournisseurs: {e}", exc_info=True)
|
||||
return []
|
||||
|
||||
|
||||
def lister_tous_fournisseurs(self, filtre=""):
|
||||
def lire_fournisseur(self, code):
|
||||
"""
|
||||
✅ CORRIGÉ : Liste les fournisseurs depuis le cache dédié
|
||||
✅ NOUVEAU : Lecture d'un fournisseur par code
|
||||
|
||||
Utilise FactoryFournisseur.ReadNumero() directement
|
||||
"""
|
||||
# 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()
|
||||
if not self.cial:
|
||||
return None
|
||||
|
||||
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
|
||||
try:
|
||||
with self._com_context(), self._lock_com:
|
||||
factory = self.cial.CptaApplication.FactoryFournisseur
|
||||
persist = factory.ReadNumero(code)
|
||||
|
||||
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
|
||||
if not persist:
|
||||
logger.warning(f"Fournisseur {code} introuvable")
|
||||
return None
|
||||
|
||||
fourn = self._cast_client(persist)
|
||||
|
||||
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()
|
||||
if not fourn:
|
||||
return None
|
||||
|
||||
with self._lock_clients:
|
||||
return self._cache_fournisseurs_dict.get(code_fournisseur)
|
||||
# Extraction directe (même logique que lister_tous_fournisseurs)
|
||||
numero = getattr(fourn, "CT_Num", "").strip()
|
||||
intitule = getattr(fourn, "CT_Intitule", "").strip()
|
||||
|
||||
data = {
|
||||
"numero": numero,
|
||||
"intitule": intitule,
|
||||
"type": 1,
|
||||
"est_fournisseur": True
|
||||
}
|
||||
|
||||
# Adresse
|
||||
try:
|
||||
adresse_obj = getattr(fourn, "Adresse", None)
|
||||
if adresse_obj:
|
||||
data["adresse"] = getattr(adresse_obj, "Adresse", "").strip()
|
||||
data["code_postal"] = getattr(adresse_obj, "CodePostal", "").strip()
|
||||
data["ville"] = getattr(adresse_obj, "Ville", "").strip()
|
||||
except:
|
||||
data["adresse"] = ""
|
||||
data["code_postal"] = ""
|
||||
data["ville"] = ""
|
||||
|
||||
# Télécom
|
||||
try:
|
||||
telecom_obj = getattr(fourn, "Telecom", None)
|
||||
if telecom_obj:
|
||||
data["telephone"] = getattr(telecom_obj, "Telephone", "").strip()
|
||||
data["email"] = getattr(telecom_obj, "EMail", "").strip()
|
||||
except:
|
||||
data["telephone"] = ""
|
||||
data["email"] = ""
|
||||
|
||||
logger.info(f"✅ Fournisseur {code} lu: {intitule}")
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur lecture fournisseur {code}: {e}")
|
||||
return None
|
||||
|
||||
# =========================================================================
|
||||
# API PUBLIQUE (ultra-rapide grâce au cache)
|
||||
|
|
@ -515,13 +510,8 @@ class SageConnector:
|
|||
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é")
|
||||
|
||||
|
||||
|
|
@ -557,22 +547,6 @@ class SageConnector:
|
|||
}
|
||||
}
|
||||
|
||||
# ✅ 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
|
||||
|
||||
|
|
@ -602,83 +576,106 @@ class SageConnector:
|
|||
# =========================================================================
|
||||
|
||||
def _extraire_client(self, client_obj):
|
||||
"""MISE À JOUR : Extraction avec détection prospect ET type"""
|
||||
"""
|
||||
✅ CORRECTION : Extraction ULTRA-ROBUSTE pour clients ET fournisseurs
|
||||
|
||||
Gère tous les cas où des champs peuvent être manquants
|
||||
"""
|
||||
try:
|
||||
# ✅ LOGS DÉTAILLÉS
|
||||
# === 1. CHAMPS OBLIGATOIRES ===
|
||||
try:
|
||||
numero = getattr(client_obj, "CT_Num", "")
|
||||
numero = getattr(client_obj, "CT_Num", "").strip()
|
||||
if not numero:
|
||||
logger.debug("⚠️ Objet sans CT_Num, skip")
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur lecture CT_Num: {e}")
|
||||
raise
|
||||
logger.debug(f"❌ Erreur lecture CT_Num: {e}")
|
||||
return None
|
||||
|
||||
try:
|
||||
intitule = getattr(client_obj, "CT_Intitule", "")
|
||||
intitule = getattr(client_obj, "CT_Intitule", "").strip()
|
||||
if not intitule:
|
||||
logger.debug(f"⚠️ {numero} sans 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
|
||||
logger.debug(f"⚠️ Erreur CT_Intitule sur {numero}: {e}")
|
||||
intitule = ""
|
||||
|
||||
# === 2. CONSTRUCTION OBJET MINIMAL ===
|
||||
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,
|
||||
}
|
||||
|
||||
# Adresse (non critique)
|
||||
# === 3. CHAMPS OPTIONNELS (avec try-except individuels) ===
|
||||
|
||||
# Type
|
||||
try:
|
||||
data["type"] = getattr(client_obj, "CT_Type", 0)
|
||||
except:
|
||||
data["type"] = 0
|
||||
|
||||
# Qualité
|
||||
try:
|
||||
qualite = getattr(client_obj, "CT_Qualite", None)
|
||||
data["qualite"] = qualite
|
||||
data["est_fournisseur"] = qualite in [2, 3] if qualite is not None else False
|
||||
except:
|
||||
data["qualite"] = None
|
||||
data["est_fournisseur"] = False
|
||||
|
||||
# Prospect
|
||||
try:
|
||||
data["est_prospect"] = getattr(client_obj, "CT_Prospect", 0) == 1
|
||||
except:
|
||||
data["est_prospect"] = False
|
||||
|
||||
# === 4. 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", "")
|
||||
try:
|
||||
data["adresse"] = getattr(adresse, "Adresse", "").strip()
|
||||
except:
|
||||
data["adresse"] = ""
|
||||
|
||||
try:
|
||||
data["code_postal"] = getattr(adresse, "CodePostal", "").strip()
|
||||
except:
|
||||
data["code_postal"] = ""
|
||||
|
||||
try:
|
||||
data["ville"] = getattr(adresse, "Ville", "").strip()
|
||||
except:
|
||||
data["ville"] = ""
|
||||
except Exception as e:
|
||||
logger.debug(f"⚠️ Erreur adresse sur {numero}: {e}")
|
||||
data["adresse"] = ""
|
||||
data["code_postal"] = ""
|
||||
data["ville"] = ""
|
||||
|
||||
# Telecom (non critique)
|
||||
# === 5. TELECOM (non critique) ===
|
||||
try:
|
||||
telecom = getattr(client_obj, "Telecom", None)
|
||||
if telecom:
|
||||
data["telephone"] = getattr(telecom, "Telephone", "")
|
||||
data["email"] = getattr(telecom, "EMail", "")
|
||||
try:
|
||||
data["telephone"] = getattr(telecom, "Telephone", "").strip()
|
||||
except:
|
||||
data["telephone"] = ""
|
||||
|
||||
try:
|
||||
data["email"] = getattr(telecom, "EMail", "").strip()
|
||||
except:
|
||||
data["email"] = ""
|
||||
except Exception as e:
|
||||
logger.debug(f"⚠️ Erreur telecom sur {numero}: {e}")
|
||||
data["telephone"] = ""
|
||||
data["email"] = ""
|
||||
|
||||
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 {
|
||||
"reference": getattr(article_obj, "AR_Ref", ""),
|
||||
"designation": getattr(article_obj, "AR_Design", ""),
|
||||
"prix_vente": getattr(article_obj, "AR_PrixVen", 0.0),
|
||||
"prix_achat": getattr(article_obj, "AR_PrixAch", 0.0),
|
||||
"stock_reel": getattr(article_obj, "AR_Stock", 0.0),
|
||||
"stock_mini": getattr(article_obj, "AR_StockMini", 0.0),
|
||||
}
|
||||
return None
|
||||
|
||||
# =========================================================================
|
||||
# CRÉATION DEVIS (US-A1) - VERSION TRANSACTIONNELLE
|
||||
|
|
|
|||
Loading…
Reference in a new issue