from fastapi import APIRouter, Depends, HTTPException, Query, Request from typing import Optional import os from shared.auth.dependencies import get_current_user from shared.auth.models import CurrentUser import logging logger = logging.getLogger(__name__) from ..models.dashboard import DashboardSummary, TrendsResponse, TrendData from ..models.financial_indicators import FinancialIndicatorsResponse from ..services.dashboard_service import DashboardService from ..services.financial_indicators_service import FinancialIndicatorsService from ..cache.decorators import cached router = APIRouter() @router.get("/summary") async def get_dashboard_summary( request: Request, company: str = Query(description="Codul firmei"), luna: Optional[int] = Query(None, ge=1, le=12, description="Luna contabilă (1-12)"), an: Optional[int] = Query(None, ge=2000, le=2100, description="Anul contabil"), current_user: CurrentUser = Depends(get_current_user) ): """ Obține toate datele pentru dashboard într-un singur apel - Necesită autentificare JWT - Returnează statistici clienți/furnizori și trezorerie - Include metadata cache pentru Telegram Bot (X-Include-Cache-Metadata header) - Suportă filtrare pe luna/an contabil (dacă nu sunt specificate, folosește ultima perioadă) """ try: # Verifică dacă utilizatorul are acces la firma specificată if company not in current_user.companies: raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}") server_id = getattr(request.state, 'server_id', None) result = await DashboardService.get_complete_summary(company, current_user.username, luna=luna, an=an, request=request, server_id=server_id) # Convert Pydantic model to dict for JSON serialization result_dict = result.dict() if hasattr(result, 'dict') else result # Add cache metadata if requested (for Telegram Bot) include_metadata = request.headers.get('X-Include-Cache-Metadata', '').lower() == 'true' if include_metadata: cache_hit = getattr(request.state, 'cache_hit', False) response_time = getattr(request.state, 'response_time_ms', 0) cache_source = getattr(request.state, 'cache_source', None) result_dict['cache_hit'] = cache_hit result_dict['response_time_ms'] = response_time # Always include cache_source, even if None result_dict['cache_source'] = cache_source return result_dict except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: raise HTTPException(status_code=500, detail=f"Eroare la obținerea datelor dashboard: {str(e)}") @router.get("/trends", response_model=TrendsResponse) async def get_dashboard_trends( request: Request, company: str = Query(description="Codul firmei"), period: str = Query(default="30d", description="Perioada pentru trends: 7d, 30d, ytd, 12m"), luna: Optional[int] = Query(None, ge=1, le=12, description="Luna contabilă (1-12)"), an: Optional[int] = Query(None, ge=2000, le=2100, description="Anul contabil"), compare_previous: bool = Query(default=True, description="Compară cu perioada anterioară"), current_user: CurrentUser = Depends(get_current_user) ): """ Obține trenduri pentru indicatorii principali (clienți/furnizori) - period: "7d" (7 zile), "30d" (30 zile), "ytd" (year to date), "12m" (12 luni) - luna/an: perioada contabilă de referință (dacă nu sunt specificate, folosește ultima perioadă) - compare_previous: dacă să compare cu perioada anterioară - Necesită autentificare JWT - Returnează date pentru grafice de trenduri """ try: # Verifică dacă utilizatorul are acces la firma specificată if company not in current_user.companies: raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}") # Validează perioada valid_periods = ["7d", "30d", "ytd", "12m"] if period not in valid_periods: raise HTTPException( status_code=400, detail=f"Perioadă nevalidă: {period}. Valori permise: {', '.join(valid_periods)}" ) server_id = getattr(request.state, 'server_id', None) # Obține datele de trenduri result = await DashboardService.get_trends(int(company), period, luna=luna, an=an, request=request, server_id=server_id) # Convert to dict if needed result_dict = result.dict() if hasattr(result, 'dict') else result # Add cache metadata if requested (for Telegram Bot) include_metadata = request.headers.get('X-Include-Cache-Metadata', '').lower() == 'true' if include_metadata: cache_hit = getattr(request.state, 'cache_hit', False) response_time = getattr(request.state, 'response_time_ms', 0) cache_source = getattr(request.state, 'cache_source', None) result_dict['cache_hit'] = cache_hit result_dict['response_time_ms'] = response_time # Always include cache_source, even if None result_dict['cache_source'] = cache_source # Return as TrendsResponse return TrendsResponse(**result_dict) except ValueError as e: logger.error(f"Value error in trends endpoint: {str(e)}") raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Eroare la obținerea trendurilor: {str(e)}") raise HTTPException(status_code=500, detail=f"Eroare la obținerea trendurilor: {str(e)}") @router.get("/detailed-data") async def get_detailed_data( request: Request, company: str = Query(description="Codul firmei"), data_type: str = Query(description="Tipul de date: clients, suppliers, treasury"), luna: Optional[int] = Query(None, ge=1, le=12, description="Luna contabilă (1-12)"), an: Optional[int] = Query(None, ge=2000, le=2100, description="Anul contabil"), page: int = Query(default=1, ge=1), page_size: int = Query(default=25, ge=1, le=100), search: str = Query(default="", description="Termen de căutare"), current_user: CurrentUser = Depends(get_current_user) ): """ Obține date detaliate pentru tabelele din dashboard """ logger.info(f"[ROUTER] detailed-data called: company={company}, data_type={data_type}") try: if company not in current_user.companies: raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}") server_id = getattr(request.state, 'server_id', None) logger.info(f"[ROUTER] Calling DashboardService.get_detailed_data") result = await DashboardService.get_detailed_data( company=company, data_type=data_type, luna=luna, an=an, page=page, page_size=page_size, search=search, server_id=server_id ) logger.info(f"[ROUTER] Service returned: {len(result.get('data', []))} rows") return result except Exception as e: logger.error(f"Eroare la obținerea datelor detaliate: {str(e)}") raise HTTPException(status_code=500, detail=str(e)) @router.get("/performance") async def get_performance( request: Request, company: int = Query(..., description="ID-ul firmei"), period: str = Query("7d", regex="^(7d|1m|3m|6m|ytd|12m)$", description="Perioada pentru analiză"), current_user: CurrentUser = Depends(get_current_user) ): """ Returnează date performanță pentru perioada selectată - Necesită autentificare JWT - Returnează grafice încasări vs plăți pentru perioada selectată - Calculează indicatori: rata încasării, cash conversion, working capital """ try: # Verifică dacă utilizatorul are acces la firma specificată if str(company) not in current_user.companies: raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}") server_id = getattr(request.state, 'server_id', None) result = await DashboardService.get_performance_data(company, period, server_id=server_id) # Convert to Chart.js compatible format return { "labels": result.get("labels", []), "datasets": [{ "data": result.get("data", []), "label": result.get("label", "Performance"), "borderColor": result.get("borderColor", "#3B82F6"), "backgroundColor": result.get("backgroundColor", "rgba(59, 130, 246, 0.1)"), "tension": 0.4 }] } except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Eroare la obținerea datelor de performanță: {str(e)}") raise HTTPException(status_code=500, detail=f"Eroare la obținerea datelor de performanță: {str(e)}") @router.get("/cashflow") async def get_cashflow( request: Request, company: int = Query(..., description="ID-ul firmei"), period: str = Query("7d", regex="^(7d|1m|3m|6m)$", description="Perioada pentru previziune"), current_user: CurrentUser = Depends(get_current_user) ): """ Returnează previziune cash flow pentru perioada selectată - Necesită autentificare JWT - Analizează scadențele viitoare pentru calculul cash flow-ului - Identifică zilele critice cu deficit de cash """ try: # Verifică dacă utilizatorul are acces la firma specificată if str(company) not in current_user.companies: raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}") server_id = getattr(request.state, 'server_id', None) result = await DashboardService.get_cashflow_forecast(company, period, server_id=server_id) # Convert to Chart.js compatible format return { "labels": result.get("labels", []), "datasets": [{ "data": result.get("data", []), "label": result.get("label", "Cash Flow"), "borderColor": result.get("borderColor", "#10B981"), "backgroundColor": result.get("backgroundColor", "rgba(16, 185, 129, 0.1)"), "tension": 0.4 }] } except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Eroare la obținerea previziunii cash flow: {str(e)}") raise HTTPException(status_code=500, detail=f"Eroare la obținerea previziunii cash flow: {str(e)}") @router.get("/maturity") async def get_maturity_analysis( request: Request, company: int = Query(..., description="ID-ul firmei"), period: str = Query("7d", regex="^(7d|1m|3m|6m|12m|all)$", description="Orizont de planificare pentru analiza scadențelor"), luna: Optional[int] = Query(None, ge=1, le=12, description="Luna contabilă (1-12)"), an: Optional[int] = Query(None, ge=2000, le=2100, description="Anul contabil"), current_user: CurrentUser = Depends(get_current_user) ): """ Returnează analiza scadențelor pentru orizontul de planificare selectat - Necesită autentificare JWT - Logică: Include TOATE restanțele + scadențele viitoare din perioada selectată - luna/an: perioada contabilă de referință (dacă nu sunt specificate, folosește ultima perioadă) - Perioade disponibile: * 7d: Toate restanțele + scadențe următoarelor 7 zile * 1m: Toate restanțele + scadențe următoarelor 30 zile * 3m: Toate restanțele + scadențe următoarelor 90 zile * 6m: Toate restanțele + scadențe următoarelor 180 zile * 12m: Toate restanțele + scadențe următoarelor 365 zile * all: Toate soldurile (fără filtru) - Compară scadențele clienți vs furnizori - Calculează balanța și oferă recomandări - Returnează metadate cu statistici complete - Include metadata cache pentru Telegram Bot (X-Include-Cache-Metadata header) """ try: # Verifică dacă utilizatorul are acces la firma specificată if str(company) not in current_user.companies: raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}") server_id = getattr(request.state, 'server_id', None) result = await DashboardService.get_maturity_analysis(company, period, luna=luna, an=an, request=request, server_id=server_id) # Convert to dict if needed result_dict = result.dict() if hasattr(result, 'dict') else result # Add cache metadata if requested (for Telegram Bot) include_metadata = request.headers.get('X-Include-Cache-Metadata', '').lower() == 'true' if include_metadata: cache_hit = getattr(request.state, 'cache_hit', False) response_time = getattr(request.state, 'response_time_ms', 0) cache_source = getattr(request.state, 'cache_source', None) result_dict['cache_hit'] = cache_hit result_dict['response_time_ms'] = response_time # Always include cache_source, even if None result_dict['cache_source'] = cache_source return result_dict except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Eroare la obținerea analizei scadențelor: {str(e)}") raise HTTPException(status_code=500, detail=f"Eroare la obținerea analizei scadențelor: {str(e)}") @router.get("/monthly-flows") async def get_monthly_flows( request: Request, company: int = Query(..., description="ID-ul firmei"), luna: Optional[int] = Query(None, ge=1, le=12, description="Luna contabilă (1-12)"), an: Optional[int] = Query(None, ge=2000, le=2100, description="Anul contabil"), current_user: CurrentUser = Depends(get_current_user) ): """ Returnează fluxurile lunare pentru firma selectată - Necesită autentificare JWT - Returnează date pentru analiza fluxurilor lunare - luna/an: perioada contabilă de referință (dacă nu sunt specificate, folosește ultima perioadă) - Include metadata cache pentru Telegram Bot (X-Include-Cache-Metadata header) """ try: # Verifică dacă utilizatorul are acces la firma specificată if str(company) not in current_user.companies: raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}") server_id = getattr(request.state, 'server_id', None) # Apelăm serviciul cu request pentru cache metadata result = await DashboardService.get_monthly_flows(company, luna=luna, an=an, request=request, server_id=server_id) # Convert to dict if needed result_dict = result.dict() if hasattr(result, 'dict') else result # Add cache metadata if requested (for Telegram Bot / Dashboard) include_metadata = request.headers.get('X-Include-Cache-Metadata', '').lower() == 'true' if include_metadata: cache_hit = getattr(request.state, 'cache_hit', False) response_time = getattr(request.state, 'response_time_ms', 0) cache_source = getattr(request.state, 'cache_source', None) result_dict['cache_hit'] = cache_hit result_dict['response_time_ms'] = response_time result_dict['cache_source'] = cache_source return result_dict except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Eroare la obținerea fluxurilor lunare: {str(e)}") raise HTTPException(status_code=500, detail=f"Eroare la obținerea fluxurilor lunare: {str(e)}") @router.get("/treasury-breakdown") async def get_treasury_breakdown( request: Request, company: int = Query(..., description="ID-ul firmei"), luna: Optional[int] = Query(None, ge=1, le=12, description="Luna contabilă (1-12)"), an: Optional[int] = Query(None, ge=2000, le=2100, description="Anul contabil"), current_user: CurrentUser = Depends(get_current_user) ): """ Returnează defalcarea trezoreriei pentru firma selectată - Necesită autentificare JWT - Returnează distribuția soldurilor pe conturi și tipuri - luna/an: perioada contabilă de referință (dacă nu sunt specificate, folosește ultima perioadă) - Include metadata cache pentru Telegram Bot (X-Include-Cache-Metadata header) """ try: # Verifică dacă utilizatorul are acces la firma specificată if str(company) not in current_user.companies: raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}") server_id = getattr(request.state, 'server_id', None) result = await DashboardService.get_treasury_breakdown(company, luna=luna, an=an, request=request, server_id=server_id) # Convert to dict if needed result_dict = result.dict() if hasattr(result, 'dict') else result # Add cache metadata if requested (for Telegram Bot) include_metadata = request.headers.get('X-Include-Cache-Metadata', '').lower() == 'true' if include_metadata: cache_hit = getattr(request.state, 'cache_hit', False) response_time = getattr(request.state, 'response_time_ms', 0) cache_source = getattr(request.state, 'cache_source', None) result_dict['cache_hit'] = cache_hit result_dict['response_time_ms'] = response_time # Always include cache_source, even if None result_dict['cache_source'] = cache_source return result_dict except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Eroare la obținerea defalcării trezoreriei: {str(e)}") raise HTTPException(status_code=500, detail=f"Eroare la obținerea defalcării trezoreriei: {str(e)}") @router.get("/net-balance-breakdown") async def get_net_balance_breakdown( request: Request, company: int = Query(..., description="ID-ul firmei"), luna: Optional[int] = Query(None, ge=1, le=12, description="Luna contabilă (1-12)"), an: Optional[int] = Query(None, ge=2000, le=2100, description="Anul contabil"), current_user: CurrentUser = Depends(get_current_user) ): """ Returnează defalcarea balanței nete pentru firma selectată - Necesită autentificare JWT - Returnează analiza detaliată a balanței nete - luna/an: perioada contabilă de referință (dacă nu sunt specificate, folosește ultima perioadă) - Include metadata cache pentru Telegram Bot (X-Include-Cache-Metadata header) """ try: # Verifică dacă utilizatorul are acces la firma specificată if str(company) not in current_user.companies: raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}") server_id = getattr(request.state, 'server_id', None) result = await DashboardService.get_net_balance_breakdown(company, luna=luna, an=an, request=request, server_id=server_id) # Convert to dict if needed result_dict = result.dict() if hasattr(result, 'dict') else result # Add cache metadata if requested (for Telegram Bot) include_metadata = request.headers.get('X-Include-Cache-Metadata', '').lower() == 'true' if include_metadata: cache_hit = getattr(request.state, 'cache_hit', False) response_time = getattr(request.state, 'response_time_ms', 0) cache_source = getattr(request.state, 'cache_source', None) result_dict['cache_hit'] = cache_hit result_dict['response_time_ms'] = response_time # Always include cache_source, even if None result_dict['cache_source'] = cache_source return result_dict except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Eroare la obținerea defalcării balanței nete: {str(e)}") raise HTTPException(status_code=500, detail=f"Eroare la obținerea defalcării balanței nete: {str(e)}") @router.get("/current-period") async def get_current_period( request: Request, company: int = Query(..., description="ID-ul firmei"), current_user: CurrentUser = Depends(get_current_user) ): """ Returnează perioada curentă (an și lună) din calendarul Oracle - Necesită autentificare JWT - Returnează anul, luna și perioada curentă în format YYYY-MM - Folosit pentru afișarea lunii curente în dashboard """ try: # Verifică dacă utilizatorul are acces la firma specificată if str(company) not in current_user.companies: raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}") server_id = getattr(request.state, 'server_id', None) result = await DashboardService.get_current_period(company, server_id=server_id) return result except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Eroare la obținerea perioadei curente: {str(e)}") raise HTTPException(status_code=500, detail=f"Eroare la obținerea perioadei curente: {str(e)}") @router.get( "/financial-indicators", tags=["dashboard"] ) async def get_financial_indicators( request: Request, company: int = Query(..., description="ID-ul firmei (required)"), luna: Optional[int] = Query(None, ge=1, le=12, description="Luna contabilă (1-12)"), an: Optional[int] = Query(None, ge=2000, le=2100, description="Anul contabil"), include_sparklines: bool = Query(True, description="Include date istorice pentru sparklines (12 luni)"), current_user: CurrentUser = Depends(get_current_user) ): """ Returnează toți indicatorii financiari calculați pentru firma selectată. Acest endpoint agregă datele din: - Lichiditate: Current Ratio, Quick Ratio, Cash Ratio - Eficiență: DSO, DPO, Cash Conversion Cycle, rate încasare/plată - Risc: creanțe/datorii restante, raport datorii/trezorerie - Cash Flow: flux net lunar, YTD, YoY, acoperire - Dinamică: creștere vânzări/achiziții YoY, marjă implicită - Altman Z-Score: scor și componente X1-X4 Parametri: - company (required): ID-ul firmei pentru care se calculează indicatorii - luna (optional): Luna contabilă (1-12). Dacă nu este specificată, se folosește ultima perioadă disponibilă. - an (optional): Anul contabil (2000-2100). Dacă nu este specificat, se folosește anul curent. - include_sparklines (optional, default=true): Dacă să includă date istorice pentru vizualizarea trendului pe ultimele 12 luni (sparkline_data și sparkline_labels în fiecare indicator) Cache: - TTL: 30 minute pentru indicatori curenți (cache_type='financial_indicators') - TTL: 1 oră pentru date istorice sparkline (cache_type='financial_indicators_historical') - Se invalidează automat la schimbarea datelor din balanță Necesită autentificare JWT și acces la firma specificată. """ try: # Verifică dacă utilizatorul are acces la firma specificată if str(company) not in current_user.companies: raise HTTPException( status_code=403, detail=f"Nu aveți acces la firma {company}" ) # Dacă luna/an nu sunt specificate, obținem perioada curentă # Folosim variabile tipizate explicit pentru a evita erori de tip resolved_luna: int resolved_an: int server_id = getattr(request.state, 'server_id', None) if luna is None or an is None: try: current_period = await DashboardService.get_current_period(company, server_id=server_id) resolved_luna = luna if luna is not None else current_period.get('luna', 12) resolved_an = an if an is not None else current_period.get('an', 2024) except Exception as e: logger.warning(f"Could not get current period: {e}, using defaults") from datetime import datetime resolved_luna = luna if luna is not None else datetime.now().month resolved_an = an if an is not None else datetime.now().year else: resolved_luna = luna resolved_an = an # Dacă include_sparklines este True, folosim metoda care include datele istorice if include_sparklines: response = await FinancialIndicatorsService.get_indicators_with_sparklines( company, resolved_luna, resolved_an, months=12, request=request, server_id=server_id ) # FIX: Cache poate returna dict în loc de obiect Pydantic # Extragem valorile pentru logging în mod compatibil cu ambele tipuri if isinstance(response, dict): zscore_val = response.get('altman_zscore', {}).get('zscore', {}).get('value') zscore_status = response.get('altman_zscore', {}).get('zscore', {}).get('status') else: zscore_val = response.altman_zscore.zscore.value zscore_status = response.altman_zscore.zscore.status logger.info( f"Financial indicators with sparklines for company {company}, " f"luna={resolved_luna}, an={resolved_an}: " f"Z-Score={zscore_val} ({zscore_status}), " f"cache_hit={getattr(request.state, 'cache_hit', False)}, " f"response_time={getattr(request.state, 'response_time_ms', 0):.1f}ms" ) # Add cache metadata if requested (for Telegram Bot / Dashboard) include_metadata = request.headers.get('X-Include-Cache-Metadata', '').lower() == 'true' if include_metadata: result_dict = response.dict() if hasattr(response, 'dict') else response result_dict['cache_hit'] = getattr(request.state, 'cache_hit', False) result_dict['response_time_ms'] = getattr(request.state, 'response_time_ms', 0) result_dict['cache_source'] = getattr(request.state, 'cache_source', None) return result_dict return response # Dacă include_sparklines este False, calculăm doar indicatorii curenți import asyncio # Apelăm serviciul pentru fiecare categorie de indicatori lichiditate_task = FinancialIndicatorsService.calculate_liquidity_indicators( company, resolved_luna, resolved_an, server_id=server_id ) eficienta_task = FinancialIndicatorsService.calculate_efficiency_indicators( company, resolved_luna, resolved_an, server_id=server_id ) risc_task = FinancialIndicatorsService.calculate_risk_indicators( company, resolved_luna, resolved_an, server_id=server_id ) cash_flow_task = FinancialIndicatorsService.calculate_cashflow_indicators( company, resolved_luna, resolved_an, server_id=server_id ) dinamica_task = FinancialIndicatorsService.calculate_dynamics_indicators( company, resolved_luna, resolved_an, server_id=server_id ) altman_task = FinancialIndicatorsService.calculate_altman_zscore( company, resolved_luna, resolved_an, server_id=server_id ) profitabilitate_task = FinancialIndicatorsService.calculate_profitability_indicators( company, resolved_luna, resolved_an, server_id=server_id ) solvabilitate_task = FinancialIndicatorsService.calculate_solvability_indicators( company, resolved_luna, resolved_an, server_id=server_id ) # Executăm toate calculele în paralel pentru performanță ( lichiditate, eficienta, risc, cash_flow, dinamica, altman_zscore, profitabilitate, solvabilitate ) = await asyncio.gather( lichiditate_task, eficienta_task, risc_task, cash_flow_task, dinamica_task, altman_task, profitabilitate_task, solvabilitate_task ) # Construim răspunsul response = FinancialIndicatorsResponse( lichiditate=lichiditate, eficienta=eficienta, risc=risc, cash_flow=cash_flow, dinamica=dinamica, altman_zscore=altman_zscore, profitabilitate=profitabilitate, solvabilitate=solvabilitate ) # FIX: Cache poate returna dict în loc de obiect Pydantic if isinstance(altman_zscore, dict): zscore_val = altman_zscore.get('zscore', {}).get('value') zscore_status = altman_zscore.get('zscore', {}).get('status') else: zscore_val = altman_zscore.zscore.value zscore_status = altman_zscore.zscore.status logger.info( f"Financial indicators for company {company}, luna={resolved_luna}, an={resolved_an}: " f"Z-Score={zscore_val} ({zscore_status})" ) # Add cache metadata if requested (for Telegram Bot / Dashboard) include_metadata = request.headers.get('X-Include-Cache-Metadata', '').lower() == 'true' if include_metadata: result_dict = response.dict() if hasattr(response, 'dict') else response result_dict['cache_hit'] = getattr(request.state, 'cache_hit', False) result_dict['response_time_ms'] = getattr(request.state, 'response_time_ms', 0) result_dict['cache_source'] = getattr(request.state, 'cache_source', None) return result_dict return response except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Eroare la obținerea indicatorilor financiari: {str(e)}") raise HTTPException( status_code=500, detail=f"Eroare la obținerea indicatorilor financiari: {str(e)}" )