No description
  • Python 99.9%
  • Dockerfile 0.1%
Find a file
Murepex 42d8adf746 fix: Mängelbehebung — Edit-Bug, Import-Filter, Verträge, Performance, Spartöpfe
- Kategorie-Dropdown in Monatsdetail öffnet kein Detail-Popup mehr (click.stop)
- CSV-Import ergänzt 'date' in tx_dict und re-evaluiert Filter nach dem Import
- Wöchentliche Verträge: Buchungen werden anzahl- statt datumsbasiert zugeordnet
- find_category_for_transaction scoped Filtergruppen über user_id (Multi-User)
- Monatsübersicht/Dashboard: Connection-Reuse + Pot-Cache (Memoization) statt
  N+1-Zeitreise-Simulation pro Monat; O(M) statt O(M²) für Spartopf-Aggregation
- Spartopf-Erstelldialog nutzt Icon-Picker statt Textfeld
- Button "Filter rückwirkend anwenden" für Kategorien
2026-06-12 17:20:38 +02:00
data Initial commit 2026-05-30 10:45:34 +02:00
docs/images docs: Screenshots (Dashboard, Monatsansicht, Kategorien) hinzufügen 2026-05-30 16:37:27 +02:00
src fix: Mängelbehebung — Edit-Bug, Import-Filter, Verträge, Performance, Spartöpfe 2026-06-12 17:20:38 +02:00
tests ci: GitLab-Pipeline mit Tests und Docker-Image-Build 2026-05-30 13:14:10 +02:00
.dockerignore docs: Screenshots (Dashboard, Monatsansicht, Kategorien) hinzufügen 2026-05-30 16:37:27 +02:00
.gitignore Initial commit 2026-05-30 10:45:34 +02:00
.gitlab-ci.yml Revert "ci: Kaniko --insecure-Flags für HTTP-Registry" 2026-05-30 15:53:41 +02:00
CLAUDE.md fix: OIDC-Login/Logout zuverlässig, Account-Linking, case-insensitive Usernamen + Anzeigename 2026-05-31 19:52:39 +02:00
docker-compose.yml feat: OIDC-Verbesserungen — Public-URL, Login-Schalter, Provider-Name 2026-05-30 19:29:56 +02:00
Dockerfile fix: Container-User auf feste UID/GID 10001 pinnen + Volume-Perms dokumentieren 2026-05-30 19:02:52 +02:00
main.py Initial commit 2026-05-30 10:45:34 +02:00
pytest.ini ci: GitLab-Pipeline mit Tests und Docker-Image-Build 2026-05-30 13:14:10 +02:00
README.md chore: Abhängigkeiten aktualisieren — NiceGUI 3.12.1, pandas 3.0.3, joserfc 1.6.8, pydantic 2.13.4 2026-05-30 23:21:40 +02:00
requirements-dev.txt ci: GitLab-Pipeline mit Tests und Docker-Image-Build 2026-05-30 13:14:10 +02:00
requirements.txt chore: Abhängigkeiten aktualisieren — NiceGUI 3.12.1, pandas 3.0.3, joserfc 1.6.8, pydantic 2.13.4 2026-05-30 23:21:40 +02:00

💰 FinanceUs

Persönliche Finanzverwaltung als Web-App — Einnahmen, Ausgaben, Abos, Spartöpfe und wiederkehrende Buchungen über mehrere Bankkonten hinweg.

pipeline status Python NiceGUI License

Gebaut mit Python · NiceGUI (FastAPI + Vue.js) · SQLite · Pandas — UI auf Deutsch


Inhalt


Features

  • 📊 Dashboard — KPIs und interaktive Charts (ECharts) zu Einnahmen, Ausgaben und Salden
  • 🗓️ Monatsübersicht — Monatskarten mit Zusammenfassung, Drill-down in einzelne Transaktionen
  • 🏦 Mehrere Konten — getrennte Verwaltung beliebig vieler Bankkonten
  • 🏷️ Kategorien & Auto-Kategorisierung — regelbasierte Zuordnung über DNF-Filter (ODER von UNDs)
  • 🔁 Wiederkehrende Buchungen — Verträge/Abos mit Soll-/Ist-Abgleich pro Periode
  • 🐷 Spartöpfe — virtuelle Töpfe mit regelbasierten monatlichen Zuweisungen und Ledger-Historie
  • 📥 CSV-Import — Volksbank-Format inkl. konfigurierbarer Import-Profile und Import-Verlauf
  • 🔎 Globale Suche — über Transaktionen, Verträge, Spartöpfe und Kategorien, mit Bulk-Aktionen
  • 👥 Mehrbenutzer — Admin-Panel mit Rollen (admin / user)
  • 🔐 OIDC / SSO (optional) — Login via Authentik, Keycloak & Co. mit PKCE und JIT-Provisioning
  • 🌗 Dark / Light Mode — persistent pro Nutzer

Screenshots

Dashboard

Dashboard

Monatsansicht

Monatsansicht

Kategorien

Kategorien


Schnellstart

Variante A — Docker Compose (empfohlen)

# Session-Secret erzeugen und in docker-compose.yml eintragen
openssl rand -base64 48

# Datenverzeichnis anlegen und für den Container-User schreibbar machen (siehe unten)
mkdir -p financeus_data
sudo chown -R 10001:10001 financeus_data

docker compose up -d

App läuft anschließend auf http://localhost:8080. Datenbank, Secrets und Session-Storage werden über Volumes (./financeus_data, financeus_nicegui) persistiert.

Volume-Berechtigungen (wichtig)

Der Container läuft aus Sicherheitsgründen als Non-root-User (feste UID:GID = 10001:10001, im Dockerfile gepinnt). Ein per Bind-Mount eingebundenes Host-Verzeichnis (./financeus_data) gehört beim ersten Start aber root — der Container-User darf dann nicht hineinschreiben, und der Start bricht mit sqlite3.OperationalError: unable to open database file ab.

Deshalb muss das Datenverzeichnis dem Container-User gehören:

mkdir -p financeus_data
sudo chown -R 10001:10001 financeus_data

Die Datenbank (finanzus.db) wird danach beim ersten Start automatisch von SQLite angelegt — sie muss nicht manuell erstellt werden.

Alternative ohne chown: Statt des Bind-Mounts ein Named Volume verwenden (financeus_data:/app/data in docker-compose.yml). Docker übernimmt dann beim Anlegen automatisch die korrekte Ownership aus dem Image. Nachteil: kein direkter Host-Pfad-Zugriff auf die Daten (Backups via docker cp / docker run -v).

Variante B — Lokal mit Python 3.14

pip install -r requirements.txt
python main.py

App läuft auf http://localhost:8080.

Standard-Login

Benutzer Passwort
admin admin123

⚠️ Nach dem ersten Login das Admin-Passwort ändern (Admin-Panel unter /admin).


Konfiguration

Umgebungsvariablen

Variable Pflicht Zweck
FINANCEUS_STORAGE_SECRET empfohlen Secret für die Verschlüsselung des NiceGUI-Session-Storage. Fehlt es, wird ein zufälliges Secret in data/.storage_secret erzeugt — ohne stabiles Secret gehen Sessions bei jedem Neustart verloren. Generieren mit openssl rand -base64 48.
FINANCEUS_PUBLIC_URL bei Reverse-Proxy Öffentlich erreichbare Basis-URL der App (ohne trailing Slash), z.B. https://finance.deine-domain. Wird für die korrekte OIDC-Callback-URI benötigt — ohne diese Variable leitet die App die Callback-URI aus dem internen Request ab, was hinter einem Reverse-Proxy falsch sein kann.

OIDC / SSO (optional)

Vollständig über die Admin-Einstellungsseite (/settings, nur Admin) konfigurierbar — kein Code- oder Env-Eingriff nötig:

  • Issuer, Client-ID/Secret, Scopes, Admin-Gruppe
  • Anzeigename des Providers — erscheint auf der Login-Seite als „Anmelden mit <Name>"
  • Das Client-Secret wird Fernet-verschlüsselt in der DB gespeichert
  • Vollständiger Authorization-Code-Flow mit PKCE, JIT-User-Provisioning und Backchannel-Logout
  • Lokales Login deaktivieren — optionaler Schalter, der die Login-Seite auf den OIDC-Button reduziert; Notfall-Backdoor für Admins bleibt über /login?local=1 erreichbar

Die Redirect URI für den OIDC-Provider wird in der Einstellungsseite angezeigt und automatisch aus FINANCEUS_PUBLIC_URL (oder localhost:8080 als Fallback) zusammengesetzt. Format: <FINANCEUS_PUBLIC_URL>/auth/oidc/callback.

Es gibt keinen öffentlichen Registrierungsflow — Nutzer werden ausschließlich vom Admin oder via OIDC angelegt.


Architektur

Stack: NiceGUI 3.12.1 (FastAPI + Vue.js) · SQLite3 · Pandas · Authlib (OIDC) · Cryptography (Fernet)

Kernkonzepte

  • SPA-Navigation — alle Seiten laufen unter @ui.page('/') in pages/spa.py. Sidebar-Klicks rufen navigate(view, **params) auf: kein Browser-Reload, kein Flash; die URL wird via history.pushState aktualisiert. Alle übrigen Routen leiten auf / um.
  • Content-Renderer — jede Seite stellt eine render_X_content(user_id, navigate_fn=None)-Funktion bereit.
  • Auth-Guard — check_auth() (core/auth.py) schützt die SPA-Shell; user_id liegt in app.storage.user.
  • Refreshable UI — @ui.refreshable-Komponenten rendern bei Datenänderungen neu.
  • DB-Zugriff — direkte sqlite3-Verbindungen in database/, kein ORM.
  • Auto-Kategorisierung — DNF-Matching: Filtergruppen werden ge-OR-t, Bedingungen innerhalb einer Gruppe ge-AND-et (database/automations.py).

Datenbank-Schema (Auszug)

  • Kern: users, accounts, categories, category_types, transactions, contracts
  • Filter: filter_groups + filter_conditions (DNF-Regeln)
  • Spartöpfe: virtual_pots + pot_ledger + pot_rules
  • OIDC: app_settings, oidc_sessions

Projektstruktur

main.py              → Thin Launcher: fügt src/ zum sys.path, importiert src/app.py
src/
  app.py             → Einstiegspunkt (NiceGUI-Config, DB-Init, Page-Imports, ui.run())
  pages/             → SPA-Shell + Content-Renderer (eine Datei pro View)
    spa.py           → @ui.page('/') — einziger echter Einstiegspunkt
  ui/                → Layout, wiederverwendbare Komponenten, Dialoge
    components/
      dialogs/       → dedizierte Dialog-Komponenten
  core/              → Auth-Guard, CSV-Import, OIDC, Secret-Store, Konstanten
  database/          → alle SQL-Queries und Schema (SQLite via sqlite3)
data/                → finanzus.db + Beispiel-CSVs (Laufzeitdaten, außerhalb src/)
tests/               → pytest-Suite (Import-Smoke, DB, Auth, Constants)
Dockerfile           → Python 3.14-slim, Non-root, Healthcheck
docker-compose.yml   → Service-Definition mit persistenten Volumes
.gitlab-ci.yml       → CI/CD-Pipeline

Tests & CI/CD

Tests lokal ausführen

pip install -r requirements-dev.txt
pytest -q

Abgedeckt:

  • Import-Smoke-Test — importiert jedes Modul unter src/ und fängt Syntax-/Import-Fehler ab
  • DB-Schema — idempotentes init_db(), Kern-Tabellen vorhanden
  • Auth — PBKDF2-Passwort-Hashing (salted, timing-safe), User-CRUD, verify_user()
  • Constants — format_month_de(), get_chart_colors()

GitLab-Pipeline (.gitlab-ci.yml)

Stage Job Was passiert
lint compile Byte-Kompilierung aller Quellen (compileall)
test pytest Installiert Dependencies, führt die Test-Suite aus (inkl. Import-Smoke-Test der gesamten App)
build build-image Baut das Docker-Image mit Kaniko (daemonlos, kein dind). Push in die Container Registry nur auf Default-Branch / Tags (→ :latest bzw. Tag-Name); auf Feature-Branches nur Build-Validierung (--no-push)

Runner-Voraussetzung: docker-Executor. Kein privileged nötig — Kaniko baut im Userspace ohne Docker-Daemon. Die Registry-Credentials liest Kaniko automatisch aus den eingebauten CI-Variablen ($CI_REGISTRY*).


Theming (Dark/Light Mode)

Das Theme-System basiert auf Tailwind CSS v4 (NiceGUI 3.12.1) mit einem statisch injizierten <style>-Block in ui/layout.py::_DARK_CSS, der !important-Regeln gegen die Quasar-/Tailwind-Layer durchsetzt. Die Präferenz liegt in app.storage.user['dark_mode'] (Default True); der Toggle löst bewusst einen window.location.reload() aus, damit Chart-Farben korrekt neu rendern.

Details und „goldene Regeln“ für Karten-Hintergründe stehen in CLAUDE.md.


FinanceUs · privates Projekt