feat: stabilize KYC, international assets and multi-currency schema

- Split mother's name in KYC (last/first)
- Added mileage_unit and fuel_type to Assets
- Expanded AssetCost for international VAT and original currency
- Fixed SQLAlchemy IndexError in asset catalog lookup
- Added exchange_rate and ratings tables to models
This commit is contained in:
2026-02-08 23:41:07 +00:00
parent 451900ae1a
commit 24d35fe0c1
34 changed files with 709 additions and 347 deletions

View File

@@ -1,19 +1,15 @@
from app.db.base import Base
from app.db.base_class import Base
from .identity import User, Person, Wallet, UserRole, VerificationToken
from .organization import Organization, OrgType
from .vehicle import (
VehicleCatalog,
Asset,
AssetEvent,
AssetRating,
ServiceProvider,
Vehicle, # Alias az Asset-re a kompatibilitás miatt
ServiceRecord # Alias az AssetEvent-re a kompatibilitás miatt
)
from .organization import Organization, OrganizationMember
from .asset import Asset, AssetCatalog, AssetCost, AssetEvent
from .address import Address, GeoPostalCode, GeoStreet, GeoStreetType
from .gamification import UserStats, PointsLedger
# Aliasok a kód többi részének szinkronizálásához
UserVehicle = Vehicle
VehicleOwnership = Asset
# Aliasok a kompatibilitás és a tiszta kód érdekében
Vehicle = Asset
UserVehicle = Asset
VehicleCatalog = AssetCatalog
ServiceRecord = AssetEvent
__all__ = [
"Base",
@@ -23,14 +19,19 @@ __all__ = [
"UserRole",
"VerificationToken",
"Organization",
"OrgType",
"VehicleCatalog",
"OrganizationMember",
"Asset",
"AssetEvent",
"AssetRating",
"ServiceProvider",
"AssetCatalog",
"AssetCost",
"AssetEvent",
"Address",
"GeoPostalCode",
"GeoStreet",
"GeoStreetType",
"UserStats",
"PointsLedger",
"Vehicle",
"UserVehicle",
"ServiceRecord",
"VehicleOwnership"
"VehicleCatalog",
"ServiceRecord"
]

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,45 @@
import uuid
from sqlalchemy import Column, String, Integer, ForeignKey, Text, DateTime
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
from sqlalchemy.sql import func
from app.db.base_class import Base
class GeoPostalCode(Base):
__tablename__ = "geo_postal_codes"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
country_code = Column(String(5), default="HU")
zip_code = Column(String(10), nullable=False)
city = Column(String(100), nullable=False)
class GeoStreet(Base):
__tablename__ = "geo_streets"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
postal_code_id = Column(Integer, ForeignKey("data.geo_postal_codes.id"))
name = Column(String(200), nullable=False)
class GeoStreetType(Base):
__tablename__ = "geo_street_types"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=True, nullable=False)
class Address(Base):
__tablename__ = "addresses"
__table_args__ = {"schema": "data"}
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
postal_code_id = Column(Integer, ForeignKey("data.geo_postal_codes.id"))
street_name = Column(String(200), nullable=False)
street_type = Column(String(50), nullable=False)
house_number = Column(String(50), nullable=False)
stairwell = Column(String(20))
floor = Column(String(20))
door = Column(String(20))
parcel_id = Column(String(50)) # HRSZ
full_address_text = Column(Text)
created_at = Column(DateTime(timezone=True), server_default=func.now())

133
backend/app/models/asset.py Normal file
View File

@@ -0,0 +1,133 @@
import uuid
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, Numeric, text
from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
from sqlalchemy.sql import func
from app.db.base_class import Base
class AssetCatalog(Base):
"""Központi jármű katalógus (Admin/Bot által tölthető)"""
__tablename__ = "vehicle_catalog"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True, index=True)
make = Column(String, index=True, nullable=False)
model = Column(String, index=True, nullable=False)
generation = Column(String)
year_from = Column(Integer)
year_to = Column(Integer)
vehicle_class = Column(String) # land, sea, air
fuel_type = Column(String)
engine_code = Column(String)
assets = relationship("Asset", back_populates="catalog")
class Asset(Base):
"""A Jármű Identitás (Digital Twin törzsadatok)"""
__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)
catalog_id = Column(Integer, ForeignKey("data.vehicle_catalog.id"))
# Nemzetközi mutatók
quality_index = Column(Numeric(3, 2), default=1.00)
system_mileage = Column(Integer, default=0)
mileage_unit = Column(String(10), default="km") # Nemzetközi: km, miles, hours
is_verified = Column(Boolean, default=False)
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())
catalog = relationship("AssetCatalog", back_populates="assets")
assignments = relationship("AssetAssignment", back_populates="asset")
events = relationship("AssetEvent", back_populates="asset")
costs = relationship("AssetCost", back_populates="asset")
class AssetAssignment(Base):
"""Birtoklás követése (Kié a jármű és mettől meddig)"""
__tablename__ = "asset_assignments"
__table_args__ = {"schema": "data"}
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
asset_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), nullable=False)
organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=False)
assigned_at = Column(DateTime(timezone=True), server_default=func.now())
released_at = Column(DateTime(timezone=True), nullable=True)
status = Column(String(30), default="active")
notes = Column(String)
asset = relationship("Asset", back_populates="assignments")
organization = relationship("Organization", back_populates="assets")
class AssetEvent(Base):
"""Élettörténeti események (Szerviz, km-óra állások, balesetek)"""
__tablename__ = "asset_events"
__table_args__ = {"schema": "data"}
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
asset_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), nullable=False)
event_type = Column(String(50), nullable=False)
event_date = Column(DateTime(timezone=True), server_default=func.now())
recorded_mileage = Column(Integer)
description = Column(String)
data = Column(JSON, server_default=text("'{}'::jsonb"))
asset = relationship("Asset", back_populates="events")
class AssetCost(Base):
"""
Költségkezelő modell: Bruttó/Nettó/ÁFA és deviza támogatás.
"""
__tablename__ = "asset_costs"
__table_args__ = {"schema": "data"}
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
asset_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), nullable=False)
organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=False)
cost_type = Column(String(50), nullable=False) # fuel, service, tax, insurance, toll
# Pénzügyi adatok
amount = Column(Numeric(18, 2), nullable=False) # Bruttó összeg
net_amount = Column(Numeric(18, 2)) # Nettó összeg
vat_amount = Column(Numeric(18, 2)) # ÁFA érték
vat_rate = Column(Numeric(5, 2)) # ÁFA kulcs (pl. 27.00)
# Nemzetközi deviza kezelés
currency = Column(String(3), default="HUF") # Riportálási deviza
original_currency = Column(String(3)) # Számla eredeti devizája
exchange_rate_at_cost = Column(Numeric(18, 6)) # Rögzítéskori árfolyam
date = Column(DateTime(timezone=True), server_default=func.now())
description = Column(String)
invoice_id = Column(String)
mileage_at_cost = Column(Integer)
data = Column(JSON, server_default=text("'{}'::jsonb"))
asset = relationship("Asset", back_populates="costs")
class ExchangeRate(Base):
"""Napi árfolyamok tárolása (ECB/MNB adatok alapján)"""
__tablename__ = "exchange_rates"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True, index=True)
base_currency = Column(String(3), default="EUR")
target_currency = Column(String(3), nullable=False)
rate = Column(Numeric(18, 6), nullable=False)
rate_date = Column(DateTime(timezone=False), index=True)
provider = Column(String(50), default="ECB")
updated_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -1,8 +1,14 @@
import uuid
from datetime import datetime
from typing import Optional
from typing import Optional, TYPE_CHECKING
from sqlalchemy import ForeignKey, String, Integer, DateTime, func, Boolean
from sqlalchemy.orm import Mapped, mapped_column
from app.db.base import Base
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
from app.db.base_class import Base
# Típusvizsgálathoz a körkörös import elkerülése érdekében
if TYPE_CHECKING:
from app.models.identity import User
# Közös beállítás az összes táblához ebben a fájlban
SCHEMA_ARGS = {"schema": "data"}
@@ -36,21 +42,26 @@ class PointsLedger(Base):
__tablename__ = "points_ledger"
__table_args__ = SCHEMA_ARGS
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
# JAVÍTVA: data.users.id hivatkozás
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.users.id"))
points: Mapped[int] = mapped_column(Integer)
reason: Mapped[str] = mapped_column(String)
created_at: Mapped[datetime] = mapped_column(DateTime, default=func.now())
# Kapcsolat a felhasználóhoz
user: Mapped["User"] = relationship("User")
class UserStats(Base):
__tablename__ = "user_stats"
__table_args__ = SCHEMA_ARGS
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
# JAVÍTVA: data.users.id hivatkozás
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.users.id"), unique=True)
total_points: Mapped[int] = mapped_column(Integer, default=0)
# user_id a PK, mert 1:1 kapcsolat a User-rel
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.users.id"), primary_key=True)
total_xp: Mapped[int] = mapped_column(Integer, default=0)
social_points: Mapped[int] = mapped_column(Integer, default=0)
current_level: Mapped[int] = mapped_column(Integer, default=1)
last_activity: Mapped[datetime] = mapped_column(DateTime, default=func.now())
updated_at: Mapped[datetime] = mapped_column(DateTime, default=func.now(), onupdate=func.now())
# EZ A JAVÍTÁS: A visszamutató kapcsolat definiálása
user: Mapped["User"] = relationship("User", back_populates="stats")
class Badge(Base):
__tablename__ = "badges"
@@ -64,7 +75,18 @@ class UserBadge(Base):
__tablename__ = "user_badges"
__table_args__ = SCHEMA_ARGS
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
# JAVÍTVA: data.users.id hivatkozás
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.users.id"))
badge_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.badges.id"))
earned_at: Mapped[datetime] = mapped_column(DateTime, default=func.now())
earned_at: Mapped[datetime] = mapped_column(DateTime, default=func.now())
user: Mapped["User"] = relationship("User")
class Rating(Base): # <--- Az új értékelési modell
__tablename__ = "ratings"
__table_args__ = SCHEMA_ARGS
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
author_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.users.id"))
target_type: Mapped[str] = mapped_column(String(20))
target_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True))
score: Mapped[int] = mapped_column(Integer)
comment: Mapped[Optional[str]] = mapped_column(String)

View File

@@ -2,9 +2,9 @@ import uuid
import enum
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, Numeric, text, Enum, BigInteger
from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
from sqlalchemy.sql import func
from app.db.base import Base
from app.db.base_class import Base
class UserRole(str, enum.Enum):
admin = "admin"
@@ -18,23 +18,22 @@ class Person(Base):
__table_args__ = {"schema": "data"}
id = Column(BigInteger, primary_key=True, index=True)
id_uuid = Column(UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False)
id_uuid = Column(PG_UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False)
address_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.addresses.id"), nullable=True)
last_name = Column(String, nullable=False)
first_name = Column(String, nullable=False)
mothers_name = Column(String, nullable=True)
mothers_last_name = Column(String, nullable=True)
mothers_first_name = Column(String, nullable=True)
birth_place = Column(String, nullable=True)
birth_date = Column(DateTime, nullable=True)
phone = Column(String, nullable=True)
# JSONB mezők az okmányoknak és orvosi adatoknak
identity_docs = Column(JSON, server_default=text("'{}'::jsonb"))
medical_emergency = Column(JSON, server_default=text("'{}'::jsonb"))
ice_contact = Column(JSON, server_default=text("'{}'::jsonb"))
# KYC státusz
is_active = Column(Boolean, default=False, nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
@@ -47,28 +46,32 @@ class User(Base):
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True, nullable=False)
hashed_password = Column(String, nullable=True)
role = Column(Enum(UserRole), default=UserRole.user)
is_active = Column(Boolean, default=False)
region_code = Column(String, default="HU")
is_deleted = Column(Boolean, default=False)
person_id = Column(BigInteger, ForeignKey("data.persons.id"), nullable=True)
person = relationship("Person", back_populates="users")
wallet = relationship("Wallet", back_populates="user", uselist=False)
owned_organizations = relationship("Organization", back_populates="owner", lazy="select")
# Itt a trükk: csak a string hivatkozás marad, így nincs import hiba,
# de a SQLAlchemy tudni fogja, hogy a UserStats-ra gondolunk.
stats = relationship("UserStats", back_populates="user", uselist=False)
owned_organizations = relationship("Organization", back_populates="owner", lazy="select")
created_at = Column(DateTime(timezone=True), server_default=func.now())
class Wallet(Base):
"""Kétoszlopos pénztárca: Coin (Szerviz) és Kredit (Prémium)."""
__tablename__ = "wallets"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("data.users.id"), unique=True)
coin_balance = Column(Numeric(18, 2), default=0.00)
xp_balance = Column(Integer, default=0)
credit_balance = Column(Numeric(18, 2), default=0.00)
currency = Column(String(3), default="HUF")
user = relationship("User", back_populates="wallet")
@@ -77,9 +80,9 @@ class VerificationToken(Base):
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True, index=True)
token = Column(UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False)
token = Column(PG_UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False)
user_id = Column(Integer, ForeignKey("data.users.id", ondelete="CASCADE"), nullable=False)
token_type = Column(String(20), nullable=False) # 'registration' vagy 'password_reset'
token_type = Column(String(20), nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
expires_at = Column(DateTime(timezone=True), nullable=False)
is_used = Column(Boolean, default=False)

View File

@@ -1,9 +1,11 @@
import enum
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON
import uuid
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, text
from sqlalchemy.dialects.postgresql import ENUM as PG_ENUM
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.db.base import Base
from app.db.base_class import Base
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
class OrgType(str, enum.Enum):
individual = "individual"
@@ -19,18 +21,21 @@ class Organization(Base):
id = Column(Integer, primary_key=True, index=True)
# ÚJ MEZŐ: Egységes címkezelés (GeoService hibrid)
address_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.addresses.id"), nullable=True)
# --- NÉVKEZELÉS ---
full_name = Column(String, nullable=False) # Teljes hivatalos név
name = Column(String, nullable=False) # Rövidített cégnév (pl. ProfiBot Kft.)
display_name = Column(String(50)) # Alkalmazáson belüli rövidítés (pl. ProfiBot)
name = Column(String, nullable=False) # Rövidített cégnév
display_name = Column(String(50)) # Alkalmazáson belüli rövidítés
# --- ATOMIZÁLT CÍMKEZELÉS ---
# --- ATOMIZÁLT CÍMKEZELÉS (Kompatibilitási réteg) ---
address_zip = Column(String(10))
address_city = Column(String(100))
address_street_name = Column(String(150))
address_street_type = Column(String(50)) # utca, út, tér, dűlő, stb.
address_street_type = Column(String(50))
address_house_number = Column(String(20))
address_hrsz = Column(String(50)) # Helyrajzi szám
address_hrsz = Column(String(50))
address_stairwell = Column(String(20))
address_floor = Column(String(20))
address_door = Column(String(20))
@@ -40,7 +45,6 @@ class Organization(Base):
tax_number = Column(String(20), unique=True, index=True)
reg_number = Column(String(50))
# PG_ENUM használata a Python Enum-mal szinkronizálva
org_type = Column(
PG_ENUM(OrgType, name="orgtype", inherit_schema=True),
default=OrgType.individual
@@ -49,13 +53,8 @@ class Organization(Base):
status = Column(String(30), default="pending_verification")
is_deleted = Column(Boolean, default=False)
notification_settings = Column(JSON, default={
"notify_owner": True,
"notify_manager": True,
"notify_contact": True,
"alert_days_before": [30, 15, 7, 1]
})
external_integration_config = Column(JSON, default={})
notification_settings = Column(JSON, server_default=text("'{ \"notify_owner\": true, \"alert_days_before\": [30, 15, 7, 1] }'::jsonb"))
external_integration_config = Column(JSON, server_default=text("'{}'::jsonb"))
owner_id = Column(Integer, ForeignKey("data.users.id"), nullable=True)
is_active = Column(Boolean, default=True)
@@ -67,8 +66,8 @@ class Organization(Base):
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Kapcsolatok
assets = relationship("Asset", back_populates="organization", cascade="all, delete-orphan")
members = relationship("OrganizationMember", back_populates="organization")
assets = relationship("AssetAssignment", back_populates="organization", cascade="all, delete-orphan")
members = relationship("OrganizationMember", back_populates="organization", cascade="all, delete-orphan")
owner = relationship("User", back_populates="owned_organizations")
class OrganizationMember(Base):
@@ -77,9 +76,13 @@ class OrganizationMember(Base):
id = Column(Integer, primary_key=True, index=True)
organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=False)
user_id = Column(Integer, ForeignKey("data.users.id"), nullable=False)
role = Column(String, default="driver")
role = Column(String, default="driver") # owner, manager, driver, service_staff
# JAVÍTVA: Jogosultságok JSONB mezője (can_add_asset, etc.)
permissions = Column(JSON, server_default=text("'{}'::jsonb"))
organization = relationship("Organization", back_populates="members")
user = relationship("app.models.identity.User") # Visszamutató kapcsolat a felhasználóra
# Kompatibilitási réteg a korábbi kódokhoz
# Kompatibilitási réteg
Organization.vehicles = Organization.assets

View File

@@ -1,122 +0,0 @@
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, Numeric, DateTime, JSON, Date
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
import uuid
from app.db.base import Base
# 1. GLOBÁLIS KATALÓGUS (A rendszer agya)
class VehicleCatalog(Base):
__tablename__ = "vehicle_catalog"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True, index=True)
brand = Column(String(100), nullable=False)
model = Column(String(100), nullable=False)
generation = Column(String(100))
year_from = Column(Integer)
year_to = Column(Integer)
category = Column(String(50))
engine_type = Column(String(50))
engine_power_kw = Column(Integer)
# Robot státusz és gyári adatok
verification_status = Column(String(20), default="verified")
factory_specs = Column(JSON, default={})
maintenance_plan = Column(JSON, default={})
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Kapcsolat az egyedi példányok felé
assets = relationship("Asset", back_populates="catalog_entry")
# 2. EGYEDI ESZKÖZ (Asset) - A felhasználó tulajdona
class Asset(Base):
__tablename__ = "assets"
__table_args__ = {"schema": "data"}
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
uai = Column(String(100), unique=True, nullable=False) # VIN, HIN vagy Serial Number
organization_id = Column(Integer, ForeignKey("data.organizations.id"))
catalog_id = Column(Integer, ForeignKey("data.vehicle_catalog.id"), nullable=True)
asset_type = Column(String(20), nullable=False) # car, boat, plane
name = Column(String(255)) # "Kincses E39"
# Dinamikus állapot
current_plate_number = Column(String(20))
current_country_code = Column(String(2))
odometer_value = Column(Numeric(12, 2), default=0)
operating_hours = Column(Numeric(12, 2), default=0)
# Egyedi DNS (Gyári config + utólagos módosítások)
factory_config = Column(JSON, default={})
aftermarket_mods = Column(JSON, default={})
# Állapot és láthatóság (EZ HIÁNYZOTT)
status = Column(String(50), default="active")
privacy_level = Column(String(20), default="private")
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Kapcsolatok
organization = relationship("Organization", back_populates="assets")
catalog_entry = relationship("VehicleCatalog", back_populates="assets")
events = relationship("AssetEvent", back_populates="asset", cascade="all, delete-orphan")
ratings = relationship("AssetRating", back_populates="asset")
# 3. DIGITÁLIS SZERVIZKÖNYV / ESEMÉNYTÁR
class AssetEvent(Base):
__tablename__ = "asset_events"
__table_args__ = {"schema": "data"}
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
asset_id = Column(UUID(as_uuid=True), ForeignKey("data.assets.id"))
event_type = Column(String(50)) # SERVICE, REPAIR, INSPECTION, ACCIDENT, PLATE_CHANGE
odometer_at_event = Column(Numeric(12, 2))
hours_at_event = Column(Numeric(12, 2))
description = Column(String)
cost = Column(Numeric(12, 2))
currency = Column(String(3), default="EUR")
is_verified = Column(Boolean, default=False)
attachments = Column(JSON, default=[]) # Számlák, fotók linkjei
created_at = Column(DateTime(timezone=True), server_default=func.now())
asset = relationship("Asset", back_populates="events")
# 4. EMOCIONÁLIS ÉRTÉKELÉS
class AssetRating(Base):
__tablename__ = "asset_ratings"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
asset_id = Column(UUID(as_uuid=True), ForeignKey("data.assets.id"))
user_id = Column(Integer, ForeignKey("data.users.id"))
comfort_score = Column(Integer) # 1-10
experience_score = Column(Integer) # 1-10
practicality_score = Column(Integer) # 1-10
comment = Column(String)
created_at = Column(DateTime(timezone=True), server_default=func.now())
asset = relationship("Asset", back_populates="ratings")
# 5. SZOLGÁLTATÓK (Szerelők, Partnerek)
class ServiceProvider(Base):
__tablename__ = "service_providers"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True, index=True)
name = Column(String(255), nullable=False)
official_brand_partner = Column(Boolean, default=False)
technical_rating_pct = Column(Integer, default=80)
location_city = Column(String(100))
is_active = Column(Boolean, default=True)
# --- KOMPATIBILITÁSI RÉTEG ---
Vehicle = Asset
ServiceRecord = AssetEvent