"""Routing & optimization aggregates — Phase 10.6.""" from __future__ import annotations import uuid from datetime import datetime from sqlalchemy import DateTime, Index, Integer, String, Text, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.types import JSON from app.core.database import Base from app.models.base import ( ActorAuditMixin, OptimisticLockMixin, SoftDeleteMixin, TenantMixin, TimestampMixin, UUIDPrimaryKeyMixin, ) from app.models.types import GUID, OptimizationRunStatus, RoutePlanStatus, RouteStopStatus class RoutePlan( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, OptimisticLockMixin, ): __tablename__ = "route_plans" organization_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) code: Mapped[str] = mapped_column(String(50), nullable=False) status: Mapped[RoutePlanStatus] = mapped_column( default=RoutePlanStatus.DRAFT, nullable=False ) driver_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True) dispatch_job_ids: Mapped[list | None] = mapped_column(JSON, nullable=True) routing_engine_ref: Mapped[str | None] = mapped_column(String(255), nullable=True) planned_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True ) metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True) __table_args__ = ( UniqueConstraint("tenant_id", "code", name="uq_route_plans_tenant_code"), Index("ix_route_plans_tenant_status", "tenant_id", "status"), Index("ix_route_plans_tenant_deleted", "tenant_id", "is_deleted"), ) class RouteStop( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, ): __tablename__ = "route_stops" route_plan_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) sequence: Mapped[int] = mapped_column(Integer, nullable=False) status: Mapped[RouteStopStatus] = mapped_column( default=RouteStopStatus.PENDING, nullable=False ) address_ref: Mapped[str | None] = mapped_column(String(255), nullable=True) external_order_ref: Mapped[str | None] = mapped_column(String(255), nullable=True) notes: Mapped[str | None] = mapped_column(Text, nullable=True) metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True) __table_args__ = ( Index("ix_route_stops_plan", "tenant_id", "route_plan_id"), Index("ix_route_stops_tenant_deleted", "tenant_id", "is_deleted"), ) class OptimizationRun( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, ): """External routing via RoutingEngineProvider protocol refs only.""" __tablename__ = "optimization_runs" route_plan_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True) status: Mapped[OptimizationRunStatus] = mapped_column( default=OptimizationRunStatus.PENDING, nullable=False ) routing_engine_ref: Mapped[str | None] = mapped_column(String(255), nullable=True) request_payload: Mapped[dict | None] = mapped_column(JSON, nullable=True) result_payload: Mapped[dict | None] = mapped_column(JSON, nullable=True) started_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True ) completed_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True ) error_message: Mapped[str | None] = mapped_column(Text, nullable=True) __table_args__ = ( Index("ix_optimization_runs_tenant_status", "tenant_id", "status"), Index("ix_optimization_runs_plan", "tenant_id", "route_plan_id"), )