- Repo-Struktur für Backend, Frontend, Dokumentation und Gitea-Workflows - FastAPI-Skeleton mit pydantic-settings und RFC-7807-artigem Fehlerformat - Vollständiges Datenmodell (15 Tabellen) als SQLAlchemy-2.0-Modelle - Alembic gegen die Async-Engine inklusive Erstmigration - Idempotenter Seed für den deutschen Kategoriebaum und Standardregeln - Endpunkte /api/health und /api/version - pytest-Infrastruktur mit transaktionsisolierten Fixtures Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014e7t8UpmoVNMtWivY5LiSH
111 lines
3.6 KiB
Python
111 lines
3.6 KiB
Python
"""Deutscher Standard-Kategoriebaum für den Erst-Seed."""
|
|
|
|
from dataclasses import dataclass, field
|
|
|
|
from app.models.enums import EntryKind
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class SeedCategory:
|
|
"""Ein Knoten des Seed-Baums. Unterkategorien erben `kind` vom Elternknoten."""
|
|
|
|
name: str
|
|
icon: str = "circle"
|
|
color: str = "#64748b"
|
|
is_fixed_cost: bool = False
|
|
children: tuple["SeedCategory", ...] = field(default_factory=tuple)
|
|
|
|
|
|
# Ausgaben ----------------------------------------------------------------------
|
|
EXPENSE_TREE: tuple[SeedCategory, ...] = (
|
|
SeedCategory(
|
|
name="Wohnen",
|
|
icon="house",
|
|
color="#f97316",
|
|
is_fixed_cost=True,
|
|
children=(
|
|
SeedCategory("Miete", "key-round", "#f97316", True),
|
|
SeedCategory("Nebenkosten", "droplets", "#fb923c", True),
|
|
SeedCategory("Strom", "zap", "#facc15", True),
|
|
SeedCategory("Internet", "wifi", "#38bdf8", True),
|
|
SeedCategory("Rundfunkbeitrag", "radio", "#a78bfa", True),
|
|
),
|
|
),
|
|
SeedCategory(
|
|
name="Versicherungen",
|
|
icon="shield",
|
|
color="#0ea5e9",
|
|
is_fixed_cost=True,
|
|
children=(
|
|
SeedCategory("Haftpflicht", "shield-check", "#0ea5e9", True),
|
|
SeedCategory("Hausrat", "shield-half", "#22d3ee", True),
|
|
SeedCategory("Kfz", "car-front", "#38bdf8", True),
|
|
SeedCategory("Kranken", "heart-pulse", "#f472b6", True),
|
|
SeedCategory("BU", "briefcase-medical", "#818cf8", True),
|
|
),
|
|
),
|
|
SeedCategory(
|
|
name="Abos & Medien",
|
|
icon="repeat",
|
|
color="#a855f7",
|
|
is_fixed_cost=True,
|
|
children=(
|
|
SeedCategory("Streaming", "clapperboard", "#e11d48", True),
|
|
SeedCategory("Software", "app-window", "#8b5cf6", True),
|
|
SeedCategory("Cloud", "cloud", "#60a5fa", True),
|
|
SeedCategory("Zeitungen", "newspaper", "#94a3b8", True),
|
|
),
|
|
),
|
|
SeedCategory(
|
|
name="Mobilität",
|
|
icon="car",
|
|
color="#14b8a6",
|
|
children=(
|
|
SeedCategory("Kfz-Steuer", "landmark", "#14b8a6", True),
|
|
SeedCategory("Sprit", "fuel", "#f59e0b"),
|
|
SeedCategory("ÖPNV", "bus", "#10b981", True),
|
|
SeedCategory("Werkstatt", "wrench", "#64748b"),
|
|
),
|
|
),
|
|
SeedCategory(
|
|
name="Lebenshaltung",
|
|
icon="shopping-basket",
|
|
color="#84cc16",
|
|
children=(
|
|
SeedCategory("Lebensmittel", "shopping-cart", "#84cc16"),
|
|
SeedCategory("Drogerie", "sparkles", "#22c55e"),
|
|
SeedCategory("Restaurant", "utensils", "#f97316"),
|
|
),
|
|
),
|
|
SeedCategory(
|
|
name="Finanzen",
|
|
icon="banknote",
|
|
color="#6366f1",
|
|
children=(
|
|
SeedCategory("Kredite", "hand-coins", "#ef4444", True),
|
|
SeedCategory("Sparen", "piggy-bank", "#10b981", True),
|
|
SeedCategory("Gebühren", "receipt", "#94a3b8", True),
|
|
),
|
|
),
|
|
)
|
|
|
|
# Einkünfte ---------------------------------------------------------------------
|
|
INCOME_TREE: tuple[SeedCategory, ...] = (
|
|
SeedCategory(
|
|
name="Einkünfte",
|
|
icon="wallet",
|
|
color="#22c55e",
|
|
children=(
|
|
SeedCategory("Gehalt", "briefcase", "#22c55e"),
|
|
SeedCategory("Nebeneinkünfte", "laptop", "#4ade80"),
|
|
SeedCategory("Erstattungen", "undo-2", "#34d399"),
|
|
SeedCategory("Zinsen", "percent", "#a3e635"),
|
|
),
|
|
),
|
|
)
|
|
|
|
SEED_TREE: tuple[tuple[EntryKind, tuple[SeedCategory, ...]], ...] = (
|
|
(EntryKind.EXPENSE, EXPENSE_TREE),
|
|
(EntryKind.INCOME, INCOME_TREE),
|
|
)
|