Align modifier_client with creer_client

This commit is contained in:
fanilo 2025-12-26 09:38:48 +01:00
parent 557f43bd18
commit fde2b4615e

View file

@ -5720,16 +5720,67 @@ class SageConnector:
raise RuntimeError(f"Erreur technique: {e}")
def modifier_client(self, code: str, client_data: Dict) -> Dict:
"""
Modification client Sage - Version complète alignée sur creer_client
"""
if not self.cial:
raise RuntimeError("Connexion Sage non établie")
try:
with self._com_context(), self._lock_com:
# ========================================
# ÉTAPE 1 : CHARGER LE CLIENT EXISTANT
# ========================================
logger.info(f" Recherche client {code}...")
logger.info("=" * 80)
logger.info(f"[MODIFICATION CLIENT SAGE - {code}]")
logger.info("=" * 80)
# ============================================================
# UTILITAIRES (identiques à creer_client)
# ============================================================
def clean_str(value, max_len: int) -> str:
if value is None or str(value).lower() in ('none', 'null', ''):
return ""
return str(value)[:max_len].strip()
def safe_int(value, default=None):
if value is None:
return default
try:
return int(value)
except (ValueError, TypeError):
return default
def safe_float(value, default=None):
if value is None:
return default
try:
return float(value)
except (ValueError, TypeError):
return default
def try_set_attribute(obj, attr_name, value, variants=None):
"""Essaie de définir un attribut avec plusieurs variantes de noms"""
if variants is None:
variants = [attr_name]
else:
variants = [attr_name] + variants
for variant in variants:
try:
if hasattr(obj, variant):
setattr(obj, variant, value)
logger.debug(f" {variant} = {value} [OK]")
return True
except Exception as e:
logger.debug(f" {variant} echec: {str(e)[:50]}")
return False
champs_modifies = []
# ============================================================
# ÉTAPE 1 : CHARGER LE CLIENT EXISTANT
# ============================================================
logger.info("[ETAPE 1] CHARGEMENT CLIENT")
factory_client = self.cial.CptaApplication.FactoryClient
persist = factory_client.ReadNumero(code)
@ -5739,151 +5790,501 @@ class SageConnector:
client = win32com.client.CastTo(persist, "IBOClient3")
client.Read()
logger.info(
f" Client {code} trouvé: {getattr(client, 'CT_Intitule', '')}"
)
logger.info(f" Client chargé: {getattr(client, 'CT_Intitule', '')}")
# ========================================
# ÉTAPE 2 : METTRE À JOUR LES CHAMPS FOURNIS
# ========================================
logger.info(" Mise à jour des champs...")
champs_modifies = []
# Intitulé
# ============================================================
# ÉTAPE 2 : IDENTIFICATION
# ============================================================
logger.info("[ETAPE 2] IDENTIFICATION")
if "intitule" in client_data:
intitule = str(client_data["intitule"])[:69].strip()
intitule = clean_str(client_data["intitule"], 69)
client.CT_Intitule = intitule
champs_modifies.append(f"intitule='{intitule}'")
# Adresse
if any(
k in client_data
for k in ["adresse", "code_postal", "ville", "pays"]
):
champs_modifies.append("intitule")
logger.info(f" CT_Intitule = {intitule}")
if "qualite" in client_data:
qualite = clean_str(client_data["qualite"], 17)
if try_set_attribute(client, "CT_Qualite", qualite):
champs_modifies.append("qualite")
if "classement" in client_data:
if try_set_attribute(client, "CT_Classement", clean_str(client_data["classement"], 17)):
champs_modifies.append("classement")
if "raccourci" in client_data:
raccourci = clean_str(client_data["raccourci"], 7).upper()
# Vérifier unicité
try:
exist_client = factory_client.ReadRaccourci(raccourci)
if exist_client and exist_client.CT_Num != code:
logger.warning(f" CT_Raccourci = {raccourci} [EXISTE DEJA - ignoré]")
else:
if try_set_attribute(client, "CT_Raccourci", raccourci):
champs_modifies.append("raccourci")
except:
if try_set_attribute(client, "CT_Raccourci", raccourci):
champs_modifies.append("raccourci")
if "siret" in client_data:
if try_set_attribute(client, "CT_Siret", clean_str(client_data["siret"], 15)):
champs_modifies.append("siret")
if "tva_intra" in client_data:
if try_set_attribute(client, "CT_Identifiant", clean_str(client_data["tva_intra"], 25)):
champs_modifies.append("tva_intra")
if "code_naf" in client_data:
if try_set_attribute(client, "CT_Ape", clean_str(client_data["code_naf"], 7)):
champs_modifies.append("code_naf")
# ============================================================
# ÉTAPE 3 : ADRESSE
# ============================================================
adresse_keys = ["contact", "adresse", "complement", "code_postal", "ville", "region", "pays"]
if any(k in client_data for k in adresse_keys):
logger.info("[ETAPE 3] ADRESSE")
try:
# CT_Contact - DOUBLE AFFECTATION
if "contact" in client_data:
contact_nom = clean_str(client_data["contact"], 35)
# Sur l'objet client
try:
client.CT_Contact = contact_nom
champs_modifies.append("contact (client)")
logger.info(f" CT_Contact (client) = {contact_nom} [OK]")
except Exception as e:
logger.warning(f" CT_Contact (client) [ECHEC: {e}]")
# Via l'objet Adresse
try:
adresse_obj = client.Adresse
adresse_obj.Contact = contact_nom
champs_modifies.append("contact (adresse)")
logger.info(f" Contact (adresse) = {contact_nom} [OK]")
except Exception as e:
logger.warning(f" Contact (adresse) [ECHEC: {e}]")
# Autres champs adresse
adresse_obj = client.Adresse
if "adresse" in client_data:
adresse = str(client_data["adresse"])[:35].strip()
adresse_obj.Adresse = adresse
adresse_obj.Adresse = clean_str(client_data["adresse"], 35)
champs_modifies.append("adresse")
if "complement" in client_data:
adresse_obj.Complement = clean_str(client_data["complement"], 35)
champs_modifies.append("complement")
if "code_postal" in client_data:
cp = str(client_data["code_postal"])[:9].strip()
adresse_obj.CodePostal = cp
adresse_obj.CodePostal = clean_str(client_data["code_postal"], 9)
champs_modifies.append("code_postal")
if "ville" in client_data:
ville = str(client_data["ville"])[:35].strip()
adresse_obj.Ville = ville
adresse_obj.Ville = clean_str(client_data["ville"], 35)
champs_modifies.append("ville")
if "region" in client_data:
adresse_obj.CodeRegion = clean_str(client_data["region"], 25)
champs_modifies.append("region")
if "pays" in client_data:
pays = str(client_data["pays"])[:35].strip()
adresse_obj.Pays = pays
adresse_obj.Pays = clean_str(client_data["pays"], 35)
champs_modifies.append("pays")
logger.info(f" Adresse mise à jour ({len([k for k in adresse_keys if k in client_data])} champs)")
except Exception as e:
logger.warning(f"Erreur mise à jour adresse: {e}")
logger.error(f" Adresse erreur: {e}")
# Télécom
if "email" in client_data or "telephone" in client_data:
# ============================================================
# ÉTAPE 4 : TELECOM
# ============================================================
telecom_keys = ["telephone", "telecopie", "email", "site_web", "portable", "facebook", "linkedin"]
if any(k in client_data for k in telecom_keys):
logger.info("[ETAPE 4] TELECOM")
try:
telecom_obj = client.Telecom
if "email" in client_data:
email = str(client_data["email"])[:69].strip()
telecom_obj.EMail = email
champs_modifies.append("email")
if "telephone" in client_data:
tel = str(client_data["telephone"])[:21].strip()
telecom_obj.Telephone = tel
telecom_obj.Telephone = clean_str(client_data["telephone"], 21)
champs_modifies.append("telephone")
if "telecopie" in client_data:
telecom_obj.Telecopie = clean_str(client_data["telecopie"], 21)
champs_modifies.append("telecopie")
if "email" in client_data:
telecom_obj.EMail = clean_str(client_data["email"], 69)
champs_modifies.append("email")
if "site_web" in client_data:
telecom_obj.Site = clean_str(client_data["site_web"], 69)
champs_modifies.append("site_web")
if "portable" in client_data:
portable = clean_str(client_data["portable"], 21)
if try_set_attribute(telecom_obj, "Portable", portable):
champs_modifies.append("portable")
if "facebook" in client_data:
facebook = clean_str(client_data["facebook"], 69)
if not try_set_attribute(telecom_obj, "Facebook", facebook):
try_set_attribute(client, "CT_Facebook", facebook)
champs_modifies.append("facebook")
if "linkedin" in client_data:
linkedin = clean_str(client_data["linkedin"], 69)
if not try_set_attribute(telecom_obj, "LinkedIn", linkedin):
try_set_attribute(client, "CT_LinkedIn", linkedin)
champs_modifies.append("linkedin")
logger.info(f" Telecom mis à jour ({len([k for k in telecom_keys if k in client_data])} champs)")
except Exception as e:
logger.warning(f"Erreur mise à jour télécom: {e}")
logger.error(f" Telecom erreur: {e}")
# SIRET
if "siret" in client_data:
# ============================================================
# ÉTAPE 5 : COMPTE GENERAL
# ============================================================
if "compte_general" in client_data:
logger.info("[ETAPE 5] COMPTE GENERAL")
compte = clean_str(client_data["compte_general"], 13)
factory_compte = self.cial.CptaApplication.FactoryCompteG
try:
siret = str(client_data["siret"])[:14].strip()
client.CT_Siret = siret
champs_modifies.append("siret")
persist_compte = factory_compte.ReadNumero(compte)
if persist_compte:
compte_obj = win32com.client.CastTo(persist_compte, "IBOCompteG3")
compte_obj.Read()
type_compte = getattr(compte_obj, 'CG_Type', None)
if type_compte == 0:
client.CompteGPrinc = compte_obj
champs_modifies.append("compte_general")
logger.info(f" CompteGPrinc = {compte} [OK]")
else:
logger.warning(f" Compte {compte} - Type {type_compte} incompatible")
except Exception as e:
logger.warning(f"Erreur mise à jour SIRET: {e}")
logger.warning(f" CompteGPrinc erreur: {e}")
# TVA Intracommunautaire
if "tva_intra" in client_data:
try:
tva = str(client_data["tva_intra"])[:25].strip()
client.CT_Identifiant = tva
champs_modifies.append("tva_intra")
except Exception as e:
logger.warning(f"Erreur mise à jour TVA: {e}")
# ============================================================
# ÉTAPE 6 : CATEGORIES
# ============================================================
if "categorie_tarifaire" in client_data or "categorie_comptable" in client_data:
logger.info("[ETAPE 6] CATEGORIES")
if "categorie_tarifaire" in client_data:
try:
cat_id = str(client_data["categorie_tarifaire"])
factory_cat_tarif = self.cial.CptaApplication.FactoryCategorieTarif
persist_cat = factory_cat_tarif.ReadIntitule(cat_id)
if persist_cat:
cat_tarif_obj = win32com.client.CastTo(persist_cat, "IBOCategorieTarif3")
cat_tarif_obj.Read()
client.CatTarif = cat_tarif_obj
champs_modifies.append("categorie_tarifaire")
logger.info(f" CatTarif = {cat_id} [OK]")
except Exception as e:
logger.warning(f" CatTarif erreur: {e}")
if "categorie_comptable" in client_data:
try:
cat_id = str(client_data["categorie_comptable"])
factory_cat_compta = self.cial.CptaApplication.FactoryCategorieCompta
persist_cat = factory_cat_compta.ReadIntitule(cat_id)
if persist_cat:
cat_compta_obj = win32com.client.CastTo(persist_cat, "IBOCategorieCompta3")
cat_compta_obj.Read()
client.CatCompta = cat_compta_obj
champs_modifies.append("categorie_comptable")
logger.info(f" CatCompta = {cat_id} [OK]")
except Exception as e:
logger.warning(f" CatCompta erreur: {e}")
# ============================================================
# ÉTAPE 7 : TAUX
# ============================================================
taux_modifies = False
for i in range(1, 5):
key = f"taux{i:02d}"
if key in client_data:
if not taux_modifies:
logger.info("[ETAPE 7] TAUX")
taux_modifies = True
val = safe_float(client_data[key])
if try_set_attribute(client, f"CT_Taux{i:02d}", val):
champs_modifies.append(key)
# ============================================================
# ÉTAPE 8 : STATISTIQUES
# ============================================================
stat_keys = ["statistique01", "secteur"] + [f"statistique{i:02d}" for i in range(2, 11)]
stat_modifies = False
stat01 = client_data.get("statistique01") or client_data.get("secteur")
if stat01:
if not stat_modifies:
logger.info("[ETAPE 8] STATISTIQUES")
stat_modifies = True
if try_set_attribute(client, "CT_Statistique01", clean_str(stat01, 21)):
champs_modifies.append("statistique01")
for i in range(2, 11):
key = f"statistique{i:02d}"
if key in client_data:
if not stat_modifies:
logger.info("[ETAPE 8] STATISTIQUES")
stat_modifies = True
if try_set_attribute(client, f"CT_Statistique{i:02d}", clean_str(client_data[key], 21)):
champs_modifies.append(key)
# ============================================================
# ÉTAPE 9 : COMMERCIAL
# ============================================================
commercial_keys = ["encours_autorise", "assurance_credit", "langue", "commercial_code"]
if any(k in client_data for k in commercial_keys):
logger.info("[ETAPE 9] COMMERCIAL")
if "encours_autorise" in client_data:
if try_set_attribute(client, "CT_Encours", safe_float(client_data["encours_autorise"])):
champs_modifies.append("encours_autorise")
if "assurance_credit" in client_data:
if try_set_attribute(client, "CT_Assurance", safe_float(client_data["assurance_credit"])):
champs_modifies.append("assurance_credit")
if "langue" in client_data:
if try_set_attribute(client, "CT_Langue", safe_int(client_data["langue"])):
champs_modifies.append("langue")
if "commercial_code" in client_data:
co_no = safe_int(client_data["commercial_code"])
if not try_set_attribute(client, "CO_No", co_no):
try:
factory_collab = self.cial.CptaApplication.FactoryCollaborateur
persist_collab = factory_collab.ReadIntitule(str(co_no))
if persist_collab:
collab_obj = win32com.client.CastTo(persist_collab, "IBOCollaborateur3")
collab_obj.Read()
client.Collaborateur = collab_obj
champs_modifies.append("commercial_code")
logger.info(f" Collaborateur = {co_no} [OK]")
except Exception as e:
logger.warning(f" Collaborateur erreur: {e}")
# ============================================================
# ÉTAPE 10 : FACTURATION
# ============================================================
facturation_keys = [
"lettrage_auto", "est_actif", "type_facture", "est_prospect",
"bl_en_facture", "saut_page", "validation_echeance", "controle_encours",
"exclure_relance", "exclure_penalites", "bon_a_payer"
]
if any(k in client_data for k in facturation_keys):
logger.info("[ETAPE 10] FACTURATION")
if "lettrage_auto" in client_data:
if try_set_attribute(client, "CT_Lettrage", 1 if client_data["lettrage_auto"] else 0):
champs_modifies.append("lettrage_auto")
if "est_actif" in client_data:
if try_set_attribute(client, "CT_Sommeil", 0 if client_data["est_actif"] else 1):
champs_modifies.append("est_actif")
if "type_facture" in client_data:
if try_set_attribute(client, "CT_Facture", safe_int(client_data["type_facture"])):
champs_modifies.append("type_facture")
if "est_prospect" in client_data:
if try_set_attribute(client, "CT_Prospect", 1 if client_data["est_prospect"] else 0):
champs_modifies.append("est_prospect")
factu_map = {
"CT_BLFact": "bl_en_facture",
"CT_Saut": "saut_page",
"CT_ValidEch": "validation_echeance",
"CT_ControlEnc": "controle_encours",
"CT_NotRappel": "exclure_relance",
"CT_NotPenal": "exclure_penalites",
"CT_BonAPayer": "bon_a_payer",
}
for attr, key in factu_map.items():
if key in client_data:
if try_set_attribute(client, attr, safe_int(client_data[key])):
champs_modifies.append(key)
# ============================================================
# ÉTAPE 11 : LOGISTIQUE
# ============================================================
logistique_keys = ["priorite_livraison", "livraison_partielle", "delai_transport", "delai_appro"]
if any(k in client_data for k in logistique_keys):
logger.info("[ETAPE 11] LOGISTIQUE")
logistique_map = {
"CT_PrioriteLivr": "priorite_livraison",
"CT_LivrPartielle": "livraison_partielle",
"CT_DelaiTransport": "delai_transport",
"CT_DelaiAppro": "delai_appro",
}
for attr, key in logistique_map.items():
if key in client_data:
if try_set_attribute(client, attr, safe_int(client_data[key])):
champs_modifies.append(key)
# ============================================================
# ÉTAPE 12 : COMMENTAIRE
# ============================================================
if "commentaire" in client_data:
logger.info("[ETAPE 12] COMMENTAIRE")
if try_set_attribute(client, "CT_Commentaire", clean_str(client_data["commentaire"], 35)):
champs_modifies.append("commentaire")
# ============================================================
# ÉTAPE 13 : ANALYTIQUE
# ============================================================
if "section_analytique" in client_data:
logger.info("[ETAPE 13] ANALYTIQUE")
if try_set_attribute(client, "CA_Num", clean_str(client_data["section_analytique"], 13)):
champs_modifies.append("section_analytique")
# ============================================================
# ÉTAPE 14 : ORGANISATION & SURVEILLANCE
# ============================================================
organisation_keys = [
"mode_reglement_code", "surveillance_active", "coface",
"forme_juridique", "effectif", "sv_regularite", "sv_cotation",
"sv_objet_maj", "ca_annuel", "sv_chiffre_affaires", "sv_resultat"
]
if any(k in client_data for k in organisation_keys):
logger.info("[ETAPE 14] ORGANISATION & SURVEILLANCE")
# Mode règlement
if "mode_reglement_code" in client_data:
mr_no = safe_int(client_data["mode_reglement_code"])
if not try_set_attribute(client, "MR_No", mr_no):
try:
factory_mr = self.cial.CptaApplication.FactoryModeRegl
persist_mr = factory_mr.ReadIntitule(str(mr_no))
if persist_mr:
mr_obj = win32com.client.CastTo(persist_mr, "IBOModeRegl3")
mr_obj.Read()
client.ModeRegl = mr_obj
champs_modifies.append("mode_reglement_code")
logger.info(f" ModeRegl = {mr_no} [OK]")
except Exception as e:
logger.warning(f" ModeRegl erreur: {e}")
# Surveillance - DOIT être défini AVANT Coface
if "surveillance_active" in client_data:
surveillance = 1 if client_data["surveillance_active"] else 0
try:
client.CT_Surveillance = surveillance
champs_modifies.append("surveillance_active")
logger.info(f" CT_Surveillance = {surveillance} [OK]")
except Exception as e:
logger.warning(f" CT_Surveillance [ECHEC: {e}]")
# Coface
if "coface" in client_data:
coface = clean_str(client_data["coface"], 25)
try:
client.CT_Coface = coface
champs_modifies.append("coface")
logger.info(f" CT_Coface = {coface} [OK]")
except Exception as e:
logger.warning(f" CT_Coface [ECHEC: {e}]")
# Autres champs surveillance
if "forme_juridique" in client_data:
if try_set_attribute(client, "CT_SvFormeJuri", clean_str(client_data["forme_juridique"], 33)):
champs_modifies.append("forme_juridique")
if "effectif" in client_data:
if try_set_attribute(client, "CT_SvEffectif", clean_str(client_data["effectif"], 11)):
champs_modifies.append("effectif")
if "sv_regularite" in client_data:
if try_set_attribute(client, "CT_SvRegul", clean_str(client_data["sv_regularite"], 3)):
champs_modifies.append("sv_regularite")
if "sv_cotation" in client_data:
if try_set_attribute(client, "CT_SvCotation", clean_str(client_data["sv_cotation"], 5)):
champs_modifies.append("sv_cotation")
if "sv_objet_maj" in client_data:
if try_set_attribute(client, "CT_SvObjetMaj", clean_str(client_data["sv_objet_maj"], 61)):
champs_modifies.append("sv_objet_maj")
ca = client_data.get("ca_annuel") or client_data.get("sv_chiffre_affaires")
if ca:
if try_set_attribute(client, "CT_SvCA", safe_float(ca)):
champs_modifies.append("ca_annuel/sv_chiffre_affaires")
if "sv_resultat" in client_data:
if try_set_attribute(client, "CT_SvResultat", safe_float(client_data["sv_resultat"])):
champs_modifies.append("sv_resultat")
# ============================================================
# VALIDATION ET WRITE
# ============================================================
if not champs_modifies:
logger.warning("Aucun champ à modifier")
return self._extraire_client(client)
logger.info(f" Champs à modifier: {', '.join(champs_modifies)}")
# ========================================
# ÉTAPE 3 : ÉCRIRE LES MODIFICATIONS
# ========================================
logger.info(" Écriture des modifications...")
logger.info("=" * 80)
logger.info(f"[WRITE] {len(champs_modifies)} champs modifiés:")
for i, champ in enumerate(champs_modifies, 1):
logger.info(f" {i}. {champ}")
logger.info("=" * 80)
try:
client.Write()
logger.info(" Write() réussi !")
client.Read()
logger.info("[OK] Write réussi")
except Exception as e:
error_detail = str(e)
try:
sage_error = self.cial.CptaApplication.LastError
if sage_error:
error_detail = (
f"{sage_error.Description} (Code: {sage_error.Number})"
)
error_detail = f"{sage_error.Description} (Code: {sage_error.Number})"
except:
pass
logger.error(f"[ERREUR] {error_detail}")
raise RuntimeError(f"Echec Write(): {error_detail}")
logger.error(f" Erreur Write(): {error_detail}")
raise RuntimeError(f"Échec modification: {error_detail}")
# ========================================
# ÉTAPE 4 : RELIRE ET RETOURNER
# ========================================
client.Read()
logger.info(
f" CLIENT MODIFIÉ: {code} ({len(champs_modifies)} champs) "
)
# Refresh cache
logger.info("=" * 80)
logger.info(f"[SUCCES] CLIENT MODIFIÉ: {code} ({len(champs_modifies)} champs)")
logger.info("=" * 80)
return self._extraire_client(client)
except ValueError as e:
logger.error(f" Erreur métier: {e}")
logger.error(f"[ERREUR VALIDATION] {e}")
raise
except Exception as e:
logger.error(f" Erreur modification client: {e}", exc_info=True)
error_message = str(e)
if self.cial:
try:
err = self.cial.CptaApplication.LastError
if err:
error_message = f"Erreur Sage: {err.Description}"
except:
pass
raise RuntimeError(f"Erreur technique Sage: {error_message}")
logger.error(f"[ERREUR] {e}", exc_info=True)
raise RuntimeError(f"Erreur technique: {e}")
def creer_commande_enrichi(self, commande_data: dict) -> Dict:
"""
Crée une commande dans Sage avec support des dates.