"""HTTP routes for leave wishes. Routers stay thin: validate via Pydantic, call the service, map domain errors to HTTP. Business logic (recurrence/parent_mode validation, month-day range checks) lives in LeaveWishService. See PLANNING_ENGINE_SPEC.md §4. """ from uuid import UUID from fastapi import APIRouter, HTTPException, status from app.dependencies import LeaveWishServiceDep, WorkspaceIdDep, WorkspaceScopeDep from app.models.leave_wish import ( LeaveWishCreate, LeaveWishResponse, LeaveWishUpdate, ) from app.utils.errors import NotFoundError, ValidationError router = APIRouter(prefix="/api/leave-wishes", tags=["leave-wishes"]) @router.post("", response_model=LeaveWishResponse, status_code=status.HTTP_201_CREATED) async def create_leave_wish( payload: LeaveWishCreate, service: LeaveWishServiceDep, scope: WorkspaceScopeDep ) -> LeaveWishResponse: scope.check_workspace(payload.workspace_id) if payload.child_id is not None: await scope.check_child(payload.child_id) if payload.parent_personal_data_uuid is not None: await scope.check_member(payload.parent_personal_data_uuid) try: entity = await service.create( workspace_id=payload.workspace_id, child_id=payload.child_id, name=payload.name, name_message_key=payload.name_message_key, window_start_month=payload.window_start_month, window_start_day=payload.window_start_day, window_end_month=payload.window_end_month, window_end_day=payload.window_end_day, target_weeks=payload.target_weeks, target_days=payload.target_days, recurrence=payload.recurrence, first_year=payload.first_year, last_year=payload.last_year, priority=payload.priority, parent_mode=payload.parent_mode, parent_personal_data_uuid=payload.parent_personal_data_uuid, window_mode=payload.window_mode, wants_dubbeldag=payload.wants_dubbeldag, ) except ValidationError as exc: raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, str(exc)) from exc return LeaveWishResponse.from_entity(entity) @router.get("/{workspace_id}", response_model=list[LeaveWishResponse]) async def list_leave_wishes( workspace_id: WorkspaceIdDep, service: LeaveWishServiceDep ) -> list[LeaveWishResponse]: """List all leave wishes for a workspace. Matches PLANNING_ENGINE_SPEC.md §4 (`GET /api/leave-wishes/{workspace_id}` — list-by-workspace, not get-by-id).""" entities = await service.list_for_workspace(workspace_id) return [LeaveWishResponse.from_entity(e) for e in entities] @router.put("/{leave_wish_id}", response_model=LeaveWishResponse) async def update_leave_wish( leave_wish_id: UUID, payload: LeaveWishUpdate, service: LeaveWishServiceDep, scope: WorkspaceScopeDep, ) -> LeaveWishResponse: if payload.child_id is not None: await scope.check_child(payload.child_id) if payload.parent_personal_data_uuid is not None: await scope.check_member(payload.parent_personal_data_uuid) fields_set = payload.model_fields_set try: entity = await service.update( leave_wish_id=leave_wish_id, workspace_id=scope.workspace_id, child_id=payload.child_id, name=payload.name, name_message_key=payload.name_message_key, window_start_month=payload.window_start_month, window_start_day=payload.window_start_day, window_end_month=payload.window_end_month, window_end_day=payload.window_end_day, target_weeks=payload.target_weeks, target_days=payload.target_days, recurrence=payload.recurrence, first_year=payload.first_year, last_year=payload.last_year, priority=payload.priority, parent_mode=payload.parent_mode, parent_personal_data_uuid=payload.parent_personal_data_uuid, window_mode=payload.window_mode, wants_dubbeldag=payload.wants_dubbeldag, _clear_child_id="child_id" in fields_set and payload.child_id is None, _clear_last_year="last_year" in fields_set and payload.last_year is None, _clear_parent_personal_data_uuid=( "parent_personal_data_uuid" in fields_set and payload.parent_personal_data_uuid is None ), _clear_name_message_key=( "name_message_key" in fields_set and payload.name_message_key is None ), _clear_target_days="target_days" in fields_set and payload.target_days is None, ) except ValidationError as exc: raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, str(exc)) from exc except NotFoundError as exc: raise HTTPException(status.HTTP_404_NOT_FOUND, str(exc)) from exc return LeaveWishResponse.from_entity(entity) @router.delete("/{leave_wish_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_leave_wish( leave_wish_id: UUID, service: LeaveWishServiceDep, scope: WorkspaceScopeDep ) -> None: try: await service.delete(leave_wish_id, workspace_id=scope.workspace_id) except NotFoundError as exc: raise HTTPException(status.HTTP_404_NOT_FOUND, str(exc)) from exc