246 lines
10 KiB
Python
246 lines
10 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Seed script: fleet_finance dictionary tables (CostCategory & InsuranceProvider).
|
|
|
|
Idempotent seeder that populates:
|
|
1. fleet_finance.cost_categories - Hierarchical cost categories (5 parents + children)
|
|
2. fleet_finance.insurance_providers - Base insurance providers (HU/EU)
|
|
|
|
Usage:
|
|
docker compose exec sf_api python -m backend.scripts.seed_finance_dictionaries
|
|
"""
|
|
import asyncio
|
|
import logging
|
|
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
|
from sqlalchemy.orm import sessionmaker
|
|
from sqlalchemy import select, text
|
|
|
|
from app.core.config import settings
|
|
from app.models.fleet_finance.models import CostCategory, InsuranceProvider
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# =============================================================================
|
|
# Cost Categories - Hierarchical structure
|
|
# =============================================================================
|
|
# Format: (code, name, description, children)
|
|
# Children format: (code, name, description)
|
|
|
|
COST_CATEGORY_TREE = [
|
|
{
|
|
"code": "FINANCING",
|
|
"name": "Finanszírozás",
|
|
"description": "Jármű finanszírozásával kapcsolatos költségek",
|
|
"children": [
|
|
("FINANCING_LEASE", "Lízingdíj", "Lízingdíj fizetés"),
|
|
("FINANCING_LOAN", "Hiteltörlesztő", "Hiteltörlesztő részlet"),
|
|
],
|
|
},
|
|
{
|
|
"code": "INSURANCE",
|
|
"name": "Biztosítás",
|
|
"description": "Biztosítási díjak és kötvények",
|
|
"children": [
|
|
("INSURANCE_KGFB", "KGFB", "Kötelező gépjármű-felelősségbiztosítás"),
|
|
("INSURANCE_CASCO", "Casco", "Casco biztosítás"),
|
|
("INSURANCE_ASSISTANCE", "Assistance", "Assistance szolgáltatás"),
|
|
],
|
|
},
|
|
{
|
|
"code": "ADMIN_FEES",
|
|
"name": "Hatósági díjak",
|
|
"description": "Hatósági és adminisztratív költségek",
|
|
"children": [
|
|
("ADMIN_FEES_VEHICLE_TAX", "Gépjárműadó", "Gépjárműadó fizetés"),
|
|
("ADMIN_FEES_MOT", "Műszaki vizsga", "Műszaki vizsgadíj"),
|
|
("ADMIN_FEES_DOCS", "Okmányok", "Okmányokkal kapcsolatos díjak"),
|
|
],
|
|
},
|
|
{
|
|
"code": "OPERATION",
|
|
"name": "Üzemeltetés",
|
|
"description": "Jármű üzemeltetésével kapcsolatos költségek",
|
|
"children": [
|
|
("OPERATION_FUEL", "Üzemanyag", "Üzemanyag költség"),
|
|
("OPERATION_ELECTRIC", "Elektromos töltés", "Elektromos töltési költség"),
|
|
("OPERATION_TOLL", "Autópálya", "Autópálya használati díj"),
|
|
("OPERATION_PARKING", "Parkolás", "Parkolási díj"),
|
|
("OPERATION_WASH", "Mosás", "Járműmosás és ápolás"),
|
|
],
|
|
},
|
|
{
|
|
"code": "SERVICE",
|
|
"name": "Szerviz",
|
|
"description": "Szerviz és karbantartási költségek",
|
|
"children": [
|
|
("SERVICE_SCHEDULED", "Kötelező szerviz", "Kötelező időszakos szerviz"),
|
|
("SERVICE_REPAIR", "Eseti javítás", "Eseti javítási költség"),
|
|
("SERVICE_TIRES", "Gumiabroncs", "Gumiabroncs csere és tárolás"),
|
|
],
|
|
},
|
|
]
|
|
|
|
# =============================================================================
|
|
# Insurance Providers
|
|
# =============================================================================
|
|
|
|
INSURANCE_PROVIDERS = [
|
|
{"name": "Allianz Hungária Zrt.", "claim_phone": "+36-1-301-7100", "claim_url": "https://www.allianz.hu/karterites"},
|
|
{"name": "Generali Biztosító Zrt.", "claim_phone": "+36-1-452-3200", "claim_url": "https://www.generali.hu/karterites"},
|
|
{"name": "K&H Biztosító Zrt.", "claim_phone": "+36-1-335-3300", "claim_url": "https://www.kh.hu/biztositas/karterites"},
|
|
{"name": "Groupama Biztosító Zrt.", "claim_phone": "+36-1-477-4800", "claim_url": "https://www.groupama.hu/karterites"},
|
|
{"name": "Uniqa Biztosító Zrt.", "claim_phone": "+36-1-301-7300", "claim_url": "https://www.uniqa.hu/karterites"},
|
|
{"name": "Aegon Magyarország / Alfa Vienna Insurance Group", "claim_phone": "+36-1-477-4700", "claim_url": "https://www.alfabiztosito.hu/karterites"},
|
|
{"name": "Signal Biztosító Zrt.", "claim_phone": "+36-1-457-4400", "claim_url": "https://www.signal.hu/karterites"},
|
|
{"name": "Wáberer Hungária Biztosító Zrt.", "claim_phone": "+36-1-237-7000", "claim_url": "https://www.whb.hu/karterites"},
|
|
{"name": "Posta Biztosító Zrt.", "claim_phone": "+36-1-477-4900", "claim_url": "https://www.postabiztosito.hu/karterites"},
|
|
{"name": "Magyar Posta Életbiztosító Zrt.", "claim_phone": "+36-1-477-5000", "claim_url": "https://www.mpelb.hu/karterites"},
|
|
]
|
|
|
|
|
|
async def seed_cost_categories(db: AsyncSession) -> int:
|
|
"""Seed cost_categories table with hierarchical data. Returns count of inserted parents."""
|
|
inserted_count = 0
|
|
|
|
for parent_def in COST_CATEGORY_TREE:
|
|
# Check if parent already exists by code
|
|
existing_parent = await db.execute(
|
|
select(CostCategory).where(CostCategory.code == parent_def["code"])
|
|
)
|
|
parent = existing_parent.scalar_one_or_none()
|
|
|
|
if not parent:
|
|
parent = CostCategory(
|
|
code=parent_def["code"],
|
|
name=parent_def["name"],
|
|
description=parent_def["description"],
|
|
is_system=True,
|
|
visibility="both",
|
|
min_tier="free",
|
|
parent_id=None,
|
|
)
|
|
db.add(parent)
|
|
await db.flush() # Get the parent ID
|
|
inserted_count += 1
|
|
logger.info(f" ✅ PARENT [{parent.code:20s}] {parent.name}")
|
|
|
|
# Process children
|
|
for child_code, child_name, child_desc in parent_def["children"]:
|
|
existing_child = await db.execute(
|
|
select(CostCategory).where(CostCategory.code == child_code)
|
|
)
|
|
child = existing_child.scalar_one_or_none()
|
|
|
|
if not child:
|
|
child = CostCategory(
|
|
code=child_code,
|
|
name=child_name,
|
|
description=child_desc,
|
|
is_system=True,
|
|
visibility="both",
|
|
min_tier="free",
|
|
parent_id=parent.id,
|
|
)
|
|
db.add(child)
|
|
await db.flush()
|
|
logger.info(f" └─ CHILD [{child.code:20s}] {child.name}")
|
|
else:
|
|
logger.info(f" └─ EXISTS [{child.code:20s}] {child.name}")
|
|
|
|
return inserted_count
|
|
|
|
|
|
async def seed_insurance_providers(db: AsyncSession) -> int:
|
|
"""Seed insurance_providers table. Returns count of inserted providers."""
|
|
inserted_count = 0
|
|
|
|
for prov_def in INSURANCE_PROVIDERS:
|
|
existing = await db.execute(
|
|
select(InsuranceProvider).where(InsuranceProvider.name == prov_def["name"])
|
|
)
|
|
provider = existing.scalar_one_or_none()
|
|
|
|
if not provider:
|
|
provider = InsuranceProvider(
|
|
name=prov_def["name"],
|
|
country_code="HU",
|
|
claim_phone=prov_def["claim_phone"],
|
|
claim_url=prov_def["claim_url"],
|
|
services_offered={"kgfb": True, "casco": True},
|
|
is_active=True,
|
|
)
|
|
db.add(provider)
|
|
inserted_count += 1
|
|
logger.info(f" ✅ {provider.name} (HU)")
|
|
else:
|
|
# Update existing provider's country_code if not set
|
|
if not provider.country_code:
|
|
provider.country_code = "HU"
|
|
logger.info(f" UPDATED {provider.name} → country_code='HU'")
|
|
else:
|
|
logger.info(f" EXISTS {provider.name} (country_code='{provider.country_code}')")
|
|
|
|
return inserted_count
|
|
|
|
|
|
async def main():
|
|
"""Main seeder entry point."""
|
|
engine = create_async_engine(str(settings.SQLALCHEMY_DATABASE_URI))
|
|
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
|
|
|
async with async_session() as db:
|
|
try:
|
|
logger.info("=" * 60)
|
|
logger.info("📦 fleet_finance dictionary seeder")
|
|
logger.info("=" * 60)
|
|
|
|
# --- Cost Categories ---
|
|
logger.info("\n📁 Cost Categories (hierarchical):")
|
|
cat_count = await seed_cost_categories(db)
|
|
await db.commit()
|
|
logger.info(f" → {cat_count} new parent categories inserted")
|
|
|
|
# --- Insurance Providers ---
|
|
logger.info("\n🏢 Insurance Providers:")
|
|
prov_count = await seed_insurance_providers(db)
|
|
await db.commit()
|
|
logger.info(f" → {prov_count} new providers inserted")
|
|
|
|
# --- Summary ---
|
|
total_cats = await db.execute(
|
|
select(CostCategory).where(CostCategory.parent_id.is_(None))
|
|
)
|
|
parents = total_cats.scalars().all()
|
|
logger.info("\n" + "=" * 60)
|
|
logger.info("📊 SEED SUMMARY")
|
|
logger.info("=" * 60)
|
|
logger.info(f" CostCategory parents: {len(parents)}")
|
|
for p in parents:
|
|
child_count = await db.execute(
|
|
select(CostCategory).where(CostCategory.parent_id == p.id)
|
|
)
|
|
children = child_count.scalars().all()
|
|
logger.info(f" ├─ {p.code:20s} ({p.name}) → {len(children)} children")
|
|
|
|
total_providers = await db.execute(
|
|
select(InsuranceProvider).where(InsuranceProvider.is_active.is_(True))
|
|
)
|
|
providers = total_providers.scalars().all()
|
|
logger.info(f" InsuranceProviders (active): {len(providers)}")
|
|
for p in providers:
|
|
logger.info(f" ├─ {p.name}")
|
|
|
|
logger.info("\n✅ Seeding completed successfully!")
|
|
|
|
except Exception as e:
|
|
await db.rollback()
|
|
logger.error(f"❌ Seeding failed: {e}")
|
|
raise
|
|
finally:
|
|
await engine.dispose()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|