Files
moneyfy/backend/app/api/deps.py
T
Jonas MenzelandClaude Opus 5 0a6261fc55
CI / backend (push) Successful in 2m33s
Images bauen / build (backend) (push) Successful in 3m26s
Images bauen / build (frontend) (push) Successful in 4m4s
CI / frontend (push) Successful in 6m32s
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
2026-09-10 09:52:51 +02:00

65 lines
2.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Wiederverwendbare FastAPI-Dependencies."""
from typing import Annotated
from fastapi import Depends, Request
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.cookies import ACCESS_COOKIE
from app.core.errors import AppError, AuthError
from app.core.security import decode_token
from app.db.session import get_session
from app.models import AppUser
from app.services.auth import get_user
from app.services.settings import month_start_day
DbSession = Annotated[AsyncSession, Depends(get_session)]
class PasswordChangeRequiredError(AppError):
"""Der Zugriff ist erst nach dem erzwungenen Passwortwechsel möglich."""
status_code = 403
code = "password_change_required"
async def get_current_user(request: Request, session: DbSession) -> AppUser:
"""Liest das Access-Token aus dem Cookie und lädt den Benutzer."""
token = request.cookies.get(ACCESS_COOKIE)
if not token:
raise AuthError("Nicht angemeldet.", code="not_authenticated")
payload = decode_token(token, "access")
try:
user_id = int(payload["sub"])
except (KeyError, TypeError, ValueError) as exc:
raise AuthError("Ungültiges Token.", code="invalid_token") from exc
user = await get_user(session, user_id)
if user is None:
raise AuthError("Der Benutzer existiert nicht mehr.", code="invalid_token")
return user
CurrentUser = Annotated[AppUser, Depends(get_current_user)]
async def get_active_user(user: CurrentUser) -> AppUser:
"""Wie `get_current_user`, verlangt aber einen abgeschlossenen Passwortwechsel."""
if user.must_change_password:
raise PasswordChangeRequiredError(
"Das Passwort muss zuerst geändert werden.",
)
return user
ActiveUser = Annotated[AppUser, Depends(get_active_user)]
async def get_month_start_day(session: DbSession) -> int:
"""Der eingestellte Monatsbeginn jede monatsbezogene Auswertung richtet sich danach."""
return await month_start_day(session)
MonthStartDay = Annotated[int, Depends(get_month_start_day)]