feat: add per-member payment frequencies

This commit is contained in:
Marcel Peterkau
2026-07-30 19:17:26 +02:00
parent 6d9c50b148
commit c3cdf71506
8 changed files with 168 additions and 6 deletions
@@ -9,6 +9,21 @@ from ccma.rules.scripts._shared import CONTRIBUTION_STATUSES
RULE_ID = "contribution-claims"
ORDER = 40
CENT = Decimal("0.01")
MONTH_NAMES = (
"",
"Januar",
"Februar",
"März",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Dezember",
)
def evaluate(context: RuleContext):
@@ -85,6 +100,26 @@ def _membership_claims(
("first-half", 1, 6, _due_date(year, configured_due_dates[0], "01-31")),
("second-half", 7, 12, _due_date(year, configured_due_dates[1], "07-31")),
]
elif member.payment_frequency == "quarterly":
periods = [
(
f"quarter-{quarter}",
first_month,
first_month + 2,
_recurring_due_date(year, first_month, rule.get("annual_due"), "01-31"),
)
for quarter, first_month in enumerate((1, 4, 7, 10), start=1)
]
elif member.payment_frequency == "monthly":
periods = [
(
f"month-{month:02d}",
month,
month,
_recurring_due_date(year, month, rule.get("annual_due"), "01-31"),
)
for month in range(1, 13)
]
else:
periods = [("annual", 1, 12, _due_date(year, rule.get("annual_due"), "01-31"))]
@@ -168,7 +203,21 @@ def _due_date(year: int, value, fallback: str) -> date:
return date(year, month, day)
def _recurring_due_date(year: int, month: int, value, fallback: str) -> date:
try:
_configured_month, day = (int(part) for part in str(value or fallback).split("-", 1))
except (TypeError, ValueError):
day = int(fallback.split("-", 1)[1])
return date(year, month, min(day, calendar.monthrange(year, month)[1]))
def _title(year: int, suffix: str) -> str:
if suffix.startswith("month-"):
month = int(suffix.removeprefix("month-"))
return f"Mitgliedsbeitrag {MONTH_NAMES[month]} {year}"
if suffix.startswith("quarter-"):
quarter = int(suffix.removeprefix("quarter-"))
return f"Mitgliedsbeitrag {quarter}. Quartal {year}"
if suffix == "first-half":
return f"Mitgliedsbeitrag 1. Halbjahr {year}"
if suffix == "second-half":