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,49 @@
|
||||
"""Schemata für den zweistufigen Kategoriebaum."""
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from app.models.enums import EntryKind
|
||||
from app.schemas.common import ApiModel, HexColor, InputModel
|
||||
|
||||
|
||||
class CategoryCreate(InputModel):
|
||||
name: str = Field(min_length=1, max_length=120, examples=["Streaming"])
|
||||
kind: EntryKind = Field(description="Wird bei Unterkategorien vom Elternknoten übernommen.")
|
||||
parent_id: int | None = Field(
|
||||
default=None, description="Nur Oberkategorien zulässig – der Baum ist zweistufig."
|
||||
)
|
||||
color: HexColor = "#64748b"
|
||||
icon: str = Field(default="circle", max_length=64)
|
||||
is_fixed_cost: bool = Field(
|
||||
default=False, description="Zählt in die Kennzahl 'Verfügbar nach Fixkosten'."
|
||||
)
|
||||
sort_order: int = 0
|
||||
is_archived: bool = False
|
||||
|
||||
|
||||
class CategoryUpdate(InputModel):
|
||||
name: str | None = Field(default=None, min_length=1, max_length=120)
|
||||
parent_id: int | None = None
|
||||
color: HexColor | None = None
|
||||
icon: str | None = Field(default=None, max_length=64)
|
||||
is_fixed_cost: bool | None = None
|
||||
sort_order: int | None = None
|
||||
is_archived: bool | None = None
|
||||
|
||||
|
||||
class CategoryOut(ApiModel):
|
||||
id: int
|
||||
parent_id: int | None
|
||||
name: str
|
||||
kind: EntryKind
|
||||
color: str
|
||||
icon: str
|
||||
is_fixed_cost: bool
|
||||
sort_order: int
|
||||
is_archived: bool
|
||||
|
||||
|
||||
class CategoryTreeOut(CategoryOut):
|
||||
"""Oberkategorie mit ihren Unterkategorien."""
|
||||
|
||||
children: list[CategoryOut] = Field(default_factory=list)
|
||||
Reference in New Issue
Block a user