588 lines
22 KiB
Python
588 lines
22 KiB
Python
from pydantic import BaseModel, Field, validator, field_validator
|
|
from typing import List, Optional
|
|
from datetime import date
|
|
|
|
from utils import (
|
|
NomenclatureType,
|
|
SuiviStockType,
|
|
TypeArticle,
|
|
normalize_enum_to_int,
|
|
normalize_string_field,
|
|
)
|
|
|
|
class Article(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_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[dict] = Field(
|
|
default_factory=list, description="Détail du stock par emplacement"
|
|
)
|
|
nb_emplacements: int = Field(0, description="Nombre d'emplacements")
|
|
|
|
# Champs énumérés normalisés
|
|
suivi_stock: Optional[int] = Field(
|
|
None,
|
|
description="Type de suivi de stock (AR_SuiviStock): 0=Aucun, 1=CMUP, 2=FIFO/LIFO, 3=Sérialisé",
|
|
)
|
|
suivi_stock_libelle: Optional[str] = Field(
|
|
None, description="Libellé du type de suivi de stock"
|
|
)
|
|
|
|
nomenclature: Optional[int] = Field(
|
|
None,
|
|
description="Type de nomenclature (AR_Nomencl): 0=Non, 1=Fabrication, 2=Commerciale",
|
|
)
|
|
nomenclature_libelle: Optional[str] = Field(
|
|
None, description="Libellé du type de nomenclature"
|
|
)
|
|
|
|
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[dict] = Field(default_factory=list, description="Détail des gammes")
|
|
nb_gammes: int = Field(0, description="Nombre de gammes")
|
|
|
|
tarifs_clients: List[dict] = Field(
|
|
default_factory=list, description="Tarifs spécifiques par client/catégorie"
|
|
)
|
|
nb_tarifs_clients: int = Field(0, description="Nombre de tarifs clients")
|
|
|
|
composants: List[dict] = Field(
|
|
default_factory=list, description="Composants/Opérations de production"
|
|
)
|
|
nb_composants: int = Field(0, description="Nombre de composants")
|
|
|
|
compta_vente: List[dict] = Field(
|
|
default_factory=list, description="Comptabilité vente"
|
|
)
|
|
compta_achat: List[dict] = Field(
|
|
default_factory=list, description="Comptabilité achat"
|
|
)
|
|
compta_stock: List[dict] = Field(
|
|
default_factory=list, description="Comptabilité stock"
|
|
)
|
|
|
|
fournisseurs: List[dict] = Field(
|
|
default_factory=list, description="Tous les fournisseurs de l'article"
|
|
)
|
|
nb_fournisseurs: int = Field(0, description="Nombre de fournisseurs")
|
|
|
|
refs_enumerees: List[dict] = Field(
|
|
default_factory=list, description="Références énumérées"
|
|
)
|
|
nb_refs_enumerees: int = Field(0, description="Nombre de références énumérées")
|
|
|
|
medias: List[dict] = Field(default_factory=list, description="Médias attachés")
|
|
nb_medias: int = Field(0, description="Nombre de médias")
|
|
|
|
prix_gammes: List[dict] = Field(
|
|
default_factory=list, description="Prix par combinaison de gammes"
|
|
)
|
|
nb_prix_gammes: int = Field(0, description="Nombre de prix par gammes")
|
|
|
|
type_article: Optional[int] = Field(
|
|
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"
|
|
)
|
|
|
|
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")
|
|
famille_type: Optional[int] = Field(
|
|
None, description="Type de famille : 0=Détail, 1=Total"
|
|
)
|
|
famille_unite_vente: Optional[str] = Field(
|
|
None, description="Unité de vente de la famille"
|
|
)
|
|
famille_coef: Optional[float] = Field(None, description="Coefficient de la famille")
|
|
famille_suivi_stock: Optional[bool] = Field(
|
|
None, description="Suivi stock de la famille"
|
|
)
|
|
famille_garantie: Optional[int] = Field(None, description="Garantie de la famille")
|
|
famille_unite_poids: Optional[str] = Field(
|
|
None, description="Unité de poids de la famille"
|
|
)
|
|
famille_delai: Optional[int] = Field(None, description="Délai de la famille")
|
|
famille_nb_colis: Optional[int] = Field(
|
|
None, description="Nombre de colis de la famille"
|
|
)
|
|
famille_code_fiscal: Optional[str] = Field(
|
|
None, description="Code fiscal de la famille"
|
|
)
|
|
famille_escompte: Optional[bool] = Field(None, description="Escompte de la famille")
|
|
famille_centrale: Optional[bool] = Field(None, description="Famille centrale")
|
|
famille_nature: Optional[int] = Field(None, description="Nature de la famille")
|
|
famille_hors_stat: Optional[bool] = Field(
|
|
None, description="Hors statistique famille"
|
|
)
|
|
famille_pays: Optional[str] = Field(None, description="Pays de la famille")
|
|
|
|
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"
|
|
)
|
|
fournisseur_nom: Optional[str] = Field(
|
|
None, description="Nom du fournisseur principal"
|
|
)
|
|
|
|
conditionnement: Optional[str] = Field(
|
|
None, description="Conditionnement d'achat (AR_Condition)"
|
|
)
|
|
conditionnement_qte: Optional[float] = Field(
|
|
None, description="Quantité conditionnement"
|
|
)
|
|
conditionnement_edi: Optional[str] = Field(
|
|
None, description="Code EDI conditionnement"
|
|
)
|
|
|
|
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)"
|
|
)
|
|
|
|
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)"
|
|
)
|
|
|
|
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)"
|
|
)
|
|
|
|
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)")
|
|
|
|
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"
|
|
)
|
|
frais_02_denomination: Optional[str] = Field(
|
|
None, description="Dénomination frais 2"
|
|
)
|
|
frais_03_denomination: Optional[str] = Field(
|
|
None, description="Dénomination frais 3"
|
|
)
|
|
|
|
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)")
|
|
stat_03: Optional[str] = Field(None, description="Statistique 3 (AR_Stat03)")
|
|
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)"
|
|
)
|
|
|
|
date_modification: Optional[str] = Field(
|
|
None, description="Date de dernière modification (AR_DateModif)"
|
|
)
|
|
|
|
marque_commerciale: Optional[str] = Field(None, description="Marque commerciale")
|
|
objectif_qtes_vendues: Optional[str] = Field(
|
|
None, description="Objectif / Quantités vendues"
|
|
)
|
|
pourcentage_or: Optional[str] = Field(None, description="Pourcentage teneur en or")
|
|
premiere_commercialisation: Optional[str] = Field(
|
|
None, description="Date de 1ère commercialisation"
|
|
)
|
|
interdire_commande: Optional[bool] = Field(
|
|
None, description="Interdire la commande"
|
|
)
|
|
exclure: Optional[bool] = Field(None, description="Exclure de certains traitements")
|
|
|
|
# ===== VALIDATEURS =====
|
|
|
|
@field_validator(
|
|
"unite_vente",
|
|
"unite_poids",
|
|
"gamme_1",
|
|
"gamme_2",
|
|
"conditionnement",
|
|
"code_fiscal",
|
|
"pays",
|
|
"article_substitut",
|
|
"reprise_code_defaut",
|
|
mode="before",
|
|
)
|
|
@classmethod
|
|
def convert_string_fields(cls, v):
|
|
"""Convertit les champs string qui peuvent venir comme int depuis la DB"""
|
|
return normalize_string_field(v)
|
|
|
|
@field_validator("suivi_stock", "nomenclature", mode="before")
|
|
@classmethod
|
|
def convert_enum_fields(cls, v):
|
|
"""Convertit les champs énumérés en int"""
|
|
return normalize_enum_to_int(v)
|
|
|
|
def model_post_init(self, __context):
|
|
"""Génère automatiquement les libellés après l'initialisation"""
|
|
if self.suivi_stock is not None:
|
|
self.suivi_stock_libelle = SuiviStockType.get_label(self.suivi_stock)
|
|
|
|
if self.nomenclature is not None:
|
|
self.nomenclature_libelle = NomenclatureType.get_label(self.nomenclature)
|
|
|
|
if self.type_article is not None:
|
|
self.type_article_libelle = TypeArticle.get_label(self.type_article)
|
|
|
|
class Config:
|
|
json_schema_extra = {
|
|
"example": {
|
|
"reference": "BAGUE-001",
|
|
"designation": "Bague Or 18K Diamant",
|
|
"prix_vente": 1299.00,
|
|
"stock_reel": 15.0,
|
|
"suivi_stock": 1,
|
|
"suivi_stock_libelle": "CMUP",
|
|
"nomenclature": 0,
|
|
"nomenclature_libelle": "Non",
|
|
}
|
|
}
|
|
|
|
|
|
class ArticleList(BaseModel):
|
|
"""Réponse pour une liste d'articles"""
|
|
|
|
total: int = Field(..., description="Nombre total d'articles")
|
|
articles: List[Article] = 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 ArticleCreate(BaseModel):
|
|
"""Schéma pour création d'article"""
|
|
|
|
reference: str = Field(..., max_length=18, description="Référence article")
|
|
designation: str = Field(..., max_length=69, description="Désignation")
|
|
famille: Optional[str] = Field(None, max_length=18, description="Code famille")
|
|
prix_vente: Optional[float] = Field(None, ge=0, description="Prix vente HT")
|
|
prix_achat: Optional[float] = Field(None, ge=0, description="Prix achat HT")
|
|
stock_reel: Optional[float] = Field(None, ge=0, description="Stock initial")
|
|
stock_mini: Optional[float] = Field(None, ge=0, description="Stock minimum")
|
|
code_ean: Optional[str] = Field(None, max_length=13, description="Code-barres")
|
|
unite_vente: Optional[str] = Field("UN", max_length=4, description="Unité")
|
|
tva_code: Optional[str] = Field(None, max_length=5, description="Code TVA")
|
|
description: Optional[str] = Field(None, description="Description")
|
|
|
|
|
|
class ArticleUpdate(BaseModel):
|
|
"""Schéma pour modification d'article"""
|
|
|
|
designation: Optional[str] = Field(None, max_length=69)
|
|
prix_vente: Optional[float] = Field(None, ge=0)
|
|
prix_achat: Optional[float] = Field(None, ge=0)
|
|
stock_reel: Optional[float] = Field(
|
|
None, ge=0, description="Critique pour erreur 2881"
|
|
)
|
|
stock_mini: Optional[float] = Field(None, ge=0)
|
|
code_ean: Optional[str] = Field(None, max_length=13)
|
|
description: Optional[str] = Field(None)
|
|
|
|
|
|
class MouvementStockLigne(BaseModel):
|
|
article_ref: str = Field(..., description="Référence de l'article")
|
|
quantite: float = Field(..., gt=0, description="Quantité (>0)")
|
|
depot_code: Optional[str] = Field(None, description="Code du dépôt (ex: '01')")
|
|
prix_unitaire: Optional[float] = Field(
|
|
None, ge=0, description="Prix unitaire (optionnel)"
|
|
)
|
|
commentaire: Optional[str] = Field(None, description="Commentaire ligne")
|
|
numero_lot: Optional[str] = Field(
|
|
None, description="Numéro de lot (pour FIFO/LIFO)"
|
|
)
|
|
stock_mini: Optional[float] = Field(
|
|
None,
|
|
ge=0,
|
|
description="""Stock minimum à définir pour cet article.
|
|
Si fourni, met à jour AS_QteMini dans F_ARTSTOCK.
|
|
Laisser None pour ne pas modifier.""",
|
|
)
|
|
stock_maxi: Optional[float] = Field(
|
|
None,
|
|
ge=0,
|
|
description="""Stock maximum à définir pour cet article.
|
|
Doit être > stock_mini si les deux sont fournis.""",
|
|
)
|
|
|
|
class Config:
|
|
json_schema_extra = {
|
|
"example": {
|
|
"article_ref": "ARTS-001",
|
|
"quantite": 50.0,
|
|
"depot_code": "01",
|
|
"prix_unitaire": 100.0,
|
|
"commentaire": "Réapprovisionnement",
|
|
"numero_lot": "LOT20241217",
|
|
"stock_mini": 10.0,
|
|
"stock_maxi": 200.0,
|
|
}
|
|
}
|
|
|
|
@validator("stock_maxi")
|
|
def validate_stock_maxi(cls, v, values):
|
|
"""Valide que stock_maxi > stock_mini si les deux sont fournis"""
|
|
if (
|
|
v is not None
|
|
and "stock_mini" in values
|
|
and values["stock_mini"] is not None
|
|
):
|
|
if v <= values["stock_mini"]:
|
|
raise ValueError(
|
|
"stock_maxi doit être strictement supérieur à stock_mini"
|
|
)
|
|
return v
|
|
|
|
|
|
class EntreeStock(BaseModel):
|
|
"""Création d'un bon d'entrée en stock"""
|
|
|
|
date_entree: Optional[date] = Field(
|
|
None, description="Date du mouvement (aujourd'hui par défaut)"
|
|
)
|
|
reference: Optional[str] = Field(None, description="Référence externe")
|
|
depot_code: Optional[str] = Field(
|
|
None, description="Dépôt principal (si applicable)"
|
|
)
|
|
lignes: List[MouvementStockLigne] = Field(
|
|
..., min_items=1, description="Lignes du mouvement"
|
|
)
|
|
commentaire: Optional[str] = Field(None, description="Commentaire général")
|
|
|
|
class Config:
|
|
json_schema_extra = {
|
|
"example": {
|
|
"date_entree": "2025-01-15",
|
|
"reference": "REC-2025-001",
|
|
"depot_code": "01",
|
|
"lignes": [
|
|
{
|
|
"article_ref": "ART001",
|
|
"quantite": 50,
|
|
"depot_code": "01",
|
|
"prix_unitaire": 10.50,
|
|
"commentaire": "Réception fournisseur",
|
|
}
|
|
],
|
|
"commentaire": "Réception livraison fournisseur XYZ",
|
|
}
|
|
}
|
|
|
|
|
|
class SortieStock(BaseModel):
|
|
"""Création d'un bon de sortie de stock"""
|
|
|
|
date_sortie: Optional[date] = Field(
|
|
None, description="Date du mouvement (aujourd'hui par défaut)"
|
|
)
|
|
reference: Optional[str] = Field(None, description="Référence externe")
|
|
depot_code: Optional[str] = Field(
|
|
None, description="Dépôt principal (si applicable)"
|
|
)
|
|
lignes: List[MouvementStockLigne] = Field(
|
|
..., min_items=1, description="Lignes du mouvement"
|
|
)
|
|
commentaire: Optional[str] = Field(None, description="Commentaire général")
|
|
|
|
class Config:
|
|
json_schema_extra = {
|
|
"example": {
|
|
"date_sortie": "2025-01-15",
|
|
"reference": "SOR-2025-001",
|
|
"depot_code": "01",
|
|
"lignes": [
|
|
{
|
|
"article_ref": "ART001",
|
|
"quantite": 10,
|
|
"depot_code": "01",
|
|
"commentaire": "Utilisation interne",
|
|
}
|
|
],
|
|
"commentaire": "Consommation atelier",
|
|
}
|
|
}
|
|
|
|
|
|
class MouvementStock(BaseModel):
|
|
"""Réponse pour un mouvement de stock"""
|
|
|
|
article_ref: str = Field(..., description="Numéro d'article")
|
|
numero: str = Field(..., description="Numéro du mouvement")
|
|
type: int = Field(..., description="Type (0=Entrée, 1=Sortie)")
|
|
type_libelle: str = Field(..., description="Libellé du type")
|
|
date: str = Field(..., description="Date du mouvement")
|
|
reference: Optional[str] = Field(None, description="Référence externe")
|
|
nb_lignes: int = Field(..., description="Nombre de lignes")
|