# -*- coding: utf-8 -*- """Pruefstand: der Hinweis auf die Erprobungsfassung und die Kenntnisnahme. Sein Wortlaut vom 06.09.2026: "wenn er nein drueckt oder ablehnt, schliesst sich die software und geht beim naechsten start wieder mit dem selben auf, bis er auf ja drueckt" "und das alles auch noch mal in die info mit signatur: user hat dies gelesen und zur kenntnis genommen" Geprueft wird die ganze Strecke, weil jeder Teil fuer sich wertlos waere: 1. Der Hinweis kommt beim Start und nennt Haftung, Vorschau und die Fehleradresse. 2. NEIN -> das Fenster ist zu, und in der Konfiguration steht NICHTS. Ein "Nein", das trotzdem vermerkt wird, waere eine Luege. 3. Neuer Start -> es wird WIEDER gefragt. 4. JA -> Signatur mit Benutzer, Zeit und Fassung; danach wird nicht mehr gefragt. 5. Andere Fassung des Textes -> es wird erneut gefragt. Eine Zustimmung gilt fuer das, was dagestanden hat, nicht fuer alles Kuenftige. """ import json import os import time import shutil import sys import tempfile import threading QUELLE = r"C:\claude\rechnungstool" sys.path.insert(0, QUELLE) def _reissleine(): print("ABBRUCH | Pruefstand haengt (Zeitgrenze)") os._exit(2) _wecker = threading.Timer(120, _reissleine) _wecker.daemon = True _wecker.start() tmp = tempfile.mkdtemp(prefix="hinweis_pruef_") ordner = os.path.join(tmp, "rechnungen") os.makedirs(ordner) import config # noqa: E402 shutil.copy(os.path.join(QUELLE, "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["output_dir"] = ordner roh["nummer_eingerichtet"] = True for weg in ("hinweis_fassung", "hinweis_zeit", "hinweis_benutzer"): roh.pop(weg, None) json.dump(roh, open(config.CONFIG_PATH, "w", encoding="utf-8"), ensure_ascii=False, indent=2) os.environ["CAMPINGHOF_TEST"] = "1" import app as A # noqa: E402 import hinweis as H # noqa: E402 from tkinter import messagebox # noqa: E402 from pruefhelfer import fehler, pruefe antworten = {"Fertig": False, "Hausnummer": True} gesehen = [] def askyesno(titel, text, **kw): gesehen.append((str(titel), str(text))) for schlue, wert in antworten.items(): if schlue in titel: return wert raise AssertionError(f"unangemeldeter Dialog: {titel!r}") messagebox.askyesno = askyesno messagebox.showinfo = lambda *a, **k: None messagebox.showwarning = lambda *a, **k: None messagebox.showerror = lambda *a, **k: print("[fehler]", a[0] if a else "") def pumpen(fenster, bedingung, sekunden=6): """Ereignisschleife drehen, bis die Bedingung stimmt. Der Hinweis wartet, bis das Fenster gezeichnet ist, und meldet sich per after(). Ein einzelnes update() erreicht das nicht - hier wird die Schleife gedreht wie mainloop() es taete. Abgekuerzt wird nichts. """ ende = time.time() + sekunden while time.time() < ende: try: fenster.update() except Exception: # noqa: BLE001 - Fenster kann zwischendurch weg sein return bedingung() if bedingung(): return True time.sleep(0.02) return bedingung() def gelesen(): return json.load(open(config.CONFIG_PATH, encoding="utf-8")) # ---- 1./2. Ablehnen: Fenster zu, nichts vermerkt ----------------------- antworten["Erprobungsfassung"] = False f = A.RechnungsApp() pumpen(f, lambda: any("Erprobungsfassung" in t[0] for t in gesehen)) treffer = [t for t in gesehen if "Erprobungsfassung" in t[0]] pruefe(bool(treffer), "der Hinweis kommt beim Start") if treffer: inhalt = treffer[0][1] pruefe("ERPROBUNGSFASSUNG" in inhalt.upper(), "er nennt die Erprobungsfassung") pruefe("Haftung" in inhalt, "er nennt die Haftung") pruefe("Vorschau" in inhalt, "er verweist auf die Vorschau") pruefe("git.pcore.de/TheMockTv/rechnungstool" in inhalt, "er nennt die Adresse fuer Fehlermeldungen") def fenster_zu(fenster): """True, wenn es das Fenster nicht mehr gibt. Nach destroy() antwortet winfo_exists() nicht mehr mit 0, sondern wirft - die ganze Anwendung ist weg. Beides heisst hier dasselbe. """ import tkinter try: return not fenster.winfo_exists() except tkinter.TclError: return True pruefe(fenster_zu(f), "nach Nein ist das Fenster zu") cfg = gelesen() pruefe(not cfg.get("hinweis_zeit"), "nach Nein steht nichts in der Konfiguration") # ---- 3. Neuer Start: es wird wieder gefragt ---------------------------- gesehen.clear() antworten["Erprobungsfassung"] = True f = A.RechnungsApp() pumpen(f, lambda: any("Erprobungsfassung" in t[0] for t in gesehen)) pruefe(any("Erprobungsfassung" in t[0] for t in gesehen), "beim naechsten Start wird wieder gefragt") # ---- 4. Zustimmen: Signatur steht --------------------------------------- cfg = gelesen() pruefe(cfg.get("hinweis_fassung") == H.FASSUNG, f"die Fassung ist vermerkt ({cfg.get('hinweis_fassung')!r})") pruefe(bool(cfg.get("hinweis_zeit")), "der Zeitpunkt ist vermerkt") pruefe(bool(cfg.get("hinweis_benutzer")), "der Benutzer ist vermerkt") sig = H.signatur(f.cfg) pruefe("gelesen und zur kenntnis genommen" in sig.lower(), f"die Signaturzeile ist lesbar ({sig!r})") # ---- 5. Kein zweites Mal fragen ----------------------------------------- gesehen.clear() f.destroy() f = A.RechnungsApp() pumpen(f, lambda: any("Erprobungsfassung" in t[0] for t in gesehen), sekunden=2) pruefe(not any("Erprobungsfassung" in t[0] for t in gesehen), "nach dem Ja wird nicht noch einmal gefragt") # ---- 6. Andere Fassung -> wieder fragen --------------------------------- gesehen.clear() f.destroy() H.FASSUNG = "9999-12-31" # so, als waere der Text geaendert worden f = A.RechnungsApp() pumpen(f, lambda: any("Erprobungsfassung" in t[0] for t in gesehen)) pruefe(any("Erprobungsfassung" in t[0] for t in gesehen), "bei geaenderter Fassung wird erneut gefragt") _wecker.cancel() try: f.destroy() except Exception: # noqa: BLE001 pass shutil.rmtree(tmp, ignore_errors=True) if fehler: print(f"ROT | {len(fehler)} Punkt(e) nicht erfuellt") sys.exit(1) print("GRUEN | Hinweis, Ablehnung und Signatur wirken")