# app/modules/loyalty/services/loyalty_features.py """ Loyalty feature provider for the billing feature system. Declares loyalty-related billable features (stamps, points, cards, wallets, etc.) for feature gating. """ from __future__ import annotations import logging from typing import TYPE_CHECKING from app.modules.contracts.features import ( FeatureDeclaration, FeatureScope, FeatureType, FeatureUsage, ) if TYPE_CHECKING: from sqlalchemy.orm import Session logger = logging.getLogger(__name__) class LoyaltyFeatureProvider: """Feature provider for the loyalty module. Declares: - loyalty_stamps: stamp-based loyalty programs - loyalty_points: points-based loyalty programs - loyalty_hybrid: combined stamps and points - loyalty_cards: customer card management - loyalty_enrollment: customer enrollment - loyalty_staff_pins: staff PIN management - loyalty_anti_fraud: cooldown and daily limits - loyalty_google_wallet: Google Wallet passes - loyalty_apple_wallet: Apple Wallet passes - loyalty_stats: dashboard statistics - loyalty_reports: transaction reports """ @property def feature_category(self) -> str: return "loyalty" def get_feature_declarations(self) -> list[FeatureDeclaration]: return [ FeatureDeclaration( code="loyalty_stamps", name_key="loyalty.features.loyalty_stamps.name", description_key="loyalty.features.loyalty_stamps.description", category="loyalty", feature_type=FeatureType.BINARY, scope=FeatureScope.MERCHANT, ui_icon="stamp", display_order=10, ), FeatureDeclaration( code="loyalty_points", name_key="loyalty.features.loyalty_points.name", description_key="loyalty.features.loyalty_points.description", category="loyalty", feature_type=FeatureType.BINARY, scope=FeatureScope.MERCHANT, ui_icon="coins", display_order=20, ), FeatureDeclaration( code="loyalty_hybrid", name_key="loyalty.features.loyalty_hybrid.name", description_key="loyalty.features.loyalty_hybrid.description", category="loyalty", feature_type=FeatureType.BINARY, scope=FeatureScope.MERCHANT, ui_icon="layers", display_order=30, ), FeatureDeclaration( code="loyalty_cards", name_key="loyalty.features.loyalty_cards.name", description_key="loyalty.features.loyalty_cards.description", category="loyalty", feature_type=FeatureType.BINARY, scope=FeatureScope.MERCHANT, ui_icon="credit-card", display_order=40, ), FeatureDeclaration( code="loyalty_enrollment", name_key="loyalty.features.loyalty_enrollment.name", description_key="loyalty.features.loyalty_enrollment.description", category="loyalty", feature_type=FeatureType.BINARY, scope=FeatureScope.MERCHANT, ui_icon="user-plus", display_order=50, ), FeatureDeclaration( code="loyalty_staff_pins", name_key="loyalty.features.loyalty_staff_pins.name", description_key="loyalty.features.loyalty_staff_pins.description", category="loyalty", feature_type=FeatureType.BINARY, scope=FeatureScope.MERCHANT, ui_icon="shield", display_order=60, ), FeatureDeclaration( code="loyalty_anti_fraud", name_key="loyalty.features.loyalty_anti_fraud.name", description_key="loyalty.features.loyalty_anti_fraud.description", category="loyalty", feature_type=FeatureType.BINARY, scope=FeatureScope.MERCHANT, ui_icon="lock", display_order=70, ), FeatureDeclaration( code="loyalty_google_wallet", name_key="loyalty.features.loyalty_google_wallet.name", description_key="loyalty.features.loyalty_google_wallet.description", category="loyalty", feature_type=FeatureType.BINARY, scope=FeatureScope.MERCHANT, ui_icon="smartphone", display_order=80, ), FeatureDeclaration( code="loyalty_apple_wallet", name_key="loyalty.features.loyalty_apple_wallet.name", description_key="loyalty.features.loyalty_apple_wallet.description", category="loyalty", feature_type=FeatureType.BINARY, scope=FeatureScope.MERCHANT, ui_icon="smartphone", display_order=85, ), FeatureDeclaration( code="loyalty_stats", name_key="loyalty.features.loyalty_stats.name", description_key="loyalty.features.loyalty_stats.description", category="loyalty", feature_type=FeatureType.BINARY, scope=FeatureScope.MERCHANT, ui_icon="bar-chart", display_order=90, ), FeatureDeclaration( code="loyalty_reports", name_key="loyalty.features.loyalty_reports.name", description_key="loyalty.features.loyalty_reports.description", category="loyalty", feature_type=FeatureType.BINARY, scope=FeatureScope.MERCHANT, ui_icon="file-text", display_order=100, ), ] def get_store_usage( self, db: Session, store_id: int, ) -> list[FeatureUsage]: if db is None: return [] from sqlalchemy import func from app.modules.loyalty.models import LoyaltyCard, StaffPin active_cards = ( db.query(func.count(LoyaltyCard.id)) .filter( LoyaltyCard.enrolled_at_store_id == store_id, LoyaltyCard.is_active == True, ) .scalar() or 0 ) active_pins = ( db.query(func.count(StaffPin.id)) .filter( StaffPin.store_id == store_id, StaffPin.is_active == True, ) .scalar() or 0 ) return [ FeatureUsage(feature_code="loyalty_cards", current_usage=active_cards), FeatureUsage(feature_code="loyalty_staff_pins", current_usage=active_pins), ] def get_merchant_usage( self, db: Session, merchant_id: int, platform_id: int, ) -> list[FeatureUsage]: if db is None: return [] from sqlalchemy import func from app.modules.loyalty.models import ( AppleDeviceRegistration, LoyaltyCard, StaffPin, ) active_cards = ( db.query(func.count(LoyaltyCard.id)) .filter( LoyaltyCard.merchant_id == merchant_id, LoyaltyCard.is_active == True, ) .scalar() or 0 ) active_pins = ( db.query(func.count(StaffPin.id)) .filter( StaffPin.merchant_id == merchant_id, StaffPin.is_active == True, ) .scalar() or 0 ) apple_registrations = ( db.query(func.count(AppleDeviceRegistration.id)) .join(LoyaltyCard) .filter(LoyaltyCard.merchant_id == merchant_id) .scalar() or 0 ) google_wallets = ( db.query(func.count(LoyaltyCard.id)) .filter( LoyaltyCard.merchant_id == merchant_id, LoyaltyCard.google_object_id.isnot(None), ) .scalar() or 0 ) return [ FeatureUsage(feature_code="loyalty_cards", current_usage=active_cards), FeatureUsage(feature_code="loyalty_staff_pins", current_usage=active_pins), FeatureUsage(feature_code="loyalty_apple_wallet", current_usage=apple_registrations), FeatureUsage(feature_code="loyalty_google_wallet", current_usage=google_wallets), ] # Singleton instance for module registration loyalty_feature_provider = LoyaltyFeatureProvider() __all__ = [ "LoyaltyFeatureProvider", "loyalty_feature_provider", ]