diff --git a/backend/tests/test_import.py b/backend/tests/test_import.py index 628c914..3d53040 100644 --- a/backend/tests/test_import.py +++ b/backend/tests/test_import.py @@ -622,3 +622,72 @@ def test_verlauf_merkt_sich_die_bank_vorlage(admin): verlauf = admin.get("/api/imports/batches").json() meins = [b for b in verlauf if b["account_id"] == konto["id"]] assert meins[0]["preset"] == "Raiffeisen (CH)" + + +def _csv(*zeilen): + return "Datum;Betrag;Empfaenger;Zweck;GegenIBAN\n" + "".join(z + "\n" for z in zeilen) + + +def _lies(admin, konto_id, text): + return admin.post( + "/api/imports/preview", + files={"file": ("a.csv", io.BytesIO(text.encode("utf-8")), "text/csv")}, + data={"account_id": str(konto_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() + + +def test_monatsauszug_ueber_bereits_gebuchte_zeilen_hinweg(admin): + """Der übliche Ablauf: Mitte Monat einlesen, Ende Monat den ganzen Monat. + + Die bereits gebuchten Zeilen dürfen nicht erneut in den Saldo laufen, die + neuen müssen ankommen. Zwei echte, gleich aussehende Buchungen desselben + Tages bleiben dabei beide erhalten – sie sind kein Duplikat. + """ + konto = _account(admin, name="Monatskonto", currency="CHF") + bis_18 = _csv( + "05.08.2026;-20,00;Coop;Einkauf;", + "12.08.2026;-7,50;Kiosk;Snack;", + "12.08.2026;-7,50;Kiosk;Snack;", # echte zweite Buchung, kein Duplikat + "18.08.2026;3.000,00;Arbeitgeber;Lohn;", + ) + v = _lies(admin, konto["id"], bis_18) + assert sum(1 for r in v["rows"] if r["include"]) == 4 + admin.post("/api/imports/commit", json={ + "account_id": konto["id"], "filename": "aug-teil.csv", + "rows": [r for r in v["rows"] if r["include"]], + }) + stand = lambda: admin.get(f"/api/accounts/{konto['id']}").json()["balance_cents"] + assert stand() == 296500 # -2000 -750 -750 +300000 + + ganzer_august = bis_18 + "".join(z + "\n" for z in ( + "22.08.2026;-45,00;Migros;Einkauf;", + "29.08.2026;-12,00;SBB;Billett;", + )) + v2 = _lies(admin, konto["id"], ganzer_august) + dubletten = [r for r in v2["rows"] if r["is_duplicate"]] + neue = [r for r in v2["rows"] if r["include"]] + assert len(dubletten) == 4, [r["payee"] for r in dubletten] + assert [r["payee"] for r in neue] == ["Migros", "SBB"] + + admin.post("/api/imports/commit", json={ + "account_id": konto["id"], "filename": "aug-ganz.csv", + "rows": neue, + }) + assert stand() == 296500 - 4500 - 1200 + + +def test_dritte_gleiche_buchung_gilt_nicht_als_dublette(admin): + """Kommt am selben Tag eine DRITTE gleiche Buchung dazu, ist sie echt.""" + konto = _account(admin, name="Dreimal", currency="CHF") + zwei = _csv("03.08.2026;-5,00;Bäckerei;Brot;", "03.08.2026;-5,00;Bäckerei;Brot;") + v = _lies(admin, konto["id"], zwei) + admin.post("/api/imports/commit", json={ + "account_id": konto["id"], "filename": "zwei.csv", + "rows": [r for r in v["rows"] if r["include"]]}) + + drei = zwei + "03.08.2026;-5,00;Bäckerei;Brot;\n" + v2 = _lies(admin, konto["id"], drei) + assert sum(1 for r in v2["rows"] if r["is_duplicate"]) == 2 + assert sum(1 for r in v2["rows"] if r["include"]) == 1