Files
Jonas MenzelandClaude Opus 5 6d182d0054
Images bauen / build (backend) (push) Successful in 4m13s
Images bauen / build (frontend) (push) Successful in 5m34s
CI / frontend (push) Successful in 6m48s
CI / backend (push) Successful in 2m41s
chore: Version 0.1.2
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
2026-09-10 10:05:23 +02:00

212 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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