Wer nach dem Gehaltseingang plant, stellt unter Einstellungen den Tag ein, ab dem ein neuer Monat zählt. Der Zeitraum läuft dann vom Gehaltstag bis zum Vortag des nächsten und trägt den Namen des Monats, in dem er beginnt: Mit dem 25. umfasst „September 2026“ den 25.09. bis zum 24.10. Ein Starttag jenseits der Monatslänge rutscht auf den Monatsletzten, sodass 31 verlässlich den letzten Tag des Monats meint. Dashboard, Cashflow-Kalender, Budgets, Zwölf-Monats-Vorschau, die Kategorienauswertung, der Monatsexport und die Benachrichtigung über überschrittene Budgets rechnen mit diesem Zeitraum. Budgets bleiben je Monat gepflegt; der Bezeichner ist weiterhin der Monatserste, nur der Schnitt verschiebt sich. Bestandsinstallationen bleiben beim Ersten. Die Einstellung liegt in einer einzeiligen Tabelle hinter GET/PUT /api/settings; die Monatsauswertungen liefern zusätzlich period_start und period_end. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fj7mB1PGA1aDHyfdSGHgzD
12 KiB
Changelog
Alle nennenswerten Änderungen an diesem Projekt werden hier dokumentiert.
Das Format orientiert sich an Keep a Changelog, die Versionierung folgt Semantic Versioning.
Unreleased
Hinzugefügt
- Einstellbarer Monatsbeginn: Wer am Gehaltstag rechnet, setzt unter
Einstellungen › Monatsbeginn den Tag, ab dem ein neuer Monat zählt. Der
Zeitraum läuft dann vom Gehaltstag bis zum Vortag des nächsten und trägt den
Namen des Monats, in dem er beginnt – der 25. September eröffnet also
„September“ und schließt am 24. Oktober. Ein Starttag jenseits der
Monatslänge rutscht auf den Monatsletzten, sodass 31 verlässlich „letzter Tag
des Monats“ bedeutet. Dashboard, Cashflow-Kalender, Budgets, Vorschau,
Kategorien, Monatsexport und die Budget-Benachrichtigung rechnen mit diesem
Zeitraum. Neue Endpunkte
GET/PUT /api/settings; die Monatsauswertungen liefern zusätzlichperiod_startundperiod_end. Bestandsinstallationen bleiben beim Monatsersten. python -m app.scripts.reset_passwordsetzt das Passwort eines Benutzers oder legt ihn an. Nötig, weil die Erstanlage beim Start nur bei leerer Benutzertabelle greift und ein nachträglich geändertesMONEYFY_ADMIN_PASSWORDdanach wirkungslos bleibt.
Behoben
- Eine Anmeldung über HTTP bei aktivem
COOKIE_SECUREfiel stillschweigend auf die Anmeldemaske zurück: Der Browser verwirft dasSecure-Cookie, die Anmeldung selbst meldet aber Erfolg. Die Oberfläche prüft die Sitzung jetzt unmittelbar nach der Anmeldung und erklärt den Fall samt beider Auswege; das Backend protokolliert dieselbe Kombination als Warnung.
0.1.1 – 2026-09-09
Politur und ein Fehler, den erst die CI zutage gefördert hat.
Hinzugefügt
- Demo-Seed mit einem realistischen deutschen Haushalt: drei Konten, zehn Firmen
samt Logos, zwölf wiederkehrende Posten (Miete, Strom, Netflix, Spotify,
jährliche Kfz-Versicherung, Handyvertrag mit Mindestlaufzeit, Kredit über 36
Raten, Gehalt), bestätigte Historie, Buchungen, Budgets und Sparziele. Alle
Termine liegen relativ zum heutigen Monat. Aufruf über
make seed-demo. - Tastaturkürzel:
nneue Buchung,rneuer Posten,/Suche,?Übersicht. Sie greifen nur, solange kein Eingabefeld den Fokus hat. - Fehlergrenze für unerwartete Renderfehler und eine eigene 404-Seite.
Geändert
- Farbtoken beider Modi gegen die WCAG-Kontraste geprüft und nachgezogen: der Hilfstext lag im hellen Modus bei 2,56:1, Weiß auf dem Primärknopf bei 3,30:1. Jetzt erreicht jeder Text mindestens 4,5:1 und jedes Bedienelement 3:1.
- Die Abo-Übersicht heißt „Laufende Kosten" – sie enthält jede wiederkehrende Ausgabe, auch Miete und Sparplan, und nicht nur Abonnements.
build.ymlweicht auf den automatischenGITEA_TOKENaus, wenn das SecretREGISTRY_TOKENfehlt, und schreibt bei einer gescheiterten Anmeldung die nötigen Schritte in die Job-Zusammenfassung.
Behoben
- Die Testsuite hing an einer nicht eingecheckten lokalen
.env: ohne sie galtCOOKIE_SECURE=true, httpx verwarf die Secure-Cookies über http, und 106 Tests scheiterten mit 401. Aufgefallen ist das erst in der CI.
0.1.0 – 2026-09-09
Erste lauffähige Fassung: Planung, Auswertung und Benachrichtigung stehen, die Anwendung lässt sich per Compose betreiben.
Aus diesem Tag sind keine Images entstanden – der Build scheiterte am noch nicht angelegten Registry-Secret. Verwende 0.1.1.
Hinzugefügt
-
Projektgerüst mit getrennten Bereichen für Backend, Frontend, Dokumentation und CI.
-
FastAPI-Backend mit Konfiguration über
pydantic-settingsund einheitlichem Fehlerformat ({"detail": ..., "code": ...}). -
Vollständiges Datenmodell als SQLAlchemy-2.0-Modelle: Konten, Kategorien, Firmen, Logo-Assets, Recurrences, Preishistorie, Occurrences, Buchungen, Budgets samt Vorlagen, Sparziele, Rücklagen, Benachrichtigungsregeln und -protokoll, Benutzer.
-
Alembic-Setup gegen die Async-Engine samt Erstmigration des kompletten Schemas; der Downgrade entfernt auch die nativen Enum-Typen, sodass Auf- und Abwärtsmigration beliebig oft wiederholbar sind.
-
Idempotenter Seed für den deutschen Standard-Kategoriebaum (35 Kategorien) und die Standard-Benachrichtigungsregeln.
-
Endpunkte
GET /api/healthundGET /api/version. -
Testinfrastruktur mit pytest, httpx und transaktionsisolierten Fixtures.
-
.env.example,docker-compose.override.yml.exampleund Makefile für die lokale Entwicklung. -
Recurrence-Engine (
app/services/recurrence.py) mit vollständiger RFC-5545-Expansion:expandliefert nominale und tatsächliche Zahltage,expand_by_due_datefiltert nach dem Zahltag für Kalender und Monatsberichte. -
Wochenend- und Feiertagsverschiebung über den NRW-Kalender; das nominale Datum bleibt stabiler Schlüssel zur Zuordnung materialisierter Fälligkeiten.
-
Auflösung der Preishistorie je Fälligkeitsdatum, Overlays aus
occurrence, Kappung der Serie durchuntil, Kündigung und Ratenzahl. -
installments_remaining(Restraten und Restschuld),contract_termundnotice_deadline(Mindestlaufzeit, automatische Verlängerung, Kündigungsfrist) sowieannual_burdenundmonthly_reservefür die Rücklagenbildung. -
validate_rruleundnext_datesals Grundlage für Eingabeprüfung und Vorschau. -
Authentifizierung mit Argon2id, JWT in httpOnly-Cookies (Access 30 min, Refresh 14 Tage) und echter Token-Rotation über die Tabelle
refresh_token; ein erneut eingelöstes Refresh-Token beendet alle Sitzungen. -
AuthProvider-Protokoll mit lokalem Provider als Vorbereitung für OIDC. -
Anlage des Administrators beim Erststart mit erzwungenem Passwortwechsel; bis dahin sind alle Fachrouten gesperrt.
-
CRUD für Konten, Kategorien, Firmen, wiederkehrende Posten, Preisversionen, Buchungen, Budgets, Budgetvorlagen und Sparziele.
-
Fälligkeiten-API mit Overlay-Logik: abrufen, bestätigen (auch mit abweichendem Betrag oder Datum), auslassen und zurücksetzen.
-
Kontosalden zum Stichtag aus Eröffnungssaldo, Buchungen und bestätigten Fälligkeiten, inklusive abweichender Konten je Fälligkeit.
-
Monatsübersicht mit Plan-Ist-Vergleich, Aufteilung in fixe und variable Kosten, Rücklagen und Vergleich zum Vormonat.
-
Vollständig annotiertes OpenAPI-Dokument unter
/api/docs. -
Logo-Service mit fünfstufiger Provider-Kette: lokal gevendorte simple-icons (3.459 Marken, rund 2 MB), logo.dev und Brandfetch bei gesetztem Schlüssel, Favicon-Fallback und generierter Buchstaben-Avatar als Garantie.
-
Logo-Cache im Dateisystem, benannt nach dem SHA-256 des Inhalts; Auslieferung ausschließlich über
GET /api/logos/{id}mitimmutable-Cache-Header und ETag-Unterstützung. -
Ermittlung der Markenfarbe: bei SVG aus den
fill-Attributen, bei Rastergrafiken per k-Means über 64×64 Pixel; dazu eine im HSL-Raum aufgehellte Variante, die gegen den dunklen Hintergrund mindestens 4,5:1 Kontrast erreicht. -
Auswahldialog-Endpunkte: Kandidatensuche mit Vorauswahl, Übernahme eines Kandidaten, Upload (SVG/PNG/JPEG bis 1 MB) und Zurücksetzen.
-
Die Logosuche läuft nach dem Anlegen einer Firma im Hintergrund; der Aufruf antwortet sofort.
-
make vendor-iconserzeugt den simple-icons-Index neu. -
Frontend auf React 18, TypeScript, Vite und TailwindCSS mit dunklem Standard- Theme, umschaltbar und im Browser gespeichert.
-
Anmeldung, erzwungener Passwortwechsel und Layout mit Seitenleiste.
-
TanStack-Query-Client mit zentralen Fehler-Toasts und stiller Token-Erneuerung bei abgelaufenem Access-Token.
-
Seiten für wiederkehrende Posten (inklusive Detail-Drawer mit Preishistorie, Ratenfortschritt und Vertragsfristen), Buchungen, Firmen und Einstellungen.
-
Geführter RRULE-Editor mit sechs Vorlagen, Expertenmodus, deutscher Klartextfassung und den nächsten fünf Terminen aus dem
preview-Endpunkt. -
Firmen als Kachelgrid mit Logo, Markenfarbe, Jahreskosten und Vertragszahl; Logo-Auswahldialog mit Vorauswahl, erneuter Suche über eine Domain und Upload.
-
Beträge durchgängig über
Intl.NumberFormat('de-DE'); Eingaben akzeptieren deutsche wie englische Schreibweise. -
Auswertungen: Monatsübersicht, 12-Monats-Forecast, Kategorienaufteilung mit Drilldown, Abo-Übersicht mit Jahreskosten, Jahresvergleich, Cashflow-Kalender, Budget-Ampel und Fortschritt der Sparziele.
-
Alle Berichte bauen auf einer gemeinsamen Bewegungsschicht auf, sodass Monatsübersicht, Forecast und Kalender nachweislich dieselben Zahlen zeigen.
-
Budgetübertrag über Monatsgrenzen und Budgets auf Oberkategorien, die deren Unterkategorien einschließen.
-
GET /api/reports/dashboardbündelt alles, was die Startseite braucht. -
Export als CSV (Semikolon, Komma als Dezimaltrenner, UTF-8 mit BOM) und XLSX mit typisierten Beträgen und deutschem Zahlenformat – für Buchungen, Posten und die Monatsauswertung.
-
Dashboard, Cashflow-Kalender, Budget-, Sparziel- und Auswertungsseite im Frontend; Diagramme mit Recharts.
-
Die Farbpalette der Diagramme wurde gegen die hellen und dunklen Flächen der Anwendung auf Kontrast und Farbfehlsichtigkeit geprüft. Grün/Rot schied als Serienpaar aus (bei Deuteranopie nicht unterscheidbar) und bleibt den Vorzeichen im Text vorbehalten.
-
Benachrichtigungen über SMTP (HTML-Mail mit eingebetteten Firmenlogos) und Apprise, gesteuert über Regeln je Anlass und Kanal.
-
Vier Anlässe: bald fällige Posten, Kündigungsfristen (30, 14 und 7 Tage vor dem Termin), überschrittene Budgets und anstehende Vertragsverlängerungen.
-
APScheduler-Job täglich um 07:00
Europe/Berlin, der zugleich abgelaufene Sitzungen aufräumt; verpasste Läufe werden einmal nachgeholt. -
Duplikatsschutz über
(rule_id, ref_type, ref_id, Zieltag). Maßgeblich ist der Zieltag des Ereignisses, nicht der Versandtag – ein zweiter Lauf am selben oder am nächsten Tag erzeugt keine zweite Nachricht. Ein fehlgeschlagener Versand wird beim nächsten Lauf erneut versucht. -
POST /api/notifications/testverschickt eine Testnachricht über beide Kanäle,POST /api/notifications/runführt einen Lauf sofort aus. -
Benachrichtigungen sind in den Einstellungen pflegbar, samt Einrichtungsstand der Kanäle und Versandprotokoll.
-
Mehrstufige Dockerfiles für Backend und Frontend, beide mit einer Stufe für die Entwicklung und einer für den Betrieb; beide Images laufen unprivilegiert.
-
Entrypoint des Backends wartet auf die Datenbank, spielt die Migrationen ein und legt die Stammdaten nur beim leeren Kategoriebaum an.
-
docker-compose.ymlmit Netz-Trennung (die Datenbank ist ausschließlich intern erreichbar), Healthchecks und benannten Volumes für Datenbank und Logo-Cache. -
nginx liefert das Bundle aus und reicht
/apiweiter;index.htmlbleibt ungecacht, die gehashten Assets ein Jahr. -
Gitea-Workflows
ci.yml(Lint, Tests, Bundle-Bau) undbuild.yml(Images für linux/amd64 in die Gitea-Registry, ohne Deploy-Schritt). -
docs/runner.mdzur Einrichtung des act_runners samt Fehlersuche unddocs/deployment.mdzu NPMplus, Aktualisierung und Sicherung. -
python -m app.scripts.seed --if-emptyseedt nur bei leerem Kategoriebaum.
Geändert
SECRET_KEYmuss mindestens 32 Zeichen lang sein (Vorgabe von HS256); in Produktion wird der Platzhalterwert beim Start abgelehnt.- Der Bildtyp eines Uploads wird ausschließlich am Inhalt bestimmt; die vom Aufrufer gemeldete Kopfzeile wird nicht mehr als Nachweis akzeptiert.