diff --git a/cleaner.py b/cleaner.py index a0a3b1b..3476145 100644 --- a/cleaner.py +++ b/cleaner.py @@ -14,4 +14,4 @@ def supprimer_commentaires_ligne(fichier: str) -> None: if __name__ == "__main__": - supprimer_commentaires_ligne("api.py") + supprimer_commentaires_ligne("sage_connector.py") diff --git a/sage_connector.py b/sage_connector.py index e4c919d..f21b8a8 100644 --- a/sage_connector.py +++ b/sage_connector.py @@ -136,16 +136,64 @@ class SageConnector: pass def lister_tous_fournisseurs(self, filtre=""): + """ + Liste tous les fournisseurs avec TOUS les champs + Symétrie complète avec lister_tous_clients + """ try: with self._get_sql_connection() as conn: cursor = conn.cursor() query = """ SELECT + -- IDENTIFICATION (9) CT_Num, CT_Intitule, CT_Type, CT_Qualite, - CT_Adresse, CT_Ville, CT_CodePostal, CT_Pays, - CT_Telephone, CT_EMail, CT_Siret, CT_Identifiant, - CT_Sommeil, CT_Contact + CT_Classement, CT_Raccourci, CT_Siret, CT_Identifiant, + CT_Ape, + + -- ADRESSE (7) + CT_Contact, CT_Adresse, CT_Complement, + CT_CodePostal, CT_Ville, CT_CodeRegion, CT_Pays, + + -- TELECOM (6) + CT_Telephone, CT_Telecopie, CT_EMail, CT_Site, + CT_Facebook, CT_LinkedIn, + + -- TAUX (4) + CT_Taux01, CT_Taux02, CT_Taux03, CT_Taux04, + + -- STATISTIQUES (10) + CT_Statistique01, CT_Statistique02, CT_Statistique03, + CT_Statistique04, CT_Statistique05, CT_Statistique06, + CT_Statistique07, CT_Statistique08, CT_Statistique09, + CT_Statistique10, + + -- COMMERCIAL (4) + CT_Encours, CT_Assurance, CT_Langue, CO_No, + + -- FACTURATION (11) + CT_Lettrage, CT_Sommeil, CT_Facture, CT_Prospect, + CT_BLFact, CT_Saut, CT_ValidEch, CT_ControlEnc, + CT_NotRappel, CT_NotPenal, CT_BonAPayer, + + -- LOGISTIQUE (4) + CT_PrioriteLivr, CT_LivrPartielle, + CT_DelaiTransport, CT_DelaiAppro, + + -- COMMENTAIRE (1) + CT_Commentaire, + + -- ANALYTIQUE (1) + CA_Num, + + -- ORGANISATION / SURVEILLANCE (10) + MR_No, CT_Surveillance, CT_Coface, + CT_SvFormeJuri, CT_SvEffectif, CT_SvRegul, + CT_SvCotation, CT_SvObjetMaj, CT_SvCA, CT_SvResultat, + + -- COMPTE GENERAL ET CATEGORIES (3) + CG_NumPrinc, N_CatTarif, N_CatCompta + FROM F_COMPTET WHERE CT_Type = 1 """ @@ -163,83 +211,268 @@ class SageConnector: fournisseurs = [] for row in rows: - fournisseurs.append( - { - "numero": self._safe_strip(row.CT_Num), - "intitule": self._safe_strip(row.CT_Intitule), - "type": 1, # Fournisseur - "est_fournisseur": True, - "qualite": self._safe_strip(row.CT_Qualite), - "adresse": self._safe_strip(row.CT_Adresse), - "ville": self._safe_strip(row.CT_Ville), - "code_postal": self._safe_strip(row.CT_CodePostal), - "pays": self._safe_strip(row.CT_Pays), - "telephone": self._safe_strip(row.CT_Telephone), - "email": self._safe_strip(row.CT_EMail), - "siret": self._safe_strip(row.CT_Siret), - "tva_intra": self._safe_strip(row.CT_Identifiant), - "est_actif": (row.CT_Sommeil == 0), - "contact": self._safe_strip(row.CT_Contact), - } - ) + fournisseur = { + "numero": self._safe_strip(row.CT_Num), + "intitule": self._safe_strip(row.CT_Intitule), + "type_tiers": row.CT_Type, + "qualite": self._safe_strip(row.CT_Qualite), + "classement": self._safe_strip(row.CT_Classement), + "raccourci": self._safe_strip(row.CT_Raccourci), + "siret": self._safe_strip(row.CT_Siret), + "tva_intra": self._safe_strip(row.CT_Identifiant), + "code_naf": self._safe_strip(row.CT_Ape), + + "contact": self._safe_strip(row.CT_Contact), + "adresse": self._safe_strip(row.CT_Adresse), + "complement": self._safe_strip(row.CT_Complement), + "code_postal": self._safe_strip(row.CT_CodePostal), + "ville": self._safe_strip(row.CT_Ville), + "region": self._safe_strip(row.CT_CodeRegion), + "pays": self._safe_strip(row.CT_Pays), + + "telephone": self._safe_strip(row.CT_Telephone), + "telecopie": self._safe_strip(row.CT_Telecopie), + "email": self._safe_strip(row.CT_EMail), + "site_web": self._safe_strip(row.CT_Site), + "facebook": self._safe_strip(row.CT_Facebook), + "linkedin": self._safe_strip(row.CT_LinkedIn), + + "taux01": row.CT_Taux01, + "taux02": row.CT_Taux02, + "taux03": row.CT_Taux03, + "taux04": row.CT_Taux04, + + "statistique01": self._safe_strip(row.CT_Statistique01), + "statistique02": self._safe_strip(row.CT_Statistique02), + "statistique03": self._safe_strip(row.CT_Statistique03), + "statistique04": self._safe_strip(row.CT_Statistique04), + "statistique05": self._safe_strip(row.CT_Statistique05), + "statistique06": self._safe_strip(row.CT_Statistique06), + "statistique07": self._safe_strip(row.CT_Statistique07), + "statistique08": self._safe_strip(row.CT_Statistique08), + "statistique09": self._safe_strip(row.CT_Statistique09), + "statistique10": self._safe_strip(row.CT_Statistique10), + + "encours_autorise": row.CT_Encours, + "assurance_credit": row.CT_Assurance, + "langue": row.CT_Langue, + "commercial_code": row.CO_No, + + "lettrage_auto": (row.CT_Lettrage == 1), + "est_actif": (row.CT_Sommeil == 0), + "type_facture": row.CT_Facture, + "est_prospect": (row.CT_Prospect == 1), + "bl_en_facture": row.CT_BLFact, + "saut_page": row.CT_Saut, + "validation_echeance": row.CT_ValidEch, + "controle_encours": row.CT_ControlEnc, + "exclure_relance": (row.CT_NotRappel == 1), + "exclure_penalites": (row.CT_NotPenal == 1), + "bon_a_payer": row.CT_BonAPayer, + + "priorite_livraison": row.CT_PrioriteLivr, + "livraison_partielle": row.CT_LivrPartielle, + "delai_transport": row.CT_DelaiTransport, + "delai_appro": row.CT_DelaiAppro, + + "commentaire": self._safe_strip(row.CT_Commentaire), + + "section_analytique": self._safe_strip(row.CA_Num), + + "mode_reglement_code": row.MR_No, + "surveillance_active": (row.CT_Surveillance == 1), + "coface": self._safe_strip(row.CT_Coface), + "forme_juridique": self._safe_strip(row.CT_SvFormeJuri), + "effectif": self._safe_strip(row.CT_SvEffectif), + "sv_regularite": self._safe_strip(row.CT_SvRegul), + "sv_cotation": self._safe_strip(row.CT_SvCotation), + "sv_objet_maj": self._safe_strip(row.CT_SvObjetMaj), + "sv_chiffre_affaires": row.CT_SvCA, + "sv_resultat": row.CT_SvResultat, + + "compte_general": self._safe_strip(row.CG_NumPrinc), + "categorie_tarif": row.N_CatTarif, + "categorie_compta": row.N_CatCompta, + } - logger.info(f" SQL: {len(fournisseurs)} fournisseurs") + # Récupérer les contacts (réutiliser la même méthode) + fournisseur["contacts"] = self._get_contacts_client(row.CT_Num, conn) + + fournisseurs.append(fournisseur) + + logger.info(f"✅ SQL: {len(fournisseurs)} fournisseurs avec {len(fournisseur)} champs") return fournisseurs except Exception as e: - logger.error(f" Erreur SQL fournisseurs: {e}") - return [] + logger.error(f"❌ Erreur SQL fournisseurs: {e}") + raise RuntimeError(f"Erreur lecture fournisseurs: {str(e)}") - def lire_fournisseur(self, code): + + def lire_fournisseur(self, code_fournisseur): + """ + Lit un fournisseur avec TOUS les champs (identique à lister_tous_fournisseurs) + Symétrie complète GET/POST + """ try: with self._get_sql_connection() as conn: cursor = conn.cursor() - cursor.execute( - """ + query = """ SELECT + -- IDENTIFICATION (9) CT_Num, CT_Intitule, CT_Type, CT_Qualite, - CT_Adresse, CT_Complement, CT_Ville, CT_CodePostal, CT_Pays, - CT_Telephone, CT_Portable, CT_EMail, CT_Telecopie, - CT_Siret, CT_Identifiant, CT_Sommeil, - CT_Contact, CT_FormeJuridique + CT_Classement, CT_Raccourci, CT_Siret, CT_Identifiant, + CT_Ape, + + -- ADRESSE (7) + CT_Contact, CT_Adresse, CT_Complement, + CT_CodePostal, CT_Ville, CT_CodeRegion, CT_Pays, + + -- TELECOM (6) + CT_Telephone, CT_Telecopie, CT_EMail, CT_Site, + CT_Facebook, CT_LinkedIn, + + -- TAUX (4) + CT_Taux01, CT_Taux02, CT_Taux03, CT_Taux04, + + -- STATISTIQUES (10) + CT_Statistique01, CT_Statistique02, CT_Statistique03, + CT_Statistique04, CT_Statistique05, CT_Statistique06, + CT_Statistique07, CT_Statistique08, CT_Statistique09, + CT_Statistique10, + + -- COMMERCIAL (4) + CT_Encours, CT_Assurance, CT_Langue, CO_No, + + -- FACTURATION (11) + CT_Lettrage, CT_Sommeil, CT_Facture, CT_Prospect, + CT_BLFact, CT_Saut, CT_ValidEch, CT_ControlEnc, + CT_NotRappel, CT_NotPenal, CT_BonAPayer, + + -- LOGISTIQUE (4) + CT_PrioriteLivr, CT_LivrPartielle, + CT_DelaiTransport, CT_DelaiAppro, + + -- COMMENTAIRE (1) + CT_Commentaire, + + -- ANALYTIQUE (1) + CA_Num, + + -- ORGANISATION / SURVEILLANCE (10) + MR_No, CT_Surveillance, CT_Coface, + CT_SvFormeJuri, CT_SvEffectif, CT_SvRegul, + CT_SvCotation, CT_SvObjetMaj, CT_SvCA, CT_SvResultat, + + -- COMPTE GENERAL ET CATEGORIES (3) + CG_NumPrinc, N_CatTarif, N_CatCompta + FROM F_COMPTET WHERE CT_Num = ? AND CT_Type = 1 - """, - (code.upper(),), - ) + """ + cursor.execute(query, (code_fournisseur.upper(),)) row = cursor.fetchone() if not row: return None - return { + fournisseur = { "numero": self._safe_strip(row.CT_Num), "intitule": self._safe_strip(row.CT_Intitule), - "type": 1, - "est_fournisseur": True, + "type_tiers": row.CT_Type, "qualite": self._safe_strip(row.CT_Qualite), - "adresse": self._safe_strip(row.CT_Adresse), - "complement": self._safe_strip(row.CT_Complement), - "ville": self._safe_strip(row.CT_Ville), - "code_postal": self._safe_strip(row.CT_CodePostal), - "pays": self._safe_strip(row.CT_Pays), - "telephone": self._safe_strip(row.CT_Telephone), - "portable": self._safe_strip(row.CT_Portable), - "email": self._safe_strip(row.CT_EMail), - "telecopie": self._safe_strip(row.CT_Telecopie), + "classement": self._safe_strip(row.CT_Classement), + "raccourci": self._safe_strip(row.CT_Raccourci), "siret": self._safe_strip(row.CT_Siret), "tva_intra": self._safe_strip(row.CT_Identifiant), - "est_actif": (row.CT_Sommeil == 0), + "code_naf": self._safe_strip(row.CT_Ape), + "contact": self._safe_strip(row.CT_Contact), - "forme_juridique": self._safe_strip(row.CT_FormeJuridique), + "adresse": self._safe_strip(row.CT_Adresse), + "complement": self._safe_strip(row.CT_Complement), + "code_postal": self._safe_strip(row.CT_CodePostal), + "ville": self._safe_strip(row.CT_Ville), + "region": self._safe_strip(row.CT_CodeRegion), + "pays": self._safe_strip(row.CT_Pays), + + "telephone": self._safe_strip(row.CT_Telephone), + "telecopie": self._safe_strip(row.CT_Telecopie), + "email": self._safe_strip(row.CT_EMail), + "site_web": self._safe_strip(row.CT_Site), + "facebook": self._safe_strip(row.CT_Facebook), + "linkedin": self._safe_strip(row.CT_LinkedIn), + + "taux01": row.CT_Taux01, + "taux02": row.CT_Taux02, + "taux03": row.CT_Taux03, + "taux04": row.CT_Taux04, + + "statistique01": self._safe_strip(row.CT_Statistique01), + "statistique02": self._safe_strip(row.CT_Statistique02), + "statistique03": self._safe_strip(row.CT_Statistique03), + "statistique04": self._safe_strip(row.CT_Statistique04), + "statistique05": self._safe_strip(row.CT_Statistique05), + "statistique06": self._safe_strip(row.CT_Statistique06), + "statistique07": self._safe_strip(row.CT_Statistique07), + "statistique08": self._safe_strip(row.CT_Statistique08), + "statistique09": self._safe_strip(row.CT_Statistique09), + "statistique10": self._safe_strip(row.CT_Statistique10), + + "encours_autorise": row.CT_Encours, + "assurance_credit": row.CT_Assurance, + "langue": row.CT_Langue, + "commercial_code": row.CO_No, + + "lettrage_auto": (row.CT_Lettrage == 1), + "est_actif": (row.CT_Sommeil == 0), + "type_facture": row.CT_Facture, + "est_prospect": (row.CT_Prospect == 1), + "bl_en_facture": row.CT_BLFact, + "saut_page": row.CT_Saut, + "validation_echeance": row.CT_ValidEch, + "controle_encours": row.CT_ControlEnc, + "exclure_relance": (row.CT_NotRappel == 1), + "exclure_penalites": (row.CT_NotPenal == 1), + "bon_a_payer": row.CT_BonAPayer, + + "priorite_livraison": row.CT_PrioriteLivr, + "livraison_partielle": row.CT_LivrPartielle, + "delai_transport": row.CT_DelaiTransport, + "delai_appro": row.CT_DelaiAppro, + + "commentaire": self._safe_strip(row.CT_Commentaire), + + "section_analytique": self._safe_strip(row.CA_Num), + + "mode_reglement_code": row.MR_No, + "surveillance_active": (row.CT_Surveillance == 1), + "coface": self._safe_strip(row.CT_Coface), + "forme_juridique": self._safe_strip(row.CT_SvFormeJuri), + "effectif": self._safe_strip(row.CT_SvEffectif), + "sv_regularite": self._safe_strip(row.CT_SvRegul), + "sv_cotation": self._safe_strip(row.CT_SvCotation), + "sv_objet_maj": self._safe_strip(row.CT_SvObjetMaj), + "sv_chiffre_affaires": row.CT_SvCA, + "sv_resultat": row.CT_SvResultat, + + "compte_general": self._safe_strip(row.CG_NumPrinc), + "categorie_tarif": row.N_CatTarif, + "categorie_compta": row.N_CatCompta, } + # Récupérer les contacts + fournisseur["contacts"] = self._get_contacts_client(row.CT_Num, conn) + + logger.info(f"✅ SQL: Fournisseur {code_fournisseur} avec {len(fournisseur)} champs") + return fournisseur + except Exception as e: - logger.error(f" Erreur SQL fournisseur {code}: {e}") + logger.error(f"❌ Erreur SQL fournisseur {code_fournisseur}: {e}") return None + + def creer_fournisseur(self, fournisseur_data: Dict) -> Dict: if not self.cial: raise RuntimeError("Connexion Sage non établie") @@ -686,6 +919,76 @@ class SageConnector: raise RuntimeError(f"Erreur technique Sage: {error_message}") + + def _get_contacts_client(self, ct_num: str, conn) -> list: + """ + Récupère tous les contacts d'un client avec TOUS les champs + """ + try: + cursor = conn.cursor() + + query = """ + SELECT + -- IDENTIFICATION + CT_Num, CT_No, N_Contact, + + -- IDENTITÉ + CT_Civilite, CT_Nom, CT_Prenom, CT_Fonction, + + -- ORGANISATION + N_Service, + + -- COORDONNÉES + CT_Telephone, CT_TelPortable, CT_Telecopie, CT_EMail, + + -- RÉSEAUX SOCIAUX + CT_Facebook, CT_LinkedIn, CT_Skype + + FROM F_CONTACTT + WHERE CT_Num = ? + ORDER BY N_Contact, CT_Nom, CT_Prenom + """ + + cursor.execute(query, [ct_num]) + rows = cursor.fetchall() + + contacts = [] + for row in rows: + contact = { + # IDENTIFICATION + "ct_num": self._safe_strip(row.CT_Num), + "ct_no": row.CT_No, + "n_contact": row.N_Contact, + + # IDENTITÉ + "civilite": self._safe_strip(row.CT_Civilite), + "nom": self._safe_strip(row.CT_Nom), + "prenom": self._safe_strip(row.CT_Prenom), + "fonction": self._safe_strip(row.CT_Fonction), + + # ORGANISATION + "service_code": row.N_Service, + + # COORDONNÉES + "telephone": self._safe_strip(row.CT_Telephone), + "portable": self._safe_strip(row.CT_TelPortable), + "telecopie": self._safe_strip(row.CT_Telecopie), + "email": self._safe_strip(row.CT_EMail), + + # RÉSEAUX SOCIAUX + "facebook": self._safe_strip(row.CT_Facebook), + "linkedin": self._safe_strip(row.CT_LinkedIn), + "skype": self._safe_strip(row.CT_Skype) + } + contacts.append(contact) + + return contacts + + except Exception as e: + logger.warning(f"⚠️ Impossible de récupérer contacts pour {ct_num}: {e}") + return [] + + def lister_tous_clients(self, filtre=""): """ Liste tous les clients avec TOUS les champs gérés par creer_client @@ -845,6 +1148,8 @@ class SageConnector: "categorie_tarif": row.N_CatTarif, "categorie_compta": row.N_CatCompta, } + + client["contacts"] = self._get_contacts_client(row.CT_Num, conn) clients.append(client) @@ -857,49 +1162,168 @@ class SageConnector: def lire_client(self, code_client): + """ + Lit un client avec TOUS les champs (identique à lister_tous_clients) + Symétrie complète GET/POST + """ try: with self._get_sql_connection() as conn: cursor = conn.cursor() - cursor.execute( - """ + query = """ SELECT + -- IDENTIFICATION (8) CT_Num, CT_Intitule, CT_Type, CT_Qualite, - CT_Adresse, CT_Ville, CT_CodePostal, CT_Pays, - CT_Telephone, CT_EMail, CT_Siret, CT_Identifiant, - CT_Sommeil, CT_Prospect, CT_Contact + CT_Classement, CT_Raccourci, CT_Siret, CT_Identifiant, + CT_Ape, + + -- ADRESSE (7) + CT_Contact, CT_Adresse, CT_Complement, + CT_CodePostal, CT_Ville, CT_CodeRegion, CT_Pays, + + -- TELECOM (7) + CT_Telephone, CT_Telecopie, CT_EMail, CT_Site, + CT_Facebook, CT_LinkedIn, + + -- TAUX (4) + CT_Taux01, CT_Taux02, CT_Taux03, CT_Taux04, + + -- STATISTIQUES (10) + CT_Statistique01, CT_Statistique02, CT_Statistique03, + CT_Statistique04, CT_Statistique05, CT_Statistique06, + CT_Statistique07, CT_Statistique08, CT_Statistique09, + CT_Statistique10, + + -- COMMERCIAL (4) + CT_Encours, CT_Assurance, CT_Langue, CO_No, + + -- FACTURATION (11) + CT_Lettrage, CT_Sommeil, CT_Facture, CT_Prospect, + CT_BLFact, CT_Saut, CT_ValidEch, CT_ControlEnc, + CT_NotRappel, CT_NotPenal, CT_BonAPayer, + + -- LOGISTIQUE (4) + CT_PrioriteLivr, CT_LivrPartielle, + CT_DelaiTransport, CT_DelaiAppro, + + -- COMMENTAIRE (1) + CT_Commentaire, + + -- ANALYTIQUE (1) + CA_Num, + + -- ORGANISATION / SURVEILLANCE (10) + MR_No, CT_Surveillance, CT_Coface, + CT_SvFormeJuri, CT_SvEffectif, CT_SvRegul, + CT_SvCotation, CT_SvObjetMaj, CT_SvCA, CT_SvResultat, + + -- COMPTE GENERAL ET CATEGORIES (3) + CG_NumPrinc, N_CatTarif, N_CatCompta + FROM F_COMPTET - WHERE CT_Num = ? - """, - (code_client.upper(),), - ) + WHERE CT_Num = ? AND CT_Type = 0 + """ + cursor.execute(query, (code_client.upper(),)) row = cursor.fetchone() if not row: return None - return { - "numero": self._safe_strip(row[0]), - "intitule": self._safe_strip(row[1]), - "type": row[2], - "qualite": self._safe_strip(row[3]), - "adresse": self._safe_strip(row[4]), - "ville": self._safe_strip(row[5]), - "code_postal": self._safe_strip(row[6]), - "pays": self._safe_strip(row[7]), - "telephone": self._safe_strip(row[8]), - "email": self._safe_strip(row[9]), - "siret": self._safe_strip(row[10]), - "tva_intra": self._safe_strip(row[11]), - "est_actif": (row[12] == 0), - "est_prospect": (row[13] == 1), - "contact": self._safe_strip(row[14]), + client = { + "numero": self._safe_strip(row.CT_Num), + "intitule": self._safe_strip(row.CT_Intitule), + "type_tiers": row.CT_Type, + "qualite": self._safe_strip(row.CT_Qualite), + "classement": self._safe_strip(row.CT_Classement), + "raccourci": self._safe_strip(row.CT_Raccourci), + "siret": self._safe_strip(row.CT_Siret), + "tva_intra": self._safe_strip(row.CT_Identifiant), + "code_naf": self._safe_strip(row.CT_Ape), + + "contact": self._safe_strip(row.CT_Contact), + "adresse": self._safe_strip(row.CT_Adresse), + "complement": self._safe_strip(row.CT_Complement), + "code_postal": self._safe_strip(row.CT_CodePostal), + "ville": self._safe_strip(row.CT_Ville), + "region": self._safe_strip(row.CT_CodeRegion), + "pays": self._safe_strip(row.CT_Pays), + + "telephone": self._safe_strip(row.CT_Telephone), + "telecopie": self._safe_strip(row.CT_Telecopie), + "email": self._safe_strip(row.CT_EMail), + "site_web": self._safe_strip(row.CT_Site), + "facebook": self._safe_strip(row.CT_Facebook), + "linkedin": self._safe_strip(row.CT_LinkedIn), + + "taux01": row.CT_Taux01, + "taux02": row.CT_Taux02, + "taux03": row.CT_Taux03, + "taux04": row.CT_Taux04, + + "statistique01": self._safe_strip(row.CT_Statistique01), + "statistique02": self._safe_strip(row.CT_Statistique02), + "statistique03": self._safe_strip(row.CT_Statistique03), + "statistique04": self._safe_strip(row.CT_Statistique04), + "statistique05": self._safe_strip(row.CT_Statistique05), + "statistique06": self._safe_strip(row.CT_Statistique06), + "statistique07": self._safe_strip(row.CT_Statistique07), + "statistique08": self._safe_strip(row.CT_Statistique08), + "statistique09": self._safe_strip(row.CT_Statistique09), + "statistique10": self._safe_strip(row.CT_Statistique10), + + "encours_autorise": row.CT_Encours, + "assurance_credit": row.CT_Assurance, + "langue": row.CT_Langue, + "commercial_code": row.CO_No, + + "lettrage_auto": (row.CT_Lettrage == 1), + "est_actif": (row.CT_Sommeil == 0), + "type_facture": row.CT_Facture, + "est_prospect": (row.CT_Prospect == 1), + "bl_en_facture": row.CT_BLFact, + "saut_page": row.CT_Saut, + "validation_echeance": row.CT_ValidEch, + "controle_encours": row.CT_ControlEnc, + "exclure_relance": (row.CT_NotRappel == 1), + "exclure_penalites": (row.CT_NotPenal == 1), + "bon_a_payer": row.CT_BonAPayer, + + "priorite_livraison": row.CT_PrioriteLivr, + "livraison_partielle": row.CT_LivrPartielle, + "delai_transport": row.CT_DelaiTransport, + "delai_appro": row.CT_DelaiAppro, + + "commentaire": self._safe_strip(row.CT_Commentaire), + + "section_analytique": self._safe_strip(row.CA_Num), + + "mode_reglement_code": row.MR_No, + "surveillance_active": (row.CT_Surveillance == 1), + "coface": self._safe_strip(row.CT_Coface), + "forme_juridique": self._safe_strip(row.CT_SvFormeJuri), + "effectif": self._safe_strip(row.CT_SvEffectif), + "sv_regularite": self._safe_strip(row.CT_SvRegul), + "sv_cotation": self._safe_strip(row.CT_SvCotation), + "sv_objet_maj": self._safe_strip(row.CT_SvObjetMaj), + "sv_chiffre_affaires": row.CT_SvCA, + "sv_resultat": row.CT_SvResultat, + + "compte_general": self._safe_strip(row.CG_NumPrinc), + "categorie_tarif": row.N_CatTarif, + "categorie_compta": row.N_CatCompta, } + # Récupérer les contacts + client["contacts"] = self._get_contacts_client(row.CT_Num, conn) + + logger.info(f"✅ SQL: Client {code_client} avec {len(client)} champs") + return client + except Exception as e: - logger.error(f" Erreur SQL client {code_client}: {e}") + logger.error(f"❌ Erreur SQL client {code_client}: {e}") return None + def lister_tous_articles(self, filtre="", avec_stock=True): try: