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
205 lines
12 KiB
Markdown
205 lines
12 KiB
Markdown
# Changelog
|
||
|
||
Alle nennenswerten Änderungen an diesem Projekt werden hier dokumentiert.
|
||
|
||
Das Format orientiert sich an [Keep a Changelog](https://keepachangelog.com/de/1.1.0/),
|
||
die Versionierung folgt [Semantic Versioning](https://semver.org/lang/de/).
|
||
|
||
## [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ätzlich `period_start` und `period_end`. Bestandsinstallationen
|
||
bleiben beim Monatsersten.
|
||
- `python -m app.scripts.reset_password` setzt 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ändertes
|
||
`MONEYFY_ADMIN_PASSWORD` danach wirkungslos bleibt.
|
||
|
||
### Behoben
|
||
|
||
- Eine Anmeldung über HTTP bei aktivem `COOKIE_SECURE` fiel stillschweigend auf
|
||
die Anmeldemaske zurück: Der Browser verwirft das `Secure`-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: `n` neue Buchung, `r` neuer 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.yml` weicht auf den automatischen `GITEA_TOKEN` aus, wenn das Secret
|
||
`REGISTRY_TOKEN` fehlt, 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 galt
|
||
`COOKIE_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-settings` und 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/health` und `GET /api/version`.
|
||
- Testinfrastruktur mit pytest, httpx und transaktionsisolierten Fixtures.
|
||
- `.env.example`, `docker-compose.override.yml.example` und Makefile für die
|
||
lokale Entwicklung.
|
||
- Recurrence-Engine (`app/services/recurrence.py`) mit vollständiger
|
||
RFC-5545-Expansion: `expand` liefert nominale und tatsächliche Zahltage,
|
||
`expand_by_due_date` filtert 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 durch `until`, Kündigung und Ratenzahl.
|
||
- `installments_remaining` (Restraten und Restschuld), `contract_term` und
|
||
`notice_deadline` (Mindestlaufzeit, automatische Verlängerung, Kündigungsfrist)
|
||
sowie `annual_burden` und `monthly_reserve` für die Rücklagenbildung.
|
||
- `validate_rrule` und `next_dates` als 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}` mit `immutable`-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-icons` erzeugt 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/dashboard` bü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/test` verschickt eine Testnachricht über beide Kanäle,
|
||
`POST /api/notifications/run` fü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.yml` mit 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 `/api` weiter; `index.html` bleibt
|
||
ungecacht, die gehashten Assets ein Jahr.
|
||
- Gitea-Workflows `ci.yml` (Lint, Tests, Bundle-Bau) und `build.yml` (Images für
|
||
linux/amd64 in die Gitea-Registry, ohne Deploy-Schritt).
|
||
- `docs/runner.md` zur Einrichtung des act_runners samt Fehlersuche und
|
||
`docs/deployment.md` zu NPMplus, Aktualisierung und Sicherung.
|
||
- `python -m app.scripts.seed --if-empty` seedt nur bei leerem Kategoriebaum.
|
||
|
||
### Geändert
|
||
|
||
- `SECRET_KEY` muss 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.
|
||
|
||
[Unreleased]: https://git.menzel.center/menzeljonas/moneyfy/compare/v0.1.1...HEAD
|
||
[0.1.1]: https://git.menzel.center/menzeljonas/moneyfy/compare/v0.1.0...v0.1.1
|
||
[0.1.0]: https://git.menzel.center/menzeljonas/moneyfy/releases/tag/v0.1.0
|