"""Contact model for companies and persons.""" import uuid from datetime import datetime from sqlalchemy import String, DateTime, ForeignKey, func, Table, Column from sqlalchemy.orm import Mapped, mapped_column, relationship from app.db.base import Base # Many-to-many association table: contacts ↔ tags contact_tags = Table( "contact_tags", Base.metadata, Column( "contact_id", String(36), ForeignKey("contacts.id", ondelete="CASCADE"), primary_key=True, ), Column( "tag_id", String(36), ForeignKey("tags.id", ondelete="CASCADE"), primary_key=True, ), ) class Contact(Base): """Represents a contact (company or person) within a tenant account.""" __tablename__ = "contacts" id: Mapped[str] = mapped_column( String(36), primary_key=True, default=lambda: str(uuid.uuid4()) ) account_id: Mapped[str] = mapped_column( String(36), ForeignKey("accounts.id", ondelete="CASCADE"), nullable=False ) type: Mapped[str] = mapped_column( String(10), nullable=False ) # 'company' or 'person' company_name: Mapped[str | None] = mapped_column(String(255), nullable=True) first_name: Mapped[str | None] = mapped_column(String(100), nullable=True) last_name: Mapped[str | None] = mapped_column(String(100), nullable=True) email: Mapped[str | None] = mapped_column(String(255), nullable=True) phone: Mapped[str | None] = mapped_column(String(50), nullable=True) mobile: Mapped[str | None] = mapped_column(String(50), nullable=True) website: Mapped[str | None] = mapped_column(String(500), nullable=True) # Billing address billing_street: Mapped[str | None] = mapped_column(String(255), nullable=True) billing_number: Mapped[str | None] = mapped_column(String(20), nullable=True) billing_postalcode: Mapped[str | None] = mapped_column(String(20), nullable=True) billing_city: Mapped[str | None] = mapped_column(String(100), nullable=True) billing_country: Mapped[str | None] = mapped_column(String(100), nullable=True) # Shipping address shipping_street: Mapped[str | None] = mapped_column(String(255), nullable=True) shipping_number: Mapped[str | None] = mapped_column(String(20), nullable=True) shipping_postalcode: Mapped[str | None] = mapped_column(String(20), nullable=True) shipping_city: Mapped[str | None] = mapped_column(String(100), nullable=True) shipping_country: Mapped[str | None] = mapped_column(String(100), nullable=True) # Additional fields from design.md tax_number: Mapped[str | None] = mapped_column(String(50), nullable=True) note: Mapped[str | None] = mapped_column(String, nullable=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), nullable=False ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False, ) # Relationships account: Mapped["Account"] = relationship("Account", back_populates="contacts") tags: Mapped[list["Tag"]] = relationship( "Tag", secondary=contact_tags, back_populates="contacts" ) # projects_as_customer relationship will be added in Phase 3 when Project model is created