# -*- coding: utf-8 -*- """ Dialog für eine Buchung von Hand (z.B. Barzahlung ohne PDF-Rechnung) und zum Nachbessern einer eingelesenen Zeile. Schließen mit ESC, Speichern mit Enter oder Knopf. """ import tkinter as tk from tkinter import ttk, messagebox from datetime import datetime, date from modell import (Buchung, STANDARD_SATZ, steuer_betrag, eur, zahl_aus_text, zerlege_nummer) class BuchungDialog(tk.Toplevel): """ Modal. Nach dem Schließen steht das Ergebnis in .ergebnis – die gespeicherte Buchung (oder None, wenn abgebrochen) .loeschen – True, wenn die Buchung gelöscht werden soll """ def __init__(self, master, jahr: int, satz: float = STANDARD_SATZ, buchung: Buchung = None): super().__init__(master) self.ergebnis = None self.loeschen = False self._vorlage = buchung self._jahr = buchung.jahr if buchung else jahr self.title("Buchung bearbeiten" if buchung else "Neue Buchung") self.resizable(False, False) self.transient(master) self.grab_set() rahmen = ttk.Frame(self, padding=14) rahmen.grid(sticky="nsew") 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(".", ",")), ] 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)) e = ttk.Entry(rahmen, width=26) e.insert(0, wert) e.grid(row=i, column=1, sticky="ew", pady=3) e.bind("", lambda _e: self._vorschau()) self.felder[schluessel] = e self.vorschau = ttk.Label(rahmen, text="", foreground="black", font=("Segoe UI", 10, "bold"), anchor="e") self.vorschau.grid(row=len(zeilen), column=0, columnspan=2, sticky="ew", pady=(10, 4)) knoepfe = ttk.Frame(rahmen) 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)) rahmen.columnconfigure(1, weight=1) self.bind("", lambda _e: self.destroy()) self.bind("", lambda _e: self._speichern()) self.felder["datum" if buchung else "nummer"].focus_set() self._vorschau() self.update_idletasks() x = master.winfo_rootx() + (master.winfo_width() - self.winfo_width()) // 2 y = master.winfo_rooty() + 120 self.geometry(f"+{max(0, x)}+{max(0, y)}") # ---- Eingaben lesen ---------------------------------------------------- def _werte(self): gezahlt = zahl_aus_text(self.felder["gezahlt"].get()) satz = zahl_aus_text(self.felder["satz"].get()) return gezahlt, satz def _vorschau(self): gezahlt, satz = self._werte() self.vorschau.config(text=f"Beherbergungssteuer: {eur(steuer_betrag(gezahlt, satz))}") def _loeschen(self): b = self._vorlage hinweis = ("\n\nDie Rechnungs-PDF bleibt liegen – beim nächsten Einlesen " "taucht die Buchung wieder auf.") if b.quelle == "pdf" else "" if not messagebox.askyesno( "Buchung löschen", f"Rechnung {b.voll_nummer} ({b.nachname or 'ohne Namen'}) " f"aus dem Journal löschen?{hinweis}", parent=self): return self.loeschen = True self.destroy() def _speichern(self): text_datum = self.felder["datum"].get().strip() datum = None for fmt in ("%d.%m.%Y", "%d.%m.%y", "%Y-%m-%d"): try: datum = datetime.strptime(text_datum, fmt).date() break except ValueError: continue if not datum: messagebox.showerror("Datum", f"Datum nicht lesbar: {text_datum}\n" "Erwartet wird z.B. 12.07.2026", parent=self) return # "238" und "2026-238" werden beide angenommen jahr, nummer = zerlege_nummer(self.felder["nummer"].get(), datum.year) if not nummer: messagebox.showerror("Rechnungsnummer", "Bitte eine Rechnungsnummer eintragen " "(z.B. 238).", parent=self) return gezahlt, satz = self._werte() if gezahlt <= 0: messagebox.showerror("Gezahlt", "Bitte den Rechnungsbetrag (Zwischensumme) " "eintragen.", parent=self) return self.ergebnis = Buchung( datum=datum, rechnungsnummer=nummer, jahr=jahr, nachname=self.felder["nachname"].get().strip(), 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, ) self.destroy()