feat: Pruefskript sagt, WELCHE der beiden Zeilen weg muss

Es liess die Entscheidung beim Nutzer, obwohl die Datenbank sie hergibt: In
bank_account_links steht, welche Konten Certain selbst abholt. Sind beide
angebunden, liefert die Bank beide Seiten und der Uebertrag ist falsch. Wird
das Gegenkonto nicht abgerufen, ist der Uebertrag richtig und die zusaetzliche
Buchung ueberzaehlig. Das steht jetzt je Fall dabei.

Ausserdem war die Zusammenfassung irrefuehrend: Sie addierte Betraege in beide
Richtungen. Eine zusaetzliche Gutschrift macht das Konto zu hoch, eine
zusaetzliche Belastung zu niedrig – aus '2'026 zu viel' werden so '1'125 zu
hoch'.

Dazu zwei Tests zu der Frage, ob der naechste Abruf alles wieder anlegt: Nein.
Der Umsatz wird an der Kennung der Bank wiedererkannt, unabhaengig davon, was
aus der Buchung geworden ist, und ein spaeterer CSV-Auszug faellt ueber den
Eingangs-Abgleich. Aufraeumen haelt also.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-08-27 11:40:38 +02:00
co-authored by Claude Opus 5
parent 0cd0aab90d
commit 0288b2be9f
2 changed files with 95 additions and 18 deletions
+37 -18
View File
@@ -34,6 +34,15 @@ def geld(cents, waehrung=""):
konten = {r["id"]: r for r in con.execute("SELECT id, name, currency FROM accounts")}
# Welche Konten holt Certain selbst von der Bank? Daran haengt, welche der
# beiden Zeilen die falsche ist – und das muss niemand raten.
try:
angebunden = {r[0] for r in con.execute(
"SELECT account_id FROM bank_account_links WHERE account_id IS NOT NULL")}
except sqlite3.OperationalError:
angebunden = set() # aeltere Datenbank ohne Bankanbindung
verdacht = []
for u in con.execute("""
@@ -65,43 +74,53 @@ for u in con.execute("""
WHERE account_id = ? AND type = ? AND date BETWEEN ? AND ? AND id != ?
""", (konto_id, richtung, von.isoformat(), bis.isoformat(), u["id"])):
if abs(k["amount_cents"] - betrag) <= spielraum:
verdacht.append((u, konto_id, betrag, k))
verdacht.append((u, konto_id, betrag, k, richtung))
if not verdacht:
print("Nichts gefunden – kein Übertrag hat eine zusätzlich gebuchte Gegenseite.")
sys.exit(0)
print(f"{len(verdacht)} Stelle(n) zu prüfen.\n")
summe = {}
for u, konto_id, betrag, k in verdacht:
abweichung = {}
for u, konto_id, betrag, k, richtung in verdacht:
konto = konten.get(konto_id, {"name": "?", "currency": ""})
a = konten.get(u["account_id"], {"name": "?"})
b = konten.get(u["to_account_id"], {"name": "?"})
gegen_id = u["to_account_id"] if konto_id == u["account_id"] else u["account_id"]
print(f"Übertrag #{u['id']} {u['date']} {a['name']} → {b['name']} "
f"{geld(u['amount_cents'])}")
print(f" {u['payee'] or u['description'] or 'ohne Text'}")
print(f" daneben auf '{konto['name']}': Buchung #{k['id']} {k['date']} "
f"{geld(k['amount_cents'], konto['currency'])}")
print(f" {k['payee'] or k['description'] or 'ohne Text'}")
print(f" → dasselbe Geld zweimal auf '{konto['name']}'\n")
summe[konto_id] = summe.get(konto_id, 0) + k["amount_cents"]
print("Zu viel auf den Konten, wenn sich der Verdacht bestätigt:")
for konto_id, cents in sorted(summe.items()):
# Das Vorzeichen entscheidet, in welche Richtung das Konto danebenliegt.
vorzeichen = 1 if richtung == "income" else -1
zu = "zu hoch" if vorzeichen > 0 else "zu niedrig"
print(f" → '{konto['name']}' ist um {geld(k['amount_cents'])} {zu}")
if u["account_id"] in angebunden and u["to_account_id"] in angebunden:
print(f" → BEIDE Konten sind angebunden: Die Bank liefert beide Seiten "
f"selbst.\n Falsch ist der ÜBERTRAG #{u['id']} – löschen. Danach "
f"'{a['name']}' neu abrufen,\n dann verbindet Certain die beiden.")
elif gegen_id not in angebunden:
gegen = konten.get(gegen_id, {"name": "?"})
print(f" → '{gegen['name']}' wird NICHT abgerufen: Der Übertrag ist richtig.\n"
f" Überzählig ist Buchung #{k['id']} – die kann weg.")
else:
print(f" → Nur ein Konto ist angebunden. Sieh dir das Paar von Hand an.")
print()
abweichung[konto_id] = abweichung.get(konto_id, 0) + vorzeichen * k["amount_cents"]
print("Wenn sich der Verdacht bestätigt, liegen die Konten so daneben:")
for konto_id, cents in sorted(abweichung.items()):
konto = konten.get(konto_id, {"name": "?", "currency": ""})
print(f" {konto['name']:<28} {geld(cents, konto['currency'])}")
richtung = "zu hoch" if cents > 0 else "zu niedrig"
print(f" {konto['name']:<28} {geld(abs(cents), konto['currency'])} {richtung}")
print("""
So räumst du auf – je Paar EINE der beiden Zeilen, nicht beide:
Beide Seiten kommen von der Bank? Dann sind BEIDE Buchungen richtig, und
falsch ist nur der Übertrag. Lösch ihn und lass die zwei Buchungen von
Certain verbinden ("Zu erledigen" → Mögliche Überträge). Die Belastung, aus
der der Übertrag entstand, holst du mit einem erneuten Abruf des Kontos
zurück.
Das Gegenkonto holt Certain NICHT selbst ab? Dann ist der Übertrag richtig
und die zusätzliche Buchung überzählig – die kann weg.
Lösch je Paar nur EINE der beiden Zeilen – welche, steht oben.
Im Zweifel: Kontenseite ansehen. Steht dort "Saldo stimmt mit der Bank
überein", ist das Konto in Ordnung, egal was hier steht.""")