Files
F-r-ldraledigplannerare/backend/app/routers/planned_days.py
T
jacobh2 5e96c352fb Persist the wish and pinned flag a planned day is created with
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.
2026-09-17 21:04:44 +02:00

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