Просмотр исходного кода

Forderung-Abfragen halb-dynamisch eingebunden

gc-server3 1 месяц назад
Родитель
Сommit
26f21aae4d

+ 2 - 1
app/main.py

@@ -12,7 +12,8 @@ app.include_router(router)
 @app.on_event("startup")
 @app.on_event("startup")
 def on_startup():
 def on_startup():
     # Create tables for demo if they do not exist
     # Create tables for demo if they do not exist
-    Base.metadata.create_all(bind=engine)
+    # Base.metadata.create_all(bind=engine)
+    pass
 
 
 
 
 app.mount("/static", StaticFiles(directory="static"), name="static")
 app.mount("/static", StaticFiles(directory="static"), name="static")

+ 80 - 3
app/routes.py

@@ -1,4 +1,5 @@
 import io
 import io
+from datetime import datetime
 
 
 import pandas as pd
 import pandas as pd
 from docx import Document
 from docx import Document
@@ -6,6 +7,7 @@ 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 HTMLResponse, RedirectResponse, StreamingResponse
 from fastapi.templating import Jinja2Templates
 from fastapi.templating import Jinja2Templates
+from sqlalchemy import text
 from sqlalchemy.orm import Session
 from sqlalchemy.orm import Session
 
 
 from .auth import ldap_authenticate
 from .auth import ldap_authenticate
@@ -41,7 +43,82 @@ def login_post(username: str = Form(...), password: str = Form(...)):
 @router.get("/forderungen", response_class=HTMLResponse)
 @router.get("/forderungen", response_class=HTMLResponse)
 def forderungsliste(request: Request, db: Session = Depends(get_session), limit: int = 100):
 def forderungsliste(request: Request, db: Session = Depends(get_session), limit: int = 100):
     q = db.query(Forderung).order_by(Forderung.faelligkeit.asc()).limit(limit).all()
     q = db.query(Forderung).order_by(Forderung.faelligkeit.asc()).limit(limit).all()
-    return templates.TemplateResponse("base/list.html", {"request": request, "forderungen": q})
+    return templates.TemplateResponse(request, "base/list.html", {"request": request, "forderungen": q})
+
+
+def number_format(input: float) -> str:
+    return format(input, "0,.2f").replace(".", ":").replace(",", ".").replace(":", ",")
+
+
+def date_format(input: datetime) -> str:
+    return input.strftime("%d.%m.%Y")
+
+
+templates.env.filters["number_format"] = number_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):
+    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'"},
+        "BenutzerSelect": {"selectedOptionValue": "winter"},
+    }
+    query = templates.TemplateResponse(request, "forderungen/queries/forderungen_liste.sql", context).body.decode(
+        "utf-8"
+    )
+    # print(query)
+    # q = db.execute(text("SELECT * FROM [dbo].[Forderungen]"))
+    q = db.execute(text(query))
+    return templates.TemplateResponse(request, "forderungen/liste.html", {"request": request, "forderungen_liste": q})
+
+
+@router.get("/forderungen/details/{client_db}_{document_no}", response_class=HTMLResponse)
+def forderungen_details(
+    request: Request, client_db: str, document_no: str, db: Session = Depends(get_session), limit: int = 100
+):
+    context = {
+        "appsmith": {
+            "URL": {
+                "queryParams": {
+                    "Client_DB": "'" + client_db + "'",
+                    "Document_No": "'" + document_no + "'",
+                }
+            }
+        },
+        "BelegeFilter": {"selectedOptionValue": "D"},
+        "BenutzerSelect": {"selectedOptionValue": "winter"},
+    }
+
+    template_context = {}
+    for filename in [
+        "auftrag_positionen",
+        "forderung_belege",
+        "forderung_kommentar",
+        "forderung_kopf",
+        "forderung_mahnung",
+    ]:
+        query = templates.TemplateResponse(request, f"forderungen/queries/{filename}.sql", context).body.decode("utf-8")
+        template_context[filename] = db.execute(text(query)).fetchall()
+    template_context["forderung"] = {
+        "id": "12345",
+        "rechnungsnummer": "WERE123445",
+        "kunde": "Burghard",
+        "betrag": "120,00",
+        "faelligkeit": "23.12.1983",
+    }
+    return templates.TemplateResponse(request, "forderungen/details.html", template_context)
 
 
 
 
 @router.get("/chat", response_class=HTMLResponse)
 @router.get("/chat", response_class=HTMLResponse)
@@ -106,7 +183,7 @@ def detail(request: Request, id: int, db: Session = Depends(get_session)):
     f = db.query(Forderung).filter(Forderung.id == id).first()
     f = db.query(Forderung).filter(Forderung.id == id).first()
     if not f:
     if not f:
         raise HTTPException(status_code=404)
         raise HTTPException(status_code=404)
-    return templates.TemplateResponse("base/detail.html", {"request": request, "forderung": f})
+    return templates.TemplateResponse(request, "base/detail.html", {"request": request, "forderung": f})
 
 
 
 
 @router.post("/detail/{id}/bemerkung")
 @router.post("/detail/{id}/bemerkung")
@@ -142,7 +219,7 @@ def export_docx(id: int, db: Session = Depends(get_session)):
 
 
 @router.get("/export/docx")
 @router.get("/export/docx")
 def export_docx2(id: int, db: Session = Depends(get_session)):
 def export_docx2(id: int, db: Session = Depends(get_session)):
-    doc = DocxTemplate("templates\\docx\\Mahnung_AHR.docx")
+    doc = DocxTemplate("templates\\forderungen\\docs\\Mahnung_AHR.docx")
     context = {
     context = {
         "Kunde_Name": "Robert Burghard",
         "Kunde_Name": "Robert Burghard",
         "Kunde_Adresse": "Im Waldhof 14a",
         "Kunde_Adresse": "Im Waldhof 14a",

+ 143 - 0
templates/forderungen/details.html

@@ -0,0 +1,143 @@
+{% extends "base/base.html" %}
+{% block content %}
+<div class="card mb-3">
+  <div class="card-body">
+    <h5 class="card-title">Rechnung {{ forderung.rechnungsnummer }}</h5>
+    <p>Kunde: {{ forderung.kunde.name if forderung.kunde else '' }}</p>
+    <p>Betrag: {{ forderung.betrag }}</p>
+    <p>Fälligkeit: {{ forderung.faelligkeit }}</p>
+    <div class="mt-3">
+      <a class="btn btn-outline-secondary me-2" href="/detail/{{ forderung.id }}/export/docx">Export DOCX</a>
+      <a class="btn btn-outline-primary" href="mailto:?subject=Rechnung%20{{ forderung.rechnungsnummer }}&body=Sie%20finden%20Details%20unter%20{{ request.url }}">E-Mail (mailto)</a>
+    </div>
+  </div>
+</div>
+
+<div class="card">
+  <div class="card-body">
+    <h6>Neue Bemerkung</h6>
+    <form method="post" action="/detail/{{ forderung.id }}/bemerkung">
+      <div class="mb-2">
+        <textarea name="bemerkung" class="form-control" rows="3"></textarea>
+      </div>
+      <div class="mb-2">
+        <label>Wiedervorlage</label>
+        <input type="date" name="wiedervorlage_datum" class="form-control" />
+      </div>
+      <button class="btn btn-primary" type="submit">Speichern</button>
+      <a class="btn btn-secondary ms-2" href="/forderungen">Zurück</a>
+    </form>
+  </div>
+</div>
+
+{% if forderung.bemerkungen %}
+<div class="mt-3">
+  <h5>Bemerkungen</h5>
+  <ul class="list-group">
+    {% for b in forderung.bemerkungen %}
+    <li class="list-group-item">{{ b.zeitstempel }} - {{ b.benutzer }}: {{ b.bemerkung }} {% if b.wiedervorlage_datum %} (WV: {{ b.wiedervorlage_datum }}){% endif %}</li>
+    {% endfor %}
+  </ul>
+</div>
+{% endif %}
+
+<h2>Forderung Details</h2>
+
+<table class="table table-striped">
+  <tbody>
+    {% for row in forderung_kopf %}
+    
+    {% for value in row %}
+    <tr>
+      <th>{{ loop.index }}</th>
+      <td>{{ value }}</td>
+    </tr>
+      {% endfor %}
+    {% endfor %}
+  </tbody>
+</table>
+
+<h2>Kommentare</h2>
+
+<table class="table table-striped">
+  <thead>
+    <tr>
+        <th>Timestamp</th>
+        <th>Name</th>
+        <th>Rolle</th>
+        <th>Begründung</th>
+        <th>Wiedervorlage</th>
+        <th>Kommentar</th>
+    </tr>
+  </thead>
+  <tbody>
+    {% for row in forderung_kommentar %}
+    <tr>
+      <td>{{ row.Timestamp|date_format }}</td>
+      <td>{{ row.Name }}</td>
+      <td>{{ row.Rolle }}</td>
+      <td>{{ row.Begründung }}</td>
+      <td>{{ row.Wiedervorlage }}</td>
+      <td>{{ row.Kommentar }}</td>
+    </tr>
+    {% endfor %}
+  </tbody>
+</table>
+
+
+<h2>Mahnungen</h2>
+
+<table class="table table-striped">
+  <thead>
+    <tr>
+        <th>Kunde_Nr</th>
+        <th>Mahnung_Nr</th>
+        <th>Mahndatum</th>
+        <th>Mahnstufe</th>
+        <th>Rechnung_Nr</th>
+        <th>Rechnung_Datum</th>
+        <th>Betrag</th>
+        <th>Offen</th>
+    </tr>
+  </thead>
+  <tbody>
+    {% for row in forderung_mahnung %}
+    <tr>
+      <td>{{ row.Kunde_Nr }}</td>
+      <td>{{ row.Mahnung_Nr }}</td>
+      <td>{{ row.Mahndatum|date_format }}</td>
+      <td>{{ row.Rechnung_Nr }}</td>
+      <td>{{ row.Rechnung_Datum|date_format }}</td>
+      <td>{{ row.Betrag|number_format }}</td>
+      <td>{{ row.Offen|number_format }}</td>
+    </tr>
+    {% endfor %}
+  </tbody>
+</table>
+
+
+
+<h2>Auftragspositionen</h2>
+
+<table class="table table-striped">
+  <thead>
+    <tr>
+        <th>Auftrag_Nr</th>
+        <th>Auftrag_Position</th>
+        <th>Betrag</th>
+    </tr>
+  </thead>
+  <tbody>
+    {% for row in auftrag_positionen %}
+    <tr>
+      <td>{{ row.Auftrag_Nr }}</td>
+      <td>{{ row.Auftrag_Position }}</td>
+      <td>{{ row.Betrag|number_format }}</td>
+    </tr>
+    {% endfor %}
+  </tbody>
+</table>
+
+
+
+{% endblock %}

+ 0 - 0
templates/docx/Mahnung_AHR.docx → templates/forderungen/docs/Mahnung_AHR.docx


+ 43 - 0
templates/forderungen/liste.html

@@ -0,0 +1,43 @@
+{% extends "base/base.html" %}
+{% block content %}
+<div class="d-flex mb-3">
+  <a class="btn btn-secondary me-2" href="/export/csv">Export CSV</a>
+  <a class="btn btn-secondary" href="/export/xlsx">Export XLSX</a>
+</div>
+<table class="table table-striped">
+  <thead>
+    <tr>
+        <th>.</th>
+        <th>Filiale</th>
+        <th>Kunde</th>
+        <th>Bereich</th>
+        <th>Verursacher</th>
+        <th>RG-Nr.</th>
+        <th>RG-Datum</th>
+        <th>Tage</th>
+        <th>Mahnstufe</th>
+        <th>offen</th>
+        <th>Kunde<br>offen ges.</th>
+        <th>Kommentar</th>
+    </tr>
+  </thead>
+  <tbody>
+    {% for row in forderungen_liste %}
+    <tr>
+      <td><a href="/forderungen/details/{{ row.Client_DB }}_{{ row.Document_No }}" class="btn btn-sm btn-primary">X</a></td>
+      <td>{{ row.Standort_Name }}</td>
+      <td>{{ row.Kunde }}</td>
+      <td>{{ row.Bereich }}</td>
+      <td>{{ row.Verursacher }}</td>
+      <td>{{ row.Document_No }}</td>
+      <td>{{ row.Invoice_Date|date_format }}</td>
+      <td>{{ row.Tage }}</td>
+      <td>{{ row.Mahnstufe }}</td>
+      <td>{{ row.offen|number_format }}</td>
+      <td>{{ row.offen_Kunde_gesamt|number_format }}</td>
+      <td>{{ row.Kommentar_Fibu or '' }}</td>
+    </tr>
+    {% endfor %}
+  </tbody>
+</table>
+{% endblock %}

+ 8 - 0
templates/forderungen/queries/auftrag_positionen.sql

@@ -0,0 +1,8 @@
+SELECT TOP 100 [Auftrag_Nr]
+     , [Auftrag_Position]
+     , [Betrag]
+FROM [dbo].[Auftrag_Positionen]
+WHERE (
+	Client_DB = {{ appsmith.URL.queryParams.Client_DB }}
+AND [Rechnung_Nr] = {{ appsmith.URL.queryParams.Document_No }}
+) OR 1 != 1

+ 30 - 0
templates/forderungen/queries/forderung_belege.sql

@@ -0,0 +1,30 @@
+SELECT TOP 1000 [Client_DB]
+     , [Kunde_Nr]
+     , [Fahrzeug_Nr]
+     , [Beleg_Nr]
+     , [Bezug_Beleg_Nr]
+     , [Beleg_Datum]
+     , [Beleg_Beschreibung]
+     , [Betrag]
+     , [Benutzer]
+     , iif([Open] = '0', 'N', 'J') as [Status]
+     , CASE
+           WHEN [Document Type] = '2' THEN 'R'
+           WHEN [Document Type] = '3' THEN 'R'
+           WHEN [Document Type] = '1' THEN 'Z'
+           WHEN [Document Type] = '0' THEN 'K'
+           ELSE 'X' END AS [Beleg_Art]
+FROM [dbo].[Forderung_Belege]
+WHERE (
+	[Client_DB] = {{ appsmith.URL.queryParams.Client_DB }}
+AND [Kunde_Nr] IN (SELECT [Kunde_Nr] FROM [dbo].[Forderung_Mahnung] WHERE [Rechnung_Nr] = {{ appsmith.URL.queryParams.Document_No }} )
+AND (
+	('D' = '{{ BelegeFilter.selectedOptionValue }}')
+	OR
+	('F' = '{{ BelegeFilter.selectedOptionValue }}' AND [Fahrzeug_Nr] IN (SELECT [Fahrzeug_Nr] FROM [dbo].[Forderung_Mahnung] WHERE [Rechnung_Nr] = {{ appsmith.URL.queryParams.Document_No }} )) 
+
+)
+	
+) OR 1 != 1
+
+ORDER BY Beleg_Datum

+ 10 - 0
templates/forderungen/queries/forderung_kommentar.sql

@@ -0,0 +1,10 @@
+SELECT Timestamp,
+ Name,
+ Rolle,
+ Begründung,
+ Wiedervorlage,
+ Kommentar
+ 
+FROM dbo.Forderungen_Kommentar_Benutzer
+WHERE Client_DB = {{ appsmith.URL.queryParams.Client_DB }}
+AND Document_No = {{ appsmith.URL.queryParams.Document_No }}

+ 4 - 0
templates/forderungen/queries/forderung_kopf.sql

@@ -0,0 +1,4 @@
+SELECT * 
+FROM dbo.Forderungen
+WHERE Client_DB = {{ appsmith.URL.queryParams.Client_DB }}
+AND Document_No = {{ appsmith.URL.queryParams.Document_No }}

+ 15 - 0
templates/forderungen/queries/forderung_mahnung.sql

@@ -0,0 +1,15 @@
+SELECT TOP 100 [Client_DB]
+     , [Kunde_Nr]
+     , [Mahnung_Nr]
+     , [Mahndatum]
+     , [Mahnstufe]
+     , [Rechnung_Nr]
+     , [Rechnung_Datum]
+     , [Betrag]
+     , [Offen]
+FROM [dbo].[Forderung_Mahnung]
+WHERE (
+	Client_DB = {{ appsmith.URL.queryParams.Client_DB }}
+AND [Kunde_Nr] IN (SELECT [Kunde_Nr] FROM [dbo].[Forderung_Mahnung] WHERE [Rechnung_Nr] = {{ appsmith.URL.queryParams.Document_No }} )
+) OR 1 != 1
+ORDER BY Mahndatum

+ 52 - 0
templates/forderungen/queries/forderungen_liste.sql

@@ -0,0 +1,52 @@
+SELECT [T1].[Client_DB]
+     , [T1].[Hauptbetrieb_ID]
+     , [T1].[Hauptbetrieb_Name]
+     , [T1].[Standort_ID]
+     , [T1].[Standort_Name]     
+     , [T1].[Document_No]
+     , [T1].[Rechnung_Gutschrift]     
+     , [T1].[User_ID]
+     , [T1].[VIN]
+     , [T1].[Comment]
+     , [T1].[offen]
+     , [T1].[offen_Kunde_gesamt]
+     , [T1].[Invoice_Date]
+     , [T1].[Verursacher]
+     , [T1].[Kunde]
+     , [T1].[Beleg]
+     , [T1].[Bereich]
+     , [T1].[Tage]
+		 , case when [T1].[Tage] > 90 then 3
+		 when [T1].[Tage] > 60 then 2
+		   when [T1].[Tage] > 30 then 1
+			 else 0 end as [Stufe]
+     , [T1].[Staffel]
+     , [T1].[Mahnstufe]
+     , [T1].[Forderungsart]
+     , [T1].[Abwarten]
+     , [T1].[Verursacher_Benutzer_ID]
+	   , T3.[Kommentar_Fibu]
+     , T3.[Kommentar_Abteilung] 
+		 , T3.[Wiedervorlage]
+		 , T3.[Begründung]
+
+FROM [dbo].[Forderungen] T1
+INNER JOIN [dbo].[Benutzer_Rechte] T2 ON (T2.Benutzer_ID = '{{ BenutzerSelect.selectedOptionValue }}' OR T2.Benutzer_ID = '')
+AND T1.Hauptbetrieb_ID = T2.Hauptbetrieb_ID AND T1.Standort_ID = T2.Standort_ID AND (T2.Rolle = 'Buchhaltung' OR T2.Benutzer_ID = T1.Verursacher_Benutzer_ID)
+LEFT JOIN [dbo].[Forderungen_Kommentar_letzte] T3 ON T1.Client_DB = T3.Client_DB AND T1.Document_No = T3.Document_No
+WHERE 1 = 1
+AND [Invoice_Date] >= {{ RechnungsdatumVon.selectedDate }}
+AND [Invoice_Date] <= {{ RechnungsdatumBis.selectedDate }}
+AND T1.[Client_DB] LIKE '%' + {{ Hauptbetrieb.selectedOptionValue }}
+AND T1.[Standort_ID] LIKE '%' + {{ Standort.selectedOptionValue }}
+AND T1.[Document_No] LIKE '%' + {{ Rechnungsnummer.text }} + '%'
+AND [Kunde] LIKE '%' + {{ Kunde.selectedOptionValue }}
+AND [Verursacher] LIKE '%' + {{ Verursacher.selectedOptionValue }}
+AND [VIN] LIKE '%' + {{ Fahrzeug.selectedOptionValue }}
+AND [Staffel] LIKE '%' + {{ Staffel.selectedOptionValue }}
+AND [Mahnstufe] LIKE '%' + {{ Mahnstufe.selectedOptionValue }}
+AND (T3.Wiedervorlage IS NULL OR (
+	T3.Wiedervorlage >= {{ WiedervorlageVon.selectedDate }}
+	AND T3.Wiedervorlage <= {{ WiedervorlageBis.selectedDate }}
+))
+ORDER BY [Tage] DESC, T3.[Wiedervorlage] ASC

+ 9 - 0
templates/forderungen/queries/insert_kommentar.sql

@@ -0,0 +1,9 @@
+INSERT INTO dbo.Forderungen_Kommentar (Client_DB, Document_No, Timestamp, Benutzer_ID, Begründung, Wiedervorlage, Kommentar) VALUES (
+	'{{frm_Kommentar.data.txt_Client_DB}}',
+	'{{frm_Kommentar.data.txt_Document_No}}',
+  getdate(),
+	'{{appsmith.URL.queryParams.Benutzer_ID}}',
+	'{{frm_Kommentar.data.Select1.selectedOptionValue}}',
+	'{{frm_Kommentar.data.DatePicker1}}',
+  '{{frm_Kommentar.data.txt_Kommentar}}'
+	)