From 9d4f620bdc2d4dcb3bd89cfae917800bdcd79ffa Mon Sep 17 00:00:00 2001 From: Fanilo-Nantenaina Date: Sat, 6 Dec 2025 08:01:50 +0300 Subject: [PATCH] Truncating too long fields --- sage_connector.py | 160 ++++++++++++++++++++++++---------------------- 1 file changed, 82 insertions(+), 78 deletions(-) diff --git a/sage_connector.py b/sage_connector.py index 3812b26..42dc660 100644 --- a/sage_connector.py +++ b/sage_connector.py @@ -2378,69 +2378,54 @@ class SageConnector: def creer_client(self, client_data: Dict) -> Dict: """ Crée un nouveau client dans Sage 100c via l'API COM. - ✅ CORRECTION : CT_Type est en lecture seule, défini automatiquement par la factory + ✅ Gestion stricte des longueurs de champs """ if not self.cial: raise RuntimeError("Connexion Sage non établie") try: with self._com_context(), self._lock_com: - # 1. Accès à la Factory Client (définit automatiquement CT_Type = 0) + # 1. Factory Client factory_client = self.cial.CptaApplication.FactoryClient - - # Create() retourne IBIPersistObject persist = factory_client.Create() if not persist: raise RuntimeError("Factory.Create() a retourné None") - logger.debug(f"📦 Objet IBIPersistObject créé") - # Cast vers IBOClient3 - try: - client = win32com.client.CastTo(persist, "IBOClient3") - logger.debug(f"✅ Cast réussi vers IBOClient3") - except Exception as e: - raise RuntimeError(f"Impossible de caster vers IBOClient3: {e}") + client = win32com.client.CastTo(persist, "IBOClient3") + logger.debug(f"✅ Cast réussi vers IBOClient3") - # Vérifier que CT_Type est bien défini automatiquement - try: - ct_type_auto = getattr(client, "CT_Type", None) - logger.debug(f"✅ CT_Type défini automatiquement par factory: {ct_type_auto}") - except Exception as e: - logger.warning(f"⚠️ Impossible de lire CT_Type: {e}") - - # 2. Remplissage des Champs OBLIGATOIRES + # 2. Remplissage avec VALIDATION DES LONGUEURS logger.info(f"📝 Création client: {client_data['intitule']}") - # CT_Intitule - OBLIGATOIRE + # ===== CT_Intitule - OBLIGATOIRE (max 69 car) ===== + intitule = client_data["intitule"][:69] # Tronquer si nécessaire try: - client.CT_Intitule = client_data["intitule"] - logger.debug(f"✅ CT_Intitule défini: {client_data['intitule']}") + client.CT_Intitule = intitule + logger.debug(f"✅ CT_Intitule: {intitule}") except Exception as e: raise RuntimeError(f"Impossible de définir CT_Intitule: {e}") - # ❌ CT_Type - NE PAS DÉFINIR (lecture seule, auto-défini par factory) - # La FactoryClient définit automatiquement CT_Type = 0 - - # CT_Num (Numéro de compte) - Optionnel si auto-numérotation + # ===== CT_Num - Optionnel (max 17 car, ALPHA MAJ) ===== num_prop = client_data.get("num", "") if num_prop: + num_prop = num_prop.upper()[:17] # Majuscules + tronquer try: - client.CT_Num = num_prop.upper() - logger.debug(f"✅ CT_Num défini: {num_prop.upper()}") + client.CT_Num = num_prop + logger.debug(f"✅ CT_Num: {num_prop}") except Exception as e: - logger.warning(f"⚠️ Impossible de définir CT_Num: {e}") + logger.warning(f"⚠️ CT_Num non défini: {e}") - # Compte comptable général + # ===== CT_CompteG - Compte collectif (max 13 car) ===== + compte = client_data.get("compte_collectif", "411000")[:13] try: - if client_data.get("compte_collectif"): - client.CT_CompteG = client_data["compte_collectif"] - logger.debug(f"✅ CT_CompteG: {client_data['compte_collectif']}") + client.CT_CompteG = compte + logger.debug(f"✅ CT_CompteG: {compte}") except Exception as e: - logger.warning(f"⚠️ Impossible de définir CT_CompteG: {e}") + logger.warning(f"⚠️ CT_CompteG non défini: {e}") - # Initialisation des champs catégoriels (si disponibles) + # ===== Catégories (valeurs numériques, pas de pb de longueur) ===== try: client.N_CatTarif = 1 client.N_CatCompta = 1 @@ -2450,84 +2435,104 @@ class SageConnector: client.N_Risque = 1 logger.debug("✅ Catégories initialisées") except Exception as e: - logger.warning(f"⚠️ Catégories non disponibles: {e}") + logger.warning(f"⚠️ Catégories: {e}") - # CT_NumPayeur - Géré après Write() si auto-numérotation + # ===== CT_NumPayeur (max 17 car) ===== if num_prop: try: - client.CT_NumPayeur = num_prop.upper() - logger.debug(f"✅ CT_NumPayeur: {num_prop.upper()}") + client.CT_NumPayeur = num_prop # Déjà tronqué plus haut + logger.debug(f"✅ CT_NumPayeur: {num_prop}") except Exception as e: - logger.warning(f"⚠️ Impossible de définir CT_NumPayeur: {e}") + logger.warning(f"⚠️ CT_NumPayeur: {e}") - # 3. CHAMPS OPTIONNELS - - # --- Adresse principale --- + # ===== ADRESSE (tous max 35 car sauf code postal = 9) ===== try: adresse_obj = getattr(client, "Adresse", None) if adresse_obj: - if client_data.get("adresse"): - adresse_obj.Adresse = client_data["adresse"] - if client_data.get("code_postal"): - adresse_obj.CodePostal = client_data["code_postal"] - if client_data.get("ville"): - adresse_obj.Ville = client_data["ville"] - if client_data.get("pays"): - adresse_obj.Pays = client_data["pays"] + if client_data.get("adresse"): + adresse_obj.Adresse = client_data["adresse"][:35] + if client_data.get("code_postal"): + adresse_obj.CodePostal = client_data["code_postal"][:9] + if client_data.get("ville"): + adresse_obj.Ville = client_data["ville"][:35] + if client_data.get("pays"): + adresse_obj.Pays = client_data["pays"][:35] logger.debug("✅ Adresse définie") except Exception as e: - logger.warning(f"⚠️ Impossible de définir l'adresse: {e}") + logger.warning(f"⚠️ Adresse: {e}") - # --- Contact / Télécom --- + # ===== TELECOM (Téléphone max 21, Email max 69) ===== try: telecom_obj = getattr(client, "Telecom", None) if telecom_obj: - if client_data.get("telephone"): - telecom_obj.Telephone = client_data["telephone"] - if client_data.get("email"): - telecom_obj.EMail = client_data["email"] + if client_data.get("telephone"): + telecom_obj.Telephone = client_data["telephone"][:21] + if client_data.get("email"): + telecom_obj.EMail = client_data["email"][:69] logger.debug("✅ Télécom défini") except Exception as e: - logger.warning(f"⚠️ Impossible de définir les télécoms: {e}") + logger.warning(f"⚠️ Télécom: {e}") - # --- Identifiants --- + # ===== IDENTIFIANTS (SIRET max 14, TVA max 25) ===== try: - if client_data.get("siret"): - client.CT_Siret = client_data["siret"] - logger.debug(f"✅ SIRET: {client_data['siret']}") - if client_data.get("tva_intra"): - client.CT_Identifiant = client_data["tva_intra"] - logger.debug(f"✅ TVA: {client_data['tva_intra']}") + if client_data.get("siret"): + client.CT_Siret = client_data["siret"][:14] + logger.debug(f"✅ SIRET: {client_data['siret'][:14]}") + if client_data.get("tva_intra"): + client.CT_Identifiant = client_data["tva_intra"][:25] + logger.debug(f"✅ TVA: {client_data['tva_intra'][:25]}") except Exception as e: - logger.warning(f"⚠️ Impossible de définir les identifiants: {e}") + logger.warning(f"⚠️ Identifiants: {e}") + + # 3. DIAGNOSTIC PRÉ-WRITE (pour debug) + logger.info("🔍 === DIAGNOSTIC PRÉ-WRITE ===") + try: + logger.info(f" CT_Intitule: '{client.CT_Intitule}' (len={len(client.CT_Intitule)})") + logger.info(f" CT_Num: '{getattr(client, 'CT_Num', 'AUTO')}'") + logger.info(f" CT_CompteG: '{getattr(client, 'CT_CompteG', 'N/A')}'") + logger.info(f" CT_Type: {getattr(client, 'CT_Type', '?')}") + except Exception as e: + logger.warning(f"⚠️ Erreur diagnostic: {e}") # 4. Écriture en base logger.info("💾 Écriture du client dans Sage...") try: client.Write() - logger.debug("✅ Write() réussi") + logger.info("✅ Write() réussi !") except Exception as e: - # Récupérer l'erreur Sage détaillée si disponible + # Récupérer erreur Sage détaillé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})" + logger.error(f"❌ Erreur Sage détaillée: {error_detail}") except: pass + + # Log des valeurs pour debug + logger.error("❌ ÉCHEC Write() - Dump des champs définis:") + for attr in dir(client): + if attr.startswith("CT_") or attr.startswith("N_"): + try: + val = getattr(client, attr, None) + if val and not callable(val): + logger.error(f" {attr}: {val} (len={len(str(val))})") + except: + pass + raise RuntimeError(f"Échec Write(): {error_detail}") # 5. Relire pour récupérer le numéro auto-généré try: client.Read() - logger.debug("✅ Read() après Write() réussi") except Exception as e: - logger.warning(f"⚠️ Impossible de relire après Write(): {e}") + logger.warning(f"⚠️ Impossible de relire: {e}") num_final = getattr(client, "CT_Num", "") if not num_final: - raise RuntimeError("CT_Num vide après Write() - création échouée") + raise RuntimeError("CT_Num vide après Write()") # Si CT_NumPayeur était vide, le définir maintenant if not num_prop: @@ -2536,18 +2541,18 @@ class SageConnector: client.Write() logger.debug(f"✅ CT_NumPayeur auto-défini: {num_final}") except Exception as e: - logger.warning(f"⚠️ Impossible de définir CT_NumPayeur après création: {e}") + logger.warning(f"⚠️ CT_NumPayeur: {e}") - logger.info(f"✅✅✅ Client créé avec succès: {num_final} - {client.CT_Intitule} ✅✅✅") + logger.info(f"✅✅✅ CLIENT CRÉÉ: {num_final} - {client.CT_Intitule} ✅✅✅") - # 6. Forcer le refresh du cache + # 6. Refresh cache self._refresh_cache_clients() return { "numero": num_final, "intitule": client.CT_Intitule, "compte_collectif": getattr(client, "CT_CompteG", ""), - "type": getattr(client, "CT_Type", 0), # Retourner le type auto-défini + "type": getattr(client, "CT_Type", 0), "adresse": client_data.get("adresse"), "ville": client_data.get("ville"), "email": client_data.get("email") @@ -2556,7 +2561,6 @@ class SageConnector: except Exception as e: logger.error(f"❌ Erreur création client: {e}", exc_info=True) - # Gestion d'erreur avec détails Sage error_message = str(e) if self.cial: try: @@ -2564,9 +2568,9 @@ class SageConnector: if err: error_message = f"Erreur Sage: {err.Description}" if "doublon" in err.Description.lower() or "existe" in err.Description.lower(): - raise ValueError(f"Ce client existe déjà dans Sage. {error_message}") + raise ValueError(f"Ce client existe déjà. {error_message}") except ValueError: - raise # Re-lever les ValueError pour différencier erreurs métier vs techniques + raise except: pass