# /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")