frontend kínlódás
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/schemas/asset.py
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic import BaseModel, ConfigDict, Field, validator
|
||||
from typing import Optional, Dict, Any, List
|
||||
from uuid import UUID
|
||||
from datetime import datetime
|
||||
@@ -30,39 +30,143 @@ class AssetCatalogResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class AssetResponse(BaseModel):
|
||||
""" A konkrét járműpéldány (Asset) teljes válaszmodellje. """
|
||||
""" A konkrét járműpéldány (Asset) teljes válaszmodellje - Thick Digital Twin. """
|
||||
# === IDENTIFICATION ===
|
||||
id: UUID
|
||||
vin: Optional[str] = Field(None, min_length=1, max_length=50)
|
||||
license_plate: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
year_of_manufacture: Optional[int] = None
|
||||
catalog_id: Optional[int] = None
|
||||
|
||||
# Státusz és ellenőrzés
|
||||
# === CLASSIFICATION ===
|
||||
vehicle_class: Optional[str] = None
|
||||
brand: Optional[str] = None
|
||||
model: Optional[str] = None
|
||||
trim_level: Optional[str] = None
|
||||
|
||||
# === TECHNICAL SPECS ===
|
||||
fuel_type: Optional[str] = None
|
||||
engine_capacity: Optional[int] = None
|
||||
power_kw: Optional[int] = None
|
||||
torque_nm: Optional[int] = None
|
||||
cylinder_layout: Optional[str] = None
|
||||
transmission_type: Optional[str] = None
|
||||
drive_type: Optional[str] = None
|
||||
euro_classification: Optional[str] = None
|
||||
|
||||
# === PHYSICAL DIMENSIONS ===
|
||||
curb_weight: Optional[int] = None
|
||||
max_weight: Optional[int] = None
|
||||
cargo_volume_x: Optional[float] = None
|
||||
cargo_volume_y: Optional[float] = None
|
||||
door_count: Optional[int] = None
|
||||
seat_count: Optional[int] = None
|
||||
|
||||
# === EQUIPMENT ===
|
||||
roof_type: Optional[str] = None
|
||||
audio_system_type: Optional[str] = None
|
||||
individual_equipment: Dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
# === STATUS ===
|
||||
current_mileage: int = Field(default=0)
|
||||
condition_score: int = Field(default=100)
|
||||
status: str
|
||||
data_status: Optional[str] = None
|
||||
is_verified: bool
|
||||
verification_method: Optional[str] = None
|
||||
catalog_match_score: Optional[float] = None
|
||||
|
||||
# Kapcsolt adatok
|
||||
catalog_id: Optional[int] = None
|
||||
catalog: Optional[AssetCatalogResponse] = None # Itt jön a dúsítás!
|
||||
|
||||
owner_organization_id: Optional[int] = None
|
||||
operator_person_id: Optional[int] = None
|
||||
|
||||
# Profile completion percentage (0-100)
|
||||
profile_completion_percentage: int = Field(default=0, ge=0, le=100)
|
||||
|
||||
# === TIMELINE ===
|
||||
year_of_manufacture: Optional[int] = None
|
||||
first_registration_date: Optional[datetime] = None
|
||||
created_at: datetime
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
# === SALES MODULE ===
|
||||
is_for_sale: bool = Field(default=False)
|
||||
price: Optional[float] = None
|
||||
currency: str = Field(default="EUR")
|
||||
|
||||
# === ORGANIZATION & LOCATION ===
|
||||
current_organization_id: Optional[int] = None
|
||||
branch_id: Optional[UUID] = None
|
||||
relocation_performed: bool = Field(default=False)
|
||||
|
||||
# === IDENTITY RELATIONSHIPS ===
|
||||
owner_organization_id: Optional[int] = None
|
||||
operator_person_id: Optional[int] = None
|
||||
owner_person_id: Optional[int] = None
|
||||
operator_org_id: Optional[int] = None
|
||||
|
||||
# === CATALOG RELATIONSHIP ===
|
||||
catalog: Optional[AssetCatalogResponse] = None
|
||||
|
||||
# === PROFILE COMPLETION ===
|
||||
profile_completion_percentage: int = Field(default=0, ge=0, le=100)
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class AssetCreate(BaseModel):
|
||||
""" Jármű létrehozásához szükséges adatok. """
|
||||
vin: Optional[str] = Field(None, min_length=1, max_length=50, description="VIN szám (1-50 karakter, opcionális draft módban)")
|
||||
""" Jármű létrehozásához szükséges adatok - Thick Digital Twin támogatással. """
|
||||
# === CORE IDENTIFICATION (Required for status determination) ===
|
||||
license_plate: str = Field(..., min_length=2, max_length=20, description="Rendszám")
|
||||
vin: Optional[str] = Field(None, min_length=1, max_length=50, description="VIN szám (opcionális)")
|
||||
|
||||
# === CLASSIFICATION (Optional, but affects status) ===
|
||||
brand: Optional[str] = Field(None, max_length=100, description="Márka (ha nincs catalog_id)")
|
||||
model: Optional[str] = Field(None, max_length=100, description="Modell (ha nincs catalog_id)")
|
||||
vehicle_class: Optional[str] = Field(None, max_length=50, description="Járműosztály")
|
||||
fuel_type: Optional[str] = Field(None, max_length=50, description="Üzemanyag típus")
|
||||
|
||||
# === TECHNICAL SPECS (Optional) ===
|
||||
catalog_id: Optional[int] = Field(None, description="Opcionális katalógus ID (ha ismert a modell)")
|
||||
organization_id: Optional[int] = Field(None, description="Szervezet ID, amelyhez a jármű tartozik (opcionális, alapértelmezett a felhasználó szervezete)")
|
||||
engine_capacity: Optional[int] = Field(None, ge=0, description="Hengerűrtartalom (cm³)")
|
||||
power_kw: Optional[int] = Field(None, ge=0, description="Teljesítmény (kW)")
|
||||
torque_nm: Optional[int] = Field(None, ge=0, description="Nyomaték (Nm)")
|
||||
cylinder_layout: Optional[str] = Field(None, max_length=50, description="Hengerelrendezés")
|
||||
transmission_type: Optional[str] = Field(None, max_length=50, description="Váltó típus")
|
||||
drive_type: Optional[str] = Field(None, max_length=50, description="Hajtás")
|
||||
euro_classification: Optional[str] = Field(None, max_length=10, description="EURO besorolás")
|
||||
|
||||
# === PHYSICAL DIMENSIONS (Optional) ===
|
||||
curb_weight: Optional[int] = Field(None, ge=0, description="Saját tömeg (kg)")
|
||||
max_weight: Optional[int] = Field(None, ge=0, description="Össztömeg (kg)")
|
||||
cargo_volume_x: Optional[float] = Field(None, ge=0, description="Csomagtartó hossz (cm)")
|
||||
cargo_volume_y: Optional[float] = Field(None, ge=0, description="Csomagtartó szélesség (cm)")
|
||||
door_count: Optional[int] = Field(None, ge=0, description="Ajtók száma")
|
||||
seat_count: Optional[int] = Field(None, ge=0, description="Ülések száma")
|
||||
|
||||
# === EQUIPMENT (Optional) ===
|
||||
trim_level: Optional[str] = Field(None, max_length=100, description="Felszereltségi szint")
|
||||
roof_type: Optional[str] = Field(None, max_length=50, description="Tető típus")
|
||||
audio_system_type: Optional[str] = Field(None, max_length=100, description="Hangrendszer")
|
||||
individual_equipment: Dict[str, Any] = Field(default_factory=dict, description="Egyedi felszerelések")
|
||||
|
||||
# === TIMELINE (Optional) ===
|
||||
year_of_manufacture: Optional[int] = Field(None, ge=1900, le=2100, description="Gyártási év")
|
||||
first_registration_date: Optional[datetime] = Field(None, description="Első forgalomba helyezés dátuma")
|
||||
|
||||
# === ORGANIZATION (Optional) ===
|
||||
organization_id: Optional[int] = Field(None, description="Szervezet ID (alapértelmezett a felhasználó szervezete)")
|
||||
|
||||
# === STATUS VALIDATION ===
|
||||
@validator('status', pre=True, always=True)
|
||||
def determine_status(cls, v, values):
|
||||
"""Automatikus státusz meghatározás az adatkomplettség alapján."""
|
||||
if v is not None:
|
||||
return v
|
||||
|
||||
# Ellenőrizzük az 5 alapvető mezőt
|
||||
required_fields = ['license_plate', 'brand', 'model', 'vehicle_class', 'fuel_type']
|
||||
has_all_required = all(
|
||||
values.get(field) is not None and str(values.get(field)).strip() != ''
|
||||
for field in required_fields
|
||||
)
|
||||
|
||||
return "active" if has_all_required else "draft"
|
||||
|
||||
# === COMPUTED FIELD: status ===
|
||||
status: Optional[str] = Field(None, description="Automatikusan számított státusz (draft/active)")
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
78
backend/app/schemas/asset_event.py
Normal file
78
backend/app/schemas/asset_event.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/schemas/asset_event.py
|
||||
from pydantic import BaseModel, ConfigDict, Field, validator
|
||||
from typing import Optional, Dict, Any, List
|
||||
from uuid import UUID
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
class AssetEventTypeEnum(str, Enum):
|
||||
"""Digitális Szervizkönyv eseménytípusok."""
|
||||
SERVICE = "SERVICE" # Szerviz
|
||||
REPAIR = "REPAIR" # Javítás
|
||||
ACCIDENT = "ACCIDENT" # Baleset
|
||||
INSPECTION = "INSPECTION" # Műszaki vizsga
|
||||
TIRE_CHANGE = "TIRE_CHANGE" # Gumi csere
|
||||
MAINTENANCE = "MAINTENANCE" # Karbantartás
|
||||
UPGRADE = "UPGRADE" # Fejlesztés
|
||||
RECALL = "RECALL" # Visszahívás
|
||||
OTHER = "OTHER" # Egyéb
|
||||
|
||||
class AssetEventCreate(BaseModel):
|
||||
"""Digitális Szervizkönyv esemény létrehozásához szükséges adatok."""
|
||||
event_type: AssetEventTypeEnum = Field(..., description="Esemény típusa")
|
||||
odometer_reading: Optional[int] = Field(None, ge=0, description="Km óra állás az eseménykor")
|
||||
description: str = Field(..., min_length=1, max_length=1000, description="Esemény leírása")
|
||||
event_date: Optional[datetime] = Field(None, description="Esemény dátuma (alapértelmezett: most)")
|
||||
cost_id: Optional[UUID] = Field(None, description="Kapcsolódó költség rekord ID (opcionális)")
|
||||
|
||||
# RBAC ellenőrzés: csak a tulajdonos vagy operátor szervezet adhat hozzá eseményt
|
||||
# Ezt a service rétegben validáljuk, nem a sémában
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class AssetEventResponse(BaseModel):
|
||||
"""Digitális Szervizkönyv esemény teljes válaszmodellje."""
|
||||
id: UUID
|
||||
asset_id: UUID
|
||||
user_id: Optional[int] = None
|
||||
organization_id: Optional[int] = None
|
||||
|
||||
event_type: str
|
||||
odometer_reading: Optional[int] = None
|
||||
description: Optional[str] = None
|
||||
cost_id: Optional[UUID] = None
|
||||
|
||||
event_date: datetime
|
||||
created_at: datetime
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
# Kapcsolódó entitások (opcionális, csak ha eager loadoltuk)
|
||||
user_name: Optional[str] = Field(None, description="Felhasználó neve")
|
||||
organization_name: Optional[str] = Field(None, description="Szervezet neve")
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class AssetEventUpdate(BaseModel):
|
||||
"""Digitális Szervizkönyv esemény frissítéséhez szükséges adatok."""
|
||||
event_type: Optional[AssetEventTypeEnum] = Field(None, description="Esemény típusa")
|
||||
odometer_reading: Optional[int] = Field(None, ge=0, description="Km óra állás az eseménykor")
|
||||
description: Optional[str] = Field(None, min_length=1, max_length=1000, description="Esemény leírása")
|
||||
event_date: Optional[datetime] = Field(None, description="Esemény dátuma")
|
||||
cost_id: Optional[UUID] = Field(None, description="Kapcsolódó költség rekord ID")
|
||||
|
||||
@validator('description')
|
||||
def validate_description(cls, v):
|
||||
if v is not None and len(v.strip()) == 0:
|
||||
raise ValueError('Description cannot be empty or whitespace only')
|
||||
return v
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class AssetEventListResponse(BaseModel):
|
||||
"""Digitális Szervizkönyv események listázásának válasza."""
|
||||
items: List[AssetEventResponse] = Field(default_factory=list)
|
||||
total: int = Field(0, description="Összes esemény száma")
|
||||
page: int = Field(1, description="Aktuális oldal")
|
||||
page_size: int = Field(20, description="Oldalméret")
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
@@ -28,4 +28,10 @@ class UserUpdate(BaseModel):
|
||||
ui_mode: Optional[str] = None
|
||||
|
||||
class ActiveOrganizationUpdate(BaseModel):
|
||||
organization_id: Optional[str] = None # UUID/string or None to revert to personal mode
|
||||
organization_id: Optional[str] = None # UUID/string or None to revert to personal mode
|
||||
|
||||
class UserWithTokenResponse(BaseModel):
|
||||
"""User response with new JWT token for organization switching"""
|
||||
user: UserResponse
|
||||
access_token: str
|
||||
token_type: str = "bearer"
|
||||
Reference in New Issue
Block a user