Эх сурвалжийг харах

Tabelle-Template ausgelagert, Infinite Scroll, Dashboard

gc-server3 4 долоо хоног өмнө
parent
commit
59f24cf8bb

+ 35 - 7
app/routes.py

@@ -1,5 +1,6 @@
 import io
 from datetime import datetime
+from operator import itemgetter
 from pathlib import Path
 from urllib.parse import unquote, urlencode
 
@@ -82,18 +83,19 @@ def forderungen_liste(request: Request, db: Session = Depends(get_session), limi
     if len(params) < len(request.query_params):
         return RedirectResponse(url="/app/forderungen/liste?" + urlencode(params))
     context = {
-        "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", ""))},
+        "Bereich": {"selectedOptionValue": single_quote(params.get("Bereich", ""))},
+        "Verursacher": {"selectedOptionValue": single_quote(params.get("Verursacher", ""))},
         "Rechnungsnummer": {"text": single_quote(params.get("Rechnungsnummer", ""))},
+        "RechnungsdatumVon": {"selectedDate": single_quote(params.get("RechnungsdatumVon", "2000-01-01T00:00:00"))},
+        "RechnungsdatumBis": {"selectedDate": single_quote(params.get("RechnungsdatumBis", "2027-01-01T00:00:00"))},
         "Kunde": {"selectedOptionValue": single_quote(params.get("Kunde", ""))},
-        "Verursacher": {"selectedOptionValue": single_quote(params.get("Verursacher", ""))},
+        "WiedervorlageVon": {"selectedDate": single_quote(params.get("WiedervorlageVon", "2000-01-01T00:00:00"))},
+        "WiedervorlageBis": {"selectedDate": single_quote(params.get("WiedervorlageBis", "2027-01-01T00:00:00"))},
         "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"},
     }
     query = templates.TemplateResponse(request, "forderungen/queries/forderungen_liste.sql", context).body.decode(
@@ -111,6 +113,7 @@ def forderungen_liste(request: Request, db: Session = Depends(get_session), limi
         "Standort": ("Standort_ID", "Standort_Name"),
         # "Rechnungsnummer": {"text": "''"},
         "Kunde": ("Kunde", "Kunde"),
+        "Bereich": ("Bereich", "Bereich"),
         "Verursacher": ("Verursacher", "Verursacher"),
         "Fahrzeug": ("VIN", "VIN"),
         "Staffel": ("Staffel", "Staffel"),
@@ -125,6 +128,11 @@ def forderungen_liste(request: Request, db: Session = Depends(get_session), limi
     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}
 
+    summary = {
+        "offen": sum([r[col_names.index("offen")] for r in q]),
+        "Anzahl": len(q),
+    }
+
     q_limit = q[page * limit : (page + 1) * limit]
 
     defaults = {}
@@ -132,10 +140,24 @@ def forderungen_liste(request: Request, db: Session = Depends(get_session), limi
         defaults[filter_name] = unquote(request.query_params.get(filter_name, ""))
     # print(filters["Standort"])
 
+    if len(q_limit) == 0:
+        return None
+
+    template = "forderungen/liste.html"
+    if page > 0:
+        template = "forderungen/liste_tabelle.html"
+
     return templates.TemplateResponse(
         request,
-        "forderungen/liste.html",
-        {"request": request, "forderungen_liste": q_limit, "filter": filters, "defaults": defaults},
+        template,
+        {
+            "request": request,
+            "forderungen_liste": q_limit,
+            "filter": filters,
+            "defaults": defaults,
+            "summary": summary,
+            "page": page,
+        },
     )
 
 
@@ -178,6 +200,12 @@ def forderungen_details(
     return templates.TemplateResponse(request, "forderungen/details.html", template_context)
 
 
+@router.get("/app/forderungen/dashboard", response_class=HTMLResponse)
+def forderungen_dashboard(request: Request, db: Session = Depends(get_session)):
+    context = {"summary": False}
+    return templates.TemplateResponse(request, "forderungen/dashboard.html", context)
+
+
 @router.get("/chat", response_class=HTMLResponse)
 def chat(request: Request):
     # q = db.query(Forderung).order_by(Forderung.faelligkeit.asc()).limit(limit).all()

+ 15 - 0
static/assets/css/main.css

@@ -0,0 +1,15 @@
+html {
+  position: relative;
+  min-height: 100%;
+}
+body {
+  margin-bottom: 60px; /* Margin bottom by footer height */
+}
+.footer {
+  position: absolute;
+  bottom: 0;
+  width: 100%;
+  height: 60px; /* Set the fixed height of the footer here */
+  line-height: 60px; /* Vertically center the text there */
+  background-color: #f5f5f5;
+}

+ 37 - 10
templates/base/base.html

@@ -13,11 +13,15 @@
 
   <link href="https://cdn.jsdelivr.net/npm/@coreui/coreui@5.6.1/dist/css/coreui.min.css" rel="stylesheet">
   <link href="https://cdn.jsdelivr.net/npm/@coreui/icons@3.0.1/css/all.min.css" rel="stylesheet">
-  
+
   <script src="https://cdn.jsdelivr.net/npm/@coreui/coreui@5.6.1/dist/js/coreui.bundle.min.js"></script>
   <script src="https://cdn.jsdelivr.net/npm/@coreui/icons@3.0.1/dist/cjs/index.min.js"></script>
+
+
+  <!--<link href="/static/assets/css/mainasdf.css" rel="stylesheet">-->
+
   <!-- Favicon icon -->
-	<link rel="icon" href="/static/assets/images/favicon.ico" type="image/x-icon">
+  <link rel="icon" href="/static/assets/images/favicon.ico" type="image/x-icon">
 </head>
 
 <body class="container py-3" hx-boost="true">
@@ -56,6 +60,7 @@
         <ul class="header-nav d-none d-lg-flex">
           <li class="nav-item"><a class="nav-link" href="/select">Übersicht</a></li>
           <li class="nav-item"><a class="nav-link" href="/app/forderungen/liste">Liste</a></li>
+          <li class="nav-item"><a class="nav-link" href="/app/forderungen/dashboard">Dashboard</a></li>
           <li class="nav-item"><a class="nav-link" href="#">Einstellungen</a></li>
         </ul>
         <ul class="header-nav ms-auto">
@@ -99,24 +104,28 @@
           </li>
           <li class="nav-item dropdown"><a class="nav-link py-0 pe-0" data-coreui-toggle="dropdown" href="#"
               role="button" aria-haspopup="true" aria-expanded="false">
-              <div class="avatar avatar-md"><img class="avatar-img" src="/static/assets/img/avatars/winter.jpg" alt="winter@global-cube.de">
+              <div class="avatar avatar-md"><img class="avatar-img" src="/static/assets/img/avatars/winter.jpg"
+                  alt="winter@global-cube.de">
               </div>
             </a>
             <div class="dropdown-menu dropdown-menu-end pt-0">
               <div class="dropdown-header bg-body-tertiary text-body-secondary fw-semibold rounded-top mb-2">Account
               </div><a class="dropdown-item" href="#">
-                <i class="icon me-2 cil-bell"></i> Updates<span class="badge badge-sm bg-info ms-2">42</span></a><a class="dropdown-item" href="#">
-                <i class="icon me-2 cil-envelope-open"></i> Messages<span class="badge badge-sm bg-success ms-2">42</span></a><a class="dropdown-item"
-                href="#">
-                <i class="icon me-2 cil-task"></i> Tasks<span class="badge badge-sm bg-danger ms-2">42</span></a><a class="dropdown-item" href="#">
-                <i class="icon me-2 cil-comment-square"></i> Comments<span class="badge badge-sm bg-warning ms-2">42</span></a>
+                <i class="icon me-2 cil-bell"></i> Updates<span class="badge badge-sm bg-info ms-2">42</span></a><a
+                class="dropdown-item" href="#">
+                <i class="icon me-2 cil-envelope-open"></i> Messages<span
+                  class="badge badge-sm bg-success ms-2">42</span></a><a class="dropdown-item" href="#">
+                <i class="icon me-2 cil-task"></i> Tasks<span class="badge badge-sm bg-danger ms-2">42</span></a><a
+                class="dropdown-item" href="#">
+                <i class="icon me-2 cil-comment-square"></i> Comments<span
+                  class="badge badge-sm bg-warning ms-2">42</span></a>
               <div class="dropdown-header bg-body-tertiary text-body-secondary fw-semibold my-2">
                 <div class="fw-semibold">Settings</div>
               </div><a class="dropdown-item" href="#">
                 <i class="icon me-2 cil-user"></i> Profile</a><a class="dropdown-item" href="#">
                 <i class="icon me-2 cil-settings"></i> Settings</a><a class="dropdown-item" href="#">
-                <i class="icon me-2 cil-credit-card"></i> Payments<span class="badge badge-sm bg-secondary ms-2">42</span></a><a class="dropdown-item"
-                href="#">
+                <i class="icon me-2 cil-credit-card"></i> Payments<span
+                  class="badge badge-sm bg-secondary ms-2">42</span></a><a class="dropdown-item" href="#">
                 <i class="icon me-2 cil-file"></i> Projects<span class="badge badge-sm bg-primary ms-2">42</span></a>
               <div class="dropdown-divider"></div><a class="dropdown-item" href="#">
                 <i class="icon me-2 cil-lock-locked"></i> Lock Account</a><a class="dropdown-item" href="#">
@@ -125,6 +134,8 @@
           </li>
         </ul>
       </div>
+    </header>
+    <main role="main" class="container">
       <div class="container-fluid px-6">
         <img src="/static/assets/images/Reisacher.png">
         <h2>Forderungsmanagement</h2>
@@ -132,6 +143,22 @@
       <div class="container-fluid px-6">
         {% block content %}{% endblock %}
       </div>
+    </main>
+  </div>
+
+  {% if summary %}
+  <footer class="footer footer-sticky">
+    <div class="container-fluid">
+      <ul class="header-nav d-none d-lg-flex">
+        <li class="nav-item">Datensätze: {{ summary.Anzahl }}</li>
+      </ul>
+      <ul class="header-nav ms-auto">
+        <li class="nav-item">Gesamt: <strong>{{ summary.offen|number_format }}</strong></li>
+      </ul>
+    </div>
+  </footer>
+  {% endif %}
+
 </body>
 
 </html>

+ 76 - 0
templates/forderungen/dashboard.html

@@ -0,0 +1,76 @@
+{% extends "base/base.html" %}
+{% block content %}
+
+<div>
+  <canvas id="myChart"></canvas>
+</div>
+
+<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
+
+<script>
+  var CHART_COLORS = {
+    red: 'rgb(255, 99, 132)',
+    orange: 'rgb(255, 159, 64)',
+    yellow: 'rgb(255, 205, 86)',
+    green: 'rgb(75, 192, 192)',
+    blue: 'rgb(54, 162, 235)',
+    purple: 'rgb(153, 102, 255)',
+    grey: 'rgb(201, 203, 207)'
+  };
+
+  var ctx = document.getElementById('myChart');
+
+  new Chart(ctx, {
+    type: 'bar',
+    data: {
+      labels: ['  < 2 Wochen',
+        ' 2 - 4 Wochen',
+        ' 4 - 6 Wochen',
+        ' 6 - 12 Wochen',
+        ' noch nicht fällig',
+        '> 12 Wochen'],
+      datasets: [{
+        label: 'Verkauf',
+        data: [12, 19, 3, 5, 2, 3],
+        borderWidth: 1,
+        backgroundColor: CHART_COLORS.red
+      },
+    {
+        label: 'Service',
+        data: [12, 19, 3, 5, 2, 3],
+        borderWidth: 1,
+        backgroundColor: CHART_COLORS.blue
+      },
+      {
+        label: 'TZ',
+        data: [12, 19, 3, 5, 2, 3],
+        borderWidth: 1,
+        backgroundColor: CHART_COLORS.green
+      },{
+        label: 'Sonstige',
+        data: [12, 19, 3, 5, 2, 3],
+        borderWidth: 1,
+        backgroundColor: CHART_COLORS.yellow
+      }]
+    },
+    options: {
+      plugins: {
+        title: {
+          display: true,
+          text: 'Chart.js Bar Chart - Stacked'
+        },
+      },
+      responsive: true,
+      scales: {
+        x: {
+          stacked: true,
+        },
+        y: {
+          stacked: true
+        }
+      }
+    }
+  });
+</script>
+
+{% endblock %}

+ 26 - 33
templates/forderungen/liste.html

@@ -16,7 +16,11 @@
     <select id="Hauptbetrieb" name="Hauptbetrieb" class="form-select">
         <option value="">-</option>
       {% for key, value in filter.Hauptbetrieb.items() %}
+        {% if key == defaults.Hauptbetrieb %}
+        <option value="{{ key }}" selected>{{ value }}</option>
+        {% else %}
         <option value="{{ key }}">{{ value }}</option>
+        {% endif %}
       {% endfor %}
     </select>
   </div>
@@ -25,7 +29,11 @@
     <select id="Standort" name="Standort" class="form-select">
         <option value="">-</option>
       {% for key, value in filter.Standort.items() %}
+        {% if key == defaults.Standort %}
+        <option value="{{ key }}" selected>{{ value }}</option>
+        {% else %}
         <option value="{{ key }}">{{ value }}</option>
+        {% endif %}
       {% endfor %}
     </select>
   </div>
@@ -33,6 +41,13 @@
     <label for="Bereich" class="form-label">Bereich</label>
     <select id="Bereich" name="Bereich" class="form-select">
         <option value="">-</option>
+      {% for key, value in filter.Bereich.items() %}
+        {% if key == defaults.Bereich %}
+        <option value="{{ key }}" selected>{{ value }}</option>
+        {% else %}
+        <option value="{{ key }}">{{ value }}</option>
+        {% endif %}
+      {% endfor %}        
     </select>
   </div>
   <div class="col-md-4">
@@ -91,7 +106,11 @@
     <select id="Fahrzeug" name="Fahrzeug" class="form-select">
         <option value="">-</option>
       {% for key, value in filter.Fahrzeug.items() %}
+        {% if key == defaults.Fahrzeug %}
+        <option value="{{ key }}" selected>{{ value }}</option>
+        {% else %}
         <option value="{{ key }}">{{ value }}</option>
+        {% endif %}
       {% endfor %}
     </select>
   </div>
@@ -100,7 +119,11 @@
     <select id="Staffel" name="Staffel" class="form-select">
         <option value="">-</option>
       {% for key, value in filter.Staffel.items() %}
+        {% if key == defaults.Staffel %}
+        <option value="{{ key }}" selected>{{ value }}</option>
+        {% else %}
         <option value="{{ key }}">{{ value }}</option>
+        {% endif %}
       {% endfor %}
     </select>
   </div>
@@ -109,7 +132,7 @@
     <select id="Mahnstufe" name="Mahnstufe" class="form-select">
         <option value="">-</option>
       {% for key, value in filter.Mahnstufe.items() %}
-        {% if value == defaults[key] %}
+        {% if key == defaults.Mahnstufe %}
         <option value="{{ key }}" selected>{{ value }}</option>
         {% else %}
         <option value="{{ key }}">{{ value }}</option>
@@ -146,39 +169,9 @@
     </tr>
   </thead>
   <tbody>
-    {% for row in forderungen_liste %}
-    <tr>
-      <td>
-        <a href="/app/forderungen/details/{{ row.Client_DB }}_{{ row.Document_No }}" class="btn btn-sm btn-primary"><i class="icon cil-pencil"></i></a>
-      </td>
-      <td>{{ row.Standort_Name }}</td>
-      <td>
-        <a href="/app/forderungen/liste?Kunde={{ row.Kunde|urlencode }}">{{ row.Kunde }}</a>
-        {% if row.Kunde.startswith("K") %}
-        <a href="mailto:dummy@xyz.de?subject={{ row.Document_No }}" class="btn btn-sm btn-outline-secondary">
-            <i class="cil-at"></i>
-        </a>
-        {% endif %}
-      </td>
-      <td>{{ row.Bereich }}</td>
-      <td>
-        <a href="/app/forderungen/liste?Verursacher={{ row.Verursacher|urlencode }}">{{ row.Verursacher }}</a>
-        {% if row.Verursacher == 'N.N.' %}
-        <a class="btn btn-sm btn-outline-secondary">
-            <i class="cil-user-follow"></i>
-        </a>
-        {% endif %}
-      </td>
-      <td>{{ row.Document_No }}</td>
-      <td>{{ row.Invoice_Date|date_format }}</td>
-      <td class="text-end">{{ row.Tage }}</td>
-      <td class="text-end">{{ row.Mahnstufe }}</td>
-      <td class="text-end">{{ row.offen|number_format }}</td>
-      <td class="text-end">{{ row.offen_Kunde_gesamt|number_format }}</td>
-      <td>{{ row.Kommentar_Fibu or '' }}</td>
-    </tr>
-    {% endfor %}
+    {%include 'forderungen/liste_tabelle.html' %}
   </tbody>
 </table>
 </div>
+
 {% endblock %}

+ 7 - 0
templates/forderungen/liste_tabelle.html

@@ -0,0 +1,7 @@
+    {% for row in forderungen_liste %}
+      {%include 'forderungen/liste_zeile.html' %}
+    {% endfor %}
+
+    <tr hx-trigger="revealed" hx-swap="outerHTML" hx-get="" hx-vals='{"page" : "{{ page+1 }}"}'>
+
+    </tr>

+ 31 - 0
templates/forderungen/liste_zeile.html

@@ -0,0 +1,31 @@
+
+    <tr>
+      <td>
+        <a href="/app/forderungen/details/{{ row.Client_DB }}_{{ row.Document_No }}" class="btn btn-sm btn-primary"><i class="icon cil-pencil"></i></a>
+      </td>
+      <td>{{ row.Standort_Name }}</td>
+      <td>
+        <a href="/app/forderungen/liste?Kunde={{ row.Kunde|urlencode }}">{{ row.Kunde }}</a>
+        {% if row.Kunde.startswith("K") %}
+        <a href="mailto:dummy@xyz.de?subject={{ row.Document_No }}" class="btn btn-sm btn-outline-secondary">
+            <i class="cil-at"></i>
+        </a>
+        {% endif %}
+      </td>
+      <td>{{ row.Bereich }}</td>
+      <td>
+        <a href="/app/forderungen/liste?Verursacher={{ row.Verursacher|urlencode }}">{{ row.Verursacher }}</a>
+        {% if row.Verursacher == 'N.N.' %}
+        <a class="btn btn-sm btn-outline-secondary">
+            <i class="cil-user-follow"></i>
+        </a>
+        {% endif %}
+      </td>
+      <td>{{ row.Document_No }}</td>
+      <td>{{ row.Invoice_Date|date_format }}</td>
+      <td class="text-end">{{ row.Tage }}</td>
+      <td class="text-end">{{ row.Mahnstufe }}</td>
+      <td class="text-end">{{ row.offen|number_format }}</td>
+      <td class="text-end">{{ row.offen_Kunde_gesamt|number_format }}</td>
+      <td>{{ row.Kommentar_Fibu or '' }}</td>
+    </tr>

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

@@ -40,6 +40,7 @@ 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.[Bereich] LIKE '%' + {{ Bereich.selectedOptionValue }}
 AND T1.[Document_No] LIKE '%' + {{ Rechnungsnummer.text }} + '%'
 AND [Kunde] LIKE '%' + {{ Kunde.selectedOptionValue }}
 AND [Verursacher] LIKE '%' + {{ Verursacher.selectedOptionValue }}