"""Business logic for vacation days. The service owns rules; the repository only persists. Routers call services, not repositories directly. This is the REFERENCE service — keep services thin and free of HTTP/DB details. """ from datetime import datetime from uuid import UUID, uuid4 from app.entities.vacation_days import VacationDays from app.repositories.vacation_days_repository import VacationDaysRepository from app.utils.errors import NotFoundError, ValidationError class VacationDaysService: def __init__(self, repo: VacationDaysRepository) -> None: self._repo = repo async def upsert( self, personal_data_uuid: UUID, workspace_id: UUID, year: int, total_days: int, saved_days_in: int = 0, ) -> VacationDays: """Create or update the (parent, year) vacation entry. Raises ValidationError on negative inputs (schema also guards this, but a service should not trust its caller). """ if total_days < 0 or saved_days_in < 0: raise ValidationError("Vacation day counts cannot be negative") existing = await self._repo.get_by_parent_year(personal_data_uuid, year) if existing is not None: existing.total_days = total_days existing.saved_days_in = saved_days_in return await self._repo.update(existing.id, existing) entity = VacationDays( id=uuid4(), personal_data_uuid=personal_data_uuid, workspace_id=workspace_id, year=year, total_days=total_days, saved_days_in=saved_days_in, created_at=datetime.now(), updated_at=datetime.now(), ) return await self._repo.create(entity) async def get(self, vacation_days_id: UUID, workspace_id: UUID | None = None) -> VacationDays: entity = await self._repo.get_by_id(vacation_days_id) if entity is None or (workspace_id is not None and entity.workspace_id != workspace_id): raise NotFoundError(f"VacationDays {vacation_days_id} not found") return entity async def list_for_workspace(self, workspace_id: UUID) -> list[VacationDays]: return await self._repo.list(workspace_id=workspace_id)