# /opt/docker/dev/service_finder/backend/app/services/subscription_service.py """ Subscription & Entitlement Service — Premium költségkategória láthatóság. A szolgáltatás a felhasználó előfizetési szintje (tier) alapján szabályozza, hogy mely költségkategóriák (CostCategory) érhetők el. A tier-ek hierarchikusak: free < premium < enterprise. Hierarchia: - free: alap kategóriák (üzemanyag, szerviz, gumik, biztosítás, adók) - premium: free + útdíj/parkolás, ápolás, egyéb - enterprise: premium + minden (nincs korlátozás) """ from __future__ import annotations import logging from typing import Optional, List from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from app.models.vehicle.vehicle import CostCategory logger = logging.getLogger(__name__) # ── Subscription Feature Matrix ────────────────────────────────────────────── # Minden funkció/kategória minimális tier szintje. # A tier-ek: free < premium < enterprise TIER_HIERARCHY = { "free": 0, "premium": 1, "enterprise": 2, } SUBSCRIPTION_FEATURES: dict[str, str] = { # Feature -> minimum required tier "cost_category:fuel": "free", "cost_category:service": "free", "cost_category:tires": "free", "cost_category:insurance": "free", "cost_category:taxes": "free", "cost_category:toll_parking": "premium", "cost_category:cleaning": "premium", "cost_category:other": "premium", "cost_category:all": "enterprise", # Feature flags "export_csv": "free", "analytics_tco": "premium", "analytics_detailed": "enterprise", "api_access": "free", "multi_vehicle": "free", "unlimited_vehicles": "premium", } class SubscriptionService: """ Entitlement Mátrix — előfizetési szintek kezelése. Metódusok: get_user_tier(db, user_id) -> str can_access_feature(tier, feature_key) -> bool get_visible_categories(db, tier) -> list[CostCategory] require_tier(tier, required_feature_or_min_tier) -> bool """ @staticmethod def _tier_to_int(tier: str) -> int: """Tier string konvertálása numerikus értékre a hierarchiában.""" return TIER_HIERARCHY.get(tier.lower(), 0) @staticmethod async def get_user_tier(db: AsyncSession, user_id: int) -> str: """ Visszaadja a felhasználó aktuális előfizetési szintjét. Jelenlegi implementáció: minden user 'free' tier-t kap. A jövőben ez a subscription/org táblákból lesz lekérdezve. """ # TODO: Éles implementáció — subscription tábla lekérdezése # TODO: Szervezeti tier felülírás (pl. enterprise org) return "free" @staticmethod def can_access_feature(user_tier: str, feature_key: str) -> bool: """ Ellenőrzi, hogy a felhasználó hozzáfér-e egy adott funkcióhoz. Args: user_tier: A felhasználó tier szintje (free/premium/enterprise) feature_key: A funkció kulcsa (pl. 'cost_category:fuel', 'analytics_tco') Returns: bool: True ha hozzáfér, False ha nem """ required_tier = SUBSCRIPTION_FEATURES.get(feature_key) if required_tier is None: # Ismeretlen feature — alapértelmezetten tiltva logger.warning(f"Unknown feature key: {feature_key}") return False user_level = SubscriptionService._tier_to_int(user_tier) required_level = SubscriptionService._tier_to_int(required_tier) return user_level >= required_level @staticmethod async def get_visible_categories( db: AsyncSession, user_tier: str ) -> List[CostCategory]: """ Visszaadja azokat a CostCategory-ket, amelyek a user tier-jének megfelelnek. Szűrési logika: - A kategória min_tier mezője alapján szűrünk. - Ha a kategória min_tier értéke kisebb vagy egyenlő, mint a user tier, akkor látható. - Ha a min_tier nincs beállítva (None), akkor 'free'-ként kezeljük. Args: db: Adatbázis session user_tier: A felhasználó tier szintje Returns: List[CostCategory]: A látható kategóriák listája """ user_level = SubscriptionService._tier_to_int(user_tier) # Lekérjük az összes kategóriát stmt = select(CostCategory).order_by(CostCategory.id) result = await db.execute(stmt) all_categories = result.scalars().all() # Szűrés a min_tier alapján visible = [] for cat in all_categories: cat_tier = getattr(cat, "min_tier", "free") or "free" cat_level = SubscriptionService._tier_to_int(cat_tier) if user_level >= cat_level: visible.append(cat) return visible @staticmethod def require_tier(user_tier: str, required_feature_or_min_tier: str) -> bool: """ Egyszerű tier ellenőrzés — dependency check-hez. Args: user_tier: A felhasználó tier szintje required_feature_or_min_tier: Vagy egy feature kulcs, vagy egy tier név Returns: bool: True ha megfelel, False ha nem """ # Először feature kulcsként próbáljuk if required_feature_or_min_tier in SUBSCRIPTION_FEATURES: return SubscriptionService.can_access_feature( user_tier, required_feature_or_min_tier ) # Ha nem feature kulcs, akkor tier névként kezeljük user_level = SubscriptionService._tier_to_int(user_tier) required_level = SubscriptionService._tier_to_int(required_feature_or_min_tier) return user_level >= required_level