# app/modules/tenancy/models/merchant.py """ Merchant model representing the business entity that owns one or more store brands. A Merchant represents the legal/business entity with contact information, while Stores represent the individual brands/storefronts operated by that merchant. """ from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Text from sqlalchemy.orm import relationship from app.core.database import Base from models.database.base import TimestampMixin class Merchant(Base, TimestampMixin): """ Represents a merchant (business entity) in the system. A merchant owns one or more store brands. All business/contact information is stored at the merchant level to avoid duplication. """ __tablename__ = "merchants" # ======================================================================== # Basic Information # ======================================================================== id = Column(Integer, primary_key=True, index=True) """Unique identifier for the merchant.""" name = Column(String, nullable=False, index=True) """Merchant legal/business name.""" description = Column(Text) """Optional description of the merchant.""" # ======================================================================== # Ownership # ======================================================================== owner_user_id = Column(Integer, ForeignKey("users.id"), nullable=False) """Foreign key to the user who owns this merchant.""" # ======================================================================== # Contact Information # ======================================================================== contact_email = Column(String, nullable=False) """Primary business contact email.""" contact_phone = Column(String) """Business phone number.""" website = Column(String) """Merchant website URL.""" # ======================================================================== # Business Details # ======================================================================== business_address = Column(Text) """Physical business address.""" tax_number = Column(String) """Tax/VAT registration number.""" # ======================================================================== # Status Flags # ======================================================================== is_active = Column(Boolean, default=True, nullable=False) """Whether the merchant is active. Affects all associated stores.""" is_verified = Column(Boolean, default=False, nullable=False) """Whether the merchant has been verified by platform admins.""" # ======================================================================== # Relationships # ======================================================================== owner = relationship("User", back_populates="owned_merchants") """The user who owns this merchant.""" stores = relationship( "Store", back_populates="merchant", cascade="all, delete-orphan", order_by="Store.name", ) """All store brands operated by this merchant.""" merchant_domains = relationship( "MerchantDomain", back_populates="merchant", cascade="all, delete-orphan", ) """Custom domains registered at the merchant level (inherited by all stores).""" def __repr__(self): """String representation of the Merchant object.""" return f"" # ======================================================================== # Helper Properties # ======================================================================== @property def store_count(self) -> int: """Get the number of stores belonging to this merchant.""" return len(self.stores) if self.stores else 0 @property def primary_domain(self) -> str | None: """Get the primary active and verified merchant domain.""" for md in self.merchant_domains: if md.is_primary and md.is_active and md.is_verified: return md.domain return None @property def active_store_count(self) -> int: """Get the number of active stores belonging to this merchant.""" if not self.stores: return 0 return sum(1 for v in self.stores if v.is_active) __all__ = ["Merchant"]