from __future__ import annotations import tkinter as tk from collections.abc import Callable from tkinter import messagebox, ttk from ccma.domain.contributions import CONTRIBUTION_OVERRIDE_KIND_LABELS from ccma.domain.dates import format_month_for_display, month_input_hint from ccma.storage.repository import MemberRepository, RepositoryError from ccma.ui.labels import storage_key class _Dialog(tk.Toplevel): def __init__(self, master: tk.Misc, title: str, on_saved: Callable[[], None]): super().__init__(master) self.on_saved = on_saved self.title(title) self.transient(master.winfo_toplevel()) self.resizable(False, False) self.frame = ttk.Frame(self, padding=18) self.frame.pack(fill="both", expand=True) self.bind("", lambda _event: self.destroy()) self.after_idle(self.grab_set) def _buttons(self, row: int, command: Callable[[], None]) -> None: buttons = ttk.Frame(self.frame) buttons.grid(row=row, column=0, columnspan=2, sticky="e", pady=(16, 0)) ttk.Button(buttons, text="Abbrechen", command=self.destroy).pack(side="left", padx=(0, 8)) ttk.Button(buttons, text="Speichern", style="Accent.TButton", command=command).pack(side="left") class ContributionOverrideEditDialog(_Dialog): """Records an individually agreed membership fee for a member -- e.g. a reduced student rate -- for a month-granular date range, either as a fixed annual amount or as a percentage discount off whichever base rate is in effect at the time.""" def __init__( self, master: tk.Misc, repository: MemberRepository, member_id: str, on_saved: Callable[[], None], override: dict | None = None, ): super().__init__( master, "Abweichenden Beitrag bearbeiten" if override else "Abweichenden Beitrag anlegen", on_saved, ) self.repository = repository self.member_id = member_id self.override_id = str(override.get("override_id")) if override else None kind_label = CONTRIBUTION_OVERRIDE_KIND_LABELS.get( str(override.get("kind", "")) if override else "amount", CONTRIBUTION_OVERRIDE_KIND_LABELS["amount"], ) self.variables = { "valid_from": tk.StringVar( value=format_month_for_display(str(override.get("valid_from", ""))) if override else "" ), "valid_until": tk.StringVar( value=format_month_for_display(str(override.get("valid_until", ""))) if override else "" ), "kind": tk.StringVar(value=kind_label), "value": tk.StringVar(value=str(override.get("value", "")) if override else ""), } self.frame.columnconfigure(1, weight=1) ttk.Label(self.frame, text=f"Ab ({month_input_hint()})").grid( row=0, column=0, sticky="w", pady=5, padx=(0, 12) ) ttk.Entry(self.frame, textvariable=self.variables["valid_from"], width=42).grid( row=0, column=1, sticky="ew", pady=5 ) ttk.Label(self.frame, text=f"Bis ({month_input_hint()}, leer = unbefristet)").grid( row=1, column=0, sticky="w", pady=5, padx=(0, 12) ) ttk.Entry(self.frame, textvariable=self.variables["valid_until"], width=42).grid( row=1, column=1, sticky="ew", pady=5 ) ttk.Label(self.frame, text="Art").grid(row=2, column=0, sticky="w", pady=5, padx=(0, 12)) kind_combo = ttk.Combobox( self.frame, textvariable=self.variables["kind"], values=list(CONTRIBUTION_OVERRIDE_KIND_LABELS.values()), state="readonly", width=39, ) kind_combo.grid(row=2, column=1, sticky="ew", pady=5) kind_combo.bind("<>", lambda _event: self._update_value_label()) self.value_label_var = tk.StringVar() ttk.Label(self.frame, textvariable=self.value_label_var).grid( row=3, column=0, sticky="w", pady=5, padx=(0, 12) ) ttk.Entry(self.frame, textvariable=self.variables["value"], width=42).grid( row=3, column=1, sticky="ew", pady=5 ) ttk.Label(self.frame, text="Begründung").grid(row=4, column=0, sticky="nw", pady=5, padx=(0, 12)) self.reason_text = tk.Text(self.frame, width=42, height=4, wrap="word") self.reason_text.grid(row=4, column=1, sticky="ew", pady=5) if override: self.reason_text.insert("1.0", str(override.get("reason", ""))) self._update_value_label() self._buttons(5, self._save) def _update_value_label(self) -> None: kind = storage_key(CONTRIBUTION_OVERRIDE_KIND_LABELS, self.variables["kind"].get()) self.value_label_var.set("Jahresbetrag (EUR)" if kind == "amount" else "Ermäßigung (%)") def _save(self) -> None: kind = storage_key(CONTRIBUTION_OVERRIDE_KIND_LABELS, self.variables["kind"].get()) reason = self.reason_text.get("1.0", "end-1c") try: if self.override_id: self.repository.update_contribution_override( self.member_id, self.override_id, valid_from=self.variables["valid_from"].get(), valid_until=self.variables["valid_until"].get(), kind=kind, value=self.variables["value"].get(), reason=reason, ) else: self.repository.record_contribution_override( self.member_id, valid_from=self.variables["valid_from"].get(), valid_until=self.variables["valid_until"].get(), kind=kind, value=self.variables["value"].get(), reason=reason, ) except RepositoryError as exc: messagebox.showerror( "Abweichender Beitrag konnte nicht gespeichert werden", str(exc), parent=self ) return self.destroy() self.on_saved()