124 lines
4.3 KiB
Python
124 lines
4.3 KiB
Python
"""Routes C4 / M5 — Littérature : inventaire, ajustement, gestion catalogue."""
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select
|
|
|
|
from app.core.database import get_db
|
|
from app.core.security import get_current_membre
|
|
from app.models.litterature import Litterature
|
|
from app.models.affectation import Affectation
|
|
from app.models.poste import Poste
|
|
from app.schemas.phase6 import LitteratureCreate, LitteratureOut, LitteratureUpdate, AjustementStock
|
|
|
|
router = APIRouter(prefix="/litterature", tags=["C4/M5 — Littérature"])
|
|
|
|
|
|
async def require_resp_litterature(membre=Depends(get_current_membre), db: AsyncSession = Depends(get_db)):
|
|
"""Vérifie que le membre est responsable de la littérature."""
|
|
result = await db.execute(
|
|
select(Affectation).join(Poste).where(
|
|
Affectation.membre_id == membre.id,
|
|
Affectation.statut == "actif",
|
|
Poste.nom.ilike("%littérature%"),
|
|
)
|
|
)
|
|
if not result.scalars().first():
|
|
raise HTTPException(status_code=403, detail="Réservé au responsable de la littérature")
|
|
return membre
|
|
|
|
|
|
def _to_out(item: Litterature) -> LitteratureOut:
|
|
return LitteratureOut(
|
|
id=item.id,
|
|
titre=item.titre,
|
|
categorie=item.categorie,
|
|
prix_coutant=float(item.prix_coutant) if item.prix_coutant else None,
|
|
quantite_stock=item.quantite_stock,
|
|
seuil_commande=item.seuil_commande,
|
|
code_bsg=item.code_bsg,
|
|
stock_bas=item.quantite_stock <= item.seuil_commande,
|
|
)
|
|
|
|
|
|
@router.get("/", response_model=list[LitteratureOut])
|
|
async def inventaire(
|
|
membre=Depends(get_current_membre),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""C4 — Inventaire de la littérature du groupe avec prix."""
|
|
result = await db.execute(
|
|
select(Litterature)
|
|
.where(Litterature.groupe_id == membre.groupe_id)
|
|
.order_by(Litterature.categorie, Litterature.titre)
|
|
)
|
|
return [_to_out(item) for item in result.scalars().all()]
|
|
|
|
|
|
@router.post("/", response_model=LitteratureOut)
|
|
async def ajouter_titre(
|
|
req: LitteratureCreate,
|
|
membre=Depends(require_resp_litterature),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Ajouter un titre au catalogue du groupe."""
|
|
categories_valides = ["livre", "brochure", "depliant", "pamphlet"]
|
|
if req.categorie not in categories_valides:
|
|
raise HTTPException(status_code=400, detail=f"Catégorie invalide. Valeurs: {categories_valides}")
|
|
|
|
item = Litterature(
|
|
groupe_id=membre.groupe_id,
|
|
titre=req.titre,
|
|
categorie=req.categorie,
|
|
prix_coutant=req.prix_coutant,
|
|
quantite_stock=req.quantite_stock,
|
|
seuil_commande=req.seuil_commande,
|
|
code_bsg=req.code_bsg,
|
|
)
|
|
db.add(item)
|
|
await db.flush()
|
|
return _to_out(item)
|
|
|
|
|
|
@router.patch("/{item_id}", response_model=LitteratureOut)
|
|
async def modifier_titre(
|
|
item_id: str,
|
|
req: LitteratureUpdate,
|
|
membre=Depends(require_resp_litterature),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Modifier les infos d'un titre."""
|
|
result = await db.execute(
|
|
select(Litterature).where(Litterature.id == item_id, Litterature.groupe_id == membre.groupe_id)
|
|
)
|
|
item = result.scalar_one_or_none()
|
|
if not item:
|
|
raise HTTPException(status_code=404, detail="Titre introuvable")
|
|
|
|
for field, value in req.model_dump(exclude_none=True).items():
|
|
setattr(item, field, value)
|
|
await db.flush()
|
|
return _to_out(item)
|
|
|
|
|
|
@router.patch("/{item_id}/stock", response_model=LitteratureOut)
|
|
async def ajuster_stock(
|
|
item_id: str,
|
|
req: AjustementStock,
|
|
membre=Depends(require_resp_litterature),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""M5 — Ajuster le stock après réception d'une commande ou correction."""
|
|
result = await db.execute(
|
|
select(Litterature).where(Litterature.id == item_id, Litterature.groupe_id == membre.groupe_id)
|
|
)
|
|
item = result.scalar_one_or_none()
|
|
if not item:
|
|
raise HTTPException(status_code=404, detail="Titre introuvable")
|
|
|
|
nouveau_stock = item.quantite_stock + req.quantite
|
|
if nouveau_stock < 0:
|
|
raise HTTPException(status_code=400, detail="Le stock ne peut pas être négatif")
|
|
|
|
item.quantite_stock = nouveau_stock
|
|
await db.flush()
|
|
return _to_out(item)
|