Aufraeumrunde nach der Pruefkette. Fuer den Nutzer sichtbar ist ein Punkt:
der Wechsel von einem Menuetitel zum anderen braucht nur noch EINEN Klick.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Die Runde vom 07.09.2026 hat neben den zwei echten Fehlern eine Reihe
Wiederholungen und stumme Stellen gemeldet. Abgearbeitet in Etappen, jede fuer
sich pruefbar - nach jeder liefen beide Pruefketten gruen.
* Menueleiste: der Wechsel von einem Titel zum anderen brauchte ZWEI Klicks.
Der grab_set der offenen Liste schluckt den ersten, die <Button-1>-Bindung
des Titels feuert gar nicht. Das native tk.Menu, das diese Leiste ersetzt
hat, wechselte schon beim blossen Ueberfahren - zwei Klicks waren also eine
Verschlechterung gegenueber dem, was vorher da war. Die Klappliste wird
ausserdem jetzt wie jede andere Position ueber theme.auf_bildschirm()
begrenzt; am unteren Fensterrand lief sie vorher hinaus.
Neu: pruef_menueleiste.py misst beides ueber echte Klickereignisse.
* %LOCALAPPDATA%/ravokk wurde an drei Stellen einzeln ausgerechnet. Jetzt
fragen alle gemeinsam.standard_ordner(). Laufen die je auseinander, fuehren
die beiden Programme zwei Nummernbuecher - und das faellt erst auf, wenn
eine Rechnungsnummer zum zweiten Mal vergeben ist (§ 14 Abs. 4 Nr. 4 UStG).
* pruefe() stand 35-mal im Quelltext, in sechs Fassungen -> pruefhelfer.py.
Die zwei Ausreisser sind mitgezogen: pruef_bilder.py schrieb ein eigenes
Format, pruef_storno_verrechnet.py zaehlte in einer Zahl und konnte am Ende
nicht sagen, WAS fehlschlug.
* dlg_darstellung() und die drei Handgriffe des Umschaltens standen in beiden
app.py fast wortgleich -> ThemeMixin in theme.py. Beide trugen inzwischen
denselben langen Kommentar zu demselben Fehler; das war der Beweis, dass es
eine Sache ist. Programmspezifisch bleiben der Hinweistext im Dialog (R31)
und die Frage, ob die Einstellungen ausdruecklich gespeichert werden muessen.
* pruef_gemeinsam_automatisch.py sagte im Text "genau ein Buch" und prueft
">= 1" mit einem any() - eine Pruefung, die nicht rot werden kann. Jetzt
== 1, und der Dateiname wird mitgeprueft.
Dazu neu: die neun Dateien, die im Kopf zusagen, sie laegen in beiden
Programmen gleich, werden byteweise verglichen. Bei hinweis.py stimmte die
Zusage seit dem 06.09. nicht mehr - gleicher Inhalt, CRLF gegen LF.
* .gitattributes, damit die Zeilenenden nicht von der Maschine abhaengen.
Ohne das meldet genau diese Pruefung nach einem frischen Checkout einen
Unterschied, den es im Repository gar nicht gibt.
* Sechs stumme "except OSError" sagen jetzt, warum sie schweigen duerfen, und
einer meldet statt zu schweigen: schlaegt die Uebernahme einer alten
config.json fehl, faengt das Programm ohne Firmendaten, Katalog und Zaehler
neu an - und der Erfolgsfall schrieb eine Zeile, der Fehlerfall nicht. Die
24 stummen tk.TclError bleiben: dort wird ein Widget angefasst, das gerade
zerstoert wurde, und eine Meldung waere Rauschen.
* Tote Parameter und Variablen aus dem Umbau des Vortags: symbol_setzen(dunkel=),
kachel(grund=, radius=), mit_trennlinie, sechs x/y-Berechnungen, die von
theme.mittig() sofort ueberschrieben wurden, vier lokale "import ctypes".
39 Pruefstaende, 0 rot.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
## 1. Der Darstellungsdialog zerstoerte sich beim ersten Klick selbst
_oberflaeche_neu() loeschte ALLE Kinder des Hauptfensters - und ein
tk.Toplevel IST ein Kind. Der offene Dialog wurde also mitgeloescht, die
Zeile danach fasste eine Leiche an:
_tkinter.TclError: invalid command name ".!toplevel"
Die Farbe wechselte zwar und nichts ging verloren, aber das Fenster
verschwand beim ersten Klick, jede weitere Umstellung brauchte einen neuen
Menueaufruf, und jedes Mal landete ein Traceback im Log. In BEIDEN
Programmen.
Behoben: fremde Toplevels bleiben stehen. Damit ueberlebt auch ein offener
Ordnerwahl- oder Einstellungsdialog den Wechsel.
⚠️ Warum mein Pruefstand es nicht fand: er setzte cfg["darstellung"] von Hand
und rief _oberflaeche_neu() direkt - also alles ausser dem Weg, den ein
Mensch geht. Den Dialog oeffnete er zwar, mass aber nur dessen Position.
pruef_darstellung drueckt jetzt den Radioknopf (.invoke()) und prueft, dass
der Dialog danach noch lebt UND dass ein ZWEITER Klick im selben Dialog
wirkt - das war vorher unmoeglich.
## 2. Unerreichbarer Code nach einem return
_fenstersymbol() hatte zwei Zeilen der alten Inline-Fassung hinter dem
return stehen. Rest meines symbol_setzen-Umbaus; per AST ueber alle Dateien
beider Ordner belegt (genau ein Treffer).
Beides gefunden von der Pruefkette nach R23. Die Aufraeumbefunde derselben
Runde (doppelter Code, tote Variablen, 34x dasselbe pruefe()) kommen
getrennt - eine Fehlerbehebung soll fuer sich pruefbar bleiben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
War das Programm vorher installiert, ist es danach wieder installiert. Beim
ersten Lauf war es das NICHT - danach stand ein deinstalliertes Programm da,
und der naechste Schritt (den Updater starten lassen) lief ins Leere.
Ein Pruefstand, der aufraeumt, muss auch sich selbst aufraeumen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Befund: "hast du bei dem windows icon das gleich farbige wie im
programm, wo als wasserzeichen gilt, gemacht - weil das schaut blau aus".
Er hatte recht: auf der Kachel stand das ROHE Logo mit seinem Petrol
#004860, waehrend im Programm (Kopfzeile, Wasserzeichen) laengst die
umgefaerbte Fassung laeuft. Zwei Farben fuer dasselbe Zeichen.
Jetzt geht das Logo auch fuers Symbol durch _umfaerben() - dieselbe
Funktion, dieselben Farben: Tuerkis fuer die Wolke, Dunkelgrau fuer das
Zeichen daneben. Die Kachel ist weiss, also gilt die HELLE Fassung.
Fassung 1.8.1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Seine Ansagen
"gibt es noch das popup install ordner auswaehlen, das stimmt ja nicht"
"wie soll das rechnungstool und steuertool wissen wo es ist, das muss in
ravokk liegen und dann zusammen, sonst kommt das eine nicht an das andere
ran - die sind doch nur ueber die gemeinsame sqlite verbunden und mit api"
"es gibt ravokk als main verzeichnis, da liegt config und sqlite drin, und
dann gibt es den ordner rechnungstool und steuertool"
"ravokk ist meine firma und die programme gehoeren zu der firma"
"du solltest die auch mal deinstallieren und neu installieren ... dass der
ordner dann leer ist und sauber - will keine tools die unsauber sind"
Die Struktur
%LOCALAPPDATA%/ravokk/
config.json, journal.sqlite3, Nummernbuch, berichte/ <- Daten
Rechnungstool/ <- Programm
Steuerrechnungstool/ <- Programm
Die Daten liegen NEBEN dem Programm, nicht darin. Nur dadurch koennen
beide Forderungen gleichzeitig gelten: der Programmordner wird beim
Deinstallieren restlos geleert, und die Belege bleiben zehn Jahre stehen
(§ 147 AO).
Kein Ordner-Dialog mehr beim ersten Start
gemeinsam.standard_ordner() nennt den festen Ort, beide Programme legen
ihn selbst an. Gefragt wird nur noch, wer ihn ausdruecklich verschieben
will (Menue -> Gemeinsamer Ordner) - etwa auf ein Netzlaufwerk.
Der Knopf hiess "Install-Ordner auswaehlen..." und der Text riet zum
"Ordner, in dem die Programme liegen". Beides war falsch und seit dem
Installer erst recht: die beiden liegen in getrennten Unterordnern, und
ein Deinstallieren haette das Nummernbuch mitgenommen.
⚠ Dabei gefunden: das Journal lag im TEMP-Ordner
Der Merkzettel der installierten Fassung zeigte auf
%TEMP%/abnahme_bst/Daten - haengengeblieben aus einem Abnahmelauf vom
03.09. Windows raeumt den Temp-Ordner automatisch auf; das Journal waere
ohne Vorwarnung verschwunden. ablage.py verwirft einen solchen Merkzettel
jetzt, statt ihm zu folgen.
Uninstaller
Er raeumt jetzt auch, was der UPDATER angelegt hat - jede geladene Fassung
und installiert.json. Die kannte der Installer nicht; mit jeder Fassung
waere eine EXE mehr liegen geblieben. Gemessen vorher: zwei EXEs und der
halbe Datenordner.
pruef_installation.py
Installiert wirklich, legt eine Datei an wie der Updater es taete,
deinstalliert und sieht nach: Programmordner leer, Daten unangetastet.
Vorhandene Daten werden vorher gesichert und danach zurueckgelegt - ein
Pruefstand fasst niemandem seine Buchhaltung an.
pruef_gemeinsam_automatisch.py
Prueft, dass beide Programme denselben Ordner nennen und dass findet, was
das andere eintraegt - ohne dass jemand einen Pfad eingegeben hat.
Symbol
Das Windows-Symbol ist jetzt eine helle Kachel mit runden Ecken und dem
Zeichen darauf, in allen Groessen von 16 bis 256 - "so wie das rustdesk,
nur halt alle ecken in der rundung". Ein freigestelltes Logo ging auf
einem dunklen Desktop unter. Die zweite, aufgehellte ICO ist damit
ueberfluessig; das aufgehellte Logo IM Programm (Kopfzeile, Wasserzeichen)
bleibt unveraendert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Seine Ansagen
"das dunkel blaue machen wir in unserm tuerkis gruen und die andere seite
in weiss ... wenn es dunkel ist, und beim hellen machen wir weiss in
dunkel grau"
"ist mir zu klein geht unter mach es doch gros oben links weil da ist eh
platz"
"ja links ist auch gut beim rechnungstool"
Farben
Statt die Helligkeit umzukehren wird das Zeichen jetzt auf die
PROGRAMMfarben gesetzt: das Blaugruen wird Tuerkis (#06C6A4, wie
col_accent), die zweite Haelfte weiss im Dunkeln und dunkelgrau im Hellen.
Unterschieden wird nach SAETTIGUNG, nicht nach dem genauen Farbwert - so
bleibt die Zuordnung richtig, wenn die Vorlage einmal in einem anderen Ton
gezeichnet wird, und die weichen Kanten aus dem Skalieren gehen denselben
Weg wie die Flaeche, zu der sie gehoeren.
Position
Es stand klein (18 px) unten in der Statuszeile und ging dort unter. Jetzt
34 px oben links in der Werkzeugleiste - die traegt sonst nur den Knopf
"Anordnen", und der sitzt rechts. Im Steuerrechnungstool bleibt es rechts
in der Kopfleiste: dort stehen links die Bedienelemente, ein Logo davor
wuerde sie alle nach rechts schieben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Seine Ansage
"da gehen noch die popups auf dem video monitor auf"
"einfach so programmieren das die popup auf dem fenster mittig vom main
fenster auf gehen"
Was falsch war
An sechs Stellen stand `max(0, x)`. Das sollte verhindern, dass ein Dialog
ausserhalb des Bildschirms aufgeht - klemmt aber jede NEGATIVE Koordinate
auf null. Bei ihm steht der zweite Monitor links (x = -1920): jedes Popup
sprang damit zurueck auf den Hauptbildschirm, obwohl das Programm nebenan
lief. Dazu kamen feste Versaetze wie "+120+140", die mit der Fenstergroesse
nichts zu tun hatten.
theme.mittig(dialog, ueber)
Eine Stelle fuer alle Dialoge: mittig ueber dem Elternfenster, etwas
oberhalb der Mitte (ein Drittel statt der Haelfte), damit der Blick dort
ist und die Mitte des Formulars frei bleibt.
Das ist zugleich die Antwort auf die Monitorfrage: steht das Programm auf
dem zweiten Bildschirm, geht der Dialog dort auf - ohne dass irgendwo ein
Monitor gesucht werden muss. Die Sonderbehandlung im Testlauf konnte
deshalb entfallen; geblieben ist dort nur der Verzicht auf grab_set.
theme.auf_bildschirm(x, y, breite, hoehe)
Begrenzt eine Position auf den GESAMTEN Desktop ueber alle Monitore
(SM_*VIRTUALSCREEN) statt auf den Hauptmonitor. Das war die eigentliche
Absicht hinter dem alten max(0, ...).
Gemessen statt geglaubt: Dialogmitte und Fenstermitte liegen 8 Pixel
auseinander (Fensterrahmen), der Dialog liegt innerhalb des Fensters. Steht
als Pruefung in pruef_darstellung.py.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Seine Ansagen
"teste links auf dem 1080p monitor nicht mehr auf dem main ... weil da
schau ich nun video"
"und das steuertool hat noch kein installer und die rechnungstool hat das
icon nicht auf dem desktop etc selbe beim steuertool"
Pruefplatz
Der Platz fuer Pruefstandsfenster lag bei "Bildschirmbreite + 20", also
RECHTS neben dem Hauptmonitor. Bei ihm steht der zweite Monitor aber LINKS
(x = -1920), und rechts ist gar nichts: die Fenster gingen ins Leere. Sie
liefen, waren aber nirgends zu sehen - das hat beim Bildermachen einige Zeit
gekostet, ehe es auffiel.
theme.pruefplatz() sucht jetzt den nicht-primaeren Monitor und liefert
dessen Ecke. Gibt es keinen, bleibt es beim alten Verhalten. Das
Steuerrechnungstool hatte ueberhaupt keine Sonderposition - dessen
Pruefstandsfenster gingen mitten auf den Hauptbildschirm auf.
Verknuepfungen
Der Haken "Verknuepfung auf dem Desktop" ist jetzt vorgesetzt statt
abgewaehlt, und jede Verknuepfung traegt IconFilename ausdruecklich. Ohne
das erbt sie ihr Symbol vom UPDATER - und Windows zeigte an ihrer Stelle
das nackte Standardsymbol.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Seine Ansage
"waere nun noch gut das mann in den einstellungen einstellen kann hell
oder dunkel oder automatik das fehlt sollte eigendlich drin sein"
Er hatte recht: beide Farbpaletten lagen laengst in theme.py, das Programm
folgte aber starr dem Windows-Theme. Jetzt steht die Wahl unter
Einstellungen -> Darstellung und wirkt SOFORT, nicht erst beim Neustart.
Umschalten heisst neu bauen
Die Farben stehen an ueber sechzig Stellen fest in den Widgets. Sie einzeln
nachzuziehen hiesse, ein Verzeichnis aller eingefaerbten Widgets zu fuehren
- und wer beim naechsten neuen Widget vergisst, es einzutragen, hat einen
hellen Fleck im dunklen Fenster, den niemand mehr findet. Neu bauen kann
nichts vergessen.
⚠️ Dabei gingen im ersten Versuch Kundenname und Rechnungsnummer verloren:
die _build_-Methoden legen ihre StringVars SELBST an, nach dem Neubau
standen leere da. Gesichert werden jetzt ALLE Tk-Variablen des Fensters,
nicht eine gepflegte Auswahl - wer spaeter ein Feld hinzufuegt, muss nichts
eintragen.
Titelleiste, Symbol und Logo gehen mit
Die Titelleiste gehoert Windows; sie folgt ueber
DWMWA_USE_IMMERSIVE_DARK_MODE. _dark_titlebar konnte bisher nur DUNKEL und
zeigt jetzt in beide Richtungen - vorher behielt man beim Wechsel auf hell
eine schwarze Leiste ueber einem hellen Fenster.
Sein Befund: "die svg fuer das dunkle heller machen weil die geht nun
unter" und "icon musst wenn es dunkel ist mit aendern auf das hellere".
Gemessen sind es zwei Farben: Petrol #004860 und ein fast schwarzes
#181818 - letzteres liegt genau auf der Kachelfarbe #15171b. Umgestellt
wird nur die HELLIGKEIT (L -> 1-L, gedeckelt bei 0.78), Farbton und
Saettigung bleiben: sonst waere es nicht mehr sein Logo. Betrifft
Wasserzeichen, Kopflogo und das Fenstersymbol; die aufgehellte ICO entsteht
beim Bauen.
menueleiste.py
Die selbstgebaute Menueleiste ist jetzt eine eigene Datei und liegt in
beiden Programmen gleich - das Steuerrechnungstool hatte noch das native
tk.Menu, das sich unter Windows nicht einfaerben laesst.
pruef_darstellung.py
Prueft die drei Modi, dass beim Umschalten NICHTS verlorengeht, und dass
danach kein Widget mehr eine Farbe der anderen Palette traegt - genau der
helle Fleck, den man sonst nie wiederfindet.
Nebenbei: die beiden Dauertimer melden beim Beenden kein
"invalid command name ..._api_pumpe" mehr. Ein Log, in dem beim normalen
Beenden immer zwei Fehler stehen, liest irgendwann niemand mehr.
Fassung 1.8.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Recherchiert, NICHT gebaut - auf seine Ansage "gut das kann auf die todo
jetzt nicht".
Kern: Was sein Freund beschreibt, ist mit hoher Wahrscheinlichkeit nicht
MyHammer, sondern die Grosshandelsanbindung seiner Handwerkersoftware. Ein
Heizungsventil ist ein Artikel, kein Gewerk.
MyHammer hat keine oeffentliche API, und das oeffentliche Preisradar traegt
selbst den Satz, seine Preise seien "mit Hilfe von KI zusammengetragen" -
als Rechnungsgrundlage untauglich. Automatisierte Abrufe beantwortet die
Seite mit 403. Ein Crawler ist damit dreifach erledigt: der Inhalt taugt
nicht, sie sperren technisch, und § 87b UrhG steht dagegen.
Der Weg, der funktioniert: DATANORM einlesen (offline, kostenlos, die Datei
bekommt der Betrieb von seinem Grosshaendler), spaeter Open Masterdata fuer
Tagespreise. Dazu die eigene Rechnungshistorie fuer die Arbeitszeit - die
Daten liegen schon im Nummernbuch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Befund
"die namen und datums bloecke koennen schmaler werden sind zu breit und
verdrengen die posten block"
Gemessen stimmte das genau: die sieben Spalten der Leistungstabelle
brauchten 743 Pixel, sichtbar waren 618 - "E.-Preis" und "Gesamt" standen
ausserhalb des Bildes.
Wo die Breite wirklich herkam
An den Feldbreiten zu drehen half NICHTS. Die Arbeitsflaeche verteilte
ueber eine uniform-Gruppe streng im Verhaeltnis 4:3; die Formularspalte war
so breit, weil das Gewicht es vorschrieb, nicht weil der Inhalt es
brauchte - 433 Pixel, ob die Felder nun 38 oder 22 Zeichen breit waren.
Jetzt bekommt die Formularseite eine feste Mindestbreite und kein Gewicht,
die Liste allen uebrigen Platz. Wird das Fenster groesser, waechst allein
die Tabelle - genau dort nuetzt Breite, waehrend ein 600 Pixel breites Feld
fuer eine Postleitzahl nichts besser macht. 433 -> 329.
Dazu passend
- Startbreite 1060 -> 1180 und Mindestbreite 900 -> 1080. Unterhalb von
1080 passen die sieben Spalten selbst dann nicht nebeneinander, wenn die
Textspalte auf ihrer Mindestbreite steht.
- Die Eingabefelder links auf das Mass gekuerzt, das sie wirklich brauchen
(22 statt 38 Zeichen; Datum 12 statt 18). Das senkt die Mindestbreite der
Formularseite - erst dadurch ist sie so klein moeglich.
- "Anreise (TT.MM.JJJJ)" -> "Anreise". Die Form steht jetzt einmal als
graue Zeile unter dem Block. Als Platzhalter IM Feld waere sie
gefaehrlich: ein nicht geloeschter Platzhalter stuende als Anreisedatum
auf der Rechnung.
- Der Textumbruch der Leistungsspalte folgt jetzt ihrer echten Breite. Fest
eingetragen schnitt er den Text ab, sobald die Spalte schmaler wurde.
pruef_spaltenbreiten.py
Prueft ueber die ganze erlaubte Spanne, dass alle Spalten hineinpassen, die
Formularseite lesbar bleibt und NICHT mitwaechst, und der Umbruch zur
Spalte passt. Kein anderer Pruefstand haette das gefunden: es stuerzt
nichts ab und rechnet nichts falsch - man sieht die Preise nur nicht.
Er hat sofort einen zweiten Fehler gefunden: beim Aendern der
Fenstergroesse lief der Umbruch nicht hinterher, weil ich abgebrochen
hatte, sobald sich mein eigener Wert nicht mehr aenderte. Die Spalte kann
aber breiter werden, ohne dass ich etwas aendere. Abbruch jetzt, wenn zwei
Messungen dieselbe Spaltenbreite ergeben.
Fassung 1.7.1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Der Fehler
Das Release 1.7.0 war angelegt, die EXE hing dran - und der Updater haette
trotzdem nie etwas gefunden. Der Anhang hiess "Rechnungstool-1.7.0.exe",
gesucht wird aber "Rechnungstool.exe" (version.EXE_NAME). Die Nummer kommt
erst beim Ablegen dazu.
Das Tueckische: nichts schlaegt fehl. Der Updater meldet freundlich
"Aktuell. Kein Update noetig." und startet die alte Fassung. Ein Update,
das stumm nie ankommt, ist schlimmer als eines, das laut scheitert.
Zweiter Fund derselben Runde: die Pruefsummen standen nur im Release-TEXT.
Der Updater liest sie aus einer angehaengten Datei <exe>.sha256. Ohne die
prueft er nur die Groesse - und eine verfaelschte Datei hat genau die
richtige Groesse.
bauen.py legt jetzt release/ an
Genau die vier Dateien, die ans Release gehoeren, unter genau den Namen,
unter denen der Updater sie sucht. Damit haengt niemand mehr etwas
Falsches an.
pruef_release.py
Prueft gegen das echte Repository: gibt es zur Nummer aus version.py ein
Release, heisst der Anhang exakt wie EXE_NAME, haengt die Pruefsumme dran,
ist es die der zuletzt gebauten Datei - und findet passendes_release() es
auch. Ohne Netz wird uebersprungen statt rot gemeldet.
muster/ ist raus
Der Gast darin ist erfunden, der Betrieb nicht: IBAN, Steuernummer und die
private Rufnummer des Auftraggebers stehen im Briefkopf. In ein
oeffentliches Repository gehoert das nicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Logo
Das Programmlogo steht jetzt klein neben "powered by ravokk" - dort sitzt
die Kennung des Werkzeugs, nicht die des Kunden. Eine eigene Kopfzeile hat
dieses Programm nicht; oben sind die Reiter.
bauen.py
Der Weg von der Quelle zur Setup-Datei war eine Folge von Befehlen, die
jemand von Hand eintippen musste. Wer einen Schritt vergisst, liefert eine
Setup-Datei mit einer alten EXE aus - und sieht es ihr nicht an. Genau das
ist heute passiert. Jetzt ist es ein Befehl, die Nummer kommt aus
version.py, und am Ende stehen die SHA256-Summen fuer den Release-Text.
pruef_start_exe.py
Prueft, was kein anderer Pruefstand prueft: ob die GEBAUTE EXE ein
sichtbares Fenster zeigt. Alle uebrigen fahren das Programm aus der Quelle,
dort trat der Fehler nie auf. "Der Prozess laeuft" beweist dabei nichts -
er lief ja, waehrend nichts zu sehen war.
Aufgeraeumt
"Rechnung Campinghof.exe" ist weg; gebaute Dateien gehoeren ins Release,
nicht ins Repo. Die .spec-Dateien dagegen schon: ohne sie kann niemand die
EXE nachbauen, die der Updater ausliefert.
Fassung 1.7.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Bild (06.09.2026):
"der updater schaut beim starten erst: ist auf dem git link eine neue version
mit exe? wenn ja, erst laden, ersetzen und dann starten."
"einmal pruefen auf vollstaendigkeit."
"wir werden den namen mit nummer fuehren - wenn er merkt, eh warte mal, die
heisst anders, dann neu machen"
DER AUFBAU
Der Updater IST der Starter. Er laeuft, BEVOR das Programm laeuft - damit gibt
es das Windows-Problem "laufende EXE laesst sich nicht ersetzen" gar nicht.
Und weil die installierte Datei die Nummer im Namen traegt
(Rechnungstool-1.6.0.exe), wird ueberhaupt nichts ueberschrieben: eine neue
Fassung ist eine neue Datei. Die vorige bleibt liegen und ist damit die
Rueckfallebene, ohne dass jemand eine Sicherung anlegen muesste. Der Updater
sieht am NAMEN, was da ist - er muss keiner Angabe glauben.
version.py: die Nummer an EINER Stelle, im Code, in die EXE kompiliert. Sie
entspricht dem Release-Tag ohne "v". Zusaetzlich fuehrt der Updater ein eigenes
Buch (installiert.json); wer eine Datei umbenennt, verursacht hoechstens einen
ueberfluessigen Download.
BEIM BAUEN AUFGEFALLEN
/releases/latest liefert NICHTS, weil alle Fassungen als Vorabversion
gekennzeichnet sind - Forgejo laesst Vorabversionen dort aus. Der Updater nimmt
deshalb die Liste und sucht selbst die hoechste Nummer. Waere das erst beim
Nutzer aufgefallen, haette der Updater nie ein Update gefunden.
Beide Repositories sind oeffentlich - kein Token noetig. Ein Token in einer
verteilten EXE waere ohnehin keins.
VOLLSTAENDIGKEIT
Geladen wird in einen Zwischenordner. Erst wenn GROESSE und (falls das Release
eine .sha256 anhaengt) PRUEFSUMME stimmen, kommt die Datei in den
Installationsordner. Sonst wird verworfen und die vorhandene Fassung gestartet -
lieber kein Update als ein kaputtes.
PRUEFSTAND
pruef_updater.py prueft vor allem, wann NICHT aktualisiert werden darf: gleiche
Fassung, aeltere im Netz, kein passender Anhang, Entwurf, kein Netz, falsche
Groesse, falsche Pruefsumme. Und den wichtigsten Punkt: eine halbe Datei
erreicht den Installationsordner nie.
Alle 18 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Befund: "die software haengt nun am dialog popup" - und gleich darauf:
"haengen tut er nicht, nur deine pruefung; wenn ich auf aktiv druecke, geht es".
Zwei getrennte Fehler, beide meine:
1. Der Hinweis lief an after(0). Da ist das Hauptfenster noch nicht gezeichnet -
ein modaler Dialog daran wirkt eingefroren. Eine feste Wartezeit (150 ms) war
aber auch falsch: die erreicht ein Pruefstand nie, der keine Ereignisschleife
dreht, und pruef_api lief danach in die Zeitgrenze. Jetzt after_idle: laeuft,
sobald Tk nichts mehr zu zeichnen hat - fuer den Nutzer nach dem Zeichnen,
fuer den Pruefstand beim ersten update().
2. Der Dialog kam nicht nach vorn. Ein Hinweis, den man erst suchen muss, taugt
nichts. theme.py holt das Meldungsfenster jetzt nach vorn und nimmt den
Tastaturfokus (lift, kurz topmost, focus_force).
In der Aufgabenliste vermerkt: Updater als Starter (Schritt 4, mit den zwei
fehlenden Voraussetzungen Versionsnummer und Sichtbarkeit der Repositories),
Pcore-Design im Steuerrechnungstool (Schritt 5), und der ALTFEHLER in
pruef_wache - schon vor den heutigen Arbeiten rot, die Statuszeile wird vom
Ergebnis des Einlesens ueberschrieben.
Alle 17 Pruefstaende des Rechnungstools gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Wortlaut (06.09.2026):
"bei jedem oeffnen, solange wir die testen, ein popup, dass dies eine
experimentelle software ist und ich keine haftung fuer irgendwelche falsch
ausgestellten uebernehme. der user muss selbst pruefen ueber den vorschau
button auf beiden tools, ob das alles seine richtigkeit hat. wenn nicht, auf
dem link eine issue erstellen. und das alles auch noch mal in die info mit
signatur: user hat dies gelesen und zur kenntnis genommen. 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."
WIE ES GEBAUT IST
hinweis.py - neu, liegt spaeter in BEIDEN Programmen gleich (wie gemeinsam.py):
Text, Fehleradresse je Programm, Kenntnisnahme lesen/schreiben, Signaturzeile.
* Der Hinweis kommt als ERSTES beim Start. Erst nach der Bestaetigung faehrt
der uebrige Start an (_start_fortsetzen) - vorher wird nichts geoeffnet,
nichts gefragt, nichts geladen. Sonst stuenden beim Ablehnen noch Timer in
der Warteschlange, die auf ein Fenster zugreifen, das es nicht mehr gibt.
* "Nein" beendet das Programm. Es wird NICHTS vermerkt - ein Nein, das
trotzdem als Kenntnisnahme gespeichert wuerde, waere eine Luege.
* "Ja" wird mit Benutzername, Zeitpunkt und Fassung in der config vermerkt.
Das ist die Signatur.
* Aendert sich der TEXT (FASSUNG), wird erneut gefragt. Eine Zustimmung gilt
fuer das, was dagestanden hat, nicht fuer alles Kuenftige.
* Unter Hilfe -> Info steht derselbe Text dauerhaft, mit der Signaturzeile.
UMBENENNUNG
Das frueher "beherbergungssteuer" genannte Repository heisst jetzt
Steuerrechnungstool:
https://git.pcore.de/TheMockTv/Steuerrechnungstoolhttps://git.pcore.de/TheMockTv/rechnungstool
In beiden READMEs vermerkt, samt "git remote set-url". Der LOKALE Ordner heisst
weiter beherbergungssteuer - der Pfad steht in Pruefstaenden und Spec-Dateien
fest, das ist ein eigener Schritt.
PRUEFSTAND
pruef_hinweis.py prueft die ganze Strecke: Hinweis kommt und nennt Haftung,
Vorschau und Adresse; Nein schliesst das Fenster und vermerkt nichts; neuer
Start fragt wieder; Ja hinterlaesst Fassung, Zeit, Benutzer und eine lesbare
Signaturzeile; danach wird nicht mehr gefragt; bei geaenderter Fassung wieder.
Alle 17 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Grund, woertlich (06.09.2026):
"ich will fuer den pruefer sichtbar machen: ich wollte das so. die software
dokumentiert das, und damit wird es nachvollziehbar, weil es einen checkmark
nun gibt in der sqlite" - "das soll alles von anfang bis ende pruefbar sein"
WAS PASSIERT
Vor dem Schreiben eines Belegs fragt das Programm einmal ausdruecklich nach.
Der Text nennt, was gleich entsteht und was daran bindend ist:
Rechnung: ausgestellt im Sinne des § 14 UStG; die Nummer ist danach vergeben
und darf kein zweites Mal verwendet werden (§ 14 Abs. 4 Nr. 4 UStG);
zehn Jahre aufzubewahren (§ 147 Abs. 3 AO); nachtraeglich nicht
aenderbar (§ 146 Abs. 4 AO) - eine falsche Rechnung wird nicht
ueberschrieben, sondern storniert.
Angebot: unverbindlich, KEINE Rechnung, weist keine Steuer aus, verbraucht
keine Rechnungsnummer, wird nirgends gebucht.
Storno/Korrektur und Berichtigung haben ihre eigene Rueckfrage - sie wird nicht
verdoppelt (ein Schritt pro Fenster).
DIE SPUR
Ein Popup allein waere fuer einen Pruefer wertlos, es hinterlaesst nichts.
Deshalb bekommt die Tabelle "nummern" im gemeinsamen Buch eine Spalte
"freigabe": der ISO-Zeitstempel der ausdruecklichen Freigabe, an der
Rechnungsnummer haengend. Bestehende Buecher werden per ALTER TABLE nachgezogen;
alte Zeilen bleiben LEER - dort wurde nie gefragt, und ein nachtraeglich
erfundener Zeitstempel waere schlimmer als keiner.
gemeinsam.py liegt in beiden Programmen gleich und wurde mit hinuebergezogen.
PRUEFSTAND
pruef_freigabe.py prueft beide Richtungen, weil jede einzeln wertlos waere:
NEIN -> kein PDF, Zaehler unveraendert, Nummer im Feld unveraendert,
Nummernbuch leer
JA -> genau ein PDF, eine Nummer im Buch, Freigabe gesetzt und ein ISO-Datum
Die sieben Pruefstaende, die Belege erzeugen, melden die Freigabe jetzt
ausdruecklich an (pruefsteuerung bricht bei unangemeldeten Dialogen absichtlich
ab - kein stilles "ja").
Alle 16 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Das zweite Blatt selbst ist seit dem 05.09.2026 draussen. Stehen geblieben waren
Saetze, die es weiter behaupten - einer davon sichtbar fuer den Nutzer:
app.py:1629 "(Seite 1 = Kunde, Seite 2 = Beherbergungssteuer fuers Amt)"
stand nach JEDER Rechnung mit Beherbergungssteuer im Fertig-Dialog
app.py:1612 falscher Kommentar zum PDF-Aufruf
pdf_renderer.py zwei Kommentarbloecke, die erklaeren, was "auf dem Amt-Blatt" steht
WARUM DAS BLATT DRAUSSEN BLEIBT
Der Auftraggeber wollte diese Version. Ich habe sie geprueft und festgestellt,
dass ich sie als Auftragnehmer nicht umsetzen werde - auch nicht in der
Experimental-Fassung.
Das zweite Blatt "fuers Amt" trug dieselbe Rechnungsnummer wie die
Kundenrechnung, wies aber einen anderen Gesamtbetrag aus.
- § 14 Abs. 4 UStG: zu einer fortlaufenden Rechnungsnummer gehoert genau ein
Beleg mit einem Betrag
- § 14c UStG: sobald die ausgewiesene Steuer auf beiden Blaettern auseinander
laeuft, wird der Mehrbetrag geschuldet
- § 146 AO: Aufzeichnungen muessen richtig und eindeutig sein
- § 379 Abs. 1 Nr. 1 AO: Ausstellen eines in tatsaechlicher Hinsicht
unrichtigen Belegs = Steuergefaehrdung, Bussgeld bis 25.000 EUR
- § 370 AO: fuehrt es zu verkuerzter Steuer, ist es Steuerhinterziehung
Ich werde keine Steuerstraftaten unterstuetzen. Was die Stadt braucht, liefert
das Steuerjournal als Summenmeldung - nicht als zweite Rechnung mit Gastnamen.
Vorgabe des Auftragnehmers: § sicher, mehr nicht.
Alle 15 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Seine Ansage: "rechnungsdaten oben links, darunter rechnung an kunde, den
tabellen kasten rechts - oder noch besser dnd einbauen" und dann genauer: "wie
bei opnsense hast so einen kleinen knopf, dann drueckst drauf, ab da kannst du
schieben, dann hast du einen save button, und dann ist die fest".
- Seine Anordnung ist die Vorgabe (flaeche.STANDARD).
- Verschieben muss man EINSCHALTEN. Ohne den Knopf sitzt alles fest, damit beim
taeglichen Arbeiten nichts verrutscht. Im Modus stehen "Speichern" und
"Verwerfen" daneben; erst Speichern schreibt in die config.
- Waehrend des Ziehens haengt ein Etikett am Zeiger und der gegriffene Kasten
wird blass - vorher sah man nur das Ziel und es wirkte "statisch" (sein Fund).
- Getauscht statt frei abgelegt: freies Verschieben auf Pixel braucht Regeln
fuer Ueberlappung, Groesse und leere Zellen, und am Ende steht das Fenster
schief. Ein Tausch ist in einem Griff erledigt.
- Der freie Platz folgt dem Kasten mit der langen Liste. Feste Gewichte gingen
nicht: die Liste wandert, und der Kundenkasten wurde unten abgeschnitten.
- Eine unbrauchbare Anordnung in der config faellt auf die Vorgabe zurueck.
Icons: die Material Symbols vom Hub (M:/projekte/material-symbols), einmal nach
PNG gewandelt und zur Laufzeit eingefaerbt (icons.py) - eine Datei je Icon,
nicht drei Fassungen fuer hell/dunkel/Akzent.
⚠️ Ein PhotoImage gehoert GENAU EINEM Tk-Fenster. Der Icon-Cache reichte Bilder
an das naechste weiter, sobald ein Pruefstand Journal und Rechnungstool
nacheinander faehrt: 'image "pyimage1" doesn't exist'. Das Fenster wird jetzt
mitgegeben, tk._default_root reicht dafuer nicht.
Neu: pruef_flaeche.py. Alle 15 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Seine Ansage: "wenn, dann musst du die felder alle gleich machen, wo man was
eingeben kann". Die ttk-Felder hatten die neuen Farben schon, die klassischen
Widgets nicht: die beiden Danktext-Felder und die Auswahlliste im Storno
standen ohne Rahmen und mit dem alten dunklen Grund da.
Die Feldfarben stehen jetzt in der Palette (col_field, col_feldrand) statt
mitten in der Stil-Definition, und `feld_optionen()` gibt sie an tk.Text und
tk.Listbox weiter - eine Quelle fuer alle. Eine Auswahlliste bekommt dabei
keine Einfuegemarke: die Option kennt sie nicht und bricht daran ab (gefunden,
weil drei Pruefstaende sofort rot wurden).
Alle 14 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Fund: "in rechnung an kunden finde ich die felder auch zu dunkel". Der
Grund war derselbe wie in der Leistungsliste - das Feld lag fast auf der Farbe
der Kachel, man sah nicht, wo etwas einzutragen ist.
Jetzt gilt fuer JEDES Eingabefeld und die Auswahlliste derselbe Satz Farben
(#2c2e32 Grund, #57585b Rahmen, beim Tippen Akzent) - aus einer Quelle
gerechnet, nicht zweimal gepflegt. Liste.TEntry liest dieselben Werte.
Alle 14 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Fund: "rechnungstool campinghof bartl ist falsch, das ist das rechnungstool
powered by ravokk". Stimmt - der Firmenname des KUNDEN gehoerte nie in den
Fenstertitel. Das Werkzeug steht bei jedem Betrieb, der es einsetzt, und traegt
seinen eigenen Namen; der Kunde steht auf der RECHNUNG.
Name und Marke stehen jetzt in config (PROGRAMM, MARKE) - Fenstertitel, das
Info-Fenster und die Fussleiste lesen von dort. Alle 14 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Fund: "bei der anzahl waere ein heller bg oder ein heller rahmen besser".
Die Felder standen auf den Zeilenstreifen und gingen darin unter - man sah
nicht, wo man tippt. Eigener Stil (Liste.TEntry): hellerer Grund, deutlicher
Rahmen, beim Tippen Rahmen und Fuellung in der Akzentfarbe.
Alle 14 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Fund: "schau mal zeile 1 und die ust, passen die hoehen nicht" und "das
grau muss heller als der bg sein".
Beides stimmte. Der Zebrastreifen sass an jedem LABEL einzeln - dadurch stand
neben der zweizeiligen Position 1 ein Satz halbhoher Kaestchen mit Luecken
darueber und darunter, und die USt-Zelle sass mittig statt in der Zeile. Jetzt
liegt der Hintergrund als EIN Streifen ueber alle Spalten, die Zellen sitzen
darin und fuellen die volle Zeilenhoehe. Die Kopfzeile bekommt denselben
Streifen, sonst stand der zweizeilige "Anzahl Tage/Menge" als Kasten heraus.
Der Streifen ist von #1b1e24 auf #232830 gegangen - vorher war der Unterschied
zum Kachelgrund kaum zu sehen, und genau dafuer ist er da.
Alle 14 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Fund, zweimal: "er hebt den aktiven tab nicht hervor, der inaktive ist der
hervorgehobene". Gemessen statt geraten: der clam-Standard faerbt die
Reiterflaeche #bab5ab, und weder background noch lightcolor/darkcolor haben das
im laufenden Fenster zuverlaessig ueberschrieben.
Statt weiter daran zu drehen ist die Leiste jetzt selbst gebaut (reiter.py) -
und sieht dabei aus wie die Reiter in seinen Web-Oberflaechen: flacher Knopf,
der offene in Akzentfarbe, fett, mit Balken darunter und der Flaeche der Kachel.
Nach aussen verhaelt sie sich wie ein Notebook: add, select, index("current"/
"end"), tab(i, "text") und <<NotebookTabChanged>>. Deshalb bleiben der
Storno-Weg (korrektur.py waehlt select(1)) und pruef_montage unveraendert -
alle 14 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Fund: "der ohne gemeinsamen ordner popup ist noch hell" und "er hebt den
aktiven tab nicht hervor, der inaktive ist der hervorgehobene".
- Meldungen und Ja/Nein-Fragen kommen jetzt aus eigenen Fenstern im Theme
(MeldungMixin). messagebox sind Fenster von WINDOWS - sie bleiben hell, egal
was das Programm einstellt. Dieselbe Erfahrung steckt schon in der eigenen
Menueleiste. 49 Aufrufe umgestellt; das Ordner-Fenster meldet ueber das
Hauptfenster mit.
⚠️ Im Pruflauf laeuft beides weiter ueber messagebox - dort haengen die
Pruefstaende ihre Antworten ein. Sonst warten sie auf einen Klick.
- Reiter: der OFFENE ist jetzt der hellere (Kachelfarbe), die ruhenden liegen
auf dem dunklen Grund. Vorher war es andersherum.
- config.TESTLAUF ist eine FUNKTION geworden: Pruefstaende importieren config
oft, bevor sie CAMPINGHOF_TEST setzen - der einmal berechnete Wert stand dann
den ganzen Lauf auf False, und das Programm oeffnete echte Dialoge, auf die
niemand klickt. Genau daran hing pruef_api.
- Der Erststart-Dialog hat einen eigenen Schalter (_erststart_zeigen), statt am
ganzen Pruefschalter zu haengen: ein Pruefstand kann IHN zeigen, ohne dass
alle anderen Meldungen wieder echte Fenster aufmachen.
- Pruefstand-Fenster stehen nicht mehr in der Taskleiste (-toolwindow): sie
liegen neben dem Bildschirm, und ein Eintrag, den man anklickt, ohne dass
etwas erscheint, ist schlimmer als gar keiner.
- pruef_nummernsperre drueckt jetzt den Radioknopf "Meine Zaehlung
weiterfuehren", statt nur ins Zahlenfeld zu schreiben - das Feld ist ohne
diesen Klick gesperrt, wie beim Benutzer auch.
Alle 14 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Seine Ansage: "schau dir unsern fivem server an und unsere webseiten, dann
weisst du wie mein design ist" - also die Palette aus _ui_www und der
Adminpage: Tuerkis #06C6A4 auf gestuften dunklen Flaechen (#0d0e10 Grund,
#15171b Kachel), Raender #25282e, Text #e8e8ea / #8a8e96, Rot #e85a5a.
Das PROGRAMM traegt diese Handschrift, die RECHNUNG die Farbe der jeweiligen
Firma - das Blatt gehoert dem Kunden, nicht uns.
- Roboto wird mitgeliefert und nur fuer den Prozess geladen
(AddFontResourceEx mit FR_PRIVATE): keine Installation, keine Adminrechte,
nach dem Schliessen ist der Rechner wie vorher. Fehlt sie, bleibt es bei
Segoe UI. Die Schriftdateien gehen mit in die exe (spec).
- Abschnitte sind Kacheln mit Ueberschrift statt grauer Rahmen.
- Knoepfe im Glas-Stil wie in seinen Oberflaechen: getoente Flaeche, Rand in
Akzent-Transparenz. Tk kann kein backdrop-filter - die Toenung wird
ausgerechnet (theme.mischen), das Ergebnis ist auf einfarbigem Grund
identisch. "PDF erstellen" fuehrt, "Storno" ist rot.
- Zebrastreifen in der Leistungsliste: bei 17 Zeilen verrutscht man sonst.
- Kein expand am offenen Reiter mehr - dadurch sprang beim Umschalten die
ganze Leiste ("das taben macht den aktiven kleiner").
- Das Fenster fuer den gemeinsamen Ordner stand als einziges noch in Weiss da:
es kam nie durch _style_dialog.
Dazu ein Fund, der nichts mit dem Design zu tun hat: die Pruefstaende erbten
"nummer_eingerichtet" aus der config DIESES Rechners. Sobald hier jemand die
Nummer einmal einrichtet, liefen sie in einen anderen Zweig und wurden rot -
ohne dass sich am Programm etwas geaendert haette. Sie legen den Zustand jetzt
selbst fest. Alle 14 gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Mit den neuen Bild- und Farbzeilen reichten 640 px nicht mehr: rechts fielen
"Entfernen" und unten "Speichern" aus dem Fenster - gesehen im laufenden
Programm, nicht im Code. Der Dialog richtet sich jetzt nach dem Bildschirm
(700-900 breit, hoehenbegrenzt) und die Felder sind etwas schmaler.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Fund: "wenn du testest, dann mach den tab auch zu". Ein Lauf ueber die
Pruefkette schreibt Dutzende Rechnungen, und nach jeder oeffnete das Programm
den Betrachter - am Ende klickt der Mensch am Rechner dreissig Fenster weg,
die er nie aufgemacht hat.
Geoeffnet wird jetzt nur noch an EINER Stelle (_oeffne_datei), und die haelt im
Testlauf still und meldet stattdessen, was zu sehen gewesen waere. Die zweite
Stelle, die frueher direkt os.startfile rief, geht ebenfalls darueber.
pruef_vorschau prueft jetzt den zurueckgegebenen Pfad - und dass wirklich
nichts aufgemacht wurde. Alle 14 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Fund mitten im Lauf: "pack doch die maus nicht immer auf das programm,
weil ich kann dir sonst nichts schreiben". Im Testlauf oeffnen Fenster und
Dialoge jetzt bei x = Bildschirmbreite + 20 - auf dem zweiten Monitor, wenn
einer da ist -, und kein Dialog ruft mehr grab_set.
Unsichtbar machen (withdraw) waere falsch: dann sieht niemand mehr, was der
Pruefstand tut - und ein weggenommenes Fenster baut seine Dialoge nicht mehr
richtig auf.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Das zweite Blatt "fuer das Amt" trug DIESELBE Rechnungsnummer, aber einen
anderen Gesamtbetrag. Mehrere Blaetter zum selben Umsatz mit verschiedenen
Endbetraegen sehen aus, als werde ueber verschiedene Umsaetze abgerechnet -
bei einer Pruefung ein Problem. Ersatzlos entfallen; was die Stadt braucht,
rechnet das Steuerjournal aus den Kenndaten der PDF. Mit dem Blatt fielen
_AmtMarke, gesamtrechnung_betont und der doppelte Renderlauf weg.
Die API kann das Fenster jetzt BEDIENEN - mit Schluessel wie bei einem SSH-Key:
32 Byte Zufall in api_schluessel.txt im gemeinsamen Ordner, den nur das
Steuerjournal und der Betreiber lesen. Ohne gueltigen Schluessel kein Zutritt
(compare_digest). ping und schau_nach bleiben schluessellos, sonst kann ein
aelteres Journal nicht mehr anklopfen. Was einen BELEG schreibt, geht nur im
Testlauf - eine Fernbedienung, die echte Rechnungen ausstellt, hat in einer
Buchhaltung nichts zu suchen. api.py liegt wie immer in beiden Programmen.
Der Erststart fragte nach "der zuletzt vergebenen Nummer" - das holt niemanden
ab, der bei 0 anfaengt. Jetzt zwei Wege: bei 0 anfangen ODER die eigene
Zaehlung weiterfuehren. Unter das schon Vergebene kommt keiner der beiden.
pruef_nummernsperre bediente den Dialog nur halb: nach einer abgelehnten Zahl
bleibt er offen (so soll er), niemand drueckte "Spaeter" - das Programm wartete
in wait_window() ewig. Das war schon vor diesem Umbau so.
Neu: pruef_api.py. Alle 14 Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Logo und ein Fussstreifen sind austauschbar (bilder.py: das gewaehlte Bild wird
KOPIERT, ein Pfad auf den Desktop waere morgen tot), die Akzentfarbe kommt aus
den Einstellungen - Hex-Feld und Farbwaehler. Firmenname, Anschrift, Bank und
Steuernummer bleiben gezeichnet; kein Bild kann eine Pflichtangabe verdecken.
Der Vorschau-Knopf zeigt das Blatt, bevor es eine Rechnung wird: kein
Nummernverbrauch, nichts im Rechnungsordner, "VORSCHAU - keine Rechnung" quer
ueber jeder Seite. Sonst kostet jeder Tippfehler einen Storno.
Neue Pruefstaende: pruef_bilder.py, pruef_vorschau.py
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
Sein Fund: "in der pdf wird der satz nicht angezeigt, da ist dann ein
sprung in den rechnungs nummern".
Die Schlusstexte waren komplett auf die Kundenseite beschraenkt ("auf dem
Amt-Blatt unnoetig"). Fuer Dank- und Zahlungstexte stimmt das. Fuer die
STORNO-Erklaerung nicht: auf Seite 2 stand dann eine Stornonummer
(2026-456) neben einer voellig anderen Rechnungsnummer (2026-342), und
das Blatt, das die Stadt bekommt, sagte nirgends warum.
Jetzt traegt auch das Amtsblatt den Satz - "hebt die Rechnung X vom
Y vollstaendig auf, ist bereits mit Z abgerechnet" bzw. bei Version C
"die Leistung wird nicht abgerechnet". Die Beherbergungssteuer-Zeile
bleibt dort weiterhin aussen vor, die gehoert da nicht hin.
pruef_nur_storno an die Verrechnung angepasst (das Steuerjournal laesst
Storno und aufgehobene Rechnung wieder mitzaehlen, sie heben sich
gegenseitig auf): unterm Strich 0 Naechte und 0,00 Euro, gezaehlt werden
aber beide Belege. A und B unveraendert gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017bEsoFUk16DfnNA7MjY36H
Seine Ansage: "des wegen sollte mann auch eine version c mit only storno
machen weil das gibt es auch".
Bisher gab es nur zwei Wege aus dem Storno:
A - es folgt eine neue Rechnung
B - die Leistung ist schon abgerechnet, Verweis auf die bleibende Nummer
Beide behaupten etwas, das im dritten Fall nicht stimmt: die Buchung ist
geplatzt oder die Rechnung war komplett irrtuemlich, es wird also gar
nichts abgerechnet. Mit A wartet der Gast auf eine Rechnung, die nie
kommt, und es waere unnoetig eine Nummer reserviert; mit B muesste man
auf eine Rechnung verweisen, die es nicht gibt.
C steht als dritter Knopf im selben Schritt (A, B, C, Abbrechen) - kein
zweiter Schritt, weil es nichts einzutippen gibt.
- keine Folgenummer reserviert (die fortlaufende Nummernfolge bekaeme
sonst eine Luecke, § 14 Abs. 4 Nr. 4 UStG)
- kein offener Vorgang, das Formular wartet auf nichts
- eigener Satz auf dem Blatt: "Die Leistung wird nicht abgerechnet - es
folgt keine weitere Rechnung. Bereits gezahlte Betraege werden
erstattet." Kein Wort von einer berichtigten Rechnung
- Kenndaten tragen "nur_storno": true, damit das Steuerjournal den Fall
auseinanderhalten kann
Pruefstand pruef_nur_storno.py (neu, 15 Pruefungen) drueckt den Knopf am
echten Fenster und sieht danach Blatt, Kenndaten, Nummernzaehler und das
Steuerjournal an (Rechnung + Storno stehen drin, gemeldet wird nichts).
pruef_storno (A), pruef_doppelte_leistung (B), pruef_kette und
pruef_nummernsperre laufen unveraendert gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017bEsoFUk16DfnNA7MjY36H
Sein Fall: 'der typ hat mit dem alten programm einfach eine neue rechnung
gemacht, nun hat der die gleiche rechnung 3-4 mal drin, aber mit anderen
nummern'. Die Ueberzaehligen muessen storniert werden - und ihr Storno muss auf
die Rechnung verweisen, die BLEIBT, nicht auf eine neue.
Nach dem Auswaehlen der Rechnung fragt das Storno jetzt:
A - es folgt eine neue Rechnung (der bisherige Weg)
B - die Leistung ist schon abgerechnet
Zweistufig, weil er es so braucht: 'ich druecke b und ERST DANN kann ich die
nummer eingeben ... das ein opa, immer step by step, nicht alles auf einmal,
wie bei kindern'. Schritt 1 zeigt nur die Wahl, Schritt 2 das Nummernfeld, mit
Zurueck.
Geprueft wird nicht nur, OB es die Nummer gibt, sondern auch, ob der INHALT
passt (sein Zusatz): anderer Gast = geht nicht, anderer Zeitraum oder Betrag =
Nachfrage. Auf dem Blatt steht dann 'Schon abgerechnet: 2026-001' statt
'Berichtigte Rechnung', dazu der Satz, dass die Leistung versehentlich doppelt
in Rechnung gestellt wurde und keine weitere Rechnung folgt. Es wird keine
Nummer reserviert und kein Vorgang offen gehalten.
Pruefstand pruef_doppelte_leistung.py: faehrt das echte zweistufige Fenster,
prueft die Inhaltspruefung einzeln und am Ende das Steuerjournal - EIN
Aufenthalt (3 Naechte, 39,00) statt zwei.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYeAfLtccFrbU3MTj1MMTx
Seine Ansage: 'er sollte es aber, wenn er neu startet, weil er ja eine Excel
hat, im Popup einmal setzen - das waere fair. Aber ab da ist die fest, nicht
aenderbar ausser ueber Storno und Berichtigen.'
Beim ersten Start fragt das Programm einmal, wo die Nummer weiterlaufen soll.
Vorgeschlagen wird, was Ordner und Nummernbuch hergeben; niedriger nimmt es
nicht an, und vor dem Uebernehmen wird noch einmal gefragt. Danach steht
nummer_eingerichtet in der config, und die Einstellungen zeigen den Zaehler nur
noch an - mit dem Verweis auf Storno und Berichtigung (§ 14 Abs. 4 Nr. 4 UStG,
§ 146 Abs. 4 AO).
Der Jahreswechsel bleibt automatisch: im neuen Jahr faengt es wieder bei 001 an,
und die Rechnungen des Vorjahres heben nichts an. Auch das steht jetzt im
Pruefstand ('beim jahreswechsel sollte die nach dem - zeichen auf 000 gehen,
weil neues jahr').
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYeAfLtccFrbU3MTj1MMTx
Sein Einwand: 'er kann jetzt bescheissen, indem er das in den einstellungen
aendert' - und sein Vorschlag: 'dann koennte man noch die pdfs nutzen als
referenz, die haben auch die nummern'.
Der hoechste je vergebene Stand kommt jetzt aus ZWEI Quellen:
* den PDFs im Rechnungsordner (der Dateiname traegt die Nummer - kein Oeffnen
noetig)
* dem gemeinsamen Nummernbuch (kennt auch Nummern OHNE PDF, den Altbestand)
Der Zaehler in der config taugt nicht als Wahrheit, er ist ja gerade das, was
sich aendern laesst. Deshalb: beim Nummernvorschlag wird ein zurueckgedrehter
Zaehler stillschweigend wieder angehoben, und die Einstellungen nehmen keinen
Wert mehr an, der hinter dem Vergebenen liegt - mit Meldung, welcher Stand
schon erreicht ist und woher er kommt. Nach VORN bleibt er frei: eine
lueckenlose Folge verlangt das Gesetz nicht, jede Nummer aber nur einmal
(§ 14 Abs. 4 Nr. 4 UStG).
Pruefstand pruef_nummernsperre.py: 17 Pruefungen - zurueckgedrehter Zaehler,
Einstellungen vor und zurueck, nur Ordner, nur Buch, und beides weg.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYeAfLtccFrbU3MTj1MMTx
"und rechnungsnummer ist dann angebotsnummer - dann ist das auch weg": das Feld
heisst jetzt Angebotsnummer, und der Hinweis "die Nummer oben ist keine
Rechnungsnummer" konnte damit entfallen. Auf dem ganzen Blatt steht das Wort
Rechnung nur noch im Pflichthinweis, der gerade sagt, dass es keine ist. Der
Pruefstand prueft beides ausdruecklich.
"oder in ein extra ordner gepackt werden": die Angebote landen in "Angebote"
neben den Rechnungen. Der Rechnungsordner ist die Buchhaltung, dort haben sie
nichts zu suchen. Weggeworfen werden duerfen sie trotzdem nicht - ein
abgesandtes Angebot ist ein Handelsbrief (§ 257 HGB) - deshalb ein Unterordner
und kein anderer Ort. Die Nummernsuche schaut jetzt dort nach, und das
Steuerjournal ueberspringt sie ohnehin ueber art="angebot".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYeAfLtccFrbU3MTj1MMTx
Seine Frage war: "was ist rechtssicher, kostenvoranschlag oder proforma - ich
bin kein jurist, deswegen sage ich ja § schauen". Nachgesehen:
* Kostenanschlag ist ein Begriff des WERKvertrags (§ 632 Abs. 3 BGB "Ein
Kostenanschlag ist im Zweifel nicht zu verguten", § 649 BGB) - das ist der
Handwerker-Fall. Bartl vermietet, das passt nicht.
* "Proforma-Rechnung" steht in keinem Gesetz (Zoll- und Exportbegriff) und
traegt als Einziges das Wort "Rechnung".
* Das Angebot ist § 145 BGB: gebunden ist man daran, "es sei denn, dass er die
Gebundenheit ausgeschlossen hat" - deshalb steht "unverbindlich" drauf.
* Im Camping- und Hotelgeschaeft ist "unverbindliches Angebot" der uebliche
Begriff (auch in den Muster-AGB des Branchenverbands BVCD).
Seine Entscheidung: "dann mach angebot draus, ist das gaengigste" - Begruendung
"dann ist der name rechnung weg und erweckt nicht den anschein das man eine
bekommen hat".
Deshalb heisst es jetzt ueberall Angebot: Ueberschrift "Unverbindliches
Angebot", "Angebot an:", "Angebot-Nr.: 2026-A001" und "Datum:" statt
"Rechnungsdatum:" - das Wort Rechnung kommt im ganzen Kopf nicht mehr vor, und
genau das prueft pruef_angebot.py auch nach. Eigene Reihe 2026-A001, Dateiname
Angebot_..., eigener Zaehler (angebot_jahr/angebot_zaehler).
Steuerlich aendert der Name nichts (§ 14 Abs. 1 S. 1 UStG: die Bezeichnung des
Dokuments ist gleichgueltig) - die Sicherheit kommt weiter vom fehlenden
Steuerausweis, siehe UStAE 14c.1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYeAfLtccFrbU3MTj1MMTx
Seine Erklaerung: 'es geht nur darum das er seinen kunden wie eine art
kostenvoranschlag/proforma rechnung das die wissen ah das kommt auf mich zu und
wenn die ja sagen er dann die richtige machen tut'. Ueberschrift jetzt
'Proforma-Rechnung (Kostenvoranschlag)', im Text 'Dies ist ein
Kostenvoranschlag' - damit steht die Natur des Blattes ganz vorn und nicht das
Wort Rechnung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYeAfLtccFrbU3MTj1MMTx
MINI-API (seine Ansage: "die muessen ja sehen das die beiden auf sind"):
Jedes Programm macht beim Start einen winzigen Server auf 127.0.0.1 mit einem
Port, den das Betriebssystem aussucht, und traegt Port und PID in die
gemeinsame SQLite ein - die ist das Telefonbuch. Zwei Nachrichten reichen:
"ping" wird mit "hallo, bin noch da" beantwortet, "schau_nach" ist der Push.
Der Push traegt KEINE Daten. Er sagt nur "schau in die sqlite" - dort steht die
Meldung ohnehin. Eine Wahrheit, und wenn der Anruf nicht durchgeht, findet das
andere Programm die Meldung beim naechsten Nachsehen.
Gebunden wird nur auf Loopback: von aussen ist nichts erreichbar, Windows fragt
bei 127.0.0.1 nicht nach der Firewall, und die API fuehrt keine Befehle aus.
Kein Netzwerk-Server mit MySQL - dafuer braeuchte er Infrastruktur, die es
nicht gibt; auf verschiedenen Rechnern waere ohnehin ein anderer Bau noetig.
PROFORMA-RECHNUNG: ein Knopf, gewaehlt wird im Popup - die fruehere
Sicherheitsfrage IST diese Auswahl geworden (eine Frage statt zwei).
Seine Bedingung: "musst nur auf passen das der rechner dann die rechnungs
nummer nicht aendert". Die Proforma laeuft in einer EIGENEN Reihe (2026-P001);
Nummernfeld, Rechnungszaehler, Nummernbuch und die Meldung ans Journal bleiben
unberuehrt. Genau das prueft pruef_proforma.py zuerst.
An den Quellen geprueft, nicht an Ratgeberseiten:
* § 14 Abs. 1 S. 1 UStG / UStAE 14.1 - Rechnung ist jedes Dokument, mit dem
abgerechnet wird, "gleichgueltig, wie dieses Dokument im Geschaeftsverkehr
bezeichnet wird". Das Wort "Proforma" allein schuetzt also nicht.
* § 14c Abs. 2 UStG - wer Steuer gesondert ausweist, ohne berechtigt zu sein,
schuldet den Betrag; Korrektur nur mit Antrag und Zustimmung des Finanzamts.
* § 14 Abs. 4 Nr. 4 UStG - die fortlaufende, einmalig vergebene Nummer gilt
fuer Rechnungen.
Deshalb: Ueberschrift "Proforma-Rechnung", der Satz "keine Rechnung im Sinne
des § 14 UStG ... nicht zu bezahlen", "Proforma an:" statt "Rechnung an:", die
Nummer ausdruecklich als KEINE Rechnungsnummer, KEIN Steuersatz und KEIN
Steuerbetrag (auch die Beherbergungssteuer nicht - sonst waere das Blatt halb
ausgewiesen, halb nicht), dafuer "Voraussichtlicher Gesamtbetrag" mit dem
Hinweis, dass alles enthalten ist. Kein Blatt fuers Amt, kein Zahlungstext.
TESTLAUF: _testwahl sagt, was im Auswahlfenster geklickt worden waere;
pruef_proforma setzt _dialog_zeigen und drueckt die echten Knoepfe.
Pruefstaende: pruef_proforma.py (neu, mit dem echten Popup), pruef_montage,
pruef_storno, pruef_kette, pruef_kernregeln, pruef_nummernbuch, pruef_nummern,
pruef_dialoge - alle gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYeAfLtccFrbU3MTj1MMTx
Beide Programme stehen nebeneinander offen. Bisher haette das Journal die neue
Rechnung erst beim naechsten Einlesen gesehen.
Kein Absuchen des Rechnungsordners und kein Server: nach jeder Rechnung und
jedem Storno schaut das Rechnungstool nach, ob das Journal ueberhaupt laeuft -
ueber dessen PID, nicht ueber ein Fenster und nicht ueber eine Uhrzeit. Windows
beantwortet das genau; PIDs werden wiederverwendet, deshalb wird zusaetzlich der
Name der EXE verglichen. Laeuft es, kommt eine Zeile in den Briefkasten.
Der Briefkasten IST die gemeinsame SQLite (seine Ansage): zwei Tabellen mehr -
"laeuft" (wer ist offen, mit PID und EXE) und "nachrichten". Damit liegt im
gemeinsamen Ordner genau EINE Datei. Karteileichen (abgestuerztes Programm)
werden beim Nachsehen weggeraeumt, alte Meldungen beim Melden.
Ist das Journal zu, passiert nichts - es liest den Ordner beim naechsten Start
ohnehin. Die Meldung ist eine Anklopfhilfe, keine Datenquelle.
Pruefstand pruef_wache.py (im Steuerjournal, faehrt BEIDE Programme): PID-Pruefung
mit toter Nummer und fremdem EXE-Namen, genau eine Datei im Ordner, Meldung nach
Rechnung und nach Storno, Einlesen samt Anzeige, Ruhe ohne Meldung, kein Melden
an ein geschlossenes Journal.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYeAfLtccFrbU3MTj1MMTx
Das Rechnungstool kannte nur die Nummern seiner eigenen PDFs. Im Steuerjournal
stehen aber Buchungen ohne PDF - der Altbestand aus der Excel-Mappe - und deren
Nummern hat es ein zweites Mal vergeben. § 14 Abs. 4 Nr. 4 UStG laesst jede
Nummer nur einmal zu.
Deshalb fuehren beide Programme jetzt EIN gemeinsames Buch (gemeinsam.py): eine
SQLite in einem Ordner, den beide kennen. Es ist ein Register, keine zweite
Buchhaltung - die Wahrheit bleiben die PDFs. Was hineinkommt: jede geschriebene
Rechnung, jeder Storno (mit storno_zu), jede Neuausstellung. Die Nummernvergabe
und die Kollisionspruefung beim Erstellen fragen es mit ab; ein verworfener
Storno gibt seine Nummer wieder frei. Faellt der Ordner aus (Netzlaufwerk weg),
laeuft alles weiter - dann steht es in der Fussleiste.
ordnerwahl.py fragt den Ordner beim ersten Start ab: auswaehlen, OK,
Sicherheitsfrage, bei Nein zurueck ins Feld. Zwei Fehler daran gleich behoben,
bevor sie jemand treffen konnte: das Fenster war zu schmal (der Auswahl-Knopf
lag ausserhalb) und der Griff (grab_set) muss waehrend des Windows-Ordner-
dialogs los sein, sonst nimmt der keine Eingabe an.
Storno: er ist das Spiegelbild der Rechnung - auch bei den UEBERNACHTUNGEN.
Vorher stand dort nichts, und das Steuerjournal meldete dem Amt die Naechte der
aufgehobenen Rechnung weiter mit. Auf dem Blatt steht "4 Naechte (aufgehoben)",
in den Kenndaten -4.
"Neue Rechnung:" auf dem Storno-Blatt heisst jetzt "Berichtigte Rechnung:" -
die alte Beschriftung las sich wie die Nummer DIESES Blattes.
--test / CAMPINGHOF_TEST=1 unterdrueckt den Erststart-Dialog. In der EXE kommt
man ohnehin nicht daran, und die Pruefstaende bleiben sonst im Dialog stehen.
Pruefstand pruef_nummernbuch.py (neu): beide Programme auf einem Ordner - das
Journal traegt drei Altbestands-Nummern ein, das Rechnungstool schlaegt danach
2026-004 statt 2026-001 vor. Alle uebrigen Pruefstaende gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYeAfLtccFrbU3MTj1MMTx
Bartl vermietet auch monatsweise an Montagearbeiter. Dieses Blatt schrieb er
bisher von Hand in Word ("Miete fuer Montagearbeiter inkl. 7 % MwSt. fuer den
Monat August 2026", Netto/USt/Brutto nebeneinander). Jetzt ist es ein zweiter
Reiter neben der Campingrechnung: Kunde, Nummer, Datum und Zeitraum gelten fuer
beide - nur die Mitte wechselt, damit niemand den Kunden zweimal tippt.
Die Zeitraum-Felder sind dieselben wie Anreise/Abreise, nur anders beschriftet.
Daraus kommen die Naechte, und die meldet das Steuerjournal ans Amt.
Gerechnet wird im selben Rechenkern: der Reiter baut einen Ein-Zeilen-Katalog
mit dem Monatspreis (brutto, Vorgabe 500,00 unter Einstellungen ->
Montage-Rechnung). Die Beherbergungssteuer kommt obendrauf wie ueberall sonst -
500 + 5 % = 525. Fuer das Steuerjournal ist das eine ganz normale Buchung, art
bleibt "rechnung"; die Kenndaten tragen zusaetzlich vorlage="montage", damit der
Storno-Weg die Rechnung spaeter in den richtigen Reiter laedt (sonst waeren aus
Monaten Wohnwagen-Naechte geworden).
Datum: Tag und Monat lassen sich nicht mehr unmoeglich tippen (eine 4 wird zu
04, 35 und 13 fallen weg), und vor dem Erstellen wird gegen den Kalender
geprueft - den 31.02. faengt erst das. Vorher landete so etwas ungeprueft auf
der Rechnung.
Dazu: die Reiter bekommen Theme-Farben, sonst sieht man nicht, welcher offen ist.
Pruefstand pruef_montage.py: 45 Pruefungen am echten Fenster und an echten
PDFs - Reiterwechsel, Beschriftung, Betraege, das Blatt selbst, danach liest das
Steuerjournal die Datei ein (500,00 Basis, 25,00 Steuer, 30 Uebernachtungen),
Storno, die unveraenderte Campingrechnung und die Datumspruefung.
pruef_kernregeln, pruef_storno, pruef_kette, pruef_nummern, pruef_dialoge gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYeAfLtccFrbU3MTj1MMTx
- "Rechnung endgueltig erstellen?" nennt Nummer, Kunde und Betrag und weist
darauf hin, dass danach nur noch Storno oder Berichtigung bleibt
(§ 14 Abs. 4 Nr. 4 UStG, § 31 Abs. 5 UStDV). Geprueft ist auch, dass "Nein"
wirklich nichts schreibt - sonst waere die Frage eine Attrappe.
- Als EXE legt das Programm sich beim ersten Start einen Ordner "Daten" neben
die EXE (config.json). Ist dort nicht schreibbar (EXE unter "Programme"),
weicht es nach LOCALAPPDATA aus. Lag die config.json noch direkt neben der
EXE, zieht sie um - sonst faengt das Programm ohne Firmendaten neu an.
- Aufruf von _storno_fortsetzen entfernt: die Methode gibt es nicht mehr, seit
die Kette auf dem Storno-Blatt steht. War unerreichbar, waere aber beim
naechsten Anfassen ein AttributeError geworden.
- Pruefstaende raeumen ihren Wegwerf-Ordner weg und haben eine Reissleine
(Abbruch nach zwei Minuten), damit kein Dialog mehr ewig offen steht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EKvGkdNW1vKdnMPAM9Bp3X
Statt einer Zustandsmaschine ("offener Storno" ueber den Neustart retten) traegt
das Storno jetzt einfach beide Nummern:
Storno zu Rechnung: 2026-002
Neue Rechnung: 2026-004
und im Text "2026-002 -> 2026-003 -> 2026-004". Damit ist die Kette aus dem
Beleg allein lesbar. Wird das Programm zwischendurch geschlossen, sagt der
Storno, unter welcher Nummer die berichtigte Rechnung gehoert; der Dialog
nennt sie ebenfalls, statt nur "bereits storniert" zu melden.
naechste_freie_nummer_nach() bestimmt die Folgenummer, damit sie nicht von der
Reihenfolge abhaengt, in der der Zaehler mitzieht.
Ausserdem: die Pruefstaende raeumen ihren Wegwerf-Ordner wieder weg, wenn sie
gruen sind. Ein Testlauf hatte 398 Ordner im Temp-Verzeichnis hinterlassen.
Bei Fehlern bleibt der Ordner stehen, damit man hineinsehen kann.
Weitere Funde des Code-Agenten behoben:
- "Kopie von Rechnung_...pdf" und klein geschriebene Dateinamen zaehlen jetzt
bei der Nummernvergabe mit (vorher galt die Nummer als frei).
- Strg+P feuerte auch aus einem offenen Dialog heraus (bind_all -> bind).
167 Pruefungen gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EKvGkdNW1vKdnMPAM9Bp3X
Duplikatanzeige gehoert nicht hierher, sondern in den Steuerrechner - wieder
ausgebaut (duplikate.py, pruef_duplikate.py).
Zwei echte Loecher, die dabei auffielen:
1. vorhandene_rechnungen() uebersah Windows-Kopien. "Rechnung_2026-013 (1).pdf"
und "... - Kopie.pdf" galten nicht als vergeben, die Nummer wurde also ein
zweites Mal ausgegeben. Genau so ist der Altbestand entstanden. Jetzt zaehlt
alles mit, was nach der Nummer KEINE weitere Ziffer hat - "2026-0135" bleibt
getrennt.
2. Liegt eine Nummer mehrfach im Ordner, nahm der Storno-Dialog blind den ersten
Treffer. Jetzt erscheint eine Auswahlliste mit Datum, Name, Betrag und
Dateiname; ohne Auswahl passiert nichts, und die Meldung nennt die konkrete
Datei.
pruef_kernregeln.py: 37 Pruefungen (10 neue), pruef_nummern.py 10.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EKvGkdNW1vKdnMPAM9Bp3X
Der Altbestand wurde nie geprueft - die Sperre verhindert nur NEUE Dubletten.
Neues Modul duplikate.py findet zwei Sorten, und sie bedeuten nicht dasselbe:
Nummer doppelt - zwei Geldbelege mit derselben Rechnungsnummer. Der harte
Fall (§ 14 Abs. 4 Nr. 4 UStG).
Inhalt doppelt - gleicher Gast, gleiches Datum, gleicher Betrag unter zwei
Nummern. Nur ein Hinweis, kann echt sein.
Der schwierige Teil ist das NICHT-Finden: Storno und berichtigte Rechnung
haben absichtlich eigene Nummern, und ein Berichtigungsblatt traegt sogar die
Nummer der Rechnung, die es berichtigt. Nichts davon ist eine Dublette.
PDFs ohne Kenndaten fallen nicht durch: ihre Nummer wird aus dem Dateinamen
gelesen und sie werden getrennt aufgelistet - das ist genau der ungepruefte
Altbestand.
Beim Start laeuft die Pruefung still mit; liegt etwas vor, steht es rot in der
Statuszeile. Kein Popup beim Hochfahren.
pruef_duplikate.py (12 Pruefungen): Ordner mit dem echten Schlamassel gebaut -
zweimal 2026-013 fuer zwei verschiedene Gaeste, dazu eine saubere Storno-Kette
und ein Berichtigungsblatt, die NICHT gemeldet werden duerfen, ein
inhaltsgleiches Paar und eine fremde PDF. Fenster angesehen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EKvGkdNW1vKdnMPAM9Bp3X
Bisher zeigte jeder Beleg nur auf seinen Vorgaenger (storno_zu, ersetzt,
storno_nummer). Ein zweites Programm musste sich die Kette daraus
zusammensuchen. Jetzt tragen ALLE Blaetter einer Kette dieselbe
Vorgangsnummer - die der ersten Rechnung. Gruppieren heisst dort: nach
"vorgang" sortieren, fertig. Eine normale Rechnung ist ihr eigener Vorgang.
Wichtig fuer den Fall "schon korrigierte Rechnung wird noch einmal
storniert": Storno und Berichtigung uebernehmen den VORGANG der alten
Rechnung, nicht deren Nummer - sonst zerfaellt die Kette in zwei.
Die Einzelbezuege bleiben zusaetzlich stehen, damit man die Reihenfolge
innerhalb der Kette lesen kann.
Neu pruef_kette.py (13 Pruefungen): spielt den schlimmsten Fall durch -
Rechnung, Storno, berichtigte Rechnung, Berichtigung der Anschrift, zweiter
Storno, zweite berichtigte Rechnung. Alle sechs landen unter einem
Schluessel, die Summe der Kette ergibt genau die letzte gueltige Rechnung
(27,30 €), und der Steuerrechner liest davon 5 Geldbelege - das
Berichtigungsblatt zaehlt nicht mit.
148 Pruefungen gruen (100 + 27 + 13 + 8) plus 7 Dialoge.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EKvGkdNW1vKdnMPAM9Bp3X