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,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",
]
]

View 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",
]

View 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}')"
)

View 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",
]

View 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})"
)

View File

@@ -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"

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):