feat: implement pivot-currency model, rbac smart tokens & fix circular imports

This commit is contained in:
2026-02-10 10:20:45 +00:00
parent 24d35fe0c1
commit e255fea3a5
117 changed files with 2247 additions and 3542 deletions

View File

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

View File

@@ -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())

View File

@@ -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])

View File

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

View File

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

View File

@@ -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())

View File

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

View File

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

View File

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

View File

@@ -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())

View File

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

View File

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

View File

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

View 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())

View 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())

View File

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