From 0f060757799bcb1bcd3772b1b56b81e8b26d3b8d Mon Sep 17 00:00:00 2001 From: Fanilo-Nantenaina Date: Fri, 26 Dec 2025 19:54:00 +0300 Subject: [PATCH] feat(fournisseurs): add FournisseurDetails model and update endpoints --- api.py | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 176 insertions(+), 10 deletions(-) diff --git a/api.py b/api.py index 2dab2a2..c4ea553 100644 --- a/api.py +++ b/api.py @@ -356,7 +356,177 @@ class ClientDetails(BaseModel): } } + +class FournisseurDetails(BaseModel): + """ + Modèle de réponse fournisseur complet (GET /fournisseurs/{code}) + Strictement aligné avec ClientDetails + """ + + numero: Optional[str] = Field(None, description="Code fournisseur (CT_Num)") + intitule: Optional[str] = Field(None, description="Raison sociale ou Nom complet (CT_Intitule)") + type_tiers: Optional[int] = Field(None, description="Type : 0=Client, 1=Fournisseur (CT_Type)") + qualite: Optional[str] = Field(None, description="Qualité Sage : CLI, FOU, PRO (CT_Qualite)") + classement: Optional[str] = Field(None, description="Code de classement (CT_Classement)") + raccourci: Optional[str] = Field(None, description="Code raccourci 7 car. (CT_Raccourci)") + siret: Optional[str] = Field(None, description="N° SIRET 14 chiffres (CT_Siret)") + tva_intra: Optional[str] = Field(None, description="N° TVA intracommunautaire (CT_Identifiant)") + code_naf: Optional[str] = Field(None, description="Code NAF/APE (CT_Ape)") + + contact: Optional[str] = Field(None, description="Nom du contact principal (CT_Contact)") + adresse: Optional[str] = Field(None, description="Adresse ligne 1 (CT_Adresse)") + complement: Optional[str] = Field(None, description="Complément d'adresse (CT_Complement)") + code_postal: Optional[str] = Field(None, description="Code postal (CT_CodePostal)") + ville: Optional[str] = Field(None, description="Ville (CT_Ville)") + region: Optional[str] = Field(None, description="Région/État (CT_CodeRegion)") + pays: Optional[str] = Field(None, description="Pays (CT_Pays)") + + telephone: Optional[str] = Field(None, description="Téléphone fixe (CT_Telephone)") + telecopie: Optional[str] = Field(None, description="Fax (CT_Telecopie)") + email: Optional[str] = Field(None, description="Email principal (CT_EMail)") + site_web: Optional[str] = Field(None, description="Site web (CT_Site)") + facebook: Optional[str] = Field(None, description="Profil Facebook (CT_Facebook)") + linkedin: Optional[str] = Field(None, description="Profil LinkedIn (CT_LinkedIn)") + + taux01: Optional[float] = Field(None, description="Taux personnalisé 1 (CT_Taux01)") + taux02: Optional[float] = Field(None, description="Taux personnalisé 2 (CT_Taux02)") + taux03: Optional[float] = Field(None, description="Taux personnalisé 3 (CT_Taux03)") + taux04: Optional[float] = Field(None, description="Taux personnalisé 4 (CT_Taux04)") + + statistique01: Optional[str] = Field(None, description="Statistique 1 (CT_Statistique01)") + statistique02: Optional[str] = Field(None, description="Statistique 2 (CT_Statistique02)") + statistique03: Optional[str] = Field(None, description="Statistique 3 (CT_Statistique03)") + statistique04: Optional[str] = Field(None, description="Statistique 4 (CT_Statistique04)") + statistique05: Optional[str] = Field(None, description="Statistique 5 (CT_Statistique05)") + statistique06: Optional[str] = Field(None, description="Statistique 6 (CT_Statistique06)") + statistique07: Optional[str] = Field(None, description="Statistique 7 (CT_Statistique07)") + statistique08: Optional[str] = Field(None, description="Statistique 8 (CT_Statistique08)") + statistique09: Optional[str] = Field(None, description="Statistique 9 (CT_Statistique09)") + statistique10: Optional[str] = Field(None, description="Statistique 10 (CT_Statistique10)") + + encours_autorise: Optional[float] = Field(None, description="Encours maximum autorisé (CT_Encours)") + assurance_credit: Optional[float] = Field(None, description="Montant assurance crédit (CT_Assurance)") + langue: Optional[int] = Field(None, description="Code langue 0=FR, 1=EN (CT_Langue)") + commercial_code: Optional[int] = Field(None, description="Code du commercial (CO_No)") + + lettrage_auto: Optional[bool] = Field(None, description="Lettrage automatique (CT_Lettrage)") + est_actif: Optional[bool] = Field(None, description="True si actif (CT_Sommeil=0)") + type_facture: Optional[int] = Field(None, description="Type facture 0=Facture, 1=BL (CT_Facture)") + est_prospect: Optional[bool] = Field(None, description="True si prospect (CT_Prospect=1)") + bl_en_facture: Optional[int] = Field(None, description="Imprimer BL en facture (CT_BLFact)") + saut_page: Optional[int] = Field(None, description="Saut de page sur documents (CT_Saut)") + validation_echeance: Optional[int] = Field(None, description="Valider les échéances (CT_ValidEch)") + controle_encours: Optional[int] = Field(None, description="Contrôler l'encours (CT_ControlEnc)") + exclure_relance: Optional[bool] = Field(None, description="Exclure des relances (CT_NotRappel)") + exclure_penalites: Optional[bool] = Field(None, description="Exclure des pénalités (CT_NotPenal)") + bon_a_payer: Optional[int] = Field(None, description="Bon à payer obligatoire (CT_BonAPayer)") + + priorite_livraison: Optional[int] = Field(None, description="Priorité livraison (CT_PrioriteLivr)") + livraison_partielle: Optional[int] = Field(None, description="Livraison partielle (CT_LivrPartielle)") + delai_transport: Optional[int] = Field(None, description="Délai transport jours (CT_DelaiTransport)") + delai_appro: Optional[int] = Field(None, description="Délai appro jours (CT_DelaiAppro)") + + commentaire: Optional[str] = Field(None, description="Commentaire libre (CT_Commentaire)") + + section_analytique: Optional[str] = Field(None, description="Section analytique (CA_Num)") + + mode_reglement_code: Optional[int] = Field(None, description="Code mode règlement (MR_No)") + surveillance_active: Optional[bool] = Field(None, description="Surveillance financière (CT_Surveillance)") + coface: Optional[str] = Field(None, description="Code Coface 25 car. (CT_Coface)") + forme_juridique: Optional[str] = Field(None, description="Forme juridique SA, SARL (CT_SvFormeJuri)") + effectif: Optional[str] = Field(None, description="Nombre d'employés (CT_SvEffectif)") + sv_regularite: Optional[str] = Field(None, description="Régularité paiements (CT_SvRegul)") + sv_cotation: Optional[str] = Field(None, description="Cotation crédit (CT_SvCotation)") + sv_objet_maj: Optional[str] = Field(None, description="Objet dernière MAJ (CT_SvObjetMaj)") + sv_chiffre_affaires: Optional[float] = Field(None, description="Chiffre d'affaires (CT_SvCA)") + sv_resultat: Optional[float] = Field(None, description="Résultat financier (CT_SvResultat)") + + compte_general: Optional[str] = Field(None, description="Compte général principal (CG_NumPrinc)") + categorie_tarif: Optional[int] = Field(None, description="Catégorie tarifaire (N_CatTarif)") + categorie_compta: Optional[int] = Field(None, description="Catégorie comptable (N_CatCompta)") + + contacts: Optional[List[Contact]] = Field( + default_factory=list, + description="Liste des contacts du fournisseur" + ) + + class Config: + json_schema_extra = { + "example": { + "numero": "FOU000001", + "intitule": "SARL FOURNISSEUR EXEMPLE", + "type_tiers": 1, + "qualite": "FOU", + "classement": "A", + "raccourci": "EXEMPL", + "siret": "12345678901234", + "tva_intra": "FR12345678901", + "code_naf": "6201Z", + "contact": "Jean Dupont", + "adresse": "123 Rue de la Paix", + "complement": "Bâtiment B", + "code_postal": "75001", + "ville": "Paris", + "region": "Île-de-France", + "pays": "France", + "telephone": "0123456789", + "telecopie": "0123456788", + "email": "contact@exemple.fr", + "site_web": "https://www.exemple.fr", + "facebook": "https://facebook.com/exemple", + "linkedin": "https://linkedin.com/company/exemple", + "taux01": 0.0, + "taux02": 0.0, + "taux03": 0.0, + "taux04": 0.0, + "statistique01": "Informatique", + "statistique02": "", + "statistique03": "", + "statistique04": "", + "statistique05": "", + "statistique06": "", + "statistique07": "", + "statistique08": "", + "statistique09": "", + "statistique10": "", + "encours_autorise": 50000.0, + "assurance_credit": 40000.0, + "langue": 0, + "commercial_code": 1, + "lettrage_auto": True, + "est_actif": True, + "type_facture": 1, + "est_prospect": False, + "bl_en_facture": 0, + "saut_page": 0, + "validation_echeance": 0, + "controle_encours": 1, + "exclure_relance": False, + "exclure_penalites": False, + "bon_a_payer": 0, + "priorite_livraison": 1, + "livraison_partielle": 1, + "delai_transport": 2, + "delai_appro": 0, + "commentaire": "Client important", + "section_analytique": "", + "mode_reglement_code": 1, + "surveillance_active": True, + "coface": "COF12345", + "forme_juridique": "SARL", + "effectif": "50-99", + "sv_regularite": "", + "sv_cotation": "", + "sv_objet_maj": "", + "sv_chiffre_affaires": 2500000.0, + "sv_resultat": 150000.0, + "compte_general": "4110000", + "categorie_tarif": 0, + "categorie_compta": 0, + } + } + class ArticleResponse(BaseModel): """ Modèle de réponse pour un article Sage @@ -2405,7 +2575,7 @@ async def rechercher_clients(query: Optional[str] = Query(None)): raise HTTPException(500, str(e)) -@app.get("/clients/{code}", tags=["Clients"]) +@app.get("/clients/{code}", response_model=ClientDetails , tags=["Clients"]) async def lire_client_detail(code: str): try: client = sage_client.lire_client(code) @@ -2413,7 +2583,7 @@ async def lire_client_detail(code: str): if not client: raise HTTPException(404, f"Client {code} introuvable") - return {"success": True, "data": client} + return ClientDetails(**client) except HTTPException: raise @@ -4271,7 +4441,7 @@ async def lire_prospect(code: str): -@app.get("/fournisseurs", tags=["Fournisseurs"]) +@app.get("/fournisseurs", response_model=List[FournisseurDetails], tags=["Fournisseurs"]) async def rechercher_fournisseurs(query: Optional[str] = Query(None)): try: fournisseurs = sage_client.lister_fournisseurs(filtre=query or "") @@ -4281,7 +4451,7 @@ async def rechercher_fournisseurs(query: Optional[str] = Query(None)): if len(fournisseurs) == 0: logger.warning("Aucun fournisseur retourné - vérifier la gateway Windows") - return fournisseurs + return [FournisseurDetails(**f) for f in fournisseurs] except Exception as e: logger.error(f"Erreur recherche fournisseurs: {e}") @@ -4313,7 +4483,7 @@ async def ajouter_fournisseur( raise HTTPException(500, str(e)) -@app.put("/fournisseurs/{code}", tags=["Fournisseurs"]) +@app.put("/fournisseurs/{code}", response_model=FournisseurDetails, tags=["Fournisseurs"]) async def modifier_fournisseur( code: str, fournisseur_update: FournisseurUpdateRequest, @@ -4326,11 +4496,7 @@ async def modifier_fournisseur( logger.info(f"Fournisseur {code} modifié avec succès") - return { - "success": True, - "message": f"Fournisseur {code} modifié avec succès", - "fournisseur": resultat, - } + return FournisseurDetails(**resultat) except ValueError as e: logger.warning(f"Erreur métier modification fournisseur {code}: {e}")