Merge pull request 'Feature/allocation amount prefill' (#16) from feature/allocation-amount-prefill into dev

Reviewed-on: https://git.hiabuto.net/C3MA/CCMA/pulls/16
Reviewed-by: Matcha <20+matcha@noreply.git.hiabuto.net>
This commit is contained in:
Marcel Peterkau
2026-08-14 23:24:56 +00:00
2 changed files with 31 additions and 4 deletions
+2 -1
View File
@@ -26,7 +26,8 @@
"Der Hausmeister meldet eine überfällige Forderung nicht mehr doppelt als eigene Überfällig- und Mahnungsmeldung; bei Mitgliedern mit aktivem Lastschriftmandat erscheint statt einer Mahnung ein Hinweis, dass der Lastschrifteinzug geprüft werden sollte.", "Der Hausmeister meldet eine überfällige Forderung nicht mehr doppelt als eigene Überfällig- und Mahnungsmeldung; bei Mitgliedern mit aktivem Lastschriftmandat erscheint statt einer Mahnung ein Hinweis, dass der Lastschrifteinzug geprüft werden sollte.",
"Die Spaltenbreiten in den Tabellen für Forderungen, Zahlungen, Spenden und Hausmeister-Vorgänge sind jetzt einheitlich: Datum-, Betrag- und Statusspalten haben eine feste Breite, nur die abschließende Beschreibungs- bzw. Referenzspalte passt sich dynamisch an die Fensterbreite an.", "Die Spaltenbreiten in den Tabellen für Forderungen, Zahlungen, Spenden und Hausmeister-Vorgänge sind jetzt einheitlich: Datum-, Betrag- und Statusspalten haben eine feste Breite, nur die abschließende Beschreibungs- bzw. Referenzspalte passt sich dynamisch an die Fensterbreite an.",
"Die Chronik im Mitgliedsfenster nimmt standardmäßig ein Drittel der Fensterbreite ein.", "Die Chronik im Mitgliedsfenster nimmt standardmäßig ein Drittel der Fensterbreite ein.",
"Der GnuCash-Import öffnet sich mit 80 % der Bildschirmgröße und erlaubt die Mehrfachauswahl von Buchungen per Klick, Strg+Klick oder Umschalt+Klick; Buchungen, die einer bereits vorhandenen Zahlung entsprechen, können beim Import übersprungen oder mit der Beschreibung aus GnuCash aktualisiert werden." "Der GnuCash-Import öffnet sich mit 80 % der Bildschirmgröße und erlaubt die Mehrfachauswahl von Buchungen per Klick, Strg+Klick oder Umschalt+Klick; Buchungen, die einer bereits vorhandenen Zahlung entsprechen, können beim Import übersprungen oder mit der Beschreibung aus GnuCash aktualisiert werden.",
"Beim Anklicken einer noch nicht zugeordneten Forderung oder Spende im Zahlungsfenster wird der Betrag automatisch mit dem sinnvollen Vorschlag vorausgefüllt."
] ]
}, },
{ {
+29 -3
View File
@@ -42,12 +42,14 @@ class _AllocationTable:
claim_allocations: dict[str, str], claim_allocations: dict[str, str],
donation_allocations: dict[str, str], donation_allocations: dict[str, str],
on_change: Callable[[], None], on_change: Callable[[], None],
get_payment_amount: Callable[[], Decimal],
): ):
self.repository = repository self.repository = repository
self.member_id = member_id self.member_id = member_id
self.claim_allocations = claim_allocations self.claim_allocations = claim_allocations
self.donation_allocations = donation_allocations self.donation_allocations = donation_allocations
self.on_change = on_change self.on_change = on_change
self.get_payment_amount = get_payment_amount
# Fixed snapshot of what this payment already covered when the dialog opened. # Fixed snapshot of what this payment already covered when the dialog opened.
# Capacities are computed against this baseline rather than the live, editable # Capacities are computed against this baseline rather than the live, editable
# dicts above -- otherwise "Maximal zuordenbar" would inflate every time the # dicts above -- otherwise "Maximal zuordenbar" would inflate every time the
@@ -182,7 +184,23 @@ class _AllocationTable:
return return
kind, target_id = self._target(selected[0]) kind, target_id = self._target(selected[0])
allocations = self.claim_allocations if kind == "claim" else self.donation_allocations allocations = self.claim_allocations if kind == "claim" else self.donation_allocations
self.amount_var.set(allocations.get(target_id, "0.00")) current = allocations.get(target_id)
if current:
# Already allocated: show the existing amount so it can be reviewed/edited.
self.amount_var.set(current)
return
# Not yet allocated: suggest whichever is smaller -- what this claim/donation
# still needs, or what's left of the payment -- so the common case is just
# "click the row, then Zuordnung setzen".
capacity = (self.claim_capacity if kind == "claim" else self.donation_capacity)[target_id]
free = max(self._payment_amount() - self.total_allocated(), Decimal("0"))
self.amount_var.set(money_text(min(capacity, free)))
def _payment_amount(self) -> Decimal:
try:
return self.get_payment_amount()
except ValueError:
return Decimal("0")
def _set(self) -> None: def _set(self) -> None:
selected = self.tree.selection() selected = self.tree.selection()
@@ -314,6 +332,7 @@ class PaymentCreateDialog(tk.Toplevel):
claim_allocations=self.claim_allocations, claim_allocations=self.claim_allocations,
donation_allocations=self.donation_allocations, donation_allocations=self.donation_allocations,
on_change=self._refresh_totals, on_change=self._refresh_totals,
get_payment_amount=self._current_payment_amount,
) )
ttk.Label(self, textvariable=self.total_var, style="Mono.TLabel").grid( ttk.Label(self, textvariable=self.total_var, style="Mono.TLabel").grid(
@@ -328,10 +347,13 @@ class PaymentCreateDialog(tk.Toplevel):
).pack(side="left") ).pack(side="left")
self._refresh_totals() self._refresh_totals()
def _current_payment_amount(self) -> Decimal:
return decimal_value(self.variables["amount"].get())
def _refresh_totals(self) -> None: def _refresh_totals(self) -> None:
allocated = self.allocation_table.total_allocated() if self.allocation_table else Decimal("0") allocated = self.allocation_table.total_allocated() if self.allocation_table else Decimal("0")
try: try:
payment_amount = decimal_value(self.variables["amount"].get()) payment_amount = self._current_payment_amount()
free = payment_amount - allocated free = payment_amount - allocated
self.total_var.set(f"Zugeordnet: {money_text(allocated)} EUR · Frei: {money_text(free)} EUR") self.total_var.set(f"Zugeordnet: {money_text(allocated)} EUR · Frei: {money_text(free)} EUR")
except (ValueError, InvalidOperation): except (ValueError, InvalidOperation):
@@ -448,6 +470,7 @@ class PaymentEditDialog(tk.Toplevel):
claim_allocations=self.claim_allocations, claim_allocations=self.claim_allocations,
donation_allocations=self.donation_allocations, donation_allocations=self.donation_allocations,
on_change=self._refresh_totals, on_change=self._refresh_totals,
get_payment_amount=self._current_payment_amount,
) )
ttk.Label(self, textvariable=self.total_var, style="Mono.TLabel").grid( ttk.Label(self, textvariable=self.total_var, style="Mono.TLabel").grid(
@@ -474,10 +497,13 @@ class PaymentEditDialog(tk.Toplevel):
except tk.TclError: except tk.TclError:
return return
def _current_payment_amount(self) -> Decimal:
return decimal_value(self.variables["amount"].get())
def _refresh_totals(self) -> None: def _refresh_totals(self) -> None:
allocated = self.allocation_table.total_allocated() if self.allocation_table else Decimal("0") allocated = self.allocation_table.total_allocated() if self.allocation_table else Decimal("0")
try: try:
payment_amount = decimal_value(self.variables["amount"].get()) payment_amount = self._current_payment_amount()
free = payment_amount - allocated free = payment_amount - allocated
self.total_var.set( self.total_var.set(
f"Zugeordnet: {money_text(allocated)} EUR · Frei: {money_text(free)} EUR" f"Zugeordnet: {money_text(allocated)} EUR · Frei: {money_text(free)} EUR"