feat: Auszugs-Vergleich nennt die fehlenden Buchungen einzeln

Die Tagessummen sagen nur, dass etwas fehlt. Jetzt werden die Betraege je
Tag als Menge verglichen: was im Auszug steht und nicht in Certain, und
was Certain zusaetzlich hat – Letzteres mit Art und Text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-08-21 17:04:11 +02:00
co-authored by Claude Opus 5
parent 51af9eacff
commit b4cd5b7ca3
+33 -7
View File
@@ -12,7 +12,7 @@ Aufruf (Spalten heissen wie in der Kopfzeile der Datei):
import argparse
import csv
import sqlite3
from collections import defaultdict
from collections import Counter, defaultdict
from datetime import date
NICHT_GEBUCHT = {"PENDING", "AUSSTEHEND", "REVERTED", "STORNIERT", "DECLINED",
@@ -59,6 +59,7 @@ def tag(s) -> date | None:
# --- Auszug einlesen ---------------------------------------------------------
fs, fv = (a.filt.split("=", 1) if a.filt else (None, None))
je_tag_datei: dict[date, int] = defaultdict(int)
zeilen_datei: dict[date, list] = defaultdict(list)
zeilen = 0
with open(a.datei, encoding="utf-8-sig", newline="") as f:
for r in csv.DictReader(f, delimiter=a.trenner):
@@ -71,25 +72,30 @@ with open(a.datei, encoding="utf-8-sig", newline="") as f:
continue
betrag = zahl(r.get(a.betrag)) - zahl(r.get(a.gebuehr)) if a.gebuehr else zahl(r.get(a.betrag))
je_tag_datei[d] += betrag
zeilen_datei[d].append(betrag)
zeilen += 1
# --- Datenbank ---------------------------------------------------------------
con = sqlite3.connect(f"file:{a.db}?mode=ro", uri=True)
je_tag_db: dict[date, int] = defaultdict(int)
for d, typ, betrag, ziel, zielbetrag in con.execute(
"SELECT date, type, amount_cents, to_account_id, to_amount_cents FROM transactions "
zeilen_db: dict[date, list] = defaultdict(list)
for d, typ, betrag, ziel, zielbetrag, payee, besch in con.execute(
"SELECT date, type, amount_cents, to_account_id, to_amount_cents, "
"COALESCE(payee,''), COALESCE(description,'') FROM transactions "
"WHERE account_id = ? OR to_account_id = ?", (a.konto, a.konto)):
dd = tag(d)
if dd is None:
continue
if typ == "income":
je_tag_db[dd] += betrag
wert = betrag
elif typ == "expense":
je_tag_db[dd] -= betrag
wert = -betrag
elif ziel == a.konto:
je_tag_db[dd] += zielbetrag if zielbetrag is not None else betrag
wert = zielbetrag if zielbetrag is not None else betrag
else:
je_tag_db[dd] -= betrag
wert = -betrag
je_tag_db[dd] += wert
zeilen_db[dd].append((wert, typ, (payee + " " + besch).strip()))
def geld(c):
@@ -122,3 +128,23 @@ for d, diff in tage[:25]:
f"Differenz {geld(diff):>10}")
if not tage:
print(" keine – der Auszug ist vollständig und korrekt gebucht.")
print("\n=== Welche Buchungen fehlen bzw. sind zu viel ===")
print("(Vergleich der einzelnen Betraege je Tag; der Auszug enthaelt keine Texte)")
for d, diff in tage[:10]:
aus, dbs = Counter(zeilen_datei[d]), Counter(w for w, _, _ in zeilen_db[d])
fehlt = sorted((aus - dbs).elements())
zuviel = sorted((dbs - aus).elements())
if not fehlt and not zuviel:
continue
print(f"\n {d} (Differenz {geld(diff)})")
if fehlt:
print(f" fehlt in Certain: {', '.join(geld(v) for v in fehlt)}")
if zuviel:
print(" zu viel in Certain:")
gezeigt = Counter()
for w, typ, text in sorted(zeilen_db[d], key=lambda x: x[0]):
if gezeigt[w] < Counter(zuviel)[w]:
gezeigt[w] += 1
print(f" {geld(w):>10} {typ:<9} {text[:52]}")