197 lines
6.6 KiB
Python
197 lines
6.6 KiB
Python
"""Routes S3 — Vente de littérature."""
|
|
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.litterature import Litterature
|
|
from app.models.vente_litterature import VenteLitterature
|
|
from app.models.membre import Membre
|
|
from app.models.affectation import Affectation
|
|
from app.models.poste import Poste
|
|
from app.models.transaction_comptable import TransactionComptable
|
|
from app.schemas.phase6 import VenteCreate, VenteOut
|
|
|
|
router = APIRouter(prefix="/ventes-litterature", tags=["S3 — Vente 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."""
|
|
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("%littérature%") | (Poste.categorie == "executif")),
|
|
)
|
|
)
|
|
if not result.scalars().first():
|
|
raise HTTPException(status_code=403, detail="Réservé au responsable de la littérature")
|
|
return membre
|
|
|
|
|
|
@router.post("/", response_model=VenteOut)
|
|
async def enregistrer_vente(
|
|
req: VenteCreate,
|
|
membre=Depends(require_resp_litterature),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""S3 — Enregistrer une vente de littérature. Le stock est décrémenté automatiquement."""
|
|
# Vérifier le titre
|
|
result = await db.execute(
|
|
select(Litterature).where(
|
|
Litterature.id == req.litterature_id,
|
|
Litterature.groupe_id == membre.groupe_id,
|
|
)
|
|
)
|
|
item = result.scalar_one_or_none()
|
|
if not item:
|
|
raise HTTPException(status_code=404, detail="Titre introuvable")
|
|
|
|
if req.quantite <= 0:
|
|
raise HTTPException(status_code=400, detail="La quantité doit être positive")
|
|
|
|
if item.quantite_stock < req.quantite:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Stock insuffisant ({item.quantite_stock} disponible, {req.quantite} demandé)"
|
|
)
|
|
|
|
# Décrémenter le stock
|
|
item.quantite_stock -= req.quantite
|
|
|
|
# Créer la vente
|
|
vente = VenteLitterature(
|
|
litterature_id=req.litterature_id,
|
|
groupe_id=membre.groupe_id,
|
|
quantite=req.quantite,
|
|
montant=req.montant,
|
|
date_vente=req.date_vente,
|
|
vendu_par=membre.id,
|
|
)
|
|
db.add(vente)
|
|
await db.flush()
|
|
|
|
return VenteOut(
|
|
id=vente.id,
|
|
litterature_titre=item.titre,
|
|
quantite=vente.quantite,
|
|
montant=float(vente.montant),
|
|
date_vente=vente.date_vente,
|
|
vendu_par_prenom=membre.prenom,
|
|
cree_le=vente.cree_le,
|
|
)
|
|
|
|
|
|
@router.get("/", response_model=list[VenteOut])
|
|
async def liste_ventes(
|
|
limite: int = 20,
|
|
membre=Depends(get_current_membre),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Lister les ventes récentes."""
|
|
result = await db.execute(
|
|
select(VenteLitterature)
|
|
.where(VenteLitterature.groupe_id == membre.groupe_id)
|
|
.order_by(desc(VenteLitterature.date_vente))
|
|
.limit(limite)
|
|
)
|
|
ventes = result.scalars().all()
|
|
out = []
|
|
for v in ventes:
|
|
item = await db.get(Litterature, v.litterature_id)
|
|
vendeur = await db.get(Membre, v.vendu_par) if v.vendu_par else None
|
|
out.append(VenteOut(
|
|
id=v.id,
|
|
litterature_titre=item.titre if item else "?",
|
|
quantite=v.quantite,
|
|
montant=float(v.montant),
|
|
date_vente=v.date_vente,
|
|
vendu_par_prenom=vendeur.prenom if vendeur else None,
|
|
remis_tresorier=v.remis_tresorier,
|
|
cree_le=v.cree_le,
|
|
))
|
|
return out
|
|
|
|
|
|
@router.get("/a-recevoir")
|
|
async def ventes_a_recevoir(
|
|
membre=Depends(get_current_membre),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Ventes dont le montant n'a pas encore été remis au trésorier."""
|
|
result = await db.execute(
|
|
select(VenteLitterature)
|
|
.where(
|
|
VenteLitterature.groupe_id == membre.groupe_id,
|
|
VenteLitterature.remis_tresorier == False,
|
|
)
|
|
.order_by(desc(VenteLitterature.date_vente))
|
|
)
|
|
ventes = result.scalars().all()
|
|
out = []
|
|
for v in ventes:
|
|
item = await db.get(Litterature, v.litterature_id)
|
|
vendeur = await db.get(Membre, v.vendu_par) if v.vendu_par else None
|
|
out.append({
|
|
"id": str(v.id),
|
|
"litterature_titre": item.titre if item else "?",
|
|
"quantite": v.quantite,
|
|
"montant": float(v.montant),
|
|
"date_vente": str(v.date_vente),
|
|
"vendu_par_prenom": vendeur.prenom if vendeur else None,
|
|
})
|
|
return out
|
|
|
|
|
|
@router.post("/{vente_id}/recevoir")
|
|
async def confirmer_reception_vente(
|
|
vente_id: str,
|
|
membre=Depends(get_current_membre),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Le trésorier confirme la réception du montant de la vente."""
|
|
# Vérifier que c'est le trésorier ou exécutif
|
|
if not is_sysadmin(membre):
|
|
result = await db.execute(
|
|
select(Affectation).join(Poste).where(
|
|
Affectation.membre_id == membre.id,
|
|
Affectation.statut == "actif",
|
|
(Poste.nom.ilike("%trésorier%") | (Poste.categorie == "executif")),
|
|
)
|
|
)
|
|
if not result.scalars().first():
|
|
raise HTTPException(status_code=403, detail="Réservé au trésorier ou à l'exécutif")
|
|
|
|
result = await db.execute(
|
|
select(VenteLitterature).where(
|
|
VenteLitterature.id == vente_id,
|
|
VenteLitterature.groupe_id == membre.groupe_id,
|
|
)
|
|
)
|
|
vente = result.scalar_one_or_none()
|
|
if not vente:
|
|
raise HTTPException(status_code=404, detail="Vente introuvable")
|
|
if vente.remis_tresorier:
|
|
raise HTTPException(status_code=400, detail="Déjà reçue")
|
|
|
|
from datetime import date as date_type
|
|
vente.remis_tresorier = True
|
|
vente.recu_par = membre.id
|
|
vente.date_remise = date_type.today()
|
|
db.add(TransactionComptable(
|
|
groupe_id=vente.groupe_id,
|
|
date_transaction=vente.date_remise,
|
|
type="vente_litterature",
|
|
compte="encaisse",
|
|
montant=vente.montant,
|
|
sens="debit",
|
|
source_table="ventes_litterature",
|
|
source_id=vente.id,
|
|
note="Vente de littérature reçue",
|
|
cree_par=membre.id,
|
|
))
|
|
await db.flush()
|
|
return {"message": "Réception confirmée"}
|