460 lines
14 KiB
Python
460 lines
14 KiB
Python
#!/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())
|