From d9bff8f2ec5931ea312bc978d7456b1742f61f18 Mon Sep 17 00:00:00 2001 From: TheMockTv Date: Fri, 4 Sep 2026 01:05:59 +0200 Subject: [PATCH] Blick-Skript: was die Stornos in einem Journal bewirken Reines Lesen - keine Buchung wird angelegt, geaendert oder geloescht und der Rechnungsordner wird nicht gescannt. Damit kann man es ohne Sorge auf das echte Journal loslassen. Zeigt je Monat die alte Rechenweise (Rechnung + und Storno - heben sich gegenseitig auf) gegen die jetzige (Storno und aufgehobene Rechnung zaehlen gar nicht mit) und darunter jedes Storno-Paar einzeln, mit dem Hinweis, wenn Rechnung und Storno in VERSCHIEDENEN Monaten liegen. Genau dort unterscheiden sich die beiden Rechenweisen. Anlass: die Frage "stimmen die 8.500 Euro jetzt" laesst sich nur an den echten Daten beantworten, nicht durch Herleitung. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017bEsoFUk16DfnNA7MjY36H --- zeig_stornos.py | 105 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 zeig_stornos.py diff --git a/zeig_stornos.py b/zeig_stornos.py new file mode 100644 index 0000000..b2eaf58 --- /dev/null +++ b/zeig_stornos.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +"""Zeigt, was die Stornos in einem Journal bewirken. NUR LESEN. + + python zeig_stornos.py [Pfad zur journal.sqlite3] + +Ohne Pfad nimmt es den Datenordner, den sich das Programm gemerkt hat. + +Es wird ausschliesslich gelesen - keine Buchung angelegt, geaendert oder +geloescht, kein Scan des Rechnungsordners. Man kann es also bedenkenlos auf +das echte Journal loslassen. + +Ausgegeben wird je Monat: + ALT = wie frueher gerechnet wurde (Rechnung + und Storno - stehen beide + in der Summe und heben sich gegenseitig auf) + JETZT = Storno und die von ihm aufgehobene Rechnung zaehlen gar nicht mit + (Berichtigung im Monat der Rechnung) + +Der Unterschied wird nur dort sichtbar, wo Rechnung und Storno in +VERSCHIEDENEN Monaten liegen - genau der Fall, um den es geht. +""" +import os +import sys + +HIER = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HIER) + +from modell import MONATE, aufgehobene, eur, summiere # noqa: E402 + + +def journal_pfad(argv): + if len(argv) > 1: + return argv[1] + merker = os.path.join(os.environ.get("LOCALAPPDATA", ""), + "Beherbergungssteuer", "datenordner.txt") + if os.path.exists(merker): + ordner = open(merker, encoding="utf-8").read().strip() + if ordner: + return os.path.join(ordner, "journal.sqlite3") + return os.path.join(HIER, "journal.sqlite3") + + +def main(): + pfad = journal_pfad(sys.argv) + if not os.path.exists(pfad): + print(f"Kein Journal unter {pfad}") + print("Pfad angeben: python zeig_stornos.py <...\\journal.sqlite3>") + return 2 + print(f"Journal: {pfad} (wird nur gelesen)\n") + + from db import Journal + j = Journal(pfad) + try: + for jahr in sorted(j.jahre() or []): + alle = j.buchungen(jahr=jahr) + weg = aufgehobene(alle) + stornos = [b for b in alle if b.storno_zu] + print(f"=== {jahr} === {len(alle)} Buchungen, " + f"{len(stornos)} Storno-Beleg(e), " + f"{len(weg)} aufgehobene Rechnung(en)") + + print(f"{'Monat':12s} {'ALT (verrechnet)':>18s} {'JETZT':>14s} " + f"{'Unterschied':>14s} Naechte alt/jetzt") + for m in range(1, 13): + des_monats = [b for b in alle if b.monat == m] + if not des_monats: + continue + alt_g = round(sum(float(b.gezahlt or 0) for b in des_monats), 2) + alt_n = sum(int(b.naechte or 0) for b in des_monats) + s = summiere(des_monats, weg) + marke = " <<<" if abs(alt_g - s.gezahlt) > 0.004 else "" + print(f"{MONATE[m-1]:12s} {eur(alt_g):>18s} {eur(s.gezahlt):>14s} " + f"{eur(s.gezahlt - alt_g):>14s} {alt_n}/{s.naechte}{marke}") + + jahr_alt = round(sum(float(b.gezahlt or 0) for b in alle), 2) + jahr_neu = summiere(alle, weg) + print(f"{'JAHR':12s} {eur(jahr_alt):>18s} {eur(jahr_neu.gezahlt):>14s} " + f"{eur(jahr_neu.gezahlt - jahr_alt):>14s} " + f"{sum(int(b.naechte or 0) for b in alle)}/{jahr_neu.naechte}") + + if not stornos: + print("\n(keine Stornos in diesem Jahr)\n") + continue + + print("\nDie Storno-Paare im Einzelnen:") + for s_ in sorted(stornos, key=lambda b: (b.datum, b.rechnungsnummer)): + ziel = [b for b in alle if b.id in weg and b.voll_nummer == s_.storno_zu] + z = ziel[0] if ziel else None + print(f" Storno {s_.voll_nummer} vom {s_.datum:%d.%m.%Y} " + f"({MONATE[s_.monat-1]}) {eur(s_.gezahlt)}, {s_.naechte} Naechte") + if z: + gleich = "gleicher Monat" if z.monat == s_.monat else \ + f"ANDERER Monat: Rechnung in {MONATE[z.monat-1]}" + print(f" hebt auf: {z.voll_nummer} vom {z.datum:%d.%m.%Y}, " + f"{z.nachname}, {eur(z.gezahlt)}, {z.naechte} Naechte [{gleich}]") + else: + print(f" ACHTUNG: zu {s_.storno_zu} liegt KEINE Rechnung im Journal - " + f"der Storno zieht also einfach ab, ohne dass etwas aufgehoben wird") + print() + finally: + j.schliessen() + return 0 + + +if __name__ == "__main__": + sys.exit(main())