Files
rentman-clone/backend/app/models/contact.py
T

86 lines
3.3 KiB
Python
Raw Normal View History

"""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