Der Versionshinweis im README stand noch auf 0.1.0 und wird mitgezogen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fj7mB1PGA1aDHyfdSGHgzD
212 lines
12 KiB
Markdown
212 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]
|
||
|
||
Noch keine Änderungen seit 0.1.2.
|
||
|
||
## [0.1.2] – 2026-09-10
|
||
|
||
Der Monat beginnt nicht mehr zwangsläufig am Ersten.
|
||
|
||
### 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.2...HEAD
|
||
[0.1.2]: https://git.menzel.center/menzeljonas/moneyfy/compare/v0.1.1...v0.1.2
|
||
[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
|