from pydantic import BaseModel, Field, validator, EmailStr, field_validator from typing import Optional, List, Dict from enum import Enum, IntEnum from datetime import datetime, date class ArticleCreateRequest(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") 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, description="Code-barres EAN") unite_vente: Optional[str] = Field("UN", description="Unité de vente") tva_code: Optional[str] = Field(None, description="Code TVA") description: Optional[str] = Field(None, description="Description/Commentaire") class ArticleUpdateGatewayRequest(BaseModel): """Modèle pour modification article côté gateway""" reference: str article_data: Dict 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 EntreeStockRequest(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 SortieStockRequest(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")