# 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 SoftDeleteMixin, TimestampMixin class Merchant(Base, TimestampMixin, SoftDeleteMixin): """ 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", foreign_keys="[Merchant.owner_user_id]", 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"]