Files
service-finder/backend/app/api/v1/endpoints/dictionaries.py
2026-06-23 21:11:21 +00:00

244 lines
7.8 KiB
Python

# /opt/docker/dev/service_finder/backend/app/api/v1/endpoints/dictionaries.py
"""
Szótárak és katalógusok végpontjai.
- GET /dictionaries/cost-categories: Költségkategóriák lekérése visibility szerint szűrve
- CRUD /dictionaries/insurance-providers: Biztosítók katalógusa
"""
import logging
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, desc
from app.db.session import get_db
from app.api import deps
from app.models.fleet_finance import CostCategory, InsuranceProvider
from app.schemas.fleet_finance import (
InsuranceProviderResponse,
InsuranceProviderCreate,
InsuranceProviderUpdate,
)
router = APIRouter()
logger = logging.getLogger(__name__)
@router.get("/cost-categories")
async def list_cost_categories(
visibility: Optional[str] = Query(None, description="Szűrés láthatóságra: b2c, b2b, both, internal"),
db: AsyncSession = Depends(get_db),
current_user = Depends(deps.get_current_user)
):
"""
Költségkategóriák lekérése hierarchikus struktúrában.
Opcionálisan szűrhető visibility alapján (pl. ha a frontend csak a 'b2c' és 'both' kategóriákat akarja).
"""
stmt = select(CostCategory).order_by(CostCategory.name)
if visibility:
# Ha 'b2c' van megadva, akkor a 'b2c' ÉS 'both' láthatóságú kategóriákat adjuk vissza
if visibility == "b2c":
stmt = stmt.where(CostCategory.visibility.in_(["b2c", "both"]))
elif visibility == "b2b":
stmt = stmt.where(CostCategory.visibility.in_(["b2b", "both"]))
else:
stmt = stmt.where(CostCategory.visibility == visibility)
result = await db.execute(stmt)
categories = result.scalars().all()
# Hierarchikus struktúra építése
category_map = {}
root_categories = []
for cat in categories:
cat_dict = {
"id": cat.id,
"code": cat.code,
"name": cat.name,
"description": cat.description,
"parent_id": cat.parent_id,
"visibility": cat.visibility,
"accounting_code": cat.accounting_code,
"is_system": cat.is_system,
"children": [],
}
category_map[cat.id] = cat_dict
for cat in categories:
cat_dict = category_map[cat.id]
if cat.parent_id and cat.parent_id in category_map:
category_map[cat.parent_id]["children"].append(cat_dict)
else:
root_categories.append(cat_dict)
return root_categories
# =============================================================================
# InsuranceProvider CRUD
# =============================================================================
@router.get(
"/insurance-providers",
response_model=List[InsuranceProviderResponse],
)
async def list_insurance_providers(
active_only: bool = Query(True, description="Csak az aktív biztosítók listázása"),
country_code: Optional[str] = Query(None, max_length=2, description="ISO 3166-1 alpha-2 országkód szerinti szűrés (pl. 'HU'). Visszaadja az adott ország és a globális (NULL) szolgáltatókat."),
db: AsyncSession = Depends(get_db),
current_user = Depends(deps.get_current_user),
):
"""
Biztosítók listázása.
Alapértelmezés szerint csak az aktív biztosítókat adja vissza (frontend dropdown számára).
Opcionálisan szűrhető country_code alapján: ha meg van adva, visszaadja az adott ország
szolgáltatóit ÉS a globális (country_code IS NULL) szolgáltatókat is.
"""
stmt = select(InsuranceProvider).order_by(InsuranceProvider.name)
if active_only:
stmt = stmt.where(InsuranceProvider.is_active == True)
if country_code:
stmt = stmt.where(
(InsuranceProvider.country_code == country_code) |
(InsuranceProvider.country_code.is_(None))
)
result = await db.execute(stmt)
providers = result.scalars().all()
return providers
@router.get(
"/insurance-providers/{provider_id}",
response_model=InsuranceProviderResponse,
)
async def get_insurance_provider(
provider_id: int,
db: AsyncSession = Depends(get_db),
current_user = Depends(deps.get_current_user),
):
"""
Egy biztosító adatainak lekérése ID alapján.
"""
stmt = select(InsuranceProvider).where(InsuranceProvider.id == provider_id)
result = await db.execute(stmt)
provider = result.scalar_one_or_none()
if not provider:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Biztosító nem található",
)
return provider
@router.post(
"/insurance-providers",
response_model=InsuranceProviderResponse,
status_code=status.HTTP_201_CREATED,
)
async def create_insurance_provider(
payload: InsuranceProviderCreate,
db: AsyncSession = Depends(get_db),
current_user = Depends(deps.get_current_user),
):
"""
Új biztosító hozzáadása a katalógushoz.
"""
# Check for duplicate name
existing_stmt = select(InsuranceProvider).where(
InsuranceProvider.name == payload.name
)
existing_result = await db.execute(existing_stmt)
if existing_result.scalar_one_or_none():
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"Már létezik biztosító ezzel a névvel: '{payload.name}'",
)
provider = InsuranceProvider(
name=payload.name,
claim_phone=payload.claim_phone,
claim_url=payload.claim_url,
services_offered=payload.services_offered or {},
is_active=payload.is_active,
)
db.add(provider)
await db.commit()
await db.refresh(provider)
return provider
@router.put(
"/insurance-providers/{provider_id}",
response_model=InsuranceProviderResponse,
)
async def update_insurance_provider(
provider_id: int,
payload: InsuranceProviderUpdate,
db: AsyncSession = Depends(get_db),
current_user = Depends(deps.get_current_user),
):
"""
Biztosító adatainak módosítása.
"""
stmt = select(InsuranceProvider).where(InsuranceProvider.id == provider_id)
result = await db.execute(stmt)
provider = result.scalar_one_or_none()
if not provider:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Biztosító nem található",
)
# Check for duplicate name if name is being changed
if payload.name is not None and payload.name != provider.name:
dup_stmt = select(InsuranceProvider).where(
InsuranceProvider.name == payload.name,
InsuranceProvider.id != provider_id,
)
dup_result = await db.execute(dup_stmt)
if dup_result.scalar_one_or_none():
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"Már létezik biztosító ezzel a névvel: '{payload.name}'",
)
update_data = payload.model_dump(exclude_unset=True)
for field, value in update_data.items():
setattr(provider, field, value)
await db.commit()
await db.refresh(provider)
return provider
@router.delete(
"/insurance-providers/{provider_id}",
status_code=status.HTTP_204_NO_CONTENT,
)
async def delete_insurance_provider(
provider_id: int,
db: AsyncSession = Depends(get_db),
current_user = Depends(deps.get_current_user),
):
"""
Biztosító törlése a katalógusból.
"""
stmt = select(InsuranceProvider).where(InsuranceProvider.id == provider_id)
result = await db.execute(stmt)
provider = result.scalar_one_or_none()
if not provider:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Biztosító nem található",
)
await db.delete(provider)
await db.commit()