163 lines
5.8 KiB
Python
163 lines
5.8 KiB
Python
# /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.fleet_finance 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
|