Files
BillManager/routers/devis.py
2026-03-13 01:11:18 +01:00

209 lines
6.5 KiB
Python

import json
from datetime import date, timedelta
from fastapi import APIRouter, Depends, Request, Form, HTTPException
from fastapi.responses import HTMLResponse, RedirectResponse, Response
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session
from database import get_db
from models import Devis, LigneDevis, Client, StatutDevis
from numerotation import generer_numero_devis
from auth import get_current_user
from template_helper import render
from config import settings
router = APIRouter(prefix="/devis", tags=["devis"], dependencies=[Depends(get_current_user)])
templates = Jinja2Templates(directory="templates")
@router.get("/", response_class=HTMLResponse)
def liste_devis(request: Request, db: Session = Depends(get_db)):
devis = db.query(Devis).order_by(Devis.date_emission.desc()).all()
return render(templates, "devis/liste.html", request, {
"devis": devis
})
@router.get("/nouveau", response_class=HTMLResponse)
def nouveau_devis_form(request: Request, db: Session = Depends(get_db)):
clients = db.query(Client).filter(Client.actif == True).order_by(Client.nom).all()
return render(templates, "devis/form.html", request, {
"devis": None,
"clients": clients,
"titre": "Nouveau devis",
"date_aujourd_hui": date.today().isoformat(),
"date_validite_defaut": (date.today() + timedelta(days=30)).isoformat(),
})
@router.post("/nouveau")
def creer_devis(
request: Request,
client_id: int = Form(...),
date_emission: str = Form(...),
date_validite: str = Form(...),
notes: str = Form(""),
conditions: str = Form(""),
lignes_json: str = Form(...),
db: Session = Depends(get_db)
):
numero = generer_numero_devis(db)
devis = Devis(
numero=numero,
client_id=client_id,
date_emission=date.fromisoformat(date_emission),
date_validite=date.fromisoformat(date_validite),
notes=notes,
conditions=conditions,
)
db.add(devis)
db.flush()
lignes = json.loads(lignes_json)
for i, l in enumerate(lignes):
ligne = LigneDevis(
devis_id=devis.id,
description=l["description"],
quantite=float(l["quantite"]),
prix_unitaire_ht=float(l["prix_unitaire_ht"]),
ordre=i
)
db.add(ligne)
db.commit()
return RedirectResponse(f"/devis/{devis.id}", status_code=303)
@router.get("/{devis_id}", response_class=HTMLResponse)
def voir_devis(request: Request, devis_id: int, db: Session = Depends(get_db)):
devis = db.query(Devis).get(devis_id)
if not devis:
raise HTTPException(status_code=404)
return render(templates, "devis/detail.html", request, {
"devis": devis, "StatutDevis": StatutDevis
})
@router.get("/{devis_id}/modifier", response_class=HTMLResponse)
def modifier_devis_form(request: Request, devis_id: int, db: Session = Depends(get_db)):
devis = db.query(Devis).get(devis_id)
if not devis:
raise HTTPException(status_code=404)
clients = db.query(Client).filter(Client.actif == True).order_by(Client.nom).all()
return render(templates, "devis/form.html", request, {
"devis": devis,
"clients": clients,
"titre": "Modifier le devis",
"date_aujourd_hui": date.today().isoformat(),
"date_validite_defaut": devis.date_validite.isoformat(),
})
@router.post("/{devis_id}/modifier")
def modifier_devis(
devis_id: int,
client_id: int = Form(...),
date_emission: str = Form(...),
date_validite: str = Form(...),
notes: str = Form(""),
conditions: str = Form(""),
lignes_json: str = Form(...),
db: Session = Depends(get_db)
):
devis = db.query(Devis).get(devis_id)
if not devis:
raise HTTPException(status_code=404)
devis.client_id = client_id
devis.date_emission = date.fromisoformat(date_emission)
devis.date_validite = date.fromisoformat(date_validite)
devis.notes = notes
devis.conditions = conditions
for ligne in devis.lignes:
db.delete(ligne)
db.flush()
lignes = json.loads(lignes_json)
for i, l in enumerate(lignes):
ligne = LigneDevis(
devis_id=devis.id,
description=l["description"],
quantite=float(l["quantite"]),
prix_unitaire_ht=float(l["prix_unitaire_ht"]),
ordre=i
)
db.add(ligne)
db.commit()
return RedirectResponse(f"/devis/{devis_id}", status_code=303)
@router.post("/{devis_id}/statut")
def changer_statut_devis(
devis_id: int,
statut: str = Form(...),
db: Session = Depends(get_db)
):
devis = db.query(Devis).get(devis_id)
if not devis:
raise HTTPException(status_code=404)
devis.statut = StatutDevis(statut)
db.commit()
return RedirectResponse(f"/devis/{devis_id}", status_code=303)
@router.get("/{devis_id}/pdf")
def telecharger_devis__pdf(devis_id: int, db: Session = Depends(get_db)):
from weasyprint import HTML
devis = db.query(Devis).get(devis_id)
if not devis:
raise HTTPException(status_code=404)
html_content = templates.get_template("pdf/devis.html").render({
"devis": devis,
"settings": settings,
})
pdf_bytes = HTML(string=html_content, base_url=".").write_pdf()
filename = f"devis-{devis.numero}.pdf"
return Response(
content=pdf_bytes,
media_type="application/pdf",
headers={"Content-Disposition": f'attachment; filename="{filename}"'}
)
@router.post("/{devis_id}/convertir")
def convertir_en_facture(devis_id: int, db: Session = Depends(get_db)):
from models import Facture, LigneFacture
from numerotation import generer_numero_facture
devis = db.query(Devis).get(devis_id)
if not devis:
raise HTTPException(status_code=404)
numero = generer_numero_facture(db)
facture = Facture(
numero=numero,
client_id=devis.client_id,
devis_id=devis.id,
date_emission=date.today(),
date_echeance=date.today() + timedelta(days=30),
notes=devis.notes,
)
db.add(facture)
db.flush()
for i, l in enumerate(devis.lignes):
ligne = LigneFacture(
facture_id=facture.id,
description=l.description,
quantite=l.quantite,
prix_unitaire_ht=l.prix_unitaire_ht,
ordre=i
)
db.add(ligne)
devis.statut = StatutDevis.accepte
db.commit()
return RedirectResponse(f"/factures/{facture.id}", status_code=303)