Add cache source tracking (L1/L2) for Telegram bot responses
Implements cache tier identification in Telegram bot to display data source: - "db" for database queries - "cached L1" for in-memory cache hits - "cached L2" for SQLite cache hits Backend changes: - Added cache metadata fields to TrendsResponse and DashboardSummary models (cache_hit, response_time_ms, cache_source) - Updated /api/dashboard/summary and /api/dashboard/trends endpoints to include cache metadata when X-Include-Cache-Metadata header is present - Cache metadata is extracted from request.state (set by @cached decorator) Telegram bot changes: - Updated API client to send X-Include-Cache-Metadata header - Modified helpers to extract cache_source from backend responses - Updated handlers to pass cache metadata to formatters - Performance footer now displays specific cache tier (L1 vs L2) Fixed Pydantic serialization issue: - Changed field names from _cache_hit to cache_hit (without underscore) - Pydantic excludes underscore-prefixed fields from JSON by default 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,11 @@ class TrendsResponse(BaseModel):
|
|||||||
metadata: Dict[str, Any]
|
metadata: Dict[str, Any]
|
||||||
growth_rates: Optional[Dict[str, float]] = None
|
growth_rates: Optional[Dict[str, float]] = None
|
||||||
|
|
||||||
|
# Cache metadata (optional, for Telegram Bot)
|
||||||
|
cache_hit: Optional[bool] = None
|
||||||
|
response_time_ms: Optional[float] = None
|
||||||
|
cache_source: Optional[str] = None
|
||||||
|
|
||||||
class DashboardSummary(BaseModel):
|
class DashboardSummary(BaseModel):
|
||||||
"""Model pentru toate datele dashboard-ului"""
|
"""Model pentru toate datele dashboard-ului"""
|
||||||
# CLIENȚI - statistici existente
|
# CLIENȚI - statistici existente
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
@@ -14,25 +14,42 @@ from ..services.dashboard_service import DashboardService
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/summary", response_model=DashboardSummary)
|
@router.get("/summary")
|
||||||
async def get_dashboard_summary(
|
async def get_dashboard_summary(
|
||||||
|
request: Request,
|
||||||
company: str = Query(description="Codul firmei"),
|
company: str = Query(description="Codul firmei"),
|
||||||
current_user: CurrentUser = Depends(get_current_user)
|
current_user: CurrentUser = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Obține toate datele pentru dashboard într-un singur apel
|
Obține toate datele pentru dashboard într-un singur apel
|
||||||
|
|
||||||
- Necesită autentificare JWT
|
- Necesită autentificare JWT
|
||||||
- Returnează statistici clienți/furnizori și trezorerie
|
- Returnează statistici clienți/furnizori și trezorerie
|
||||||
|
- Include metadata cache pentru Telegram Bot (X-Include-Cache-Metadata header)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Verifică dacă utilizatorul are acces la firma specificată
|
# Verifică dacă utilizatorul are acces la firma specificată
|
||||||
if company not in current_user.companies:
|
if company not in current_user.companies:
|
||||||
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
||||||
|
|
||||||
result = await DashboardService.get_complete_summary(company, current_user.username)
|
result = await DashboardService.get_complete_summary(company, current_user.username, request=request)
|
||||||
return result
|
|
||||||
|
# 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:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -40,6 +57,7 @@ async def get_dashboard_summary(
|
|||||||
|
|
||||||
@router.get("/trends", response_model=TrendsResponse)
|
@router.get("/trends", response_model=TrendsResponse)
|
||||||
async def get_dashboard_trends(
|
async def get_dashboard_trends(
|
||||||
|
request: Request,
|
||||||
company: str = Query(description="Codul firmei"),
|
company: str = Query(description="Codul firmei"),
|
||||||
period: str = Query(default="30d", description="Perioada pentru trends: 7d, 30d, ytd, 12m"),
|
period: str = Query(default="30d", description="Perioada pentru trends: 7d, 30d, ytd, 12m"),
|
||||||
compare_previous: bool = Query(default=True, description="Compară cu perioada anterioară"),
|
compare_previous: bool = Query(default=True, description="Compară cu perioada anterioară"),
|
||||||
@@ -47,7 +65,7 @@ async def get_dashboard_trends(
|
|||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Obține trenduri pentru indicatorii principali (clienți/furnizori)
|
Obține trenduri pentru indicatorii principali (clienți/furnizori)
|
||||||
|
|
||||||
- period: "7d" (7 zile), "30d" (30 zile), "ytd" (year to date), "12m" (12 luni)
|
- period: "7d" (7 zile), "30d" (30 zile), "ytd" (year to date), "12m" (12 luni)
|
||||||
- compare_previous: dacă să compare cu perioada anterioară
|
- compare_previous: dacă să compare cu perioada anterioară
|
||||||
- Necesită autentificare JWT
|
- Necesită autentificare JWT
|
||||||
@@ -57,21 +75,34 @@ async def get_dashboard_trends(
|
|||||||
# Verifică dacă utilizatorul are acces la firma specificată
|
# Verifică dacă utilizatorul are acces la firma specificată
|
||||||
if company not in current_user.companies:
|
if company not in current_user.companies:
|
||||||
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
||||||
|
|
||||||
# Validează perioada
|
# Validează perioada
|
||||||
valid_periods = ["7d", "30d", "ytd", "12m"]
|
valid_periods = ["7d", "30d", "ytd", "12m"]
|
||||||
if period not in valid_periods:
|
if period not in valid_periods:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=400,
|
status_code=400,
|
||||||
detail=f"Perioadă nevalidă: {period}. Valori permise: {', '.join(valid_periods)}"
|
detail=f"Perioadă nevalidă: {period}. Valori permise: {', '.join(valid_periods)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Obține datele de trenduri
|
# Obține datele de trenduri
|
||||||
result = await DashboardService.get_trends(int(company), period)
|
result = await DashboardService.get_trends(int(company), period, request=request)
|
||||||
|
|
||||||
# The service now returns the data in the correct format
|
# Convert to dict if needed
|
||||||
# Return it directly as TrendsResponse
|
result_dict = result.dict() if hasattr(result, 'dict') else result
|
||||||
return TrendsResponse(**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:
|
except ValueError as e:
|
||||||
logger.error(f"Value error in trends endpoint: {str(e)}")
|
logger.error(f"Value error in trends endpoint: {str(e)}")
|
||||||
@@ -191,6 +222,7 @@ async def get_cashflow(
|
|||||||
|
|
||||||
@router.get("/maturity")
|
@router.get("/maturity")
|
||||||
async def get_maturity_analysis(
|
async def get_maturity_analysis(
|
||||||
|
request: Request,
|
||||||
company: int = Query(..., description="ID-ul firmei"),
|
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"),
|
period: str = Query("7d", regex="^(7d|1m|3m|6m|12m|all)$", description="Orizont de planificare pentru analiza scadențelor"),
|
||||||
current_user: CurrentUser = Depends(get_current_user)
|
current_user: CurrentUser = Depends(get_current_user)
|
||||||
@@ -210,15 +242,31 @@ async def get_maturity_analysis(
|
|||||||
- Compară scadențele clienți vs furnizori
|
- Compară scadențele clienți vs furnizori
|
||||||
- Calculează balanța și oferă recomandări
|
- Calculează balanța și oferă recomandări
|
||||||
- Returnează metadate cu statistici complete
|
- Returnează metadate cu statistici complete
|
||||||
|
- Include metadata cache pentru Telegram Bot (X-Include-Cache-Metadata header)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Verifică dacă utilizatorul are acces la firma specificată
|
# Verifică dacă utilizatorul are acces la firma specificată
|
||||||
if str(company) not in current_user.companies:
|
if str(company) not in current_user.companies:
|
||||||
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
||||||
|
|
||||||
result = await DashboardService.get_maturity_analysis(company, period)
|
result = await DashboardService.get_maturity_analysis(company, period, request=request)
|
||||||
return result
|
|
||||||
|
# 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:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -252,23 +300,40 @@ async def get_monthly_flows(
|
|||||||
|
|
||||||
@router.get("/treasury-breakdown")
|
@router.get("/treasury-breakdown")
|
||||||
async def get_treasury_breakdown(
|
async def get_treasury_breakdown(
|
||||||
|
request: Request,
|
||||||
company: int = Query(..., description="ID-ul firmei"),
|
company: int = Query(..., description="ID-ul firmei"),
|
||||||
current_user: CurrentUser = Depends(get_current_user)
|
current_user: CurrentUser = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Returnează defalcarea trezoreriei pentru firma selectată
|
Returnează defalcarea trezoreriei pentru firma selectată
|
||||||
|
|
||||||
- Necesită autentificare JWT
|
- Necesită autentificare JWT
|
||||||
- Returnează distribuția soldurilor pe conturi și tipuri
|
- Returnează distribuția soldurilor pe conturi și tipuri
|
||||||
|
- Include metadata cache pentru Telegram Bot (X-Include-Cache-Metadata header)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Verifică dacă utilizatorul are acces la firma specificată
|
# Verifică dacă utilizatorul are acces la firma specificată
|
||||||
if str(company) not in current_user.companies:
|
if str(company) not in current_user.companies:
|
||||||
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
||||||
|
|
||||||
result = await DashboardService.get_treasury_breakdown(company)
|
result = await DashboardService.get_treasury_breakdown(company, request=request)
|
||||||
return result
|
|
||||||
|
# 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:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -277,22 +342,39 @@ async def get_treasury_breakdown(
|
|||||||
|
|
||||||
@router.get("/net-balance-breakdown")
|
@router.get("/net-balance-breakdown")
|
||||||
async def get_net_balance_breakdown(
|
async def get_net_balance_breakdown(
|
||||||
|
request: Request,
|
||||||
company: int = Query(..., description="ID-ul firmei"),
|
company: int = Query(..., description="ID-ul firmei"),
|
||||||
current_user: CurrentUser = Depends(get_current_user)
|
current_user: CurrentUser = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Returnează defalcarea balanței nete pentru firma selectată
|
Returnează defalcarea balanței nete pentru firma selectată
|
||||||
|
|
||||||
- Necesită autentificare JWT
|
- Necesită autentificare JWT
|
||||||
- Returnează analiza detaliată a balanței nete
|
- Returnează analiza detaliată a balanței nete
|
||||||
|
- Include metadata cache pentru Telegram Bot (X-Include-Cache-Metadata header)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Verifică dacă utilizatorul are acces la firma specificată
|
# Verifică dacă utilizatorul are acces la firma specificată
|
||||||
if str(company) not in current_user.companies:
|
if str(company) not in current_user.companies:
|
||||||
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
||||||
|
|
||||||
result = await DashboardService.get_net_balance_breakdown(company)
|
result = await DashboardService.get_net_balance_breakdown(company, request=request)
|
||||||
return result
|
|
||||||
|
# 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:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|||||||
@@ -234,15 +234,20 @@ class BackendAPIClient:
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict with dashboard data (sold_total, facturi, plati, etc.)
|
Dict with dashboard data (sold_total, facturi, plati, etc.)
|
||||||
|
Includes _cache_hit and _response_time_ms metadata
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.client:
|
if not self.client:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
|
# Add cache metadata header for Telegram Bot
|
||||||
|
headers = self._get_auth_headers(jwt_token)
|
||||||
|
headers['X-Include-Cache-Metadata'] = 'true'
|
||||||
|
|
||||||
response = await self.client.get(
|
response = await self.client.get(
|
||||||
"/api/dashboard/summary",
|
"/api/dashboard/summary",
|
||||||
params={"company": str(company_id)},
|
params={"company": str(company_id)},
|
||||||
headers=self._get_auth_headers(jwt_token)
|
headers=headers
|
||||||
)
|
)
|
||||||
|
|
||||||
return await self._handle_response(response)
|
return await self._handle_response(response)
|
||||||
@@ -270,9 +275,13 @@ class BackendAPIClient:
|
|||||||
if not self.client:
|
if not self.client:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
|
# Add cache metadata header for Telegram Bot
|
||||||
|
headers = self._get_auth_headers(jwt_token)
|
||||||
|
headers['X-Include-Cache-Metadata'] = 'true'
|
||||||
|
|
||||||
response = await self.client.get(
|
response = await self.client.get(
|
||||||
f"/api/dashboard/treasury-breakdown?company={company_id}",
|
f"/api/dashboard/treasury-breakdown?company={company_id}",
|
||||||
headers=self._get_auth_headers(jwt_token)
|
headers=headers
|
||||||
)
|
)
|
||||||
|
|
||||||
return await self._handle_response(response)
|
return await self._handle_response(response)
|
||||||
@@ -302,9 +311,13 @@ class BackendAPIClient:
|
|||||||
if not self.client:
|
if not self.client:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
|
# Add cache metadata header for Telegram Bot
|
||||||
|
headers = self._get_auth_headers(jwt_token)
|
||||||
|
headers['X-Include-Cache-Metadata'] = 'true'
|
||||||
|
|
||||||
response = await self.client.get(
|
response = await self.client.get(
|
||||||
f"/api/dashboard/detailed-data?company={company_id}&data_type={data_type}",
|
f"/api/dashboard/detailed-data?company={company_id}&data_type={data_type}",
|
||||||
headers=self._get_auth_headers(jwt_token)
|
headers=headers
|
||||||
)
|
)
|
||||||
|
|
||||||
return await self._handle_response(response)
|
return await self._handle_response(response)
|
||||||
@@ -334,9 +347,13 @@ class BackendAPIClient:
|
|||||||
if not self.client:
|
if not self.client:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
|
# Add cache metadata header for Telegram Bot
|
||||||
|
headers = self._get_auth_headers(jwt_token)
|
||||||
|
headers['X-Include-Cache-Metadata'] = 'true'
|
||||||
|
|
||||||
response = await self.client.get(
|
response = await self.client.get(
|
||||||
f"/api/dashboard/maturity?company={company_id}&period={period}",
|
f"/api/dashboard/maturity?company={company_id}&period={period}",
|
||||||
headers=self._get_auth_headers(jwt_token)
|
headers=headers
|
||||||
)
|
)
|
||||||
|
|
||||||
return await self._handle_response(response)
|
return await self._handle_response(response)
|
||||||
@@ -364,9 +381,13 @@ class BackendAPIClient:
|
|||||||
if not self.client:
|
if not self.client:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
|
# Add cache metadata header for Telegram Bot
|
||||||
|
headers = self._get_auth_headers(jwt_token)
|
||||||
|
headers['X-Include-Cache-Metadata'] = 'true'
|
||||||
|
|
||||||
response = await self.client.get(
|
response = await self.client.get(
|
||||||
f"/api/dashboard/performance?company={company_id}",
|
f"/api/dashboard/performance?company={company_id}",
|
||||||
headers=self._get_auth_headers(jwt_token)
|
headers=headers
|
||||||
)
|
)
|
||||||
|
|
||||||
return await self._handle_response(response)
|
return await self._handle_response(response)
|
||||||
@@ -396,9 +417,13 @@ class BackendAPIClient:
|
|||||||
if not self.client:
|
if not self.client:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
|
# Add cache metadata header for Telegram Bot
|
||||||
|
headers = self._get_auth_headers(jwt_token)
|
||||||
|
headers['X-Include-Cache-Metadata'] = 'true'
|
||||||
|
|
||||||
response = await self.client.get(
|
response = await self.client.get(
|
||||||
f"/api/dashboard/monthly-flows?company={company_id}&months={months}",
|
f"/api/dashboard/monthly-flows?company={company_id}&months={months}",
|
||||||
headers=self._get_auth_headers(jwt_token)
|
headers=headers
|
||||||
)
|
)
|
||||||
|
|
||||||
return await self._handle_response(response)
|
return await self._handle_response(response)
|
||||||
@@ -428,9 +453,13 @@ class BackendAPIClient:
|
|||||||
if not self.client:
|
if not self.client:
|
||||||
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
|
# Add cache metadata header for Telegram Bot
|
||||||
|
headers = self._get_auth_headers(jwt_token)
|
||||||
|
headers['X-Include-Cache-Metadata'] = 'true'
|
||||||
|
|
||||||
response = await self.client.get(
|
response = await self.client.get(
|
||||||
f"/api/dashboard/trends?company={company_id}&period={period}",
|
f"/api/dashboard/trends?company={company_id}&period={period}",
|
||||||
headers=self._get_auth_headers(jwt_token)
|
headers=headers
|
||||||
)
|
)
|
||||||
|
|
||||||
return await self._handle_response(response)
|
return await self._handle_response(response)
|
||||||
@@ -476,9 +505,6 @@ class BackendAPIClient:
|
|||||||
if filters:
|
if filters:
|
||||||
params.update(filters)
|
params.update(filters)
|
||||||
|
|
||||||
# ⚠️ DEBUGGING: Log exact parameters being sent
|
|
||||||
logger.info(f"📤 Searching invoices with params: {params}")
|
|
||||||
|
|
||||||
response = await self.client.get(
|
response = await self.client.get(
|
||||||
"/api/invoices/",
|
"/api/invoices/",
|
||||||
params=params,
|
params=params,
|
||||||
@@ -487,13 +513,10 @@ class BackendAPIClient:
|
|||||||
|
|
||||||
data = await self._handle_response(response)
|
data = await self._handle_response(response)
|
||||||
|
|
||||||
# ⚠️ DEBUGGING: Log response
|
|
||||||
if isinstance(data, dict) and 'invoices' in data:
|
if isinstance(data, dict) and 'invoices' in data:
|
||||||
invoice_list = data['invoices']
|
invoice_list = data['invoices']
|
||||||
logger.info(f"📥 Received {len(invoice_list)} invoices from backend")
|
|
||||||
return invoice_list
|
return invoice_list
|
||||||
elif isinstance(data, list):
|
elif isinstance(data, list):
|
||||||
logger.info(f"📥 Received {len(data)} invoices from backend (direct list)")
|
|
||||||
return data
|
return data
|
||||||
else:
|
else:
|
||||||
logger.warning(f"📥 Unexpected response format: {type(data)}")
|
logger.warning(f"📥 Unexpected response format: {type(data)}")
|
||||||
@@ -626,6 +649,113 @@ class BackendAPIClient:
|
|||||||
logger.error(f"Failed to export report: {e}")
|
logger.error(f"Failed to export report: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# =========================================================================
|
||||||
|
# CACHE MANAGEMENT
|
||||||
|
# =========================================================================
|
||||||
|
|
||||||
|
async def invalidate_cache(
|
||||||
|
self,
|
||||||
|
jwt_token: str,
|
||||||
|
company_id: Optional[int] = None,
|
||||||
|
cache_type: Optional[str] = None
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Invalidate cache entries.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
jwt_token: JWT access token
|
||||||
|
company_id: Optional company ID (None = all companies)
|
||||||
|
cache_type: Optional cache type (None = all types)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if successful
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not self.client:
|
||||||
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
|
request_data = {}
|
||||||
|
if company_id is not None:
|
||||||
|
request_data['company_id'] = company_id
|
||||||
|
if cache_type is not None:
|
||||||
|
request_data['cache_type'] = cache_type
|
||||||
|
|
||||||
|
response = await self.client.post(
|
||||||
|
"/api/cache/invalidate",
|
||||||
|
json=request_data,
|
||||||
|
headers=self._get_auth_headers(jwt_token)
|
||||||
|
)
|
||||||
|
|
||||||
|
response.raise_for_status()
|
||||||
|
logger.info(f"Cache invalidated: company_id={company_id}, cache_type={cache_type}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to invalidate cache: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def toggle_user_cache(
|
||||||
|
self,
|
||||||
|
jwt_token: str,
|
||||||
|
enabled: bool
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Toggle cache for current user.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
jwt_token: JWT access token
|
||||||
|
enabled: True to enable cache, False to disable
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if successful
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not self.client:
|
||||||
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
|
response = await self.client.post(
|
||||||
|
"/api/cache/toggle-user",
|
||||||
|
json={"enabled": enabled},
|
||||||
|
headers=self._get_auth_headers(jwt_token)
|
||||||
|
)
|
||||||
|
|
||||||
|
response.raise_for_status()
|
||||||
|
logger.info(f"User cache toggled: enabled={enabled}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to toggle user cache: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def get_cache_stats(
|
||||||
|
self,
|
||||||
|
jwt_token: str
|
||||||
|
) -> Optional[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Get cache statistics including user-specific settings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
jwt_token: JWT access token
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict with cache stats including 'user_enabled' field
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not self.client:
|
||||||
|
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
|
response = await self.client.get(
|
||||||
|
"/api/cache/stats",
|
||||||
|
headers=self._get_auth_headers(jwt_token)
|
||||||
|
)
|
||||||
|
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to get cache stats: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# HEALTH CHECK
|
# HEALTH CHECK
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
if result:
|
if result:
|
||||||
# Success!
|
# Success!
|
||||||
username = result['username']
|
username = result['username']
|
||||||
|
jwt_token = result['jwt_token']
|
||||||
|
|
||||||
# Show main menu with buttons for newly linked user
|
# Show main menu with buttons for newly linked user
|
||||||
session_manager = get_session_manager()
|
session_manager = get_session_manager()
|
||||||
@@ -77,8 +78,19 @@ async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
company_name = company['name'] if company else None
|
company_name = company['name'] if company else None
|
||||||
company_cui = company.get('cui') if company else None
|
company_cui = company.get('cui') if company else None
|
||||||
|
|
||||||
|
# Get cache status
|
||||||
|
cache_enabled = None
|
||||||
|
try:
|
||||||
|
from app.api.client import get_backend_client
|
||||||
|
client = get_backend_client()
|
||||||
|
async with client:
|
||||||
|
cache_stats = await client.get_cache_stats(jwt_token=jwt_token)
|
||||||
|
cache_enabled = cache_stats.get('user_enabled', True)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not get cache status in /start: {e}")
|
||||||
|
|
||||||
from app.bot.menus import create_main_menu, pad_message_for_wide_buttons
|
from app.bot.menus import create_main_menu, pad_message_for_wide_buttons
|
||||||
keyboard = create_main_menu(company_name, company_cui, is_authenticated=True)
|
keyboard = create_main_menu(company_name, company_cui, is_authenticated=True, cache_enabled=cache_enabled)
|
||||||
|
|
||||||
# Single welcome message with menu
|
# Single welcome message with menu
|
||||||
if company_name:
|
if company_name:
|
||||||
@@ -209,6 +221,11 @@ Dupa conectarea contului, foloseste **butoanele interactive** pentru:
|
|||||||
/help - Acest mesaj de ajutor
|
/help - Acest mesaj de ajutor
|
||||||
/unlink - Deconecteaza contul (securitate)
|
/unlink - Deconecteaza contul (securitate)
|
||||||
|
|
||||||
|
**Comenzi Cache (optimizare performanta):**
|
||||||
|
/togglecache - Activeaza/Dezactiveaza cache pentru tine
|
||||||
|
/clearcache - Sterge cache pentru compania activa
|
||||||
|
/clearcache all - Sterge tot cache-ul
|
||||||
|
|
||||||
**Conectare cont:**
|
**Conectare cont:**
|
||||||
1. Loghează-te în aplicația web ROA2WEB
|
1. Loghează-te în aplicația web ROA2WEB
|
||||||
2. Accesează Setări → Telegram Linking
|
2. Accesează Setări → Telegram Linking
|
||||||
@@ -263,6 +280,158 @@ async def clear_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def clearcache_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
|
"""
|
||||||
|
Handle /clearcache command.
|
||||||
|
|
||||||
|
Clears the cache for the current company or all companies.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
- /clearcache - Clear cache for current company
|
||||||
|
- /clearcache all - Clear entire cache (all companies)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
update: Telegram update object
|
||||||
|
context: Telegram context
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
telegram_user_id = update.effective_user.id
|
||||||
|
logger.info(f"/clearcache command from user {telegram_user_id}")
|
||||||
|
|
||||||
|
# Check if user is linked
|
||||||
|
is_linked = await check_user_linked(telegram_user_id)
|
||||||
|
if not is_linked:
|
||||||
|
await update.message.reply_text(
|
||||||
|
"**Cont neconectat**\n\nFoloseste /start pentru a conecta contul.",
|
||||||
|
parse_mode=ParseMode.MARKDOWN
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get auth data
|
||||||
|
auth_data = await get_user_auth_data(telegram_user_id)
|
||||||
|
jwt_token = auth_data['jwt_token']
|
||||||
|
|
||||||
|
# Check if user wants to clear all cache
|
||||||
|
clear_all = len(context.args) > 0 and context.args[0].lower() == 'all'
|
||||||
|
|
||||||
|
client = get_backend_client()
|
||||||
|
async with client:
|
||||||
|
if clear_all:
|
||||||
|
# Clear entire cache
|
||||||
|
result = await client.client.post(
|
||||||
|
"/api/cache/invalidate",
|
||||||
|
json={},
|
||||||
|
headers=client._get_auth_headers(jwt_token)
|
||||||
|
)
|
||||||
|
if result.status_code == 200:
|
||||||
|
await update.message.reply_text(
|
||||||
|
"✅ **Cache șters complet**\n\n"
|
||||||
|
"Toate datele cached au fost șterse.",
|
||||||
|
parse_mode=ParseMode.MARKDOWN
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await update.message.reply_text("❌ Eroare la ștergerea cache-ului.")
|
||||||
|
else:
|
||||||
|
# Get active company
|
||||||
|
session_manager = get_session_manager()
|
||||||
|
from app.bot.helpers import get_active_company_or_prompt
|
||||||
|
company = await get_active_company_or_prompt(update, session_manager, telegram_user_id)
|
||||||
|
|
||||||
|
if not company:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Clear cache for current company
|
||||||
|
result = await client.client.post(
|
||||||
|
"/api/cache/invalidate",
|
||||||
|
json={"company_id": company['id']},
|
||||||
|
headers=client._get_auth_headers(jwt_token)
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.status_code == 200:
|
||||||
|
await update.message.reply_text(
|
||||||
|
f"✅ **Cache șters pentru {company['name']}**\n\n"
|
||||||
|
"Datele vor fi reîncărcate la următoarea interogare.",
|
||||||
|
parse_mode=ParseMode.MARKDOWN
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await update.message.reply_text("❌ Eroare la ștergerea cache-ului.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in clearcache_command: {e}", exc_info=True)
|
||||||
|
await update.message.reply_text("Eroare la ștergerea cache-ului.")
|
||||||
|
|
||||||
|
|
||||||
|
async def togglecache_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
|
"""
|
||||||
|
Handle /togglecache command.
|
||||||
|
|
||||||
|
Toggles cache on/off for the current user.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
update: Telegram update object
|
||||||
|
context: Telegram context
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
telegram_user_id = update.effective_user.id
|
||||||
|
logger.info(f"/togglecache command from user {telegram_user_id}")
|
||||||
|
|
||||||
|
# Check if user is linked
|
||||||
|
is_linked = await check_user_linked(telegram_user_id)
|
||||||
|
if not is_linked:
|
||||||
|
await update.message.reply_text(
|
||||||
|
"**Cont neconectat**\n\nFoloseste /start pentru a conecta contul.",
|
||||||
|
parse_mode=ParseMode.MARKDOWN
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get auth data
|
||||||
|
auth_data = await get_user_auth_data(telegram_user_id)
|
||||||
|
jwt_token = auth_data['jwt_token']
|
||||||
|
|
||||||
|
client = get_backend_client()
|
||||||
|
async with client:
|
||||||
|
# Get current cache stats to determine current state
|
||||||
|
stats_response = await client.client.get(
|
||||||
|
"/api/cache/stats",
|
||||||
|
headers=client._get_auth_headers(jwt_token)
|
||||||
|
)
|
||||||
|
|
||||||
|
if stats_response.status_code == 200:
|
||||||
|
stats = stats_response.json()
|
||||||
|
current_enabled = stats.get('user_cache_enabled', True)
|
||||||
|
|
||||||
|
# Toggle to opposite state
|
||||||
|
new_state = not current_enabled
|
||||||
|
|
||||||
|
toggle_response = await client.client.post(
|
||||||
|
"/api/cache/toggle-user",
|
||||||
|
json={"enabled": new_state},
|
||||||
|
headers=client._get_auth_headers(jwt_token)
|
||||||
|
)
|
||||||
|
|
||||||
|
if toggle_response.status_code == 200:
|
||||||
|
if new_state:
|
||||||
|
await update.message.reply_text(
|
||||||
|
"✅ **Cache activat**\n\n"
|
||||||
|
"Interogările tale vor folosi cache-ul pentru răspunsuri mai rapide.",
|
||||||
|
parse_mode=ParseMode.MARKDOWN
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await update.message.reply_text(
|
||||||
|
"⚠️ **Cache dezactivat**\n\n"
|
||||||
|
"Interogările tale vor accesa direct baza de date Oracle.",
|
||||||
|
parse_mode=ParseMode.MARKDOWN
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await update.message.reply_text("❌ Eroare la comutarea cache-ului.")
|
||||||
|
else:
|
||||||
|
await update.message.reply_text("❌ Eroare la citirea stării cache-ului.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in togglecache_command: {e}", exc_info=True)
|
||||||
|
await update.message.reply_text("Eroare la comutarea cache-ului.")
|
||||||
|
|
||||||
|
|
||||||
async def companies_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def companies_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""
|
"""
|
||||||
Handle /companies command.
|
Handle /companies command.
|
||||||
@@ -574,6 +743,11 @@ async def trezorerie_command(update: Update, context: ContextTypes.DEFAULT_TYPE)
|
|||||||
await update.message.reply_text("Eroare la incarcarea trezoreriei.")
|
await update.message.reply_text("Eroare la incarcarea trezoreriei.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Extract cache metadata
|
||||||
|
cache_hit = treasury_data.get('cache_hit', False)
|
||||||
|
response_time_ms = treasury_data.get('response_time_ms', 0)
|
||||||
|
cache_source = treasury_data.get('cache_source', None)
|
||||||
|
|
||||||
# Format combined response (casa + banca) - rotunjit la leu (0 zecimale)
|
# Format combined response (casa + banca) - rotunjit la leu (0 zecimale)
|
||||||
casa_total = round(treasury_data['casa']['total'])
|
casa_total = round(treasury_data['casa']['total'])
|
||||||
banca_total = round(treasury_data['banca']['total'])
|
banca_total = round(treasury_data['banca']['total'])
|
||||||
@@ -589,6 +763,11 @@ async def trezorerie_command(update: Update, context: ContextTypes.DEFAULT_TYPE)
|
|||||||
from app.bot.menus import format_response_with_company
|
from app.bot.menus import format_response_with_company
|
||||||
text = format_response_with_company(content, company['name'])
|
text = format_response_with_company(content, company['name'])
|
||||||
|
|
||||||
|
# Add performance footer
|
||||||
|
if response_time_ms > 0:
|
||||||
|
from app.bot.formatters import add_performance_footer
|
||||||
|
text = add_performance_footer(text, cache_hit, response_time_ms, cache_source)
|
||||||
|
|
||||||
# Add buttons to view details
|
# Add buttons to view details
|
||||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||||
keyboard = InlineKeyboardMarkup([
|
keyboard = InlineKeyboardMarkup([
|
||||||
@@ -645,9 +824,20 @@ async def menu_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
company_name = company['name'] if company else None
|
company_name = company['name'] if company else None
|
||||||
company_cui = company.get('cui') if company else None
|
company_cui = company.get('cui') if company else None
|
||||||
|
|
||||||
|
# Get cache status for user
|
||||||
|
cache_enabled = None
|
||||||
|
try:
|
||||||
|
from app.api.client import get_backend_client
|
||||||
|
client = get_backend_client()
|
||||||
|
async with client:
|
||||||
|
cache_stats = await client.get_cache_stats(jwt_token=auth_data['jwt_token'])
|
||||||
|
cache_enabled = cache_stats.get('user_enabled', True)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not get cache status: {e}")
|
||||||
|
|
||||||
# Create main menu (user is authenticated if they passed the is_linked check)
|
# Create main menu (user is authenticated if they passed the is_linked check)
|
||||||
from app.bot.menus import create_main_menu, get_menu_message
|
from app.bot.menus import create_main_menu, get_menu_message
|
||||||
keyboard = create_main_menu(company_name, company_cui, is_authenticated=True)
|
keyboard = create_main_menu(company_name, company_cui, is_authenticated=True, cache_enabled=cache_enabled)
|
||||||
menu_text = get_menu_message(company_name, company_cui)
|
menu_text = get_menu_message(company_name, company_cui)
|
||||||
|
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
@@ -708,11 +898,19 @@ async def trezorerie_casa_command(update: Update, context: ContextTypes.DEFAULT_
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Format response
|
# Format response
|
||||||
from app.bot.formatters import format_treasury_casa_response
|
from app.bot.formatters import format_treasury_casa_response, add_performance_footer
|
||||||
from app.bot.menus import create_action_buttons, format_response_with_company
|
from app.bot.menus import create_action_buttons, format_response_with_company
|
||||||
|
|
||||||
content = format_treasury_casa_response(treasury_data['casa'])
|
content = format_treasury_casa_response(treasury_data['casa'])
|
||||||
response = format_response_with_company(content, company['name'])
|
response = format_response_with_company(content, company['name'])
|
||||||
|
|
||||||
|
# Add performance footer if cache metadata is available
|
||||||
|
if 'cache_hit' in treasury_data and 'response_time_ms' in treasury_data:
|
||||||
|
cache_hit = treasury_data['cache_hit']
|
||||||
|
response_time_ms = treasury_data['response_time_ms']
|
||||||
|
cache_source = treasury_data.get('cache_source', None)
|
||||||
|
response = add_performance_footer(response, cache_hit, response_time_ms, cache_source)
|
||||||
|
|
||||||
keyboard = create_action_buttons("casa", show_export=True)
|
keyboard = create_action_buttons("casa", show_export=True)
|
||||||
|
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
@@ -773,11 +971,19 @@ async def trezorerie_banca_command(update: Update, context: ContextTypes.DEFAULT
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Format response
|
# Format response
|
||||||
from app.bot.formatters import format_treasury_banca_response
|
from app.bot.formatters import format_treasury_banca_response, add_performance_footer
|
||||||
from app.bot.menus import create_action_buttons, format_response_with_company
|
from app.bot.menus import create_action_buttons, format_response_with_company
|
||||||
|
|
||||||
content = format_treasury_banca_response(treasury_data['banca'])
|
content = format_treasury_banca_response(treasury_data['banca'])
|
||||||
response = format_response_with_company(content, company['name'])
|
response = format_response_with_company(content, company['name'])
|
||||||
|
|
||||||
|
# Add performance footer if cache metadata is available
|
||||||
|
if 'cache_hit' in treasury_data and 'response_time_ms' in treasury_data:
|
||||||
|
cache_hit = treasury_data['cache_hit']
|
||||||
|
response_time_ms = treasury_data['response_time_ms']
|
||||||
|
cache_source = treasury_data.get('cache_source', None)
|
||||||
|
response = add_performance_footer(response, cache_hit, response_time_ms, cache_source)
|
||||||
|
|
||||||
keyboard = create_action_buttons("banca", show_export=True)
|
keyboard = create_action_buttons("banca", show_export=True)
|
||||||
|
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
@@ -838,8 +1044,13 @@ async def clienti_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
await update.message.reply_text("Eroare la incarcarea datelor clienti.")
|
await update.message.reply_text("Eroare la incarcarea datelor clienti.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Extract cache metadata
|
||||||
|
cache_hit = clients_data.get('cache_hit', False)
|
||||||
|
response_time_ms = clients_data.get('response_time_ms', 0)
|
||||||
|
cache_source = clients_data.get('cache_source', None)
|
||||||
|
|
||||||
# Format response
|
# Format response
|
||||||
from app.bot.formatters import format_clients_balance_response
|
from app.bot.formatters import format_clients_balance_response, add_performance_footer
|
||||||
from app.bot.menus import create_client_list_keyboard, format_response_with_company
|
from app.bot.menus import create_client_list_keyboard, format_response_with_company
|
||||||
|
|
||||||
content = format_clients_balance_response(
|
content = format_clients_balance_response(
|
||||||
@@ -847,6 +1058,11 @@ async def clienti_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
clients_data['maturity']
|
clients_data['maturity']
|
||||||
)
|
)
|
||||||
response = format_response_with_company(content, company['name'])
|
response = format_response_with_company(content, company['name'])
|
||||||
|
|
||||||
|
# Add performance footer
|
||||||
|
if response_time_ms > 0:
|
||||||
|
response = add_performance_footer(response, cache_hit, response_time_ms, cache_source)
|
||||||
|
|
||||||
keyboard = create_client_list_keyboard(clients_data['clients'], page=0)
|
keyboard = create_client_list_keyboard(clients_data['clients'], page=0)
|
||||||
|
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
@@ -907,8 +1123,13 @@ async def furnizori_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
await update.message.reply_text("Eroare la incarcarea datelor furnizori.")
|
await update.message.reply_text("Eroare la incarcarea datelor furnizori.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Extract cache metadata
|
||||||
|
cache_hit = suppliers_data.get('cache_hit', False)
|
||||||
|
response_time_ms = suppliers_data.get('response_time_ms', 0)
|
||||||
|
cache_source = suppliers_data.get('cache_source', None)
|
||||||
|
|
||||||
# Format response
|
# Format response
|
||||||
from app.bot.formatters import format_suppliers_balance_response
|
from app.bot.formatters import format_suppliers_balance_response, add_performance_footer
|
||||||
from app.bot.menus import create_supplier_list_keyboard, format_response_with_company
|
from app.bot.menus import create_supplier_list_keyboard, format_response_with_company
|
||||||
|
|
||||||
content = format_suppliers_balance_response(
|
content = format_suppliers_balance_response(
|
||||||
@@ -916,6 +1137,11 @@ async def furnizori_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
suppliers_data['maturity']
|
suppliers_data['maturity']
|
||||||
)
|
)
|
||||||
response = format_response_with_company(content, company['name'])
|
response = format_response_with_company(content, company['name'])
|
||||||
|
|
||||||
|
# Add performance footer
|
||||||
|
if response_time_ms > 0:
|
||||||
|
response = add_performance_footer(response, cache_hit, response_time_ms, cache_source)
|
||||||
|
|
||||||
keyboard = create_supplier_list_keyboard(suppliers_data['suppliers'], page=0)
|
keyboard = create_supplier_list_keyboard(suppliers_data['suppliers'], page=0)
|
||||||
|
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
@@ -976,7 +1202,7 @@ async def evolutie_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Format response
|
# Format response
|
||||||
from app.bot.formatters import format_cashflow_evolution_response
|
from app.bot.formatters import format_cashflow_evolution_response, add_performance_footer
|
||||||
from app.bot.menus import create_action_buttons, format_response_with_company
|
from app.bot.menus import create_action_buttons, format_response_with_company
|
||||||
|
|
||||||
content = format_cashflow_evolution_response(
|
content = format_cashflow_evolution_response(
|
||||||
@@ -984,6 +1210,14 @@ async def evolutie_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
evolution_data['monthly']
|
evolution_data['monthly']
|
||||||
)
|
)
|
||||||
response = format_response_with_company(content, company['name'])
|
response = format_response_with_company(content, company['name'])
|
||||||
|
|
||||||
|
# Add performance footer if cache metadata is available
|
||||||
|
if 'cache_hit' in evolution_data and 'response_time_ms' in evolution_data:
|
||||||
|
cache_hit = evolution_data['cache_hit']
|
||||||
|
response_time_ms = evolution_data['response_time_ms']
|
||||||
|
cache_source = evolution_data.get('cache_source', None)
|
||||||
|
response = add_performance_footer(response, cache_hit, response_time_ms, cache_source)
|
||||||
|
|
||||||
keyboard = create_action_buttons("evolutie", show_export=False, show_refresh=False)
|
keyboard = create_action_buttons("evolutie", show_export=False, show_refresh=False)
|
||||||
|
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
@@ -1205,11 +1439,19 @@ async def handle_menu_callback(query, telegram_user_id: int, callback_data: str)
|
|||||||
from app.bot.helpers import get_treasury_breakdown_split
|
from app.bot.helpers import get_treasury_breakdown_split
|
||||||
treasury_data = await get_treasury_breakdown_split(company['id'], jwt_token)
|
treasury_data = await get_treasury_breakdown_split(company['id'], jwt_token)
|
||||||
|
|
||||||
from app.bot.formatters import format_treasury_casa_response
|
from app.bot.formatters import format_treasury_casa_response, add_performance_footer
|
||||||
from app.bot.menus import create_action_buttons, format_response_with_company
|
from app.bot.menus import create_action_buttons, format_response_with_company
|
||||||
|
|
||||||
content = format_treasury_casa_response(treasury_data['casa'])
|
content = format_treasury_casa_response(treasury_data['casa'])
|
||||||
response = format_response_with_company(content, company['name'])
|
response = format_response_with_company(content, company['name'])
|
||||||
|
|
||||||
|
# Add performance footer if cache metadata is available
|
||||||
|
if 'cache_hit' in treasury_data and 'response_time_ms' in treasury_data:
|
||||||
|
cache_hit = treasury_data['cache_hit']
|
||||||
|
response_time_ms = treasury_data['response_time_ms']
|
||||||
|
cache_source = treasury_data.get('cache_source', None)
|
||||||
|
response = add_performance_footer(response, cache_hit, response_time_ms, cache_source)
|
||||||
|
|
||||||
keyboard = create_action_buttons("casa", show_export=False, show_refresh=False)
|
keyboard = create_action_buttons("casa", show_export=False, show_refresh=False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1228,11 +1470,19 @@ async def handle_menu_callback(query, telegram_user_id: int, callback_data: str)
|
|||||||
from app.bot.helpers import get_treasury_breakdown_split
|
from app.bot.helpers import get_treasury_breakdown_split
|
||||||
treasury_data = await get_treasury_breakdown_split(company['id'], jwt_token)
|
treasury_data = await get_treasury_breakdown_split(company['id'], jwt_token)
|
||||||
|
|
||||||
from app.bot.formatters import format_treasury_banca_response
|
from app.bot.formatters import format_treasury_banca_response, add_performance_footer
|
||||||
from app.bot.menus import create_action_buttons, format_response_with_company
|
from app.bot.menus import create_action_buttons, format_response_with_company
|
||||||
|
|
||||||
content = format_treasury_banca_response(treasury_data['banca'])
|
content = format_treasury_banca_response(treasury_data['banca'])
|
||||||
response = format_response_with_company(content, company['name'])
|
response = format_response_with_company(content, company['name'])
|
||||||
|
|
||||||
|
# Add performance footer if cache metadata is available
|
||||||
|
if 'cache_hit' in treasury_data and 'response_time_ms' in treasury_data:
|
||||||
|
cache_hit = treasury_data['cache_hit']
|
||||||
|
response_time_ms = treasury_data['response_time_ms']
|
||||||
|
cache_source = treasury_data.get('cache_source', None)
|
||||||
|
response = add_performance_footer(response, cache_hit, response_time_ms, cache_source)
|
||||||
|
|
||||||
keyboard = create_action_buttons("banca", show_export=False, show_refresh=False)
|
keyboard = create_action_buttons("banca", show_export=False, show_refresh=False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1251,7 +1501,7 @@ async def handle_menu_callback(query, telegram_user_id: int, callback_data: str)
|
|||||||
from app.bot.helpers import get_clients_with_maturity
|
from app.bot.helpers import get_clients_with_maturity
|
||||||
clients_data = await get_clients_with_maturity(company['id'], jwt_token)
|
clients_data = await get_clients_with_maturity(company['id'], jwt_token)
|
||||||
|
|
||||||
from app.bot.formatters import format_clients_balance_response
|
from app.bot.formatters import format_clients_balance_response, add_performance_footer
|
||||||
from app.bot.menus import create_client_list_keyboard, format_response_with_company
|
from app.bot.menus import create_client_list_keyboard, format_response_with_company
|
||||||
|
|
||||||
content = format_clients_balance_response(
|
content = format_clients_balance_response(
|
||||||
@@ -1259,6 +1509,14 @@ async def handle_menu_callback(query, telegram_user_id: int, callback_data: str)
|
|||||||
clients_data['maturity']
|
clients_data['maturity']
|
||||||
)
|
)
|
||||||
response = format_response_with_company(content, company['name'])
|
response = format_response_with_company(content, company['name'])
|
||||||
|
|
||||||
|
# Add performance footer if cache metadata is available
|
||||||
|
if 'cache_hit' in clients_data and 'response_time_ms' in clients_data:
|
||||||
|
cache_hit = clients_data['cache_hit']
|
||||||
|
response_time_ms = clients_data['response_time_ms']
|
||||||
|
cache_source = clients_data.get('cache_source', None)
|
||||||
|
response = add_performance_footer(response, cache_hit, response_time_ms, cache_source)
|
||||||
|
|
||||||
keyboard = create_client_list_keyboard(clients_data['clients'], page=0)
|
keyboard = create_client_list_keyboard(clients_data['clients'], page=0)
|
||||||
|
|
||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
@@ -1272,7 +1530,7 @@ async def handle_menu_callback(query, telegram_user_id: int, callback_data: str)
|
|||||||
from app.bot.helpers import get_suppliers_with_maturity
|
from app.bot.helpers import get_suppliers_with_maturity
|
||||||
suppliers_data = await get_suppliers_with_maturity(company['id'], jwt_token)
|
suppliers_data = await get_suppliers_with_maturity(company['id'], jwt_token)
|
||||||
|
|
||||||
from app.bot.formatters import format_suppliers_balance_response
|
from app.bot.formatters import format_suppliers_balance_response, add_performance_footer
|
||||||
from app.bot.menus import create_supplier_list_keyboard, format_response_with_company
|
from app.bot.menus import create_supplier_list_keyboard, format_response_with_company
|
||||||
|
|
||||||
content = format_suppliers_balance_response(
|
content = format_suppliers_balance_response(
|
||||||
@@ -1280,6 +1538,14 @@ async def handle_menu_callback(query, telegram_user_id: int, callback_data: str)
|
|||||||
suppliers_data['maturity']
|
suppliers_data['maturity']
|
||||||
)
|
)
|
||||||
response = format_response_with_company(content, company['name'])
|
response = format_response_with_company(content, company['name'])
|
||||||
|
|
||||||
|
# Add performance footer if cache metadata is available
|
||||||
|
if 'cache_hit' in suppliers_data and 'response_time_ms' in suppliers_data:
|
||||||
|
cache_hit = suppliers_data['cache_hit']
|
||||||
|
response_time_ms = suppliers_data['response_time_ms']
|
||||||
|
cache_source = suppliers_data.get('cache_source', None)
|
||||||
|
response = add_performance_footer(response, cache_hit, response_time_ms, cache_source)
|
||||||
|
|
||||||
keyboard = create_supplier_list_keyboard(suppliers_data['suppliers'], page=0)
|
keyboard = create_supplier_list_keyboard(suppliers_data['suppliers'], page=0)
|
||||||
|
|
||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
@@ -1293,7 +1559,7 @@ async def handle_menu_callback(query, telegram_user_id: int, callback_data: str)
|
|||||||
from app.bot.helpers import get_cashflow_evolution_data
|
from app.bot.helpers import get_cashflow_evolution_data
|
||||||
evolution_data = await get_cashflow_evolution_data(company['id'], jwt_token)
|
evolution_data = await get_cashflow_evolution_data(company['id'], jwt_token)
|
||||||
|
|
||||||
from app.bot.formatters import format_cashflow_evolution_response
|
from app.bot.formatters import format_cashflow_evolution_response, add_performance_footer
|
||||||
from app.bot.menus import create_action_buttons, format_response_with_company
|
from app.bot.menus import create_action_buttons, format_response_with_company
|
||||||
|
|
||||||
content = format_cashflow_evolution_response(
|
content = format_cashflow_evolution_response(
|
||||||
@@ -1301,6 +1567,14 @@ async def handle_menu_callback(query, telegram_user_id: int, callback_data: str)
|
|||||||
evolution_data['monthly']
|
evolution_data['monthly']
|
||||||
)
|
)
|
||||||
response = format_response_with_company(content, company['name'])
|
response = format_response_with_company(content, company['name'])
|
||||||
|
|
||||||
|
# Add performance footer if cache metadata is available
|
||||||
|
if 'cache_hit' in evolution_data and 'response_time_ms' in evolution_data:
|
||||||
|
cache_hit = evolution_data['cache_hit']
|
||||||
|
response_time_ms = evolution_data['response_time_ms']
|
||||||
|
cache_source = evolution_data.get('cache_source', None)
|
||||||
|
response = add_performance_footer(response, cache_hit, response_time_ms, cache_source)
|
||||||
|
|
||||||
keyboard = create_action_buttons("evolutie", show_export=False, show_refresh=False)
|
keyboard = create_action_buttons("evolutie", show_export=False, show_refresh=False)
|
||||||
|
|
||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
@@ -1309,6 +1583,72 @@ async def handle_menu_callback(query, telegram_user_id: int, callback_data: str)
|
|||||||
parse_mode=ParseMode.MARKDOWN
|
parse_mode=ParseMode.MARKDOWN
|
||||||
)
|
)
|
||||||
|
|
||||||
|
elif action == "togglecache":
|
||||||
|
# Toggle cache pentru user
|
||||||
|
try:
|
||||||
|
client = get_backend_client()
|
||||||
|
async with client:
|
||||||
|
cache_stats = await client.get_cache_stats(jwt_token=jwt_token)
|
||||||
|
user_enabled = cache_stats.get('user_enabled', True)
|
||||||
|
|
||||||
|
# Create toggle buttons
|
||||||
|
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||||
|
keyboard = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton(
|
||||||
|
"✅ Activează" if not user_enabled else "❌ Dezactivează",
|
||||||
|
callback_data=f"cache_toggle:{'on' if not user_enabled else 'off'}"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
[InlineKeyboardButton("« Înapoi la Meniu", callback_data="action:menu")]
|
||||||
|
]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
|
status = "ACTIVAT" if user_enabled else "DEZACTIVAT"
|
||||||
|
message = f"**Cache Status**\n\nCurent: {status}\n\n"
|
||||||
|
|
||||||
|
if user_enabled:
|
||||||
|
message += "Vrei să dezactivezi cache-ul temporar?\nFolosește pentru teste de performanță."
|
||||||
|
else:
|
||||||
|
message += "Cache-ul este dezactivat.\nToate queries merg direct la Oracle."
|
||||||
|
|
||||||
|
await query.edit_message_text(
|
||||||
|
message,
|
||||||
|
reply_markup=reply_markup,
|
||||||
|
parse_mode=ParseMode.MARKDOWN
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Toggle cache menu error: {e}", exc_info=True)
|
||||||
|
await query.answer("Eroare la obținerea status cache.", show_alert=True)
|
||||||
|
|
||||||
|
elif action == "clearcache":
|
||||||
|
# Clear cache
|
||||||
|
try:
|
||||||
|
# Create inline keyboard
|
||||||
|
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||||
|
keyboard = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton("Toate companiile", callback_data="cache_clear:all"),
|
||||||
|
InlineKeyboardButton("Doar compania mea", callback_data="cache_clear:current")
|
||||||
|
],
|
||||||
|
[InlineKeyboardButton("« Înapoi la Meniu", callback_data="action:menu")]
|
||||||
|
]
|
||||||
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
|
||||||
|
message = "**🔄 Invalidare Cache**\n\n"
|
||||||
|
if company:
|
||||||
|
message += f"Compania curentă: {company['name']}\n\n"
|
||||||
|
message += "Alege scope:"
|
||||||
|
|
||||||
|
await query.edit_message_text(
|
||||||
|
message,
|
||||||
|
reply_markup=reply_markup,
|
||||||
|
parse_mode=ParseMode.MARKDOWN
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Clear cache menu error: {e}", exc_info=True)
|
||||||
|
await query.answer("Eroare la afișarea opțiuni cache.", show_alert=True)
|
||||||
|
|
||||||
elif action == "select_company":
|
elif action == "select_company":
|
||||||
# ✅ MODIFICARE: Folosim funcția comună
|
# ✅ MODIFICARE: Folosim funcția comună
|
||||||
await _handle_selectcompany_view(
|
await _handle_selectcompany_view(
|
||||||
@@ -1346,10 +1686,22 @@ async def handle_action_callback(query, telegram_user_id: int, callback_data: st
|
|||||||
auth_data = await get_user_auth_data(telegram_user_id)
|
auth_data = await get_user_auth_data(telegram_user_id)
|
||||||
is_authenticated = auth_data is not None
|
is_authenticated = auth_data is not None
|
||||||
|
|
||||||
|
# Get cache status for user
|
||||||
|
cache_enabled = None
|
||||||
|
if is_authenticated:
|
||||||
|
try:
|
||||||
|
from app.api.client import get_backend_client
|
||||||
|
client = get_backend_client()
|
||||||
|
async with client:
|
||||||
|
cache_stats = await client.get_cache_stats(jwt_token=auth_data['jwt_token'])
|
||||||
|
cache_enabled = cache_stats.get('user_enabled', True)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not get cache status: {e}")
|
||||||
|
|
||||||
from app.bot.menus import create_main_menu, get_menu_message
|
from app.bot.menus import create_main_menu, get_menu_message
|
||||||
company_name = company['name'] if company else None
|
company_name = company['name'] if company else None
|
||||||
company_cui = company.get('cui') if company else None
|
company_cui = company.get('cui') if company else None
|
||||||
keyboard = create_main_menu(company_name, company_cui, is_authenticated)
|
keyboard = create_main_menu(company_name, company_cui, is_authenticated, cache_enabled)
|
||||||
menu_text = get_menu_message(company_name, company_cui)
|
menu_text = get_menu_message(company_name, company_cui)
|
||||||
|
|
||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
@@ -1897,6 +2249,87 @@ async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
elif callback_data.startswith("nav:back:"):
|
elif callback_data.startswith("nav:back:"):
|
||||||
await handle_navigation_back(query, telegram_user_id, callback_data)
|
await handle_navigation_back(query, telegram_user_id, callback_data)
|
||||||
|
|
||||||
|
# ========== CACHE CALLBACKS (FAZA 6) ==========
|
||||||
|
|
||||||
|
elif callback_data.startswith("cache_toggle:"):
|
||||||
|
# Handle cache toggle button
|
||||||
|
action = callback_data.split(":")[1]
|
||||||
|
enabled = action == "on"
|
||||||
|
|
||||||
|
auth_data = await get_user_auth_data(telegram_user_id)
|
||||||
|
jwt_token = auth_data['jwt_token']
|
||||||
|
|
||||||
|
try:
|
||||||
|
client = get_backend_client()
|
||||||
|
async with client:
|
||||||
|
await client.toggle_user_cache(jwt_token=jwt_token, enabled=enabled)
|
||||||
|
|
||||||
|
status = "activat" if enabled else "dezactivat"
|
||||||
|
message = f"✅ **Cache {status}** pentru tine.\n\n"
|
||||||
|
|
||||||
|
if enabled:
|
||||||
|
message += "Queries vor fi servite din cache când e posibil."
|
||||||
|
else:
|
||||||
|
message += "Toate queries vor merge direct la Oracle.\nFolosește /togglecache din nou pentru reactivare."
|
||||||
|
|
||||||
|
# Add back button
|
||||||
|
keyboard = InlineKeyboardMarkup([
|
||||||
|
[InlineKeyboardButton("« Înapoi la Meniu", callback_data="action:menu")]
|
||||||
|
])
|
||||||
|
|
||||||
|
await query.edit_message_text(
|
||||||
|
message,
|
||||||
|
reply_markup=keyboard,
|
||||||
|
parse_mode=ParseMode.MARKDOWN
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Toggle cache callback error: {e}", exc_info=True)
|
||||||
|
await query.answer("❌ Eroare la modificarea setării cache.", show_alert=True)
|
||||||
|
|
||||||
|
elif callback_data.startswith("cache_clear:"):
|
||||||
|
# Handle clear cache button
|
||||||
|
scope = callback_data.split(":")[1]
|
||||||
|
|
||||||
|
auth_data = await get_user_auth_data(telegram_user_id)
|
||||||
|
jwt_token = auth_data['jwt_token']
|
||||||
|
|
||||||
|
try:
|
||||||
|
client = get_backend_client()
|
||||||
|
|
||||||
|
if scope == "all":
|
||||||
|
# Clear all cache
|
||||||
|
async with client:
|
||||||
|
await client.invalidate_cache(jwt_token=jwt_token, company_id=None)
|
||||||
|
message = "✅ Cache invalidat pentru **toate companiile**.\n\nDatele vor fi refreshate la următoarea interogare."
|
||||||
|
|
||||||
|
elif scope == "current":
|
||||||
|
# Clear only current company
|
||||||
|
session_manager = get_session_manager()
|
||||||
|
session = await session_manager.get_or_create_session(telegram_user_id)
|
||||||
|
company = session.get_active_company()
|
||||||
|
|
||||||
|
if not company:
|
||||||
|
await query.answer("Nu ai o companie selectată.", show_alert=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
async with client:
|
||||||
|
await client.invalidate_cache(jwt_token=jwt_token, company_id=company['id'])
|
||||||
|
message = f"✅ Cache invalidat pentru **{company['name']}**.\n\nDatele vor fi refreshate la următoarea interogare."
|
||||||
|
|
||||||
|
# Add back button
|
||||||
|
keyboard = InlineKeyboardMarkup([
|
||||||
|
[InlineKeyboardButton("« Înapoi la Meniu", callback_data="action:menu")]
|
||||||
|
])
|
||||||
|
|
||||||
|
await query.edit_message_text(
|
||||||
|
message,
|
||||||
|
reply_markup=keyboard,
|
||||||
|
parse_mode=ParseMode.MARKDOWN
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Clear cache callback error: {e}", exc_info=True)
|
||||||
|
await query.answer("❌ Eroare la ștergerea cache-ului.", show_alert=True)
|
||||||
|
|
||||||
# ========== PAGINATION CALLBACKS ==========
|
# ========== PAGINATION CALLBACKS ==========
|
||||||
|
|
||||||
elif callback_data.startswith("clients_page:"):
|
elif callback_data.startswith("clients_page:"):
|
||||||
@@ -2144,11 +2577,20 @@ async def _handle_sold_view(
|
|||||||
await query_or_update.message.reply_text(error_msg)
|
await query_or_update.message.reply_text(error_msg)
|
||||||
return
|
return
|
||||||
|
|
||||||
from app.bot.formatters import format_dashboard_response
|
from app.bot.formatters import format_dashboard_response, add_performance_footer
|
||||||
from app.bot.menus import create_action_buttons, format_response_with_company
|
from app.bot.menus import create_action_buttons, format_response_with_company
|
||||||
|
|
||||||
|
# Extract cache metadata
|
||||||
|
cache_hit = data.get('cache_hit', False)
|
||||||
|
response_time_ms = data.get('response_time_ms', 0)
|
||||||
|
cache_source = data.get('cache_source', None)
|
||||||
|
|
||||||
content = format_dashboard_response(data)
|
content = format_dashboard_response(data)
|
||||||
response = format_response_with_company(content, company['name'])
|
response = format_response_with_company(content, company['name'])
|
||||||
|
|
||||||
|
# Add performance footer
|
||||||
|
if response_time_ms > 0:
|
||||||
|
response = add_performance_footer(response, cache_hit, response_time_ms, cache_source)
|
||||||
keyboard = create_action_buttons("sold", show_export=False, show_refresh=False)
|
keyboard = create_action_buttons("sold", show_export=False, show_refresh=False)
|
||||||
|
|
||||||
if is_callback:
|
if is_callback:
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ async def get_treasury_breakdown_split(
|
|||||||
for item in banca_data.get('items', [])
|
for item in banca_data.get('items', [])
|
||||||
]
|
]
|
||||||
|
|
||||||
return {
|
result = {
|
||||||
'casa': {
|
'casa': {
|
||||||
'accounts': casa_accounts,
|
'accounts': casa_accounts,
|
||||||
'total': float(casa_data.get('total', 0))
|
'total': float(casa_data.get('total', 0))
|
||||||
@@ -355,6 +355,16 @@ async def get_treasury_breakdown_split(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Pass through cache metadata if present
|
||||||
|
if 'cache_hit' in breakdown:
|
||||||
|
result['cache_hit'] = breakdown['cache_hit']
|
||||||
|
if 'response_time_ms' in breakdown:
|
||||||
|
result['response_time_ms'] = breakdown['response_time_ms']
|
||||||
|
if 'cache_source' in breakdown:
|
||||||
|
result['cache_source'] = breakdown['cache_source']
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting treasury breakdown split: {e}", exc_info=True)
|
logger.error(f"Error getting treasury breakdown split: {e}", exc_info=True)
|
||||||
return None
|
return None
|
||||||
@@ -425,7 +435,7 @@ async def get_clients_with_maturity(
|
|||||||
overdue = sum(c['balance'] for c in clients if c.get('daysOverdue', 0) > 0)
|
overdue = sum(c['balance'] for c in clients if c.get('daysOverdue', 0) > 0)
|
||||||
in_term = total - overdue
|
in_term = total - overdue
|
||||||
|
|
||||||
return {
|
result = {
|
||||||
'clients': clients,
|
'clients': clients,
|
||||||
'maturity': {
|
'maturity': {
|
||||||
'in_term': in_term,
|
'in_term': in_term,
|
||||||
@@ -434,6 +444,16 @@ async def get_clients_with_maturity(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Pass through cache metadata if present
|
||||||
|
if 'cache_hit' in maturity_response:
|
||||||
|
result['cache_hit'] = maturity_response['cache_hit']
|
||||||
|
if 'response_time_ms' in maturity_response:
|
||||||
|
result['response_time_ms'] = maturity_response['response_time_ms']
|
||||||
|
if 'cache_source' in maturity_response:
|
||||||
|
result['cache_source'] = maturity_response['cache_source']
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting clients with maturity: {e}", exc_info=True)
|
logger.error(f"Error getting clients with maturity: {e}", exc_info=True)
|
||||||
return None
|
return None
|
||||||
@@ -504,7 +524,7 @@ async def get_suppliers_with_maturity(
|
|||||||
overdue = sum(s['balance'] for s in suppliers if s.get('daysOverdue', 0) > 0)
|
overdue = sum(s['balance'] for s in suppliers if s.get('daysOverdue', 0) > 0)
|
||||||
in_term = total - overdue
|
in_term = total - overdue
|
||||||
|
|
||||||
return {
|
result = {
|
||||||
'suppliers': suppliers,
|
'suppliers': suppliers,
|
||||||
'maturity': {
|
'maturity': {
|
||||||
'in_term': in_term,
|
'in_term': in_term,
|
||||||
@@ -513,6 +533,16 @@ async def get_suppliers_with_maturity(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Pass through cache metadata if present
|
||||||
|
if 'cache_hit' in maturity_response:
|
||||||
|
result['cache_hit'] = maturity_response['cache_hit']
|
||||||
|
if 'response_time_ms' in maturity_response:
|
||||||
|
result['response_time_ms'] = maturity_response['response_time_ms']
|
||||||
|
if 'cache_source' in maturity_response:
|
||||||
|
result['cache_source'] = maturity_response['cache_source']
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting suppliers with maturity: {e}", exc_info=True)
|
logger.error(f"Error getting suppliers with maturity: {e}", exc_info=True)
|
||||||
return None
|
return None
|
||||||
@@ -650,11 +680,21 @@ async def get_cashflow_evolution_data(
|
|||||||
'plati_prev': last_12_plati_prev
|
'plati_prev': last_12_plati_prev
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
result = {
|
||||||
'performance': performance,
|
'performance': performance,
|
||||||
'monthly': monthly
|
'monthly': monthly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Pass through cache metadata if present
|
||||||
|
if 'cache_hit' in trends_data:
|
||||||
|
result['cache_hit'] = trends_data['cache_hit']
|
||||||
|
if 'response_time_ms' in trends_data:
|
||||||
|
result['response_time_ms'] = trends_data['response_time_ms']
|
||||||
|
if 'cache_source' in trends_data:
|
||||||
|
result['cache_source'] = trends_data['cache_source']
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting cashflow evolution data: {e}", exc_info=True)
|
logger.error(f"Error getting cashflow evolution data: {e}", exc_info=True)
|
||||||
return None
|
return None
|
||||||
|
|||||||
Reference in New Issue
Block a user