frontend költség beállítások

This commit is contained in:
Roo
2026-06-23 21:11:21 +00:00
parent 5b437b220d
commit 71ef33bb85
90 changed files with 11850 additions and 1053 deletions

View File

@@ -8,8 +8,6 @@ from .vehicle_definitions import (
)
from .vehicle import (
CostCategory,
VehicleCost,
VehicleUserRating,
GbCatalogDiscovery,
)
@@ -19,15 +17,17 @@ from .external_reference_queue import ExternalReferenceQueue
from .asset import (
Asset,
AssetCatalog,
AssetCost,
AssetEvent,
AssetFinancials,
AssetAssignment,
AssetTelemetry,
AssetReview,
ExchangeRate,
CatalogDiscovery,
VehicleOwnership,
OdometerReading,
AssetEventTypeEnum,
AssetEventStatusEnum,
VehicleTransferRequest,
)
from .history import AuditLog, LogSeverity
@@ -40,17 +40,14 @@ __all__ = [
"VehicleType",
"FeatureDefinition",
"ModelFeatureMap",
"CostCategory",
"VehicleCost",
"VehicleUserRating",
"GbCatalogDiscovery",
"ExternalReferenceLibrary",
"ExternalReferenceQueue",
"Asset",
"AssetCatalog",
"AssetCost",
"AssetEvent",
"AssetFinancials",
"AssetAssignment",
"AssetTelemetry",
"AssetReview",
"ExchangeRate",
@@ -58,8 +55,10 @@ __all__ = [
"VehicleOwnership",
"AuditLog",
"LogSeverity",
# --- EXPORT LISTA KIEGÉSZÍTÉSE ---
"OdometerReading",
"AssetEventTypeEnum",
"AssetEventStatusEnum",
"VehicleTransferRequest",
"MotorcycleSpecs",
"BodyTypeDictionary",
"OdometerReading",
]
]

View File

@@ -2,9 +2,9 @@
from __future__ import annotations
import uuid
import enum
from datetime import datetime
from datetime import datetime, date
from typing import List, Optional, TYPE_CHECKING
from sqlalchemy import String, Boolean, DateTime, ForeignKey, Numeric, text, Text, UniqueConstraint, CheckConstraint, BigInteger, Integer, Float
from sqlalchemy import String, Boolean, DateTime, ForeignKey, Numeric, text, Text, UniqueConstraint, CheckConstraint, BigInteger, Integer, Float, Date
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.dialects.postgresql import UUID as PG_UUID, JSONB
from sqlalchemy.sql import func
@@ -129,6 +129,46 @@ class Asset(Base):
is_under_warranty: Mapped[bool] = mapped_column(Boolean, default=False, server_default=text('false'))
warranty_expiry_date: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
# === REGISTRATION DOCUMENTS ===
registration_certificate_number: Mapped[Optional[str]] = mapped_column(
String(50), nullable=True, default=None,
comment="Forgalmi engedély száma"
)
registration_certificate_validity: Mapped[Optional[datetime]] = mapped_column(
DateTime(timezone=True), nullable=True, default=None,
comment="Forgalmi engedély érvényességi ideje"
)
vehicle_registration_document_number: Mapped[Optional[str]] = mapped_column(
String(50), nullable=True, default=None,
comment="Törzskönyv száma"
)
# === INTERNATIONALIZATION (ÚJ) ===
registration_country: Mapped[Optional[str]] = mapped_column(
String(2), nullable=True, index=True,
comment="Regisztráció országa (ISO 3166-1 alpha-2, pl. 'HU', 'SK', 'DE')"
)
first_domestic_registration_date: Mapped[Optional[date]] = mapped_column(
Date, nullable=True,
comment="Első belföldi forgalomba helyezés dátuma"
)
import_country: Mapped[Optional[str]] = mapped_column(
String(2), nullable=True,
comment="Importország (ISO 3166-1 alpha-2), ha importált jármű"
)
title_document_number: Mapped[Optional[str]] = mapped_column(
String(50), nullable=True,
comment="Törzskönyv szám (title document)"
)
engine_number: Mapped[Optional[str]] = mapped_column(
String(100), nullable=True,
comment="Motorszám"
)
number_of_previous_owners: Mapped[Optional[int]] = mapped_column(
Integer, nullable=True,
comment="Előző tulajdonosok száma"
)
# === SALES MODULE ===
is_for_sale: Mapped[bool] = mapped_column(Boolean, default=False, index=True)
price: Mapped[Optional[float]] = mapped_column(Numeric(15, 2))
@@ -147,7 +187,9 @@ class Asset(Base):
# --- KAPCSOLATOK ---
catalog: Mapped["AssetCatalog"] = relationship("AssetCatalog", back_populates="assets")
financials: Mapped[Optional["AssetFinancials"]] = relationship("AssetFinancials", back_populates="asset", uselist=False)
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")
@@ -229,71 +271,6 @@ class Asset(Base):
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 AssetCostStatusEnum(str, enum.Enum):
"""Költség státuszok a jóváhagyási munkafolyamathoz."""
DRAFT = "DRAFT"
PENDING_APPROVAL = "PENDING_APPROVAL"
APPROVED = "APPROVED"
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)
category_id: Mapped[int] = mapped_column(Integer, ForeignKey("vehicle.cost_categories.id"), index=True, nullable=False)
amount_net: Mapped[float] = mapped_column(Numeric(18, 2), nullable=False)
amount_gross: Mapped[Optional[float]] = mapped_column(Numeric(18, 2), nullable=True)
vat_rate: Mapped[Optional[float]] = mapped_column(Numeric(5, 2), nullable=True)
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)
document_id: Mapped[Optional[uuid.UUID]] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("system.documents.id"), nullable=True)
# Státusz a jóváhagyási munkafolyamathoz
status: Mapped[str] = mapped_column(String(30), default="DRAFT", server_default=text("'DRAFT'"), index=True)
# Kétirányú hivatkozás az AssetEvent felé (kettős könyvelés elkerülése)
linked_asset_event_id: Mapped[Optional[uuid.UUID]] = mapped_column(
PG_UUID(as_uuid=True),
ForeignKey("vehicle.asset_events.id"),
nullable=True,
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")
category: Mapped["CostCategory"] = relationship("CostCategory")
# Kapcsolat a hivatkozott eseményhez
linked_event: Mapped[Optional["AssetEvent"]] = relationship(
"AssetEvent",
foreign_keys=[linked_asset_event_id],
post_update=True
)
class VehicleLogbook(Base):
""" Útnyilvántartás (NAV, Kiküldetés, Munkábajárás). """
@@ -391,11 +368,14 @@ class OdometerReading(Base):
reading: Mapped[int] = mapped_column(Integer, nullable=False) # km óra állás
recorded_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), index=True)
source: Mapped[str] = mapped_column(String(30), default="manual") # manual, api, telemetry
cost_id: Mapped[Optional[uuid.UUID]] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.asset_costs.id"), nullable=True)
cost_id: Mapped[Optional[uuid.UUID]] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("fleet_finance.asset_costs.id"), nullable=True)
# Relationships
asset: Mapped["Asset"] = relationship("Asset", back_populates="odometer_readings")
cost: Mapped[Optional["AssetCost"]] = relationship("AssetCost")
cost: Mapped[Optional["AssetCost"]] = relationship(
"AssetCost",
primaryjoin="OdometerReading.cost_id == AssetCost.id"
)
class AssetAssignment(Base):
@@ -443,7 +423,7 @@ class AssetEvent(Base):
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)
cost_id: Mapped[Optional[uuid.UUID]] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("fleet_finance.asset_costs.id"), nullable=True)
# Státusz a feldolgozási munkafolyamathoz
status: Mapped[str] = mapped_column(String(30), default="DRAFT", server_default=text("'DRAFT'"), index=True)
@@ -451,7 +431,7 @@ class AssetEvent(Base):
# Kétirányú hivatkozás az AssetCost felé (kettős könyvelés elkerülése)
linked_expense_id: Mapped[Optional[uuid.UUID]] = mapped_column(
PG_UUID(as_uuid=True),
ForeignKey("vehicle.asset_costs.id"),
ForeignKey("fleet_finance.asset_costs.id"),
nullable=True,
index=True
)
@@ -511,23 +491,6 @@ class CatalogDiscovery(Base):
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."""

View File

@@ -1,8 +1,8 @@
# /opt/docker/dev/service_finder/backend/app/models/vehicle/vehicle.py
"""
TCO (Total Cost of Ownership) alapmodelljei a 'vehicle' sémában.
- CostCategory: Standardizált költségkategóriák hierarchiája
- VehicleCost: Járműhöz kapcsolódó tényleges költségnapló
- CostCategory és VehicleCost áthelyezve a fleet_finance sémába.
- Itt marad: VehicleUserRating, GbCatalogDiscovery
"""
from __future__ import annotations
@@ -16,99 +16,6 @@ from sqlalchemy.sql import func
from app.database import Base
class CostCategory(Base):
"""
Standardizált költségkategóriák hierarchikus fája.
Rendszerkategóriák (is_system=True) nem törölhetők, csak felhasználói kategóriák.
"""
__tablename__ = "cost_categories"
__table_args__ = {"schema": "vehicle", "extend_existing": True}
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
parent_id: Mapped[Optional[int]] = mapped_column(
Integer,
ForeignKey("vehicle.cost_categories.id", ondelete="SET NULL"),
nullable=True,
index=True
)
code: Mapped[str] = mapped_column(String(50), unique=True, index=True, nullable=False)
name: Mapped[str] = mapped_column(String(100), nullable=False)
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
is_system: Mapped[bool] = mapped_column(Boolean, default=False, server_default="false")
visibility: Mapped[str] = mapped_column(String(20), default="both", server_default="'both'")
min_tier: Mapped[str] = mapped_column(String(20), default="free", server_default="'free'") # free, premium, enterprise
accounting_code: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), onupdate=func.now(), server_default=func.now())
# Hierarchikus kapcsolatok
parent: Mapped[Optional["CostCategory"]] = relationship(
"CostCategory",
remote_side=[id],
back_populates="children",
foreign_keys=[parent_id]
)
children: Mapped[list["CostCategory"]] = relationship(
"CostCategory",
back_populates="parent",
foreign_keys=[parent_id]
)
# Kapcsolódó költségek
costs: Mapped[list["VehicleCost"]] = relationship("VehicleCost", back_populates="category")
def __repr__(self) -> str:
return f"CostCategory(id={self.id}, code='{self.code}', name='{self.name}')"
class VehicleCost(Base):
"""
Járműhöz kapcsolódó tényleges költségnapló.
Minden költséghez kötelező az odometer állás (km) és a dátum.
Az organization_id az Univerzális Flotta hivatkozás (fleet.organizations).
"""
__tablename__ = "costs"
__table_args__ = (
UniqueConstraint("vehicle_id", "category_id", "date", "odometer", name="uq_cost_unique_entry"),
{"schema": "vehicle"}
)
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
vehicle_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("vehicle.vehicle_model_definitions.id", ondelete="CASCADE"),
nullable=False,
index=True
)
organization_id: Mapped[Optional[int]] = mapped_column(
Integer,
ForeignKey("fleet.organizations.id", ondelete="SET NULL"),
nullable=True,
index=True
)
category_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("vehicle.cost_categories.id", ondelete="RESTRICT"),
nullable=False,
index=True
)
amount: Mapped[float] = mapped_column(Numeric(12, 2), nullable=False) # Összeg
currency: Mapped[str] = mapped_column(String(3), default="HUF", server_default="'HUF'") # ISO valutakód
odometer: Mapped[int] = mapped_column(Integer, nullable=False) # Kilométeróra állás (km)
date: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, index=True)
notes: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), onupdate=func.now(), server_default=func.now())
# Kapcsolatok
vehicle: Mapped["VehicleModelDefinition"] = relationship("VehicleModelDefinition", back_populates="costs")
organization: Mapped[Optional["Organization"]] = relationship("Organization", back_populates="vehicle_costs")
category: Mapped["CostCategory"] = relationship("CostCategory", back_populates="costs")
def __repr__(self) -> str:
return f"VehicleCost(id={self.id}, vehicle_id={self.vehicle_id}, amount={self.amount} {self.currency})"
class VehicleUserRating(Base):
"""
Jármű értékelési rendszer - User -> Vehicle kapcsolat.
@@ -173,4 +80,4 @@ class GbCatalogDiscovery(Base):
make: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
model: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
status: Mapped[str] = mapped_column(String(20), default='pending')
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())

View File

@@ -12,7 +12,7 @@ from app.database import Base
# Típus ellenőrzés a körkörös importok elkerülésére
if TYPE_CHECKING:
from .asset import AssetCatalog
from .vehicle import VehicleCost, VehicleUserRating
from .vehicle import VehicleUserRating
class VehicleType(Base):
""" Jármű kategóriák (pl. Személyautó, Motorkerékpár, Teherautó, Hajó) """
@@ -142,7 +142,6 @@ class VehicleModelDefinition(Base):
# JAVÍTÁS: Ez a sor hiányzott az API indításához!
ratings: Mapped[List["VehicleUserRating"]] = relationship("VehicleUserRating", back_populates="vehicle", cascade="all, delete-orphan")
costs: Mapped[List["VehicleCost"]] = relationship("VehicleCost", back_populates="vehicle", cascade="all, delete-orphan")
class ModelFeatureMap(Base):