from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from typing import List from app.core.database import get_db from app.models.department import Department from app.models.user import User from app.schemas.department import DepartmentCreate, DepartmentUpdate, DepartmentResponse from app.middleware.auth import require_permission, require_system_admin router = APIRouter() @router.get("", response_model=List[DepartmentResponse]) async def list_departments( skip: int = 0, limit: int = 100, db: Session = Depends(get_db), current_user: User = Depends(require_permission("users.read")), ): """ List all departments. """ departments = db.query(Department).offset(skip).limit(limit).all() return departments @router.get("/{department_id}", response_model=DepartmentResponse) async def get_department( department_id: str, db: Session = Depends(get_db), current_user: User = Depends(require_permission("users.read")), ): """ Get a specific department by ID. """ department = db.query(Department).filter(Department.id == department_id).first() if not department: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Department not found", ) return department @router.post("", response_model=DepartmentResponse, status_code=status.HTTP_201_CREATED) async def create_department( department_data: DepartmentCreate, db: Session = Depends(get_db), current_user: User = Depends(require_system_admin), ): """ Create a new department. Requires system admin. """ # Check if parent exists if specified if department_data.parent_id: parent = db.query(Department).filter( Department.id == department_data.parent_id ).first() if not parent: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Parent department not found", ) department = Department(**department_data.model_dump()) db.add(department) db.commit() db.refresh(department) return department @router.patch("/{department_id}", response_model=DepartmentResponse) async def update_department( department_id: str, department_update: DepartmentUpdate, db: Session = Depends(get_db), current_user: User = Depends(require_system_admin), ): """ Update a department. Requires system admin. """ department = db.query(Department).filter(Department.id == department_id).first() if not department: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Department not found", ) # Check if new parent exists if specified update_data = department_update.model_dump(exclude_unset=True) if "parent_id" in update_data and update_data["parent_id"]: # Prevent circular reference if update_data["parent_id"] == department_id: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Department cannot be its own parent", ) parent = db.query(Department).filter( Department.id == update_data["parent_id"] ).first() if not parent: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Parent department not found", ) for field, value in update_data.items(): setattr(department, field, value) db.commit() db.refresh(department) return department @router.delete("/{department_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_department( department_id: str, db: Session = Depends(get_db), current_user: User = Depends(require_system_admin), ): """ Delete a department. Requires system admin. """ department = db.query(Department).filter(Department.id == department_id).first() if not department: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Department not found", ) # Check if department has users user_count = db.query(User).filter(User.department_id == department_id).count() if user_count > 0: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Cannot delete department with {user_count} users", ) # Check if department has children child_count = db.query(Department).filter( Department.parent_id == department_id ).count() if child_count > 0: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Cannot delete department with {child_count} child departments", ) db.delete(department) db.commit()