5e96c352fb
The create and update handlers for a planned day accepted both fields in their request models and passed neither to the service, so a day hand-edited in the calendar was never marked as pinned and never kept its link back to the period that produced it. The allocator would then feel free to retype it on the next sync, which is precisely what pinning exists to prevent. Two investigations alongside this found no defect. A period that occurs once rather than yearly does get placed, and a window that wraps past New Year keeps the half that falls after today; both are now covered by tests, since neither case had any. Where those looked broken, the wish had simply run out of budget for that year, which the interface does not yet explain.
107 lines
3.5 KiB
Python
107 lines
3.5 KiB
Python
"""HTTP routes for planned leave days (calendar planning)."""
|
|
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, HTTPException, Query, status
|
|
|
|
from app.dependencies import CurrentUserDep, PlanningServiceDep
|
|
from app.models.planned_leave import (
|
|
PlannedLeaveDayCreate,
|
|
PlannedLeaveDayResponse,
|
|
PlannedLeaveDayUpdate,
|
|
)
|
|
from app.utils.errors import NotFoundError, ValidationError
|
|
|
|
router = APIRouter(prefix="/api/planned-days", tags=["planned-days"])
|
|
|
|
|
|
def _to_response(day) -> PlannedLeaveDayResponse:
|
|
return PlannedLeaveDayResponse.from_entity(day)
|
|
|
|
|
|
@router.post("", response_model=PlannedLeaveDayResponse, status_code=status.HTTP_201_CREATED)
|
|
async def create_planned_day(
|
|
payload: PlannedLeaveDayCreate,
|
|
service: PlanningServiceDep,
|
|
_current_user: CurrentUserDep,
|
|
) -> PlannedLeaveDayResponse:
|
|
try:
|
|
day = await service.add_planned_day(
|
|
workspace_id=payload.workspace_id,
|
|
child_id=payload.child_id,
|
|
parent_personal_data_uuid=payload.parent_personal_data_uuid,
|
|
date=payload.date,
|
|
day_type=payload.day_type,
|
|
is_dubbeldag=payload.is_dubbeldag,
|
|
wish_id=payload.wish_id,
|
|
is_pinned=payload.is_pinned,
|
|
)
|
|
return _to_response(day)
|
|
except ValidationError as exc:
|
|
raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, str(exc)) from exc
|
|
|
|
|
|
@router.get("", response_model=list[PlannedLeaveDayResponse])
|
|
async def list_planned_days(
|
|
service: PlanningServiceDep,
|
|
_current_user: CurrentUserDep,
|
|
child_id: UUID = Query(...),
|
|
) -> list[PlannedLeaveDayResponse]:
|
|
days = await service.list_by_child(child_id)
|
|
return [_to_response(d) for d in days]
|
|
|
|
|
|
@router.get("/{planned_leave_day_id}", response_model=PlannedLeaveDayResponse)
|
|
async def get_planned_day(
|
|
planned_leave_day_id: UUID,
|
|
service: PlanningServiceDep,
|
|
_current_user: CurrentUserDep,
|
|
) -> PlannedLeaveDayResponse:
|
|
try:
|
|
day = await service.get(planned_leave_day_id)
|
|
return _to_response(day)
|
|
except NotFoundError as exc:
|
|
raise HTTPException(status.HTTP_404_NOT_FOUND, str(exc)) from exc
|
|
|
|
|
|
@router.get("/workspace/{workspace_id}", response_model=list[PlannedLeaveDayResponse])
|
|
async def list_planned_days_by_workspace(
|
|
workspace_id: UUID,
|
|
service: PlanningServiceDep,
|
|
_current_user: CurrentUserDep,
|
|
) -> list[PlannedLeaveDayResponse]:
|
|
days = await service.list_by_workspace(workspace_id)
|
|
return [_to_response(d) for d in days]
|
|
|
|
|
|
@router.put("/{planned_leave_day_id}", response_model=PlannedLeaveDayResponse)
|
|
async def update_planned_day(
|
|
planned_leave_day_id: UUID,
|
|
payload: PlannedLeaveDayUpdate,
|
|
service: PlanningServiceDep,
|
|
_current_user: CurrentUserDep,
|
|
) -> PlannedLeaveDayResponse:
|
|
try:
|
|
day = await service.update(
|
|
planned_leave_day_id,
|
|
day_type=payload.day_type,
|
|
is_dubbeldag=payload.is_dubbeldag,
|
|
wish_id=payload.wish_id,
|
|
is_pinned=payload.is_pinned,
|
|
)
|
|
return _to_response(day)
|
|
except NotFoundError as exc:
|
|
raise HTTPException(status.HTTP_404_NOT_FOUND, str(exc)) from exc
|
|
|
|
|
|
@router.delete("/{planned_leave_day_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def delete_planned_day(
|
|
planned_leave_day_id: UUID,
|
|
service: PlanningServiceDep,
|
|
_current_user: CurrentUserDep,
|
|
) -> None:
|
|
try:
|
|
await service.remove_planned_day(planned_leave_day_id)
|
|
except NotFoundError as exc:
|
|
raise HTTPException(status.HTTP_404_NOT_FOUND, str(exc)) from exc
|