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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017bEsoFUk16DfnNA7MjY36H
This commit is contained in:
TheMockTv 2026-09-04 01:05:59 +02:00
parent 448afb3c62
commit d9bff8f2ec

105
zeig_stornos.py Normal file
View file

@ -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())