mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-08-25 23:15:18 +02:00
Allow deleting claims, creating standalone payments, and add a donations tab
Claims could previously only be cancelled (stornieren), which blocks once a payment is allocated. Add a hard delete that releases any linked payments/credits back to being unallocated instead of destroying them. Payments could only be created from within a claim, forcing immediate allocation. Add a bare payment creation flow in the Zahlungen tab so incoming transfers can be logged first and allocated later. Add a per-member Spenden tab (donations, backed by a new donations list on ContributionData) so amounts paid beyond the membership fee can be tracked and existing free payments allocated to them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
a61ea3cb57
commit
42fb4c4224
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import tkinter as tk
|
||||
from collections.abc import Callable
|
||||
from datetime import date
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from tkinter import messagebox, ttk
|
||||
|
||||
@@ -15,6 +16,77 @@ from ccma.domain.dates import date_input_hint, format_date_for_display
|
||||
from ccma.storage.repository import MemberRepository, RepositoryError
|
||||
|
||||
|
||||
class PaymentCreateDialog(tk.Toplevel):
|
||||
"""Records a payment without requiring it to be tied to a claim right away.
|
||||
Useful for logging an incoming bank transfer as soon as it arrives; it can be
|
||||
allocated to claims or donations afterwards."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
master: tk.Misc,
|
||||
repository: MemberRepository,
|
||||
member_id: str,
|
||||
on_saved: Callable[[], None],
|
||||
):
|
||||
super().__init__(master)
|
||||
self.repository = repository
|
||||
self.member_id = member_id
|
||||
self.on_saved = on_saved
|
||||
self.title("Zahlung anlegen")
|
||||
self.transient(master.winfo_toplevel())
|
||||
self.resizable(False, False)
|
||||
self.bind("<Escape>", lambda _event: self.destroy())
|
||||
self.frame = ttk.Frame(self, padding=18)
|
||||
self.frame.pack(fill="both", expand=True)
|
||||
self.frame.columnconfigure(1, weight=1)
|
||||
self.variables = {
|
||||
"date": tk.StringVar(value=format_date_for_display(date.today().isoformat())),
|
||||
"amount": tk.StringVar(),
|
||||
"gnucash": tk.StringVar(),
|
||||
"reference": tk.StringVar(),
|
||||
}
|
||||
fields = (
|
||||
(f"Zahlungsdatum ({date_input_hint()})", "date"),
|
||||
("Zahlungsbetrag", "amount"),
|
||||
("GnuCash-ID (optional)", "gnucash"),
|
||||
("Referenz", "reference"),
|
||||
)
|
||||
for row, (label, key) in enumerate(fields):
|
||||
ttk.Label(self.frame, text=label).grid(row=row, column=0, sticky="w", padx=(0, 12), pady=5)
|
||||
ttk.Entry(self.frame, textvariable=self.variables[key], width=42).grid(
|
||||
row=row, column=1, sticky="ew", pady=5
|
||||
)
|
||||
ttk.Label(
|
||||
self.frame,
|
||||
text=(
|
||||
"Die Zahlung wird zunächst ohne Zuordnung gespeichert. Sie kann anschließend "
|
||||
"einer Forderung oder Spende zugeordnet werden."
|
||||
),
|
||||
style="Mono.TLabel",
|
||||
wraplength=380,
|
||||
).grid(row=len(fields), column=0, columnspan=2, sticky="w", pady=(8, 0))
|
||||
buttons = ttk.Frame(self.frame)
|
||||
buttons.grid(row=len(fields) + 1, 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=self._save).pack(side="left")
|
||||
self.after_idle(self.grab_set)
|
||||
|
||||
def _save(self) -> None:
|
||||
try:
|
||||
self.repository.create_payment(
|
||||
self.member_id,
|
||||
payment_date=self.variables["date"].get(),
|
||||
amount=self.variables["amount"].get(),
|
||||
gnucash_transaction_id=self.variables["gnucash"].get(),
|
||||
reference=self.variables["reference"].get(),
|
||||
)
|
||||
except RepositoryError as exc:
|
||||
messagebox.showerror("Zahlung konnte nicht gespeichert werden", str(exc), parent=self)
|
||||
return
|
||||
self.destroy()
|
||||
self.on_saved()
|
||||
|
||||
|
||||
class PaymentEditDialog(tk.Toplevel):
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user