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:
moneyfy
2026-09-09 13:40:37 +02:00
co-authored by Claude Opus 5
parent 70d73cf8d3
commit b586d27b77
46 changed files with 4866 additions and 21 deletions
+64
View File
@@ -0,0 +1,64 @@
"""Schemata für einzelne Fälligkeiten."""
from datetime import date
from pydantic import Field
from app.models.enums import EntryKind, OccurrenceStatus
from app.schemas.common import ApiModel, InputModel, Money, PositiveMoney
class OccurrenceOut(ApiModel):
"""Eine berechnete Fälligkeit, ggf. überlagert von einer materialisierten Zeile."""
recurrence_id: int
recurrence_title: str
kind: EntryKind
category_id: int
merchant_id: int | None
account_id: int | None
nominal_date: date = Field(
description="Von der Wiederholungsregel geliefertes Datum. Schlüssel für "
"`confirm` und `skip`, auch wenn der Zahltag verschoben ist."
)
due_date: date = Field(description="Zahltag nach Wochenend- und Feiertagsverschiebung.")
effective_date: date = Field(description="Ist-Datum, sonst der Zahltag.")
amount: Money = Field(description="Sollbetrag laut Preishistorie.")
actual_amount: Money | None = None
effective_amount: Money = Field(description="Ist-Betrag, sonst Soll. Ausgelassene zählen 0.")
status: OccurrenceStatus
is_variable: bool
occurrence_id: int | None = None
note: str | None = None
installment_number: int | None = None
installments_total: int | None = None
class OccurrenceConfirm(InputModel):
"""Bestätigt eine Fälligkeit, wahlweise mit abweichendem Betrag oder Datum."""
recurrence_id: int
occurrence_date: date = Field(description="Das nominale Datum aus `OccurrenceOut`.")
actual_amount: PositiveMoney | None = Field(
default=None, description="Ohne Angabe gilt der Sollbetrag."
)
actual_date: date | None = Field(
default=None, description="Ohne Angabe gilt der berechnete Zahltag."
)
account_id: int | None = Field(default=None, description="Abweichendes Konto.")
note: str | None = None
class OccurrenceSkip(InputModel):
"""Markiert eine Fälligkeit als ausgefallen; sie zählt danach nirgends mehr mit."""
recurrence_id: int
occurrence_date: date
note: str | None = None
class OccurrenceReset(InputModel):
"""Nimmt eine Bestätigung oder Auslassung zurück."""
recurrence_id: int
occurrence_date: date