_execute_housekeeper_run() showed the success dialog but never called
refresh()/on_changed(), so retroactively created claims/payments stayed
invisible in the currently open Mitgliedsakte until it was closed and
reopened, and the overview list wasn't notified either. Match the pattern
used by every other mutating action in this file (save, payment, credit,
reminder, ...): refresh the tab's own contribution lists and notify the
overview callback right after the run succeeds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A prior push mixup (feature/member-contribution-overrides' origin ref had
briefly advanced to include this branch's own "Document the single-member
housekeeper run feature" commit) meant that commit's changelog line already
rode along into dev through PR #17 -- without the actual feature code, which
only exists here. CI's PR gate expects CHANGELOG.json to differ from dev, but
the merged text was already byte-identical, so this branch's PR couldn't pass.
Rewording the entry (adding the actual "Mitgliedsakte" entry point) keeps it
accurate and gives this branch a real changelog diff again.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add a "Hausmeister für dieses Mitglied" button to the member view. It
runs every rule scoped to just that member -- e.g. after editing a
contribution override or payment frequency, to recompute their claims on
demand instead of waiting for or triggering a full run over every
member. A checkbox lets the board force retroactive claim creation for
just that one run, without changing the application-wide default
setting.
Housekeeper.run() gained member_id and retroactive_claims parameters.
Scoping to one member skips the asset checks and leaves every other
member's tasks completely untouched, since their rules never ran this
pass and so fall outside the resolved-scope set that
_resolve_stale_tasks uses to decide what to clear.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Members are sometimes given a fee that deviates from the regular
schedule -- e.g. a reduced rate for students -- for a specific period.
Add a per-member "Beitrag" tab where such deviations can be recorded
with a month-granular date range (Ab/Bis), a mandatory reason, and
either a fixed annual amount or a percentage discount off whichever
base rate is in effect at the time.
Data model: ContributionData gets a contribution_overrides list, each
entry validated (month format, Bis >= Ab, non-overlapping ranges per
member, reason required) and CRUD'd through the repository
(record/update/delete/get_contribution_override), consistent with how
donations already work.
Integration: contribution_claims.py now computes each membership-fee
claim's amount month by month instead of a single rate for the whole
billing period, picking up whichever override (if any) covers each
individual month. That handles an override starting or ending mid
period correctly (e.g. a semiannual payer whose discount begins in
March) without changing behavior for members without overrides.
Already-created claims are never recalculated retroactively, matching
how changes to the global contribution rates already behave.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Clicking a claim or donation row in the payment allocation table used to
just show "0.00" if nothing was allocated to it yet, requiring the board
to type the amount by hand every time. It now suggests whichever is
smaller: what that claim/donation still has open, or what's left unused
of the payment -- so the common case is click the row, then Zuordnung
setzen, done. Rows that already have an allocation still show their
current value instead, so editing one isn't overwritten by the
suggestion.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Selecting bookings to import now uses the Treeview's own multi-selection
(click, Ctrl+click, Shift+click for ranges) instead of a dedicated
checkbox column that had to be clicked precisely -- more standard and
much faster for marking many rows at once.
Bookings matching an existing payment's date+amount are no longer
blocked from selection; they're still flagged (red row, "Bereits
vorhanden"). If any selected booking is such a duplicate, importing now
asks whether to skip those or instead adopt the booking's description
onto the already-recorded payment. That relabeling is handled by a new
repository.update_payment_reference, which only touches the reference
and gnucash_transaction_id fields, leaving date/amount/allocations
untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Hausmeister: reorder columns to Level, Fällig, Vorgang, Details. Level,
Fällig and Vorgang get a fixed width; only Details stretches to fill the
remaining space.
GnuCash-Import dialog: opens at 80% of the current monitor's size
(monitor-aware via the existing screeninfo-based helpers, not just the
raw/virtual screen size) instead of a fixed 960x640. In its booking
table, Import, Datum, Betrag and Status get a fixed width; Beschreibung
and Memo share the remaining space.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Forderungen: reorder columns to Fällig, Betrag, Status, Forderung.
Zahlungen and Spenden already had the requested order. Across all three
tables, the date/amount-like columns now share one fixed width, the
status column is a third wider (it tends to hold longer label text),
and only the trailing description/reference column stretches to fill
the remaining space.
The Chronik pane now defaults to a third of the member view's width
instead of three fifths, matching the other panes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The claim-status and reminder-due housekeeper rules used to run
independently, so once a claim was both overdue and past its dunning
grace period, both a generic "überfällig" task and a "Mahnung fällig"
task showed up for the same problem. Rules can't see each other's
output, so the fix is to give reminder-due sole ownership of the whole
overdue lifecycle: a plain overdue notice during the grace period, then
either the dunning escalation or a fallback overdue notice (dunning
hold active, or waiting between reminder levels) -- never both at once.
claim-status now only handles claims that aren't overdue yet ("bald
fällig").
Members with an active SEPA mandate get a new sepa_debit_overdue finding
instead of the postal dunning escalation once a claim is past its grace
period, since mailing a Mahnung makes no sense for a member who pays by
direct debit -- the board needs to check/retrigger the debit instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AppConfig now keeps a gnucash_last_accounts map (file path -> account
guid). When the import dialog opens a file it already knows, it
auto-selects whichever account was picked last time for that specific
file instead of always defaulting to the first one alphabetically;
picking a different account updates and persists the mapping right away.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a "Zahlungen importieren" button to the member Zahlungen tab that opens
a dedicated window: point it at a GnuCash file (plain or gzip-compressed
XML, defaults to the file configured in Optionen), pick one of its accounts,
and narrow the account's bookings down with a description-contains filter
and an optional date range.
Each matching booking gets a checkbox to mark it for import as a payment.
Before anything is ticked, bookings are cross-checked against this member's
existing payments by date + amount; a match is highlighted and its checkbox
is refused, so re-importing the same statement can't create a duplicate
payment. Only bookings with a positive amount on the selected account are
offered, since those are the ones that make sense as an incoming payment.
Parsing lives in ccma.services.gnucash_import, independent of the UI, and
is covered by tests against a synthetic GnuCash XML fixture (plain and
gzip-compressed) -- no gnucash/piecash dependency needed since the native
file format is just XML.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Show a claim or donation in the Zahlung anlegen/bearbeiten allocation
table only if it still has an open balance, or is already linked to this
payment -- fully settled ones no longer clutter the list.
Visibility is decided from a snapshot taken when the dialog opens (or
when a donation is created inline), not recomputed live, so a row never
disappears just because the user temporarily unassigned it with
"Zuordnung lösen" -- it stays there to add back with "Zuordnung setzen"
until the dialog is saved.
Also fixes a capacity display bug this surfaced: "Maximal zuordenbar"
was computed by mixing the persisted claim/donation balance with the
live, currently-edited allocation amount, so it inflated every time the
amount was changed during a session. It's now computed against a fixed
baseline captured at dialog open, so it stays accurate throughout.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Recording a payment previously meant saving it bare, then separately
opening a claim to allocate money to it. Zahlung anlegen now lists a
member's open claims and donations right in the same dialog with a
select + amount field to assign parts of the payment on the spot, and a
"Neue Spende anlegen" button to create a donation inline and allocate to
it immediately -- covering members who pay more than the membership fee
in one transfer.
Shared the same allocation table in the existing payment-edit dialog so
editing a payment shows and preserves donation allocations too; before
this, saving an edited payment silently dropped any donation allocation
because update_payment only round-tripped claim allocations.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>