452 lines
24 KiB
Python
452 lines
24 KiB
Python
# /opt/docker/dev/service_finder/backend/app/models/vehicle/asset.py
|
|
from __future__ import annotations
|
|
import uuid
|
|
import enum
|
|
from datetime import datetime
|
|
from typing import List, Optional, TYPE_CHECKING
|
|
from sqlalchemy import String, Boolean, DateTime, ForeignKey, Numeric, text, Text, UniqueConstraint, BigInteger, Integer, Float
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
from sqlalchemy.dialects.postgresql import UUID as PG_UUID, JSONB
|
|
from sqlalchemy.sql import func
|
|
from app.database import Base
|
|
|
|
|
|
class AssetCatalog(Base):
|
|
""" Jármű katalógus mesteradatok (Validált technikai sablonok). """
|
|
__tablename__ = "vehicle_catalog"
|
|
__table_args__ = (
|
|
UniqueConstraint('make', 'model', 'year_from', 'fuel_type', name='uix_vehicle_catalog_full'),
|
|
{"schema": "vehicle"}
|
|
)
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
|
master_definition_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("vehicle.vehicle_model_definitions.id"))
|
|
|
|
make: Mapped[str] = mapped_column(String, index=True, nullable=False)
|
|
model: Mapped[str] = mapped_column(String, index=True, nullable=False)
|
|
generation: Mapped[Optional[str]] = mapped_column(String, index=True)
|
|
year_from: Mapped[Optional[int]] = mapped_column(Integer)
|
|
year_to: Mapped[Optional[int]] = mapped_column(Integer)
|
|
fuel_type: Mapped[Optional[str]] = mapped_column(String, index=True)
|
|
power_kw: Mapped[Optional[int]] = mapped_column(Integer, index=True)
|
|
engine_capacity: Mapped[Optional[int]] = mapped_column(Integer, index=True)
|
|
|
|
factory_data: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb"))
|
|
|
|
master_definition: Mapped[Optional["VehicleModelDefinition"]] = relationship("VehicleModelDefinition", back_populates="variants")
|
|
assets: Mapped[List["Asset"]] = relationship("Asset", back_populates="catalog")
|
|
|
|
|
|
class VehicleClassEnum(str, enum.Enum):
|
|
"""Jármű osztályok a 99_Adattarolás.md alapján."""
|
|
PERSONAL = "personal" # Személygépjármű
|
|
MOTORCYCLE = "motorcycle" # Motorkerékpár
|
|
LIGHT_COMMERCIAL = "light_commercial" # Kishaszon gépjármű
|
|
COMMERCIAL = "commercial" # Haszonjármű
|
|
WORK_MACHINE = "work_machine" # Munkagép
|
|
TRAILER = "trailer" # Pótkocsi/utánfutó
|
|
BUS = "bus" # Autóbusz
|
|
CAMPER = "camper" # Lakókocsi/lakóautó
|
|
BOAT = "boat" # Hajó
|
|
AIRCRAFT = "aircraft" # Repülőgép
|
|
|
|
|
|
class RoofTypeEnum(str, enum.Enum):
|
|
"""Tető típusok a 99_Adattarolás.md alapján."""
|
|
METAL = "metal" # Lemeztető
|
|
FABRIC = "fabric" # Vászontető
|
|
HARDTOP = "hardtop" # Nyitható keménytető
|
|
FOLDING = "folding" # Harmonikatető
|
|
TARGA = "targa" # Targatető
|
|
FIXED_GLASS = "fixed_glass" # Fix üvegtető
|
|
PANORAMIC = "panoramic" # Panorámatető
|
|
FIXED_SUNROOF = "fixed_sunroof" # Fix napfénytető
|
|
OPENABLE_SUNROOF = "openable_sunroof" # Nyitható napfénytető
|
|
RETRACTABLE_SUNROOF = "retractable_sunroof" # Elhúzható napfénytető
|
|
MOTORIZED_SUNROOF = "motorized_sunroof" # Motoros napfénytető
|
|
OPENABLE_PANORAMIC = "openable_panoramic" # Nyitható panorámatető
|
|
|
|
|
|
class Asset(Base):
|
|
""" A fizikai eszköz (Digital Twin) - Minden adat itt fut össze. """
|
|
__tablename__ = "assets"
|
|
__table_args__ = {"schema": "vehicle"}
|
|
|
|
# === IDENTIFICATION ===
|
|
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
vin: Mapped[Optional[str]] = mapped_column(String(17), unique=True, index=True, nullable=True)
|
|
license_plate: Mapped[Optional[str]] = mapped_column(String(20), index=True)
|
|
name: Mapped[Optional[str]] = mapped_column(String)
|
|
catalog_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("vehicle.vehicle_catalog.id"))
|
|
|
|
# === CLASSIFICATION ===
|
|
vehicle_class: Mapped[Optional[str]] = mapped_column(String(50), index=True) # VehicleClassEnum értékek
|
|
brand: Mapped[Optional[str]] = mapped_column(String(100), index=True) # Márka (ha nincs catalog)
|
|
model: Mapped[Optional[str]] = mapped_column(String(100), index=True) # Modell (ha nincs catalog)
|
|
trim_level: Mapped[Optional[str]] = mapped_column(String(100)) # Felszereltségi szint/kivitel
|
|
|
|
# === TECHNICAL SPECS ===
|
|
fuel_type: Mapped[Optional[str]] = mapped_column(String(50), index=True) # benzin, diesel, elektromos, etanol, gáz
|
|
engine_capacity: Mapped[Optional[int]] = mapped_column(Integer, index=True) # cm³
|
|
power_kw: Mapped[Optional[int]] = mapped_column(Integer, index=True) # kW
|
|
torque_nm: Mapped[Optional[int]] = mapped_column(Integer) # Nm
|
|
cylinder_layout: Mapped[Optional[str]] = mapped_column(String(50)) # soros, V, boxer, stb.
|
|
transmission_type: Mapped[Optional[str]] = mapped_column(String(50), index=True) # kézi, autómata, CVT, DCT
|
|
drive_type: Mapped[Optional[str]] = mapped_column(String(50), index=True) # első, hátsó, összkerék
|
|
euro_classification: Mapped[Optional[str]] = mapped_column(String(10)) # EURO 1-6
|
|
|
|
# === PHYSICAL DIMENSIONS ===
|
|
curb_weight: Mapped[Optional[int]] = mapped_column(Integer) # saját tömeg (kg)
|
|
max_weight: Mapped[Optional[int]] = mapped_column(Integer) # össztömeg (kg)
|
|
cargo_volume_x: Mapped[Optional[float]] = mapped_column(Numeric(10, 2)) # csomagtartó hossz (cm)
|
|
cargo_volume_y: Mapped[Optional[float]] = mapped_column(Numeric(10, 2)) # csomagtartó szélesség (cm)
|
|
door_count: Mapped[Optional[int]] = mapped_column(Integer) # ajtók száma
|
|
seat_count: Mapped[Optional[int]] = mapped_column(Integer) # ülések száma
|
|
|
|
# === EQUIPMENT ===
|
|
roof_type: Mapped[Optional[str]] = mapped_column(String(50)) # RoofTypeEnum értékek
|
|
audio_system_type: Mapped[Optional[str]] = mapped_column(String(100)) # rádió típusa
|
|
individual_equipment: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb")) # JSONB extra felszerelések
|
|
|
|
# === STATUS ===
|
|
current_mileage: Mapped[int] = mapped_column(Integer, default=0, index=True)
|
|
condition_score: Mapped[int] = mapped_column(Integer, default=100) # 0-100
|
|
status: Mapped[str] = mapped_column(String(20), default="active")
|
|
data_status: Mapped[Optional[str]] = mapped_column(String(20), nullable=True, server_default=text("'draft'"))
|
|
|
|
# === TIMELINE ===
|
|
year_of_manufacture: Mapped[Optional[int]] = mapped_column(Integer, index=True)
|
|
first_registration_date: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), onupdate=func.now())
|
|
|
|
# === SALES MODULE ===
|
|
is_for_sale: Mapped[bool] = mapped_column(Boolean, default=False, index=True)
|
|
price: Mapped[Optional[float]] = mapped_column(Numeric(15, 2))
|
|
currency: Mapped[str] = mapped_column(String(3), default="EUR")
|
|
|
|
# === ORGANIZATION & LOCATION ===
|
|
current_organization_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("fleet.organizations.id"))
|
|
branch_id: Mapped[Optional[uuid.UUID]] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("fleet.branches.id"))
|
|
relocation_performed: Mapped[bool] = mapped_column(Boolean, server_default=text('false'), default=False)
|
|
|
|
# === IDENTITY RELATIONSHIPS ===
|
|
owner_person_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("identity.persons.id"))
|
|
owner_org_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("fleet.organizations.id"))
|
|
operator_person_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("identity.persons.id"))
|
|
operator_org_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("fleet.organizations.id"))
|
|
|
|
# --- KAPCSOLATOK ---
|
|
catalog: Mapped["AssetCatalog"] = relationship("AssetCatalog", back_populates="assets")
|
|
financials: Mapped[Optional["AssetFinancials"]] = relationship("AssetFinancials", back_populates="asset", uselist=False)
|
|
costs: Mapped[List["AssetCost"]] = relationship("AssetCost", back_populates="asset")
|
|
events: Mapped[List["AssetEvent"]] = relationship("AssetEvent", back_populates="asset")
|
|
logbook: Mapped[List["VehicleLogbook"]] = relationship("VehicleLogbook", back_populates="asset")
|
|
inspections: Mapped[List["AssetInspection"]] = relationship("AssetInspection", back_populates="asset")
|
|
reviews: Mapped[List["AssetReview"]] = relationship("AssetReview", back_populates="asset")
|
|
telemetry: Mapped[Optional["AssetTelemetry"]] = relationship("AssetTelemetry", back_populates="asset", uselist=False)
|
|
assignments: Mapped[List["AssetAssignment"]] = relationship("AssetAssignment", back_populates="asset")
|
|
ownership_history: Mapped[List["VehicleOwnership"]] = relationship("VehicleOwnership", back_populates="asset")
|
|
service_requests: Mapped[List["ServiceRequest"]] = relationship("ServiceRequest", back_populates="asset", cascade="all, delete-orphan")
|
|
|
|
# --- COMPUTED PROPERTIES (for Pydantic schema compatibility) ---
|
|
@property
|
|
def is_verified(self) -> bool:
|
|
"""Always False for now, as verification is not yet implemented."""
|
|
return False
|
|
|
|
@property
|
|
def profile_completion_percentage(self) -> int:
|
|
"""
|
|
Calculate profile completion percentage based on available data.
|
|
Uses dynamic weights from system.system_data_completion_weights table.
|
|
Default weights (if not configured):
|
|
- license_plate: 20%
|
|
- make: 15% (from catalog)
|
|
- model: 15% (from catalog)
|
|
- vin: 30%
|
|
- year_of_manufacture: 20%
|
|
"""
|
|
# Default weights (fallback if dynamic weights not available)
|
|
default_weights = {
|
|
'license_plate': 20,
|
|
'make': 15,
|
|
'model': 15,
|
|
'vin': 30,
|
|
'year_of_manufacture': 20
|
|
}
|
|
|
|
total_score = 0
|
|
|
|
# 1. license_plate
|
|
if self.license_plate and self.license_plate.strip():
|
|
total_score += default_weights['license_plate']
|
|
|
|
# 2. make (from catalog or brand field)
|
|
if (self.catalog and self.catalog.make) or self.brand:
|
|
total_score += default_weights['make']
|
|
|
|
# 3. model (from catalog or model field)
|
|
if (self.catalog and self.catalog.model) or self.model:
|
|
total_score += default_weights['model']
|
|
|
|
# 4. vin
|
|
if self.vin and self.vin.strip():
|
|
total_score += default_weights['vin']
|
|
|
|
# 5. year_of_manufacture
|
|
if self.year_of_manufacture:
|
|
total_score += default_weights['year_of_manufacture']
|
|
|
|
return min(total_score, 100)
|
|
|
|
|
|
class AssetFinancials(Base):
|
|
""" I. Beszerzés és IV. Értékcsökkenés (Amortizáció). """
|
|
__tablename__ = "asset_financials"
|
|
__table_args__ = {"schema": "vehicle"}
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), unique=True)
|
|
|
|
purchase_price_net: Mapped[float] = mapped_column(Numeric(18, 2))
|
|
purchase_price_gross: Mapped[float] = mapped_column(Numeric(18, 2))
|
|
vat_rate: Mapped[float] = mapped_column(Numeric(5, 2), default=27.00)
|
|
activation_date: Mapped[Optional[datetime]] = mapped_column(DateTime)
|
|
verified_purchase_date: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
|
|
financing_type: Mapped[str] = mapped_column(String(50))
|
|
accounting_details: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb"))
|
|
|
|
asset: Mapped["Asset"] = relationship("Asset", back_populates="financials")
|
|
|
|
|
|
class AssetCost(Base):
|
|
""" II. Üzemeltetés és TCO kimutatás. """
|
|
__tablename__ = "asset_costs"
|
|
__table_args__ = {"schema": "vehicle"}
|
|
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
|
organization_id: Mapped[int] = mapped_column(Integer, ForeignKey("fleet.organizations.id"), nullable=False)
|
|
|
|
cost_category: Mapped[str] = mapped_column(String(50), index=True)
|
|
amount_net: Mapped[float] = mapped_column(Numeric(18, 2), nullable=False)
|
|
currency: Mapped[str] = mapped_column(String(3), default="HUF")
|
|
date: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
invoice_number: Mapped[Optional[str]] = mapped_column(String(100), index=True)
|
|
|
|
data: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb"))
|
|
asset: Mapped["Asset"] = relationship("Asset", back_populates="costs")
|
|
organization: Mapped["Organization"] = relationship("Organization")
|
|
|
|
|
|
class VehicleLogbook(Base):
|
|
""" Útnyilvántartás (NAV, Kiküldetés, Munkábajárás). """
|
|
__tablename__ = "vehicle_logbook"
|
|
__table_args__ = {"schema": "vehicle"}
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
|
driver_id: Mapped[int] = mapped_column(Integer, ForeignKey("identity.users.id"), nullable=False)
|
|
|
|
trip_type: Mapped[str] = mapped_column(String(30), index=True)
|
|
is_reimbursable: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
start_mileage: Mapped[int] = mapped_column(Integer)
|
|
end_mileage: Mapped[Optional[int]] = mapped_column(Integer)
|
|
distance_km: Mapped[Optional[float]] = mapped_column(Numeric(10, 2), nullable=True)
|
|
|
|
# GPS koordináták
|
|
start_lat: Mapped[Optional[float]] = mapped_column(Numeric(10, 6), nullable=True)
|
|
start_lng: Mapped[Optional[float]] = mapped_column(Numeric(10, 6), nullable=True)
|
|
end_lat: Mapped[Optional[float]] = mapped_column(Numeric(10, 6), nullable=True)
|
|
end_lng: Mapped[Optional[float]] = mapped_column(Numeric(10, 6), nullable=True)
|
|
gps_calculated_distance: Mapped[Optional[float]] = mapped_column(Numeric(10, 2), nullable=True)
|
|
|
|
obd_verified: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
max_acceleration: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
|
average_speed: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
|
|
|
asset: Mapped["Asset"] = relationship("Asset", back_populates="logbook")
|
|
driver: Mapped["User"] = relationship("User")
|
|
|
|
|
|
class AssetInspection(Base):
|
|
""" Napi ellenőrző lista és Biztonsági check. """
|
|
__tablename__ = "asset_inspections"
|
|
__table_args__ = {"schema": "vehicle"}
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
|
inspector_id: Mapped[int] = mapped_column(Integer, ForeignKey("identity.users.id"), nullable=False)
|
|
|
|
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
checklist_results: Mapped[dict] = mapped_column(JSONB, nullable=False)
|
|
is_safe: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
|
|
asset: Mapped["Asset"] = relationship("Asset", back_populates="inspections")
|
|
inspector: Mapped["User"] = relationship("User")
|
|
|
|
|
|
class AssetReview(Base):
|
|
""" Jármű értékelések és visszajelzések. """
|
|
__tablename__ = "asset_reviews"
|
|
__table_args__ = {"schema": "vehicle"}
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
|
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("identity.users.id"), nullable=False)
|
|
|
|
overall_rating: Mapped[Optional[int]] = mapped_column(Integer) # 1-5 csillag
|
|
comment: Mapped[Optional[str]] = mapped_column(Text)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
asset: Mapped["Asset"] = relationship("Asset", back_populates="reviews")
|
|
user: Mapped["User"] = relationship("User")
|
|
|
|
|
|
class VehicleOwnership(Base):
|
|
""" Tulajdonosváltások története. """
|
|
__tablename__ = "vehicle_ownership_history"
|
|
__table_args__ = {"schema": "vehicle"}
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
|
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("identity.users.id"), nullable=False)
|
|
|
|
acquired_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
disposed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
|
|
|
asset: Mapped["Asset"] = relationship("Asset", back_populates="ownership_history")
|
|
# JAVÍTVA: Kapcsolat a User modellhez
|
|
user: Mapped["User"] = relationship("User", back_populates="ownership_history")
|
|
|
|
|
|
class AssetTelemetry(Base):
|
|
__tablename__ = "asset_telemetry"
|
|
__table_args__ = {"schema": "vehicle"}
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), unique=True)
|
|
current_mileage: Mapped[int] = mapped_column(Integer, default=0)
|
|
asset: Mapped["Asset"] = relationship("Asset", back_populates="telemetry")
|
|
|
|
|
|
class AssetAssignment(Base):
|
|
""" Eszköz-Szervezet összerendelés. """
|
|
__tablename__ = "asset_assignments"
|
|
__table_args__ = {"schema": "fleet"}
|
|
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
|
organization_id: Mapped[int] = mapped_column(Integer, ForeignKey("fleet.organizations.id"), nullable=False)
|
|
status: Mapped[str] = mapped_column(String(30), default="active")
|
|
|
|
asset: Mapped["Asset"] = relationship("Asset", back_populates="assignments")
|
|
organization: Mapped["Organization"] = relationship("Organization", back_populates="assets")
|
|
|
|
|
|
class AssetEventTypeEnum(str, enum.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
|
|
|
|
|
|
class AssetEvent(Base):
|
|
""" Digitális Szervizkönyv - Szerviz, baleset és egyéb jelentős események. """
|
|
__tablename__ = "asset_events"
|
|
__table_args__ = {"schema": "vehicle"}
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
|
user_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id"), nullable=True)
|
|
organization_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("fleet.organizations.id"), nullable=True)
|
|
|
|
event_type: Mapped[str] = mapped_column(String(50), nullable=False) # AssetEventTypeEnum értékek
|
|
odometer_reading: Mapped[Optional[int]] = mapped_column(Integer) # Km óra állás az eseménykor
|
|
description: Mapped[Optional[str]] = mapped_column(Text)
|
|
cost_id: Mapped[Optional[uuid.UUID]] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.asset_costs.id"), nullable=True)
|
|
|
|
event_date: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), onupdate=func.now())
|
|
|
|
# Relationships
|
|
asset: Mapped["Asset"] = relationship("Asset", back_populates="events")
|
|
user: Mapped[Optional["User"]] = relationship("User")
|
|
organization: Mapped[Optional["Organization"]] = relationship("Organization")
|
|
cost: Mapped[Optional["AssetCost"]] = relationship("AssetCost")
|
|
|
|
|
|
class ExchangeRate(Base):
|
|
__tablename__ = "exchange_rates"
|
|
__table_args__ = {"schema": "finance"}
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
rate: Mapped[float] = mapped_column(Numeric(18, 6), nullable=False)
|
|
|
|
|
|
class CatalogDiscovery(Base):
|
|
""" Robot munkaterület a felfedezett modelleknek. """
|
|
__tablename__ = "catalog_discovery"
|
|
__table_args__ = (
|
|
# KIBŐVÍTETT EGYEDISÉGI SZABÁLY: Márka + Modell + Osztály + Piac + Évjárat
|
|
UniqueConstraint('make', 'model', 'vehicle_class', 'market', 'model_year', name='_make_model_market_year_uc'),
|
|
# Alapvető egyediség: make + model + vehicle_class (piac és évjárat nélkül)
|
|
UniqueConstraint('make', 'model', 'vehicle_class', name='uq_make_model_class'),
|
|
{"schema": "vehicle"}
|
|
)
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
|
make: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
|
|
model: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
|
|
vehicle_class: Mapped[str] = mapped_column(String(50), server_default=text("'car'"), index=True)
|
|
|
|
# --- ÚJ MEZŐK A STATISZTIKÁHOZ ÉS PIACAZONOSÍTÁSHOZ ---
|
|
market: Mapped[str] = mapped_column(String(20), server_default=text("'GLOBAL'"), index=True) # pl: RDW, DVLA, USA_IMPORT
|
|
model_year: Mapped[Optional[int]] = mapped_column(Integer, index=True)
|
|
|
|
# Robot vezérlés
|
|
status: Mapped[str] = mapped_column(String(20), server_default=text("'pending'"), index=True)
|
|
source: Mapped[Optional[str]] = mapped_column(String(100)) # pl: STRATEGIST-V2, NHTSA-V1
|
|
priority_score: Mapped[int] = mapped_column(Integer, server_default=text("0"))
|
|
attempts: Mapped[int] = mapped_column(Integer, server_default=text("0"))
|
|
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
|
|
|
|
|
class VehicleExpenses(Base):
|
|
""" Jármű költségek a jelentésekhez. """
|
|
__tablename__ = "vehicle_expenses"
|
|
__table_args__ = {"schema": "vehicle"}
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
vehicle_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False, index=True)
|
|
category: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
|
|
amount: Mapped[float] = mapped_column(Numeric(18, 2), nullable=False)
|
|
date: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), index=True)
|
|
description: Mapped[Optional[str]] = mapped_column(Text)
|
|
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
# Relationship
|
|
asset: Mapped["Asset"] = relationship("Asset")
|
|
|
|
|
|
class VehicleTransferRequest(Base):
|
|
"""Járműátadási kérelem - asset átruházás másik tulajdonosnak vagy szervezetnek."""
|
|
__tablename__ = "vehicle_transfer_requests"
|
|
__table_args__ = {"schema": "vehicle"}
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False, index=True)
|
|
requester_id: Mapped[int] = mapped_column(Integer, ForeignKey("identity.users.id"), nullable=False, index=True)
|
|
current_owner_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.persons.id"), nullable=True, index=True)
|
|
status: Mapped[str] = mapped_column(String(20), default="pending", index=True)
|
|
proof_document_id: Mapped[Optional[uuid.UUID]] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("system.documents.id"), nullable=True)
|
|
|
|
requested_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
processed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
|
notes: Mapped[Optional[str]] = mapped_column(Text)
|
|
|
|
# Relationships
|
|
asset: Mapped["Asset"] = relationship("Asset")
|
|
requester: Mapped["User"] = relationship("User", foreign_keys=[requester_id])
|
|
current_owner: Mapped[Optional["Person"]] = relationship("Person", foreign_keys=[current_owner_id])
|
|
proof_document: Mapped[Optional["Document"]] = relationship("Document")
|