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:
@@ -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]}")
|
||||
|
||||
Reference in New Issue
Block a user