diff --git a/app/models/deal_stage_history.py b/app/models/deal_stage_history.py new file mode 100644 index 0000000..86de67c --- /dev/null +++ b/app/models/deal_stage_history.py @@ -0,0 +1,44 @@ +"""DealStageHistory model: audit trail of stage transitions for a deal.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional + +from sqlalchemy import ForeignKey, String +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from app.models.base import Base, OrgScopedMixin, TimestampMixin +from app.models.deal import DealStage + +if TYPE_CHECKING: + from app.models.user import User + from app.models.deal import Deal + + +class DealStageHistory(Base, TimestampMixin, OrgScopedMixin): + """Records each deal stage transition. Append-only — no soft-delete.""" + + __tablename__ = "deal_stage_history" + + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + deal_id: Mapped[int] = mapped_column( + ForeignKey("deals.id"), nullable=False, index=True + ) + from_stage: Mapped[Optional[DealStage]] = mapped_column(String(32), nullable=True) + to_stage: Mapped[DealStage] = mapped_column(String(32), nullable=False) + changed_by: Mapped[int] = mapped_column( + ForeignKey("users.id"), nullable=False + ) + + deal: Mapped["Deal"] = relationship( + "Deal", back_populates="stage_history", lazy="joined" + ) + changer: Mapped["User"] = relationship( + "User", foreign_keys=[changed_by], lazy="joined" + ) + + def __repr__(self) -> str: + return f"{self.to_stage}>" + + +__all__ = ["DealStageHistory"]