#!/usr/bin/env python3 """ Seed szkript a jövőbiztos SaaS csomag-architektúra 1. fázisához. Létrehozza a 6 alap subscription tier-t a system.subscription_tiers táblában. Használat: docker compose exec sf_api python3 /app/backend/app/scripts/seed_packages.py Architektúra: A csomagok rules JSONB oszlopa egy szabványos struktúrát követ: { "type": "private" | "corporate", "pricing_zones": { "HU": {"monthly_price": 3000, "yearly_price": 32500, "currency": "HUF"}, "DEFAULT": {"monthly_price": 12.99, "yearly_price": 129.99, "currency": "EUR"} }, "allowances": {"max_vehicles": int, "max_garages": int, "monthly_free_credits": int}, "entitlements": ["SRV_..."], "affiliate": {"commission_rate_percent": int, "referral_bonus_credits": int} } """ import asyncio import json import logging from sqlalchemy import select, delete from app.database import AsyncSessionLocal from app.models.core_logic import SubscriptionTier logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", ) logger = logging.getLogger("Seed-Packages") # --------------------------------------------------------------------------- # Pricing Zones helper # --------------------------------------------------------------------------- def pricing_zones( eur_monthly: float, eur_yearly: float, eur_credit: int = 0, huf_monthly: int = 0, huf_yearly: int = 0, huf_credit: int = 0, usd_monthly: float = 0, usd_yearly: float = 0, usd_credit: int = 0, ) -> dict: """Create a pricing_zones dict with DEFAULT (EUR), HU (HUF), and optionally USD zones.""" zones = { "DEFAULT": { "monthly_price": eur_monthly, "yearly_price": eur_yearly, "currency": "EUR", "credit_price": eur_credit if eur_credit > 0 else None, }, } if huf_monthly > 0: zones["HU"] = { "monthly_price": huf_monthly, "yearly_price": huf_yearly, "currency": "HUF", "credit_price": huf_credit if huf_credit > 0 else None, } if usd_monthly > 0: zones["US"] = { "monthly_price": usd_monthly, "yearly_price": usd_yearly, "currency": "USD", "credit_price": usd_credit if usd_credit > 0 else None, } return zones # --------------------------------------------------------------------------- # Csomagdefiníciók # --------------------------------------------------------------------------- PACKAGES = [ { "name": "private_free_v1", "rules": { "type": "private", "display_name": "Privát Ingyenes", "pricing": { "monthly_price": 0.0, "yearly_price": 0.0, "currency": "EUR", "credit_price": 0, }, "pricing_zones": pricing_zones( eur_monthly=0.0, eur_yearly=0.0, huf_monthly=0, huf_yearly=0, ), "allowances": { "max_vehicles": 1, "max_garages": 1, "monthly_free_credits": 0, }, "entitlements": [], "affiliate": { "commission_rate_percent": 0, "referral_bonus_credits": 0, }, "lifecycle": { "is_public": True, }, "duration": { "days": 30, "allow_stacking": True, }, "ad_policy": { "show_ads": True, "ad_free_grace_days": 0, "max_daily_impressions": 50, }, "marketing": { "subtitle": "Alap szintű járműkövetés kezdőknek.", "badge": "Ingyenes", }, }, "is_custom": False, }, { "name": "private_pro_v1", "rules": { "type": "private", "display_name": "Privát Pro", "pricing": { "monthly_price": 4.99, "yearly_price": 49.99, "currency": "EUR", "credit_price": 500, }, "pricing_zones": pricing_zones( eur_monthly=4.99, eur_yearly=49.99, eur_credit=500, huf_monthly=1890, huf_yearly=18990, huf_credit=500, usd_monthly=5.99, usd_yearly=59.99, usd_credit=500, ), "allowances": { "max_vehicles": 3, "max_garages": 1, "monthly_free_credits": 50, }, "entitlements": ["SRV_DATA_EXPORT"], "affiliate": { "commission_rate_percent": 10, "referral_bonus_credits": 25, }, "lifecycle": { "is_public": True, }, "duration": { "days": 30, "allow_stacking": True, }, "ad_policy": { "show_ads": True, "ad_free_grace_days": 3, "max_daily_impressions": 100, }, "marketing": { "subtitle": "Kisebb flotta esetén ideális választás.", "badge": "Pro", "highlight_color": "#70BC84", }, }, "is_custom": False, }, { "name": "private_vip_v1", "rules": { "type": "private", "display_name": "Privát VIP", "pricing": { "monthly_price": 9.99, "yearly_price": 99.99, "currency": "EUR", "credit_price": 1200, }, "pricing_zones": pricing_zones( eur_monthly=9.99, eur_yearly=99.99, eur_credit=1200, huf_monthly=3790, huf_yearly=37990, huf_credit=1200, usd_monthly=11.99, usd_yearly=119.99, usd_credit=1200, ), "allowances": { "max_vehicles": 10, "max_garages": 1, "monthly_free_credits": 150, }, "entitlements": ["SRV_DATA_EXPORT", "SRV_AI_UPLOAD"], "affiliate": { "commission_rate_percent": 15, "referral_bonus_credits": 50, }, "lifecycle": { "is_public": True, }, "duration": { "days": 30, "allow_stacking": True, }, "ad_policy": { "show_ads": False, "ad_free_grace_days": 0, "max_daily_impressions": None, }, "marketing": { "subtitle": "VIP profi garázs kisvállalkozók számára.", "badge": "VIP", "highlight_color": "#F59E0B", }, }, "is_custom": False, }, { "name": "corp_premium_v1", "rules": { "type": "corporate", "display_name": "Céges Prémium", "pricing": { "monthly_price": 29.99, "yearly_price": 299.99, "currency": "EUR", "credit_price": 3000, }, "pricing_zones": pricing_zones( eur_monthly=29.99, eur_yearly=299.99, eur_credit=3000, huf_monthly=11990, huf_yearly=119990, huf_credit=3000, usd_monthly=34.99, usd_yearly=349.99, usd_credit=3000, ), "allowances": { "max_vehicles": 20, "max_garages": 3, "monthly_free_credits": 300, }, "entitlements": ["SRV_DATA_EXPORT", "SRV_AI_UPLOAD"], "affiliate": { "commission_rate_percent": 15, "referral_bonus_credits": 100, }, "lifecycle": { "is_public": True, }, "duration": { "days": 30, "allow_stacking": True, }, "ad_policy": { "show_ads": False, "ad_free_grace_days": 0, "max_daily_impressions": None, }, "marketing": { "subtitle": "Közepes flották számára tervezve.", "badge": "Prémium", "highlight_color": "#3B82F6", }, }, "is_custom": False, }, { "name": "corp_premium_plus_v1", "rules": { "type": "corporate", "display_name": "Céges Prémium Plus", "pricing": { "monthly_price": 59.99, "yearly_price": 599.99, "currency": "EUR", "credit_price": 6000, }, "pricing_zones": pricing_zones( eur_monthly=59.99, eur_yearly=599.99, eur_credit=6000, huf_monthly=23990, huf_yearly=239990, huf_credit=6000, usd_monthly=69.99, usd_yearly=699.99, usd_credit=6000, ), "allowances": { "max_vehicles": 50, "max_garages": 10, "monthly_free_credits": 800, }, "entitlements": [ "SRV_DATA_EXPORT", "SRV_AI_UPLOAD", "SRV_ACCOUNTING_SYNC", ], "affiliate": { "commission_rate_percent": 20, "referral_bonus_credits": 250, }, "lifecycle": { "is_public": True, }, "duration": { "days": 30, "allow_stacking": True, }, "ad_policy": { "show_ads": False, "ad_free_grace_days": 0, "max_daily_impressions": None, }, "marketing": { "subtitle": "Nagy flották prémium menedzsmentje.", "badge": "Plus", "highlight_color": "#8B5CF6", }, }, "is_custom": False, }, { "name": "corp_vip_v1", "rules": { "type": "corporate", "display_name": "Céges VIP", "pricing": { "monthly_price": 149.99, "yearly_price": 1499.99, "currency": "EUR", "credit_price": 15000, }, "pricing_zones": pricing_zones( eur_monthly=149.99, eur_yearly=1499.99, eur_credit=15000, huf_monthly=59990, huf_yearly=599990, huf_credit=15000, usd_monthly=169.99, usd_yearly=1699.99, usd_credit=15000, ), "allowances": { "max_vehicles": 200, "max_garages": 50, "monthly_free_credits": 2500, }, "entitlements": [ "SRV_DATA_EXPORT", "SRV_AI_UPLOAD", "SRV_ACCOUNTING_SYNC", "SRV_API_ACCESS", ], "affiliate": { "commission_rate_percent": 25, "referral_bonus_credits": 500, }, "lifecycle": { "is_public": True, }, "duration": { "days": 30, "allow_stacking": True, }, "ad_policy": { "show_ads": False, "ad_free_grace_days": 0, "max_daily_impressions": None, }, "marketing": { "subtitle": "Vállalati szintű flottairányítás.", "badge": "VIP", "highlight_color": "#EF4444", }, }, "is_custom": False, }, ] async def seed(): logger.info("=" * 60) logger.info("Seed Packages - Advanced Subscription Tiers (Phase 1)") logger.info("=" * 60) async with AsyncSessionLocal() as db: # 1. UPSERT: Meglévő csomagok frissítése vagy új beszúrása from sqlalchemy.dialects.postgresql import insert as pg_insert upserted_count = 0 for pkg in PACKAGES: stmt = pg_insert(SubscriptionTier).values( name=pkg["name"], rules=pkg["rules"], is_custom=pkg["is_custom"], ) stmt = stmt.on_conflict_do_update( index_elements=[SubscriptionTier.name], set_={ "rules": stmt.excluded.rules, "is_custom": stmt.excluded.is_custom, } ) await db.execute(stmt) upserted_count += 1 logger.info(f" ➕ Upsert: {pkg['name']}") await db.commit() logger.info(f"✅ {upserted_count} csomag sikeresen upsertelve.") # 2. Visszaigazolás logger.info("-" * 60) logger.info("Visszaigazolás - system.subscription_tiers tartalma:") logger.info("-" * 60) result = await db.execute( select(SubscriptionTier).order_by(SubscriptionTier.id) ) tiers = result.scalars().all() for tier in tiers: rules_json = json.dumps(tier.rules, indent=2, ensure_ascii=False) logger.info(f"\n ID: {tier.id}") logger.info(f" Name: {tier.name}") logger.info(f" Is Custom: {tier.is_custom}") logger.info(f" Rules:\n{rules_json}") logger.info(" " + "-" * 40) logger.info(f"\n✅ Összesen {len(tiers)} aktív csomag a rendszerben.") # 3. JSONB példa kiírása if tiers: sample = tiers[0] logger.info("=" * 60) logger.info("JSONB PÉLDA (private_free_v1):") logger.info("=" * 60) logger.info(json.dumps(sample.rules, indent=2, ensure_ascii=False)) if __name__ == "__main__": asyncio.run(seed())