from typing import Annotated from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import get_session, get_tenant_context, require_min_role from app.crud.plant import crud_plant, crud_plant_family from app.models.user import TenantRole from app.schemas.plant import PlantCreate, PlantFamilyRead, PlantRead, PlantUpdate router = APIRouter(tags=["Pflanzen"]) TenantCtx = Annotated[tuple, Depends(get_tenant_context)] WriteCtx = Annotated[tuple, Depends(require_min_role(TenantRole.READ_WRITE))] @router.get("/plant-families", response_model=list[PlantFamilyRead]) async def list_plant_families( db: Annotated[AsyncSession, Depends(get_session)], _: TenantCtx, ) -> list[PlantFamilyRead]: families = await crud_plant_family.get_all(db) return [PlantFamilyRead.model_validate(f) for f in families] @router.get("/plants", response_model=list[PlantRead]) async def list_plants( db: Annotated[AsyncSession, Depends(get_session)], ctx: TenantCtx, ) -> list[PlantRead]: _, tenant_id = ctx plants = await crud_plant.get_multi_for_tenant(db, tenant_id=tenant_id) return [PlantRead.model_validate(p) for p in plants] @router.get("/plants/{plant_id}", response_model=PlantRead) async def get_plant( plant_id: UUID, db: Annotated[AsyncSession, Depends(get_session)], _: TenantCtx, ) -> PlantRead: plant = await crud_plant.get(db, id=plant_id) if not plant: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Pflanze nicht gefunden.") return PlantRead.model_validate(plant) @router.post("/plants", response_model=PlantRead, status_code=status.HTTP_201_CREATED) async def create_plant( body: PlantCreate, db: Annotated[AsyncSession, Depends(get_session)], ctx: WriteCtx, ) -> PlantRead: _, tenant_id, _ = ctx plant = await crud_plant.create_for_tenant(db, obj_in=body, tenant_id=tenant_id) return PlantRead.model_validate(plant) @router.put("/plants/{plant_id}", response_model=PlantRead) async def update_plant( plant_id: UUID, body: PlantUpdate, db: Annotated[AsyncSession, Depends(get_session)], ctx: WriteCtx, ) -> PlantRead: user, tenant_id, _ = ctx plant = await crud_plant.get(db, id=plant_id) if not plant: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Pflanze nicht gefunden.") # Global plants: only superadmin if plant.tenant_id is None and not user.is_superadmin: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Globale Pflanzen können nur von Superadmins bearbeitet werden.") # Tenant plants: must belong to current tenant if plant.tenant_id is not None and plant.tenant_id != tenant_id: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Kein Zugriff auf diese Pflanze.") updated = await crud_plant.update(db, db_obj=plant, obj_in=body) return PlantRead.model_validate(updated) @router.delete("/plants/{plant_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_plant( plant_id: UUID, db: Annotated[AsyncSession, Depends(get_session)], ctx: WriteCtx, ) -> None: user, tenant_id, _ = ctx plant = await crud_plant.get(db, id=plant_id) if not plant: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Pflanze nicht gefunden.") if plant.tenant_id is None and not user.is_superadmin: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Globale Pflanzen können nur von Superadmins gelöscht werden.") if plant.tenant_id is not None and plant.tenant_id != tenant_id: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Kein Zugriff auf diese Pflanze.") await crud_plant.remove(db, id=plant_id)