from pydantic import BaseModel, Field, validator from typing import Optional, List, Dict from datetime import date class ArticleCreate(BaseModel): reference: str = Field(..., description="Référence article (max 18 car)") designation: str = Field(..., description="Désignation (max 69 car)") famille: Optional[str] = Field(None, 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") coef: Optional[float] = Field(None, ge=0, description="Coefficient") stock_reel: Optional[float] = Field(None, ge=0, description="Stock initial") stock_mini: Optional[float] = Field(None, ge=0, description="Stock minimum") stock_maxi: Optional[float] = Field(None, ge=0, description="Stock maximum") code_ean: Optional[str] = Field(None, description="Code-barres EAN") unite_vente: Optional[str] = Field("UN", description="Unité de vente") tva_code: Optional[str] = Field(None, description="Code TVA") code_fiscal: Optional[str] = Field(None, description="Code fiscal") description: Optional[str] = Field(None, description="Description/Commentaire") pays: Optional[str] = Field(None, description="Pays d'origine") garantie: Optional[int] = Field(None, ge=0, description="Garantie en mois") delai: Optional[int] = Field(None, ge=0, description="Délai livraison jours") poids_net: Optional[float] = Field(None, ge=0, description="Poids net kg") poids_brut: Optional[float] = Field(None, ge=0, description="Poids brut kg") stat_01: Optional[str] = Field(None, description="Statistique 1") stat_02: Optional[str] = Field(None, description="Statistique 2") stat_03: Optional[str] = Field(None, description="Statistique 3") stat_04: Optional[str] = Field(None, description="Statistique 4") stat_05: Optional[str] = Field(None, description="Statistique 5") soumis_escompte: Optional[bool] = Field(None, description="Soumis à escompte") publie: Optional[bool] = Field(None, description="Publié web/catalogue") en_sommeil: Optional[bool] = Field(None, description="Article en sommeil") class ArticleUpdate(BaseModel): reference: str = Field(..., description="Référence de l'article à modifier") article_data: Dict = Field(..., description="Données à modifier") class Config: json_schema_extra = { "example": { "reference": "ART001", "article_data": { "designation": "Nouvelle désignation", "prix_vente": 150.0, "famille": "FAM01", "stock_reel": 100.0, "stock_mini": 10.0, "code_fiscal": "V19", "garantie": 24, }, } } class MouvementStockLigneRequest(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: 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[MouvementStockLigneRequest] = Field( ..., min_items=1, description="Lignes du mouvement" ) commentaire: Optional[str] = Field(None, description="Commentaire général") 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[MouvementStockLigneRequest] = Field( ..., min_items=1, description="Lignes du mouvement" ) commentaire: Optional[str] = Field(None, description="Commentaire général")