Ship fleet, availability, pricing, dispatch, routing, tracking, and settlement on delivery-service 0.10.8.0 with migrations and phase tests green. Co-authored-by: Cursor <cursoragent@cursor.com>
185 lines
6.3 KiB
Python
185 lines
6.3 KiB
Python
"""Fleet repositories — Phase 10.2."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Sequence
|
|
from uuid import UUID
|
|
|
|
from sqlalchemy import func, select
|
|
|
|
from app.models.fleet import Fleet, Vehicle, VehicleAssignment, VehicleType
|
|
from app.models.types import VehicleAssignmentStatus
|
|
from app.repositories.base import TenantBaseRepository
|
|
from app.specifications.fleet import FleetListSpec, VehicleListSpec, VehicleTypeListSpec
|
|
|
|
|
|
class FleetRepository(TenantBaseRepository[Fleet]):
|
|
model = Fleet
|
|
|
|
async def get_by_code(self, tenant_id: UUID, code: str) -> Fleet | None:
|
|
stmt = select(self.model).where(
|
|
self.model.tenant_id == tenant_id,
|
|
self.model.code == code,
|
|
self.model.is_deleted.is_(False),
|
|
)
|
|
result = await self.session.execute(stmt)
|
|
return result.scalar_one_or_none()
|
|
|
|
async def list_filtered(
|
|
self,
|
|
tenant_id: UUID,
|
|
*,
|
|
offset: int = 0,
|
|
limit: int = 20,
|
|
spec: FleetListSpec | None = None,
|
|
) -> Sequence[Fleet]:
|
|
stmt = select(self.model).where(
|
|
self.model.tenant_id == tenant_id,
|
|
self.model.is_deleted.is_(False),
|
|
)
|
|
if spec is not None:
|
|
stmt = spec.apply(stmt)
|
|
else:
|
|
stmt = stmt.order_by(self.model.created_at.desc())
|
|
stmt = stmt.offset(offset).limit(limit)
|
|
result = await self.session.execute(stmt)
|
|
return result.scalars().all()
|
|
|
|
async def count_filtered(
|
|
self, tenant_id: UUID, *, spec: FleetListSpec | None = None
|
|
) -> int:
|
|
stmt = select(func.count()).select_from(self.model).where(
|
|
self.model.tenant_id == tenant_id,
|
|
self.model.is_deleted.is_(False),
|
|
)
|
|
if spec is not None:
|
|
clauses = spec.filter_clauses()
|
|
if clauses:
|
|
stmt = stmt.where(*clauses)
|
|
result = await self.session.execute(stmt)
|
|
return int(result.scalar_one())
|
|
|
|
|
|
class VehicleTypeRepository(TenantBaseRepository[VehicleType]):
|
|
model = VehicleType
|
|
|
|
async def get_by_code(self, tenant_id: UUID, code: str) -> VehicleType | None:
|
|
stmt = select(self.model).where(
|
|
self.model.tenant_id == tenant_id,
|
|
self.model.code == code,
|
|
self.model.is_deleted.is_(False),
|
|
)
|
|
result = await self.session.execute(stmt)
|
|
return result.scalar_one_or_none()
|
|
|
|
async def list_filtered(
|
|
self,
|
|
tenant_id: UUID,
|
|
*,
|
|
offset: int = 0,
|
|
limit: int = 20,
|
|
spec: VehicleTypeListSpec | None = None,
|
|
) -> Sequence[VehicleType]:
|
|
stmt = select(self.model).where(
|
|
self.model.tenant_id == tenant_id,
|
|
self.model.is_deleted.is_(False),
|
|
)
|
|
if spec is not None:
|
|
stmt = spec.apply(stmt)
|
|
else:
|
|
stmt = stmt.order_by(self.model.created_at.desc())
|
|
stmt = stmt.offset(offset).limit(limit)
|
|
result = await self.session.execute(stmt)
|
|
return result.scalars().all()
|
|
|
|
async def count_filtered(
|
|
self, tenant_id: UUID, *, spec: VehicleTypeListSpec | None = None
|
|
) -> int:
|
|
stmt = select(func.count()).select_from(self.model).where(
|
|
self.model.tenant_id == tenant_id,
|
|
self.model.is_deleted.is_(False),
|
|
)
|
|
if spec is not None:
|
|
clauses = spec.filter_clauses()
|
|
if clauses:
|
|
stmt = stmt.where(*clauses)
|
|
result = await self.session.execute(stmt)
|
|
return int(result.scalar_one())
|
|
|
|
|
|
class VehicleRepository(TenantBaseRepository[Vehicle]):
|
|
model = Vehicle
|
|
|
|
async def get_by_code(self, tenant_id: UUID, code: str) -> Vehicle | None:
|
|
stmt = select(self.model).where(
|
|
self.model.tenant_id == tenant_id,
|
|
self.model.code == code,
|
|
self.model.is_deleted.is_(False),
|
|
)
|
|
result = await self.session.execute(stmt)
|
|
return result.scalar_one_or_none()
|
|
|
|
async def list_filtered(
|
|
self,
|
|
tenant_id: UUID,
|
|
*,
|
|
offset: int = 0,
|
|
limit: int = 20,
|
|
spec: VehicleListSpec | None = None,
|
|
) -> Sequence[Vehicle]:
|
|
stmt = select(self.model).where(
|
|
self.model.tenant_id == tenant_id,
|
|
self.model.is_deleted.is_(False),
|
|
)
|
|
if spec is not None:
|
|
stmt = spec.apply(stmt)
|
|
else:
|
|
stmt = stmt.order_by(self.model.created_at.desc())
|
|
stmt = stmt.offset(offset).limit(limit)
|
|
result = await self.session.execute(stmt)
|
|
return result.scalars().all()
|
|
|
|
async def count_filtered(
|
|
self, tenant_id: UUID, *, spec: VehicleListSpec | None = None
|
|
) -> int:
|
|
stmt = select(func.count()).select_from(self.model).where(
|
|
self.model.tenant_id == tenant_id,
|
|
self.model.is_deleted.is_(False),
|
|
)
|
|
if spec is not None:
|
|
clauses = spec.filter_clauses()
|
|
if clauses:
|
|
stmt = stmt.where(*clauses)
|
|
result = await self.session.execute(stmt)
|
|
return int(result.scalar_one())
|
|
|
|
|
|
class VehicleAssignmentRepository(TenantBaseRepository[VehicleAssignment]):
|
|
model = VehicleAssignment
|
|
|
|
async def list_for_vehicle(
|
|
self, tenant_id: UUID, vehicle_id: UUID
|
|
) -> Sequence[VehicleAssignment]:
|
|
stmt = (
|
|
select(self.model)
|
|
.where(
|
|
self.model.tenant_id == tenant_id,
|
|
self.model.vehicle_id == vehicle_id,
|
|
self.model.is_deleted.is_(False),
|
|
)
|
|
.order_by(self.model.created_at.desc())
|
|
)
|
|
result = await self.session.execute(stmt)
|
|
return result.scalars().all()
|
|
|
|
async def get_active_for_vehicle(
|
|
self, tenant_id: UUID, vehicle_id: UUID
|
|
) -> VehicleAssignment | None:
|
|
stmt = select(self.model).where(
|
|
self.model.tenant_id == tenant_id,
|
|
self.model.vehicle_id == vehicle_id,
|
|
self.model.status == VehicleAssignmentStatus.ACTIVE,
|
|
self.model.is_deleted.is_(False),
|
|
)
|
|
result = await self.session.execute(stmt)
|
|
return result.scalar_one_or_none()
|