""" Shared Calendar Router Factory for ROA2WEB Applications Creates a FastAPI router for /api/calendar endpoints that can be used by both the unified monolith backend. Usage: from shared.routes.calendar import create_calendar_router calendar_router = create_calendar_router(oracle_pool, cache_decorator=cached) app.include_router(calendar_router, prefix="/api/calendar") """ import logging from typing import Optional, Callable, List from fastapi import APIRouter, Depends, HTTPException, Query from auth.dependencies import get_current_user from auth.models import CurrentUser from models.calendar import CalendarPeriod, CalendarPeriodsResponse logger = logging.getLogger(__name__) # Romanian month names MONTH_NAMES_RO = [ "Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie", "Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie" ] def create_calendar_router( oracle_pool, cache_decorator: Optional[Callable] = None, tags: Optional[List[str]] = None ) -> APIRouter: """ Factory function to create a calendar router. Args: oracle_pool: The Oracle connection pool instance cache_decorator: Optional caching decorator (e.g., @cached) tags: OpenAPI tags for the router Returns: Configured FastAPI router for calendar endpoints """ router = APIRouter( redirect_slashes=False, tags=tags or ["calendar"] ) # Helper to get schema for company async def _get_schema_for_company(company_id: int) -> Optional[str]: """Get Oracle schema for company ID.""" async with oracle_pool.get_connection() as connection: with connection.cursor() as cursor: cursor.execute(""" SELECT SCHEMA FROM CONTAFIN_ORACLE.V_NOM_FIRME WHERE ID_FIRMA = :company_id """, {'company_id': company_id}) result = cursor.fetchone() return result[0] if result else None # Apply cache to schema lookup if decorator provided if cache_decorator: _get_schema_for_company = cache_decorator( cache_type='schema', key_params=['company_id'] )(_get_schema_for_company) # Helper to get periods - can be cached async def _get_available_periods(company_id: int) -> CalendarPeriodsResponse: """Get available accounting periods for a company.""" schema = await _get_schema_for_company(company_id) if not schema: logger.warning(f"Schema not found for company {company_id}") return CalendarPeriodsResponse(periods=[], current_period=None, total_count=0) try: async with oracle_pool.get_connection() as connection: with connection.cursor() as cursor: cursor.execute(f""" SELECT ANUL, LUNA FROM {schema}.CALENDAR ORDER BY ANUL DESC, LUNA DESC """) rows = cursor.fetchall() periods = [] for row in rows: an, luna = row[0], row[1] month_name = MONTH_NAMES_RO[luna - 1] periods.append(CalendarPeriod( an=an, luna=luna, display_name=f"{month_name} {an}" )) current_period = periods[0] if periods else None logger.info(f"Loaded {len(periods)} periods for company {company_id}") return CalendarPeriodsResponse( periods=periods, current_period=current_period, total_count=len(periods) ) except Exception as e: logger.error(f"Error fetching periods for company {company_id}: {e}") return CalendarPeriodsResponse(periods=[], current_period=None, total_count=0) # Apply cache decorator if provided if cache_decorator: _get_available_periods = cache_decorator( cache_type='calendar_periods', key_params=['company_id'] )(_get_available_periods) @router.get("/periods", response_model=CalendarPeriodsResponse) async def get_calendar_periods( company: int = Query(..., description="Company ID"), current_user: CurrentUser = Depends(get_current_user) ) -> CalendarPeriodsResponse: """ Get available accounting periods for a company. Returns periods ordered by year DESC, month DESC with Romanian month names. """ # Validate company access if str(company) not in current_user.companies: raise HTTPException(403, f"Nu aveți acces la firma {company}") return await _get_available_periods(company) return router