"""Kategorie-Gruppen und Kategorien (zweistufig). Alles owner-gescoped. Eine Kategorie erbt ihre Art (Einnahme/Ausgabe) von ihrer Gruppe. """ from fastapi import APIRouter, Depends, status from sqlalchemy.orm import Session from ..database import get_db from ..deps import get_current_user, get_owned from ..models import Category, CategoryGroup, User from ..schemas import ( CategoryCreate, CategoryGroupCreate, CategoryGroupOut, CategoryGroupUpdate, CategoryOut, CategoryUpdate, ) router = APIRouter(prefix="/categories", tags=["categories"]) def group_out(g: CategoryGroup) -> CategoryGroupOut: cats = sorted(g.categories, key=lambda c: (c.position, c.name.lower())) return CategoryGroupOut( id=g.id, name=g.name, kind=g.kind, icon=g.icon, color=g.color, position=g.position, is_builtin=g.is_builtin, categories=[CategoryOut.model_validate(c) for c in cats], ) def _next_position(db: Session, model, **filters) -> int: q = db.query(model.position) for k, v in filters.items(): q = q.filter(getattr(model, k) == v) row = q.order_by(model.position.desc()).first() return (row[0] + 1) if row else 0 @router.get("", response_model=list[CategoryGroupOut]) def list_categories(db: Session = Depends(get_db), user: User = Depends(get_current_user)): groups = ( db.query(CategoryGroup) .filter(CategoryGroup.owner_id == user.id) .order_by(CategoryGroup.position, CategoryGroup.name) .all() ) return [group_out(g) for g in groups] # --- Gruppen --------------------------------------------------------------- @router.post("/groups", response_model=CategoryGroupOut, status_code=status.HTTP_201_CREATED) def create_group( payload: CategoryGroupCreate, db: Session = Depends(get_db), user: User = Depends(get_current_user) ): g = CategoryGroup( owner_id=user.id, name=payload.name.strip(), kind=payload.kind, icon=payload.icon, color=payload.color, position=_next_position(db, CategoryGroup, owner_id=user.id), ) db.add(g) db.commit() db.refresh(g) return group_out(g) @router.patch("/groups/{group_id}", response_model=CategoryGroupOut) def update_group( group_id: int, payload: CategoryGroupUpdate, db: Session = Depends(get_db), user: User = Depends(get_current_user), ): g = get_owned(db, CategoryGroup, group_id, user) for key, value in payload.model_dump(exclude_unset=True).items(): if value is not None: setattr(g, key, value) db.commit() db.refresh(g) return group_out(g) @router.delete("/groups/{group_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_group( group_id: int, db: Session = Depends(get_db), user: User = Depends(get_current_user) ): g = get_owned(db, CategoryGroup, group_id, user) db.delete(g) db.commit() # --- Kategorien ------------------------------------------------------------ @router.post("", response_model=CategoryOut, status_code=status.HTTP_201_CREATED) def create_category( payload: CategoryCreate, db: Session = Depends(get_db), user: User = Depends(get_current_user) ): group = get_owned(db, CategoryGroup, payload.group_id, user) cat = Category( owner_id=user.id, group_id=group.id, name=payload.name.strip(), type=group.kind, # erbt die Art der Gruppe icon=payload.icon, color=payload.color, position=_next_position(db, Category, owner_id=user.id, group_id=group.id), ) db.add(cat) db.commit() db.refresh(cat) return CategoryOut.model_validate(cat) @router.patch("/{category_id}", response_model=CategoryOut) def update_category( category_id: int, payload: CategoryUpdate, db: Session = Depends(get_db), user: User = Depends(get_current_user), ): cat = get_owned(db, Category, category_id, user) data = payload.model_dump(exclude_unset=True) new_group_id = data.pop("group_id", None) if new_group_id is not None: group = get_owned(db, CategoryGroup, new_group_id, user) cat.group_id = group.id cat.type = group.kind # Art an die neue Gruppe angleichen for key, value in data.items(): if value is not None or key == "is_archived": setattr(cat, key, value) db.commit() db.refresh(cat) return CategoryOut.model_validate(cat) @router.delete("/{category_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_category( category_id: int, db: Session = Depends(get_db), user: User = Depends(get_current_user) ): cat = get_owned(db, Category, category_id, user) db.delete(cat) db.commit()