"""Dispatch engine aggregates — Phase 10.5.""" from __future__ import annotations import uuid from datetime import datetime from sqlalchemy import DateTime, Index, 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 DispatchJobStatus, GUID, JobAssignmentStatus class DispatchJob( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, OptimisticLockMixin, ): """Vertical job refs only — no deep order ownership.""" __tablename__ = "dispatch_jobs" organization_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) hub_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True) code: Mapped[str] = mapped_column(String(50), nullable=False) status: Mapped[DispatchJobStatus] = mapped_column( default=DispatchJobStatus.PENDING, nullable=False ) external_order_ref: Mapped[str | None] = mapped_column(String(255), nullable=True) external_source: Mapped[str | None] = mapped_column(String(100), nullable=True) priority: Mapped[int] = mapped_column(default=0, nullable=False) pickup_address_ref: Mapped[str | None] = mapped_column(String(255), nullable=True) dropoff_address_ref: Mapped[str | None] = mapped_column(String(255), nullable=True) scheduled_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True ) status_reason: Mapped[str | None] = mapped_column(String(500), nullable=True) metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True) __table_args__ = ( UniqueConstraint("tenant_id", "code", name="uq_dispatch_jobs_tenant_code"), Index("ix_dispatch_jobs_tenant_status", "tenant_id", "status"), Index("ix_dispatch_jobs_external_ref", "tenant_id", "external_order_ref"), Index("ix_dispatch_jobs_tenant_deleted", "tenant_id", "is_deleted"), ) class JobAssignment( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, OptimisticLockMixin, ): __tablename__ = "job_assignments" dispatch_job_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) driver_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) vehicle_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True) status: Mapped[JobAssignmentStatus] = mapped_column( default=JobAssignmentStatus.PENDING, nullable=False ) assigned_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), 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_job_assignments_job", "tenant_id", "dispatch_job_id"), Index("ix_job_assignments_driver", "tenant_id", "driver_id"), Index("ix_job_assignments_tenant_status", "tenant_id", "status"), Index("ix_job_assignments_tenant_deleted", "tenant_id", "is_deleted"), )