234 lines
9.2 KiB
Python
234 lines
9.2 KiB
Python
"""
|
|
Delivery Route Repository
|
|
"""
|
|
|
|
from typing import List, Dict, Any, Optional
|
|
from datetime import date, datetime
|
|
import uuid
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.future import select
|
|
|
|
from app.models.distribution import DeliveryRoute, DeliveryRouteStatus
|
|
from shared.database.base import Base
|
|
|
|
|
|
class DeliveryRouteRepository:
|
|
def __init__(self, db_session: AsyncSession):
|
|
self.db_session = db_session
|
|
|
|
async def create_route(self, route_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""
|
|
Create a new delivery route
|
|
"""
|
|
# Define system user ID to use when user_id is not provided
|
|
SYSTEM_USER_ID = uuid.UUID("50000000-0000-0000-0000-000000000004")
|
|
|
|
route = DeliveryRoute(
|
|
id=uuid.uuid4(),
|
|
tenant_id=route_data['tenant_id'],
|
|
route_number=route_data['route_number'],
|
|
route_date=route_data['route_date'],
|
|
vehicle_id=route_data.get('vehicle_id'),
|
|
driver_id=route_data.get('driver_id'),
|
|
total_distance_km=route_data.get('total_distance_km'),
|
|
estimated_duration_minutes=route_data.get('estimated_duration_minutes'),
|
|
route_sequence=route_data.get('route_sequence'),
|
|
status=route_data.get('status', 'planned'),
|
|
created_by=route_data.get('created_by', SYSTEM_USER_ID),
|
|
updated_by=route_data.get('updated_by', SYSTEM_USER_ID)
|
|
)
|
|
|
|
self.db_session.add(route)
|
|
await self.db_session.commit()
|
|
await self.db_session.refresh(route)
|
|
|
|
# Convert SQLAlchemy object to dict for return
|
|
return {
|
|
'id': str(route.id),
|
|
'tenant_id': str(route.tenant_id),
|
|
'route_number': route.route_number,
|
|
'route_date': route.route_date,
|
|
'vehicle_id': route.vehicle_id,
|
|
'driver_id': route.driver_id,
|
|
'total_distance_km': route.total_distance_km,
|
|
'estimated_duration_minutes': route.estimated_duration_minutes,
|
|
'route_sequence': route.route_sequence,
|
|
'status': route.status.value if hasattr(route.status, 'value') else route.status,
|
|
'created_at': route.created_at,
|
|
'updated_at': route.updated_at
|
|
}
|
|
|
|
async def get_routes_by_date(self, tenant_id: str, target_date: date) -> List[Dict[str, Any]]:
|
|
"""
|
|
Get all delivery routes for a specific date and tenant
|
|
"""
|
|
stmt = select(DeliveryRoute).where(
|
|
(DeliveryRoute.tenant_id == tenant_id) &
|
|
(DeliveryRoute.route_date >= datetime.combine(target_date, datetime.min.time())) &
|
|
(DeliveryRoute.route_date < datetime.combine(target_date, datetime.max.time().replace(hour=23, minute=59, second=59)))
|
|
)
|
|
|
|
result = await self.db_session.execute(stmt)
|
|
routes = result.scalars().all()
|
|
|
|
return [
|
|
{
|
|
'id': str(route.id),
|
|
'tenant_id': str(route.tenant_id),
|
|
'route_number': route.route_number,
|
|
'route_date': route.route_date,
|
|
'vehicle_id': route.vehicle_id,
|
|
'driver_id': route.driver_id,
|
|
'total_distance_km': route.total_distance_km,
|
|
'estimated_duration_minutes': route.estimated_duration_minutes,
|
|
'route_sequence': route.route_sequence,
|
|
'status': route.status.value if hasattr(route.status, 'value') else route.status,
|
|
'created_at': route.created_at,
|
|
'updated_at': route.updated_at
|
|
}
|
|
for route in routes
|
|
]
|
|
|
|
async def get_routes_by_date_range(self, tenant_id: str, start_date: date, end_date: date) -> List[Dict[str, Any]]:
|
|
"""
|
|
Get all delivery routes for a specific date range and tenant
|
|
"""
|
|
stmt = select(DeliveryRoute).where(
|
|
(DeliveryRoute.tenant_id == tenant_id) &
|
|
(DeliveryRoute.route_date >= datetime.combine(start_date, datetime.min.time())) &
|
|
(DeliveryRoute.route_date <= datetime.combine(end_date, datetime.max.time().replace(hour=23, minute=59, second=59)))
|
|
)
|
|
|
|
result = await self.db_session.execute(stmt)
|
|
routes = result.scalars().all()
|
|
|
|
return [
|
|
{
|
|
'id': str(route.id),
|
|
'tenant_id': str(route.tenant_id),
|
|
'route_number': route.route_number,
|
|
'route_date': route.route_date,
|
|
'vehicle_id': route.vehicle_id,
|
|
'driver_id': route.driver_id,
|
|
'total_distance_km': route.total_distance_km,
|
|
'estimated_duration_minutes': route.estimated_duration_minutes,
|
|
'route_sequence': route.route_sequence,
|
|
'status': route.status.value if hasattr(route.status, 'value') else route.status,
|
|
'created_at': route.created_at,
|
|
'updated_at': route.updated_at
|
|
}
|
|
for route in routes
|
|
]
|
|
|
|
async def get_route_by_id(self, route_id: str) -> Optional[Dict[str, Any]]:
|
|
"""
|
|
Get a specific delivery route by ID
|
|
"""
|
|
stmt = select(DeliveryRoute).where(DeliveryRoute.id == route_id)
|
|
result = await self.db_session.execute(stmt)
|
|
route = result.scalar_one_or_none()
|
|
|
|
if route:
|
|
return {
|
|
'id': str(route.id),
|
|
'tenant_id': str(route.tenant_id),
|
|
'route_number': route.route_number,
|
|
'route_date': route.route_date,
|
|
'vehicle_id': route.vehicle_id,
|
|
'driver_id': route.driver_id,
|
|
'total_distance_km': route.total_distance_km,
|
|
'estimated_duration_minutes': route.estimated_duration_minutes,
|
|
'route_sequence': route.route_sequence,
|
|
'status': route.status.value if hasattr(route.status, 'value') else route.status,
|
|
'created_at': route.created_at,
|
|
'updated_at': route.updated_at
|
|
}
|
|
return None
|
|
|
|
async def update_route_status(self, route_id: str, status: str, user_id: str) -> Optional[Dict[str, Any]]:
|
|
"""
|
|
Update route status
|
|
"""
|
|
stmt = select(DeliveryRoute).where(DeliveryRoute.id == route_id)
|
|
result = await self.db_session.execute(stmt)
|
|
route = result.scalar_one_or_none()
|
|
|
|
if not route:
|
|
return None
|
|
|
|
# Handle system user ID if passed as string
|
|
if user_id == 'system':
|
|
SYSTEM_USER_ID = uuid.UUID("50000000-0000-0000-0000-000000000004")
|
|
route.updated_by = SYSTEM_USER_ID
|
|
else:
|
|
route.updated_by = user_id
|
|
route.status = status
|
|
await self.db_session.commit()
|
|
await self.db_session.refresh(route)
|
|
|
|
return {
|
|
'id': str(route.id),
|
|
'tenant_id': str(route.tenant_id),
|
|
'route_number': route.route_number,
|
|
'route_date': route.route_date,
|
|
'vehicle_id': route.vehicle_id,
|
|
'driver_id': route.driver_id,
|
|
'total_distance_km': route.total_distance_km,
|
|
'estimated_duration_minutes': route.estimated_duration_minutes,
|
|
'route_sequence': route.route_sequence,
|
|
'status': route.status.value if hasattr(route.status, 'value') else route.status,
|
|
'created_at': route.created_at,
|
|
'updated_at': route.updated_at
|
|
}
|
|
|
|
async def get_all_routes_for_tenant(self, tenant_id: str) -> List[Dict[str, Any]]:
|
|
"""
|
|
Get all delivery routes for a tenant
|
|
"""
|
|
stmt = select(DeliveryRoute).where(DeliveryRoute.tenant_id == tenant_id)
|
|
|
|
result = await self.db_session.execute(stmt)
|
|
routes = result.scalars().all()
|
|
|
|
return [
|
|
{
|
|
'id': str(route.id),
|
|
'tenant_id': str(route.tenant_id),
|
|
'route_number': route.route_number,
|
|
'route_date': route.route_date,
|
|
'vehicle_id': route.vehicle_id,
|
|
'driver_id': route.driver_id,
|
|
'total_distance_km': route.total_distance_km,
|
|
'estimated_duration_minutes': route.estimated_duration_minutes,
|
|
'route_sequence': route.route_sequence,
|
|
'status': route.status.value if hasattr(route.status, 'value') else route.status,
|
|
'created_at': route.created_at,
|
|
'updated_at': route.updated_at
|
|
}
|
|
for route in routes
|
|
]
|
|
|
|
async def delete_demo_routes_for_tenant(self, tenant_id: str) -> int:
|
|
"""
|
|
Delete all demo routes for a tenant
|
|
Used for demo session cleanup
|
|
|
|
Args:
|
|
tenant_id: The tenant ID to delete routes for
|
|
|
|
Returns:
|
|
Number of routes deleted
|
|
"""
|
|
from sqlalchemy import delete
|
|
|
|
# Delete routes with DEMO- prefix in route_number
|
|
stmt = delete(DeliveryRoute).where(
|
|
(DeliveryRoute.tenant_id == uuid.UUID(tenant_id)) &
|
|
(DeliveryRoute.route_number.like('DEMO-%'))
|
|
)
|
|
|
|
result = await self.db_session.execute(stmt)
|
|
await self.db_session.commit()
|
|
|
|
deleted_count = result.rowcount
|
|
return deleted_count |