frontend költség beállítások
This commit is contained in:
@@ -8,18 +8,24 @@ from .identity.identity import Person, User, Wallet, VerificationToken, SocialAc
|
||||
# 2. Szervezeti felépítés (MUST be before Address/Rating due to FK references)
|
||||
from .marketplace.organization import Organization, OrganizationMember, OrganizationFinancials, OrganizationSalesAssignment, OrgType, OrgUserRole, OrgRole, Branch
|
||||
|
||||
# 2b. B2B kapcsolatok és kapcsolattartók (fleet schema)
|
||||
from .fleet import OrganizationRelationship, ContactPerson
|
||||
|
||||
# 3. Földrajzi adatok és címek
|
||||
from .identity.address import Address, GeoPostalCode, GeoStreet, GeoStreetType, Rating
|
||||
|
||||
# 4. Jármű definíciók
|
||||
from .vehicle.vehicle_definitions import VehicleModelDefinition, VehicleType, FeatureDefinition, ModelFeatureMap
|
||||
from .reference_data import ReferenceLookup
|
||||
from .vehicle.vehicle import CostCategory, VehicleCost, GbCatalogDiscovery
|
||||
from .vehicle.vehicle import VehicleUserRating, GbCatalogDiscovery
|
||||
from .vehicle.external_reference import ExternalReferenceLibrary
|
||||
from .vehicle.external_reference_queue import ExternalReferenceQueue
|
||||
|
||||
# 5. Eszközök és katalógusok
|
||||
from .vehicle.asset import Asset, AssetCatalog, AssetCost, AssetEvent, AssetAssignment, AssetFinancials, AssetTelemetry, AssetReview, ExchangeRate, CatalogDiscovery, VehicleOwnership, OdometerReading
|
||||
from .vehicle.asset import Asset, AssetCatalog, AssetEvent, AssetAssignment, AssetTelemetry, AssetReview, ExchangeRate, CatalogDiscovery, VehicleOwnership, OdometerReading
|
||||
|
||||
# 5b. Flotta pénzügyek (fleet_finance schema - áthelyezve vehicle-ból)
|
||||
from .fleet_finance import AssetCost, CostCategory, AssetFinancials, InsuranceProvider, VehicleInsurancePolicy, VehicleTaxObligation
|
||||
|
||||
# 6. Üzleti logika és előfizetések
|
||||
from .core_logic import SubscriptionTier, OrganizationSubscription, CreditTransaction, ServiceSpecialty
|
||||
@@ -27,7 +33,6 @@ from .marketplace.payment import PaymentIntent, PaymentIntentStatus
|
||||
from .marketplace.finance import Issuer, IssuerType
|
||||
|
||||
# 7. Szolgáltatások és staging
|
||||
# JAVÍTVA: ServiceStaging és társai a staged_data-ból jönnek!
|
||||
from .marketplace.service import ServiceProfile, ExpertiseTag, ServiceExpertise
|
||||
from .marketplace.staged_data import ServiceStaging, DiscoveryParameter, StagedVehicleData
|
||||
from .marketplace.service_request import ServiceRequest
|
||||
@@ -38,12 +43,10 @@ from .identity.social import ServiceProvider, Vote, Competition, UserScore, Serv
|
||||
# 9. Rendszer, Gamification és egyebek
|
||||
from .gamification.gamification import PointRule, LevelConfig, UserStats, Badge, UserBadge, PointsLedger, UserContribution, Season
|
||||
|
||||
# --- 2.2 ÚJDONSÁG: InternalNotification hozzáadása ---
|
||||
from .system.system import SystemParameter, ParameterScope, InternalNotification, SystemServiceStaging
|
||||
|
||||
from .system.document import Document
|
||||
from .system.translation import Translation
|
||||
# Direct import from audit module
|
||||
from .system.audit import SecurityAuditLog, OperationalLog, ProcessLog, FinancialLedger, WalletType, LedgerStatus, LedgerEntryType
|
||||
from .vehicle.history import AuditLog, LogSeverity
|
||||
from .identity.security import PendingAction, ActionStatus
|
||||
@@ -67,30 +70,30 @@ ServiceRecord = AssetEvent
|
||||
__all__ = [
|
||||
"Base", "User", "Person", "Wallet", "UserRole", "VerificationToken", "SocialAccount",
|
||||
"Organization", "OrganizationMember", "OrganizationSalesAssignment", "OrgType", "OrgUserRole", "OrgRole",
|
||||
"OrganizationRelationship", "ContactPerson",
|
||||
"Asset", "AssetCatalog", "AssetCost", "AssetEvent", "AssetAssignment", "AssetFinancials",
|
||||
"AssetTelemetry", "AssetReview", "ExchangeRate", "CatalogDiscovery", "OdometerReading",
|
||||
"Address", "GeoPostalCode", "GeoStreet", "GeoStreetType", "Branch",
|
||||
"PointRule", "LevelConfig", "UserStats", "Badge", "UserBadge", "Rating", "PointsLedger", "UserContribution",
|
||||
|
||||
# --- 2.2 ÚJDONSÁG KIEGÉSZÍTÉS ---
|
||||
"SystemParameter", "ParameterScope", "InternalNotification",
|
||||
|
||||
# Social models (Social 3)
|
||||
"ServiceProvider", "Vote", "Competition", "UserScore", "ServiceReview", "ModerationStatus", "SourceType",
|
||||
|
||||
"Document", "Translation", "PendingAction", "ActionStatus",
|
||||
"SubscriptionTier", "OrganizationSubscription", "CreditTransaction", "ServiceSpecialty",
|
||||
"PaymentIntent", "PaymentIntentStatus",
|
||||
"AuditLog", "VehicleOwnership", "LogSeverity",
|
||||
"SecurityAuditLog", "OperationalLog", "ProcessLog",
|
||||
"SecurityAuditLog", "OperationalLog", "ProcessLog",
|
||||
"FinancialLedger", "WalletType", "LedgerStatus", "LedgerEntryType",
|
||||
"ServiceProfile", "ExpertiseTag", "ServiceExpertise", "ServiceStaging", "DiscoveryParameter", "ServiceRequest",
|
||||
"Vehicle", "UserVehicle", "VehicleCatalog", "ServiceRecord", "VehicleModelDefinition", "ReferenceLookup",
|
||||
"VehicleType", "FeatureDefinition", "ModelFeatureMap", "LegalDocument", "LegalAcceptance",
|
||||
"Location", "LocationType", "Issuer", "IssuerType", "CostCategory", "VehicleCost", "ExternalReferenceLibrary", "ExternalReferenceQueue",
|
||||
"Location", "LocationType", "Issuer", "IssuerType", "CostCategory", "ExternalReferenceLibrary", "ExternalReferenceQueue",
|
||||
"GbCatalogDiscovery", "Season", "StagedVehicleData", "OneTimePassword",
|
||||
"InsuranceProvider", "VehicleInsurancePolicy", "VehicleTaxObligation",
|
||||
# Marketing / Ad Engine
|
||||
"Campaign", "Creative", "Placement", "CampaignCreative",
|
||||
"CampaignPlacement", "AdImpression", "AdClick",
|
||||
"CampaignStatus", "CreativeType", "PlacementType",
|
||||
]
|
||||
]
|
||||
|
||||
13
backend/app/models/fleet/__init__.py
Normal file
13
backend/app/models/fleet/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/fleet/__init__.py
|
||||
"""
|
||||
fleet schema: B2B kapcsolatok és kapcsolattartók.
|
||||
- OrganizationRelationship: Két szervezet közötti B2B kapcsolat (vevő, beszállító, partner)
|
||||
- ContactPerson: Szervezethez tartozó kapcsolattartó személy
|
||||
"""
|
||||
|
||||
from .organization import OrganizationRelationship, ContactPerson
|
||||
|
||||
__all__ = [
|
||||
"OrganizationRelationship",
|
||||
"ContactPerson",
|
||||
]
|
||||
146
backend/app/models/fleet/organization.py
Normal file
146
backend/app/models/fleet/organization.py
Normal file
@@ -0,0 +1,146 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/fleet/organization.py
|
||||
"""
|
||||
fleet schema: B2B kapcsolatok és kapcsolattartók.
|
||||
- OrganizationRelationship: Két szervezet közötti B2B kapcsolat (vevő, beszállító, partner)
|
||||
- ContactPerson: Szervezethez tartozó kapcsolattartó személy
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from sqlalchemy import (
|
||||
String, Boolean, DateTime, ForeignKey, Numeric, Text, Integer, BigInteger, text
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from app.database import Base
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.models.marketplace.organization import Organization
|
||||
from app.models.identity.identity import Person
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# OrganizationRelationship
|
||||
# =============================================================================
|
||||
|
||||
class OrganizationRelationship(Base):
|
||||
"""
|
||||
Két szervezet közötti B2B kapcsolat.
|
||||
Lehetővé teszi a vevő-beszállító, partneri kapcsolatok nyilvántartását
|
||||
anélkül, hogy a szervezet rekordokat duplikálni kellene.
|
||||
"""
|
||||
__tablename__ = "org_relationships"
|
||||
__table_args__ = {"schema": "fleet"}
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
||||
|
||||
# A kapcsolat két oldala
|
||||
source_org_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("fleet.organizations.id"), nullable=False, index=True
|
||||
)
|
||||
target_org_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("fleet.organizations.id"), nullable=False, index=True
|
||||
)
|
||||
|
||||
# Kapcsolat típusa
|
||||
relationship_type: Mapped[str] = mapped_column(
|
||||
String(30), nullable=False, comment="CUSTOMER, SUPPLIER, PARTNER"
|
||||
)
|
||||
|
||||
# Státusz
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(20), default="ACTIVE", server_default=text("'ACTIVE'")
|
||||
)
|
||||
|
||||
# Szerződéses adatok
|
||||
contract_ref: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
valid_from: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
valid_until: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# Pénzügyi feltételek
|
||||
credit_limit: Mapped[Optional[float]] = mapped_column(Numeric(18, 2), nullable=True)
|
||||
payment_terms: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
|
||||
|
||||
# Megjegyzések
|
||||
notes: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
|
||||
# Időbélyegek
|
||||
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(), server_default=func.now()
|
||||
)
|
||||
|
||||
# Kapcsolatok
|
||||
source_org: Mapped["Organization"] = relationship(
|
||||
"Organization", foreign_keys=[source_org_id]
|
||||
)
|
||||
target_org: Mapped["Organization"] = relationship(
|
||||
"Organization", foreign_keys=[target_org_id]
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"OrganizationRelationship(id={self.id}, "
|
||||
f"source={self.source_org_id}, target={self.target_org_id}, "
|
||||
f"type='{self.relationship_type}')"
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# ContactPerson
|
||||
# =============================================================================
|
||||
|
||||
class ContactPerson(Base):
|
||||
"""
|
||||
Kapcsolattartó személy egy szervezethez.
|
||||
A személy rekord az identity.persons táblában él,
|
||||
ez a modell csak a szervezeti kapcsolatot és szerepkört tárolja.
|
||||
"""
|
||||
__tablename__ = "contact_persons"
|
||||
__table_args__ = {"schema": "fleet"}
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
||||
|
||||
# Melyik szervezethez tartozik
|
||||
organization_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("fleet.organizations.id"), nullable=False, index=True
|
||||
)
|
||||
|
||||
# Hivatkozás a személy rekordra (identity sémában)
|
||||
person_id: Mapped[int] = mapped_column(
|
||||
BigInteger, ForeignKey("identity.persons.id"), nullable=False, index=True
|
||||
)
|
||||
|
||||
# Szerepkör a szervezetben (pl. "CEO", "Fleet Manager", "Accountant")
|
||||
role: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||
|
||||
# Elsődleges kapcsolattartó?
|
||||
is_primary: Mapped[bool] = mapped_column(Boolean, default=False, server_default=text("false"))
|
||||
|
||||
# Részleg
|
||||
department: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
|
||||
# Megjegyzések
|
||||
notes: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
|
||||
# Időbélyegek
|
||||
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(), server_default=func.now()
|
||||
)
|
||||
|
||||
# Kapcsolatok
|
||||
organization: Mapped["Organization"] = relationship(
|
||||
"Organization", foreign_keys=[organization_id]
|
||||
)
|
||||
person: Mapped["Person"] = relationship(
|
||||
"Person", foreign_keys=[person_id]
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"ContactPerson(id={self.id}, org_id={self.organization_id}, "
|
||||
f"person_id={self.person_id}, role='{self.role}')"
|
||||
)
|
||||
25
backend/app/models/fleet_finance/__init__.py
Normal file
25
backend/app/models/fleet_finance/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/fleet_finance/__init__.py
|
||||
"""
|
||||
fleet_finance schema: Flotta pénzügyi adatok (költségek, finanszírozás, biztosítás, adók).
|
||||
Áthelyezve a vehicle sémából: AssetCost, CostCategory, AssetFinancials.
|
||||
"""
|
||||
|
||||
from .models import (
|
||||
AssetCost,
|
||||
AssetCostStatusEnum,
|
||||
CostCategory,
|
||||
AssetFinancials,
|
||||
InsuranceProvider,
|
||||
VehicleInsurancePolicy,
|
||||
VehicleTaxObligation,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AssetCost",
|
||||
"AssetCostStatusEnum",
|
||||
"CostCategory",
|
||||
"AssetFinancials",
|
||||
"InsuranceProvider",
|
||||
"VehicleInsurancePolicy",
|
||||
"VehicleTaxObligation",
|
||||
]
|
||||
316
backend/app/models/fleet_finance/models.py
Normal file
316
backend/app/models/fleet_finance/models.py
Normal file
@@ -0,0 +1,316 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/fleet_finance/models.py
|
||||
"""
|
||||
fleet_finance schema: Flotta pénzügyi adatok.
|
||||
- CostCategory: Standardizált költségkategóriák hierarchiája (áthelyezve vehicle -> fleet_finance)
|
||||
- AssetCost: Eszközhöz kapcsolódó tényleges költségnapló (áthelyezve vehicle -> fleet_finance)
|
||||
- AssetFinancials: Beszerzés és értékcsökkenés (áthelyezve vehicle -> fleet_finance, kibővítve finanszírozási adatokkal)
|
||||
- InsuranceProvider: Biztosítók katalógusa (ÚJ)
|
||||
- VehicleInsurancePolicy: Jármű biztosítási kötvények (ÚJ)
|
||||
- VehicleTaxObligation: Jármű adókötelezettségek (ÚJ)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
import uuid
|
||||
import enum
|
||||
from datetime import datetime, date
|
||||
from typing import Optional, List, TYPE_CHECKING
|
||||
from sqlalchemy import (
|
||||
String, Boolean, DateTime, ForeignKey, Numeric, text, Text,
|
||||
UniqueConstraint, Integer, Date
|
||||
)
|
||||
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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.models.vehicle.asset import Asset
|
||||
from app.models.marketplace.organization import Organization
|
||||
from app.models.system.document import Document
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# CostCategory (áthelyezve vehicle -> fleet_finance)
|
||||
# =============================================================================
|
||||
|
||||
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.
|
||||
Áthelyezve a vehicle sémából a fleet_finance sémába.
|
||||
"""
|
||||
__tablename__ = "cost_categories"
|
||||
__table_args__ = {"schema": "fleet_finance", "extend_existing": True}
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
||||
parent_id: Mapped[Optional[int]] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("fleet_finance.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'")
|
||||
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 (AssetCost)
|
||||
asset_costs: Mapped[list["AssetCost"]] = relationship("AssetCost", back_populates="category")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"CostCategory(id={self.id}, code='{self.code}', name='{self.name}')"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# AssetCostStatusEnum
|
||||
# =============================================================================
|
||||
|
||||
class AssetCostStatusEnum(str, enum.Enum):
|
||||
"""Költség státuszok a jóváhagyási munkafolyamathoz."""
|
||||
DRAFT = "DRAFT"
|
||||
PENDING_APPROVAL = "PENDING_APPROVAL"
|
||||
APPROVED = "APPROVED"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# AssetCost (áthelyezve vehicle -> fleet_finance)
|
||||
# =============================================================================
|
||||
|
||||
class AssetCost(Base):
|
||||
""" II. Üzemeltetés és TCO kimutatás. Áthelyezve a vehicle sémából. """
|
||||
__tablename__ = "asset_costs"
|
||||
__table_args__ = {"schema": "fleet_finance"}
|
||||
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("fleet_finance.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
|
||||
)
|
||||
|
||||
# === BESZÁLLÍTÓI HIVATKOZÁSOK (B2B expansion) ===
|
||||
# Hivatkozás a fleet.organizations táblában lévő beszállítóra
|
||||
vendor_organization_id: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, ForeignKey("fleet.organizations.id"), nullable=True, index=True
|
||||
)
|
||||
# Szabadon gépelhető beszállító név (ha nincs a rendszerben)
|
||||
external_vendor_name: Mapped[Optional[str]] = mapped_column(String(200), nullable=True)
|
||||
|
||||
# === KÖNYVELÉSI DÁTUMOK ===
|
||||
invoice_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
|
||||
fulfillment_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
|
||||
|
||||
data: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb"))
|
||||
asset: Mapped["Asset"] = relationship("Asset", back_populates="costs")
|
||||
organization: Mapped["Organization"] = relationship("Organization", foreign_keys=[organization_id])
|
||||
category: Mapped["CostCategory"] = relationship("CostCategory", back_populates="asset_costs")
|
||||
|
||||
# Kapcsolat a hivatkozott eseményhez
|
||||
linked_event: Mapped[Optional["AssetEvent"]] = relationship(
|
||||
"AssetEvent",
|
||||
foreign_keys=[linked_asset_event_id],
|
||||
post_update=True
|
||||
)
|
||||
|
||||
# Beszállító kapcsolat
|
||||
vendor_organization: Mapped[Optional["Organization"]] = relationship(
|
||||
"Organization", foreign_keys=[vendor_organization_id]
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# AssetFinancials (áthelyezve vehicle -> fleet_finance, kibővítve)
|
||||
# =============================================================================
|
||||
|
||||
class AssetFinancials(Base):
|
||||
"""
|
||||
I. Beszerzés és IV. Értékcsökkenés (Amortizáció).
|
||||
Áthelyezve a vehicle sémából a fleet_finance sémába.
|
||||
Kibővítve finanszírozási adatokkal (lízing, hitel).
|
||||
"""
|
||||
__tablename__ = "asset_financials"
|
||||
__table_args__ = {"schema": "fleet_finance"}
|
||||
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)
|
||||
|
||||
# === BESZERZÉS ===
|
||||
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"))
|
||||
|
||||
# === FINANSZÍROZÁS (ÚJ) ===
|
||||
down_payment: Mapped[Optional[float]] = mapped_column(Numeric(18, 2), nullable=True)
|
||||
monthly_installment: Mapped[Optional[float]] = mapped_column(Numeric(18, 2), nullable=True)
|
||||
residual_value: Mapped[Optional[float]] = mapped_column(Numeric(18, 2), nullable=True)
|
||||
contract_number: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
financing_provider: Mapped[Optional[str]] = mapped_column(String(200), nullable=True)
|
||||
interest_rate: Mapped[Optional[float]] = mapped_column(Numeric(5, 2), nullable=True)
|
||||
total_contract_value: Mapped[Optional[float]] = mapped_column(Numeric(18, 2), nullable=True)
|
||||
lease_start_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
|
||||
lease_end_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
|
||||
|
||||
# === KAPCSOLATOK ===
|
||||
asset: Mapped["Asset"] = relationship("Asset", back_populates="financials")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# InsuranceProvider (ÚJ)
|
||||
# =============================================================================
|
||||
|
||||
class InsuranceProvider(Base):
|
||||
"""
|
||||
Biztosítók katalógusa.
|
||||
Tárolja a biztosítók elérhetőségeit és szolgáltatásait.
|
||||
"""
|
||||
__tablename__ = "insurance_providers"
|
||||
__table_args__ = {"schema": "fleet_finance"}
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
||||
name: Mapped[str] = mapped_column(String(150), nullable=False, index=True)
|
||||
country_code: Mapped[Optional[str]] = mapped_column(String(2), index=True, nullable=True, comment="ISO 3166-1 alpha-2 országkód (pl. 'HU'). Ha NULL, akkor globális.")
|
||||
claim_phone: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
|
||||
claim_url: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
||||
services_offered: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb"))
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, server_default=text("true"))
|
||||
|
||||
# Kapcsolatok
|
||||
policies: Mapped[List["VehicleInsurancePolicy"]] = relationship(
|
||||
"VehicleInsurancePolicy", back_populates="provider"
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"InsuranceProvider(id={self.id}, name='{self.name}')"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# VehicleInsurancePolicy (ÚJ)
|
||||
# =============================================================================
|
||||
|
||||
class VehicleInsurancePolicy(Base):
|
||||
"""
|
||||
Jármű biztosítási kötvények.
|
||||
Minden kötvény egy járműhöz (asset) és egy biztosítóhoz (provider) tartozik.
|
||||
"""
|
||||
__tablename__ = "vehicle_insurance_policies"
|
||||
__table_args__ = {"schema": "fleet_finance"}
|
||||
|
||||
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
|
||||
)
|
||||
provider_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("fleet_finance.insurance_providers.id"), nullable=False, index=True
|
||||
)
|
||||
|
||||
insurance_type: Mapped[str] = mapped_column(
|
||||
String(20), nullable=False, comment="KGFB, CASCO"
|
||||
)
|
||||
policy_number: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
|
||||
start_date: Mapped[date] = mapped_column(Date, nullable=False)
|
||||
expiry_date: Mapped[date] = mapped_column(Date, nullable=False)
|
||||
premium_amount: Mapped[float] = mapped_column(Numeric(18, 2), nullable=False)
|
||||
currency: Mapped[str] = mapped_column(String(3), default="HUF")
|
||||
|
||||
# Dokumentum kapcsolat (PDF kötvény)
|
||||
document_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
||||
PG_UUID(as_uuid=True), ForeignKey("system.documents.id"), nullable=True
|
||||
)
|
||||
|
||||
# Kapcsolatok
|
||||
asset: Mapped["Asset"] = relationship("Asset")
|
||||
provider: Mapped["InsuranceProvider"] = relationship(
|
||||
"InsuranceProvider", back_populates="policies"
|
||||
)
|
||||
document: Mapped[Optional["Document"]] = relationship("Document")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"VehicleInsurancePolicy(id={self.id}, asset_id={self.asset_id}, "
|
||||
f"type='{self.insurance_type}', policy='{self.policy_number}')"
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# VehicleTaxObligation (ÚJ)
|
||||
# =============================================================================
|
||||
|
||||
class VehicleTaxObligation(Base):
|
||||
"""
|
||||
Jármű adókötelezettségek.
|
||||
Tárolja a különböző típusú adókat (súlyadó, cégautóadó, stb.).
|
||||
"""
|
||||
__tablename__ = "vehicle_tax_obligations"
|
||||
__table_args__ = {"schema": "fleet_finance"}
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
tax_type: Mapped[str] = mapped_column(
|
||||
String(30), nullable=False, comment="WEIGHT_TAX, COMPANY_CAR_TAX"
|
||||
)
|
||||
tax_year: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
|
||||
amount: Mapped[float] = mapped_column(Numeric(18, 2), nullable=False)
|
||||
currency: Mapped[str] = mapped_column(String(3), default="HUF")
|
||||
due_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
|
||||
payment_status: Mapped[str] = mapped_column(
|
||||
String(20), default="pending", server_default=text("'pending'")
|
||||
)
|
||||
|
||||
# Dokumentum kapcsolat (PDF adóbevallás)
|
||||
document_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
||||
PG_UUID(as_uuid=True), ForeignKey("system.documents.id"), nullable=True
|
||||
)
|
||||
|
||||
# Kapcsolatok
|
||||
asset: Mapped["Asset"] = relationship("Asset")
|
||||
document: Mapped[Optional["Document"]] = relationship("Document")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"VehicleTaxObligation(id={self.id}, asset_id={self.asset_id}, "
|
||||
f"type='{self.tax_type}', year={self.tax_year})"
|
||||
)
|
||||
@@ -139,6 +139,7 @@ class Organization(Base):
|
||||
|
||||
# ── 📦 ELŐFIZETÉS / SUBSCRIPTION ──
|
||||
subscription_plan: Mapped[str] = mapped_column(String(30), server_default=text("'FREE'"), index=True)
|
||||
subscription_expires_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
base_asset_limit: Mapped[int] = mapped_column(Integer, server_default=text("1"))
|
||||
purchased_extra_slots: Mapped[int] = mapped_column(Integer, server_default=text("0"))
|
||||
|
||||
@@ -208,8 +209,6 @@ class Organization(Base):
|
||||
# Kapcsolat az örök személy rekordhoz
|
||||
legal_owner: Mapped[Optional["Person"]] = relationship("Person", back_populates="owned_business_entities")
|
||||
|
||||
# Kapcsolat a jármű költségekhez (TCO rendszer)
|
||||
vehicle_costs: Mapped[List["VehicleCost"]] = relationship("VehicleCost", back_populates="organization")
|
||||
|
||||
class OrganizationFinancials(Base):
|
||||
__tablename__ = "organization_financials"
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
]
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user