"""SQLAlchemy models for contacts and contact persons.""" import enum import uuid from datetime import datetime from sqlalchemy import ( Boolean, CheckConstraint, DateTime, ForeignKey, String, func, ) from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database import Base class ContactRole(str, enum.Enum): kaeufer = "kaeufer" verkaeufer = "verkaeufer" beide = "beide" class VatIdStatus(str, enum.Enum): ungeprueft = "ungeprueft" geprueft = "geprueft" ungueltig = "ungueltig" manuell_bestaetigt = "manuell_bestaetigt" class Contact(Base): """Contact entity representing a company or private contact.""" __tablename__ = "contacts" __table_args__ = ( CheckConstraint( "role IN ('kaeufer', 'verkaeufer', 'beide')", name="ck_contacts_role", ), CheckConstraint( "vat_id_status IN ('ungeprueft', 'geprueft', 'ungueltig', 'manuell_bestaetigt')", name="ck_contacts_vat_id_status", ), CheckConstraint( "char_length(address_country) = 2", name="ck_contacts_country_alpha2", ), ) id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, ) company_name: Mapped[str] = mapped_column( String(255), nullable=False, index=True, ) legal_form: Mapped[str | None] = mapped_column( String(50), nullable=True, ) address_street: Mapped[str | None] = mapped_column( String(255), nullable=True, ) address_zip: Mapped[str | None] = mapped_column( String(10), nullable=True, ) address_city: Mapped[str | None] = mapped_column( String(100), nullable=True, ) address_country: Mapped[str] = mapped_column( String(2), nullable=False, default="DE", index=True, ) vat_id: Mapped[str | None] = mapped_column( String(20), nullable=True, ) phone: Mapped[str | None] = mapped_column( String(50), nullable=True, ) email: Mapped[str | None] = mapped_column( String(255), nullable=True, ) website: Mapped[str | None] = mapped_column( String(255), nullable=True, ) role: Mapped[str] = mapped_column( String(20), nullable=False, index=True, ) vat_id_status: Mapped[str] = mapped_column( String(20), nullable=False, default="ungeprueft", ) is_private: Mapped[bool] = mapped_column( Boolean, nullable=False, default=False, ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now(), ) deleted_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True, index=True, ) contact_persons: Mapped[list["ContactPerson"]] = relationship( back_populates="contact", cascade="all, delete-orphan", lazy="selectin", ) def __repr__(self) -> str: return f"" def to_dict(self) -> dict: """Serialize contact for API responses.""" return { "id": str(self.id), "company_name": self.company_name, "legal_form": self.legal_form, "address_street": self.address_street, "address_zip": self.address_zip, "address_city": self.address_city, "address_country": self.address_country, "vat_id": self.vat_id, "phone": self.phone, "email": self.email, "website": self.website, "role": self.role, "vat_id_status": self.vat_id_status, "is_private": self.is_private, "created_at": (self.created_at.isoformat() if self.created_at else None), "updated_at": (self.updated_at.isoformat() if self.updated_at else None), "deleted_at": (self.deleted_at.isoformat() if self.deleted_at else None), "contact_persons": [p.to_dict() for p in (self.contact_persons or [])], } class ContactPerson(Base): """Contact person associated with a contact (company).""" __tablename__ = "contact_persons" id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, ) contact_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("contacts.id", ondelete="CASCADE"), nullable=False, index=True, ) name: Mapped[str] = mapped_column( String(255), nullable=False, ) function: Mapped[str | None] = mapped_column( String(100), nullable=True, ) phone: Mapped[str | None] = mapped_column( String(50), nullable=True, ) email: Mapped[str | None] = mapped_column( String(255), nullable=True, ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) contact: Mapped["Contact"] = relationship(back_populates="contact_persons") def __repr__(self) -> str: return f"" def to_dict(self) -> dict: """Serialize contact person for API responses.""" return { "id": str(self.id), "contact_id": str(self.contact_id), "name": self.name, "function": self.function, "phone": self.phone, "email": self.email, "created_at": (self.created_at.isoformat() if self.created_at else None), }