mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-08-25 15:05:18 +02:00
Allow editing reminders (incl. undoing "sent") and prefill a Rücklastschrift reason
A generated/sent Mahnung email couldn't be corrected before: sent reminders were
locked, so a wording mistake meant living with it. Add edit_reminder_draft() for
draft/generated reminders and revert_reminder_sent() to safely undo a "sent"
reminder (removing the fee items it booked, refusing if a payment already covers
them or a higher Mahnstufe already exists) so it can be edited and resent.
ReminderDialog now supports an edit mode (reminder=... prefills name/detail/
items/deadline/channel, level stays fixed) and claim_tab gained a "Mahnung
bearbeiten" button that reverts-then-edits for sent reminders automatically,
asking for confirmation first since it un-books the fee.
The Rücklastschrift preset now also prefills "Details" with a short explanation
("Leider konnte die Lastschrift ... nicht eingelöst werden.") since that field
goes straight into the reminder email -- the board can append the concrete
reason (mangels Deckung, Konto ungültig, ...) right there instead of the field
starting empty.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
f3bdffd347
commit
186af530b0
@@ -281,6 +281,142 @@ def test_reminder_policy_rejects_invalid_input(tmp_path) -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_edit_reminder_draft_updates_fields_but_not_level(tmp_path) -> None:
|
||||
repository, member = _overdue_claim_repository(tmp_path)
|
||||
draft = repository.create_reminder_draft(
|
||||
member.member_id,
|
||||
"claim-1",
|
||||
level=1,
|
||||
name="Zahlungserinnerung",
|
||||
payment_deadline_days=14,
|
||||
items=[],
|
||||
)
|
||||
|
||||
edited = repository.edit_reminder_draft(
|
||||
member.member_id,
|
||||
"claim-1",
|
||||
draft["reminder_id"],
|
||||
name="Erste Mahnung",
|
||||
payment_deadline_days=10,
|
||||
detail="Bitte um Ausgleich.",
|
||||
items=[{"description": "Mahngebühr", "amount": "5.00"}],
|
||||
)
|
||||
assert edited["level"] == 1
|
||||
assert edited["name"] == "Erste Mahnung"
|
||||
assert edited["payment_deadline_days"] == 10
|
||||
assert edited["detail"] == "Bitte um Ausgleich."
|
||||
assert edited["items"] == [{"description": "Mahngebühr", "amount": "5.00"}]
|
||||
|
||||
|
||||
def test_edit_reminder_draft_rejects_already_sent_reminder(tmp_path) -> None:
|
||||
repository, member = _overdue_claim_repository(tmp_path)
|
||||
draft = repository.create_reminder_draft(
|
||||
member.member_id,
|
||||
"claim-1",
|
||||
level=1,
|
||||
name="Zahlungserinnerung",
|
||||
payment_deadline_days=14,
|
||||
items=[],
|
||||
)
|
||||
repository.mark_reminder_sent(member.member_id, "claim-1", draft["reminder_id"])
|
||||
|
||||
with pytest.raises(RepositoryError, match="Nur ein Entwurf"):
|
||||
repository.edit_reminder_draft(
|
||||
member.member_id,
|
||||
"claim-1",
|
||||
draft["reminder_id"],
|
||||
name="Andere Bezeichnung",
|
||||
payment_deadline_days=14,
|
||||
)
|
||||
|
||||
|
||||
def test_revert_reminder_sent_removes_booked_fee_and_reopens_draft(tmp_path) -> None:
|
||||
repository, member = _overdue_claim_repository(tmp_path)
|
||||
draft = repository.create_reminder_draft(
|
||||
member.member_id,
|
||||
"claim-1",
|
||||
level=1,
|
||||
name="Rücklastschrift",
|
||||
payment_deadline_days=14,
|
||||
items=[{"description": "Rücklastschriftgebühr", "amount": "5.00"}],
|
||||
)
|
||||
sent = repository.mark_reminder_sent(member.member_id, "claim-1", draft["reminder_id"])
|
||||
assert len(sent["fee_item_ids"]) == 1
|
||||
_data, claim = repository.get_claim(member.member_id, "claim-1")
|
||||
assert claim_total(claim) == Decimal("105.00")
|
||||
|
||||
reverted = repository.revert_reminder_sent(member.member_id, "claim-1", draft["reminder_id"])
|
||||
assert reverted["status"] == "draft"
|
||||
assert reverted["sent_at"] is None
|
||||
assert reverted["payment_deadline"] is None
|
||||
assert reverted["fee_item_ids"] == []
|
||||
|
||||
_data, claim = repository.get_claim(member.member_id, "claim-1")
|
||||
assert claim_total(claim) == Decimal("100.00")
|
||||
descriptions = {item["description"] for item in claim["items"]}
|
||||
assert "Rücklastschriftgebühr" not in descriptions
|
||||
|
||||
# Now editable and re-sendable, e.g. after fixing the wording.
|
||||
repository.edit_reminder_draft(
|
||||
member.member_id,
|
||||
"claim-1",
|
||||
draft["reminder_id"],
|
||||
name="Rücklastschrift",
|
||||
payment_deadline_days=14,
|
||||
items=[{"description": "Rücklastschriftgebühr", "amount": "7.50"}],
|
||||
)
|
||||
repository.mark_reminder_sent(member.member_id, "claim-1", draft["reminder_id"])
|
||||
_data, claim = repository.get_claim(member.member_id, "claim-1")
|
||||
assert claim_total(claim) == Decimal("107.50")
|
||||
|
||||
|
||||
def test_revert_reminder_sent_rejects_when_higher_level_exists(tmp_path) -> None:
|
||||
repository, member = _overdue_claim_repository(tmp_path)
|
||||
first = repository.create_reminder_draft(
|
||||
member.member_id,
|
||||
"claim-1",
|
||||
level=1,
|
||||
name="Zahlungserinnerung",
|
||||
payment_deadline_days=14,
|
||||
items=[],
|
||||
)
|
||||
repository.mark_reminder_sent(member.member_id, "claim-1", first["reminder_id"])
|
||||
repository.create_reminder_draft(
|
||||
member.member_id,
|
||||
"claim-1",
|
||||
level=2,
|
||||
name="Erste Mahnung",
|
||||
payment_deadline_days=14,
|
||||
items=[{"description": "Mahngebühr", "amount": "5.00"}],
|
||||
)
|
||||
|
||||
with pytest.raises(RepositoryError, match="höhere Mahnstufe"):
|
||||
repository.revert_reminder_sent(member.member_id, "claim-1", first["reminder_id"])
|
||||
|
||||
|
||||
def test_revert_reminder_sent_rejects_when_payment_already_covers_fee(tmp_path) -> None:
|
||||
repository, member = _overdue_claim_repository(tmp_path)
|
||||
draft = repository.create_reminder_draft(
|
||||
member.member_id,
|
||||
"claim-1",
|
||||
level=1,
|
||||
name="Rücklastschrift",
|
||||
payment_deadline_days=14,
|
||||
items=[{"description": "Rücklastschriftgebühr", "amount": "5.00"}],
|
||||
)
|
||||
repository.mark_reminder_sent(member.member_id, "claim-1", draft["reminder_id"])
|
||||
repository.record_payment(
|
||||
member.member_id,
|
||||
"claim-1",
|
||||
payment_date="2026-02-10",
|
||||
amount="105.00",
|
||||
allocation_amount="105.00",
|
||||
)
|
||||
|
||||
with pytest.raises(RepositoryError, match="bereits Zahlungen verbucht"):
|
||||
repository.revert_reminder_sent(member.member_id, "claim-1", draft["reminder_id"])
|
||||
|
||||
|
||||
def test_reminder_policy_backfills_standard_fee_items_for_old_repositories(tmp_path) -> None:
|
||||
repository = MemberRepository(tmp_path)
|
||||
repository.initialize()
|
||||
|
||||
Reference in New Issue
Block a user