# app/modules/loyalty/models/merchant_settings.py """ Merchant loyalty settings database model. Admin-controlled settings that apply to a merchant's loyalty program. These settings are managed by platform administrators, not stores. """ from sqlalchemy import ( Boolean, Column, ForeignKey, Index, Integer, String, ) from sqlalchemy.orm import relationship from app.core.database import Base from models.database.base import TimestampMixin class StaffPinPolicy(str): """Staff PIN policy options.""" REQUIRED = "required" # Staff PIN always required OPTIONAL = "optional" # Store can choose DISABLED = "disabled" # Staff PIN not used class MerchantLoyaltySettings(Base, TimestampMixin): """ Admin-controlled settings for merchant loyalty programs. These settings are managed by platform administrators and cannot be changed by stores. They apply to all stores within the merchant. """ __tablename__ = "merchant_loyalty_settings" id = Column(Integer, primary_key=True, index=True) # Merchant association (one settings per merchant) merchant_id = Column( Integer, ForeignKey("merchants.id", ondelete="CASCADE"), unique=True, nullable=False, index=True, comment="Merchant these settings apply to", ) # ========================================================================= # Staff PIN Policy (Admin-controlled) # ========================================================================= staff_pin_policy = Column( String(20), default=StaffPinPolicy.REQUIRED, nullable=False, comment="Staff PIN policy: required, optional, disabled", ) staff_pin_lockout_attempts = Column( Integer, default=5, nullable=False, comment="Max failed PIN attempts before lockout", ) staff_pin_lockout_minutes = Column( Integer, default=30, nullable=False, comment="Lockout duration in minutes", ) # ========================================================================= # Feature Toggles (Admin-controlled) # ========================================================================= allow_self_enrollment = Column( Boolean, default=True, nullable=False, comment="Allow customers to self-enroll via QR code", ) allow_void_transactions = Column( Boolean, default=True, nullable=False, comment="Allow voiding points for returns", ) allow_cross_location_redemption = Column( Boolean, default=True, nullable=False, comment="Allow redemption at any merchant location", ) # ========================================================================= # Audit Settings # ========================================================================= require_order_reference = Column( Boolean, default=False, nullable=False, comment="Require order reference when earning points", ) log_ip_addresses = Column( Boolean, default=True, nullable=False, comment="Log IP addresses for transactions", ) # ========================================================================= # Relationships # ========================================================================= merchant = relationship("Merchant", backref="loyalty_settings") # Indexes __table_args__ = ( Index("idx_merchant_loyalty_settings_merchant", "merchant_id"), ) def __repr__(self) -> str: return f"" @property def is_staff_pin_required(self) -> bool: """Check if staff PIN is required.""" return self.staff_pin_policy == StaffPinPolicy.REQUIRED @property def is_staff_pin_disabled(self) -> bool: """Check if staff PIN is disabled.""" return self.staff_pin_policy == StaffPinPolicy.DISABLED