- Split mother's name in KYC (last/first) - Added mileage_unit and fuel_type to Assets - Expanded AssetCost for international VAT and original currency - Fixed SQLAlchemy IndexError in asset catalog lookup - Added exchange_rate and ratings tables to models
136 lines
5.0 KiB
Python
136 lines
5.0 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select, func, and_
|
|
import os
|
|
import logging
|
|
|
|
from app.db.session import get_db
|
|
from app.api.deps import get_current_user
|
|
from app.schemas.asset import AssetCreate, AssetResponse
|
|
from app.models.asset import Asset, AssetCatalog, AssetAssignment, AssetEvent
|
|
from app.models.identity import User
|
|
from app.models.organization import Organization, OrganizationMember, OrgType
|
|
from app.core.config import settings
|
|
|
|
# VIN Validator - Standard 17 karakter, tiltott karakterek (I, O, Q) szűrése
|
|
class VINValidator:
|
|
@staticmethod
|
|
def validate(vin: str) -> bool:
|
|
vin = vin.upper()
|
|
if len(vin) != 17:
|
|
return False
|
|
if any(c in vin for c in "IOQ"):
|
|
return False
|
|
return True
|
|
|
|
router = APIRouter()
|
|
logger = logging.getLogger(__name__)
|
|
|
|
@router.post("/", response_model=AssetResponse, status_code=status.HTTP_201_CREATED)
|
|
async def create_asset(
|
|
asset_in: AssetCreate,
|
|
target_org_id: int = None,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
# 1. VIN Validáció
|
|
if not VINValidator.validate(asset_in.vin):
|
|
raise HTTPException(status_code=400, detail="Érvénytelen alvázszám (VIN) formátum!")
|
|
|
|
# 2. Célflotta ellenőrzése
|
|
if not target_org_id:
|
|
stmt_org = select(Organization).join(OrganizationMember).where(
|
|
and_(
|
|
OrganizationMember.user_id == current_user.id,
|
|
Organization.org_type == OrgType.individual
|
|
)
|
|
)
|
|
org = (await db.execute(stmt_org)).scalar_one_or_none()
|
|
if not org:
|
|
raise HTTPException(status_code=404, detail="Privát flotta nem található. KYC szükséges.")
|
|
final_org_id = org.id
|
|
else:
|
|
# Céges jogosultság ellenőrzése
|
|
stmt_mem = select(OrganizationMember).where(
|
|
and_(
|
|
OrganizationMember.organization_id == target_org_id,
|
|
OrganizationMember.user_id == current_user.id
|
|
)
|
|
)
|
|
member = (await db.execute(stmt_mem)).scalar_one_or_none()
|
|
if not member or (member.role != "owner" and not (member.permissions or {}).get("can_add_asset")):
|
|
raise HTTPException(status_code=403, detail="Nincs jogod ehhez a flottához!")
|
|
final_org_id = target_org_id
|
|
|
|
# 3. Katalógus ellenőrzése
|
|
stmt_cat = select(AssetCatalog).where(
|
|
and_(
|
|
AssetCatalog.make.ilike(asset_in.make), # Simán ilike, nem kell func() köré
|
|
AssetCatalog.model.ilike(asset_in.model)
|
|
)
|
|
)
|
|
catalog_item = (await db.execute(stmt_cat)).scalar_one_or_none()
|
|
|
|
if not catalog_item:
|
|
catalog_item = AssetCatalog(
|
|
make=asset_in.make,
|
|
model=asset_in.model,
|
|
vehicle_class=asset_in.vehicle_class,
|
|
fuel_type=asset_in.fuel_type
|
|
)
|
|
db.add(catalog_item)
|
|
await db.flush()
|
|
|
|
# 4. Asset létrehozása vagy betöltése (Shadow Identity)
|
|
stmt_exist = select(Asset).where(Asset.vin == asset_in.vin.upper())
|
|
new_asset = (await db.execute(stmt_exist)).scalar_one_or_none()
|
|
|
|
if not new_asset:
|
|
new_asset = Asset(
|
|
vin=asset_in.vin.upper(),
|
|
license_plate=asset_in.license_plate,
|
|
name=asset_in.name or f"{asset_in.make} {asset_in.model}",
|
|
year_of_manufacture=asset_in.year_of_manufacture,
|
|
fuel_type=asset_in.fuel_type, # JAVÍTVA: Most már átadjuk
|
|
vehicle_class=asset_in.vehicle_class, # JAVÍTVA: Most már átadjuk
|
|
mileage_unit=asset_in.reading_unit, # JAVÍTVA: Most már átadjuk
|
|
catalog_id=catalog_item.id,
|
|
quality_index=1.00,
|
|
system_mileage=0
|
|
)
|
|
db.add(new_asset)
|
|
await db.flush()
|
|
|
|
# 5. Assignment
|
|
new_assignment = AssetAssignment(
|
|
asset_id=new_asset.id,
|
|
organization_id=final_org_id,
|
|
status="active"
|
|
)
|
|
db.add(new_assignment)
|
|
|
|
# 6. Kezdő KM esemény
|
|
if asset_in.current_reading:
|
|
db.add(AssetEvent(
|
|
asset_id=new_asset.id,
|
|
event_type="initial_reading",
|
|
recorded_mileage=asset_in.current_reading,
|
|
description="Kezdeti óraállás rögzítése",
|
|
data={"source": "user_registration"}
|
|
))
|
|
|
|
try:
|
|
await db.commit()
|
|
await db.refresh(new_asset)
|
|
|
|
# 7. NAS mappa struktúra
|
|
nas_base = getattr(settings, "NAS_STORAGE_PATH", "/opt/docker/dev/service_finder/nas/assets")
|
|
asset_path = os.path.join(nas_base, str(new_asset.id))
|
|
os.makedirs(os.path.join(asset_path, "docs"), exist_ok=True)
|
|
os.makedirs(os.path.join(asset_path, "photos"), exist_ok=True)
|
|
|
|
return new_asset
|
|
except Exception as e:
|
|
await db.rollback()
|
|
logger.error(f"Asset Creation Error: {str(e)}")
|
|
raise HTTPException(status_code=500, detail="Hiba a mentés során.") |