From 184247f94f2dda16503f53e162e9fb54214fdf64 Mon Sep 17 00:00:00 2001 From: Aniq Javed Date: Thu, 9 Jul 2026 20:33:41 +0500 Subject: [PATCH 1/4] point db pool at the real database url --- backend/app/core/database_pool.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/backend/app/core/database_pool.py b/backend/app/core/database_pool.py index d638dfcfe..eea590f43 100644 --- a/backend/app/core/database_pool.py +++ b/backend/app/core/database_pool.py @@ -1,6 +1,5 @@ import asyncio from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker -from sqlalchemy.pool import QueuePool import logging from ..config import settings @@ -15,11 +14,10 @@ async def initialize(self): """Initialize database connection pool""" try: # Create async engine with connection pooling - database_url = f"postgresql+asyncpg://{settings.supabase_db_user}:{settings.supabase_db_password}@{settings.supabase_db_host}:{settings.supabase_db_port}/{settings.supabase_db_name}" + database_url = settings.database_url.replace("postgresql://", "postgresql+asyncpg://", 1) self.engine = create_async_engine( database_url, - poolclass=QueuePool, pool_size=20, # Number of connections to maintain max_overflow=30, # Additional connections when needed pool_pre_ping=True, # Validate connections @@ -45,7 +43,7 @@ async def close(self): if self.engine: await self.engine.dispose() - async def get_session(self) -> AsyncSession: + def get_session(self) -> AsyncSession: """Get database session from pool""" if not self.session_factory: raise Exception("Database pool not initialized") From 82babe56953f553425ef56bb595eeca96b0a34f1 Mon Sep 17 00:00:00 2001 From: Aniq Javed Date: Thu, 9 Jul 2026 20:47:12 +0500 Subject: [PATCH 2/4] fix cache leak between tenants --- backend/app/services/cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/services/cache.py b/backend/app/services/cache.py index b81474957..672e3f9d6 100644 --- a/backend/app/services/cache.py +++ b/backend/app/services/cache.py @@ -10,7 +10,7 @@ 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}" + cache_key = f"revenue:{tenant_id}:{property_id}" # Try to get from cache cached = await redis_client.get(cache_key) From addbded6f78cb3816a0ab59ca83625ad5a06ea64 Mon Sep 17 00:00:00 2001 From: Aniq Javed Date: Thu, 9 Jul 2026 21:05:55 +0500 Subject: [PATCH 3/4] count revenue in the property's timezone --- backend/app/services/reservations.py | 46 +++++++++++++++++----------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/backend/app/services/reservations.py b/backend/app/services/reservations.py index 384bd00ab..687c8baf6 100644 --- a/backend/app/services/reservations.py +++ b/backend/app/services/reservations.py @@ -2,34 +2,44 @@ from decimal import Decimal from typing import Dict, Any, List -async def calculate_monthly_revenue(property_id: str, month: int, year: int, db_session=None) -> Decimal: +async def calculate_monthly_revenue(property_id: str, month: int, year: int, tenant_id: str) -> Decimal: """ Calculates revenue for a specific month. """ + # month window in the property's local time, not UTC start_date = datetime(year, month, 1) if month < 12: end_date = datetime(year, month + 1, 1) else: end_date = datetime(year + 1, 1, 1) - - print(f"DEBUG: Querying revenue for {property_id} from {start_date} to {end_date}") - # SQL Simulation (This would be executed against the actual DB) - query = """ - SELECT SUM(total_amount) as total - FROM reservations - WHERE property_id = $1 - AND tenant_id = $2 - AND check_in_date >= $3 - AND check_in_date < $4 - """ - - # In production this query executes against a database session. - # result = await db.fetch_val(query, property_id, tenant_id, start_date, end_date) - # return result or Decimal('0') - - return Decimal('0') # Placeholder for now until DB connection is finalized + from app.core.database_pool import DatabasePool + from sqlalchemy import text + + db_pool = DatabasePool() + await db_pool.initialize() + + async with db_pool.get_session() as session: + query = text(""" + SELECT COALESCE(SUM(r.total_amount), 0) as total + FROM reservations r + JOIN properties p ON p.id = r.property_id AND p.tenant_id = r.tenant_id + WHERE r.property_id = :property_id + AND r.tenant_id = :tenant_id + AND (r.check_in_date AT TIME ZONE p.timezone) >= :start_date + AND (r.check_in_date AT TIME ZONE p.timezone) < :end_date + """) + + result = await session.execute(query, { + "property_id": property_id, + "tenant_id": tenant_id, + "start_date": start_date, + "end_date": end_date, + }) + row = result.fetchone() + + return Decimal(str(row.total)) async def calculate_total_revenue(property_id: str, tenant_id: str) -> Dict[str, Any]: """ From a819a9909bd4ea17bbed201a61d1b4bdbe6af5e1 Mon Sep 17 00:00:00 2001 From: Aniq Javed Date: Thu, 9 Jul 2026 21:18:08 +0500 Subject: [PATCH 4/4] keep revenue as decimal so totals don't drift --- backend/app/api/v1/dashboard.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/app/api/v1/dashboard.py b/backend/app/api/v1/dashboard.py index 1ec352d7e..23d80e65a 100644 --- a/backend/app/api/v1/dashboard.py +++ b/backend/app/api/v1/dashboard.py @@ -1,5 +1,6 @@ from fastapi import APIRouter, Depends, HTTPException from typing import Dict, Any +from decimal import Decimal, ROUND_HALF_UP from app.services.cache import get_revenue_summary from app.core.auth import authenticate_request as get_current_user @@ -15,11 +16,11 @@ async def get_dashboard_summary( revenue_data = await get_revenue_summary(property_id, tenant_id) - total_revenue_float = float(revenue_data['total']) - + total_revenue = Decimal(revenue_data['total']).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) + return { "property_id": revenue_data['property_id'], - "total_revenue": total_revenue_float, + "total_revenue": float(total_revenue), "currency": revenue_data['currency'], "reservations_count": revenue_data['count'] }