feat(robot): hunter v2.7, geocoding support, docker network fix, changelog update

This commit is contained in:
2026-02-13 01:15:34 +00:00
parent 09a0430384
commit f38a75a025
41 changed files with 1801 additions and 153 deletions

View File

@@ -1,54 +1,55 @@
import uuid
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, Numeric, text, Text
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Numeric, text, Text, UniqueConstraint
from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
from sqlalchemy.dialects.postgresql import UUID as PG_UUID, JSONB
from sqlalchemy.sql import func
from app.db.base_class import Base
class AssetCatalog(Base):
"""Globális járműkatalógus (Márka -> Típus -> Generáció -> Motor)."""
__tablename__ = "vehicle_catalog"
__table_args__ = {"schema": "data"}
__table_args__ = (
UniqueConstraint(
'make', 'model', 'year_from', 'engine_variant', 'fuel_type',
name='uix_vehicle_catalog_full'
),
{"schema": "data"}
)
id = Column(Integer, primary_key=True, index=True)
make = Column(String, index=True, nullable=False) # 1. Szint: Audi
model = Column(String, index=True, nullable=False) # 2. Szint: A4
generation = Column(String, index=True) # 3. Szint: B8 (2008-2015)
engine_variant = Column(String) # 4. Szint: 2.0 TDI (150 LE)
make = Column(String, index=True, nullable=False)
model = Column(String, index=True, nullable=False)
generation = Column(String, index=True)
engine_variant = Column(String, index=True)
year_from = Column(Integer)
year_to = Column(Integer)
vehicle_class = Column(String)
fuel_type = Column(String)
fuel_type = Column(String, index=True)
engine_code = Column(String)
factory_data = Column(JSON, server_default=text("'{}'::jsonb")) # Technikai specifikációk
factory_data = Column(JSONB, server_default=text("'{}'::jsonb"))
assets = relationship("Asset", back_populates="catalog")
class Asset(Base):
"""Egyedi jármű (Asset) példány - Az ökoszisztéma magja."""
__tablename__ = "assets"
__table_args__ = {"schema": "data"}
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
vin = Column(String(17), unique=True, index=True, nullable=False)
license_plate = Column(String(20), index=True)
name = Column(String)
year_of_manufacture = Column(Integer)
# --- BIZTONSÁGI ÉS JOGOSULTSÁGI IZOLÁCIÓ ---
# A current_organization_id biztosítja a gyors, adatbázis-szintű Scoped RBAC védelmet.
current_organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=True)
catalog_id = Column(Integer, ForeignKey("data.vehicle_catalog.id"))
is_verified = Column(Boolean, default=False)
verification_method = Column(String(20)) # 'robot', 'ocr', 'manual'
status = Column(String(20), default="active")
# Moderációs mezők a Robot 3 (OCR) számára
is_verified = Column(Boolean, default=False)
verification_method = Column(String(20)) # 'manual', 'ocr', 'vin_api'
verification_notes = Column(Text, nullable=True) # Eltérések jegyzőkönyve
catalog_match_score = Column(Numeric(5, 2), nullable=True) # 0-100% egyezési arány
status = Column(String(20), default="active")
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Kapcsolatok (Digital Twin Modules)
catalog = relationship("AssetCatalog", back_populates="assets")
current_org = relationship("Organization")
financials = relationship("AssetFinancials", back_populates="asset", uselist=False)
@@ -57,6 +58,7 @@ class Asset(Base):
events = relationship("AssetEvent", back_populates="asset")
costs = relationship("AssetCost", back_populates="asset")
reviews = relationship("AssetReview", back_populates="asset")
ownership_history = relationship("VehicleOwnership", back_populates="vehicle")
class AssetFinancials(Base):
__tablename__ = "asset_financials"
@@ -87,15 +89,13 @@ class AssetReview(Base):
asset_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), nullable=False)
user_id = Column(Integer, ForeignKey("data.users.id"), nullable=False)
overall_rating = Column(Integer)
criteria_scores = Column(JSON, server_default=text("'{}'::jsonb"))
criteria_scores = Column(JSONB, server_default=text("'{}'::jsonb"))
comment = Column(Text)
created_at = Column(DateTime(timezone=True), server_default=func.now())
asset = relationship("Asset", back_populates="reviews")
user = relationship("User")
class AssetAssignment(Base):
"""Jármű flotta-történetének nyilvántartása."""
__tablename__ = "asset_assignments"
__table_args__ = {"schema": "data"}
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
@@ -104,7 +104,6 @@ class AssetAssignment(Base):
assigned_at = Column(DateTime(timezone=True), server_default=func.now())
released_at = Column(DateTime(timezone=True), nullable=True)
status = Column(String(30), default="active")
asset = relationship("Asset", back_populates="assignments")
organization = relationship("Organization")
@@ -115,7 +114,7 @@ class AssetEvent(Base):
asset_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), nullable=False)
event_type = Column(String(50), nullable=False)
recorded_mileage = Column(Integer)
data = Column(JSON, server_default=text("'{}'::jsonb"))
data = Column(JSONB, server_default=text("'{}'::jsonb"))
asset = relationship("Asset", back_populates="events")
class AssetCost(Base):
@@ -129,10 +128,12 @@ class AssetCost(Base):
amount_local = Column(Numeric(18, 2), nullable=False)
currency_local = Column(String(3), nullable=False)
amount_eur = Column(Numeric(18, 2), nullable=True)
net_amount_local = Column(Numeric(18, 2))
vat_rate = Column(Numeric(5, 2))
exchange_rate_used = Column(Numeric(18, 6))
date = Column(DateTime(timezone=True), server_default=func.now())
mileage_at_cost = Column(Integer)
data = Column(JSON, server_default=text("'{}'::jsonb"))
data = Column(JSONB, server_default=text("'{}'::jsonb"))
asset = relationship("Asset", back_populates="costs")
organization = relationship("Organization")
driver = relationship("User")
@@ -143,5 +144,4 @@ class ExchangeRate(Base):
id = Column(Integer, primary_key=True)
base_currency = Column(String(3), default="EUR")
target_currency = Column(String(3), unique=True)
rate = Column(Numeric(18, 6), nullable=False)
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
rate = Column(Numeric(18, 6), nullable=False)