feat: add OpenDocument PDF templates

This commit is contained in:
Marcel Peterkau
2026-06-21 22:10:16 +02:00
parent b34135b34a
commit 0622a22794
14 changed files with 942 additions and 14 deletions
+136
View File
@@ -0,0 +1,136 @@
from __future__ import annotations
import tkinter as tk
from collections.abc import Callable
from datetime import date
from pathlib import Path
from tkinter import messagebox, ttk
from ccma.services.documents import DocumentError, DocumentService, GeneratedDocument
from ccma.storage.repository import MemberRepository, RepositoryError
class DocumentTemplateDialog(tk.Toplevel):
def __init__(
self,
master: tk.Misc,
repository: MemberRepository,
member_id: str,
on_generated: Callable[[Path], None],
*,
claim_id: str | None = None,
reminder_id: str | None = None,
):
super().__init__(master)
self.service = DocumentService(repository)
self.member_id = member_id
self.claim_id = claim_id
self.reminder_id = reminder_id
self.on_generated = on_generated
self.templates = self.service.compatible_templates(
has_claim=claim_id is not None,
has_reminder=reminder_id is not None,
)
self.template_by_label = {
f"{template.name} ({template.relative_path})": template for template in self.templates
}
self.title("Dokument aus Template erzeugen")
self.transient(master.winfo_toplevel())
self.resizable(False, False)
self.template_var = tk.StringVar()
self.filename_var = tk.StringVar()
self._build_ui()
self.bind("<Escape>", lambda _event: self.destroy())
self.after_idle(self._grab_if_visible)
def _grab_if_visible(self) -> None:
try:
if self.winfo_exists() and self.winfo_viewable():
self.grab_set()
except tk.TclError:
pass
def _build_ui(self) -> None:
frame = ttk.Frame(self, padding=18)
frame.pack(fill="both", expand=True)
ttk.Label(frame, text="OpenDocument-Template").grid(
row=0, column=0, sticky="w", pady=5, padx=(0, 12)
)
template_box = ttk.Combobox(
frame,
textvariable=self.template_var,
values=list(self.template_by_label),
state="readonly",
width=56,
)
template_box.grid(row=0, column=1, sticky="ew", pady=5)
template_box.bind("<<ComboboxSelected>>", lambda _event: self._template_selected())
ttk.Label(frame, text="PDF-Dateiname").grid(
row=1, column=0, sticky="w", pady=5, padx=(0, 12)
)
ttk.Entry(frame, textvariable=self.filename_var, width=58).grid(
row=1, column=1, sticky="ew", pady=5
)
context = "Mitglied"
if self.claim_id:
context += " · Forderung"
if self.reminder_id:
context += " · ausgewählte Mahnung"
ttk.Label(frame, text=f"Kontext: {context}", style="Mono.TLabel").grid(
row=2, column=0, columnspan=2, sticky="w", pady=(8, 0)
)
ttk.Label(
frame,
text="Das PDF wird unter files/documents in der Mitgliederakte abgelegt.",
style="Muted.TLabel",
).grid(row=3, column=0, columnspan=2, sticky="w", pady=(4, 0))
buttons = ttk.Frame(frame)
buttons.grid(row=4, column=0, columnspan=2, sticky="e", pady=(18, 0))
ttk.Button(buttons, text="Abbrechen", command=self.destroy).pack(side="left", padx=(0, 8))
self.generate_button = ttk.Button(
buttons,
text="PDF erzeugen",
style="Accent.TButton",
command=self._generate,
)
self.generate_button.pack(side="left")
if self.template_by_label:
self.template_var.set(next(iter(self.template_by_label)))
self._template_selected()
else:
self.generate_button.configure(state="disabled")
ttk.Label(
frame,
text="Für diesen Kontext ist kein passendes Template vorhanden.",
style="Warning.TLabel",
).grid(row=5, column=0, columnspan=2, sticky="w", pady=(12, 0))
def _template_selected(self) -> None:
template = self.template_by_label.get(self.template_var.get())
if template:
self.filename_var.set(f"{date.today().isoformat()} - {template.name}.pdf")
def _generate(self) -> None:
template = self.template_by_label.get(self.template_var.get())
if template is None:
messagebox.showerror("Template auswählen", "Bitte ein Template auswählen.", parent=self)
return
self.generate_button.configure(state="disabled")
self.configure(cursor="watch")
self.update_idletasks()
try:
generated: GeneratedDocument = self.service.generate(
template,
self.member_id,
output_name=self.filename_var.get(),
claim_id=self.claim_id,
reminder_id=self.reminder_id,
)
except (DocumentError, RepositoryError, OSError) as exc:
messagebox.showerror("Dokument konnte nicht erzeugt werden", str(exc), parent=self)
self.generate_button.configure(state="normal")
self.configure(cursor="")
return
self.destroy()
self.on_generated(generated.path)
messagebox.showinfo("Dokument erzeugt", f"PDF gespeichert:\n{generated.path}", parent=self.master)