""" Client Profile Model - ServiceManagerWeb Modelo para perfil empresarial de clientes Almacena información detallada de la empresa cliente """ from sqlalchemy import String, Boolean, DateTime, ForeignKey, Text, Numeric from sqlalchemy.orm import Mapped, mapped_column, relationship from typing import Optional, TYPE_CHECKING import uuid from datetime import datetime from app.core.database import Base, GUID if TYPE_CHECKING: from app.models.tenant import Tenant class ClientProfile(Base): """Modelo de Perfil de Cliente Empresarial.""" __tablename__ = "client_profiles" # Relación con tenant (uno a uno) tenant_id: Mapped[uuid.UUID] = mapped_column( GUID(), ForeignKey("tenants.id", ondelete="CASCADE"), unique=True, nullable=False, index=True ) # === INFORMACIÓN GENERAL === business_name: Mapped[Optional[str]] = mapped_column(String(255)) # Razón social commercial_name: Mapped[Optional[str]] = mapped_column(String(255)) # Nombre comercial client_code: Mapped[Optional[str]] = mapped_column(String(50)) # Clave de cliente client_type: Mapped[Optional[str]] = mapped_column(String(50)) # Tipo de cliente rfc: Mapped[Optional[str]] = mapped_column(String(13)) # RFC México tax_id: Mapped[Optional[str]] = mapped_column(String(50)) # ID fiscal general # === UBICACIÓN === country: Mapped[Optional[str]] = mapped_column(String(100)) state: Mapped[Optional[str]] = mapped_column(String(100)) city: Mapped[Optional[str]] = mapped_column(String(100)) address: Mapped[Optional[str]] = mapped_column(Text) external_number: Mapped[Optional[str]] = mapped_column(String(20)) internal_number: Mapped[Optional[str]] = mapped_column(String(20)) postal_code: Mapped[Optional[str]] = mapped_column(String(10)) neighborhood: Mapped[Optional[str]] = mapped_column(String(100)) # === CONTACTO === main_phone: Mapped[Optional[str]] = mapped_column(String(20)) secondary_phone: Mapped[Optional[str]] = mapped_column(String(20)) direct_phone: Mapped[Optional[str]] = mapped_column(String(20)) phone_extension: Mapped[Optional[str]] = mapped_column(String(10)) fax: Mapped[Optional[str]] = mapped_column(String(20)) # === INFORMACIÓN ADICIONAL === business_hours: Mapped[Optional[str]] = mapped_column(String(255)) website: Mapped[Optional[str]] = mapped_column(String(255)) main_email: Mapped[Optional[str]] = mapped_column(String(320)) billing_email: Mapped[Optional[str]] = mapped_column(String(320)) # === MARKETING === advertising_medium: Mapped[Optional[str]] = mapped_column(String(255)) nationality: Mapped[Optional[str]] = mapped_column(String(100)) # === CONFIGURACIÓN EMPRESARIAL === logo_url: Mapped[Optional[str]] = mapped_column(String(500)) company_representative: Mapped[Optional[str]] = mapped_column(String(255)) # Encargado/Representante legal_representative: Mapped[Optional[str]] = mapped_column(String(255)) # === FINANZAS/FACTURACIÓN === credit_limit: Mapped[Optional[float]] = mapped_column(Numeric(15, 2)) payment_terms: Mapped[Optional[str]] = mapped_column(String(100)) preferred_currency: Mapped[str] = mapped_column(String(3), default="MXN") # === METADATOS === send_to_billing: Mapped[bool] = mapped_column(Boolean, default=False) is_active_client: Mapped[bool] = mapped_column(Boolean, default=True) is_prospect: Mapped[bool] = mapped_column(Boolean, default=False) notes: Mapped[Optional[str]] = mapped_column(Text) # === RELACIONES === tenant: Mapped["Tenant"] = relationship("Tenant", back_populates="client_profile") def __repr__(self) -> str: return f"" @property def full_address(self) -> str: """Dirección completa formateada.""" address_parts = [] if self.address: address_parts.append(self.address) if self.external_number: if self.internal_number: address_parts.append(f"#{self.external_number}-{self.internal_number}") else: address_parts.append(f"#{self.external_number}") if self.neighborhood: address_parts.append(f"Col. {self.neighborhood}") if self.city and self.state: address_parts.append(f"{self.city}, {self.state}") if self.postal_code: address_parts.append(f"C.P. {self.postal_code}") if self.country: address_parts.append(self.country) return ", ".join(address_parts) @property def display_name(self) -> str: """Nombre para mostrar (comercial o razón social).""" return self.commercial_name or self.business_name or "Sin nombre"