frontend költség beállítások

This commit is contained in:
Roo
2026-06-23 21:11:21 +00:00
parent 5b437b220d
commit 71ef33bb85
90 changed files with 11850 additions and 1053 deletions

View File

@@ -0,0 +1,245 @@
#!/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())