diff --git a/app/models/activity.py b/app/models/activity.py new file mode 100644 index 0000000..b013841 --- /dev/null +++ b/app/models/activity.py @@ -0,0 +1,77 @@ +"""Activity model: calls, meetings, emails, tasks, notes tied to any entity.""" + +from __future__ import annotations + +from datetime import datetime +from enum import Enum +from typing import TYPE_CHECKING, Optional + +from sqlalchemy import DateTime, ForeignKey, String, Text +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from app.models.base import Base, OrgScopedMixin, SoftDeleteMixin, TimestampMixin + +if TYPE_CHECKING: + from app.models.user import User + from app.models.account import Account + from app.models.contact import Contact + from app.models.deal import Deal + + +class ActivityType(str, Enum): + """Type of activity.""" + + call = "call" + meeting = "meeting" + email = "email" + task = "task" + note = "note" + + +class Activity(Base, TimestampMixin, SoftDeleteMixin, OrgScopedMixin): + __tablename__ = "activities" + + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + type: Mapped[ActivityType] = mapped_column( + String(32), nullable=False, index=True + ) + subject: Mapped[str] = mapped_column(String(255), nullable=False) + body: Mapped[Optional[str]] = mapped_column(Text, nullable=True) + due_date: Mapped[Optional[datetime]] = mapped_column( + DateTime(timezone=True), nullable=True, index=True + ) + completed_at: Mapped[Optional[datetime]] = mapped_column( + DateTime(timezone=True), nullable=True + ) + account_id: Mapped[Optional[int]] = mapped_column( + ForeignKey("accounts.id"), nullable=True, index=True + ) + contact_id: Mapped[Optional[int]] = mapped_column( + ForeignKey("contacts.id"), nullable=True, index=True + ) + deal_id: Mapped[Optional[int]] = mapped_column( + ForeignKey("deals.id"), nullable=True, index=True + ) + owner_id: Mapped[int] = mapped_column( + ForeignKey("users.id"), nullable=False, index=True + ) + + # Relationships + account: Mapped[Optional["Account"]] = relationship( + "Account", back_populates="activities", lazy="selectin" + ) + contact: Mapped[Optional["Contact"]] = relationship( + "Contact", back_populates="activities", lazy="selectin" + ) + deal: Mapped[Optional["Deal"]] = relationship( + "Deal", back_populates="activities", lazy="selectin" + ) + owner: Mapped["User"] = relationship( + "User", foreign_keys=[owner_id], lazy="joined" + ) + + def __repr__(self) -> str: + return f"" + + +__all__ = ["Activity", "ActivityType"]