""" CUSTOMER Models """ from sqlalchemy import Column, String, Boolean, Integer, Text, Date, DateTime, Numeric, ForeignKey, Double from sqlalchemy.dialects.postgresql import UUID, JSONB, ARRAY from sqlalchemy.orm import relationship from app.models.base import BaseModel from app.models.enums import * class Customer(BaseModel): """ Customers (End Users) Customers are the end-users who purchase telecom services. This table stores their identity and primary contact information. Service-specific addresses are stored in sales_orders and subscriptions. Key Features: - Deduplication by phone_primary (unique constraint) - Location hierarchy: Country → Region → City → Address → Coordinates - Links to project_region for administrative region tracking - Flexible additional_metadata JSONB for future needs Business Rules: - One customer per phone number globally (prevents duplicates) - phone_primary is required and must be unique - phone_alternative can be used for alternate contact - Linked to a client (telecom operator) - Can have multiple subscriptions (one per service address) """ __tablename__ = "customers" # Organization Link client_id = Column(UUID(as_uuid=True), ForeignKey("clients.id", ondelete="RESTRICT"), nullable=False, index=True) # Identity customer_name = Column(Text, nullable=False) id_number = Column(Text, nullable=True) # National ID or passport business_name = Column(Text, nullable=True) # For business customers # Contact Information phone_primary = Column(Text, nullable=False, index=True) # Unique constraint via index phone_alternative = Column(Text, nullable=True, index=True) email = Column(Text, nullable=True) # Primary Address (where customer can be reached, not necessarily service address) # Region reference provides country/region/city hierarchy project_region_id = Column(UUID(as_uuid=True), ForeignKey("project_regions.id", ondelete="SET NULL"), nullable=True, index=True) primary_address_line1 = Column(Text, nullable=True) primary_address_line2 = Column(Text, nullable=True) primary_maps_link = Column(Text, nullable=True) # Google Maps share link (primary source) primary_latitude = Column(Double, nullable=True) primary_longitude = Column(Double, nullable=True) # Status is_active = Column(Boolean, default=True, nullable=False) # Additional metadata for future flexibility additional_metadata = Column(JSONB, default={}, nullable=False) # Relationships client = relationship("Client", back_populates="customers") project_region = relationship("ProjectRegion", foreign_keys=[project_region_id]) sales_orders = relationship("SalesOrder", back_populates="customer", lazy="dynamic") subscriptions = relationship("Subscription", back_populates="customer", lazy="dynamic") incidents = relationship("Incident", back_populates="customer", lazy="dynamic") communications = relationship("CustomerCommunication", back_populates="customer", cascade="all, delete-orphan", lazy="select") def __repr__(self): return f"" @property def has_active_subscriptions(self) -> bool: """Check if customer has any active subscriptions""" return any(sub.status == 'active' for sub in self.subscriptions) @property def subscription_count(self) -> int: """Count of all subscriptions (active and inactive)""" return self.subscriptions.count() @property def has_location(self) -> bool: """Check if customer has location coordinates""" return self.primary_latitude is not None and self.primary_longitude is not None