groupe-meditation/backend/app/routers/commandes_jetons.py

73 lines
2.8 KiB
Python
Raw Normal View History

2026-03-04 14:01:00 -05:00
"""Routes M6 — Commander des jetons."""
from datetime import date, timedelta
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func
from app.core.database import get_db
2026-05-25 17:39:38 -04:00
from app.core.security import get_current_membre, is_sysadmin
2026-05-26 09:05:58 -04:00
from app.core.roles import membre_a_module
from app.models.vente_jeton import VenteJeton
2026-03-04 14:01:00 -05:00
from app.models.affectation import Affectation
from app.models.poste import Poste
from app.schemas.phase5 import StockJetonsOut, TYPES_JETONS
from fastapi import HTTPException
router = APIRouter(prefix="/commandes-jetons", tags=["M6 — Commander jetons"])
async def require_resp_jetons(membre=Depends(get_current_membre), db: AsyncSession = Depends(get_db)):
"""Vérifie que le membre est responsable des jetons."""
2026-05-26 09:05:58 -04:00
if is_sysadmin(membre) or await membre_a_module(membre.id, "M6", db):
2026-05-25 17:39:38 -04:00
return membre
2026-03-04 14:01:00 -05:00
result = await db.execute(
select(Affectation).join(Poste).where(
Affectation.membre_id == membre.id,
2026-06-01 06:35:09 -04:00
Affectation.groupe_id == membre.groupe_id,
2026-03-04 14:01:00 -05:00
Affectation.statut == "actif",
2026-06-01 06:35:09 -04:00
Poste.groupe_id == membre.groupe_id,
2026-03-04 14:01:00 -05:00
Poste.nom.ilike("%jeton%"),
)
)
2026-03-13 09:53:55 -04:00
if not result.scalars().first():
2026-03-04 14:01:00 -05:00
raise HTTPException(status_code=403, detail="Réservé au responsable des jetons")
return membre
@router.get("/stock", response_model=list[StockJetonsOut])
async def etat_stock_jetons(
membre=Depends(require_resp_jetons),
db: AsyncSession = Depends(get_db),
):
"""M6 — État du stock de jetons (basé sur les ventes, pas les remises)."""
2026-03-04 14:01:00 -05:00
il_y_a_30_jours = date.today() - timedelta(days=30)
# Compter les jetons vendus par type dans les 30 derniers jours.
# Les remises sont statistiques et ne décrémentent pas l'inventaire.
2026-03-04 14:01:00 -05:00
result = await db.execute(
select(VenteJeton.type_jeton, func.coalesce(func.sum(VenteJeton.quantite), 0))
2026-03-04 14:01:00 -05:00
.where(
VenteJeton.groupe_id == membre.groupe_id,
VenteJeton.date_vente >= il_y_a_30_jours,
2026-03-04 14:01:00 -05:00
)
.group_by(VenteJeton.type_jeton)
2026-03-04 14:01:00 -05:00
)
usage = {row[0]: row[1] for row in result.all()}
stock = []
for t in TYPES_JETONS:
remis = usage.get(t["value"], 0)
# Suggestion simple : si plus de 2 remis dans le mois, suggérer commande
suggestion = "Bas — commander" if remis >= 2 else "OK"
# Le jeton de désir est toujours à surveiller (remis chaque semaine)
if t["value"] == "desir" and remis >= 4:
suggestion = "Bas — commander"
stock.append(StockJetonsOut(
type_jeton=t["value"],
label=f"{t['emoji']} {t['label']}",
remis_dernier_mois=remis,
stock_suggestion=suggestion,
))
return stock