Storno und Berichtigung: das Journal darf die Buchung nicht verlieren

Das Rechnungstool schreibt seit heute auch Storno- und Berichtigungsblaetter in
denselben Ordner. Zwei Fehler, die daraus entstanden waeren - beide still:

1. Ein Berichtigungsblatt (§ 31 Abs. 5 UStDV) traegt die Nummer DER RECHNUNG,
   die es berichtigt, und lauter Nullen. Unter dem alten Schluessel
   (jahr, rechnungsnummer) hat es die echte Buchung ueberschrieben - im
   Amtsbericht fehlte die Uebernachtung dann. Wird jetzt uebersprungen
   (pdf_parser.keine_buchung), in beiden Scannern.
2. Der Schluessel ist jetzt die DATEI. Zwei Rechnungen mit derselben Nummer -
   der Altbestand, bevor das Rechnungstool eine Sperre bekam - ueberschrieben
   sich sonst gegenseitig. Bestehende Journale werden beim Oeffnen einmalig
   umgebaut, in einer Transaktion, mit Rueckfall auf den alten Stand.
   Buchungen von Hand (kein PDF-Pfad) bleiben davon unberuehrt.

Stornos zaehlen weiter mit - sie sind negativ und heben die Uebernachtung im
Bericht genau so auf, wie es sein soll.

pruef_berichtigung.py (neu): schreibt mit dem echten Renderer des
Rechnungstools eine Rechnung, ein Berichtigungsblatt dazu und zwei Rechnungen
mit derselben Nummer, liest sie ein und prueft das Ergebnis in der Tabelle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EKvGkdNW1vKdnMPAM9Bp3X
This commit is contained in:
TheMockTv 2026-09-02 21:39:41 +02:00
parent 700ddc3bd1
commit 65eec9937a
3 changed files with 224 additions and 4 deletions

View file

@ -177,6 +177,18 @@ def _aus_metadaten(pfad: str):
return daten
def keine_buchung(pfad: str) -> bool:
"""True fuer Blaetter, die keine Uebernachtung abrechnen.
Ein Berichtigungsdokument (§ 31 Abs. 5 UStDV) reicht nur fehlende
Empfaengerangaben nach. Es traegt in den Kenndaten die Nummer DER RECHNUNG,
die es berichtigt, und lauter Nullen. Wer es einliest, ueberschreibt damit
die echte Buchung - im Amtsbericht fehlt die Uebernachtung dann.
"""
kopf = _aus_metadaten(pfad)
return bool(kopf and kopf.get("art") == "berichtigung")
def parse_pdf(pfad: str, standard_satz: float = STANDARD_SATZ) -> Buchung:
"""Rechnungs-PDF -> Buchung. Wirft ParserFehler, wenn Pflichtfelder fehlen."""
meta = _aus_metadaten(pfad)
@ -294,6 +306,8 @@ def scanne_ordner(ordner: str, standard_satz: float = STANDARD_SATZ, rekursiv: b
if not name.lower().endswith(".pdf"):
continue
pfad = os.path.join(wurzel, name)
if keine_buchung(pfad):
continue
try:
buchungen.append(parse_pdf(pfad, standard_satz))
except ParserFehler as e: