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
107 lines
4 KiB
Python
107 lines
4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Pruefstand: finden sich die beiden Programme von SELBST?
|
|
|
|
Sein Einwand vom 07.09.2026, wortwoertlich:
|
|
|
|
"wie soll das rechnungstool und steuertool wissen wo es ist, das muss im
|
|
... 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, die sagt hallo da ist was neues"
|
|
|
|
Er hat recht: die beiden haengen an genau zwei Faeden - dieser SQLite-Datei
|
|
und der kleinen API. Vorher fragte jedes Programm beim ersten Start nach einem
|
|
Ordner, und der Nutzer musste in BEIDEN denselben eintragen. Ein Buchstabe
|
|
daneben, und jedes Programm fuehrt sein eigenes Buch weiter - auffallen wuerde
|
|
das erst, wenn eine Rechnungsnummer zum zweiten Mal vergeben ist
|
|
(§ 14 Abs. 4 Nr. 4 UStG).
|
|
|
|
GEPRUEFT WIRD
|
|
|
|
1. Beide Programme nennen denselben Standardordner. Das ist die Grundlage;
|
|
weichen sie hier ab, hilft alles Weitere nichts.
|
|
2. Der Ordner laesst sich anlegen, und darin entsteht EIN Nummernbuch.
|
|
3. Was das eine eintraegt, findet das andere - ohne dass jemand einen Pfad
|
|
eingegeben hat.
|
|
|
|
Angefasst wird dabei nichts Echtes: der Standardordner wird ueber die
|
|
Umgebungsvariable LOCALAPPDATA auf einen Temp-Ordner umgelenkt.
|
|
"""
|
|
import os
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
|
|
fehler = []
|
|
|
|
|
|
def pruefe(bedingung, text):
|
|
zeile = (" OK | " if bedingung else "FEHLER | ") + text
|
|
kod = sys.stdout.encoding or "utf-8"
|
|
print(zeile.encode(kod, "replace").decode(kod))
|
|
if not bedingung:
|
|
fehler.append(text)
|
|
|
|
|
|
RECHNUNG = r"C:\claude\rechnungstool"
|
|
STEUER = r"C:\claude\beherbergungssteuer"
|
|
|
|
# ---- 1. Beide Programme meinen denselben Ort ----------------------------
|
|
# Geladen wird jede Fassung EINZELN, damit wirklich die Datei des jeweiligen
|
|
# Programms befragt wird und nicht die zufaellig zuerst importierte.
|
|
import importlib.util # noqa: E402
|
|
|
|
|
|
def modul_aus(ordner, name):
|
|
pfad = os.path.join(ordner, name + ".py")
|
|
kennung = f"{name}_{os.path.basename(ordner)}"
|
|
spec = importlib.util.spec_from_file_location(kennung, pfad)
|
|
m = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(m)
|
|
return m
|
|
|
|
|
|
tmp = tempfile.mkdtemp(prefix="gemeinsam_auto_")
|
|
os.environ["LOCALAPPDATA"] = tmp
|
|
|
|
g_rechnung = modul_aus(RECHNUNG, "gemeinsam")
|
|
g_steuer = modul_aus(STEUER, "gemeinsam")
|
|
|
|
a, b = g_rechnung.standard_ordner(), g_steuer.standard_ordner()
|
|
pruefe(a == b, f"beide Programme nennen denselben Ordner ({a})")
|
|
pruefe(a.startswith(tmp),
|
|
"und er haengt an LOCALAPPDATA, nicht an einem festen Pfad im Quelltext")
|
|
pruefe(os.path.basename(a) == "ravokk",
|
|
f"er heisst 'ravokk', wie er es wollte ({os.path.basename(a)})")
|
|
|
|
# ---- 2. Er wird angelegt, und darin liegt EIN Buch ----------------------
|
|
angelegt = g_rechnung.standard_anlegen()
|
|
pruefe(bool(angelegt) and os.path.isdir(angelegt),
|
|
f"der Ordner wird angelegt ({angelegt})")
|
|
pruefe(g_rechnung.DATEINAME == g_steuer.DATEINAME,
|
|
f"beide erwarten dieselbe Datei ({g_rechnung.DATEINAME})")
|
|
pruefe(g_rechnung.pfad_zur_datei(angelegt) == g_steuer.pfad_zur_datei(angelegt),
|
|
"und damit denselben vollen Pfad")
|
|
|
|
# ---- 3. Was das eine eintraegt, findet das andere -----------------------
|
|
buch_r = g_rechnung.Nummernbuch(angelegt)
|
|
buch_s = g_steuer.Nummernbuch(angelegt)
|
|
|
|
buch_r.eintragen("2026-777", jahr=2026, nachname="Probe", quelle="rechnungstool")
|
|
gefunden = [n for n in buch_s.nummern(2026) if n == "2026-777"]
|
|
pruefe(bool(gefunden),
|
|
"was das Rechnungstool eintraegt, sieht das Steuerjournal sofort")
|
|
|
|
buch_s.eintragen("2026-778", jahr=2026, nachname="Gegenprobe", quelle="journal")
|
|
zurueck = [n for n in buch_r.nummern(2026) if n == "2026-778"]
|
|
pruefe(bool(zurueck), "und umgekehrt genauso")
|
|
|
|
pruefe(len(os.listdir(angelegt)) >= 1
|
|
and any(n.endswith(".sqlite3") for n in os.listdir(angelegt)),
|
|
f"es liegt genau ein Buch dort ({os.listdir(angelegt)})")
|
|
|
|
shutil.rmtree(tmp, ignore_errors=True)
|
|
|
|
if fehler:
|
|
print(f"ROT | {len(fehler)} Punkt(e) nicht erfuellt")
|
|
sys.exit(1)
|
|
print("GRUEN | Beide Programme finden dasselbe Nummernbuch ohne Nachfrage")
|