# /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()