feat: Hilfe-Seite mit Gebrauchsanleitung

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 15:02:19 +02:00
parent d5103a0fb3
commit e40442b73b
4 changed files with 91 additions and 2 deletions

View File

@@ -159,6 +159,11 @@ def _scenario_rows(session: Session, scenarios: list, loans: list) -> list[dict]
return rows
@router.get("/hilfe", dependencies=[Depends(gui_session)])
def hilfe_page(request: Request):
return templates.TemplateResponse(request, "hilfe.html", {})
@router.get("/planung", dependencies=[Depends(gui_session)])
def planung_page(request: Request, session: Session = Depends(get_session)):
categories = session.execute(select(Category)).scalars().all()

View File

@@ -13,6 +13,7 @@
<a href="/import">Import</a>
<a href="/buchungen">Buchungen</a>
<a href="/planung">Planung</a>
<a href="/hilfe">Hilfe</a>
<a href="http://{{ request.url.hostname or '127.0.0.1' }}:8097" target="_blank" rel="noopener">Grafana</a>
<form method="post" action="/logout">
<button type="submit">Logout</button>

View File

@@ -0,0 +1,82 @@
{% extends "base.html" %}
{% block title %}Hilfe Finanzberatung{% endblock %}
{% block content %}
<h1>Gebrauchsanleitung</h1>
<h2>Was dieses Tool ist</h2>
<p>
Liquiditätsplanung auf Basis echter Kontoauszüge: Ist-Stand aus importierten
Auszügen, Zukunft aus gepflegten Planposten und durchgerechneten Szenarien.
</p>
<h2>Konzepte</h2>
<ul>
<li>
<strong>Konten:</strong> entstehen automatisch beim ersten Import (per IBAN).
Über die Übersicht umbenennbar (z. B. "DKB Giro").
</li>
<li>
<strong>Kontoauszug-Import:</strong> PDF in die Drop-Zone auf der
Import-Seite ziehen (oder in die Inbox
<code>~/.local/share/finance_pod/data/inbox/</code> legen und "Inbox
scannen"). Jeder Import wird geprüft (Anfangssaldo + Buchungen = Endsaldo)
und landet als Entwurf: erst nach Kontrolle der Vorschau "Übernehmen"
klicken. Duplikate werden erkannt und nicht doppelt übernommen.
</li>
<li>
<strong>Kategorien & Regeln:</strong> Buchungen lassen sich kategorisieren;
eine Regel ("Muster im Text → Kategorie") kategorisiert künftige Importe
automatisch. Regel direkt aus einer Buchung erzeugen: Button in der
Buchungsliste.
</li>
<li>
<strong>Wiederkehrende Posten:</strong> Miete, Gehalt, Abos mit Rhythmus
(monatlich/quartalsweise/jährlich) und Fälligkeitstag. Der
Vorschlags-Knopf erkennt Kandidaten aus mind. 3 Monaten gleichartiger
Buchungen.
</li>
<li>
<strong>Einmalposten:</strong> einzelne künftige Zahlungen (z. B.
Steuernachzahlung).
</li>
<li>
<strong>Kredite:</strong> Annuität oder endfällig; Tilgungsplan
aufklappbar. Ein Kredit wirkt erst, wenn er einem Szenario zugeordnet ist.
</li>
<li>
<strong>Szenarien:</strong> eine "Was-wäre-wenn"-Rechnung. Basis = alle
wiederkehrenden Posten + Einmalposten. Varianten entstehen durch Zuordnen
von Krediten und Modifikatoren (Kategorie oder Posten prozentual/absolut
kürzen oder ganz streichen). "Durchrechnen" liefert: tiefster Kontostand
mit Datum, erstes Datum unter 0 und unter der Warnschwelle.
</li>
<li>
<strong>Grafana:</strong> Kontostand-Verläufe, Monatsausgaben nach
Kategorie und der Szenario-Vergleich als Kurven (einmal mit demselben
Passwort wie hier anmelden, dann erscheint das Dashboard auch in der
Übersicht).
</li>
</ul>
<h2>Empfohlener Arbeitsablauf</h2>
<ul>
<li>Monatlich: neue Auszüge importieren, Vorschau prüfen, übernehmen.</li>
<li>
Unkategorisierte Buchungen durchsehen; für Wiederkehrendes Regeln
anlegen.
</li>
<li>Planung pflegen: Vorschläge prüfen, Einmalposten eintragen.</li>
<li>
Fragestellung ("Können wir uns X leisten?") als Szenario-Varianten
abbilden und durchrechnen; Entscheidung anhand Tiefpunkt und
Unterschreitungsdaten treffen, Kurven in Grafana vergleichen.
</li>
</ul>
<h2>Grundregeln</h2>
<ul>
<li>Bestätigte Buchungen sind die Wahrheit nie ändern, nur kategorisieren.</li>
<li>Zukunft ausschließlich über Szenarien planen.</li>
<li>Empfehlungen immer mit durchgerechneten Zahlen begründen.</li>
</ul>
{% endblock %}

View File

@@ -1,5 +1,5 @@
def test_pages_require_login(client):
for path in ("/", "/buchungen", "/import", "/planung"):
for path in ("/", "/buchungen", "/import", "/planung", "/hilfe"):
r = client.get(path, follow_redirects=False)
assert r.status_code in (302, 303), path
assert r.headers["location"] == "/login"
@@ -7,10 +7,11 @@ def test_pages_require_login(client):
def test_pages_render_after_login(client):
client.post("/login", data={"username": "admin", "password": "geheim"})
for path in ("/", "/buchungen", "/import", "/planung"):
for path in ("/", "/buchungen", "/import", "/planung", "/hilfe"):
r = client.get(path)
assert r.status_code == 200
assert "Finanzberatung" in r.text
assert "Gebrauchsanleitung" in client.get("/hilfe").text
def test_import_page_has_dropzone_and_list(client):