import enum import uuid from sqlalchemy import ( JSON, Boolean, Column, DateTime, Enum as SQLEnum, Float, ForeignKey, Integer, String, Text, ) from sqlalchemy.orm import relationship from sqlalchemy.sql import func from core.database import Base class LeadStatus(str, enum.Enum): NEW = "new" QUALIFIED = "qualified" DISQUALIFIED = "disqualified" CONTACTED = "contacted" SPAM = "spam" class DealStage(str, enum.Enum): DISCOVERY = "discovery" QUALIFICATION = "qualification" PROPOSAL = "proposal" NEGOTIATION = "negotiation" CLOSED_WON = "closed_won" CLOSED_LOST = "closed_lost" class CommissionStatus(str, enum.Enum): ACCRUED = "accrued" APPROVED = "approved" PAID = "paid" CANCELLED = "cancelled" class Lead(Base): __tablename__ = "sales_leads" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) workspace_id = Column(String, ForeignKey("workspaces.id"), nullable=False) external_id = Column(String, nullable=True, index=True) # HubSpot/Salesforce ID email = Column(String, nullable=False) first_name = Column(String, nullable=True) last_name = Column(String, nullable=True) company = Column(String, nullable=True) source = Column(String, nullable=True) # Website, LinkedIn, etc. status = Column(SQLEnum(LeadStatus), default=LeadStatus.NEW) # AI Enrichment ai_score = Column(Float, default=0.0) ai_qualification_summary = Column(Text, nullable=True) is_spam = Column(Boolean, default=False) is_converted = Column(Boolean, default=False) metadata_json = Column(JSON, nullable=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), onupdate=func.now()) class NegotiationState(str, enum.Enum): INITIAL = "initial" DISCOVERY = "discovery" BARGAINING = "bargaining" CLOSING = "closing" FOLLOW_UP = "follow_up" WON = "won" LOST = "lost" class Deal(Base): __tablename__ = "sales_deals" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) workspace_id = Column(String, ForeignKey("workspaces.id"), nullable=False) external_id = Column(String, nullable=True, index=True) name = Column(String, nullable=False) value = Column(Float, default=0.0) currency = Column(String, default="USD") stage = Column(SQLEnum(DealStage), default=DealStage.DISCOVERY) probability = Column(Float, default=0.0) # Intelligence health_score = Column(Float, default=0.0) # 0 to 100 risk_level = Column(String, default="low") # low, medium, high last_engagement_at = Column(DateTime(timezone=True), nullable=True) negotiation_state = Column(SQLEnum(NegotiationState), default=NegotiationState.INITIAL) last_followup_at = Column(DateTime(timezone=True), nullable=True) followup_count = Column(Integer, default=0) metadata_json = Column(JSON, nullable=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), onupdate=func.now()) # Relationships transcripts = relationship("CallTranscript", back_populates="deal") commissions = relationship("CommissionEntry", back_populates="deal") class CommissionEntry(Base): __tablename__ = "sales_commissions" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) workspace_id = Column(String, ForeignKey("workspaces.id"), nullable=False) deal_id = Column(String, ForeignKey("sales_deals.id"), nullable=False) invoice_id = Column(String, nullable=True) # Linked accounting invoice payee_id = Column(String, nullable=True) # User/Rep ID amount = Column(Float, nullable=False) currency = Column(String, default="USD") status = Column(SQLEnum(CommissionStatus), default=CommissionStatus.ACCRUED) calculated_at = Column(DateTime(timezone=True), server_default=func.now()) paid_at = Column(DateTime(timezone=True), nullable=True) metadata_json = Column(JSON, nullable=True) # Relationships deal = relationship("Deal", back_populates="commissions") class CallTranscript(Base): __tablename__ = "sales_call_transcripts" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) workspace_id = Column(String, ForeignKey("workspaces.id"), nullable=False) deal_id = Column(String, ForeignKey("sales_deals.id"), nullable=True) meeting_id = Column(String, nullable=True) # Zoom/Teams ID title = Column(String, nullable=True) raw_transcript = Column(Text, nullable=False) summary = Column(Text, nullable=True) objections = Column(JSON, nullable=True) # List of extracted objections action_items = Column(JSON, nullable=True) # List of extracted tasks metadata_json = Column(JSON, nullable=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) # Relationships deal = relationship("Deal", back_populates="transcripts") class FollowUpTask(Base): __tablename__ = "sales_follow_up_tasks" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) workspace_id = Column(String, ForeignKey("workspaces.id"), nullable=False) deal_id = Column(String, ForeignKey("sales_deals.id"), nullable=False) description = Column(Text, nullable=False) suggested_date = Column(DateTime(timezone=True), nullable=True) is_completed = Column(Boolean, default=False) # Reason why AI suggested this ai_rationale = Column(Text, nullable=True) created_at = Column(DateTime(timezone=True), server_default=func.now())