Wiederkehrende Zahlungen erkennen + Monatsend-Prognose (#2) #12

Merged
glow merged 2 commits from feature/2-recurring-payments-forecast into main 2026-07-08 20:36:17 +00:00
glow commented 2026-07-08 19:37:55 +00:00 (Migrated from gitlab.fluffyplace.de)

Closes #2.

Was

Erkennung wiederkehrender Zahlungen (detectRecurring()): gruppiert Ausgaben nach normalisiertem merchant_name (Ziffern und deutsche Monatsnamen werden entfernt, damit z.B. "Miete Juli" und "Miete August" als eine Serie erkannt werden), klassifiziert eine Serie als monatlich/jährlich, sobald mindestens ein Abstand zwischen zwei Buchungen ins jeweilige Fenster fällt (25–35 bzw. 350–380 Tage).

Preisänderungs-Hinweis: vergleicht die letzten beiden Beträge einer Serie, ab 50 Cent Differenz wird's als "Betrag geändert" markiert.

Monatsend-Prognose (GET /api/stats/forecast): aktueller Saldo (Startsaldo + alle Buchungen bis heute) plus die erwarteten, aber noch nicht bis Monatsende gebuchten wiederkehrenden Abbuchungen. Bereits gebuchte Zahlungen der laufenden Serie werden dabei nicht doppelt gezählt.

Neue Seite "Abos" listet alle erkannten Serien mit Intervall, letztem Betrag, Preisänderungs-Badge und erwartetem nächsten Termin. Dashboard bekommt bei Monatsansicht eine 4. KPI-Kachel "Prognose Monatsende".

Bewusst außerhalb des Scopes

Keine In-App-Kündigungsfunktion (siehe Begründung in #2 selbst) — nur ein reiner Hinweis, wenn sich ein Betrag geändert hat.

Update: Follow-up aus der ursprünglichen MR-Beschreibung bereits umgesetzt

Branch wurde auf den aktuellen main (inkl. gemergter #4/MR!11) rebased. Die Gruppierung läuft jetzt über merchant_name statt über den rohen buchungstext (mit Fallback auf buchungstext für Altdatensätze ohne merchant_name). Das ist mehr als nur kosmetisch: die SEPA-Ref-Suffixe in echten Buchungstexten sind oft alphanumerisch, nicht rein numerisch — die alte, nur ziffernstrippende Normalisierung hätte z.B. drei Netflix-Abbuchungen mit Ref-Codes wie "AAABBBCCC"/"XXXYYYZZZ"/"QQQRRRSSS" nicht als eine Serie erkannt. Mit merchant_name funktioniert das jetzt zuverlässig (manuell verifiziert).

Tests

9 Unit-Tests für die reinen Kernfunktionen (normalizeForGrouping, classifyInterval, daysBetween/addDays). Zusätzlich end-to-end gegen einen laufenden Server verifiziert: Miete über 4 Monate mit wechselnden Monatsnamen korrekt als eine Serie erkannt, Netflix-Preiserhöhung (14,99€→17,99€) korrekt geflaggt, unregelmäßige REWE-Einkäufe korrekt nicht als wiederkehrend erkannt, Prognose zählt bereits gebuchte Julimiete nicht doppelt, und die merchant_name-basierte Gruppierung über wechselnde Ref-Suffixe hinweg (siehe oben).

Closes #2. ## Was **Erkennung wiederkehrender Zahlungen** (`detectRecurring()`): gruppiert Ausgaben nach normalisiertem `merchant_name` (Ziffern und deutsche Monatsnamen werden entfernt, damit z.B. "Miete Juli" und "Miete August" als eine Serie erkannt werden), klassifiziert eine Serie als monatlich/jährlich, sobald mindestens ein Abstand zwischen zwei Buchungen ins jeweilige Fenster fällt (25–35 bzw. 350–380 Tage). **Preisänderungs-Hinweis**: vergleicht die letzten beiden Beträge einer Serie, ab 50 Cent Differenz wird's als "Betrag geändert" markiert. **Monatsend-Prognose** (`GET /api/stats/forecast`): aktueller Saldo (Startsaldo + alle Buchungen bis heute) plus die erwarteten, aber noch nicht bis Monatsende gebuchten wiederkehrenden Abbuchungen. Bereits gebuchte Zahlungen der laufenden Serie werden dabei nicht doppelt gezählt. **Neue Seite "Abos"** listet alle erkannten Serien mit Intervall, letztem Betrag, Preisänderungs-Badge und erwartetem nächsten Termin. Dashboard bekommt bei Monatsansicht eine 4. KPI-Kachel "Prognose Monatsende". ## Bewusst außerhalb des Scopes Keine In-App-Kündigungsfunktion (siehe Begründung in #2 selbst) — nur ein reiner Hinweis, wenn sich ein Betrag geändert hat. ## Update: Follow-up aus der ursprünglichen MR-Beschreibung bereits umgesetzt Branch wurde auf den aktuellen `main` (inkl. gemergter #4/MR!11) rebased. Die Gruppierung läuft jetzt über `merchant_name` statt über den rohen `buchungstext` (mit Fallback auf `buchungstext` für Altdatensätze ohne `merchant_name`). Das ist mehr als nur kosmetisch: die SEPA-Ref-Suffixe in echten Buchungstexten sind oft alphanumerisch, nicht rein numerisch — die alte, nur ziffernstrippende Normalisierung hätte z.B. drei Netflix-Abbuchungen mit Ref-Codes wie "AAABBBCCC"/"XXXYYYZZZ"/"QQQRRRSSS" **nicht** als eine Serie erkannt. Mit `merchant_name` funktioniert das jetzt zuverlässig (manuell verifiziert). ## Tests 9 Unit-Tests für die reinen Kernfunktionen (`normalizeForGrouping`, `classifyInterval`, `daysBetween`/`addDays`). Zusätzlich end-to-end gegen einen laufenden Server verifiziert: Miete über 4 Monate mit wechselnden Monatsnamen korrekt als eine Serie erkannt, Netflix-Preiserhöhung (14,99€→17,99€) korrekt geflaggt, unregelmäßige REWE-Einkäufe korrekt **nicht** als wiederkehrend erkannt, Prognose zählt bereits gebuchte Julimiete nicht doppelt, und die merchant_name-basierte Gruppierung über wechselnde Ref-Suffixe hinweg (siehe oben).
glow commented 2026-07-08 20:20:54 +00:00 (Migrated from gitlab.fluffyplace.de)

added 4 commits

  • 3d4ce1b6...602c9e88 - 2 commits from branch main
  • d214e094 - ─────┬─────────────────────────────────────────────────────...
  • dbc3fe8d - ─────┬─────────────────────────────────────────────────────...

Compare with previous version

added 4 commits <ul><li>3d4ce1b6...602c9e88 - 2 commits from branch <code>main</code></li><li>d214e094 - ─────┬─────────────────────────────────────────────────────...</li><li>dbc3fe8d - ─────┬─────────────────────────────────────────────────────...</li></ul> [Compare with previous version](/homelab/finanzplaner/-/merge_requests/2/diffs?diff_id=47&start_sha=3d4ce1b670dded56382d54753d885a1b04e6ea6d)
glow commented 2026-07-08 20:21:20 +00:00 (Migrated from gitlab.fluffyplace.de)

changed the description

changed the description
glow commented 2026-07-08 20:36:17 +00:00 (Migrated from gitlab.fluffyplace.de)

mentioned in commit 5d413e52e4

mentioned in commit 5d413e52e423c6465b6bb033996d67b60c18f25b
glow (Migrated from gitlab.fluffyplace.de) merged commit 5d413e52e4 into main 2026-07-08 20:36:17 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
budmin/finanzplaner!12
No description provided.