fix: DKB-Empfaenger/Zweck-Split per CSV-Ground-Truth, Vergleichswerkzeug

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 22:08:37 +02:00
parent 7a0f71aa8c
commit 1b888307e9
3 changed files with 312 additions and 2 deletions

View File

@@ -5,6 +5,7 @@ from pathlib import Path
import pytest
from app.parsers.dkb import _split_counterparty
from app.parsers.registry import parse_pdf
from app.parsers.validate import balance_difference
@@ -56,3 +57,64 @@ def test_expected_values(filename, bank):
assert str(t.amount) == c["amount"]
assert c["counterparty_contains"].lower() in t.counterparty.lower()
assert c["purpose_contains"].lower() in t.purpose.lower()
# --- DKB Empfänger/Zweck-Split-Heuristik (D-T5-1/D-T5-2) -------------------
# Ausschließlich synthetische Zeilen (fiktive Namen/Nummern), keine echten
# Kontodaten.
def test_split_counterparty_legal_form_single():
cp, rest = _split_counterparty(
"Musterfirma Beispiel GmbH Rechnung Nr. F000123 vom 01.01.20")
assert cp == "Musterfirma Beispiel GmbH"
assert rest == "Rechnung Nr. F000123 vom 01.01.20"
def test_split_counterparty_legal_form_et_cie_chain():
cp, rest = _split_counterparty(
"Fiktiv Global S.a.r.l. et Cie S.C.A Onlinedienst Referenz 999")
assert cp == "Fiktiv Global S.a.r.l. et Cie S.C.A"
assert rest == "Onlinedienst Referenz 999"
def test_split_counterparty_reference_boundary_digit_token():
cp, rest = _split_counterparty(
"Beispiel Handelsgesellschaft Auftrag 84210 Sonderzahlung")
assert cp == "Beispiel Handelsgesellschaft Auftrag"
assert rest == "84210 Sonderzahlung"
def test_split_counterparty_reference_boundary_keyword_token():
cp, rest = _split_counterparty(
"Servicepartner Nord Vertrag ABC123 Laufzeit")
assert cp == "Servicepartner Nord"
assert rest == "Vertrag ABC123 Laufzeit"
def test_split_counterparty_no_split_pure_words():
cp, rest = _split_counterparty("MUSTER PERSON Nebenkosten")
assert cp == "MUSTER PERSON Nebenkosten"
assert rest == ""
def test_split_counterparty_siehe_anlage():
cp, rest = _split_counterparty("siehe Anlage Nr. 7")
assert cp == ""
assert rest == "siehe Anlage Nr. 7"
def test_split_counterparty_never_empty_guard():
# Erstes Token enthält eine Ziffer, darf aber nie selbst die
# Referenz-Grenze bilden (Regel greift frühestens ab Token 1) - da kein
# weiteres Token eine Ziffer/Schlüsselwort liefert, bleibt die ganze
# Zeile als counterparty erhalten (nie leer).
cp, rest = _split_counterparty("42 Testfirma Ausgleich")
assert cp == "42 Testfirma Ausgleich"
assert rest == ""
assert cp != ""
# Einzelnes Token (auch mit Ziffer): kein Schnitt möglich, bleibt
# counterparty (nie leer, nie None).
cp2, rest2 = _split_counterparty("12345")
assert cp2 == "12345"
assert rest2 == ""