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.")