# /opt/docker/dev/service_finder/backend/app/services/asset_service.py from __future__ import annotations import logging import uuid from typing import List, Optional, Dict, Any, TYPE_CHECKING from datetime import datetime from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, func, and_ from sqlalchemy.orm import selectinload from app.models.asset import Asset, AssetAssignment, AssetTelemetry, AssetFinancials from app.models.identity import User from app.services.config_service import config from app.services.gamification_service import GamificationService from app.services.security_service import security_service if TYPE_CHECKING: from .identity import User, Person from .organization import Organization from .vehicle_definitions import VehicleModelDefinition logger = logging.getLogger(__name__) class AssetService: """ Asset Service 2.0 - A Járművek Életciklus-menedzsere. Kezeli a regisztrációt, a tulajdonosváltást és a flotta-korlátokat. """ @staticmethod async def create_or_claim_vehicle( db: AsyncSession, user_id: int, org_id: int, vin: str, license_plate: str, catalog_id: int = None ): """ Intelligens Jármű Rögzítés: Ha új: létrehozza. Ha már létezik: Transzfer folyamatot indít. """ try: vin_clean = vin.strip().upper() # 1. ADMIN LIMIT ELLENŐRZÉS user_stmt = select(User).where(User.id == user_id) user = (await db.execute(user_stmt)).scalar_one() limits = await config.get_setting(db, "VEHICLE_LIMIT", default={"free": 1, "premium": 5, "vip": 50}) user_role = user.role.value if hasattr(user.role, 'value') else str(user.role) allowed_limit = limits.get(user_role, 1) count_stmt = select(func.count(Asset.id)).where(Asset.current_organization_id == org_id) current_count = (await db.execute(count_stmt)).scalar() if current_count >= allowed_limit: raise ValueError(f"Limit túllépés! A csomagod {allowed_limit} autót engedélyez.") # 2. LÉTEZIK-E MÁR A JÁRMŰ? stmt = select(Asset).where(Asset.vin == vin_clean) existing_asset = (await db.execute(stmt)).scalar_one_or_none() if existing_asset: # HA MÁR A JELENLEGI SZERVEZETNÉL VAN if existing_asset.current_organization_id == org_id: raise ValueError("Ez a jármű már a te garázsodban van.") # TRANSZFER FOLYAMAT INDÍTÁSA return await AssetService.initiate_ownership_transfer( db, existing_asset, user_id, org_id, license_plate ) # 3. ÚJ JÁRMŰ LÉTREHOZÁSA (Standard Flow) new_asset = Asset( vin=vin_clean, license_plate=license_plate.strip().upper(), catalog_id=catalog_id, current_organization_id=org_id, status="active", is_verified=False ) db.add(new_asset) await db.flush() # Digitális Iker Alapmodulok db.add(AssetAssignment(asset_id=new_asset.id, organization_id=org_id, status="active")) db.add(AssetTelemetry(asset_id=new_asset.id)) db.add(AssetFinancials(asset_id=new_asset.id)) # Gamification reward = await config.get_setting(db, "xp_reward_asset_register", default=250) await GamificationService.award_points(db, user_id, int(reward), "NEW_ASSET_REG") await db.commit() return new_asset except Exception as e: await db.rollback() logger.error(f"Asset Creation Error: {e}") raise e @staticmethod async def initiate_ownership_transfer(db: AsyncSession, asset: Asset, user_id: int, org_id: int, new_plate: str): """ Adásvétel kezelése: Az autót 'Transfer Pending' állapotba teszi. """ # Admin paraméter: Automatikus transzfer engedélyezése? auto_transfer = await config.get_setting(db, "asset_auto_transfer_enabled", default=False) # Logoljuk a kísérletet a biztonsági szolgálatnál (Sentinel) await security_service.log_event( db, user_id=user_id, action="VEHICLE_CLAIM_INITIATED", severity="warning", target_type="Asset", target_id=str(asset.id), new_data={"vin": asset.vin, "new_org": org_id} ) if auto_transfer: # Csak akkor, ha a régi tulajdonos 'sold' állapotba tette if asset.status == "sold": return await AssetService.execute_final_transfer(db, asset, org_id, new_plate) # Függőben lévő állapot: Dokumentum feltöltésre vár asset.status = "transfer_pending" asset.temp_claim_org_id = org_id # Átmeneti tároló a validálásig await db.commit() # Itt egy speciális hibaüzenetet dobunk, amit a Frontend tud kezelni (Dokumentum feltöltő ablak) raise HTTPException( status_code=202, detail="A jármű már szerepel a rendszerben. Kérjük, töltsd fel az adásvételi szerződést a tulajdonjog igazolásához." ) @staticmethod async def execute_final_transfer(db: AsyncSession, asset: Asset, new_org_id: int, new_plate: str): """ A tulajdonjog tényleges átírása az adatbázisban. """ # 1. Régi hozzárendelés lezárása await db.execute( update(AssetAssignment) .where(and_(AssetAssignment.asset_id == asset.id, AssetAssignment.status == "active")) .values(status="archived", end_date=datetime.now()) ) # 2. Új hozzárendelés és adatok frissítése asset.current_organization_id = new_org_id asset.license_plate = new_plate.upper() asset.status = "active" asset.is_verified = False # Az új tulajdonos papírjait is ellenőrizni kell! db.add(AssetAssignment(asset_id=asset.id, organization_id=new_org_id, status="active")) await db.commit() return asset