fix: Neue Tabellen ins Backup, toter Transfer-Code raus

Sieben nutzereigene Tabellen aus den Migrationen 0015-0020 standen in keiner
Backup-Liste. Am schwersten wiegt inbox_items: Der Eingang haelt fest, welche
Zahlungen von aussen schon erfasst wurden. Fehlt er nach dem Wiedereinspielen,
erkennt der naechste Kontoauszug sie nicht wieder und bucht sie ein zweites
Mal – genau der Fehler, den die Tabelle verhindern soll.

Aufgenommen: inbox_items, transfer_links, link_ignored, api_tokens.
Bewusst nicht: mail_sources/mail_senders (tragen das verschluesselte
IMAP-Passwort; ein Backup ist eine Datei, die man herumreicht) und
pending_uploads (fluechtige Rohdateien). Beides steht begruendet im Code.

Ein Test laeuft jetzt ueber Base.registry und fordert fuer jedes Modell mit
owner_id eine bewusste Entscheidung – sichern oder begruendet ausnehmen.
Genau diese Pruefung fehlte, weshalb die sieben unbemerkt durchrutschten.

Dazu der tote Code aus dem Transfer-Ausbau: is_counterpart und braucht_ziel
waren feste False, _apply_counterpart_amount damit unerreichbar. Die Tests
sichern dieselbe Garantie jetzt ueber das tatsaechliche Verhalten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-08-26 21:16:33 +02:00
co-authored by Claude Opus 5
parent c9a598a056
commit 3ee18c4d21
5 changed files with 80 additions and 47 deletions
+6 -4
View File
@@ -341,7 +341,8 @@ def test_umtausch_wird_als_ausgabe_und_einnahme_gebucht(admin):
# Der EUR-Auszug bringt den Zugang – als Einnahme, nicht als Gegenbuchung.
rows2 = _rev_preview(admin, eur["id"], EUR_UMTAUSCH)
assert rows2[0]["type"] == "income"
assert rows2[0]["is_counterpart"] is False
assert rows2[0]["type"] != "transfer" # keine Kontoverknuepfung mehr
assert rows2[0]["include"] is True
assert rows2[0]["include"] is True
r = admin.post("/api/imports/commit", json={"account_id": eur["id"], "filename": "eur.csv", "rows": rows2})
assert r.json()["imported_count"] == 1
@@ -367,7 +368,8 @@ def test_uebertrags_schlagwort_setzt_neutrale_kategorie(admin):
rows = _rev_preview(admin, chf["id"], CHF_UMTAUSCH)
assert rows[0]["type"] == "expense"
assert rows[0]["category_id"] == neutral["id"]
assert rows[0]["needs_target"] is False
assert rows[0]["type"] != "transfer"
assert rows[0]["include"] is True
def test_expense_is_not_swallowed_by_incoming_transfer(admin):
@@ -399,7 +401,7 @@ def test_expense_is_not_swallowed_by_incoming_transfer(admin):
).json()["rows"]
# Keine Zeile gilt mehr als Gegenbuchung – beide werden gebucht.
assert all(r["is_counterpart"] is False for r in rows)
assert all(r["type"] != "transfer" for r in rows)
assert all(r["include"] is True for r in rows)
assert rows[0]["type"] == "expense"
assert rows[1]["type"] == "income"
@@ -512,7 +514,7 @@ def test_keine_zeile_bleibt_wegen_fehlendem_gegenkonto_liegen(admin):
},
).json()
assert not [r for r in res["rows"] if r.get("needs_target")]
assert all(r["type"] != "transfer" for r in res["rows"])
uebertrag = res["rows"][1]
assert uebertrag["type"] == "income" and uebertrag["include"] is True