groupe-meditation/backend/app/routers/jetons.py
2026-05-25 17:39:38 -04:00

111 lines
3.7 KiB
Python

"""Routes S2 — Remise de jeton."""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, desc
from app.core.database import get_db
from app.core.security import get_current_membre, is_sysadmin
from app.models.jeton import Jeton
from app.models.membre import Membre
from app.models.affectation import Affectation
from app.models.poste import Poste
from app.schemas.phase5 import JetonCreate, JetonOut, TYPES_JETONS
router = APIRouter(prefix="/jetons", tags=["S2 — Remise de jeton"])
async def require_resp_jetons(membre=Depends(get_current_membre), db: AsyncSession = Depends(get_db)):
"""Vérifie que le membre est responsable des jetons."""
if is_sysadmin(membre):
return membre
result = await db.execute(
select(Affectation).join(Poste).where(
Affectation.membre_id == membre.id,
Affectation.statut == "actif",
(Poste.nom.ilike("%jeton%") | (Poste.categorie == "executif")),
)
)
if not result.scalars().first():
raise HTTPException(status_code=403, detail="Réservé au responsable des jetons")
return membre
@router.get("/types")
async def types_jetons():
"""Liste des types de jetons disponibles."""
return TYPES_JETONS
@router.post("/", response_model=JetonOut)
async def remettre_jeton(
req: JetonCreate,
membre=Depends(require_resp_jetons),
db: AsyncSession = Depends(get_db),
):
"""S2 — Enregistrer la remise d'un jeton."""
# Validation
types_valides = [t["value"] for t in TYPES_JETONS]
if req.type_jeton not in types_valides:
raise HTTPException(status_code=400, detail=f"Type de jeton invalide. Valeurs: {types_valides}")
if req.type_jeton == "multiple" and (not req.annees or req.annees < 2):
raise HTTPException(status_code=400, detail="Le nombre d'années est requis (≥ 2) pour un jeton multiple")
# Vérifier que le membre existe si spécifié
membre_prenom = None
if req.membre_id:
cible = await db.get(Membre, req.membre_id)
if not cible or not cible.actif:
raise HTTPException(status_code=404, detail="Membre introuvable")
membre_prenom = cible.prenom
jeton = Jeton(
groupe_id=membre.groupe_id,
membre_id=req.membre_id,
type_jeton=req.type_jeton,
date_remise=req.date_remise,
annees=req.annees if req.type_jeton == "multiple" else None,
remis_par=membre.id,
)
db.add(jeton)
await db.flush()
return JetonOut(
id=jeton.id,
membre_prenom=membre_prenom,
type_jeton=jeton.type_jeton,
date_remise=jeton.date_remise,
annees=jeton.annees,
remis_par_prenom=membre.prenom,
cree_le=jeton.cree_le,
)
@router.get("/", response_model=list[JetonOut])
async def liste_jetons(
limite: int = 30,
membre=Depends(get_current_membre),
db: AsyncSession = Depends(get_db),
):
"""Lister les jetons remis récemment (tout le groupe)."""
result = await db.execute(
select(Jeton)
.where(Jeton.groupe_id == membre.groupe_id)
.order_by(desc(Jeton.date_remise))
.limit(limite)
)
jetons = result.scalars().all()
out = []
for j in jetons:
m = await db.get(Membre, j.membre_id) if j.membre_id else None
r = await db.get(Membre, j.remis_par) if j.remis_par else None
out.append(JetonOut(
id=j.id,
membre_prenom=m.prenom if m else None,
type_jeton=j.type_jeton,
date_remise=j.date_remise,
annees=j.annees,
remis_par_prenom=r.prenom if r else None,
cree_le=j.cree_le,
))
return out