|
@@ -1,11 +1,18 @@
|
|
|
import io
|
|
import io
|
|
|
from datetime import datetime
|
|
from datetime import datetime
|
|
|
|
|
+from pathlib import Path
|
|
|
|
|
+from urllib.parse import unquote, urlencode
|
|
|
|
|
|
|
|
import pandas as pd
|
|
import pandas as pd
|
|
|
from docx import Document
|
|
from docx import Document
|
|
|
from docxtpl import DocxTemplate
|
|
from docxtpl import DocxTemplate
|
|
|
from fastapi import APIRouter, Depends, Form, HTTPException, Request, Response
|
|
from fastapi import APIRouter, Depends, Form, HTTPException, Request, Response
|
|
|
-from fastapi.responses import HTMLResponse, RedirectResponse, StreamingResponse
|
|
|
|
|
|
|
+from fastapi.responses import (
|
|
|
|
|
+ FileResponse,
|
|
|
|
|
+ HTMLResponse,
|
|
|
|
|
+ RedirectResponse,
|
|
|
|
|
+ StreamingResponse,
|
|
|
|
|
+)
|
|
|
from fastapi.templating import Jinja2Templates
|
|
from fastapi.templating import Jinja2Templates
|
|
|
from sqlalchemy import text
|
|
from sqlalchemy import text
|
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy.orm import Session
|
|
@@ -21,12 +28,17 @@ templates = Jinja2Templates(directory="templates")
|
|
|
|
|
|
|
|
@router.get("/", response_class=HTMLResponse)
|
|
@router.get("/", response_class=HTMLResponse)
|
|
|
def index(request: Request):
|
|
def index(request: Request):
|
|
|
- return RedirectResponse(url="/forderungen")
|
|
|
|
|
|
|
+ return RedirectResponse(url="/login")
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/login", response_class=HTMLResponse)
|
|
@router.get("/login", response_class=HTMLResponse)
|
|
|
def login_get(request: Request):
|
|
def login_get(request: Request):
|
|
|
- return templates.TemplateResponse("base/login.html", {"request": request})
|
|
|
|
|
|
|
+ return templates.TemplateResponse(request, "base/login.html", {"request": request})
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+@router.get("/select", response_class=HTMLResponse)
|
|
|
|
|
+def select_get(request: Request):
|
|
|
|
|
+ return templates.TemplateResponse(request, "base/select.html", {"request": request})
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/login")
|
|
@router.post("/login")
|
|
@@ -51,6 +63,8 @@ def number_format(input: float) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
def date_format(input: datetime) -> str:
|
|
def date_format(input: datetime) -> str:
|
|
|
|
|
+ if input is None:
|
|
|
|
|
+ return ""
|
|
|
return input.strftime("%d.%m.%Y")
|
|
return input.strftime("%d.%m.%Y")
|
|
|
|
|
|
|
|
|
|
|
|
@@ -58,21 +72,28 @@ templates.env.filters["number_format"] = number_format
|
|
|
templates.env.filters["date_format"] = date_format
|
|
templates.env.filters["date_format"] = date_format
|
|
|
|
|
|
|
|
|
|
|
|
|
-@router.get("/forderungen/liste", response_class=HTMLResponse)
|
|
|
|
|
-def forderungen_liste(request: Request, db: Session = Depends(get_session), limit: int = 100):
|
|
|
|
|
|
|
+def single_quote(text: str):
|
|
|
|
|
+ return "'" + unquote(text) + "'"
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+@router.get("/app/forderungen/liste", response_class=HTMLResponse)
|
|
|
|
|
+def forderungen_liste(request: Request, db: Session = Depends(get_session), limit: int = 100, page: int = 0):
|
|
|
|
|
+ params = {k: v for k, v in request.query_params.items() if v != ""}
|
|
|
|
|
+ if len(params) < len(request.query_params):
|
|
|
|
|
+ return RedirectResponse(url="/app/forderungen/liste?" + urlencode(params))
|
|
|
context = {
|
|
context = {
|
|
|
- "RechnungsdatumVon": {"selectedDate": "'2000-01-01T00:00:00'"},
|
|
|
|
|
- "RechnungsdatumBis": {"selectedDate": "'2027-01-01T00:00:00'"},
|
|
|
|
|
- "Hauptbetrieb": {"selectedOptionValue": "''"},
|
|
|
|
|
- "Standort": {"selectedOptionValue": "''"},
|
|
|
|
|
- "Rechnungsnummer": {"text": "''"},
|
|
|
|
|
- "Kunde": {"selectedOptionValue": "''"},
|
|
|
|
|
- "Verursacher": {"selectedOptionValue": "''"},
|
|
|
|
|
- "Fahrzeug": {"selectedOptionValue": "''"},
|
|
|
|
|
- "Staffel": {"selectedOptionValue": "''"},
|
|
|
|
|
- "Mahnstufe": {"selectedOptionValue": "''"},
|
|
|
|
|
- "WiedervorlageVon": {"selectedDate": "'2000-01-01T00:00:00'"},
|
|
|
|
|
- "WiedervorlageBis": {"selectedDate": "'2027-01-01T00:00:00'"},
|
|
|
|
|
|
|
+ "RechnungsdatumVon": {"selectedDate": single_quote(params.get("RechnungsdatumVon", "2000-01-01T00:00:00"))},
|
|
|
|
|
+ "RechnungsdatumBis": {"selectedDate": single_quote(params.get("RechnungsdatumBis", "2027-01-01T00:00:00"))},
|
|
|
|
|
+ "Hauptbetrieb": {"selectedOptionValue": single_quote(params.get("Hauptbetrieb", ""))},
|
|
|
|
|
+ "Standort": {"selectedOptionValue": single_quote(params.get("Standort", ""))},
|
|
|
|
|
+ "Rechnungsnummer": {"text": single_quote(params.get("Rechnungsnummer", ""))},
|
|
|
|
|
+ "Kunde": {"selectedOptionValue": single_quote(params.get("Kunde", ""))},
|
|
|
|
|
+ "Verursacher": {"selectedOptionValue": single_quote(params.get("Verursacher", ""))},
|
|
|
|
|
+ "Fahrzeug": {"selectedOptionValue": single_quote(params.get("Fahrzeug", ""))},
|
|
|
|
|
+ "Staffel": {"selectedOptionValue": single_quote(params.get("Staffel", ""))},
|
|
|
|
|
+ "Mahnstufe": {"selectedOptionValue": single_quote(params.get("Mahnstufe", ""))},
|
|
|
|
|
+ "WiedervorlageVon": {"selectedDate": single_quote(params.get("WiedervorlageVon", "2000-01-01T00:00:00"))},
|
|
|
|
|
+ "WiedervorlageBis": {"selectedDate": single_quote(params.get("WiedervorlageBis", "2027-01-01T00:00:00"))},
|
|
|
"BenutzerSelect": {"selectedOptionValue": "winter"},
|
|
"BenutzerSelect": {"selectedOptionValue": "winter"},
|
|
|
}
|
|
}
|
|
|
query = templates.TemplateResponse(request, "forderungen/queries/forderungen_liste.sql", context).body.decode(
|
|
query = templates.TemplateResponse(request, "forderungen/queries/forderungen_liste.sql", context).body.decode(
|
|
@@ -104,14 +125,21 @@ def forderungen_liste(request: Request, db: Session = Depends(get_session), limi
|
|
|
for filter_name, (key, value) in config.items():
|
|
for filter_name, (key, value) in config.items():
|
|
|
filters[filter_name] = {row[col_names.index(key)]: row[col_names.index(value)] for row in q}
|
|
filters[filter_name] = {row[col_names.index(key)]: row[col_names.index(value)] for row in q}
|
|
|
|
|
|
|
|
|
|
+ q_limit = q[page * limit : (page + 1) * limit]
|
|
|
|
|
+
|
|
|
|
|
+ defaults = {}
|
|
|
|
|
+ for filter_name in config.keys():
|
|
|
|
|
+ defaults[filter_name] = unquote(request.query_params.get(filter_name, ""))
|
|
|
# print(filters["Standort"])
|
|
# print(filters["Standort"])
|
|
|
|
|
|
|
|
return templates.TemplateResponse(
|
|
return templates.TemplateResponse(
|
|
|
- request, "forderungen/liste.html", {"request": request, "forderungen_liste": q, "filter": filters}
|
|
|
|
|
|
|
+ request,
|
|
|
|
|
+ "forderungen/liste.html",
|
|
|
|
|
+ {"request": request, "forderungen_liste": q_limit, "filter": filters, "defaults": defaults},
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
-@router.get("/forderungen/details/{client_db}_{document_no}", response_class=HTMLResponse)
|
|
|
|
|
|
|
+@router.get("/app/forderungen/details/{client_db}_{document_no}", response_class=HTMLResponse)
|
|
|
def forderungen_details(
|
|
def forderungen_details(
|
|
|
request: Request, client_db: str, document_no: str, db: Session = Depends(get_session), limit: int = 100
|
|
request: Request, client_db: str, document_no: str, db: Session = Depends(get_session), limit: int = 100
|
|
|
):
|
|
):
|
|
@@ -145,6 +173,8 @@ def forderungen_details(
|
|
|
"betrag": "120,00",
|
|
"betrag": "120,00",
|
|
|
"faelligkeit": "23.12.1983",
|
|
"faelligkeit": "23.12.1983",
|
|
|
}
|
|
}
|
|
|
|
|
+ template_context["files"] = list(Path(f"C:\\Projekte\\Reisacher-Fileserver\\{client_db}\\{document_no}").glob("*"))
|
|
|
|
|
+
|
|
|
return templates.TemplateResponse(request, "forderungen/details.html", template_context)
|
|
return templates.TemplateResponse(request, "forderungen/details.html", template_context)
|
|
|
|
|
|
|
|
|
|
|
|
@@ -244,28 +274,40 @@ def export_docx(id: int, db: Session = Depends(get_session)):
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
-@router.get("/export/docx")
|
|
|
|
|
-def export_docx2(id: int, db: Session = Depends(get_session)):
|
|
|
|
|
|
|
+@router.get("/app/forderungen/export/{client_db}_{document_no}/")
|
|
|
|
|
+def export_docx2(request: Request, client_db: str, document_no: str, db: Session = Depends(get_session)):
|
|
|
|
|
+ context = {
|
|
|
|
|
+ "appsmith": {
|
|
|
|
|
+ "URL": {
|
|
|
|
|
+ "queryParams": {
|
|
|
|
|
+ "Client_DB": "'" + client_db + "'",
|
|
|
|
|
+ "Document_No": "'" + document_no + "'",
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+ query = templates.TemplateResponse(request, "forderungen/queries/forderung_kopf.sql", context).body.decode("utf-8")
|
|
|
|
|
+ q = db.execute(text(query)).fetchone()
|
|
|
|
|
+
|
|
|
doc = DocxTemplate("templates\\forderungen\\docs\\Mahnung_AHR.docx")
|
|
doc = DocxTemplate("templates\\forderungen\\docs\\Mahnung_AHR.docx")
|
|
|
|
|
+ filename = Path(
|
|
|
|
|
+ f"C:\\Projekte\\Reisacher-Fileserver\\{client_db}\\{document_no}\\Mahnung_{client_db}_{document_no}.docx"
|
|
|
|
|
+ )
|
|
|
|
|
+ filename.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
+
|
|
|
context = {
|
|
context = {
|
|
|
- "Kunde_Name": "Robert Burghard",
|
|
|
|
|
|
|
+ "Kunde_Name": q.Kunde,
|
|
|
"Kunde_Adresse": "Im Waldhof 14a",
|
|
"Kunde_Adresse": "Im Waldhof 14a",
|
|
|
"Kunde_PLZ": "61476",
|
|
"Kunde_PLZ": "61476",
|
|
|
"Kunde_Ort": "Kronberg",
|
|
"Kunde_Ort": "Kronberg",
|
|
|
- "Datum_heute": "25.03.2026",
|
|
|
|
|
- "Kunde_Nr": "23121983",
|
|
|
|
|
|
|
+ "Datum_heute": datetime.now().strftime("%d.%m.%Y"),
|
|
|
|
|
+ "Kunde_Nr": q.Kunde.split("-")[-1].strip(),
|
|
|
"Fahrzeug_Kennzeichen": "OF-RB 512",
|
|
"Fahrzeug_Kennzeichen": "OF-RB 512",
|
|
|
- "Rechnung_Nr": "2600007",
|
|
|
|
|
|
|
+ "Rechnung_Nr": q.Document_No,
|
|
|
"Betrag_SB": "250,00",
|
|
"Betrag_SB": "250,00",
|
|
|
"Betrag_USt": "380,00",
|
|
"Betrag_USt": "380,00",
|
|
|
}
|
|
}
|
|
|
doc.render(context)
|
|
doc.render(context)
|
|
|
- # doc.save("export\\Mahnung_AHR_Burghard.docx")
|
|
|
|
|
- buf = io.BytesIO()
|
|
|
|
|
- doc.save(buf)
|
|
|
|
|
- buf.seek(0)
|
|
|
|
|
- return StreamingResponse(
|
|
|
|
|
- buf,
|
|
|
|
|
- media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
|
|
|
- headers={"Content-Disposition": "attachment; filename=Mahnung_Burghard.docx"},
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ doc.save(filename)
|
|
|
|
|
+
|
|
|
|
|
+ return FileResponse(filename, media_type="application/octet-stream", filename=filename.name)
|