# app/modules/billing/models/tier_feature_limit.py """ Feature limit models for tier-based and merchant-level access control. Provides: - TierFeatureLimit: Per-tier, per-feature limits (replaces hardcoded limit columns) - MerchantFeatureOverride: Per-merchant overrides for admin-set exceptions """ from sqlalchemy import ( Boolean, Column, ForeignKey, Index, Integer, String, UniqueConstraint, ) from sqlalchemy.orm import relationship from app.core.database import Base from models.database.base import TimestampMixin class TierFeatureLimit(Base, TimestampMixin): """ Per-tier, per-feature limit definition. Replaces hardcoded limit columns on SubscriptionTier (orders_per_month, products_limit, etc.) and the features JSON array. For BINARY features: presence in this table = feature enabled for tier. For QUANTITATIVE features: limit_value is the cap (NULL = unlimited). Example: TierFeatureLimit(tier_id=1, feature_code="products_limit", limit_value=200) TierFeatureLimit(tier_id=1, feature_code="analytics_dashboard", limit_value=None) """ __tablename__ = "tier_feature_limits" id = Column(Integer, primary_key=True, index=True) tier_id = Column( Integer, ForeignKey("subscription_tiers.id", ondelete="CASCADE"), nullable=False, index=True, ) feature_code = Column(String(80), nullable=False, index=True) # For QUANTITATIVE: cap value (NULL = unlimited) # For BINARY: ignored (presence means enabled) limit_value = Column(Integer, nullable=True) # Relationships tier = relationship( "SubscriptionTier", back_populates="feature_limits", foreign_keys=[tier_id], ) __table_args__ = ( UniqueConstraint( "tier_id", "feature_code", name="uq_tier_feature_code", ), Index("idx_tier_feature_lookup", "tier_id", "feature_code"), ) def __repr__(self): limit = f", limit={self.limit_value}" if self.limit_value is not None else "" return f"" class MerchantFeatureOverride(Base, TimestampMixin): """ Per-merchant, per-platform feature override. Allows admins to override tier limits for specific merchants. For example, giving a merchant 500 products instead of tier's 200. Example: MerchantFeatureOverride( merchant_id=1, platform_id=1, feature_code="products_limit", limit_value=500, reason="Enterprise deal - custom product limit", ) """ __tablename__ = "merchant_feature_overrides" id = Column(Integer, primary_key=True, index=True) merchant_id = Column( Integer, ForeignKey("merchants.id", ondelete="CASCADE"), nullable=False, index=True, ) platform_id = Column( Integer, ForeignKey("platforms.id", ondelete="CASCADE"), nullable=False, index=True, ) feature_code = Column(String(80), nullable=False, index=True) # Override limit (NULL = unlimited) limit_value = Column(Integer, nullable=True) # Force enable/disable (overrides tier assignment) is_enabled = Column(Boolean, default=True, nullable=False) # Admin note explaining the override reason = Column(String(255), nullable=True) # Relationships merchant = relationship("Merchant", foreign_keys=[merchant_id]) platform = relationship("Platform", foreign_keys=[platform_id]) __table_args__ = ( UniqueConstraint( "merchant_id", "platform_id", "feature_code", name="uq_merchant_platform_feature", ), Index("idx_merchant_override_lookup", "merchant_id", "platform_id", "feature_code"), ) def __repr__(self): return ( f"" ) __all__ = ["TierFeatureLimit", "MerchantFeatureOverride"]