""" Tenant Location Repository Handles database operations for tenant location data """ from typing import List, Optional, Dict, Any from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, update, delete from sqlalchemy.orm import selectinload import structlog from app.models.tenant_location import TenantLocation from app.models.tenants import Tenant from shared.database.exceptions import DatabaseError from .base import BaseRepository logger = structlog.get_logger() class TenantLocationRepository(BaseRepository): """Repository for tenant location operations""" def __init__(self, session: AsyncSession): super().__init__(TenantLocation, session) async def create_location(self, location_data: Dict[str, Any]) -> TenantLocation: """ Create a new tenant location Args: location_data: Dictionary containing location information Returns: Created TenantLocation object """ try: # Create new location instance location = TenantLocation(**location_data) self.session.add(location) await self.session.commit() await self.session.refresh(location) logger.info(f"Created new tenant location: {location.id} for tenant {location.tenant_id}") return location except Exception as e: await self.session.rollback() logger.error(f"Failed to create tenant location: {str(e)}") raise DatabaseError(f"Failed to create tenant location: {str(e)}") async def get_location_by_id(self, location_id: str) -> Optional[TenantLocation]: """ Get a location by its ID Args: location_id: UUID of the location Returns: TenantLocation object if found, None otherwise """ try: stmt = select(TenantLocation).where(TenantLocation.id == location_id) result = await self.session.execute(stmt) location = result.scalar_one_or_none() return location except Exception as e: logger.error(f"Failed to get location by ID: {str(e)}") raise DatabaseError(f"Failed to get location by ID: {str(e)}") async def get_locations_by_tenant(self, tenant_id: str) -> List[TenantLocation]: """ Get all locations for a specific tenant Args: tenant_id: UUID of the tenant Returns: List of TenantLocation objects """ try: stmt = select(TenantLocation).where(TenantLocation.tenant_id == tenant_id) result = await self.session.execute(stmt) locations = result.scalars().all() return locations except Exception as e: logger.error(f"Failed to get locations by tenant: {str(e)}") raise DatabaseError(f"Failed to get locations by tenant: {str(e)}") async def get_location_by_type(self, tenant_id: str, location_type: str) -> Optional[TenantLocation]: """ Get a location by tenant and type Args: tenant_id: UUID of the tenant location_type: Type of location (e.g., 'central_production', 'retail_outlet') Returns: TenantLocation object if found, None otherwise """ try: stmt = select(TenantLocation).where( TenantLocation.tenant_id == tenant_id, TenantLocation.location_type == location_type ) result = await self.session.execute(stmt) location = result.scalar_one_or_none() return location except Exception as e: logger.error(f"Failed to get location by type: {str(e)}") raise DatabaseError(f"Failed to get location by type: {str(e)}") async def update_location(self, location_id: str, location_data: Dict[str, Any]) -> Optional[TenantLocation]: """ Update a tenant location Args: location_id: UUID of the location to update location_data: Dictionary containing updated location information Returns: Updated TenantLocation object if successful, None if location not found """ try: stmt = ( update(TenantLocation) .where(TenantLocation.id == location_id) .values(**location_data) ) await self.session.execute(stmt) # Now fetch the updated location location_stmt = select(TenantLocation).where(TenantLocation.id == location_id) result = await self.session.execute(location_stmt) location = result.scalar_one_or_none() if location: await self.session.commit() logger.info(f"Updated tenant location: {location_id}") return location else: await self.session.rollback() logger.warning(f"Location not found for update: {location_id}") return None except Exception as e: await self.session.rollback() logger.error(f"Failed to update location: {str(e)}") raise DatabaseError(f"Failed to update location: {str(e)}") async def delete_location(self, location_id: str) -> bool: """ Delete a tenant location Args: location_id: UUID of the location to delete Returns: True if deleted successfully, False if location not found """ try: stmt = delete(TenantLocation).where(TenantLocation.id == location_id) result = await self.session.execute(stmt) if result.rowcount > 0: await self.session.commit() logger.info(f"Deleted tenant location: {location_id}") return True else: await self.session.rollback() logger.warning(f"Location not found for deletion: {location_id}") return False except Exception as e: await self.session.rollback() logger.error(f"Failed to delete location: {str(e)}") raise DatabaseError(f"Failed to delete location: {str(e)}") async def get_active_locations_by_tenant(self, tenant_id: str) -> List[TenantLocation]: """ Get all active locations for a specific tenant Args: tenant_id: UUID of the tenant Returns: List of active TenantLocation objects """ try: stmt = select(TenantLocation).where( TenantLocation.tenant_id == tenant_id, TenantLocation.is_active == True ) result = await self.session.execute(stmt) locations = result.scalars().all() return locations except Exception as e: logger.error(f"Failed to get active locations by tenant: {str(e)}") raise DatabaseError(f"Failed to get active locations by tenant: {str(e)}") async def get_locations_by_tenant_with_type(self, tenant_id: str, location_types: List[str]) -> List[TenantLocation]: """ Get locations for a specific tenant filtered by location types Args: tenant_id: UUID of the tenant location_types: List of location types to filter by Returns: List of TenantLocation objects matching the criteria """ try: stmt = select(TenantLocation).where( TenantLocation.tenant_id == tenant_id, TenantLocation.location_type.in_(location_types) ) result = await self.session.execute(stmt) locations = result.scalars().all() return locations except Exception as e: logger.error(f"Failed to get locations by tenant and type: {str(e)}") raise DatabaseError(f"Failed to get locations by tenant and type: {str(e)}")