feat(articles): add CRUD operations for articles management
This commit is contained in:
parent
44354ec9bd
commit
428093306a
2 changed files with 198 additions and 0 deletions
145
api.py
145
api.py
|
|
@ -771,7 +771,32 @@ class FactureUpdateRequest(BaseModel):
|
|||
}
|
||||
}
|
||||
|
||||
class ArticleCreateRequest(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 ArticleUpdateRequest(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)
|
||||
|
||||
|
||||
# =====================================================
|
||||
# SERVICES EXTERNES (Universign)
|
||||
# =====================================================
|
||||
|
|
@ -1061,7 +1086,127 @@ async def rechercher_articles(query: Optional[str] = Query(None)):
|
|||
logger.error(f"Erreur recherche articles: {e}")
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
@router.post("/articles", status_code=status.HTTP_201_CREATED, tags=["Articles"])
|
||||
def creer_article(article: ArticleCreateDTO):
|
||||
"""
|
||||
➕ Création d'un article dans Sage
|
||||
|
||||
**Usage**: Créer un article avec stock pour éviter l'erreur 2881
|
||||
|
||||
**Erreurs possibles**:
|
||||
- 400: Article existe déjà ou données invalides
|
||||
- 500: Erreur Sage
|
||||
"""
|
||||
try:
|
||||
resultat = sage_client.creer_article(article.dict(exclude_none=True))
|
||||
|
||||
logger.info(f"✅ Article créé: {resultat.get('reference')}")
|
||||
|
||||
return {
|
||||
"message": "Article créé avec succès",
|
||||
"article": resultat
|
||||
}
|
||||
|
||||
except ValueError as e:
|
||||
logger.warning(f"Erreur métier création article: {e}")
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, str(e))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur création article: {e}")
|
||||
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, str(e))
|
||||
|
||||
|
||||
@router.put("/articles/{reference}", tags=["Articles"])
|
||||
def modifier_article(reference: str, article: ArticleUpdateDTO):
|
||||
"""
|
||||
✏️ Modification d'un article dans Sage
|
||||
|
||||
**Usage critique**: Augmenter le stock pour résoudre l'erreur 2881
|
||||
|
||||
**Example** - Résoudre l'erreur "L'état du stock ne permet pas de créer la ligne":
|
||||
```bash
|
||||
curl -X PUT "http://api.example.com/api/articles/ART001" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"stock_reel": 100.0}'
|
||||
```
|
||||
|
||||
**Erreurs possibles**:
|
||||
- 404: Article introuvable
|
||||
- 400: Données invalides
|
||||
- 500: Erreur Sage
|
||||
"""
|
||||
try:
|
||||
# Filtrer les champs None
|
||||
article_data = article.dict(exclude_none=True)
|
||||
|
||||
if not article_data:
|
||||
raise HTTPException(
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
"Aucun champ à modifier"
|
||||
)
|
||||
|
||||
resultat = sage_client.modifier_article(reference, article_data)
|
||||
|
||||
logger.info(f"✅ Article {reference} modifié: {list(article_data.keys())}")
|
||||
|
||||
return {
|
||||
"message": f"Article {reference} modifié avec succès",
|
||||
"article": resultat,
|
||||
"champs_modifies": list(article_data.keys())
|
||||
}
|
||||
|
||||
except ValueError as e:
|
||||
logger.warning(f"Erreur métier modification article: {e}")
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, str(e))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur modification article: {e}")
|
||||
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, str(e))
|
||||
|
||||
|
||||
@router.get("/articles/{reference}", tags=["Articles"])
|
||||
def lire_article(reference: str):
|
||||
"""
|
||||
📄 Lecture d'un article par référence
|
||||
|
||||
Retourne toutes les informations incluant le stock actuel
|
||||
"""
|
||||
try:
|
||||
article = sage_client.lire_article(reference)
|
||||
|
||||
if not article:
|
||||
raise HTTPException(
|
||||
status.HTTP_404_NOT_FOUND,
|
||||
f"Article {reference} introuvable"
|
||||
)
|
||||
|
||||
return {"article": article}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lecture article: {e}")
|
||||
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, str(e))
|
||||
|
||||
|
||||
@router.get("/articles/all")
|
||||
def lister_articles(filtre: str = ""):
|
||||
"""
|
||||
📋 Liste tous les articles avec filtre optionnel
|
||||
"""
|
||||
try:
|
||||
articles = sage_client.lister_articles(filtre)
|
||||
|
||||
return {
|
||||
"articles": articles,
|
||||
"total": len(articles)
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur liste articles: {e}")
|
||||
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, str(e))
|
||||
|
||||
|
||||
@app.post("/devis", response_model=DevisResponse, status_code=201, tags=["Devis"])
|
||||
async def creer_devis(devis: DevisRequest):
|
||||
"""📝 Création de devis via gateway Windows"""
|
||||
|
|
|
|||
|
|
@ -611,5 +611,58 @@ class SageGatewayClient:
|
|||
raise
|
||||
|
||||
|
||||
def creer_article(self, article_data: Dict) -> Dict:
|
||||
"""
|
||||
➕ Création d'un article
|
||||
|
||||
Args:
|
||||
article_data: Dictionnaire contenant:
|
||||
- reference (str, obligatoire): Référence article
|
||||
- designation (str, obligatoire): Désignation
|
||||
- prix_vente (float, optionnel): Prix vente HT
|
||||
- stock_reel (float, optionnel): Stock initial
|
||||
- ... (voir ArticleCreateRequest dans main.py)
|
||||
|
||||
Returns:
|
||||
Article créé
|
||||
|
||||
Example:
|
||||
>>> article = sage_client.creer_article({
|
||||
... "reference": "ART001",
|
||||
... "designation": "Article test",
|
||||
... "prix_vente": 10.0,
|
||||
... "stock_reel": 100.0
|
||||
... })
|
||||
"""
|
||||
return self._post("/sage/articles/create", article_data).get("data", {})
|
||||
|
||||
|
||||
def modifier_article(self, reference: str, article_data: Dict) -> Dict:
|
||||
"""
|
||||
✏️ Modification d'un article
|
||||
|
||||
**Usage critique**: Augmenter le stock pour résoudre l'erreur 2881
|
||||
|
||||
Args:
|
||||
reference: Référence de l'article à modifier
|
||||
article_data: Dictionnaire contenant les champs à modifier:
|
||||
- stock_reel (float, optionnel): Nouveau stock
|
||||
- prix_vente (float, optionnel): Nouveau prix
|
||||
- ... (seuls les champs présents seront mis à jour)
|
||||
|
||||
Returns:
|
||||
Article modifié
|
||||
|
||||
Example - Résoudre erreur de stock:
|
||||
>>> # L'erreur 2881 indique un stock insuffisant
|
||||
>>> sage_client.modifier_article("ART001", {
|
||||
... "stock_reel": 100.0 # Augmenter le stock
|
||||
... })
|
||||
"""
|
||||
return self._post(
|
||||
"/sage/articles/update",
|
||||
{"reference": reference, "article_data": article_data}
|
||||
).get("data", {})
|
||||
|
||||
# Instance globale
|
||||
sage_client = SageGatewayClient()
|
||||
|
|
|
|||
Loading…
Reference in a new issue