"""Routes M4 — Envoi de contributions aux organismes AA.""" from datetime import date 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 from app.models.envoi_contribution import EnvoiContribution from app.models.config_repartition import ConfigRepartition from app.models.membre import Membre from app.models.affectation import Affectation from app.models.poste import Poste from app.schemas.phase7 import ( EnvoiContributionCreate, EnvoiContributionOut, CalculRepartitionIn, CalculRepartitionOut, DESTINATIONS, DESTINATIONS_LABELS, METHODES_CONTRIBUTION, ) router = APIRouter(prefix="/contributions", tags=["M4 — Envoi contributions"]) # Répartition par défaut selon le Guide du groupe DEFAUT_REPARTITION = { "district": 30.0, "region": 30.0, "bsg": 30.0, "intergroupe": 10.0, } async def require_tresorier(membre=Depends(get_current_membre), db: AsyncSession = Depends(get_db)): result = await db.execute( select(Affectation).join(Poste).where( Affectation.membre_id == membre.id, Affectation.statut == "actif", Poste.nom.ilike("%trésorier%"), ) ) if not result.scalar_one_or_none(): raise HTTPException(status_code=403, detail="Réservé au trésorier") return membre async def _get_repartition(groupe_id, db: AsyncSession) -> dict[str, float]: """Récupère la répartition active ou utilise les défauts.""" result = await db.execute( select(ConfigRepartition) .where(ConfigRepartition.groupe_id == groupe_id) .order_by(desc(ConfigRepartition.actif_depuis)) ) configs = result.scalars().all() # Garder la plus récente par destination repartition = {} for c in configs: if c.destination not in repartition: repartition[c.destination] = float(c.pourcentage) # Compléter avec les défauts for dest in DESTINATIONS: if dest not in repartition: repartition[dest] = DEFAUT_REPARTITION[dest] return repartition @router.post("/calculer", response_model=list[CalculRepartitionOut]) async def calculer_repartition( req: CalculRepartitionIn, membre=Depends(require_tresorier), db: AsyncSession = Depends(get_db), ): """Calculer la répartition d'un montant selon les % configurés.""" if req.montant_total <= 0: raise HTTPException(status_code=400, detail="Le montant doit être positif") repartition = await _get_repartition(membre.groupe_id, db) return [ CalculRepartitionOut( destination=dest, destination_label=DESTINATIONS_LABELS[dest], pourcentage=pct, montant=round(req.montant_total * pct / 100, 2), ) for dest, pct in repartition.items() ] @router.post("/", response_model=EnvoiContributionOut) async def enregistrer_envoi( req: EnvoiContributionCreate, membre=Depends(require_tresorier), db: AsyncSession = Depends(get_db), ): """M4 — Enregistrer un envoi de contribution à un organisme.""" if req.destination not in DESTINATIONS: raise HTTPException(status_code=400, detail=f"Destination invalide. Choix: {DESTINATIONS}") if req.methode.upper() not in METHODES_CONTRIBUTION: raise HTTPException(status_code=400, detail=f"Méthode invalide. Choix: {METHODES_CONTRIBUTION}") envoi = EnvoiContribution( groupe_id=membre.groupe_id, date_envoi=req.date_envoi, destination=req.destination, montant=req.montant, methode=req.methode.upper(), saisi_par=membre.id, ) db.add(envoi) await db.flush() return EnvoiContributionOut( id=envoi.id, date_envoi=envoi.date_envoi, destination=envoi.destination, destination_label=DESTINATIONS_LABELS[envoi.destination], montant=float(envoi.montant), methode=envoi.methode, saisi_par_prenom=membre.prenom, cree_le=envoi.cree_le, ) @router.get("/", response_model=list[EnvoiContributionOut]) async def liste_envois( limite: int = 30, membre=Depends(get_current_membre), db: AsyncSession = Depends(get_db), ): """Lister les envois de contributions récents.""" result = await db.execute( select(EnvoiContribution) .where(EnvoiContribution.groupe_id == membre.groupe_id) .order_by(desc(EnvoiContribution.date_envoi)) .limit(limite) ) envois = result.scalars().all() out = [] for e in envois: saisi = await db.get(Membre, e.saisi_par) if e.saisi_par else None out.append(EnvoiContributionOut( id=e.id, date_envoi=e.date_envoi, destination=e.destination, destination_label=DESTINATIONS_LABELS.get(e.destination, e.destination), montant=float(e.montant), methode=e.methode, saisi_par_prenom=saisi.prenom if saisi else None, cree_le=e.cree_le, )) return out