"""Clinic management aggregates — Phase 13.3.""" from __future__ import annotations import uuid from sqlalchemy import Boolean, 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 ClinicPolicyKind, GUID, LifecycleStatus class ClinicService( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, OptimisticLockMixin, ): __tablename__ = "clinic_services" clinic_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) department_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True) code: Mapped[str] = mapped_column(String(50), nullable=False) name: Mapped[str] = mapped_column(String(255), nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) duration_minutes: Mapped[int] = mapped_column(Integer, default=30, nullable=False) pricing_ref: Mapped[str | None] = mapped_column(String(100), nullable=True) status: Mapped[LifecycleStatus] = mapped_column( default=LifecycleStatus.ACTIVE, nullable=False ) metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True) __table_args__ = ( UniqueConstraint("tenant_id", "code", name="uq_clinic_services_tenant_code"), Index("ix_clinic_services_clinic", "tenant_id", "clinic_id"), ) class StaffAssignment( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, ): __tablename__ = "staff_assignments" clinic_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) department_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True) doctor_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) code: Mapped[str] = mapped_column(String(50), nullable=False) role_code: Mapped[str] = mapped_column(String(50), nullable=False) is_primary: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True) __table_args__ = ( UniqueConstraint("tenant_id", "code", name="uq_staff_assignments_tenant_code"), Index("ix_staff_assignments_clinic", "tenant_id", "clinic_id"), Index("ix_staff_assignments_doctor", "tenant_id", "doctor_id"), ) class ClinicPolicy( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, OptimisticLockMixin, ): __tablename__ = "clinic_policies" clinic_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) code: Mapped[str] = mapped_column(String(50), nullable=False) name: Mapped[str] = mapped_column(String(255), nullable=False) kind: Mapped[ClinicPolicyKind] = mapped_column(nullable=False) booking_lead_time_hours: Mapped[int] = mapped_column(Integer, default=24, nullable=False) cancellation_window_hours: Mapped[int] = mapped_column(Integer, default=12, nullable=False) privacy_flags: Mapped[dict | None] = mapped_column(JSON, nullable=True) rules: Mapped[dict | None] = mapped_column(JSON, nullable=True) status: Mapped[LifecycleStatus] = mapped_column( default=LifecycleStatus.ACTIVE, nullable=False ) __table_args__ = ( UniqueConstraint("tenant_id", "code", name="uq_clinic_policies_tenant_code"), Index("ix_clinic_policies_clinic", "tenant_id", "clinic_id"), ) class OperatingHoursPolicy( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, ): __tablename__ = "operating_hours_policies" clinic_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) branch_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True) code: Mapped[str] = mapped_column(String(50), nullable=False) name: Mapped[str] = mapped_column(String(255), nullable=False) weekly_hours: Mapped[dict] = mapped_column(JSON, nullable=False) exceptions: Mapped[dict | None] = mapped_column(JSON, nullable=True) timezone: Mapped[str] = mapped_column(String(64), default="Asia/Tehran", nullable=False) __table_args__ = ( UniqueConstraint("tenant_id", "code", name="uq_operating_hours_policies_tenant_code"), Index("ix_operating_hours_clinic", "tenant_id", "clinic_id"), )