Files
2026-04-21 21:17:36 +02:00

232 lines
6.8 KiB
Markdown

# 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`, `Freizeit` und `Persönliche Auszahlung`
- externe Kostenbeteiligte ohne App-Login
- PWA mit Manifest und Service Worker
- Web Push mit VAPID-Keys
- Rollenmodell mit `admin` und `editor`
- 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
```text
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
```bash
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 `.gitignore` bewusst nicht versioniert
- dadurch kannst du lokal testen, später Änderungen pullen und danach einfach wieder `python -m flask db upgrade` ausführen
## Wichtige Umgebungsvariablen
Siehe [.env.example](/home/hnzio/Projekte/saldo/.env.example).
- `SECRET_KEY`
- `DATABASE_URL`
- `SALDO_DATA_DIR`
- `SALDO_ADMIN_USERNAME`
- `SALDO_ADMIN_PASSWORD`
- `SALDO_ADMIN_EMAIL`
- `VAPID_PUBLIC_KEY`
- `VAPID_PRIVATE_KEY`
- `VAPID_SUBJECT`
- `SALDO_ALLOCATION_TARGET_RULES`
- `SALDO_STRONG_INCOME_CHANGE_THRESHOLD`
## CLI-Kommandos
```bash
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.json`
- `Dockerfile`
- `start.sh`
- `DESCRIPTION.md`
- `CHANGELOG`
- `POSTINSTALL.md`
Cloudron-Release-Stand:
- App-Version: `0.1.0`
- Healthcheck: `/health`
- interner Port: `8000`
- persistente Daten: `/app/data`
- Build-Hygiene: `.dockerignore` schließt lokale DBs, `instance/`, Test-Caches und Editor-Dateien aus
Empfohlene Env-Konfiguration für eine Cloudron-Subdomain wie `https://saldo.example.com`:
```bash
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:
```bash
gunicorn -c gunicorn.conf.py wsgi:app
```
Im Container übernimmt `start.sh` vor dem Start automatisch:
```bash
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/data` liegen
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.json` mit `cloudron versions add` erzeugt 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
```bash
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](/home/hnzio/Projekte/saldo/app/static/manifest.json)
- Service Worker: [app/static/service-worker.js](/home/hnzio/Projekte/saldo/app/static/service-worker.js)
- Push-Subscription-Route: [app/planning/routes.py](/home/hnzio/Projekte/saldo/app/planning/routes.py:113)
- Reminder-Logik: [app/services/notification_service.py](/home/hnzio/Projekte/saldo/app/services/notification_service.py)
## Git und Releases
Repo-Vorbereitung in diesem Stand:
- `.gitignore` verhindert, dass lokale SQLite-Datenbank, Uploads und Laufzeitdaten eingecheckt werden
- `.dockerignore` hält lokale Test- und Laufzeitdateien auch aus Cloudron-Builds heraus
- `VERSION` und `CHANGELOG` dokumentieren den aktuellen Release-Stand
- `LICENSE` erlaubt private Nutzung
Empfohlene Release-Schritte:
```bash
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
```