fix: Bestandsabgleich per Token-Match und Volatilitaets-Hinweis fuer Vorschlaege

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 21:46:10 +02:00
parent a520390e31
commit aff8e9b28d
2 changed files with 130 additions and 9 deletions

View File

@@ -253,6 +253,13 @@ def test_suggest_zwei_vertraege_getrennt(db):
out = suggest_recurring(db, today=TODAY)
assert len(out) == 2
assert {s["amount"] for s in out} == {Decimal("-346.23"), Decimal("-145.21")}
# Nachtrag 3b: beide Vertraege sind fuer sich genommen stabile,
# qualifizierte Serien - die jeweils neuere Buchung des ANDEREN Vertrags
# gehoert selbst zu einer qualifizierten Serie und darf deshalb NICHT als
# "abgetrennte neueste Buchung" gewertet werden (sonst waere einer der
# beiden faelschlich als "volatil" markiert, nur weil der andere Vertrag
# einen Tag spaeter faellig ist).
assert all(s["hinweis"] == "" for s in out)
def test_try_merge_kombiniert_serien_ueber_gruppengrenzen():
@@ -320,3 +327,43 @@ def test_suggest_umfirmierung_merge_verschiebt_kategorie_mehrheit(db):
assert out[0]["name"] == "Neue Firma GmbH" and out[0]["amount"] == Decimal("-52.00")
# Kategorie-Mehrheit kippt durch den Merge von B (3) auf A (4):
assert out[0]["category_id"] == cat_a.id
# ------------------------------------------------- Nachtrag 4 (A9-Live-Gate-Fund)
# Echter Fall aus dem Live-Gate: Fixposten 44 "Mastercard-Abrechnung Volksbank
# (variabel, letzter Wert)" (monthly, due_day 7, -296.07) deckte den Vorschlag
# "Volksbank Ulm-Biberach eG" (monthly, due_day 5, -584.43) nicht ab, weil
# weder Substring- noch Betrags-Toleranz-Regel griffen. Betraege/Namen hier
# synthetisch nachgebildet (keine echten Kontodaten, siehe CLAUDE.md).
def test_suggest_alias_recurring_item_token_match(db):
acc = _acc(db)
db.add(RecurringItem(name="Mastercard-Abrechnung Volksbank (variabel, letzter Wert)",
amount=Decimal("-296.07"), rhythm="monthly", due_day=7))
for d, a in [(date(2026, 4, 5), "-560.00"), (date(2026, 5, 5), "-580.00"),
(date(2026, 6, 5), "-590.00"), (date(2026, 7, 5), "-580.00")]:
_tx(db, acc, d, a, "Volksbank Ulm-Biberach eG")
db.commit()
# Substring-Match (a) schlaegt fehl (kein Teilstring gemeinsam), Betrags-
# Toleranz (b) auch (-580 vs. -296.07, >10%) - erst der Token-Match (c)
# ueber das gemeinsame Token "volksbank" (Rhythmus gleich, due_day 5 vs. 7
# -> Differenz 2 <= 2) deckt den Vorschlag ab.
assert suggest_recurring(db, today=TODAY) == []
def test_suggest_volatilitaetshinweis_bei_abgetrennter_neuester_buchung(db):
acc = _acc(db)
for d in [date(2026, 1, 5), date(2026, 2, 5), date(2026, 3, 5), date(2026, 4, 5)]:
_tx(db, acc, d, "-200.00", "Schwankender Anbieter GmbH")
# Neueste Buchung weicht >35% vom Serien-Betrag ab -> eigener Cluster,
# klassifiziert selbst nicht (nur 1 Buchung) -> die vorgeschlagene Serie
# bleibt die -200.00-Serie, aber mit Volatilitaets-Warnhinweis.
_tx(db, acc, date(2026, 5, 5), "-600.00", "Schwankender Anbieter GmbH")
db.commit()
out = suggest_recurring(db, today=date(2026, 5, 20))
assert len(out) == 1
assert out[0]["amount"] == Decimal("-200.00") # Betrag NICHT durch die 600er-Buchung verfaelscht
assert "schwanken" in out[0]["hinweis"].lower()