feat(api): Core-API mit Authentifizierung, CRUD und Monatsreport
- Anmeldung über Argon2id und JWT in httpOnly-Cookies, Refresh mit echter Rotation über die neue Tabelle refresh_token - AuthProvider-Protokoll als Vorbereitung für OIDC, Administrator-Anlage beim Erststart mit erzwungenem Passwortwechsel - CRUD für Konten, Kategorien (zweistufiger Baum), Firmen, Recurrences, Preisversionen, Buchungen, Budgets, Vorlagen und Sparziele - Fälligkeiten mit Overlay-Logik: abrufen, bestätigen, auslassen, zurücksetzen - Kontosalden zum Stichtag, Monatsübersicht mit Plan-Ist-Vergleich - SECRET_KEY jetzt mindestens 32 Zeichen; Platzhalter in Produktion abgelehnt - 61 neue Integrationstests, insgesamt 148 grün Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014e7t8UpmoVNMtWivY5LiSH
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
"""Auswertungen."""
|
||||
|
||||
from datetime import date
|
||||
|
||||
from fastapi import APIRouter, Query
|
||||
|
||||
from app.api.deps import DbSession
|
||||
from app.core.clock import today
|
||||
from app.schemas.report import MonthComparisonOut, MonthReportOut, TotalsOut
|
||||
from app.services.reports import Totals, month_report
|
||||
|
||||
router = APIRouter(prefix="/reports", tags=["reports"])
|
||||
|
||||
|
||||
def _totals(value: Totals) -> TotalsOut:
|
||||
return TotalsOut(income=value.income, expenses=value.expenses, balance=value.balance)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/month",
|
||||
response_model=MonthReportOut,
|
||||
summary="Monatsübersicht",
|
||||
description="Einnahmen, Ausgaben, Saldo, Plan-Ist-Vergleich, Aufteilung in fixe "
|
||||
"und variable Kosten sowie die Veränderung gegenüber dem Vormonat.",
|
||||
)
|
||||
async def read_month_report(
|
||||
session: DbSession,
|
||||
month: date | None = Query(
|
||||
default=None, description="Beliebiger Tag im gewünschten Monat; Vorgabe ist heute."
|
||||
),
|
||||
) -> MonthReportOut:
|
||||
report = await month_report(session, month or today())
|
||||
return MonthReportOut(
|
||||
month=report.month,
|
||||
planned=_totals(report.planned),
|
||||
actual=_totals(report.actual),
|
||||
previous_planned=_totals(report.previous_planned),
|
||||
previous_actual=_totals(report.previous_actual),
|
||||
delta_to_previous=MonthComparisonOut(
|
||||
income=report.income_delta,
|
||||
expenses=report.expenses_delta,
|
||||
balance=report.balance_delta,
|
||||
),
|
||||
fixed_costs=report.fixed_costs,
|
||||
variable_costs=report.variable_costs,
|
||||
reserves=report.reserves,
|
||||
available_after_fixed=report.available_after_fixed,
|
||||
confirmed_count=report.confirmed_count,
|
||||
open_count=report.open_count,
|
||||
skipped_count=report.skipped_count,
|
||||
)
|
||||
Reference in New Issue
Block a user