fix: Gegenbuchungs-Erkennung beachtet die Richtung

Die Suche prüfte nur Betrag und Zeitfenster. Ein erwarteter Transfer-EINGANG
passte damit auch auf eine gleich hohe AUSGABE: Die echte Ausgabe galt als
Gegenbuchung, wurde nicht gebucht und der Saldo lief um ihren Betrag zu hoch.

Jetzt muss das Vorzeichen zur Seite des Transfers passen (wo Geld ankam, kann
keine Ausgabe die Gegenseite sein). Test deckt genau diesen Fall ab; 87 grün.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-08-21 08:31:18 +02:00
co-authored by Claude Opus 4.8
parent 62eab06bdc
commit 187a1119a2
2 changed files with 46 additions and 3 deletions
+38
View File
@@ -368,3 +368,41 @@ def test_exchange_arrival_without_source_asks_instead_of_booking(admin):
assert r.json()["imported_count"] == 0
assert admin.get(f"/api/accounts/{eur['id']}").json()["balance_cents"] == 0
assert chf # Quellkonto bleibt unberührt
def test_expense_is_not_swallowed_by_incoming_transfer(admin):
"""Eine echte Ausgabe über denselben Betrag wie ein erwarteter Transfer-Eingang
darf NICHT als Gegenbuchung gelten – sonst fehlt sie und der Saldo läuft zu hoch."""
quelle = _account(admin, name="Quelle Z", currency="EUR", opening_balance_cents=100000)
ziel = _account(admin, name="Ziel Z", currency="EUR", opening_balance_cents=10000)
# Transfer 50,00 von Quelle -> Ziel buchen.
r = admin.post("/api/transactions", json={
"type": "transfer", "date": "2026-03-10",
"account_id": quelle["id"], "to_account_id": ziel["id"], "amount_cents": 5000,
})
assert r.status_code == 201, r.text
csv_text = (
"Datum;Betrag;Empfaenger;Zweck;GegenIBAN\n"
"11.03.2026;-50,00;Supermarkt;Einkauf;\n" # echte Ausgabe, gleicher Betrag
"12.03.2026;50,00;Von Quelle;Uebertrag;\n" # das ist die echte Gegenbuchung
)
rows = admin.post(
"/api/imports/preview",
files={"file": ("z.csv", io.BytesIO(csv_text.encode("utf-8")), "text/csv")},
data={
"account_id": str(ziel["id"]), "delimiter": ";", "decimal_sep": ",",
"date_format": "%d.%m.%Y", "encoding": "utf-8", "has_header": "true",
"skip_rows": "0", "amount_mode": "signed", "column_map": COLMAP,
},
).json()["rows"]
assert rows[0]["is_counterpart"] is False, "Ausgabe ist keine Gegenbuchung"
assert rows[0]["include"] is True
assert rows[1]["is_counterpart"] is True, "der Eingang schon"
assert rows[1]["include"] is False
admin.post("/api/imports/commit", json={"account_id": ziel["id"], "filename": "z.csv", "rows": rows})
# 100.00 + 50.00 (Transfer) - 50.00 (Einkauf) = 100.00
assert admin.get(f"/api/accounts/{ziel['id']}").json()["balance_cents"] == 10000