From e9e4aff0dbc024796ad1ba652f2c00d65244eed0 Mon Sep 17 00:00:00 2001 From: Fanilo-Nantenaina Date: Sat, 27 Dec 2025 18:08:03 +0300 Subject: [PATCH] Enriched article response --- api.py | 889 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 500 insertions(+), 389 deletions(-) diff --git a/api.py b/api.py index c4d0cb4..764cc97 100644 --- a/api.py +++ b/api.py @@ -528,292 +528,494 @@ class FournisseurDetails(BaseModel): + +class EmplacementStockModel(BaseModel): + """Détail du stock dans un emplacement spécifique""" + + depot: str = Field(..., description="Numéro du dépôt (DE_No)") + emplacement: str = Field(..., description="Code emplacement (DP_No)") + + qte_stockee: float = Field(0.0, description="Quantité stockée (AE_QteSto)") + qte_preparee: float = Field(0.0, description="Quantité préparée (AE_QtePrepa)") + qte_a_controler: float = Field(0.0, description="Quantité à contrôler (AE_QteAControler)") + + date_creation: Optional[datetime] = Field(None, description="Date création") + date_modification: Optional[datetime] = Field(None, description="Date modification") + + depot_num: Optional[str] = Field(None, description="Numéro dépôt") + depot_nom: Optional[str] = Field(None, description="Nom du dépôt (DE_Intitule)") + depot_code: Optional[str] = Field(None, description="Code dépôt (DE_Code)") + depot_adresse: Optional[str] = Field(None, description="Adresse (DE_Adresse)") + depot_complement: Optional[str] = Field(None, description="Complément adresse") + depot_code_postal: Optional[str] = Field(None, description="Code postal") + depot_ville: Optional[str] = Field(None, description="Ville") + depot_contact: Optional[str] = Field(None, description="Contact") + depot_est_principal: Optional[bool] = Field(None, description="Dépôt principal (DE_Principal)") + depot_categorie_compta: Optional[int] = Field(None, description="Catégorie comptable") + depot_region: Optional[str] = Field(None, description="Région") + depot_pays: Optional[str] = Field(None, description="Pays") + depot_email: Optional[str] = Field(None, description="Email") + depot_telephone: Optional[str] = Field(None, description="Téléphone") + depot_fax: Optional[str] = Field(None, description="Fax") + depot_emplacement_defaut: Optional[str] = Field(None, description="Emplacement par défaut") + depot_exclu: Optional[bool] = Field(None, description="Dépôt exclu") + + emplacement_code: Optional[str] = Field(None, description="Code emplacement (DP_Code)") + emplacement_libelle: Optional[str] = Field(None, description="Libellé emplacement (DP_Intitule)") + emplacement_zone: Optional[str] = Field(None, description="Zone (DP_Zone)") + emplacement_type: Optional[int] = Field(None, description="Type emplacement (DP_Type)") + + class Config: + json_schema_extra = { + "example": { + "depot": "01", + "emplacement": "A1-01", + "qte_stockee": 100.0, + "qte_preparee": 5.0, + "depot_nom": "Dépôt principal", + "depot_ville": "Paris", + "emplacement_libelle": "Allée A, Niveau 1, Case 01", + "emplacement_zone": "Zone A" + } + } + + +class GammeArticleModel(BaseModel): + """Gamme d'un article (taille, couleur, etc.)""" + + numero_gamme: int = Field(..., description="Numéro de gamme (AG_No)") + enumere: str = Field(..., description="Code énuméré (EG_Enumere)") + type_gamme: int = Field(0, description="Type de gamme (AG_Type)") + + date_creation: Optional[datetime] = Field(None, description="Date création") + date_modification: Optional[datetime] = Field(None, description="Date modification") + + ligne: Optional[int] = Field(None, description="Ligne énuméré (EG_Ligne)") + borne_sup: Optional[float] = Field(None, description="Borne supérieure (EG_BorneSup)") + gamme_nom: Optional[str] = Field(None, description="Nom de la gamme (P_GAMME.G_Intitule)") + + class Config: + json_schema_extra = { + "example": { + "numero_gamme": 1, + "enumere": "001", + "type_gamme": 0, + "ligne": 1, + "gamme_nom": "Taille" + } + } + + +class TarifClientModel(BaseModel): + """Tarif spécifique pour un client ou catégorie tarifaire""" + + categorie: int = Field(..., description="Catégorie tarifaire (AC_Categorie)") + client_num: Optional[str] = Field(None, description="Numéro client (CT_Num)") + + prix_vente: float = Field(0.0, description="Prix de vente HT (AC_PrixVen)") + coefficient: float = Field(0.0, description="Coefficient (AC_Coef)") + prix_ttc: float = Field(0.0, description="Prix TTC (AC_PrixTTC)") + arrondi: float = Field(0.0, description="Arrondi (AC_Arrondi)") + qte_montant: float = Field(0.0, description="Quantité montant (AC_QteMont)") + + enumere_gamme: int = Field(0, description="Énuméré gamme (EG_Champ)") + prix_devise: float = Field(0.0, description="Prix en devise (AC_PrixDev)") + devise: int = Field(0, description="Code devise (AC_Devise)") + + remise: float = Field(0.0, description="Remise (AC_Remise)") + mode_calcul: int = Field(0, description="Mode de calcul (AC_Calcul)") + type_remise: int = Field(0, description="Type de remise (AC_TypeRem)") + ref_client: Optional[str] = Field(None, description="Référence client (AC_RefClient)") + + coef_nouveau: float = Field(0.0, description="Nouveau coefficient (AC_CoefNouv)") + prix_vente_nouveau: float = Field(0.0, description="Nouveau prix vente (AC_PrixVenNouv)") + prix_devise_nouveau: float = Field(0.0, description="Nouveau prix devise (AC_PrixDevNouv)") + remise_nouvelle: float = Field(0.0, description="Nouvelle remise (AC_RemiseNouv)") + date_application: Optional[datetime] = Field(None, description="Date application (AC_DateApplication)") + + date_creation: Optional[datetime] = Field(None, description="Date création") + date_modification: Optional[datetime] = Field(None, description="Date modification") + + class Config: + json_schema_extra = { + "example": { + "categorie": 1, + "client_num": "CLI001", + "prix_vente": 110.00, + "coefficient": 1.294, + "remise": 12.0 + } + } + + +class ComposantModel(BaseModel): + """Composant/Opération de nomenclature""" + + operation: str = Field(..., description="Code opération (AT_Operation)") + code_ressource: Optional[str] = Field(None, description="Code ressource (RP_Code)") + + temps: float = Field(0.0, description="Temps nécessaire (AT_Temps)") + type: int = Field(0, description="Type composant (AT_Type)") + description: Optional[str] = Field(None, description="Description (AT_Description)") + ordre: int = Field(0, description="Ordre d'exécution (AT_Ordre)") + + gamme_1_comp: int = Field(0, description="Gamme 1 composant (AG_No1Comp)") + gamme_2_comp: int = Field(0, description="Gamme 2 composant (AG_No2Comp)") + + type_ressource: int = Field(0, description="Type ressource (AT_TypeRessource)") + chevauche: int = Field(0, description="Chevauchement (AT_Chevauche)") + demarre: int = Field(0, description="Démarrage (AT_Demarre)") + operation_chevauche: Optional[str] = Field(None, description="Opération chevauchée (AT_OperationChevauche)") + valeur_chevauche: float = Field(0.0, description="Valeur chevauchement (AT_ValeurChevauche)") + type_chevauche: int = Field(0, description="Type chevauchement (AT_TypeChevauche)") + + date_creation: Optional[datetime] = Field(None, description="Date création") + date_modification: Optional[datetime] = Field(None, description="Date modification") + + class Config: + json_schema_extra = { + "example": { + "operation": "OP010", + "code_ressource": "RES01", + "temps": 15.5, + "description": "Montage pièce A", + "ordre": 10 + } + } + + +class ComptaArticleModel(BaseModel): + """Comptabilité spécifique d'un article""" + + champ: int = Field(..., description="Champ (ACP_Champ)") + compte_general: Optional[str] = Field(None, description="Compte général (ACP_ComptaCPT_CompteG)") + compte_auxiliaire: Optional[str] = Field(None, description="Compte auxiliaire (ACP_ComptaCPT_CompteA)") + + taxe_1: Optional[str] = Field(None, description="Taxe 1 (ACP_ComptaCPT_Taxe1)") + taxe_2: Optional[str] = Field(None, description="Taxe 2 (ACP_ComptaCPT_Taxe2)") + taxe_3: Optional[str] = Field(None, description="Taxe 3 (ACP_ComptaCPT_Taxe3)") + + taxe_date_1: Optional[datetime] = Field(None, description="Date taxe 1") + taxe_date_2: Optional[datetime] = Field(None, description="Date taxe 2") + taxe_date_3: Optional[datetime] = Field(None, description="Date taxe 3") + + taxe_anc_1: Optional[str] = Field(None, description="Ancienne taxe 1") + taxe_anc_2: Optional[str] = Field(None, description="Ancienne taxe 2") + taxe_anc_3: Optional[str] = Field(None, description="Ancienne taxe 3") + + type_facture: int = Field(0, description="Type de facture (ACP_TypeFacture)") + + date_creation: Optional[datetime] = Field(None, description="Date création") + date_modification: Optional[datetime] = Field(None, description="Date modification") + + class Config: + json_schema_extra = { + "example": { + "champ": 1, + "compte_general": "707100", + "taxe_1": "TVA20", + "type_facture": 0 + } + } + + +class FournisseurArticleModel(BaseModel): + """Fournisseur d'un article""" + + fournisseur_num: str = Field(..., description="Numéro fournisseur (CT_Num)") + ref_fournisseur: Optional[str] = Field(None, description="Référence fournisseur (AF_RefFourniss)") + + prix_achat: float = Field(0.0, description="Prix d'achat (AF_PrixAch)") + unite: Optional[str] = Field(None, description="Unité (AF_Unite)") + conversion: float = Field(0.0, description="Conversion (AF_Conversion)") + + delai_appro: int = Field(0, description="Délai approvisionnement (AF_DelaiAppro)") + garantie: int = Field(0, description="Garantie (AF_Garantie)") + colisage: int = Field(0, description="Colisage (AF_Colisage)") + qte_mini: float = Field(0.0, description="Quantité minimum (AF_QteMini)") + qte_montant: float = Field(0.0, description="Quantité montant (AF_QteMont)") + + enumere_gamme: int = Field(0, description="Énuméré gamme (EG_Champ)") + est_principal: bool = Field(False, description="Fournisseur principal (AF_Principal)") + + prix_devise: float = Field(0.0, description="Prix devise (AF_PrixDev)") + devise: int = Field(0, description="Code devise (AF_Devise)") + remise: float = Field(0.0, description="Remise (AF_Remise)") + conversion_devise: float = Field(0.0, description="Conversion devise (AF_ConvDiv)") + type_remise: int = Field(0, description="Type remise (AF_TypeRem)") + + code_barre_fournisseur: Optional[str] = Field(None, description="Code-barres fournisseur (AF_CodeBarre)") + + prix_achat_nouveau: float = Field(0.0, description="Nouveau prix achat (AF_PrixAchNouv)") + prix_devise_nouveau: float = Field(0.0, description="Nouveau prix devise (AF_PrixDevNouv)") + remise_nouvelle: float = Field(0.0, description="Nouvelle remise (AF_RemiseNouv)") + date_application: Optional[datetime] = Field(None, description="Date application (AF_DateApplication)") + + date_creation: Optional[datetime] = Field(None, description="Date création") + date_modification: Optional[datetime] = Field(None, description="Date modification") + + class Config: + json_schema_extra = { + "example": { + "fournisseur_num": "F001", + "ref_fournisseur": "REF-FOURN-001", + "prix_achat": 85.00, + "delai_appro": 15, + "est_principal": True + } + } + + +class ReferenceEnumereeModel(BaseModel): + """Référence énumérée (article avec gammes)""" + + gamme_1: int = Field(0, description="Gamme 1 (AG_No1)") + gamme_2: int = Field(0, description="Gamme 2 (AG_No2)") + reference_enumeree: str = Field(..., description="Référence énumérée (AE_Ref)") + + prix_achat: float = Field(0.0, description="Prix achat (AE_PrixAch)") + code_barre: Optional[str] = Field(None, description="Code-barres (AE_CodeBarre)") + prix_achat_nouveau: float = Field(0.0, description="Nouveau prix achat (AE_PrixAchNouv)") + edi_code: Optional[str] = Field(None, description="Code EDI (AE_EdiCode)") + en_sommeil: bool = Field(False, description="En sommeil (AE_Sommeil)") + + date_creation: Optional[datetime] = Field(None, description="Date création") + date_modification: Optional[datetime] = Field(None, description="Date modification") + + class Config: + json_schema_extra = { + "example": { + "gamme_1": 1, + "gamme_2": 3, + "reference_enumeree": "ART001-T1-C3", + "prix_achat": 85.00 + } + } + + +class MediaArticleModel(BaseModel): + """Média attaché à un article (photo, document, etc.)""" + + commentaire: Optional[str] = Field(None, description="Commentaire (ME_Commentaire)") + fichier: Optional[str] = Field(None, description="Nom fichier (ME_Fichier)") + type_mime: Optional[str] = Field(None, description="Type MIME (ME_TypeMIME)") + origine: int = Field(0, description="Origine (ME_Origine)") + ged_id: Optional[str] = Field(None, description="ID GED (ME_GedId)") + + date_creation: Optional[datetime] = Field(None, description="Date création") + date_modification: Optional[datetime] = Field(None, description="Date modification") + + class Config: + json_schema_extra = { + "example": { + "commentaire": "Photo produit principale", + "fichier": "ART001_photo1.jpg", + "type_mime": "image/jpeg" + } + } + + +class PrixGammeModel(BaseModel): + """Prix spécifique par combinaison de gammes""" + + gamme_1: int = Field(0, description="Gamme 1 (AG_No1)") + gamme_2: int = Field(0, description="Gamme 2 (AG_No2)") + prix_net: float = Field(0.0, description="Prix net (AR_PUNet)") + cout_standard: float = Field(0.0, description="Coût standard (AR_CoutStd)") + + date_creation: Optional[datetime] = Field(None, description="Date création") + date_modification: Optional[datetime] = Field(None, description="Date modification") + + class Config: + json_schema_extra = { + "example": { + "gamme_1": 1, + "gamme_2": 3, + "prix_net": 125.50, + "cout_standard": 82.30 + } + } + + + class ArticleResponse(BaseModel): + """Article complet avec tous les enrichissements disponibles""" reference: str = Field(..., description="Référence article (AR_Ref)") designation: str = Field(..., description="Désignation principale (AR_Design)") - code_ean: Optional[str] = Field( - None, description="Code EAN / Code-barres principal (AR_CodeBarre)" + code_ean: Optional[str] = Field(None, description="Code EAN / Code-barres principal (AR_CodeBarre)") + code_barre: Optional[str] = Field(None, description="Code-barres (alias de code_ean)") + edi_code: Optional[str] = Field(None, description="Code EDI (AR_EdiCode)") + raccourci: Optional[str] = Field(None, description="Code raccourci (AR_Raccourci)") + + prix_vente: float = Field(..., description="Prix de vente HT unitaire (AR_PrixVen)") + prix_achat: Optional[float] = Field(None, description="Prix d'achat HT (AR_PrixAch)") + coef: Optional[float] = Field(None, description="Coefficient multiplicateur (AR_Coef)") + prix_net: Optional[float] = Field(None, description="Prix unitaire net (AR_PUNet)") + + prix_achat_nouveau: Optional[float] = Field(None, description="Nouveau prix d'achat à venir (AR_PrixAchNouv)") + coef_nouveau: Optional[float] = Field(None, description="Nouveau coefficient à venir (AR_CoefNouv)") + prix_vente_nouveau: Optional[float] = Field(None, description="Nouveau prix de vente à venir (AR_PrixVenNouv)") + date_application_prix: Optional[str] = Field(None, description="Date d'application des nouveaux prix (AR_DateApplication)") + + cout_standard: Optional[float] = Field(None, description="Coût standard (AR_CoutStd)") + + stock_reel: float = Field(default=0.0, description="Stock réel total (F_ARTSTOCK.AS_QteSto)") + stock_mini: Optional[float] = Field(None, description="Stock minimum (F_ARTSTOCK.AS_QteMini)") + stock_maxi: Optional[float] = Field(None, description="Stock maximum (F_ARTSTOCK.AS_QteMaxi)") + stock_reserve: Optional[float] = Field(None, description="Stock réservé / en commande client (F_ARTSTOCK.AS_QteRes)") + stock_commande: Optional[float] = Field(None, description="Stock en commande fournisseur (F_ARTSTOCK.AS_QteCom)") + stock_disponible: Optional[float] = Field(None, description="Stock disponible = réel - réservé") + + emplacements: List[EmplacementStockModel] = Field( + default_factory=list, + description="Détail du stock par emplacement (F_ARTSTOCKEMPL + F_DEPOT + F_DEPOTEMPL)" ) - code_barre: Optional[str] = Field( - None, description="Code-barres (alias de code_ean)" + nb_emplacements: int = Field(0, description="Nombre d'emplacements") + + suivi_stock: Optional[bool] = Field(None, description="Suivi de stock activé (AR_SuiviStock)") + nomenclature: Optional[bool] = Field(None, description="Article avec nomenclature (AR_Nomencl)") + qte_composant: Optional[float] = Field(None, description="Quantité de composant (AR_QteComp)") + qte_operatoire: Optional[float] = Field(None, description="Quantité opératoire (AR_QteOperatoire)") + + unite_vente: Optional[str] = Field(None, max_length=10, description="Unité de vente (AR_UniteVen)") + unite_poids: Optional[str] = Field(None, max_length=10, description="Unité de poids (AR_UnitePoids)") + poids_net: Optional[float] = Field(None, description="Poids net unitaire en kg (AR_PoidsNet)") + poids_brut: Optional[float] = Field(None, description="Poids brut unitaire en kg (AR_PoidsBrut)") + + gamme_1: Optional[str] = Field(None, description="Énumération gamme 1 (AR_Gamme1)") + gamme_2: Optional[str] = Field(None, description="Énumération gamme 2 (AR_Gamme2)") + + gammes: List[GammeArticleModel] = Field( + default_factory=list, + description="Détail des gammes (F_ARTGAMME + F_ENUMGAMME + P_GAMME)" ) - edi_code: Optional[str] = Field( - None, description="Code EDI (AR_EdiCode)" + nb_gammes: int = Field(0, description="Nombre de gammes") + + tarifs_clients: List[TarifClientModel] = Field( + default_factory=list, + description="Tarifs spécifiques par client/catégorie (F_ARTCLIENT)" ) - raccourci: Optional[str] = Field( - None, description="Code raccourci (AR_Raccourci)" + nb_tarifs_clients: int = Field(0, description="Nombre de tarifs clients") + + composants: List[ComposantModel] = Field( + default_factory=list, + description="Composants/Opérations de production (F_ARTCOMPO)" + ) + nb_composants: int = Field(0, description="Nombre de composants") + + compta_vente: List[ComptaArticleModel] = Field( + default_factory=list, + description="Comptabilité vente (F_ARTCOMPTA type 0)" + ) + compta_achat: List[ComptaArticleModel] = Field( + default_factory=list, + description="Comptabilité achat (F_ARTCOMPTA type 1)" + ) + compta_stock: List[ComptaArticleModel] = Field( + default_factory=list, + description="Comptabilité stock (F_ARTCOMPTA type 2)" ) - prix_vente: float = Field( - ..., description="Prix de vente HT unitaire (AR_PrixVen)" - ) - prix_achat: Optional[float] = Field( - None, description="Prix d'achat HT (AR_PrixAch)" - ) - coef: Optional[float] = Field( - None, description="Coefficient multiplicateur (AR_Coef)" - ) - prix_net: Optional[float] = Field( - None, description="Prix unitaire net (AR_PUNet)" + fournisseurs: List[FournisseurArticleModel] = Field( + default_factory=list, + description="Tous les fournisseurs de l'article (F_ARTFOURNISS)" ) + nb_fournisseurs: int = Field(0, description="Nombre de fournisseurs") - prix_achat_nouveau: Optional[float] = Field( - None, description="Nouveau prix d'achat à venir (AR_PrixAchNouv)" - ) - coef_nouveau: Optional[float] = Field( - None, description="Nouveau coefficient à venir (AR_CoefNouv)" - ) - prix_vente_nouveau: Optional[float] = Field( - None, description="Nouveau prix de vente à venir (AR_PrixVenNouv)" - ) - date_application_prix: Optional[str] = Field( - None, description="Date d'application des nouveaux prix (AR_DateApplication)" + refs_enumerees: List[ReferenceEnumereeModel] = Field( + default_factory=list, + description="Références énumérées (F_ARTENUMREF)" ) + nb_refs_enumerees: int = Field(0, description="Nombre de références énumérées") - cout_standard: Optional[float] = Field( - None, description="Coût standard (AR_CoutStd)" + medias: List[MediaArticleModel] = Field( + default_factory=list, + description="Médias attachés (F_ARTICLEMEDIA)" ) + nb_medias: int = Field(0, description="Nombre de médias") - stock_reel: float = Field( - default=0.0, description="Stock réel total (F_ARTSTOCK.AS_QteSto)" - ) - stock_mini: Optional[float] = Field( - None, description="Stock minimum (F_ARTSTOCK.AS_QteMini)" - ) - stock_maxi: Optional[float] = Field( - None, description="Stock maximum (F_ARTSTOCK.AS_QteMaxi)" - ) - stock_reserve: Optional[float] = Field( - None, description="Stock réservé / en commande client (F_ARTSTOCK.AS_QteRes)" - ) - stock_commande: Optional[float] = Field( - None, description="Stock en commande fournisseur (F_ARTSTOCK.AS_QteCom)" - ) - stock_disponible: Optional[float] = Field( - None, description="Stock disponible = réel - réservé" - ) - suivi_stock: Optional[bool] = Field( - None, description="Suivi de stock activé (AR_SuiviStock)" - ) - nomenclature: Optional[bool] = Field( - None, description="Article avec nomenclature (AR_Nomencl)" - ) - qte_composant: Optional[float] = Field( - None, description="Quantité de composant (AR_QteComp)" - ) - qte_operatoire: Optional[float] = Field( - None, description="Quantité opératoire (AR_QteOperatoire)" - ) - - unite_vente: Optional[str] = Field( - None, max_length=10, description="Unité de vente (AR_UniteVen)" - ) - unite_poids: Optional[str] = Field( - None, max_length=10, description="Unité de poids (AR_UnitePoids)" - ) - - poids_net: Optional[float] = Field( - None, description="Poids net unitaire en kg (AR_PoidsNet)" - ) - poids_brut: Optional[float] = Field( - None, description="Poids brut unitaire en kg (AR_PoidsBrut)" - ) - - gamme_1: Optional[str] = Field( - None, description="Énumération gamme 1 (AR_Gamme1)" - ) - gamme_2: Optional[str] = Field( - None, description="Énumération gamme 2 (AR_Gamme2)" + prix_gammes: List[PrixGammeModel] = Field( + default_factory=list, + description="Prix par combinaison de gammes (F_ARTPRIX)" ) + nb_prix_gammes: int = Field(0, description="Nombre de prix par gammes") type_article: Optional[int] = Field( - None, - ge=0, - le=3, + None, ge=0, le=3, description="Type : 0=Article, 1=Prestation, 2=Divers, 3=Nomenclature (AR_Type)" ) - type_article_libelle: Optional[str] = Field( - None, description="Libellé du type d'article" - ) + type_article_libelle: Optional[str] = Field(None, description="Libellé du type d'article") - famille_code: Optional[str] = Field( - None, max_length=20, description="Code famille (FA_CodeFamille)" - ) - famille_libelle: Optional[str] = Field( - None, description="Libellé de la famille (F_FAMILLE.FA_Intitule)" - ) - famille_type: Optional[int] = Field( - None, description="Type de famille : 0=Détail, 1=Total (F_FAMILLE.FA_Type)" - ) - famille_unite_vente: Optional[str] = Field( - None, description="Unité de vente de la famille (F_FAMILLE.FA_UniteVen)" - ) - famille_coef: Optional[float] = Field( - None, description="Coefficient de la famille (F_FAMILLE.FA_Coef)" - ) - famille_suivi_stock: Optional[bool] = Field( - None, description="Suivi stock de la famille (F_FAMILLE.FA_SuiviStock)" - ) - famille_garantie: Optional[int] = Field( - None, description="Garantie de la famille (F_FAMILLE.FA_Garantie)" - ) - famille_unite_poids: Optional[str] = Field( - None, description="Unité de poids de la famille (F_FAMILLE.FA_UnitePoids)" - ) - famille_delai: Optional[int] = Field( - None, description="Délai de la famille (F_FAMILLE.FA_Delai)" - ) - famille_nb_colis: Optional[int] = Field( - None, description="Nombre de colis de la famille (F_FAMILLE.FA_NbColis)" - ) - famille_code_fiscal: Optional[str] = Field( - None, description="Code fiscal de la famille (F_FAMILLE.FA_CodeFiscal)" - ) - famille_escompte: Optional[bool] = Field( - None, description="Escompte de la famille (F_FAMILLE.FA_Escompte)" - ) - famille_centrale: Optional[bool] = Field( - None, description="Famille centrale (F_FAMILLE.FA_Central)" - ) - famille_nature: Optional[int] = Field( - None, description="Nature de la famille (F_FAMILLE.FA_Nature)" - ) - famille_hors_stat: Optional[bool] = Field( - None, description="Hors statistique famille (F_FAMILLE.FA_HorsStat)" - ) - famille_pays: Optional[str] = Field( - None, description="Pays de la famille (F_FAMILLE.FA_Pays)" - ) + famille_code: Optional[str] = Field(None, max_length=20, description="Code famille (FA_CodeFamille)") + famille_libelle: Optional[str] = Field(None, description="Libellé de la famille (F_FAMILLE.FA_Intitule)") + famille_type: Optional[int] = Field(None, description="Type de famille : 0=Détail, 1=Total (F_FAMILLE.FA_Type)") + famille_unite_vente: Optional[str] = Field(None, description="Unité de vente de la famille (F_FAMILLE.FA_UniteVen)") + famille_coef: Optional[float] = Field(None, description="Coefficient de la famille (F_FAMILLE.FA_Coef)") + famille_suivi_stock: Optional[bool] = Field(None, description="Suivi stock de la famille (F_FAMILLE.FA_SuiviStock)") + famille_garantie: Optional[int] = Field(None, description="Garantie de la famille (F_FAMILLE.FA_Garantie)") + famille_unite_poids: Optional[str] = Field(None, description="Unité de poids de la famille (F_FAMILLE.FA_UnitePoids)") + famille_delai: Optional[int] = Field(None, description="Délai de la famille (F_FAMILLE.FA_Delai)") + famille_nb_colis: Optional[int] = Field(None, description="Nombre de colis de la famille (F_FAMILLE.FA_NbColis)") + famille_code_fiscal: Optional[str] = Field(None, description="Code fiscal de la famille (F_FAMILLE.FA_CodeFiscal)") + famille_escompte: Optional[bool] = Field(None, description="Escompte de la famille (F_FAMILLE.FA_Escompte)") + famille_centrale: Optional[bool] = Field(None, description="Famille centrale (F_FAMILLE.FA_Central)") + famille_nature: Optional[int] = Field(None, description="Nature de la famille (F_FAMILLE.FA_Nature)") + famille_hors_stat: Optional[bool] = Field(None, description="Hors statistique famille (F_FAMILLE.FA_HorsStat)") + famille_pays: Optional[str] = Field(None, description="Pays de la famille (F_FAMILLE.FA_Pays)") - nature: Optional[int] = Field( - None, description="Nature de l'article (AR_Nature)" - ) - garantie: Optional[int] = Field( - None, description="Durée de garantie en mois (AR_Garantie)" - ) - code_fiscal: Optional[str] = Field( - None, max_length=10, description="Code fiscal/TVA (AR_CodeFiscal)" - ) - pays: Optional[str] = Field( - None, description="Pays d'origine (AR_Pays)" - ) + nature: Optional[int] = Field(None, description="Nature de l'article (AR_Nature)") + garantie: Optional[int] = Field(None, description="Durée de garantie en mois (AR_Garantie)") + code_fiscal: Optional[str] = Field(None, max_length=10, description="Code fiscal/TVA (AR_CodeFiscal)") + pays: Optional[str] = Field(None, description="Pays d'origine (AR_Pays)") - fournisseur_principal: Optional[int] = Field( - None, description="N° compte du fournisseur principal (CO_No → F_COMPTET.CT_Num)" - ) - fournisseur_nom: Optional[str] = Field( - None, description="Nom du fournisseur principal (F_COMPTET.CT_Intitule)" - ) - conditionnement: Optional[str] = Field( - None, description="Conditionnement d'achat (AR_Condition)" - ) - nb_colis: Optional[int] = Field( - None, description="Nombre de colis par unité (AR_NbColis)" - ) - prevision: Optional[bool] = Field( - None, description="Gestion en prévision (AR_Prevision)" - ) + fournisseur_principal: Optional[int] = Field(None, description="N° compte du fournisseur principal (CO_No → F_COMPTET.CT_Num)") + fournisseur_nom: Optional[str] = Field(None, description="Nom du fournisseur principal (F_COMPTET.CT_Intitule)") - est_actif: bool = Field( - default=True, description="Article actif (AR_Sommeil = 0)" - ) - en_sommeil: bool = Field( - default=False, description="Article en sommeil (AR_Sommeil = 1)" - ) - article_substitut: Optional[str] = Field( - None, description="Référence article de substitution (AR_Substitut)" - ) - soumis_escompte: Optional[bool] = Field( - None, description="Soumis à escompte (AR_Escompte)" - ) - delai: Optional[int] = Field( - None, description="Délai de livraison en jours (AR_Delai)" - ) + conditionnement: Optional[str] = Field(None, description="Conditionnement d'achat (AR_Condition)") + conditionnement_qte: Optional[float] = Field(None, description="Quantité conditionnement (F_ENUMCOND.EC_Quantite)") + conditionnement_edi: Optional[str] = Field(None, description="Code EDI conditionnement (F_ENUMCOND.EC_EdiCode)") - publie: Optional[bool] = Field( - None, description="Publié sur web/catalogue (AR_Publie)" - ) - hors_statistique: Optional[bool] = Field( - None, description="Exclus des statistiques (AR_HorsStat)" - ) - vente_debit: Optional[bool] = Field( - None, description="Vente au débit (AR_VteDebit)" - ) - non_imprimable: Optional[bool] = Field( - None, description="Non imprimable sur documents (AR_NotImp)" - ) - transfere: Optional[bool] = Field( - None, description="Article transféré (AR_Transfere)" - ) - contremarque: Optional[bool] = Field( - None, description="Article en contremarque (AR_Contremarque)" - ) - fact_poids: Optional[bool] = Field( - None, description="Facturation au poids (AR_FactPoids)" - ) - fact_forfait: Optional[bool] = Field( - None, description="Facturation au forfait (AR_FactForfait)" - ) - saisie_variable: Optional[bool] = Field( - None, description="Saisie variable (AR_SaisieVar)" - ) - fictif: Optional[bool] = Field( - None, description="Article fictif (AR_Fictif)" - ) - sous_traitance: Optional[bool] = Field( - None, description="Article en sous-traitance (AR_SousTraitance)" - ) - criticite: Optional[int] = Field( - None, description="Niveau de criticité (AR_Criticite)" - ) + nb_colis: Optional[int] = Field(None, description="Nombre de colis par unité (AR_NbColis)") + prevision: Optional[bool] = Field(None, description="Gestion en prévision (AR_Prevision)") - reprise_code_defaut: Optional[str] = Field( - None, description="Code reprise par défaut (RP_CodeDefaut)" - ) - delai_fabrication: Optional[int] = Field( - None, description="Délai de fabrication (AR_DelaiFabrication)" - ) - delai_peremption: Optional[int] = Field( - None, description="Délai de péremption (AR_DelaiPeremption)" - ) - delai_securite: Optional[int] = Field( - None, description="Délai de sécurité (AR_DelaiSecurite)" - ) - type_lancement: Optional[int] = Field( - None, description="Type de lancement production (AR_TypeLancement)" - ) - cycle: Optional[int] = Field( - None, description="Cycle de production (AR_Cycle)" - ) + est_actif: bool = Field(default=True, description="Article actif (AR_Sommeil = 0)") + en_sommeil: bool = Field(default=False, description="Article en sommeil (AR_Sommeil = 1)") + article_substitut: Optional[str] = Field(None, description="Référence article de substitution (AR_Substitut)") + soumis_escompte: Optional[bool] = Field(None, description="Soumis à escompte (AR_Escompte)") + delai: Optional[int] = Field(None, description="Délai de livraison en jours (AR_Delai)") - photo: Optional[str] = Field( - None, description="Chemin/nom du fichier photo (AR_Photo)" - ) - langue_1: Optional[str] = Field( - None, description="Texte en langue 1 (AR_Langue1)" - ) - langue_2: Optional[str] = Field( - None, description="Texte en langue 2 (AR_Langue2)" - ) + publie: Optional[bool] = Field(None, description="Publié sur web/catalogue (AR_Publie)") + hors_statistique: Optional[bool] = Field(None, description="Exclus des statistiques (AR_HorsStat)") + vente_debit: Optional[bool] = Field(None, description="Vente au débit (AR_VteDebit)") + non_imprimable: Optional[bool] = Field(None, description="Non imprimable sur documents (AR_NotImp)") + transfere: Optional[bool] = Field(None, description="Article transféré (AR_Transfere)") + contremarque: Optional[bool] = Field(None, description="Article en contremarque (AR_Contremarque)") + fact_poids: Optional[bool] = Field(None, description="Facturation au poids (AR_FactPoids)") + fact_forfait: Optional[bool] = Field(None, description="Facturation au forfait (AR_FactForfait)") + saisie_variable: Optional[bool] = Field(None, description="Saisie variable (AR_SaisieVar)") + fictif: Optional[bool] = Field(None, description="Article fictif (AR_Fictif)") + sous_traitance: Optional[bool] = Field(None, description="Article en sous-traitance (AR_SousTraitance)") + criticite: Optional[int] = Field(None, description="Niveau de criticité (AR_Criticite)") - frais_01_denomination: Optional[str] = Field( - None, description="Dénomination frais 1 (AR_Frais01FR_Denomination)" - ) - frais_02_denomination: Optional[str] = Field( - None, description="Dénomination frais 2 (AR_Frais02FR_Denomination)" - ) - frais_03_denomination: Optional[str] = Field( - None, description="Dénomination frais 3 (AR_Frais03FR_Denomination)" - ) + reprise_code_defaut: Optional[str] = Field(None, description="Code reprise par défaut (RP_CodeDefaut)") + delai_fabrication: Optional[int] = Field(None, description="Délai de fabrication (AR_DelaiFabrication)") + delai_peremption: Optional[int] = Field(None, description="Délai de péremption (AR_DelaiPeremption)") + delai_securite: Optional[int] = Field(None, description="Délai de sécurité (AR_DelaiSecurite)") + type_lancement: Optional[int] = Field(None, description="Type de lancement production (AR_TypeLancement)") + cycle: Optional[int] = Field(None, description="Cycle de production (AR_Cycle)") - tva_code: Optional[str] = Field( - None, description="Code TVA (F_TAXE.TA_Code)" - ) - tva_taux: Optional[float] = Field( - None, description="Taux de TVA en % (F_TAXE.TA_Taux)" - ) + photo: Optional[str] = Field(None, description="Chemin/nom du fichier photo (AR_Photo)") + langue_1: Optional[str] = Field(None, description="Texte en langue 1 (AR_Langue1)") + langue_2: Optional[str] = Field(None, description="Texte en langue 2 (AR_Langue2)") + + frais_01_denomination: Optional[str] = Field(None, description="Dénomination frais 1 (AR_Frais01FR_Denomination)") + frais_02_denomination: Optional[str] = Field(None, description="Dénomination frais 2 (AR_Frais02FR_Denomination)") + frais_03_denomination: Optional[str] = Field(None, description="Dénomination frais 3 (AR_Frais03FR_Denomination)") + + tva_code: Optional[str] = Field(None, description="Code TVA (F_TAXE.TA_Code)") + tva_taux: Optional[float] = Field(None, description="Taux de TVA en % (F_TAXE.TA_Taux)") stat_01: Optional[str] = Field(None, description="Statistique 1 (AR_Stat01)") stat_02: Optional[str] = Field(None, description="Statistique 2 (AR_Stat02)") @@ -821,149 +1023,74 @@ class ArticleResponse(BaseModel): stat_04: Optional[str] = Field(None, description="Statistique 4 (AR_Stat04)") stat_05: Optional[str] = Field(None, description="Statistique 5 (AR_Stat05)") - categorie_1: Optional[int] = Field( - None, description="Catégorie comptable 1 (CL_No1)" - ) - categorie_2: Optional[int] = Field( - None, description="Catégorie comptable 2 (CL_No2)" - ) - categorie_3: Optional[int] = Field( - None, description="Catégorie comptable 3 (CL_No3)" - ) - categorie_4: Optional[int] = Field( - None, description="Catégorie comptable 4 (CL_No4)" - ) + categorie_1: Optional[int] = Field(None, description="Catégorie comptable 1 (CL_No1)") + categorie_2: Optional[int] = Field(None, description="Catégorie comptable 2 (CL_No2)") + categorie_3: Optional[int] = Field(None, description="Catégorie comptable 3 (CL_No3)") + categorie_4: Optional[int] = Field(None, description="Catégorie comptable 4 (CL_No4)") - date_modification: Optional[str] = Field( - None, description="Date de dernière modification (AR_DateModif)" - ) + date_modification: Optional[str] = Field(None, description="Date de dernière modification (AR_DateModif)") - marque_commerciale: Optional[str] = Field( - None, description="Marque commerciale (champ personnalisé)" - ) - objectif_qtes_vendues: Optional[str] = Field( - None, description="Objectif / Quantités vendues (champ personnalisé)" - ) - pourcentage_or: Optional[str] = Field( - None, description="Pourcentage teneur en or (champ personnalisé)" - ) - premiere_commercialisation: Optional[str] = Field( - None, description="Date de 1ère commercialisation (champ personnalisé)" - ) - interdire_commande: Optional[bool] = Field( - None, description="Interdire la commande (champ personnalisé)" - ) - exclure: Optional[bool] = Field( - None, description="Exclure de certains traitements (champ personnalisé)" - ) + marque_commerciale: Optional[str] = Field(None, description="Marque commerciale (champ personnalisé)") + objectif_qtes_vendues: Optional[str] = Field(None, description="Objectif / Quantités vendues (champ personnalisé)") + pourcentage_or: Optional[str] = Field(None, description="Pourcentage teneur en or (champ personnalisé)") + premiere_commercialisation: Optional[str] = Field(None, description="Date de 1ère commercialisation (champ personnalisé)") + interdire_commande: Optional[bool] = Field(None, description="Interdire la commande (champ personnalisé)") + exclure: Optional[bool] = Field(None, description="Exclure de certains traitements (champ personnalisé)") class Config: json_schema_extra = { "example": { "reference": "BAGUE-001", "designation": "Bague Or 18K Diamant", - "code_ean": "3760123456789", - "edi_code": "EAN13", - "raccourci": "BAG001", "prix_vente": 1299.00, - "prix_achat": 850.00, - "coef": 1.53, - "prix_net": 1199.00, - "prix_achat_nouveau": 875.00, - "date_application_prix": "2025-01-01", - "cout_standard": 860.00, "stock_reel": 15.0, - "stock_mini": 3.0, - "stock_maxi": 30.0, - "stock_reserve": 2.0, - "stock_commande": 10.0, "stock_disponible": 13.0, - "suivi_stock": True, - "nomenclature": False, - "unite_vente": "PCE", - "unite_poids": "GR", - "poids_net": 3.5, - "poids_brut": 3.8, - "gamme_1": "Or", - "gamme_2": "18K", - "type_article": 0, - "type_article_libelle": "Article", - "famille_code": "BAGUE", - "famille_libelle": "Bagues", - "famille_type": 0, - "famille_unite_vente": "PCE", - "famille_coef": 1.5, - "famille_suivi_stock": True, - "famille_garantie": 24, - "nature": 1, - "garantie": 24, - "code_fiscal": "TVA20", - "pays": "FR", - "fournisseur_principal": 150, - "fournisseur_nom": "Bijoux & Co SAS", - "conditionnement": "Écrin", - "nb_colis": 1, - "prevision": False, - "est_actif": True, - "en_sommeil": False, - "soumis_escompte": True, - "delai": 7, - "publie": True, - "hors_statistique": False, - "vente_debit": False, - "non_imprimable": False, - "transfere": False, - "contremarque": False, - "fact_poids": False, - "fact_forfait": False, - "fictif": False, - "sous_traitance": False, - "criticite": 2, - "delai_fabrication": 5, - "photo": "bague001.jpg", - "langue_1": "Gold Ring with Diamond", - "langue_2": "Anillo de Oro con Diamante", - "tva_code": "T20", - "tva_taux": 20.0, - "stat_01": "Luxe", - "stat_02": "Féminin", - "categorie_1": 1, - "categorie_2": 5, - "date_modification": "2024-12-15", - "marque_commerciale": "Elite Collection", - "objectif_qtes_vendues": "50", - "pourcentage_or": "75.0", - "premiere_commercialisation": "2024-01-01", - "interdire_commande": False, - "exclure": False + "nb_emplacements": 2, + "nb_gammes": 2, + "nb_tarifs_clients": 3, + "nb_fournisseurs": 2, + "nb_medias": 2, + "emplacements": [ + { + "depot": "01", + "emplacement": "A1-01", + "qte_stockee": 10.0, + "depot_nom": "Dépôt principal" + } + ], + "gammes": [ + { + "numero_gamme": 1, + "enumere": "001", + "gamme_nom": "Taille" + } + ] } } - + class ArticleListResponse(BaseModel): """Réponse pour une liste d'articles""" total: int = Field(..., description="Nombre total d'articles") - articles: list[ArticleResponse] = Field(..., description="Liste des articles") - filtre_applique: Optional[str] = Field( - None, description="Filtre de recherche appliqué" - ) - avec_stock: bool = Field( - True, description="Indique si les stocks ont été chargés" - ) - avec_famille: bool = Field( - True, description="Indique si les familles ont été enrichies" + articles: List[ArticleResponse] = Field(..., description="Liste des articles") + filtre_applique: Optional[str] = Field(None, description="Filtre de recherche appliqué") + avec_stock: bool = Field(True, description="Indique si les stocks ont été chargés") + avec_famille: bool = Field(True, description="Indique si les familles ont été enrichies") + avec_enrichissements_complets: bool = Field( + False, + description="Indique si tous les enrichissements sont activés" ) class Config: json_schema_extra = { "example": { "total": 1250, - "filtre_applique": "ordinateur", + "filtre_applique": "bague", "avec_stock": True, "avec_famille": True, - "articles": [ - ] + "avec_enrichissements_complets": True, + "articles": [] } } @@ -2138,7 +2265,6 @@ class FamilleCreateRequest(BaseModel): class FamilleResponse(BaseModel): """Modèle complet d'une famille avec données comptables et fournisseur""" - # ========== IDENTIFICATION ========== code: str = Field(..., description="Code famille") intitule: str = Field(..., description="Intitulé") type: int = Field(..., description="Type (0=Détail, 1=Total)") @@ -2146,33 +2272,27 @@ class FamilleResponse(BaseModel): est_total: bool = Field(..., description="True si type Total") est_detail: bool = Field(..., description="True si type Détail") - # ========== UNITÉS ET COEFFICIENTS ========== unite_vente: Optional[str] = Field(None, description="Unité de vente par défaut") unite_poids: Optional[str] = Field(None, description="Unité de poids") coef: Optional[float] = Field(None, description="Coefficient multiplicateur") - # ========== GESTION STOCK ========== suivi_stock: Optional[bool] = Field(None, description="Suivi du stock activé") garantie: Optional[int] = Field(None, description="Durée de garantie (mois)") delai: Optional[int] = Field(None, description="Délai de livraison (jours)") nb_colis: Optional[int] = Field(None, description="Nombre de colis") - # ========== FISCAL ET COMPTABLE ========== code_fiscal: Optional[str] = Field(None, description="Code fiscal par défaut") escompte: Optional[bool] = Field(None, description="Escompte autorisé") - # ========== CARACTÉRISTIQUES ========== est_centrale: Optional[bool] = Field(None, description="Famille d'achat centralisé") nature: Optional[int] = Field(None, description="Nature de la famille") pays: Optional[str] = Field(None, description="Pays d'origine") - # ========== CATÉGORIES COMPTABLES ========== categorie_1: Optional[int] = Field(None, description="Catégorie comptable 1 (CL_No1)") categorie_2: Optional[int] = Field(None, description="Catégorie comptable 2 (CL_No2)") categorie_3: Optional[int] = Field(None, description="Catégorie comptable 3 (CL_No3)") categorie_4: Optional[int] = Field(None, description="Catégorie comptable 4 (CL_No4)") - # ========== STATISTIQUES ========== stat_01: Optional[str] = Field(None, description="Statistique libre 1") stat_02: Optional[str] = Field(None, description="Statistique libre 2") stat_03: Optional[str] = Field(None, description="Statistique libre 3") @@ -2180,7 +2300,6 @@ class FamilleResponse(BaseModel): stat_05: Optional[str] = Field(None, description="Statistique libre 5") hors_statistique: Optional[bool] = Field(None, description="Exclue des statistiques") - # ========== OPTIONS DIVERSES ========== vente_debit: Optional[bool] = Field(None, description="Vente au débit") non_imprimable: Optional[bool] = Field(None, description="Non imprimable sur documents") contremarque: Optional[bool] = Field(None, description="Article en contremarque") @@ -2188,17 +2307,14 @@ class FamilleResponse(BaseModel): fact_forfait: Optional[bool] = Field(None, description="Facturation forfaitaire") publie: Optional[bool] = Field(None, description="Publié (e-commerce)") - # ========== RÉFÉRENCES ET RACCOURCIS ========== racine_reference: Optional[str] = Field(None, description="Racine pour génération auto de références") racine_code_barre: Optional[str] = Field(None, description="Racine pour génération auto de codes-barres") raccourci: Optional[str] = Field(None, description="Raccourci clavier") - # ========== GESTION AVANCÉE ========== sous_traitance: Optional[bool] = Field(None, description="Famille en sous-traitance") fictif: Optional[bool] = Field(None, description="Famille fictive (nomenclature)") criticite: Optional[int] = Field(None, description="Niveau de criticité (0-5)") - # ========== COMPTABILITÉ VENTE (F_FAMCOMPTA Type=0) ========== compte_vente: Optional[str] = Field(None, description="Compte général de vente") compte_auxiliaire_vente: Optional[str] = Field(None, description="Compte auxiliaire de vente") tva_vente_1: Optional[str] = Field(None, description="Code TVA vente principal") @@ -2206,7 +2322,6 @@ class FamilleResponse(BaseModel): tva_vente_3: Optional[str] = Field(None, description="Code TVA vente tertiaire") type_facture_vente: Optional[int] = Field(None, description="Type de facture vente") - # ========== COMPTABILITÉ ACHAT (F_FAMCOMPTA Type=1) ========== compte_achat: Optional[str] = Field(None, description="Compte général d'achat") compte_auxiliaire_achat: Optional[str] = Field(None, description="Compte auxiliaire d'achat") tva_achat_1: Optional[str] = Field(None, description="Code TVA achat principal") @@ -2214,11 +2329,9 @@ class FamilleResponse(BaseModel): tva_achat_3: Optional[str] = Field(None, description="Code TVA achat tertiaire") type_facture_achat: Optional[int] = Field(None, description="Type de facture achat") - # ========== COMPTABILITÉ STOCK (F_FAMCOMPTA Type=2) ========== compte_stock: Optional[str] = Field(None, description="Compte de stock") compte_auxiliaire_stock: Optional[str] = Field(None, description="Compte auxiliaire de stock") - # ========== FOURNISSEUR PRINCIPAL (F_FAMFOURNISS) ========== fournisseur_principal: Optional[str] = Field(None, description="N° compte fournisseur principal") fournisseur_unite: Optional[str] = Field(None, description="Unité d'achat fournisseur") fournisseur_conversion: Optional[float] = Field(None, description="Coefficient de conversion") @@ -2231,10 +2344,8 @@ class FamilleResponse(BaseModel): fournisseur_remise: Optional[float] = Field(None, description="Remise fournisseur (%)") fournisseur_type_remise: Optional[int] = Field(None, description="Type de remise (0=%, 1=Montant)") - # ========== INFORMATIONS COMPLÉMENTAIRES ========== nb_articles: Optional[int] = Field(None, description="Nombre d'articles dans la famille") - # ========== CHAMPS LEGACY (rétrocompatibilité) ========== FA_CodeFamille: Optional[str] = Field(None, description="[Legacy] Code famille") FA_Intitule: Optional[str] = Field(None, description="[Legacy] Intitulé") FA_Type: Optional[int] = Field(None, description="[Legacy] Type")