# -*- coding: utf-8 -*- """Pruefstand: das gemeinsame Nummernbuch von Rechnungstool und Steuerjournal. Der Fall, um den es geht (sein Satz vom 03.09.2026): "er hat manuelle eingetragen die das andere programm die rechnungs nummer nicht kennt - mit der sqlite beheben wir das" Im Steuerjournal stehen Buchungen aus der alten Excel-Mappe, zu denen es keine PDF gibt. Das Rechnungstool sucht die naechste freie Nummer bisher nur in seinem eigenen Ordner - und vergibt so eine Nummer zum zweiten Mal (§ 14 Abs. 4 Nr. 4 UStG: jede Nummer nur einmal). Geprueft wird mit BEIDEN echten Programmen auf EINEM gemeinsamen Ordner. """ import json import os import shutil import sys import tempfile import threading TOOL = r"C:\claude\rechnungstool" JOURNAL = r"C:\claude\beherbergungssteuer" def _reissleine(): print("ABBRUCH | Pruefstand haengt (Zeitgrenze)") os._exit(2) _wecker = threading.Timer(150, _reissleine) _wecker.daemon = True _wecker.start() os.environ["CAMPINGHOF_TEST"] = "1" # kein Erststart-Dialog tmp = tempfile.mkdtemp(prefix="buch_pruef_") gemeinsam_ordner = os.path.join(tmp, "install") # hier liegt das Buch rechnungen = os.path.join(tmp, "rechnungen") journal_daten = os.path.join(tmp, "journal_daten") for d in (gemeinsam_ordner, rechnungen, journal_daten): os.makedirs(d) # ---- Erst das Steuerjournal (eigener Datenordner) -------------------------- sys.path.insert(0, JOURNAL) import ablage # noqa: E402 ablage.daten_ordner = lambda: journal_daten import app as JA # noqa: E402 - das Journal from modell import Buchung # noqa: E402 import gemeinsam # noqa: E402 del sys.modules["app"] sys.path.remove(JOURNAL) from datetime import date # noqa: E402 from tkinter import messagebox # noqa: E402 from pruefhelfer import fehler, pruefe messagebox.showinfo = lambda *a, **k: None messagebox.showwarning = lambda *a, **k: None journal = JA.App() journal.withdraw() # Altbestand wie aus der Excel-Mappe: Nummern OHNE PDF. for nr, name in (("001", "Alt Eins"), ("002", "Alt Zwei"), ("003", "Alt Drei")): journal.journal.speichern(Buchung(date(2026, 5, 1), nr, 2026, name, 2, 50.0, quelle="xlsx")) journal.jahr.set(2026) journal.aktualisieren() journal.journal.setze("gemeinsam_ordner", gemeinsam_ordner) journal._buch_oeffnen() journal._buch_fuellen() buch = gemeinsam.Nummernbuch(gemeinsam_ordner) pruefe(os.path.exists(gemeinsam.pfad_zur_datei(gemeinsam_ordner)), "das Journal legt das Nummernbuch im gemeinsamen Ordner an") nummern = buch.nummern() pruefe(nummern == {"2026-001", "2026-002", "2026-003"}, f"die drei Altbestands-Nummern stehen drin ({sorted(nummern)})") eintrag = buch.eintrag("2026-002") pruefe(eintrag and eintrag["herkunft"] == "xlsx" and eintrag["quelle"] == "journal", f"mit Herkunft und Quelle ({eintrag and (eintrag['quelle'], eintrag['herkunft'])})") # ---- Jetzt das Rechnungstool auf denselben Ordner -------------------------- sys.path.insert(0, TOOL) import config # noqa: E402 shutil.copy(os.path.join(TOOL, "config.json"), os.path.join(tmp, "config.json")) config.CONFIG_PATH = os.path.join(tmp, "config.json") roh = json.load(open(config.CONFIG_PATH, encoding="utf-8")) roh["rechnung_jahr"], roh["rechnung_zaehler"] = 2026, 0 # Der Erststart-Zustand gehoert dem Pruefstand, nicht der config des # Entwicklerrechners: sonst laeuft er in einen anderen Zweig, sobald hier # jemand die Nummer einmal eingerichtet hat. roh["nummer_eingerichtet"] = False roh["output_dir"] = rechnungen roh["gemeinsam_ordner"] = gemeinsam_ordner json.dump(roh, open(config.CONFIG_PATH, "w", encoding="utf-8"), ensure_ascii=False, indent=2) import app as A # noqa: E402 - das Rechnungstool import bestand as BE # noqa: E402 antworten = {"Fertig": False, "endgültig erstellen": True, "Hausnummer": True, "Storno wirklich": True} def askyesno(titel, text, **kw): for schlue, wert in antworten.items(): if schlue in titel: return wert raise AssertionError(f"unangemeldeter Dialog: {titel!r}") # Die Freigabe vor dem Beleg (§ 14 UStG) wird bewusst bejaht - geprueft wird hier # der Ablauf DANACH. Dass ein "Nein" keinen Beleg erzeugt, prueft pruef_freigabe.py. antworten["jetzt erstellen?"] = True # Der Erprobungshinweis wird hier bejaht - geprueft wird er in pruef_hinweis.py. antworten["Erprobungsfassung"] = True messagebox.askyesno = askyesno tool = A.RechnungsApp() tool.cfg["output_dir"] = rechnungen tool._buch_oeffnen() pruefe(tool.buch is not None, "das Rechnungstool findet dasselbe Buch") tool._neue_nummer_vorschlagen() pruefe(tool.r_nummer.get() == "2026-004", f"es schlaegt 2026-004 vor und ueberspringt den Altbestand ({tool.r_nummer.get()})") # ---- Eine Rechnung schreiben: sie muss im Buch landen --------------------- tool.k_vorname.set("Anna") tool.k_name.set("Neu") tool.k_strasse.set("Musterweg 1") tool.k_plzort.set("12345 Musterstadt") tool.r_anreise.set("01.09.2026") tool.r_abreise.set("03.09.2026") tool.anzahl_vars[sorted(tool.anzahl_vars)[0]].set("2") tool.update() tool.pdf_erstellen() pruefe(os.path.exists(os.path.join(rechnungen, "Rechnung_2026-004_Neu.pdf")), "die Rechnung 2026-004 ist geschrieben") e4 = buch.eintrag("2026-004") pruefe(e4 is not None, "und steht im gemeinsamen Buch") pruefe(e4 and e4["quelle"] == "rechnungstool" and e4["nachname"] == "Neu", f"mit Quelle rechnungstool und Namen ({e4 and (e4['quelle'], e4['nachname'])})") pruefe(e4 and abs(float(e4["zwischensumme"]) - 26.0) < 0.005, f"und dem Betrag ({e4 and e4['zwischensumme']})") # ---- Storno: eigene Nummer ins Buch, beim Verwerfen wieder raus ----------- offen = [m for m in BE.rechnungen_im_ordner(rechnungen) if m.get("art") != "storno"] tool._storno_anlegen(offen[0], rechnungen) storno_nr = tool.storno_stand["nummer"] if tool.storno_stand else "" pruefe(buch.belegt(storno_nr), f"der Storno {storno_nr} steht im Buch") e_storno = buch.eintrag(storno_nr) pruefe(e_storno and e_storno["storno_zu"] == "2026-004", f"und sagt, welche Rechnung er aufhebt ({e_storno and e_storno['storno_zu']})") tool._storno_verwerfen("Pruefstand") pruefe(not buch.belegt(storno_nr), f"beim Verwerfen ist die Nummer wieder frei ({storno_nr})") # ---- Das Journal sieht die neue Rechnung im Buch -------------------------- journal._buch_oeffnen() pruefe("2026-004" in journal.buch.nummern(), "das Journal sieht die Nummer des Rechnungstools im Buch") # ---- Faellt das Buch aus, laeuft trotzdem alles weiter -------------------- tool.cfg["gemeinsam_ordner"] = os.path.join(tmp, "gibtsnicht") tool._buch_oeffnen() pruefe(tool.buch is None, "ein nicht erreichbarer Ordner schaltet das Buch ab") pruefe(tool._belegte_nummern() == set(), "die belegten Nummern sind dann leer") tool._neue_nummer_vorschlagen() pruefe(bool(tool.r_nummer.get()), f"und es wird trotzdem eine Nummer vorgeschlagen " f"({tool.r_nummer.get()})") tool.destroy() journal.journal.schliessen() journal.destroy() if not fehler: shutil.rmtree(tmp, ignore_errors=True) else: print(f" (Wegwerf-Ordner bleibt: {tmp})") print("---") print("alles gruen" if not fehler else f"{len(fehler)} Fehler: {fehler}") sys.exit(1 if fehler else 0)