From afc52e227e854297a4ea17a73df5b130f03897b7 Mon Sep 17 00:00:00 2001 From: sam-aydev Date: Mon, 12 Oct 2026 18:48:21 +0100 Subject: [PATCH] Fix cache data leak, decimal inaccuracies, and timezone boundaries - Fixed cross-company data leak: Added tenant_id to the Redis cache key in services/cache.py to ensure isolated caching per client. - Fixed cent inaccuracies: Replaced float with Decimal in api/v1/dashboard.py to prevent floating-point arithmetic errors in financial totals. - Fixed March revenue mismatch: Added tzinfo=timezone.utc to datetime bounds in services/reservations.py to prevent local server time from shifting revenues into the wrong month. --- backend/app/api/v1/dashboard.py | 9 ++++++--- backend/app/services/cache.py | 8 +++++++- backend/app/services/reservations.py | 19 ++++++++++++++----- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/backend/app/api/v1/dashboard.py b/backend/app/api/v1/dashboard.py index 1ec352d7e..5d689fc3a 100644 --- a/backend/app/api/v1/dashboard.py +++ b/backend/app/api/v1/dashboard.py @@ -2,6 +2,7 @@ from typing import Dict, Any from app.services.cache import get_revenue_summary from app.core.auth import authenticate_request as get_current_user +from decimal import Decimal ## I added this import to solve the floating point error router = APIRouter() @@ -15,11 +16,13 @@ async def get_dashboard_summary( revenue_data = await get_revenue_summary(property_id, tenant_id) - total_revenue_float = float(revenue_data['total']) - + # total_revenue_float = float(revenue_data['total']) - this causes accuracy loss + + # Convert to Decimal for precise financial math + total_revenue = Decimal(revenue_data['total']) return { "property_id": revenue_data['property_id'], - "total_revenue": total_revenue_float, + "total_revenue": total_revenue, "currency": revenue_data['currency'], "reservations_count": revenue_data['count'] } diff --git a/backend/app/services/cache.py b/backend/app/services/cache.py index b81474957..7da4e3dd1 100644 --- a/backend/app/services/cache.py +++ b/backend/app/services/cache.py @@ -10,8 +10,14 @@ async def get_revenue_summary(property_id: str, tenant_id: str) -> Dict[str, Any """ Fetches revenue summary, utilizing caching to improve performance. """ - cache_key = f"revenue:{property_id}" + + # multiple tenants can use similar proprty ids, making client B to see client A's cached data, + # so I included tenant_id + # cache_key = f"revenue:{property_id}" + + cache_key = f"revenue:{tenant_id}:{property_id}" + # Try to get from cache cached = await redis_client.get(cache_key) if cached: diff --git a/backend/app/services/reservations.py b/backend/app/services/reservations.py index 384bd00ab..51eb6e53e 100644 --- a/backend/app/services/reservations.py +++ b/backend/app/services/reservations.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timezone from decimal import Decimal from typing import Dict, Any, List @@ -7,12 +7,21 @@ async def calculate_monthly_revenue(property_id: str, month: int, year: int, db_ Calculates revenue for a specific month. """ - start_date = datetime(year, month, 1) + # start_date = datetime(year, month, 1) + # if month < 12: + # end_date = datetime(year, month + 1, 1) + # else: + # end_date = datetime(year + 1, 1, 1) + + + + # I made the datetimes timezone-aware (specifically UTC) , that is why i added timezone to the import + # so the database performs an absolute time comparison. + start_date = datetime(year, month, 1, tzinfo=timezone.utc) if month < 12: - end_date = datetime(year, month + 1, 1) + end_date = datetime(year, month + 1, 1, tzinfo=timezone.utc) else: - end_date = datetime(year + 1, 1, 1) - + end_date = datetime(year + 1, 1, 1, tzinfo=timezone.utc) print(f"DEBUG: Querying revenue for {property_id} from {start_date} to {end_date}") # SQL Simulation (This would be executed against the actual DB)