feat: implement pivot-currency model, rbac smart tokens & fix circular imports
This commit is contained in:
@@ -1,37 +1,32 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/__init__.py
|
||||
from app.db.base_class import Base
|
||||
|
||||
from .identity import User, Person, Wallet, UserRole, VerificationToken
|
||||
from .organization import Organization, OrganizationMember
|
||||
from .asset import Asset, AssetCatalog, AssetCost, AssetEvent
|
||||
from .asset import (
|
||||
Asset, AssetCatalog, AssetCost, AssetEvent,
|
||||
AssetFinancials, AssetTelemetry, AssetReview, ExchangeRate
|
||||
)
|
||||
from .address import Address, GeoPostalCode, GeoStreet, GeoStreetType
|
||||
from .gamification import UserStats, PointsLedger
|
||||
from .gamification import PointRule, LevelConfig, UserStats, Badge, UserBadge, Rating, PointsLedger
|
||||
from .system_config import SystemParameter
|
||||
from .document import Document
|
||||
from .core_logic import SubscriptionTier, OrganizationSubscription, CreditTransaction, ServiceSpecialty
|
||||
from .history import AuditLog, VehicleOwnership
|
||||
|
||||
# Aliasok a kompatibilitás és a tiszta kód érdekében
|
||||
# Aliasok
|
||||
Vehicle = Asset
|
||||
UserVehicle = Asset
|
||||
VehicleCatalog = AssetCatalog
|
||||
ServiceRecord = AssetEvent
|
||||
|
||||
__all__ = [
|
||||
"Base",
|
||||
"User",
|
||||
"Person",
|
||||
"Wallet",
|
||||
"UserRole",
|
||||
"VerificationToken",
|
||||
"Organization",
|
||||
"OrganizationMember",
|
||||
"Asset",
|
||||
"AssetCatalog",
|
||||
"AssetCost",
|
||||
"AssetEvent",
|
||||
"Address",
|
||||
"GeoPostalCode",
|
||||
"GeoStreet",
|
||||
"GeoStreetType",
|
||||
"UserStats",
|
||||
"PointsLedger",
|
||||
"Vehicle",
|
||||
"UserVehicle",
|
||||
"VehicleCatalog",
|
||||
"ServiceRecord"
|
||||
"Base", "User", "Person", "Wallet", "UserRole", "VerificationToken",
|
||||
"Organization", "OrganizationMember", "Asset", "AssetCatalog", "AssetCost",
|
||||
"AssetEvent", "AssetFinancials", "AssetTelemetry", "AssetReview", "ExchangeRate",
|
||||
"Address", "GeoPostalCode", "GeoStreet", "GeoStreetType", "PointRule",
|
||||
"LevelConfig", "UserStats", "Badge", "UserBadge", "Rating", "PointsLedger",
|
||||
"SystemParameter", "Document", "SubscriptionTier", "OrganizationSubscription",
|
||||
"CreditTransaction", "ServiceSpecialty", "AuditLog", "VehicleOwnership",
|
||||
"Vehicle", "UserVehicle", "VehicleCatalog", "ServiceRecord"
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
backend/app/models/__pycache__/core_logic.cpython-312.pyc
Normal file
BIN
backend/app/models/__pycache__/core_logic.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
backend/app/models/__pycache__/history.cpython-312.pyc
Normal file
BIN
backend/app/models/__pycache__/history.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
backend/app/models/__pycache__/system_config.cpython-312.pyc
Normal file
BIN
backend/app/models/__pycache__/system_config.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
@@ -1,133 +1,128 @@
|
||||
import uuid
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, Numeric, text
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, Numeric, text, Text
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base_class import Base
|
||||
|
||||
class AssetCatalog(Base):
|
||||
"""Központi jármű katalógus (Admin/Bot által tölthető)"""
|
||||
__tablename__ = "vehicle_catalog"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
make = Column(String, index=True, nullable=False)
|
||||
model = Column(String, index=True, nullable=False)
|
||||
generation = Column(String)
|
||||
year_from = Column(Integer)
|
||||
year_to = Column(Integer)
|
||||
vehicle_class = Column(String) # land, sea, air
|
||||
vehicle_class = Column(String)
|
||||
fuel_type = Column(String)
|
||||
engine_code = Column(String)
|
||||
|
||||
factory_data = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
assets = relationship("Asset", back_populates="catalog")
|
||||
|
||||
class Asset(Base):
|
||||
"""A Jármű Identitás (Digital Twin törzsadatok)"""
|
||||
__tablename__ = "assets"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
vin = Column(String(17), unique=True, index=True, nullable=False)
|
||||
license_plate = Column(String(20), index=True)
|
||||
name = Column(String)
|
||||
year_of_manufacture = Column(Integer)
|
||||
|
||||
catalog_id = Column(Integer, ForeignKey("data.vehicle_catalog.id"))
|
||||
|
||||
# Nemzetközi mutatók
|
||||
quality_index = Column(Numeric(3, 2), default=1.00)
|
||||
system_mileage = Column(Integer, default=0)
|
||||
mileage_unit = Column(String(10), default="km") # Nemzetközi: km, miles, hours
|
||||
|
||||
is_verified = Column(Boolean, default=False)
|
||||
status = Column(String(20), default="active")
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
catalog = relationship("AssetCatalog", back_populates="assets")
|
||||
financials = relationship("AssetFinancials", back_populates="asset", uselist=False)
|
||||
telemetry = relationship("AssetTelemetry", back_populates="asset", uselist=False)
|
||||
assignments = relationship("AssetAssignment", back_populates="asset")
|
||||
events = relationship("AssetEvent", back_populates="asset")
|
||||
costs = relationship("AssetCost", back_populates="asset")
|
||||
reviews = relationship("AssetReview", back_populates="asset")
|
||||
ownership_history = relationship("VehicleOwnership", back_populates="vehicle")
|
||||
|
||||
class AssetFinancials(Base):
|
||||
__tablename__ = "asset_financials"
|
||||
__table_args__ = {"schema": "data"}
|
||||
id = Column(Integer, primary_key=True)
|
||||
asset_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), unique=True)
|
||||
acquisition_price = Column(Numeric(18, 2))
|
||||
acquisition_date = Column(DateTime)
|
||||
financing_type = Column(String)
|
||||
residual_value_estimate = Column(Numeric(18, 2))
|
||||
asset = relationship("Asset", back_populates="financials")
|
||||
|
||||
class AssetTelemetry(Base):
|
||||
__tablename__ = "asset_telemetry"
|
||||
__table_args__ = {"schema": "data"}
|
||||
id = Column(Integer, primary_key=True)
|
||||
asset_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), unique=True)
|
||||
current_mileage = Column(Integer, default=0)
|
||||
mileage_unit = Column(String(10), default="km")
|
||||
vqi_score = Column(Numeric(5, 2), default=100.00)
|
||||
dbs_score = Column(Numeric(5, 2), default=100.00)
|
||||
asset = relationship("Asset", back_populates="telemetry")
|
||||
|
||||
class AssetReview(Base):
|
||||
__tablename__ = "asset_reviews"
|
||||
__table_args__ = {"schema": "data"}
|
||||
id = Column(Integer, primary_key=True)
|
||||
asset_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), nullable=False)
|
||||
user_id = Column(Integer, ForeignKey("data.users.id"), nullable=False)
|
||||
overall_rating = Column(Integer)
|
||||
criteria_scores = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
comment = Column(Text)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
asset = relationship("Asset", back_populates="reviews")
|
||||
|
||||
class AssetAssignment(Base):
|
||||
"""Birtoklás követése (Kié a jármű és mettől meddig)"""
|
||||
__tablename__ = "asset_assignments"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
asset_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), nullable=False)
|
||||
organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=False)
|
||||
|
||||
assigned_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
released_at = Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
status = Column(String(30), default="active")
|
||||
notes = Column(String)
|
||||
|
||||
asset = relationship("Asset", back_populates="assignments")
|
||||
organization = relationship("Organization", back_populates="assets")
|
||||
|
||||
class AssetEvent(Base):
|
||||
"""Élettörténeti események (Szerviz, km-óra állások, balesetek)"""
|
||||
__tablename__ = "asset_events"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
asset_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), nullable=False)
|
||||
|
||||
event_type = Column(String(50), nullable=False)
|
||||
event_date = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
event_type = Column(String(50), nullable=False)
|
||||
recorded_mileage = Column(Integer)
|
||||
description = Column(String)
|
||||
data = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
|
||||
asset = relationship("Asset", back_populates="events")
|
||||
|
||||
class AssetCost(Base):
|
||||
"""
|
||||
Költségkezelő modell: Bruttó/Nettó/ÁFA és deviza támogatás.
|
||||
"""
|
||||
__tablename__ = "asset_costs"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
asset_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), nullable=False)
|
||||
organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=False)
|
||||
|
||||
cost_type = Column(String(50), nullable=False) # fuel, service, tax, insurance, toll
|
||||
|
||||
# Pénzügyi adatok
|
||||
amount = Column(Numeric(18, 2), nullable=False) # Bruttó összeg
|
||||
net_amount = Column(Numeric(18, 2)) # Nettó összeg
|
||||
vat_amount = Column(Numeric(18, 2)) # ÁFA érték
|
||||
vat_rate = Column(Numeric(5, 2)) # ÁFA kulcs (pl. 27.00)
|
||||
|
||||
# Nemzetközi deviza kezelés
|
||||
currency = Column(String(3), default="HUF") # Riportálási deviza
|
||||
original_currency = Column(String(3)) # Számla eredeti devizája
|
||||
exchange_rate_at_cost = Column(Numeric(18, 6)) # Rögzítéskori árfolyam
|
||||
|
||||
driver_id = Column(Integer, ForeignKey("data.users.id"), nullable=True)
|
||||
cost_type = Column(String(50), nullable=False)
|
||||
amount_local = Column(Numeric(18, 2), nullable=False)
|
||||
currency_local = Column(String(3), nullable=False)
|
||||
amount_eur = Column(Numeric(18, 2), nullable=True)
|
||||
net_amount_local = Column(Numeric(18, 2))
|
||||
vat_rate = Column(Numeric(5, 2))
|
||||
exchange_rate_used = Column(Numeric(18, 6))
|
||||
date = Column(DateTime(timezone=True), server_default=func.now())
|
||||
description = Column(String)
|
||||
invoice_id = Column(String)
|
||||
mileage_at_cost = Column(Integer)
|
||||
|
||||
data = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
|
||||
asset = relationship("Asset", back_populates="costs")
|
||||
|
||||
class ExchangeRate(Base):
|
||||
"""Napi árfolyamok tárolása (ECB/MNB adatok alapján)"""
|
||||
__tablename__ = "exchange_rates"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
id = Column(Integer, primary_key=True)
|
||||
base_currency = Column(String(3), default="EUR")
|
||||
target_currency = Column(String(3), nullable=False)
|
||||
target_currency = Column(String(3), unique=True)
|
||||
rate = Column(Numeric(18, 6), nullable=False)
|
||||
rate_date = Column(DateTime(timezone=False), index=True)
|
||||
provider = Column(String(50), default="ECB")
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
rate_date = Column(DateTime, server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
@@ -1,63 +0,0 @@
|
||||
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, DateTime
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.dialects.postgresql import ENUM as PG_ENUM, UUID
|
||||
from app.db.base import Base
|
||||
import enum
|
||||
|
||||
# A Python enum marad, de a Column definíciónál pontosítunk
|
||||
class CompanyRole(str, enum.Enum):
|
||||
OWNER = "owner"
|
||||
MANAGER = "manager"
|
||||
DRIVER = "driver"
|
||||
|
||||
class Company(Base):
|
||||
__tablename__ = "companies"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
name = Column(String, nullable=False)
|
||||
tax_number = Column(String, nullable=True)
|
||||
subscription_tier = Column(String, default="free")
|
||||
owner_id = Column(Integer, ForeignKey("data.users.id"), nullable=False)
|
||||
|
||||
members = relationship("CompanyMember", back_populates="company", cascade="all, delete-orphan")
|
||||
assignments = relationship("VehicleAssignment", back_populates="company")
|
||||
|
||||
class CompanyMember(Base):
|
||||
__tablename__ = "company_members"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
company_id = Column(Integer, ForeignKey("data.companies.id"), nullable=False)
|
||||
user_id = Column(Integer, ForeignKey("data.users.id"), nullable=False)
|
||||
|
||||
# JAVÍTÁS: Kifejezetten megadjuk a natív Postgres típust
|
||||
role = Column(
|
||||
PG_ENUM('owner', 'manager', 'driver', name='companyrole', schema='data', create_type=False),
|
||||
nullable=False
|
||||
)
|
||||
|
||||
can_edit_service = Column(Boolean, default=False)
|
||||
can_see_costs = Column(Boolean, default=False)
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
company = relationship("Company", back_populates="members")
|
||||
user = relationship("User")
|
||||
|
||||
class VehicleAssignment(Base):
|
||||
__tablename__ = "vehicle_assignments"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
company_id = Column(Integer, ForeignKey("data.companies.id"), nullable=False)
|
||||
vehicle_id = Column(UUID(as_uuid=True), ForeignKey("data.vehicles.id"), nullable=False)
|
||||
driver_id = Column(Integer, ForeignKey("data.users.id"), nullable=False)
|
||||
|
||||
start_date = Column(DateTime(timezone=True), server_default=func.now())
|
||||
end_date = Column(DateTime(timezone=True), nullable=True)
|
||||
notes = Column(String, nullable=True)
|
||||
|
||||
company = relationship("Company", back_populates="assignments")
|
||||
vehicle = relationship("Vehicle") # Itt már a Vehicle-re hivatkozunk
|
||||
driver = relationship("User", foreign_keys=[driver_id])
|
||||
@@ -1,7 +1,8 @@
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, Boolean, DateTime, JSON, Numeric
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base import Base
|
||||
# JAVÍTVA: Import közvetlenül a base_class-ból
|
||||
from app.db.base_class import Base
|
||||
|
||||
class SubscriptionTier(Base):
|
||||
__tablename__ = "subscription_tiers"
|
||||
@@ -39,4 +40,4 @@ class ServiceSpecialty(Base):
|
||||
name = Column(String, nullable=False)
|
||||
slug = Column(String, unique=True)
|
||||
|
||||
parent = relationship("ServiceSpecialty", remote_side=[id], backref="children")
|
||||
parent = relationship("ServiceSpecialty", remote_side=[id], backref="children")
|
||||
@@ -2,7 +2,8 @@ from sqlalchemy import Column, String, Integer, Boolean, DateTime, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.sql import func
|
||||
import uuid
|
||||
from app.db.base import Base
|
||||
# JAVÍTVA: Közvetlenül a base_class-ból importálunk, nem a base-ből!
|
||||
from app.db.base_class import Base
|
||||
|
||||
class Document(Base):
|
||||
__tablename__ = "documents"
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base import Base
|
||||
|
||||
class EmailLog(Base):
|
||||
__tablename__ = "email_logs"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, nullable=True) # Hozzáadva
|
||||
recipient = Column(String, index=True) # Hozzáadva
|
||||
email = Column(String, index=True)
|
||||
email_type = Column(String) # Frissítve a kódhoz
|
||||
type = Column(String) # Megtartva a kompatibilitás miatt
|
||||
provider_id = Column(Integer) # Hozzáadva
|
||||
status = Column(String) # Hozzáadva
|
||||
sent_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
@@ -1,21 +0,0 @@
|
||||
from sqlalchemy import Column, Integer, String, Boolean, JSON, Float
|
||||
from app.db.base import Base
|
||||
|
||||
class EmailProviderConfig(Base):
|
||||
__tablename__ = "email_provider_configs"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
name = Column(String(50), unique=True) # Pl: SendGrid_Main, Office365_Backup
|
||||
provider_type = Column(String(20)) # SENDGRID, SMTP, MAILGUN
|
||||
priority = Column(Integer, default=1) # 1 = legfontosabb
|
||||
|
||||
# JSON-ban tároljuk a paramétereket (host, port, api_key, user, stb.)
|
||||
settings = Column(JSON, nullable=False)
|
||||
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
# Failover figyelés
|
||||
fail_count = Column(Integer, default=0)
|
||||
max_fail_threshold = Column(Integer, default=3) # Hány hiba után kapcsoljon le?
|
||||
success_rate = Column(Float, default=100.0) # Statisztika az adminnak
|
||||
@@ -1,30 +0,0 @@
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Text, Numeric
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base import Base
|
||||
|
||||
class EmailProvider(Base):
|
||||
__tablename__ = 'email_providers'
|
||||
__table_args__ = {'schema': 'data'}
|
||||
id = Column(Integer, PRIMARY KEY=True)
|
||||
name = Column(String(50), nullable=False)
|
||||
priority = Column(Integer, default=1)
|
||||
provider_type = Column(String(10), default='SMTP')
|
||||
host = Column(String(255))
|
||||
port = Column(Integer)
|
||||
username = Column(String(255))
|
||||
password_hash = Column(String(255))
|
||||
is_active = Column(Boolean, default=True)
|
||||
daily_limit = Column(Integer, default=300)
|
||||
current_daily_usage = Column(Integer, default=0)
|
||||
|
||||
class EmailLog(Base):
|
||||
__tablename__ = 'email_logs'
|
||||
__table_args__ = {'schema': 'data'}
|
||||
id = Column(Integer, PRIMARY KEY=True)
|
||||
user_id = Column(Integer, ForeignKey('data.users.id'), nullable=True)
|
||||
email_type = Column(String(50))
|
||||
recipient = Column(String(255))
|
||||
provider_id = Column(Integer, ForeignKey('data.email_providers.id'))
|
||||
status = Column(String(20))
|
||||
sent_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
error_message = Column(Text)
|
||||
@@ -1,17 +0,0 @@
|
||||
from sqlalchemy import Column, Integer, String, Text, Enum
|
||||
import enum
|
||||
from app.db.base import Base
|
||||
|
||||
class EmailType(str, enum.Enum):
|
||||
REGISTRATION = "REGISTRATION"
|
||||
PASSWORD_RESET = "PASSWORD_RESET"
|
||||
GDPR_NOTICE = "GDPR_NOTICE"
|
||||
|
||||
class EmailTemplate(Base):
|
||||
__tablename__ = "email_templates"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
type = Column(Enum(EmailType), unique=True, index=True)
|
||||
subject = Column(String(255), nullable=False)
|
||||
body_html = Column(Text, nullable=False) # Adminról szerkeszthető HTML tartalom
|
||||
@@ -1,50 +0,0 @@
|
||||
import enum
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, Enum, DateTime, Boolean, Date, JSON
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base import Base
|
||||
|
||||
# Költség Kategóriák
|
||||
class ExpenseCategory(str, enum.Enum):
|
||||
PURCHASE_PRICE = "PURCHASE_PRICE" # Vételár
|
||||
TRANSFER_TAX = "TRANSFER_TAX" # Vagyonszerzési illeték
|
||||
ADMIN_FEE = "ADMIN_FEE" # Eredetiség, forgalmi, törzskönyv
|
||||
VEHICLE_TAX = "VEHICLE_TAX" # Gépjárműadó
|
||||
INSURANCE = "INSURANCE" # Biztosítás
|
||||
REFUELING = "REFUELING" # Tankolás
|
||||
SERVICE = "SERVICE" # Szerviz / Javítás
|
||||
PARKING = "PARKING" # Parkolás
|
||||
TOLL = "TOLL" # Autópálya matrica
|
||||
FINE = "FINE" # Bírság
|
||||
TUNING_ACCESSORIES = "TUNING_ACCESSORIES" # Extrák
|
||||
OTHER = "OTHER" # Egyéb
|
||||
|
||||
class VehicleEvent(Base):
|
||||
__tablename__ = "vehicle_events"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
vehicle_id = Column(Integer, ForeignKey("data.user_vehicles.id"), nullable=False)
|
||||
|
||||
# Esemény típusa
|
||||
event_type = Column(Enum(ExpenseCategory, schema="data", name="expense_category_enum"), nullable=False)
|
||||
|
||||
date = Column(Date, nullable=False)
|
||||
|
||||
# Kilométeróra (KÖTELEZŐ!)
|
||||
odometer_value = Column(Integer, nullable=False)
|
||||
odometer_anomaly = Column(Boolean, default=False) # Ha csökkenést észlelünk, True lesz
|
||||
|
||||
# Pénzügyek
|
||||
cost_amount = Column(Integer, nullable=False, default=0) # HUF
|
||||
|
||||
# Leírás és Képek
|
||||
description = Column(String, nullable=True)
|
||||
image_paths = Column(JSON, nullable=True) # Lista a feltöltött képek (számla, fotó) útvonalairól
|
||||
|
||||
# Kapcsolat a szolgáltatóval
|
||||
# Ha is_diy=True, akkor a user maga csinálta.
|
||||
# Ha is_diy=False és service_provider_id=None, akkor ismeretlen helyen készült.
|
||||
is_diy = Column(Boolean, default=False)
|
||||
service_provider_id = Column(Integer, ForeignKey("data.service_providers.id"), nullable=True)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
@@ -1,63 +1,30 @@
|
||||
# /opt/service_finder/backend/app/models/history.py
|
||||
|
||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, JSON, Date, Text
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base import Base
|
||||
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
|
||||
from app.db.base_class import Base
|
||||
|
||||
# --- 1. Jármű Birtoklási Előzmények (Ownership History) ---
|
||||
# Ez a tábla mondja meg, kié volt az autó egy adott időpillanatban.
|
||||
# Így biztosítjuk, hogy a régi tulajdonos adatai védve legyenek az újtól.
|
||||
class VehicleOwnership(Base):
|
||||
__tablename__ = "vehicle_ownerships"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
# Kapcsolatok
|
||||
vehicle_id = Column(Integer, ForeignKey("data.user_vehicles.id"), nullable=False)
|
||||
vehicle_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), nullable=False)
|
||||
user_id = Column(Integer, ForeignKey("data.users.id"), nullable=False)
|
||||
|
||||
# Időszak
|
||||
start_date = Column(Date, nullable=False, default=func.current_date()) # Mikor került hozzá
|
||||
end_date = Column(Date, nullable=True) # Ha NULL, akkor ő a jelenlegi tulajdonos!
|
||||
|
||||
# Jegyzet (pl. adásvételi szerződés száma)
|
||||
start_date = Column(Date, nullable=False, default=func.current_date())
|
||||
end_date = Column(Date, nullable=True)
|
||||
notes = Column(Text, nullable=True)
|
||||
|
||||
# SQLAlchemy kapcsolatok (visszahivatkozások a fő modellekben kellenek majd)
|
||||
vehicle = relationship("UserVehicle", back_populates="ownership_history")
|
||||
user = relationship("User", back_populates="owned_vehicles")
|
||||
vehicle = relationship("Asset", back_populates="ownership_history")
|
||||
user = relationship("User", back_populates="ownership_history")
|
||||
|
||||
|
||||
# --- 2. Audit Log (A "Fekete Doboz") ---
|
||||
# Minden kritikus módosítást itt tárolunk. Ez a rendszer "igazságügyi naplója".
|
||||
class AuditLog(Base):
|
||||
__tablename__ = "audit_logs"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
# KI? (A felhasználó, aki a műveletet végezte)
|
||||
user_id = Column(Integer, ForeignKey("data.users.id"), nullable=True)
|
||||
|
||||
# MIT? (Milyen objektumot érintett?)
|
||||
target_type = Column(String, index=True) # pl. "vehicle", "cost", "user_profile"
|
||||
target_id = Column(Integer, index=True) # pl. az autó ID-ja
|
||||
|
||||
# HOGYAN?
|
||||
action = Column(String, nullable=False) # CREATE, UPDATE, DELETE, LOGIN_FAILED, EXPORT_DATA
|
||||
|
||||
# RÉSZLETEK (Mi változott?)
|
||||
# Pl: {"field": "odometer", "old_value": 150000, "new_value": 120000} <- Visszatekerés gyanú!
|
||||
target_type = Column(String, index=True)
|
||||
target_id = Column(String, index=True)
|
||||
action = Column(String, nullable=False)
|
||||
changes = Column(JSON, nullable=True)
|
||||
|
||||
# BIZTONSÁG
|
||||
ip_address = Column(String, nullable=True) # Honnan jött a kérés?
|
||||
user_agent = Column(String, nullable=True) # Milyen böngészőből?
|
||||
|
||||
# MIKOR?
|
||||
timestamp = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
# Kapcsolat (Opcionális, csak ha le akarjuk kérdezni a user adatait a logból)
|
||||
user = relationship("User")
|
||||
@@ -12,6 +12,7 @@ class UserRole(str, enum.Enum):
|
||||
service = "service"
|
||||
fleet_manager = "fleet_manager"
|
||||
driver = "driver"
|
||||
superadmin = "superadmin" # Hozzáadva a biztonság kedvéért
|
||||
|
||||
class Person(Base):
|
||||
__tablename__ = "persons"
|
||||
@@ -51,34 +52,37 @@ class User(Base):
|
||||
region_code = Column(String, default="HU")
|
||||
is_deleted = Column(Boolean, default=False)
|
||||
person_id = Column(BigInteger, ForeignKey("data.persons.id"), nullable=True)
|
||||
preferred_language = Column(String(5), default="hu")
|
||||
preferred_currency = Column(String(3), default="HUF")
|
||||
timezone = Column(String(50), default="Europe/Budapest")
|
||||
|
||||
person = relationship("Person", back_populates="users")
|
||||
wallet = relationship("Wallet", back_populates="user", uselist=False)
|
||||
|
||||
# Itt a trükk: csak a string hivatkozás marad, így nincs import hiba,
|
||||
# de a SQLAlchemy tudni fogja, hogy a UserStats-ra gondolunk.
|
||||
stats = relationship("UserStats", back_populates="user", uselist=False)
|
||||
|
||||
owned_organizations = relationship("Organization", back_populates="owner", lazy="select")
|
||||
# RBAC & SCOPE mezők (Visszaállítva a DB sémához)
|
||||
scope_level = Column(String(30), server_default="individual")
|
||||
scope_id = Column(String(50))
|
||||
custom_permissions = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
# Kapcsolatok
|
||||
person = relationship("Person", back_populates="users")
|
||||
wallet = relationship("Wallet", back_populates="user", uselist=False)
|
||||
stats = relationship("UserStats", back_populates="user", uselist=False)
|
||||
ownership_history = relationship("VehicleOwnership", back_populates="user")
|
||||
owned_organizations = relationship("Organization", back_populates="owner")
|
||||
|
||||
class Wallet(Base):
|
||||
"""Kétoszlopos pénztárca: Coin (Szerviz) és Kredit (Prémium)."""
|
||||
__tablename__ = "wallets"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("data.users.id"), unique=True)
|
||||
coin_balance = Column(Numeric(18, 2), default=0.00)
|
||||
credit_balance = Column(Numeric(18, 2), default=0.00)
|
||||
currency = Column(String(3), default="HUF")
|
||||
|
||||
user = relationship("User", back_populates="wallet")
|
||||
|
||||
class VerificationToken(Base):
|
||||
__tablename__ = "verification_tokens"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
token = Column(PG_UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False)
|
||||
user_id = Column(Integer, ForeignKey("data.users.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
@@ -20,16 +20,16 @@ class Organization(Base):
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
# ÚJ MEZŐ: Egységes címkezelés (GeoService hibrid)
|
||||
address_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.addresses.id"), nullable=True)
|
||||
|
||||
# --- NÉVKEZELÉS ---
|
||||
full_name = Column(String, nullable=False) # Teljes hivatalos név
|
||||
name = Column(String, nullable=False) # Rövidített cégnév
|
||||
display_name = Column(String(50)) # Alkalmazáson belüli rövidítés
|
||||
full_name = Column(String, nullable=False)
|
||||
name = Column(String, nullable=False)
|
||||
display_name = Column(String(50))
|
||||
|
||||
# --- ATOMIZÁLT CÍMKEZELÉS (Kompatibilitási réteg) ---
|
||||
default_currency = Column(String(3), default="HUF")
|
||||
country_code = Column(String(2), default="HU")
|
||||
language = Column(String(5), default="hu")
|
||||
|
||||
address_zip = Column(String(10))
|
||||
address_city = Column(String(100))
|
||||
address_street_name = Column(String(150))
|
||||
@@ -39,9 +39,7 @@ class Organization(Base):
|
||||
address_stairwell = Column(String(20))
|
||||
address_floor = Column(String(20))
|
||||
address_door = Column(String(20))
|
||||
country_code = Column(String(2), default="HU")
|
||||
|
||||
# --- ÜZLETI ADATOK ---
|
||||
tax_number = Column(String(20), unique=True, index=True)
|
||||
reg_number = Column(String(50))
|
||||
|
||||
@@ -65,7 +63,7 @@ class Organization(Base):
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# Kapcsolatok
|
||||
# String alapú hivatkozás a körkörös import ellen
|
||||
assets = relationship("AssetAssignment", back_populates="organization", cascade="all, delete-orphan")
|
||||
members = relationship("OrganizationMember", back_populates="organization", cascade="all, delete-orphan")
|
||||
owner = relationship("User", back_populates="owned_organizations")
|
||||
@@ -76,13 +74,9 @@ class OrganizationMember(Base):
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=False)
|
||||
user_id = Column(Integer, ForeignKey("data.users.id"), nullable=False)
|
||||
role = Column(String, default="driver") # owner, manager, driver, service_staff
|
||||
role = Column(String, default="driver")
|
||||
|
||||
# JAVÍTVA: Jogosultságok JSONB mezője (can_add_asset, etc.)
|
||||
permissions = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
|
||||
organization = relationship("Organization", back_populates="members")
|
||||
user = relationship("app.models.identity.User") # Visszamutató kapcsolat a felhasználóra
|
||||
|
||||
# Kompatibilitási réteg
|
||||
Organization.vehicles = Organization.assets
|
||||
user = relationship("User") # Egyszerűsített string hivatkozás
|
||||
13
backend/app/models/system.py
Normal file
13
backend/app/models/system.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from sqlalchemy import Column, String, JSON, DateTime, Boolean
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base_class import Base
|
||||
|
||||
class SystemParameter(Base):
|
||||
__tablename__ = "system_parameters"
|
||||
__table_args__ = {"schema": "data", "extend_existing": True}
|
||||
|
||||
key = Column(String, primary_key=True, index=True, nullable=False)
|
||||
value = Column(JSON, nullable=False)
|
||||
is_active = Column(Boolean, default=True)
|
||||
description = Column(String)
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), server_default=func.now())
|
||||
16
backend/app/models/system_config.py
Normal file
16
backend/app/models/system_config.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from sqlalchemy import Column, String, JSON, Integer, Boolean, DateTime, func
|
||||
from app.db.base_class import Base
|
||||
|
||||
class SystemParameter(Base):
|
||||
"""
|
||||
Globális rendszerbeállítások (A meglévő data.system_parameters tábla alapján).
|
||||
"""
|
||||
__tablename__ = "system_parameters"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
key = Column(String(50), unique=True, index=True, nullable=False)
|
||||
value = Column(JSON, nullable=False)
|
||||
is_active = Column(Boolean, default=True)
|
||||
description = Column(String, nullable=True)
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
@@ -1,54 +0,0 @@
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, Boolean, Float, JSON, Date
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.db.base import Base
|
||||
|
||||
# 1. Kategória (Autó, Motor, Kisteher...)
|
||||
class VehicleCategory(Base):
|
||||
__tablename__ = "vehicle_categories"
|
||||
__table_args__ = {"schema": "data"}
|
||||
id = Column(Integer, primary_key=True)
|
||||
name_key = Column(String, nullable=False) # i18n kulcs: 'CAR', 'MOTORCYCLE'
|
||||
|
||||
# 2. Márka (Audi, Honda, BMW...)
|
||||
class VehicleMake(Base):
|
||||
__tablename__ = "vehicle_makes"
|
||||
__table_args__ = {"schema": "data"}
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String, unique=True, nullable=False)
|
||||
logo_url = Column(String, nullable=True)
|
||||
|
||||
# 3. Modell és Generáció (pl. Audi A3 -> A3 8V)
|
||||
class VehicleModel(Base):
|
||||
__tablename__ = "vehicle_models"
|
||||
__table_args__ = {"schema": "data"}
|
||||
id = Column(Integer, primary_key=True)
|
||||
make_id = Column(Integer, ForeignKey("data.vehicle_makes.id"))
|
||||
category_id = Column(Integer, ForeignKey("data.vehicle_categories.id"))
|
||||
name = Column(String, nullable=False)
|
||||
generation_name = Column(String, nullable=True) # pl: "8V Facelift"
|
||||
production_start_year = Column(Integer, nullable=True)
|
||||
production_end_year = Column(Integer, nullable=True)
|
||||
|
||||
# 4. Motor és Hajtáslánc (Technikai specifikációk)
|
||||
class VehicleEngine(Base):
|
||||
__tablename__ = "vehicle_engines"
|
||||
__table_args__ = {"schema": "data"}
|
||||
id = Column(Integer, primary_key=True)
|
||||
model_id = Column(Integer, ForeignKey("data.vehicle_models.id"))
|
||||
|
||||
engine_code = Column(String, nullable=True)
|
||||
fuel_type = Column(String, nullable=False) # 'Petrol', 'Diesel', 'Hybrid', 'EV'
|
||||
displacement_ccm = Column(Integer, nullable=True)
|
||||
power_kw = Column(Integer, nullable=True)
|
||||
torque_nm = Column(Integer, nullable=True)
|
||||
transmission_type = Column(String, nullable=True) # 'Manual', 'Automatic'
|
||||
gears_count = Column(Integer, nullable=True)
|
||||
drive_type = Column(String, nullable=True) # 'FWD', 'RWD', 'AWD'
|
||||
|
||||
# 5. Opciók Katalógusa (Gyári extrák listája)
|
||||
class VehicleOptionCatalog(Base):
|
||||
__tablename__ = "vehicle_options_catalog"
|
||||
__table_args__ = {"schema": "data"}
|
||||
id = Column(Integer, primary_key=True)
|
||||
category = Column(String) # 'Security', 'Comfort', 'Multimedia'
|
||||
name_key = Column(String) # 'MATRIX_LED'
|
||||
Reference in New Issue
Block a user