"""Employee self-service surface for close-task owners — `/api/v1/me/close-tasks`. Self-scoped to the caller: list my close tasks, tick status, annotate. No finance role needed — owning the task is the authority (the service enforces ``owner_person_id == caller``). Leaver-gated + audited like the rest of /me. """ from __future__ import annotations import uuid from typing import Annotated from fastapi import APIRouter, Depends, Query, Request from sqlalchemy.ext.asyncio import AsyncSession from app.audit.service import AuditService from app.auth.models import AuthUser from app.core.database import get_db from app.core.dependencies import ( check_sensitive_endpoint_rate_limit, get_current_user, ) from app.core.dependencies import ( require_active_employee_for_mutations as _leaver_runtime_gate, ) from app.core.response import success_response from app.domains.finance import close_self_service as selfsvc from app.domains.finance import close_serializers as ser from app.domains.finance.close_task_schemas import TaskCommentBody, TaskStatusBody router = APIRouter( prefix="/api/v1/me/close-tasks", tags=["me-finance-close"], dependencies=[Depends(_leaver_runtime_gate)], ) def _trace_id(request: Request) -> str: return getattr(request.state, "trace_id", None) or str(uuid.uuid4()) def _serialize(payload: dict) -> dict: return {**payload, "tasks": [ser.task_to_dict(t) for t in payload["tasks"]]} @router.get("") async def my_close_tasks( request: Request, period: Annotated[str | None, Query()] = None, user: AuthUser = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """My close tasks for a period (default: my latest period).""" payload = await selfsvc.my_close_tasks(db, person_id=str(user.id), period=period) return success_response(data=_serialize(payload), request=request) @router.post("/{task_id}/status") async def set_my_status( request: Request, task_id: int, body: TaskStatusBody, user: AuthUser = Depends(get_current_user), db: AsyncSession = Depends(get_db), _rl: None = Depends(check_sensitive_endpoint_rate_limit), ): task = await selfsvc.set_my_task_status( db, person_id=str(user.id), task_id=task_id, status=body.status, ) await AuditService(db).log( trace_id=_trace_id(request), actor_type="user", actor_user_id=user.id, action="finance.close.my_task_status", status="success", resource_type="finance.close_task", resource_id=str(task_id), detail={"status": body.status}, ) await db.commit() return success_response(data=ser.task_to_dict(task), request=request) @router.post("/{task_id}/comment") async def set_my_comment( request: Request, task_id: int, body: TaskCommentBody, user: AuthUser = Depends(get_current_user), db: AsyncSession = Depends(get_db), _rl: None = Depends(check_sensitive_endpoint_rate_limit), ): task = await selfsvc.set_my_task_comment( db, person_id=str(user.id), person_label=user.display_name, task_id=task_id, comment=body.comment, ) await AuditService(db).log( trace_id=_trace_id(request), actor_type="user", actor_user_id=user.id, action="finance.close.my_task_comment", status="success", resource_type="finance.close_task", resource_id=str(task_id), ) await db.commit() return success_response(data=ser.task_to_dict(task), request=request)