Doppelte Rechnungsnummern zeigen, nur ein Fenster, keine Handbuchungen
Doppelte Rechnungsnummern werden ANGEZEIGT statt still mitgeschleppt: rote Zeile mit Warnzeichen, markierter Monatsreiter, rote Zeile unter der Jahressumme und eine Liste aller Belege mit Dateinamen. Gerechnet wird ueber das ganze Jahr - liegt die zweite Rechnung im naechsten Monat, sieht kein Monat fuer sich eine Dublette, das Amt bekommt sie trotzdem doppelt. Die Betraege bleiben in allen Summen und im Amtsbericht; gemeldet wird der Fund, korrigiert wird nichts von allein. Vorgeschichte: bis v1.2 war die Rechnungsnummer der Schluessel der Tabelle - die zweite Rechnung ueberschrieb die erste still und fehlte im Amtsbericht. v1.3 hat den Schluessel auf die PDF-Datei umgestellt, seitdem bleiben beide Zeilen stehen. Erst jetzt sieht man den Fall auch. Einzelinstanz aus dem Steuerrechner uebernommen (nur Mutex- und PID-Name geaendert): zwei Fenster lesen denselben Rechnungsordner in dieselbe journal.sqlite3 - was das eine loescht, steht im anderen noch da, und wer dort "PDF fuers Amt" drueckt, meldet einen Stand, den es nicht mehr gibt. Die Sperre greift vor ablage.daten_ordner() und nur beim echten Start. "Neue Buchung" ist raus: jede Zeile gehoert zu einer Rechnung aus dem Rechnungstool. Wer eine Buchung braucht, schreibt die Rechnung. Bestehende Zeilen lassen sich weiter bearbeiten. Pruefstaende: pruef_doppelte.py (27 Pruefungen am echten Fenster), pruef_einzelinstanz.py (echte Prozesse). Beide gruen, dazu Abnahme mit den Augen an Hauptfenster, Liste und Bearbeiten-Dialog. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYeAfLtccFrbU3MTj1MMTx
This commit is contained in:
parent
de71fce64f
commit
894bc81c46
8 changed files with 707 additions and 47 deletions
|
|
@ -1,14 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Dialog für eine Buchung von Hand (z.B. Barzahlung ohne PDF-Rechnung)
|
||||
und zum Nachbessern einer eingelesenen Zeile.
|
||||
Dialog zum Nachbessern einer Zeile, die schon im Journal steht.
|
||||
|
||||
Buchungen von Hand ANLEGEN gibt es bewusst nicht mehr: jede Zeile gehört zu
|
||||
einer Rechnung aus dem Rechnungstool. Wer eine Buchung braucht, schreibt die
|
||||
Rechnung - sonst meldet das Journal ans Amt, was in keinem Rechnungsordner
|
||||
steht.
|
||||
|
||||
Schließen mit ESC, Speichern mit Enter oder Knopf.
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox
|
||||
from datetime import datetime, date
|
||||
from datetime import datetime
|
||||
|
||||
from modell import (Buchung, STANDARD_SATZ, steuer_betrag, eur, zahl_aus_text,
|
||||
zerlege_nummer)
|
||||
|
|
@ -22,14 +26,14 @@ class BuchungDialog(tk.Toplevel):
|
|||
.loeschen – True, wenn die Buchung gelöscht werden soll
|
||||
"""
|
||||
|
||||
def __init__(self, master, jahr: int, satz: float = STANDARD_SATZ, buchung: Buchung = None):
|
||||
def __init__(self, master, buchung: Buchung, satz: float = STANDARD_SATZ):
|
||||
super().__init__(master)
|
||||
self.ergebnis = None
|
||||
self.loeschen = False
|
||||
self._vorlage = buchung
|
||||
self._jahr = buchung.jahr if buchung else jahr
|
||||
self._jahr = buchung.jahr
|
||||
|
||||
self.title("Buchung bearbeiten" if buchung else "Neue Buchung")
|
||||
self.title("Buchung bearbeiten")
|
||||
self.resizable(False, False)
|
||||
self.transient(master)
|
||||
self.grab_set()
|
||||
|
|
@ -39,13 +43,12 @@ class BuchungDialog(tk.Toplevel):
|
|||
|
||||
self.felder = {}
|
||||
zeilen = [
|
||||
("datum", "Rechnungsdatum", (buchung.datum.strftime("%d.%m.%Y") if buchung
|
||||
else date.today().strftime("%d.%m.%Y"))),
|
||||
("nummer", "Rechnungsnummer", buchung.rechnungsnummer if buchung else ""),
|
||||
("nachname", "Nachname", buchung.nachname if buchung else ""),
|
||||
("naechte", "Übernachtungen", str(buchung.naechte) if buchung else "1"),
|
||||
("gezahlt", "Gezahlt (Zwischensumme)", f"{buchung.gezahlt:.2f}".replace(".", ",") if buchung else ""),
|
||||
("satz", "Steuersatz %", f"{(buchung.satz if buchung else satz):g}".replace(".", ",")),
|
||||
("datum", "Rechnungsdatum", buchung.datum.strftime("%d.%m.%Y")),
|
||||
("nummer", "Rechnungsnummer", buchung.rechnungsnummer),
|
||||
("nachname", "Nachname", buchung.nachname),
|
||||
("naechte", "Übernachtungen", str(buchung.naechte)),
|
||||
("gezahlt", "Gezahlt (Zwischensumme)", f"{buchung.gezahlt:.2f}".replace(".", ",")),
|
||||
("satz", "Steuersatz %", f"{(buchung.satz or satz):g}".replace(".", ",")),
|
||||
]
|
||||
for i, (schluessel, beschriftung, wert) in enumerate(zeilen):
|
||||
ttk.Label(rahmen, text=beschriftung + ":").grid(row=i, column=0, sticky="w", pady=3, padx=(0, 10))
|
||||
|
|
@ -63,14 +66,13 @@ class BuchungDialog(tk.Toplevel):
|
|||
knoepfe.grid(row=len(zeilen) + 1, column=0, columnspan=2, sticky="ew", pady=(6, 0))
|
||||
ttk.Button(knoepfe, text="Speichern", command=self._speichern) \
|
||||
.pack(side="left", fill="x", expand=True)
|
||||
if buchung: # nur beim Bearbeiten – eine neue Buchung gibt es noch nicht zu löschen
|
||||
ttk.Button(knoepfe, text="Löschen", command=self._loeschen) \
|
||||
.pack(side="left", fill="x", expand=True, padx=(8, 0))
|
||||
ttk.Button(knoepfe, text="Löschen", command=self._loeschen) \
|
||||
.pack(side="left", fill="x", expand=True, padx=(8, 0))
|
||||
|
||||
rahmen.columnconfigure(1, weight=1)
|
||||
self.bind("<Escape>", lambda _e: self.destroy())
|
||||
self.bind("<Return>", lambda _e: self._speichern())
|
||||
self.felder["datum" if buchung else "nummer"].focus_set()
|
||||
self.felder["datum"].focus_set()
|
||||
self._vorschau()
|
||||
|
||||
self.update_idletasks()
|
||||
|
|
@ -135,8 +137,8 @@ class BuchungDialog(tk.Toplevel):
|
|||
naechte=int(zahl_aus_text(self.felder["naechte"].get()) or 0),
|
||||
gezahlt=gezahlt,
|
||||
satz=satz,
|
||||
quelle=self._vorlage.quelle if self._vorlage else "manuell",
|
||||
pdf_pfad=self._vorlage.pdf_pfad if self._vorlage else "",
|
||||
id=self._vorlage.id if self._vorlage else 0,
|
||||
quelle=self._vorlage.quelle,
|
||||
pdf_pfad=self._vorlage.pdf_pfad,
|
||||
id=self._vorlage.id,
|
||||
)
|
||||
self.destroy()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue