Fix housekeeper false-positive overdue errors and ended-member birthday/anniversary noise

An overdue claim whose reminder had already been sent, with that reminder's own
payment deadline still running, fell through to the plain "überfällig" error
finding whenever reminder_due's escalation check returned None to signal "not
yet time for the next Mahnstufe" -- that None was indistinguishable from "never
handled at all" to the caller, so it looked neglected even though a reminder was
already out. Give that case its own low-priority "reminder_awaiting_deadline"
finding instead of silently falling back to the overdue-error path.

Birthday and membership-anniversary findings also kept firing for members whose
status is "ended", which doesn't make sense once the membership itself is over.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Marcel Peterkau
2026-08-15 03:20:18 +02:00
co-authored by Claude Sonnet 5
parent 4ecdf87d05
commit 56a3f5f037
5 changed files with 95 additions and 1 deletions
+29
View File
@@ -61,6 +61,35 @@ def test_reminder_rule_progresses_only_after_sent_deadline(tmp_path) -> None:
assert "Erste Mahnung" in next_task.title
def test_overdue_claim_awaiting_reminder_deadline_is_not_reported_as_error(tmp_path) -> None:
repository, member = _overdue_claim_repository(tmp_path)
housekeeper = Housekeeper(repository)
draft = repository.create_reminder_draft(
member.member_id,
"claim-1",
level=1,
name="Zahlungserinnerung",
payment_deadline_days=14,
items=[],
)
sent = repository.mark_reminder_sent(member.member_id, "claim-1", draft["reminder_id"])
deadline = date.fromisoformat(sent["payment_deadline"])
findings = [
item
for item in housekeeper.run(today=deadline - timedelta(days=1))
if item.member_id == member.member_id
]
# A reminder was already sent and its own deadline hasn't expired yet -- this is
# being handled, so it must not appear as an unaddressed "error"-severity overdue
# claim, only as a low-priority note that the existing deadline is still running.
assert not any(item.code == "claim_overdue" for item in findings)
awaiting = next(item for item in findings if item.code == "reminder_awaiting_deadline")
assert awaiting.severity == "info"
assert "Zahlungserinnerung" in awaiting.title
def test_dunning_hold_suppresses_and_then_restores_task(tmp_path) -> None:
repository, member = _overdue_claim_repository(tmp_path)
housekeeper = Housekeeper(repository)