244 lines
7.8 KiB
Python
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()
|