from sqlalchemy import Boolean, ForeignKey, Integer, String, text from sqlalchemy.orm import Mapped, mapped_column from api.v1.common.base_models import TenantScopedMixin, TimestampMixin from core.database import Base class Pipeline(Base, TenantScopedMixin, TimestampMixin): """Embudo de ventas. Cada company puede tener varios embudos.""" __tablename__ = "pipelines" __table_args__ = {"schema": "crm"} id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) name: Mapped[str] = mapped_column(String(120), nullable=False) is_default: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false")) class PipelineStage(Base, TenantScopedMixin, TimestampMixin): """Etapa de un embudo (columna del Kanban).""" __tablename__ = "pipeline_stages" __table_args__ = {"schema": "crm"} id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) pipeline_id: Mapped[int] = mapped_column( Integer, ForeignKey("crm.pipelines.id"), nullable=False, index=True ) name: Mapped[str] = mapped_column(String(120), nullable=False) # Posición de la columna en el Kanban (0..n) position: Mapped[int] = mapped_column(Integer, nullable=False, server_default=text("0")) # Probabilidad de cierre asociada a la etapa (0-100) probability: Mapped[int] = mapped_column(Integer, nullable=False, server_default=text("0")) # Etapas terminales: ganada / perdida is_won: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false")) is_lost: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false"))