feat: einstellbarer Monatsbeginn zum Gehaltstag
Wer nach dem Gehaltseingang plant, stellt unter Einstellungen den Tag ein, ab dem ein neuer Monat zählt. Der Zeitraum läuft dann vom Gehaltstag bis zum Vortag des nächsten und trägt den Namen des Monats, in dem er beginnt: Mit dem 25. umfasst „September 2026“ den 25.09. bis zum 24.10. Ein Starttag jenseits der Monatslänge rutscht auf den Monatsletzten, sodass 31 verlässlich den letzten Tag des Monats meint. Dashboard, Cashflow-Kalender, Budgets, Zwölf-Monats-Vorschau, die Kategorienauswertung, der Monatsexport und die Benachrichtigung über überschrittene Budgets rechnen mit diesem Zeitraum. Budgets bleiben je Monat gepflegt; der Bezeichner ist weiterhin der Monatserste, nur der Schnitt verschiebt sich. Bestandsinstallationen bleiben beim Ersten. Die Einstellung liegt in einer einzeiligen Tabelle hinter GET/PUT /api/settings; die Monatsauswertungen liefern zusätzlich period_start und period_end. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fj7mB1PGA1aDHyfdSGHgzD
This commit is contained in:
co-authored by
Claude Opus 5
parent
998d5867df
commit
0a6261fc55
@@ -44,3 +44,45 @@ def add_months(day: date, months: int) -> date:
|
||||
month = total % 12 + 1
|
||||
last_day = month_end(date(year, month, 1)).day
|
||||
return date(year, month, min(day.day, last_day))
|
||||
|
||||
|
||||
# --- Abrechnungsmonat ----------------------------------------------------------
|
||||
#
|
||||
# Wer am Gehaltstag rechnet, für den beginnt der Monat nicht am Ersten. Ein
|
||||
# Abrechnungsmonat läuft vom Gehaltstag bis zum Vortag des nächsten und trägt
|
||||
# den Namen des Monats, in dem er beginnt: Startet er am 25. September, heißt
|
||||
# er „September“ und endet am 24. Oktober.
|
||||
|
||||
MIN_MONTH_START_DAY = 1
|
||||
MAX_MONTH_START_DAY = 31
|
||||
DEFAULT_MONTH_START_DAY = 1
|
||||
|
||||
|
||||
def month_anchor(day: date, start_day: int) -> date:
|
||||
"""Der Gehaltstag im Monat von `day`.
|
||||
|
||||
Ein Starttag jenseits der Monatslänge rutscht auf den Monatsletzten – so
|
||||
trifft der 31. in jedem Monat den letzten Tag.
|
||||
"""
|
||||
return day.replace(day=min(start_day, month_end(day).day))
|
||||
|
||||
|
||||
def period_start(day: date, start_day: int) -> date:
|
||||
"""Beginn des Abrechnungsmonats, in dem `day` liegt."""
|
||||
anchor = month_anchor(day, start_day)
|
||||
if day >= anchor:
|
||||
return anchor
|
||||
return month_anchor(add_months(month_start(day), -1), start_day)
|
||||
|
||||
|
||||
def period_key(day: date, start_day: int) -> date:
|
||||
"""Bezeichner des Abrechnungsmonats, in dem `day` liegt – immer ein Monatserster."""
|
||||
return month_start(period_start(day, start_day))
|
||||
|
||||
|
||||
def period_bounds(month: date, start_day: int) -> tuple[date, date]:
|
||||
"""Erster und letzter Tag des Abrechnungsmonats mit dem Bezeichner `month`."""
|
||||
schluessel = month_start(month)
|
||||
beginn = month_anchor(schluessel, start_day)
|
||||
ende = month_anchor(add_months(schluessel, 1), start_day) - timedelta(days=1)
|
||||
return beginn, ende
|
||||
|
||||
Reference in New Issue
Block a user