feat: Recurrence-Terminberechnung
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
25
finance/app/engine/recurrence.py
Normal file
25
finance/app/engine/recurrence.py
Normal file
@@ -0,0 +1,25 @@
|
||||
import calendar
|
||||
from datetime import date
|
||||
|
||||
STEP = {"monthly": 1, "quarterly": 3, "yearly": 12}
|
||||
|
||||
|
||||
def _clamp(y: int, m: int, day: int) -> date:
|
||||
return date(y, m, min(day, calendar.monthrange(y, m)[1]))
|
||||
|
||||
|
||||
def occurrences(rhythm: str, due_day: int, window_start: date, window_end: date,
|
||||
item_start: date | None = None, item_end: date | None = None) -> list[date]:
|
||||
step = STEP[rhythm]
|
||||
lo = max(window_start, item_start) if item_start else window_start
|
||||
hi = min(window_end, item_end) if item_end else window_end
|
||||
anchor = item_start or window_start
|
||||
d = _clamp(anchor.year, anchor.month, due_day)
|
||||
out: list[date] = []
|
||||
while d <= hi:
|
||||
if d >= lo:
|
||||
out.append(d)
|
||||
total = d.year * 12 + d.month - 1 + step
|
||||
y, m0 = divmod(total, 12)
|
||||
d = _clamp(y, m0 + 1, due_day)
|
||||
return out
|
||||
19
finance/tests/test_recurrence.py
Normal file
19
finance/tests/test_recurrence.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from datetime import date
|
||||
|
||||
from app.engine.recurrence import occurrences
|
||||
|
||||
|
||||
def test_monthly_clamps_short_months():
|
||||
got = occurrences("monthly", 31, date(2026, 1, 1), date(2026, 3, 31))
|
||||
assert got == [date(2026, 1, 31), date(2026, 2, 28), date(2026, 3, 31)]
|
||||
|
||||
|
||||
def test_quarterly_respects_item_bounds():
|
||||
got = occurrences("quarterly", 15, date(2026, 1, 1), date(2026, 12, 31),
|
||||
item_start=date(2026, 4, 1), item_end=date(2026, 10, 31))
|
||||
assert got == [date(2026, 4, 15), date(2026, 7, 15), date(2026, 10, 15)]
|
||||
|
||||
|
||||
def test_yearly():
|
||||
got = occurrences("yearly", 1, date(2026, 1, 1), date(2028, 12, 31))
|
||||
assert got == [date(2026, 1, 1), date(2027, 1, 1), date(2028, 1, 1)]
|
||||
Reference in New Issue
Block a user