diff --git a/sage_connector.py b/sage_connector.py index c202abd..8b34a3a 100644 --- a/sage_connector.py +++ b/sage_connector.py @@ -76,7 +76,7 @@ from utils.documents.devis.devis_check import ( ) from utils.tiers.contacts.contacts import ( - _get_contacts_client, + _get_contacts, _chercher_contact_en_base, _lire_contact_depuis_base, ) @@ -199,220 +199,54 @@ class SageConnector: pass def lister_tous_fournisseurs(self, filtre=""): + """Liste tous les fournisseurs avec leur commercial""" try: with self._get_sql_connection() as conn: cursor = conn.cursor() - query = """ - SELECT - -- IDENTIFICATION (9) - CT_Num, CT_Intitule, CT_Type, CT_Qualite, - 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 + query = _build_tiers_select_query() + query += """ + FROM F_COMPTET t + LEFT JOIN F_COLLABORATEUR c ON t.CO_No = c.CO_No + WHERE t.CT_Type = 1 """ params = [] - if filtre: - query += " AND (CT_Num LIKE ? OR CT_Intitule LIKE ?)" + query += " AND (t.CT_Num LIKE ? OR t.CT_Intitule LIKE ?)" params.extend([f"%{filtre}%", f"%{filtre}%"]) - query += " ORDER BY CT_Intitule" + query += " ORDER BY t.CT_Intitule" cursor.execute(query, params) rows = cursor.fetchall() fournisseurs = [] for row in rows: - fournisseur = { - "numero": _safe_strip(row.CT_Num), - "intitule": _safe_strip(row.CT_Intitule), - "type_tiers": row.CT_Type, - "qualite": _safe_strip(row.CT_Qualite), - "classement": _safe_strip(row.CT_Classement), - "raccourci": _safe_strip(row.CT_Raccourci), - "siret": _safe_strip(row.CT_Siret), - "tva_intra": _safe_strip(row.CT_Identifiant), - "code_naf": _safe_strip(row.CT_Ape), - "contact": _safe_strip(row.CT_Contact), - "adresse": _safe_strip(row.CT_Adresse), - "complement": _safe_strip(row.CT_Complement), - "code_postal": _safe_strip(row.CT_CodePostal), - "ville": _safe_strip(row.CT_Ville), - "region": _safe_strip(row.CT_CodeRegion), - "pays": _safe_strip(row.CT_Pays), - "telephone": _safe_strip(row.CT_Telephone), - "telecopie": _safe_strip(row.CT_Telecopie), - "email": _safe_strip(row.CT_EMail), - "site_web": _safe_strip(row.CT_Site), - "facebook": _safe_strip(row.CT_Facebook), - "linkedin": _safe_strip(row.CT_LinkedIn), - "taux01": row.CT_Taux01, - "taux02": row.CT_Taux02, - "taux03": row.CT_Taux03, - "taux04": row.CT_Taux04, - "statistique01": _safe_strip(row.CT_Statistique01), - "statistique02": _safe_strip(row.CT_Statistique02), - "statistique03": _safe_strip(row.CT_Statistique03), - "statistique04": _safe_strip(row.CT_Statistique04), - "statistique05": _safe_strip(row.CT_Statistique05), - "statistique06": _safe_strip(row.CT_Statistique06), - "statistique07": _safe_strip(row.CT_Statistique07), - "statistique08": _safe_strip(row.CT_Statistique08), - "statistique09": _safe_strip(row.CT_Statistique09), - "statistique10": _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": _safe_strip(row.CT_Commentaire), - "section_analytique": _safe_strip(row.CA_Num), - "mode_reglement_code": row.MR_No, - "surveillance_active": (row.CT_Surveillance == 1), - "coface": _safe_strip(row.CT_Coface), - "forme_juridique": _safe_strip(row.CT_SvFormeJuri), - "effectif": _safe_strip(row.CT_SvEffectif), - "sv_regularite": _safe_strip(row.CT_SvRegul), - "sv_cotation": _safe_strip(row.CT_SvCotation), - "sv_objet_maj": _safe_strip(row.CT_SvObjetMaj), - "sv_chiffre_affaires": row.CT_SvCA, - "sv_resultat": row.CT_SvResultat, - "compte_general": _safe_strip(row.CG_NumPrinc), - "categorie_tarif": row.N_CatTarif, - "categorie_compta": row.N_CatCompta, - } - - fournisseur["contacts"] = _get_contacts_client(row.CT_Num, conn) - + fournisseur = _row_to_tiers_dict(row) + fournisseur["contacts"] = _get_contacts(row.CT_Num, conn) fournisseurs.append(fournisseur) logger.info( - f" SQL: {len(fournisseurs)} fournisseurs avec {len(fournisseur)} champs" + f"✓ SQL: {len(fournisseurs)} fournisseurs avec {len(fournisseur)} champs" ) return fournisseurs except Exception as e: - logger.error(f" Erreur SQL fournisseurs: {e}") + logger.error(f"✗ Erreur SQL fournisseurs: {e}") raise RuntimeError(f"Erreur lecture fournisseurs: {str(e)}") def lire_fournisseur(self, code_fournisseur): + """Lit un fournisseur avec son commercial""" try: with self._get_sql_connection() as conn: cursor = conn.cursor() - query = """ - SELECT - -- IDENTIFICATION (9) - CT_Num, CT_Intitule, CT_Type, CT_Qualite, - 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 + query = _build_tiers_select_query() + query += """ + FROM F_COMPTET t + LEFT JOIN F_COLLABORATEUR c ON t.CO_No = c.CO_No + WHERE t.CT_Num = ? AND t.CT_Type = 1 """ cursor.execute(query, (code_fournisseur.upper(),)) @@ -421,88 +255,16 @@ class SageConnector: if not row: return None - fournisseur = { - "numero": _safe_strip(row.CT_Num), - "intitule": _safe_strip(row.CT_Intitule), - "type_tiers": row.CT_Type, - "qualite": _safe_strip(row.CT_Qualite), - "classement": _safe_strip(row.CT_Classement), - "raccourci": _safe_strip(row.CT_Raccourci), - "siret": _safe_strip(row.CT_Siret), - "tva_intra": _safe_strip(row.CT_Identifiant), - "code_naf": _safe_strip(row.CT_Ape), - "contact": _safe_strip(row.CT_Contact), - "adresse": _safe_strip(row.CT_Adresse), - "complement": _safe_strip(row.CT_Complement), - "code_postal": _safe_strip(row.CT_CodePostal), - "ville": _safe_strip(row.CT_Ville), - "region": _safe_strip(row.CT_CodeRegion), - "pays": _safe_strip(row.CT_Pays), - "telephone": _safe_strip(row.CT_Telephone), - "telecopie": _safe_strip(row.CT_Telecopie), - "email": _safe_strip(row.CT_EMail), - "site_web": _safe_strip(row.CT_Site), - "facebook": _safe_strip(row.CT_Facebook), - "linkedin": _safe_strip(row.CT_LinkedIn), - "taux01": row.CT_Taux01, - "taux02": row.CT_Taux02, - "taux03": row.CT_Taux03, - "taux04": row.CT_Taux04, - "statistique01": _safe_strip(row.CT_Statistique01), - "statistique02": _safe_strip(row.CT_Statistique02), - "statistique03": _safe_strip(row.CT_Statistique03), - "statistique04": _safe_strip(row.CT_Statistique04), - "statistique05": _safe_strip(row.CT_Statistique05), - "statistique06": _safe_strip(row.CT_Statistique06), - "statistique07": _safe_strip(row.CT_Statistique07), - "statistique08": _safe_strip(row.CT_Statistique08), - "statistique09": _safe_strip(row.CT_Statistique09), - "statistique10": _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": _safe_strip(row.CT_Commentaire), - "section_analytique": _safe_strip(row.CA_Num), - "mode_reglement_code": row.MR_No, - "surveillance_active": (row.CT_Surveillance == 1), - "coface": _safe_strip(row.CT_Coface), - "forme_juridique": _safe_strip(row.CT_SvFormeJuri), - "effectif": _safe_strip(row.CT_SvEffectif), - "sv_regularite": _safe_strip(row.CT_SvRegul), - "sv_cotation": _safe_strip(row.CT_SvCotation), - "sv_objet_maj": _safe_strip(row.CT_SvObjetMaj), - "sv_chiffre_affaires": row.CT_SvCA, - "sv_resultat": row.CT_SvResultat, - "compte_general": _safe_strip(row.CG_NumPrinc), - "categorie_tarif": row.N_CatTarif, - "categorie_compta": row.N_CatCompta, - } - - fournisseur["contacts"] = _get_contacts_client(row.CT_Num, conn) + fournisseur = _row_to_tiers_dict(row) + fournisseur["contacts"] = _get_contacts(row.CT_Num, conn) logger.info( - f" SQL: Fournisseur {code_fournisseur} avec {len(fournisseur)} champs" + f"✓ SQL: Fournisseur {code_fournisseur} avec {len(fournisseur)} champs" ) return fournisseur except Exception as e: - logger.error(f" Erreur SQL fournisseur {code_fournisseur}: {e}") + logger.error(f"✗ Erreur SQL fournisseur {code_fournisseur}: {e}") return None def creer_fournisseur(self, fournisseur_data: Dict) -> Dict: @@ -951,226 +713,52 @@ class SageConnector: raise RuntimeError(f"Erreur technique Sage: {error_message}") def lister_tous_clients(self, filtre=""): - """ - Liste tous les clients avec TOUS les champs gérés par creer_client - Symétrie complète GET/POST - """ + """Liste tous les clients avec leur commercial""" try: with self._get_sql_connection() as conn: cursor = conn.cursor() - query = """ - SELECT - -- IDENTIFICATION (8) - CT_Num, CT_Intitule, CT_Type, CT_Qualite, - 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_Type = 0 + query = _build_tiers_select_query() + query += """ + FROM F_COMPTET t + LEFT JOIN F_COLLABORATEUR c ON t.CO_No = c.CO_No + WHERE t.CT_Type = 0 """ params = [] - if filtre: - query += " AND (CT_Num LIKE ? OR CT_Intitule LIKE ?)" + query += " AND (t.CT_Num LIKE ? OR t.CT_Intitule LIKE ?)" params.extend([f"%{filtre}%", f"%{filtre}%"]) - query += " ORDER BY CT_Intitule" + query += " ORDER BY t.CT_Intitule" cursor.execute(query, params) rows = cursor.fetchall() clients = [] for row in rows: - client = { - "numero": _safe_strip(row.CT_Num), - "intitule": _safe_strip(row.CT_Intitule), - "type_tiers": row.CT_Type, - "qualite": _safe_strip(row.CT_Qualite), - "classement": _safe_strip(row.CT_Classement), - "raccourci": _safe_strip(row.CT_Raccourci), - "siret": _safe_strip(row.CT_Siret), - "tva_intra": _safe_strip(row.CT_Identifiant), - "code_naf": _safe_strip(row.CT_Ape), - "contact": _safe_strip(row.CT_Contact), - "adresse": _safe_strip(row.CT_Adresse), - "complement": _safe_strip(row.CT_Complement), - "code_postal": _safe_strip(row.CT_CodePostal), - "ville": _safe_strip(row.CT_Ville), - "region": _safe_strip(row.CT_CodeRegion), - "pays": _safe_strip(row.CT_Pays), - "telephone": _safe_strip(row.CT_Telephone), - "telecopie": _safe_strip(row.CT_Telecopie), - "email": _safe_strip(row.CT_EMail), - "site_web": _safe_strip(row.CT_Site), - "facebook": _safe_strip(row.CT_Facebook), - "linkedin": _safe_strip(row.CT_LinkedIn), - "taux01": row.CT_Taux01, - "taux02": row.CT_Taux02, - "taux03": row.CT_Taux03, - "taux04": row.CT_Taux04, - "statistique01": _safe_strip(row.CT_Statistique01), - "statistique02": _safe_strip(row.CT_Statistique02), - "statistique03": _safe_strip(row.CT_Statistique03), - "statistique04": _safe_strip(row.CT_Statistique04), - "statistique05": _safe_strip(row.CT_Statistique05), - "statistique06": _safe_strip(row.CT_Statistique06), - "statistique07": _safe_strip(row.CT_Statistique07), - "statistique08": _safe_strip(row.CT_Statistique08), - "statistique09": _safe_strip(row.CT_Statistique09), - "statistique10": _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": _safe_strip(row.CT_Commentaire), - "section_analytique": _safe_strip(row.CA_Num), - "mode_reglement_code": row.MR_No, - "surveillance_active": (row.CT_Surveillance == 1), - "coface": _safe_strip(row.CT_Coface), - "forme_juridique": _safe_strip(row.CT_SvFormeJuri), - "effectif": _safe_strip(row.CT_SvEffectif), - "sv_regularite": _safe_strip(row.CT_SvRegul), - "sv_cotation": _safe_strip(row.CT_SvCotation), - "sv_objet_maj": _safe_strip(row.CT_SvObjetMaj), - "sv_chiffre_affaires": row.CT_SvCA, - "sv_resultat": row.CT_SvResultat, - "compte_general": _safe_strip(row.CG_NumPrinc), - "categorie_tarif": row.N_CatTarif, - "categorie_compta": row.N_CatCompta, - } - - client["contacts"] = _get_contacts_client(row.CT_Num, conn) - + client = _row_to_tiers_dict(row) + client["contacts"] = _get_contacts(row.CT_Num, conn) clients.append(client) - logger.info(f" SQL: {len(clients)} clients avec {len(client)} champs") + logger.info(f"✓ SQL: {len(clients)} clients avec {len(client)} champs") return clients except Exception as e: - logger.error(f" Erreur SQL clients: {e}") + logger.error(f"✗ Erreur SQL clients: {e}") raise RuntimeError(f"Erreur lecture clients: {str(e)}") def lire_client(self, code_client): - """ - Lit un client avec TOUS les champs (identique à lister_tous_clients) - Symétrie complète GET/POST - """ + """Lit un client avec son commercial""" try: with self._get_sql_connection() as conn: cursor = conn.cursor() - query = """ - SELECT - -- IDENTIFICATION (8) - CT_Num, CT_Intitule, CT_Type, CT_Qualite, - 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 = ? AND CT_Type = 0 + query = _build_tiers_select_query() + query += """ + FROM F_COMPTET t + LEFT JOIN F_COLLABORATEUR c ON t.CO_No = c.CO_No + WHERE t.CT_Num = ? AND t.CT_Type = 0 """ cursor.execute(query, (code_client.upper(),)) @@ -1179,86 +767,14 @@ class SageConnector: if not row: return None - client = { - "numero": _safe_strip(row.CT_Num), - "intitule": _safe_strip(row.CT_Intitule), - "type_tiers": row.CT_Type, - "qualite": _safe_strip(row.CT_Qualite), - "classement": _safe_strip(row.CT_Classement), - "raccourci": _safe_strip(row.CT_Raccourci), - "siret": _safe_strip(row.CT_Siret), - "tva_intra": _safe_strip(row.CT_Identifiant), - "code_naf": _safe_strip(row.CT_Ape), - "contact": _safe_strip(row.CT_Contact), - "adresse": _safe_strip(row.CT_Adresse), - "complement": _safe_strip(row.CT_Complement), - "code_postal": _safe_strip(row.CT_CodePostal), - "ville": _safe_strip(row.CT_Ville), - "region": _safe_strip(row.CT_CodeRegion), - "pays": _safe_strip(row.CT_Pays), - "telephone": _safe_strip(row.CT_Telephone), - "telecopie": _safe_strip(row.CT_Telecopie), - "email": _safe_strip(row.CT_EMail), - "site_web": _safe_strip(row.CT_Site), - "facebook": _safe_strip(row.CT_Facebook), - "linkedin": _safe_strip(row.CT_LinkedIn), - "taux01": row.CT_Taux01, - "taux02": row.CT_Taux02, - "taux03": row.CT_Taux03, - "taux04": row.CT_Taux04, - "statistique01": _safe_strip(row.CT_Statistique01), - "statistique02": _safe_strip(row.CT_Statistique02), - "statistique03": _safe_strip(row.CT_Statistique03), - "statistique04": _safe_strip(row.CT_Statistique04), - "statistique05": _safe_strip(row.CT_Statistique05), - "statistique06": _safe_strip(row.CT_Statistique06), - "statistique07": _safe_strip(row.CT_Statistique07), - "statistique08": _safe_strip(row.CT_Statistique08), - "statistique09": _safe_strip(row.CT_Statistique09), - "statistique10": _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": _safe_strip(row.CT_Commentaire), - "section_analytique": _safe_strip(row.CA_Num), - "mode_reglement_code": row.MR_No, - "surveillance_active": (row.CT_Surveillance == 1), - "coface": _safe_strip(row.CT_Coface), - "forme_juridique": _safe_strip(row.CT_SvFormeJuri), - "effectif": _safe_strip(row.CT_SvEffectif), - "sv_regularite": _safe_strip(row.CT_SvRegul), - "sv_cotation": _safe_strip(row.CT_SvCotation), - "sv_objet_maj": _safe_strip(row.CT_SvObjetMaj), - "sv_chiffre_affaires": row.CT_SvCA, - "sv_resultat": row.CT_SvResultat, - "compte_general": _safe_strip(row.CG_NumPrinc), - "categorie_tarif": row.N_CatTarif, - "categorie_compta": row.N_CatCompta, - } + client = _row_to_tiers_dict(row) + client["contacts"] = _get_contacts(row.CT_Num, conn) - client["contacts"] = _get_contacts_client(row.CT_Num, conn) - - logger.info(f" SQL: Client {code_client} avec {len(client)} champs") + 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=""): @@ -3101,19 +2617,51 @@ class SageConnector: logger.info(f" CLIENT: {numero_client}") logger.info(f" CONTACT: {prenom} {nom}") - logger.info(f"[1] Chargement du client: {numero_client}") + logger.info(f"[1] Chargement du tiers: {numero_client}") factory_client = self.cial.CptaApplication.FactoryClient + factory_fournisseur = self.cial.CptaApplication.FactoryFournisseur + + persist_tiers = None + type_tiers = None + + # Tentative 1 : Client try: - persist_client = factory_client.ReadNumero(numero_client) - if not persist_client: - raise ValueError(f"Client {numero_client} non trouve") - - client_obj = win32com.client.CastTo(persist_client, "IBOClient3") - client_obj.Read() - logger.info(" OK Client charge") + logger.info(" Recherche dans Clients...") + persist_tiers = factory_client.ReadNumero(numero_client) + if persist_tiers: + type_tiers = "Client" + logger.info(" ✓ Trouvé comme Client") except Exception as e: - raise ValueError(f"Client {numero_client} introuvable: {e}") + logger.debug(f" Pas trouvé comme Client: {e}") + # Tentative 2 : Fournisseur (si pas trouvé comme client) + if not persist_tiers: + try: + logger.info(" Recherche dans Fournisseurs...") + persist_tiers = factory_fournisseur.ReadNumero(numero_client) + if persist_tiers: + type_tiers = "Fournisseur" + logger.info(" ✓ Trouvé comme Fournisseur") + except Exception as e: + logger.debug(" Pas trouvé comme Fournisseur: {e}") + + # Vérification finale + if not persist_tiers: + raise ValueError( + f"Le tiers '{numero_client}' est introuvable dans Sage 100c. " + f"Vérifiez que le code est exact et que le tiers existe " + f"(Client ou Fournisseur)." + ) + + # Cast et lecture + try: + client_obj = win32com.client.CastTo(persist_tiers, "IBOClient3") + client_obj.Read() + logger.info(f" OK {type_tiers} chargé") + except Exception as e: + raise ValueError( + f"Erreur lors du chargement du {type_tiers} '{numero_client}': {e}" + ) logger.info("[2] Creation via FactoryTiersContact") if not hasattr(client_obj, "FactoryTiersContact"): @@ -3541,19 +3089,51 @@ class SageConnector: logger.info(f"[MODIFICATION CONTACT] CT_No={contact_numero}") logger.info("=" * 80) - logger.info("[1] Chargement du client") + logger.info("[1] Chargement du tiers") factory_client = self.cial.CptaApplication.FactoryClient + factory_fournisseur = self.cial.CptaApplication.FactoryFournisseur + persist_tiers = None + type_tiers = None + + # Tentative 1 : Client try: - persist_client = factory_client.ReadNumero(numero) - if not persist_client: - raise ValueError(f"Client {numero} non trouve") - - client_obj = win32com.client.CastTo(persist_client, "IBOClient3") - client_obj.Read() - logger.info(f" OK Client charge: {client_obj.CT_Intitule}") + logger.info(" Recherche dans Clients...") + persist_tiers = factory_client.ReadNumero(numero) + if persist_tiers: + type_tiers = "Client" + logger.info(" ✓ Trouvé comme Client") except Exception as e: - raise ValueError(f"Client {numero} introuvable: {e}") + logger.debug(f" Pas trouvé comme Client: {e}") + + # Tentative 2 : Fournisseur (si pas trouvé comme client) + if not persist_tiers: + try: + logger.info(" Recherche dans Fournisseurs...") + persist_tiers = factory_fournisseur.ReadNumero(numero) + if persist_tiers: + type_tiers = "Fournisseur" + logger.info(" ✓ Trouvé comme Fournisseur") + except Exception as e: + logger.debug(f" Pas trouvé comme Fournisseur: {e}") + + # Vérification finale + if not persist_tiers: + raise ValueError( + f"Le tiers '{numero}' est introuvable dans Sage 100c. " + f"Vérifiez que le code est exact et que le tiers existe " + f"(Client ou Fournisseur)." + ) + + # Cast et lecture + try: + client_obj = win32com.client.CastTo(persist_tiers, "IBOClient3") + client_obj.Read() + logger.info(f" OK {type_tiers} chargé: {client_obj.CT_Intitule}") + except Exception as e: + raise ValueError( + f"Erreur lors du chargement du {type_tiers} '{numero}': {e}" + ) logger.info("[2] Chargement du contact") @@ -4127,7 +3707,7 @@ class SageConnector: def lister_contacts(self, numero: str) -> List[Dict]: try: with self._get_sql_connection() as conn: - return _get_contacts_client(numero, conn) + return _get_contacts(numero, conn) except Exception as e: logger.error(f"Erreur liste contacts: {e}") raise RuntimeError(f"Erreur lecture contacts: {str(e)}") @@ -11271,28 +10851,33 @@ class SageConnector: def lister_tous_tiers( self, type_tiers: Optional[str] = None, filtre: str = "" ) -> List[Dict]: + """Liste tous les tiers avec jointure sur le collaborateur/commercial""" try: with self._get_sql_connection() as conn: cursor = conn.cursor() query = _build_tiers_select_query() - query += " FROM F_COMPTET WHERE 1=1" + query += """ + FROM F_COMPTET t + LEFT JOIN F_COLLABORATEUR c ON t.CO_No = c.CO_No + WHERE 1=1 + """ params = [] if type_tiers and type_tiers != "all": if type_tiers == "prospect": - query += " AND CT_Type = 0 AND CT_Prospect = 1" + query += " AND t.CT_Type = 0 AND t.CT_Prospect = 1" elif type_tiers == "client": - query += " AND CT_Type = 0 AND CT_Prospect = 0" + query += " AND t.CT_Type = 0 AND t.CT_Prospect = 0" elif type_tiers == "fournisseur": - query += " AND CT_Type = 1" + query += " AND t.CT_Type = 1" if filtre: - query += " AND (CT_Num LIKE ? OR CT_Intitule LIKE ?)" + query += " AND (t.CT_Num LIKE ? OR t.CT_Intitule LIKE ?)" params.extend([f"%{filtre}%", f"%{filtre}%"]) - query += " ORDER BY CT_Intitule" + query += " ORDER BY t.CT_Intitule" cursor.execute(query, params) rows = cursor.fetchall() @@ -11300,26 +10885,30 @@ class SageConnector: tiers_list = [] for row in rows: tiers = _row_to_tiers_dict(row) - tiers["contacts"] = _get_contacts_client(row.CT_Num, conn) + tiers["contacts"] = _get_contacts(row.CT_Num, conn) tiers_list.append(tiers) logger.info( - f" SQL: {len(tiers_list)} tiers retournés (type={type_tiers}, filtre={filtre})" + f"✓ SQL: {len(tiers_list)} tiers retournés (type={type_tiers}, filtre={filtre})" ) return tiers_list except Exception as e: - logger.error(f" Erreur SQL tiers: {e}") + logger.error(f"✗ Erreur SQL tiers: {e}") raise RuntimeError(f"Erreur lecture tiers: {str(e)}") def lire_tiers(self, code: str) -> Optional[Dict]: - """Lit un tiers (client/fournisseur/prospect) par code""" + """Lit un tiers (client/fournisseur/prospect) par code avec son commercial""" try: with self._get_sql_connection() as conn: cursor = conn.cursor() query = _build_tiers_select_query() - query += " FROM F_COMPTET WHERE CT_Num = ?" + query += """ + FROM F_COMPTET t + LEFT JOIN F_COLLABORATEUR c ON t.CO_No = c.CO_No + WHERE t.CT_Num = ? + """ cursor.execute(query, (code.upper(),)) row = cursor.fetchone() @@ -11328,11 +10917,11 @@ class SageConnector: return None tiers = _row_to_tiers_dict(row) - tiers["contacts"] = _get_contacts_client(row.CT_Num, conn) + tiers["contacts"] = _get_contacts(row.CT_Num, conn) - logger.info(f" SQL: Tiers {code} lu avec succès") + logger.info(f"✓ SQL: Tiers {code} lu avec succès") return tiers except Exception as e: - logger.error(f" Erreur SQL tiers {code}: {e}") + logger.error(f"✗ Erreur SQL tiers {code}: {e}") return None diff --git a/utils/documents/documents_data_sql.py b/utils/documents/documents_data_sql.py index 514bf60..cf8f608 100644 --- a/utils/documents/documents_data_sql.py +++ b/utils/documents/documents_data_sql.py @@ -423,9 +423,6 @@ def _lister_documents_avec_lignes_sql( for idx, entete in enumerate(entetes): numero = _safe_strip(entete.DO_Piece) - logger.info( - f"[SQL LIST] [{idx + 1}/{len(entetes)}] Traitement {numero}..." - ) try: prefixes_vente = { @@ -785,9 +782,6 @@ def _lister_documents_avec_lignes_sql( documents.append(doc) stats["succes"] += 1 - logger.info( - f"[SQL LIST] {numero} : AJOUTÉ à la liste (total: {len(documents)})" - ) except Exception as e: logger.error( @@ -796,20 +790,6 @@ def _lister_documents_avec_lignes_sql( ) continue - logger.info("[SQL LIST] ═══════════════════════════") - logger.info("[SQL LIST] STATISTIQUES FINALES:") - logger.info(f"[SQL LIST] Total SQL: {stats['total']}") - logger.info(f"[SQL LIST] Exclus préfixe: {stats['exclus_prefixe']}") - logger.info(f"[SQL LIST] Erreur construction: {stats['erreur_construction']}") - logger.info(f"[SQL LIST] Erreur lignes: {stats['erreur_lignes']}") - logger.info( - f"[SQL LIST] Erreur transformations: {stats['erreur_transformations']}" - ) - logger.info(f"[SQL LIST] Erreur liaisons: {stats['erreur_liaisons']}") - logger.info(f"[SQL LIST] SUCCÈS: {stats['succes']}") - logger.info(f"[SQL LIST] Documents retournés: {len(documents)}") - logger.info("[SQL LIST] ═══════════════════════════") - return documents except Exception as e: diff --git a/utils/functions/items_to_dict.py b/utils/functions/items_to_dict.py index da0da4e..4fded2e 100644 --- a/utils/functions/items_to_dict.py +++ b/utils/functions/items_to_dict.py @@ -1,4 +1,4 @@ -from typing import Dict +from typing import Dict, Optional import logging from utils.functions.functions import _safe_strip @@ -78,9 +78,55 @@ def _row_to_contact_dict(row) -> Dict: } -def _row_to_tiers_dict(row) -> Dict: - """Convertit une ligne SQL en dictionnaire tiers (factorisation DRY)""" +def _row_to_collaborateur_dict(row) -> Optional[dict]: + """Convertit une ligne SQL en dictionnaire collaborateur""" + # Vérifier si le collaborateur existe + if not hasattr(row, "Collab_CO_No") or row.Collab_CO_No is None: + return None + return { + "numero": row.Collab_CO_No, + "nom": _safe_strip(row.Collab_CO_Nom), + "prenom": _safe_strip(row.Collab_CO_Prenom), + "fonction": _safe_strip(row.Collab_CO_Fonction), + "adresse": _safe_strip(row.Collab_CO_Adresse), + "complement": _safe_strip(row.Collab_CO_Complement), + "code_postal": _safe_strip(row.Collab_CO_CodePostal), + "ville": _safe_strip(row.Collab_CO_Ville), + "region": _safe_strip(row.Collab_CO_CodeRegion), + "pays": _safe_strip(row.Collab_CO_Pays), + "service": _safe_strip(row.Collab_CO_Service), + "est_vendeur": (row.Collab_CO_Vendeur == 1) + if row.Collab_CO_Vendeur is not None + else None, + "est_caissier": (row.Collab_CO_Caissier == 1) + if row.Collab_CO_Caissier is not None + else None, + "est_acheteur": (row.Collab_CO_Acheteur == 1) + if row.Collab_CO_Acheteur is not None + else None, + "telephone": _safe_strip(row.Collab_CO_Telephone), + "telecopie": _safe_strip(row.Collab_CO_Telecopie), + "email": _safe_strip(row.Collab_CO_EMail), + "tel_portable": _safe_strip(row.Collab_CO_TelPortable), + "matricule": _safe_strip(row.Collab_CO_Matricule), + "facebook": _safe_strip(row.Collab_CO_Facebook), + "linkedin": _safe_strip(row.Collab_CO_LinkedIn), + "skype": _safe_strip(row.Collab_CO_Skype), + "est_actif": (row.Collab_CO_Sommeil == 0) + if row.Collab_CO_Sommeil is not None + else None, + "est_chef_ventes": (row.Collab_CO_ChefVentes == 1) + if row.Collab_CO_ChefVentes is not None + else None, + "chef_ventes_numero": row.Collab_CO_NoChefVentes, + } + + +def _row_to_tiers_dict(row) -> dict: + """Convertit une ligne SQL en dictionnaire tiers""" + tiers = { + # IDENTIFICATION "numero": _safe_strip(row.CT_Num), "intitule": _safe_strip(row.CT_Intitule), "type_tiers": row.CT_Type, @@ -90,6 +136,7 @@ def _row_to_tiers_dict(row) -> Dict: "siret": _safe_strip(row.CT_Siret), "tva_intra": _safe_strip(row.CT_Identifiant), "code_naf": _safe_strip(row.CT_Ape), + # ADRESSE "contact": _safe_strip(row.CT_Contact), "adresse": _safe_strip(row.CT_Adresse), "complement": _safe_strip(row.CT_Complement), @@ -97,16 +144,19 @@ def _row_to_tiers_dict(row) -> Dict: "ville": _safe_strip(row.CT_Ville), "region": _safe_strip(row.CT_CodeRegion), "pays": _safe_strip(row.CT_Pays), + # TELECOM "telephone": _safe_strip(row.CT_Telephone), "telecopie": _safe_strip(row.CT_Telecopie), "email": _safe_strip(row.CT_EMail), "site_web": _safe_strip(row.CT_Site), "facebook": _safe_strip(row.CT_Facebook), "linkedin": _safe_strip(row.CT_LinkedIn), + # TAUX "taux01": row.CT_Taux01, "taux02": row.CT_Taux02, "taux03": row.CT_Taux03, "taux04": row.CT_Taux04, + # STATISTIQUES "statistique01": _safe_strip(row.CT_Statistique01), "statistique02": _safe_strip(row.CT_Statistique02), "statistique03": _safe_strip(row.CT_Statistique03), @@ -117,10 +167,13 @@ def _row_to_tiers_dict(row) -> Dict: "statistique08": _safe_strip(row.CT_Statistique08), "statistique09": _safe_strip(row.CT_Statistique09), "statistique10": _safe_strip(row.CT_Statistique10), + # COMMERCIAL "encours_autorise": row.CT_Encours, "assurance_credit": row.CT_Assurance, "langue": row.CT_Langue, "commercial_code": row.CO_No, + "commercial": _row_to_collaborateur_dict(row), + # FACTURATION "lettrage_auto": (row.CT_Lettrage == 1), "est_actif": (row.CT_Sommeil == 0), "type_facture": row.CT_Facture, @@ -132,12 +185,16 @@ def _row_to_tiers_dict(row) -> Dict: "exclure_relance": (row.CT_NotRappel == 1), "exclure_penalites": (row.CT_NotPenal == 1), "bon_a_payer": row.CT_BonAPayer, + # LOGISTIQUE "priorite_livraison": row.CT_PrioriteLivr, "livraison_partielle": row.CT_LivrPartielle, "delai_transport": row.CT_DelaiTransport, "delai_appro": row.CT_DelaiAppro, + # COMMENTAIRE "commentaire": _safe_strip(row.CT_Commentaire), + # ANALYTIQUE "section_analytique": _safe_strip(row.CA_Num), + # ORGANISATION / SURVEILLANCE "mode_reglement_code": row.MR_No, "surveillance_active": (row.CT_Surveillance == 1), "coface": _safe_strip(row.CT_Coface), @@ -148,11 +205,14 @@ def _row_to_tiers_dict(row) -> Dict: "sv_objet_maj": _safe_strip(row.CT_SvObjetMaj), "sv_chiffre_affaires": row.CT_SvCA, "sv_resultat": row.CT_SvResultat, + # COMPTE GENERAL ET CATEGORIES "compte_general": _safe_strip(row.CG_NumPrinc), "categorie_tarif": row.N_CatTarif, "categorie_compta": row.N_CatCompta, } + return tiers + __all__ = [ "_contact_to_dict", diff --git a/utils/tiers/contacts/contacts.py b/utils/tiers/contacts/contacts.py index ac0878c..412e1e9 100644 --- a/utils/tiers/contacts/contacts.py +++ b/utils/tiers/contacts/contacts.py @@ -5,7 +5,7 @@ from utils.functions.functions import _safe_strip logger = logging.getLogger(__name__) -def _get_contacts_client(numero: str, conn) -> list: +def _get_contacts(numero: str, conn) -> list: try: cursor = conn.cursor() @@ -162,7 +162,7 @@ def _lire_contact_depuis_base( __all__ = [ - "_get_contacts_client", + "_get_contacts", "_chercher_contact_en_base", "_lire_contact_depuis_base" ] \ No newline at end of file diff --git a/utils/tiers/tiers_data_sql.py b/utils/tiers/tiers_data_sql.py index 31ccb3b..5b2becc 100644 --- a/utils/tiers/tiers_data_sql.py +++ b/utils/tiers/tiers_data_sql.py @@ -1,54 +1,81 @@ -def _build_tiers_select_query() -> str: - """Construit la requête SELECT pour les tiers (factorisation)""" +def _build_tiers_select_query(): + """Construit la partie SELECT de la requête avec tous les champs tiers + collaborateur""" return """ SELECT - -- IDENTIFICATION (9) - CT_Num, CT_Intitule, CT_Type, CT_Qualite, - CT_Classement, CT_Raccourci, CT_Siret, CT_Identifiant, - CT_Ape, + -- IDENTIFICATION TIERS (9) + t.CT_Num, t.CT_Intitule, t.CT_Type, t.CT_Qualite, + t.CT_Classement, t.CT_Raccourci, t.CT_Siret, t.CT_Identifiant, + t.CT_Ape, - -- ADRESSE (7) - CT_Contact, CT_Adresse, CT_Complement, - CT_CodePostal, CT_Ville, CT_CodeRegion, CT_Pays, + -- ADRESSE TIERS (7) + t.CT_Contact, t.CT_Adresse, t.CT_Complement, + t.CT_CodePostal, t.CT_Ville, t.CT_CodeRegion, t.CT_Pays, - -- TELECOM (6) - CT_Telephone, CT_Telecopie, CT_EMail, CT_Site, - CT_Facebook, CT_LinkedIn, + -- TELECOM TIERS (6) + t.CT_Telephone, t.CT_Telecopie, t.CT_EMail, t.CT_Site, + t.CT_Facebook, t.CT_LinkedIn, - -- TAUX (4) - CT_Taux01, CT_Taux02, CT_Taux03, CT_Taux04, + -- TAUX TIERS (4) + t.CT_Taux01, t.CT_Taux02, t.CT_Taux03, t.CT_Taux04, - -- STATISTIQUES (10) - CT_Statistique01, CT_Statistique02, CT_Statistique03, - CT_Statistique04, CT_Statistique05, CT_Statistique06, - CT_Statistique07, CT_Statistique08, CT_Statistique09, - CT_Statistique10, + -- STATISTIQUES TIERS (10) + t.CT_Statistique01, t.CT_Statistique02, t.CT_Statistique03, + t.CT_Statistique04, t.CT_Statistique05, t.CT_Statistique06, + t.CT_Statistique07, t.CT_Statistique08, t.CT_Statistique09, + t.CT_Statistique10, - -- COMMERCIAL (4) - CT_Encours, CT_Assurance, CT_Langue, CO_No, + -- COMMERCIAL TIERS (4) + t.CT_Encours, t.CT_Assurance, t.CT_Langue, t.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, + -- FACTURATION TIERS (11) + t.CT_Lettrage, t.CT_Sommeil, t.CT_Facture, t.CT_Prospect, + t.CT_BLFact, t.CT_Saut, t.CT_ValidEch, t.CT_ControlEnc, + t.CT_NotRappel, t.CT_NotPenal, t.CT_BonAPayer, - -- LOGISTIQUE (4) - CT_PrioriteLivr, CT_LivrPartielle, - CT_DelaiTransport, CT_DelaiAppro, + -- LOGISTIQUE TIERS (4) + t.CT_PrioriteLivr, t.CT_LivrPartielle, + t.CT_DelaiTransport, t.CT_DelaiAppro, - -- COMMENTAIRE (1) - CT_Commentaire, + -- COMMENTAIRE TIERS (1) + t.CT_Commentaire, - -- ANALYTIQUE (1) - CA_Num, + -- ANALYTIQUE TIERS (1) + t.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, + -- ORGANISATION / SURVEILLANCE TIERS (10) + t.MR_No, t.CT_Surveillance, t.CT_Coface, + t.CT_SvFormeJuri, t.CT_SvEffectif, t.CT_SvRegul, + t.CT_SvCotation, t.CT_SvObjetMaj, t.CT_SvCA, t.CT_SvResultat, - -- COMPTE GENERAL ET CATEGORIES (3) - CG_NumPrinc, N_CatTarif, N_CatCompta + -- COMPTE GENERAL ET CATEGORIES TIERS (3) + t.CG_NumPrinc, t.N_CatTarif, t.N_CatCompta, + + -- COLLABORATEUR (23 champs) + c.CO_No AS Collab_CO_No, + c.CO_Nom AS Collab_CO_Nom, + c.CO_Prenom AS Collab_CO_Prenom, + c.CO_Fonction AS Collab_CO_Fonction, + c.CO_Adresse AS Collab_CO_Adresse, + c.CO_Complement AS Collab_CO_Complement, + c.CO_CodePostal AS Collab_CO_CodePostal, + c.CO_Ville AS Collab_CO_Ville, + c.CO_CodeRegion AS Collab_CO_CodeRegion, + c.CO_Pays AS Collab_CO_Pays, + c.CO_Service AS Collab_CO_Service, + c.CO_Vendeur AS Collab_CO_Vendeur, + c.CO_Caissier AS Collab_CO_Caissier, + c.CO_Acheteur AS Collab_CO_Acheteur, + c.CO_Telephone AS Collab_CO_Telephone, + c.CO_Telecopie AS Collab_CO_Telecopie, + c.CO_EMail AS Collab_CO_EMail, + c.CO_TelPortable AS Collab_CO_TelPortable, + c.CO_Matricule AS Collab_CO_Matricule, + c.CO_Facebook AS Collab_CO_Facebook, + c.CO_LinkedIn AS Collab_CO_LinkedIn, + c.CO_Skype AS Collab_CO_Skype, + c.CO_Sommeil AS Collab_CO_Sommeil, + c.CO_ChefVentes AS Collab_CO_ChefVentes, + c.CO_NoChefVentes AS Collab_CO_NoChefVentes """