"""Driver management aggregates — Phase 10.1. UUID refs only within delivery_db. No fleet/dispatch ownership. Identity/CRM/Storage are external refs only. """ from __future__ import annotations import uuid from datetime import date, datetime from sqlalchemy import ( Boolean, Date, 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 ( DriverCredentialKind, DriverDocumentKind, DriverLifecycleAction, DriverStatus, GUID, ) class Driver( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, OptimisticLockMixin, ): """Courier/driver profile owned by Delivery Platform.""" __tablename__ = "drivers" 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) display_name: Mapped[str] = mapped_column(String(255), nullable=False) first_name: Mapped[str | None] = mapped_column(String(100), nullable=True) last_name: Mapped[str | None] = mapped_column(String(100), nullable=True) mobile: Mapped[str | None] = mapped_column(String(32), nullable=True) email: Mapped[str | None] = mapped_column(String(255), nullable=True) status: Mapped[DriverStatus] = mapped_column( default=DriverStatus.PENDING, nullable=False ) external_user_ref: Mapped[str | None] = mapped_column(String(100), nullable=True) external_crm_contact_ref: Mapped[str | None] = mapped_column( String(100), nullable=True ) hire_date: Mapped[date | None] = mapped_column(Date, nullable=True) notes: Mapped[str | None] = mapped_column(Text, nullable=True) capability_tags: Mapped[list | None] = mapped_column(JSON, nullable=True) metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True) status_reason: Mapped[str | None] = mapped_column(String(500), nullable=True) status_changed_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True ) __table_args__ = ( UniqueConstraint("tenant_id", "code", name="uq_drivers_tenant_code"), Index("ix_drivers_tenant_status", "tenant_id", "status"), Index("ix_drivers_org", "tenant_id", "organization_id"), Index("ix_drivers_hub", "tenant_id", "hub_id"), Index("ix_drivers_mobile", "tenant_id", "mobile"), Index("ix_drivers_tenant_deleted", "tenant_id", "is_deleted"), Index("ix_drivers_display_name", "tenant_id", "display_name"), ) class DriverCredential( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, ): """Credential / compliance refs for a driver (binaries via Storage refs).""" __tablename__ = "driver_credentials" driver_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) kind: Mapped[DriverCredentialKind] = mapped_column( default=DriverCredentialKind.LICENSE, nullable=False ) number: Mapped[str] = mapped_column(String(100), nullable=False) issued_at: Mapped[date | None] = mapped_column(Date, nullable=True) expires_at: Mapped[date | None] = mapped_column(Date, nullable=True) is_verified: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) document_file_ref: Mapped[str | None] = mapped_column(String(255), nullable=True) metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True) __table_args__ = ( UniqueConstraint( "tenant_id", "driver_id", "kind", "number", name="uq_driver_credentials_tenant_kind_number", ), Index("ix_driver_credentials_driver", "tenant_id", "driver_id"), Index("ix_driver_credentials_tenant_deleted", "tenant_id", "is_deleted"), ) class DriverDocument( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, ): """Driver document metadata — Storage owns file blobs.""" __tablename__ = "driver_documents" driver_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) kind: Mapped[DriverDocumentKind] = mapped_column( default=DriverDocumentKind.OTHER, nullable=False ) title: Mapped[str] = mapped_column(String(255), nullable=False) storage_file_ref: Mapped[str] = mapped_column(String(255), nullable=False) notes: Mapped[str | None] = mapped_column(Text, nullable=True) __table_args__ = ( Index("ix_driver_documents_driver", "tenant_id", "driver_id"), Index("ix_driver_documents_tenant_deleted", "tenant_id", "is_deleted"), ) class DriverLifecycleEvent(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin): """Append-only driver lifecycle history.""" __tablename__ = "driver_lifecycle_events" driver_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) action: Mapped[DriverLifecycleAction] = mapped_column(nullable=False) from_status: Mapped[DriverStatus] = mapped_column(nullable=False) to_status: Mapped[DriverStatus] = mapped_column(nullable=False) reason: Mapped[str | None] = mapped_column(String(500), nullable=True) actor_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True) metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True) __table_args__ = ( Index("ix_driver_lifecycle_driver", "tenant_id", "driver_id"), Index("ix_driver_lifecycle_created", "tenant_id", "created_at"), )