Saldo
Saldo ist eine moderne Flask-PWA für Mehrbenutzer-Haushaltsplanung. Die App fokussiert sich auf Monatsplanung, Überweisungsverteilungen, variable Einkommen, Restbetragsvorschläge und externe Kostenbeteiligung, statt klassische Buchhaltung nachzubauen.
Aktueller Projektstand: 0.1.0
Features
- automatische Monatsanlage beim ersten Öffnen eines neuen Kalendermonats
- Kopieren des Vormonats inklusive Einkommen, Planwerten, Verteilungen und Beteiligungslogik
- variable Einkommenszeilen pro Monat
- verpflichtende Anzeigenamen für Nutzer, sichtbar in Avataren, Splits und Budgetdetails
- Vorschlags-Engine für
Sparen,Urlaub,FreizeitundPersönliche Auszahlung - externe Kostenbeteiligte ohne App-Login
- PWA mit Manifest und Service Worker
- Web Push mit VAPID-Keys
- Rollenmodell mit
adminundeditor - keine offene Registrierung
- Healthcheck unter
/health
Stack
- Python 3
- Flask
- SQLAlchemy
- Flask-Login
- Flask-Migrate / Alembic
- Jinja Templates
- leichtes Vanilla JS
- Chart.js
- SQLite als Default
- Gunicorn
Projektstruktur
app/
admin/ Admin-Views für Benutzer und Beteiligte
auth/ Login/Logout
main/ Übersicht, Auswertungen, Healthcheck
months/ Monatsliste, Locking, manuelles Anlegen
planning/ Monatliche Bearbeitung von Einkommen, Kosten, Verteilungen
services/ Domänenlogik für Monate, Vorschläge, Vergleiche, Shares, Push
static/ CSS, JS, Icons, Manifest, Service Worker
templates/ Jinja-Templates
models.py SQLAlchemy-Modelle
seed.py Seed-Daten
migrations/ Alembic-Migrationen
tests/ Pytest-Suite
Lokal starten
python -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
export FLASK_APP=wsgi:app
python -m flask db upgrade
python -m flask seed
python -m flask run --debug
Die App läuft lokal standardmäßig mit SQLite im instance- bzw. konfigurierten Datenverzeichnis.
Beim ersten Aufruf öffnet sich automatisch die Setup-Seite zur Anlage des ersten Admins.
Wichtig für lokale Tests:
- die lokale Datenbank liegt standardmäßig in
instance/saldo.db - Uploads und andere Laufzeitdaten liegen ebenfalls im Datenverzeichnis
- diese Dateien werden über
.gitignorebewusst nicht versioniert - dadurch kannst du lokal testen, später Änderungen pullen und danach einfach wieder
python -m flask db upgradeausführen
Wichtige Umgebungsvariablen
Siehe .env.example.
SECRET_KEYDATABASE_URLSALDO_DATA_DIRSALDO_ADMIN_USERNAMESALDO_ADMIN_PASSWORDSALDO_ADMIN_EMAILVAPID_PUBLIC_KEYVAPID_PRIVATE_KEYVAPID_SUBJECTSALDO_ALLOCATION_TARGET_RULESSALDO_STRONG_INCOME_CHANGE_THRESHOLD
CLI-Kommandos
python -m flask create-admin --username admin2 --display-name "Admin 2" --email admin2@example.com --password secret
python -m flask bootstrap-admin
python -m flask seed
python -m flask run-reminders
seed legt nur die Grundstruktur an. bootstrap-admin bleibt als optionaler CLI-Weg verfügbar.
run-reminders kann auf Cloudron als Scheduled Task oder per Cron ausgeführt werden.
Cloudron-Hinweise
Vorbereitete Paketdateien:
CloudronManifest.jsonDockerfilestart.shDESCRIPTION.mdCHANGELOGPOSTINSTALL.md
Cloudron-Release-Stand:
- App-Version:
0.1.0 - Healthcheck:
/health - interner Port:
8000 - persistente Daten:
/app/data - Build-Hygiene:
.dockerignoreschließt lokale DBs,instance/, Test-Caches und Editor-Dateien aus
Empfohlene Env-Konfiguration für eine Cloudron-Subdomain wie https://saldo.example.com:
SECRET_KEY=<random>
DATABASE_URL=sqlite:////app/data/saldo.db
SALDO_DATA_DIR=/app/data
SALDO_ADMIN_USERNAME=admin
SALDO_ADMIN_PASSWORD=<random>
SALDO_ADMIN_EMAIL=admin@example.invalid
VAPID_PUBLIC_KEY=<public>
VAPID_PRIVATE_KEY=<private>
VAPID_SUBJECT=mailto:admin@example.invalid
Startkommando:
gunicorn -c gunicorn.conf.py wsgi:app
Im Container übernimmt start.sh vor dem Start automatisch:
python -m flask db upgrade
python -m flask seed
Wenn du mit der Cloudron CLI paketierst oder hochlädst:
- die lokale SQLite-Datei aus
instance/wird nicht mitgeschickt - lokale
.env-Dateien werden nicht mitgeschickt - Test-/Cache-Dateien und Editor-Metadaten bleiben ebenfalls draußen
- produktive Daten sollen weiterhin ausschließlich in
/app/dataliegen
Cloudron-Kompatibilität in diesem Projekt:
- persistente Daten im konfigurierbaren Datenverzeichnis
- Logging über stdout/stderr
- Healthcheck-Route
/health - keine Benutzer-Selbstregistrierung
- Erststart-Setup im Browser für den ersten Admin
- optionaler Admin-Bootstrap per CLI
- Reminder-Check als separater wiederverwendbarer CLI-Task
- neutrale Basisdaten werden auf Fresh-Install automatisch angelegt
Hinweis für spätere Update-Verteilung:
- Laut Cloudron-Publishing-Workflow wird nach dem ersten Build die Datei
CloudronVersions.jsonmitcloudron versions adderzeugt und anschließend öffentlich bereitgestellt. - Damit erscheinen neue Versionen später automatisch als Community-App-Updates.
Basisdaten
Das Seed-Script legt an:
- Standardkonten, Kategorien und Einträge
- Gemeinschaftskonten-Grundstruktur
Nicht angelegt werden:
- Beispielzugänge
- Beispielpersonen
- Demo-Monatswerte
Tests
python -m pytest -q
Aktueller Stand:
- 31 Tests grün
Hinweise zur Kernlogik
MonthService.ensure_month():
- legt den aktuellen Kalendermonat automatisch an
- übernimmt beim Anlegen den letzten vorhandenen Monat
- fällt beim ersten Start auf einen Seed-Standardmonat zurück
AllocationSuggestionService.recompute():
- berechnet den verteilbaren Restbetrag
- respektiert gesperrte Zielkonten
- verteilt den übrigen Betrag gewichtet
- gleicht Rundungsreste in der letzten Vorschlagszeile aus
PWA und Push
- Manifest: app/static/manifest.json
- Service Worker: app/static/service-worker.js
- Push-Subscription-Route: app/planning/routes.py
- Reminder-Logik: app/services/notification_service.py
Git und Releases
Repo-Vorbereitung in diesem Stand:
.gitignoreverhindert, dass lokale SQLite-Datenbank, Uploads und Laufzeitdaten eingecheckt werden.dockerignorehält lokale Test- und Laufzeitdateien auch aus Cloudron-Builds herausVERSIONundCHANGELOGdokumentieren den aktuellen Release-StandLICENSEerlaubt private Nutzung
Empfohlene Release-Schritte:
git init -b main
git remote add origin https://git.hnz.io/hnzio/saldo.git
git add .
git commit -m "chore: prepare saldo 0.1.0 for git and cloudron"
git push -u origin main