"""Connector integration aggregates — Phase 12.7. Registrations and dispatches reference external integration platforms (accounting, CRM, loyalty, communication, delivery, website builder) by config/ref only. No httpx calls and no cross-service imports live here — outbound "delivery" is simulated via mock provider implementations. """ from __future__ import annotations import uuid from sqlalchemy import Index, String, Text from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.types import JSON from app.core.database import Base from app.models.base import ( ActorAuditMixin, SoftDeleteMixin, TenantMixin, TimestampMixin, UUIDPrimaryKeyMixin, ) from app.models.types import ( GUID, ConnectorKind, ConnectorStatus, DispatchDirection, DispatchStatus, ) class ConnectorRegistration( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, ): """A tenant-scoped registration of an external integration connector.""" __tablename__ = "connector_registrations" venue_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True) kind: Mapped[ConnectorKind] = mapped_column(nullable=False) code: Mapped[str] = mapped_column(String(50), nullable=False) name: Mapped[str] = mapped_column(String(255), nullable=False) status: Mapped[ConnectorStatus] = mapped_column( default=ConnectorStatus.DRAFT, nullable=False ) external_ref: Mapped[str | None] = mapped_column(String(255), nullable=True) config: Mapped[dict | None] = mapped_column(JSON, nullable=True) __table_args__ = ( Index( "ix_connector_registrations_tenant_code", "tenant_id", "code", unique=True, ), Index("ix_connector_registrations_venue", "tenant_id", "venue_id"), Index("ix_connector_registrations_kind", "tenant_id", "kind"), Index( "ix_connector_registrations_tenant_deleted", "tenant_id", "is_deleted" ), ) class ConnectorDispatch( Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin, ActorAuditMixin, ): """An outbound dispatch attempt from a connector registration (by reference only).""" __tablename__ = "connector_dispatches" venue_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True) registration_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) direction: Mapped[DispatchDirection] = mapped_column( default=DispatchDirection.OUTBOUND, nullable=False ) event_type: Mapped[str] = mapped_column(String(100), nullable=False) payload_ref: Mapped[str | None] = mapped_column(String(255), nullable=True) status: Mapped[DispatchStatus] = mapped_column( default=DispatchStatus.PENDING, nullable=False ) response_json: Mapped[dict | None] = mapped_column(JSON, nullable=True) notes: Mapped[str | None] = mapped_column(Text, nullable=True) __table_args__ = ( Index("ix_connector_dispatches_venue", "tenant_id", "venue_id"), Index( "ix_connector_dispatches_registration", "tenant_id", "registration_id" ), Index("ix_connector_dispatches_status", "tenant_id", "status"), Index( "ix_connector_dispatches_tenant_deleted", "tenant_id", "is_deleted" ), )