Files
orion/app/modules/tenancy/models/merchant_domain.py
Samir Boulahtit aad18c27ab
Some checks failed
CI / ruff (push) Successful in 11s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has started running
refactor: remove all backward compatibility code across 70 files
Clean up 28 backward compatibility instances identified in the codebase.
The app is not live, so all shims are replaced with the target architecture:

- Remove legacy Inventory.location column (use bin_location exclusively)
- Remove dashboard _extract_metric_value helper (use flat metrics dict)
- Remove legacy stat field duplicates (total_stores, total_imports, etc.)
- Remove 13 re-export shims and class aliases across modules
- Remove module-enabling JSON fallback (use PlatformModule junction table)
- Remove menu_to_legacy_format() conversion (return dataclasses directly)
- Remove title/description from MarketplaceProductBase schema
- Clean billing convenience method docstrings
- Clean test fixtures and backward-compat comments
- Add PlatformModule seeding to init_production.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 13:20:29 +01:00

118 lines
3.7 KiB
Python

# app/modules/tenancy/models/merchant_domain.py
"""
Merchant Domain Model - Maps custom domains to merchants for merchant-level domain routing.
When a merchant subscribes to a platform (e.g., loyalty), they can register a domain
(e.g., myloyaltyprogram.lu) that serves as the default for all their stores.
Individual stores can optionally override this with their own custom StoreDomain.
Domain Resolution Priority:
1. Store-specific custom domain (StoreDomain) -> highest priority
2. Merchant domain (MerchantDomain) -> inherited default
3. Store subdomain ({store.subdomain}.rewardflow.lu) -> fallback
"""
from sqlalchemy import (
Boolean,
Column,
DateTime,
ForeignKey,
Index,
Integer,
String,
UniqueConstraint,
)
from sqlalchemy.orm import relationship
from app.core.database import Base
from models.database.base import TimestampMixin
class MerchantDomain(Base, TimestampMixin):
"""
Maps custom domains to merchants for merchant-level domain routing.
Examples:
- myloyaltyprogram.lu -> Merchant "WizaCorp" (all stores inherit)
- Store ORION overrides with StoreDomain -> mysuperloyaltyprogram.lu
- Store WIZAGADGETS -> inherits myloyaltyprogram.lu
"""
__tablename__ = "merchant_domains"
id = Column(Integer, primary_key=True, index=True)
merchant_id = Column(
Integer, ForeignKey("merchants.id", ondelete="CASCADE"), nullable=False
)
# Domain configuration
domain = Column(String(255), nullable=False, unique=True, index=True)
is_primary = Column(Boolean, default=True, nullable=False)
is_active = Column(Boolean, default=True, nullable=False)
# SSL/TLS status (for monitoring)
ssl_status = Column(
String(50), default="pending"
) # pending, active, expired, error
ssl_verified_at = Column(DateTime(timezone=True), nullable=True)
# DNS verification (to confirm domain ownership)
verification_token = Column(String(100), unique=True, nullable=True)
is_verified = Column(Boolean, default=False, nullable=False)
verified_at = Column(DateTime(timezone=True), nullable=True)
# Platform association (for platform context resolution from custom domains)
platform_id = Column(
Integer,
ForeignKey("platforms.id", ondelete="SET NULL"),
nullable=True,
index=True,
comment="Platform this domain is associated with (for platform context resolution)",
)
# Relationships
merchant = relationship("Merchant", back_populates="merchant_domains")
platform = relationship("Platform")
# Constraints
__table_args__ = (
UniqueConstraint("merchant_id", "platform_id", name="uq_merchant_domain_platform"),
Index("idx_merchant_domain_active", "domain", "is_active"),
Index("idx_merchant_domain_primary", "merchant_id", "is_primary"),
Index("idx_merchant_domain_platform", "platform_id"),
)
def __repr__(self):
return f"<MerchantDomain(domain='{self.domain}', merchant_id={self.merchant_id})>"
@property
def full_url(self):
"""Return full URL with https"""
return f"https://{self.domain}"
@classmethod
def normalize_domain(cls, domain: str) -> str:
"""
Normalize domain for consistent storage.
Reuses the same logic as StoreDomain.normalize_domain().
Examples:
- https://example.com -> example.com
- www.example.com -> example.com
- EXAMPLE.COM -> example.com
"""
# Remove protocol
domain = domain.replace("https://", "").replace("http://", "") # noqa: SEC034
# Remove trailing slash
domain = domain.rstrip("/")
# Convert to lowercase
domain = domain.lower()
return domain
__all__ = ["MerchantDomain"]