routes.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import io
  2. import os
  3. from datetime import datetime
  4. import pandas as pd
  5. from docx import Document
  6. from fastapi import APIRouter, Depends, Form, HTTPException, Request, Response
  7. from fastapi.responses import HTMLResponse, RedirectResponse, StreamingResponse
  8. from fastapi.templating import Jinja2Templates
  9. from sqlalchemy.orm import Session
  10. from .auth import ldap_authenticate
  11. from .db import engine, get_session
  12. from .models import Bemerkung, Forderung, Kunde, Zahlung
  13. from .schemas import BemerkungIn
  14. router = APIRouter()
  15. templates = Jinja2Templates(directory="templates")
  16. @router.get("/", response_class=HTMLResponse)
  17. def index(request: Request):
  18. return RedirectResponse(url="/forderungen")
  19. @router.get("/login", response_class=HTMLResponse)
  20. def login_get(request: Request):
  21. return templates.TemplateResponse("login.html", {"request": request})
  22. @router.post("/login")
  23. def login_post(username: str = Form(...), password: str = Form(...)):
  24. user = ldap_authenticate(username, password)
  25. if not user:
  26. raise HTTPException(status_code=401, detail="Invalid credentials")
  27. # For a simple demo we set a cookie (not secure) -- replace with real session in production
  28. response = RedirectResponse(url="/forderungen", status_code=302)
  29. response.set_cookie("user", user["username"])
  30. return response
  31. @router.get("/forderungen", response_class=HTMLResponse)
  32. def forderungsliste(request: Request, db: Session = Depends(get_session), limit: int = 100):
  33. q = db.query(Forderung).order_by(Forderung.faelligkeit.asc()).limit(limit).all()
  34. return templates.TemplateResponse("list.html", {"request": request, "forderungen": q})
  35. @router.get("/export/csv")
  36. def export_csv(db: Session = Depends(get_session)):
  37. q = db.query(Forderung).all()
  38. rows = []
  39. for f in q:
  40. rows.append(
  41. {
  42. "id": f.id,
  43. "rechnungsnummer": f.rechnungsnummer,
  44. "kunde": f.kunde.name if f.kunde else "",
  45. "betrag": float(f.betrag),
  46. "faelligkeit": f.faelligkeit.isoformat() if f.faelligkeit else "",
  47. }
  48. )
  49. df = pd.DataFrame(rows)
  50. buf = io.StringIO()
  51. df.to_csv(buf, index=False)
  52. buf.seek(0)
  53. return Response(
  54. content=buf.getvalue(),
  55. media_type="text/csv",
  56. headers={"Content-Disposition": "attachment; filename=forderungen.csv"},
  57. )
  58. @router.get("/export/xlsx")
  59. def export_xlsx(db: Session = Depends(get_session)):
  60. q = db.query(Forderung).all()
  61. rows = []
  62. for f in q:
  63. rows.append(
  64. {
  65. "id": f.id,
  66. "rechnungsnummer": f.rechnungsnummer,
  67. "kunde": f.kunde.name if f.kunde else "",
  68. "betrag": float(f.betrag),
  69. "faelligkeit": f.faelligkeit.isoformat() if f.faelligkeit else "",
  70. }
  71. )
  72. df = pd.DataFrame(rows)
  73. buf = io.BytesIO()
  74. with pd.ExcelWriter(buf, engine="openpyxl") as writer:
  75. df.to_excel(writer, index=False, sheet_name="Forderungen")
  76. buf.seek(0)
  77. return StreamingResponse(
  78. buf,
  79. media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  80. headers={"Content-Disposition": "attachment; filename=forderungen.xlsx"},
  81. )
  82. @router.get("/detail/{id}", response_class=HTMLResponse)
  83. def detail(request: Request, id: int, db: Session = Depends(get_session)):
  84. f = db.query(Forderung).filter(Forderung.id == id).first()
  85. if not f:
  86. raise HTTPException(status_code=404)
  87. return templates.TemplateResponse("detail.html", {"request": request, "forderung": f})
  88. @router.post("/detail/{id}/bemerkung")
  89. def save_bemerkung(id: int, data: BemerkungIn, request: Request, db: Session = Depends(get_session)):
  90. user = request.cookies.get("user") or "anonymous"
  91. b = Bemerkung(
  92. forderung_id=id, benutzer=user, bemerkung=data.bemerkung, wiedervorlage_datum=data.wiedervorlage_datum
  93. )
  94. db.add(b)
  95. db.commit()
  96. return RedirectResponse(url=f"/detail/{id}", status_code=302)
  97. @router.get("/detail/{id}/export/docx")
  98. def export_docx(id: int, db: Session = Depends(get_session)):
  99. f = db.query(Forderung).filter(Forderung.id == id).first()
  100. if not f:
  101. raise HTTPException(status_code=404)
  102. doc = Document()
  103. doc.add_heading(f"Rechnung {f.rechnungsnummer}", level=1)
  104. doc.add_paragraph(f'Kunde: {f.kunde.name if f.kunde else ""}')
  105. doc.add_paragraph(f"Betrag: {float(f.betrag)}")
  106. doc.add_paragraph(f"Fälligkeit: {f.faelligkeit}")
  107. buf = io.BytesIO()
  108. doc.save(buf)
  109. buf.seek(0)
  110. return StreamingResponse(
  111. buf,
  112. media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  113. headers={"Content-Disposition": f"attachment; filename=rechnung_{f.rechnungsnummer}.docx"},
  114. )