feat: stabilize KYC, international assets and multi-currency schema
- Split mother's name in KYC (last/first) - Added mileage_unit and fuel_type to Assets - Expanded AssetCost for international VAT and original currency - Fixed SQLAlchemy IndexError in asset catalog lookup - Added exchange_rate and ratings tables to models
This commit is contained in:
@@ -1,19 +1,15 @@
|
||||
from app.db.base import Base
|
||||
from app.db.base_class import Base
|
||||
from .identity import User, Person, Wallet, UserRole, VerificationToken
|
||||
from .organization import Organization, OrgType
|
||||
from .vehicle import (
|
||||
VehicleCatalog,
|
||||
Asset,
|
||||
AssetEvent,
|
||||
AssetRating,
|
||||
ServiceProvider,
|
||||
Vehicle, # Alias az Asset-re a kompatibilitás miatt
|
||||
ServiceRecord # Alias az AssetEvent-re a kompatibilitás miatt
|
||||
)
|
||||
from .organization import Organization, OrganizationMember
|
||||
from .asset import Asset, AssetCatalog, AssetCost, AssetEvent
|
||||
from .address import Address, GeoPostalCode, GeoStreet, GeoStreetType
|
||||
from .gamification import UserStats, PointsLedger
|
||||
|
||||
# Aliasok a kód többi részének szinkronizálásához
|
||||
UserVehicle = Vehicle
|
||||
VehicleOwnership = Asset
|
||||
# Aliasok a kompatibilitás és a tiszta kód érdekében
|
||||
Vehicle = Asset
|
||||
UserVehicle = Asset
|
||||
VehicleCatalog = AssetCatalog
|
||||
ServiceRecord = AssetEvent
|
||||
|
||||
__all__ = [
|
||||
"Base",
|
||||
@@ -23,14 +19,19 @@ __all__ = [
|
||||
"UserRole",
|
||||
"VerificationToken",
|
||||
"Organization",
|
||||
"OrgType",
|
||||
"VehicleCatalog",
|
||||
"OrganizationMember",
|
||||
"Asset",
|
||||
"AssetEvent",
|
||||
"AssetRating",
|
||||
"ServiceProvider",
|
||||
"AssetCatalog",
|
||||
"AssetCost",
|
||||
"AssetEvent",
|
||||
"Address",
|
||||
"GeoPostalCode",
|
||||
"GeoStreet",
|
||||
"GeoStreetType",
|
||||
"UserStats",
|
||||
"PointsLedger",
|
||||
"Vehicle",
|
||||
"UserVehicle",
|
||||
"ServiceRecord",
|
||||
"VehicleOwnership"
|
||||
"VehicleCatalog",
|
||||
"ServiceRecord"
|
||||
]
|
||||
Binary file not shown.
BIN
backend/app/models/__pycache__/address.cpython-312.pyc
Normal file
BIN
backend/app/models/__pycache__/address.cpython-312.pyc
Normal file
Binary file not shown.
BIN
backend/app/models/__pycache__/asset.cpython-312.pyc
Normal file
BIN
backend/app/models/__pycache__/asset.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
45
backend/app/models/address.py
Normal file
45
backend/app/models/address.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import uuid
|
||||
from sqlalchemy import Column, String, Integer, ForeignKey, Text, DateTime
|
||||
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base_class import Base
|
||||
|
||||
class GeoPostalCode(Base):
|
||||
__tablename__ = "geo_postal_codes"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
country_code = Column(String(5), default="HU")
|
||||
zip_code = Column(String(10), nullable=False)
|
||||
city = Column(String(100), nullable=False)
|
||||
|
||||
class GeoStreet(Base):
|
||||
__tablename__ = "geo_streets"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
postal_code_id = Column(Integer, ForeignKey("data.geo_postal_codes.id"))
|
||||
name = Column(String(200), nullable=False)
|
||||
|
||||
class GeoStreetType(Base):
|
||||
__tablename__ = "geo_street_types"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String(50), unique=True, nullable=False)
|
||||
|
||||
class Address(Base):
|
||||
__tablename__ = "addresses"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
postal_code_id = Column(Integer, ForeignKey("data.geo_postal_codes.id"))
|
||||
street_name = Column(String(200), nullable=False)
|
||||
street_type = Column(String(50), nullable=False)
|
||||
house_number = Column(String(50), nullable=False)
|
||||
stairwell = Column(String(20))
|
||||
floor = Column(String(20))
|
||||
door = Column(String(20))
|
||||
parcel_id = Column(String(50)) # HRSZ
|
||||
full_address_text = Column(Text)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
133
backend/app/models/asset.py
Normal file
133
backend/app/models/asset.py
Normal file
@@ -0,0 +1,133 @@
|
||||
import uuid
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, Numeric, 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
|
||||
fuel_type = Column(String)
|
||||
engine_code = Column(String)
|
||||
|
||||
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")
|
||||
assignments = relationship("AssetAssignment", back_populates="asset")
|
||||
events = relationship("AssetEvent", back_populates="asset")
|
||||
costs = relationship("AssetCost", back_populates="asset")
|
||||
|
||||
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())
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
base_currency = Column(String(3), default="EUR")
|
||||
target_currency = Column(String(3), nullable=False)
|
||||
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())
|
||||
@@ -1,8 +1,14 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from sqlalchemy import ForeignKey, String, Integer, DateTime, func, Boolean
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from app.db.base import Base
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
|
||||
from app.db.base_class import Base
|
||||
|
||||
# Típusvizsgálathoz a körkörös import elkerülése érdekében
|
||||
if TYPE_CHECKING:
|
||||
from app.models.identity import User
|
||||
|
||||
# Közös beállítás az összes táblához ebben a fájlban
|
||||
SCHEMA_ARGS = {"schema": "data"}
|
||||
@@ -36,21 +42,26 @@ class PointsLedger(Base):
|
||||
__tablename__ = "points_ledger"
|
||||
__table_args__ = SCHEMA_ARGS
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
||||
# JAVÍTVA: data.users.id hivatkozás
|
||||
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.users.id"))
|
||||
points: Mapped[int] = mapped_column(Integer)
|
||||
reason: Mapped[str] = mapped_column(String)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=func.now())
|
||||
|
||||
# Kapcsolat a felhasználóhoz
|
||||
user: Mapped["User"] = relationship("User")
|
||||
|
||||
class UserStats(Base):
|
||||
__tablename__ = "user_stats"
|
||||
__table_args__ = SCHEMA_ARGS
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
||||
# JAVÍTVA: data.users.id hivatkozás
|
||||
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.users.id"), unique=True)
|
||||
total_points: Mapped[int] = mapped_column(Integer, default=0)
|
||||
# user_id a PK, mert 1:1 kapcsolat a User-rel
|
||||
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.users.id"), primary_key=True)
|
||||
total_xp: Mapped[int] = mapped_column(Integer, default=0)
|
||||
social_points: Mapped[int] = mapped_column(Integer, default=0)
|
||||
current_level: Mapped[int] = mapped_column(Integer, default=1)
|
||||
last_activity: Mapped[datetime] = mapped_column(DateTime, default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime, default=func.now(), onupdate=func.now())
|
||||
|
||||
# EZ A JAVÍTÁS: A visszamutató kapcsolat definiálása
|
||||
user: Mapped["User"] = relationship("User", back_populates="stats")
|
||||
|
||||
class Badge(Base):
|
||||
__tablename__ = "badges"
|
||||
@@ -64,7 +75,18 @@ class UserBadge(Base):
|
||||
__tablename__ = "user_badges"
|
||||
__table_args__ = SCHEMA_ARGS
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
||||
# JAVÍTVA: data.users.id hivatkozás
|
||||
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.users.id"))
|
||||
badge_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.badges.id"))
|
||||
earned_at: Mapped[datetime] = mapped_column(DateTime, default=func.now())
|
||||
earned_at: Mapped[datetime] = mapped_column(DateTime, default=func.now())
|
||||
|
||||
user: Mapped["User"] = relationship("User")
|
||||
|
||||
class Rating(Base): # <--- Az új értékelési modell
|
||||
__tablename__ = "ratings"
|
||||
__table_args__ = SCHEMA_ARGS
|
||||
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
author_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.users.id"))
|
||||
target_type: Mapped[str] = mapped_column(String(20))
|
||||
target_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True))
|
||||
score: Mapped[int] = mapped_column(Integer)
|
||||
comment: Mapped[Optional[str]] = mapped_column(String)
|
||||
@@ -2,9 +2,9 @@ import uuid
|
||||
import enum
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, Numeric, text, Enum, BigInteger
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base import Base
|
||||
from app.db.base_class import Base
|
||||
|
||||
class UserRole(str, enum.Enum):
|
||||
admin = "admin"
|
||||
@@ -18,23 +18,22 @@ class Person(Base):
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(BigInteger, primary_key=True, index=True)
|
||||
id_uuid = Column(UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False)
|
||||
id_uuid = Column(PG_UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False)
|
||||
address_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.addresses.id"), nullable=True)
|
||||
|
||||
last_name = Column(String, nullable=False)
|
||||
first_name = Column(String, nullable=False)
|
||||
mothers_name = Column(String, nullable=True)
|
||||
mothers_last_name = Column(String, nullable=True)
|
||||
mothers_first_name = Column(String, nullable=True)
|
||||
birth_place = Column(String, nullable=True)
|
||||
birth_date = Column(DateTime, nullable=True)
|
||||
phone = Column(String, nullable=True)
|
||||
|
||||
# JSONB mezők az okmányoknak és orvosi adatoknak
|
||||
identity_docs = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
medical_emergency = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
ice_contact = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
|
||||
# KYC státusz
|
||||
is_active = Column(Boolean, default=False, nullable=False)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
@@ -47,28 +46,32 @@ class User(Base):
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
email = Column(String, unique=True, index=True, nullable=False)
|
||||
hashed_password = Column(String, nullable=True)
|
||||
|
||||
role = Column(Enum(UserRole), default=UserRole.user)
|
||||
is_active = Column(Boolean, default=False)
|
||||
region_code = Column(String, default="HU")
|
||||
|
||||
is_deleted = Column(Boolean, default=False)
|
||||
person_id = Column(BigInteger, ForeignKey("data.persons.id"), nullable=True)
|
||||
|
||||
person = relationship("Person", back_populates="users")
|
||||
wallet = relationship("Wallet", back_populates="user", uselist=False)
|
||||
owned_organizations = relationship("Organization", back_populates="owner", lazy="select")
|
||||
|
||||
# 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")
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
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)
|
||||
xp_balance = Column(Integer, default=0)
|
||||
credit_balance = Column(Numeric(18, 2), default=0.00)
|
||||
currency = Column(String(3), default="HUF")
|
||||
|
||||
user = relationship("User", back_populates="wallet")
|
||||
|
||||
@@ -77,9 +80,9 @@ class VerificationToken(Base):
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
token = Column(UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False)
|
||||
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)
|
||||
token_type = Column(String(20), nullable=False) # 'registration' vagy 'password_reset'
|
||||
token_type = Column(String(20), nullable=False)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
expires_at = Column(DateTime(timezone=True), nullable=False)
|
||||
is_used = Column(Boolean, default=False)
|
||||
@@ -1,9 +1,11 @@
|
||||
import enum
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON
|
||||
import uuid
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, text
|
||||
from sqlalchemy.dialects.postgresql import ENUM as PG_ENUM
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base import Base
|
||||
from app.db.base_class import Base
|
||||
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
|
||||
|
||||
class OrgType(str, enum.Enum):
|
||||
individual = "individual"
|
||||
@@ -19,18 +21,21 @@ class Organization(Base):
|
||||
|
||||
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 (pl. ProfiBot Kft.)
|
||||
display_name = Column(String(50)) # Alkalmazáson belüli rövidítés (pl. ProfiBot)
|
||||
name = Column(String, nullable=False) # Rövidített cégnév
|
||||
display_name = Column(String(50)) # Alkalmazáson belüli rövidítés
|
||||
|
||||
# --- ATOMIZÁLT CÍMKEZELÉS ---
|
||||
# --- ATOMIZÁLT CÍMKEZELÉS (Kompatibilitási réteg) ---
|
||||
address_zip = Column(String(10))
|
||||
address_city = Column(String(100))
|
||||
address_street_name = Column(String(150))
|
||||
address_street_type = Column(String(50)) # utca, út, tér, dűlő, stb.
|
||||
address_street_type = Column(String(50))
|
||||
address_house_number = Column(String(20))
|
||||
address_hrsz = Column(String(50)) # Helyrajzi szám
|
||||
address_hrsz = Column(String(50))
|
||||
address_stairwell = Column(String(20))
|
||||
address_floor = Column(String(20))
|
||||
address_door = Column(String(20))
|
||||
@@ -40,7 +45,6 @@ class Organization(Base):
|
||||
tax_number = Column(String(20), unique=True, index=True)
|
||||
reg_number = Column(String(50))
|
||||
|
||||
# PG_ENUM használata a Python Enum-mal szinkronizálva
|
||||
org_type = Column(
|
||||
PG_ENUM(OrgType, name="orgtype", inherit_schema=True),
|
||||
default=OrgType.individual
|
||||
@@ -49,13 +53,8 @@ class Organization(Base):
|
||||
status = Column(String(30), default="pending_verification")
|
||||
is_deleted = Column(Boolean, default=False)
|
||||
|
||||
notification_settings = Column(JSON, default={
|
||||
"notify_owner": True,
|
||||
"notify_manager": True,
|
||||
"notify_contact": True,
|
||||
"alert_days_before": [30, 15, 7, 1]
|
||||
})
|
||||
external_integration_config = Column(JSON, default={})
|
||||
notification_settings = Column(JSON, server_default=text("'{ \"notify_owner\": true, \"alert_days_before\": [30, 15, 7, 1] }'::jsonb"))
|
||||
external_integration_config = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
|
||||
owner_id = Column(Integer, ForeignKey("data.users.id"), nullable=True)
|
||||
is_active = Column(Boolean, default=True)
|
||||
@@ -67,8 +66,8 @@ class Organization(Base):
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# Kapcsolatok
|
||||
assets = relationship("Asset", back_populates="organization", cascade="all, delete-orphan")
|
||||
members = relationship("OrganizationMember", back_populates="organization")
|
||||
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")
|
||||
|
||||
class OrganizationMember(Base):
|
||||
@@ -77,9 +76,13 @@ 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")
|
||||
role = Column(String, default="driver") # owner, manager, driver, service_staff
|
||||
|
||||
# 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 a korábbi kódokhoz
|
||||
# Kompatibilitási réteg
|
||||
Organization.vehicles = Organization.assets
|
||||
@@ -1,122 +0,0 @@
|
||||
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, Numeric, DateTime, JSON, Date
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
import uuid
|
||||
from app.db.base import Base
|
||||
|
||||
# 1. GLOBÁLIS KATALÓGUS (A rendszer agya)
|
||||
class VehicleCatalog(Base):
|
||||
__tablename__ = "vehicle_catalog"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
brand = Column(String(100), nullable=False)
|
||||
model = Column(String(100), nullable=False)
|
||||
generation = Column(String(100))
|
||||
year_from = Column(Integer)
|
||||
year_to = Column(Integer)
|
||||
category = Column(String(50))
|
||||
engine_type = Column(String(50))
|
||||
engine_power_kw = Column(Integer)
|
||||
|
||||
# Robot státusz és gyári adatok
|
||||
verification_status = Column(String(20), default="verified")
|
||||
factory_specs = Column(JSON, default={})
|
||||
maintenance_plan = Column(JSON, default={})
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# Kapcsolat az egyedi példányok felé
|
||||
assets = relationship("Asset", back_populates="catalog_entry")
|
||||
|
||||
# 2. EGYEDI ESZKÖZ (Asset) - A felhasználó tulajdona
|
||||
class Asset(Base):
|
||||
__tablename__ = "assets"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
uai = Column(String(100), unique=True, nullable=False) # VIN, HIN vagy Serial Number
|
||||
organization_id = Column(Integer, ForeignKey("data.organizations.id"))
|
||||
catalog_id = Column(Integer, ForeignKey("data.vehicle_catalog.id"), nullable=True)
|
||||
|
||||
asset_type = Column(String(20), nullable=False) # car, boat, plane
|
||||
name = Column(String(255)) # "Kincses E39"
|
||||
|
||||
# Dinamikus állapot
|
||||
current_plate_number = Column(String(20))
|
||||
current_country_code = Column(String(2))
|
||||
odometer_value = Column(Numeric(12, 2), default=0)
|
||||
operating_hours = Column(Numeric(12, 2), default=0)
|
||||
|
||||
# Egyedi DNS (Gyári config + utólagos módosítások)
|
||||
factory_config = Column(JSON, default={})
|
||||
aftermarket_mods = Column(JSON, default={})
|
||||
|
||||
# Állapot és láthatóság (EZ HIÁNYZOTT)
|
||||
status = Column(String(50), default="active")
|
||||
privacy_level = Column(String(20), default="private")
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# Kapcsolatok
|
||||
organization = relationship("Organization", back_populates="assets")
|
||||
catalog_entry = relationship("VehicleCatalog", back_populates="assets")
|
||||
events = relationship("AssetEvent", back_populates="asset", cascade="all, delete-orphan")
|
||||
ratings = relationship("AssetRating", back_populates="asset")
|
||||
|
||||
# 3. DIGITÁLIS SZERVIZKÖNYV / ESEMÉNYTÁR
|
||||
class AssetEvent(Base):
|
||||
__tablename__ = "asset_events"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
asset_id = Column(UUID(as_uuid=True), ForeignKey("data.assets.id"))
|
||||
event_type = Column(String(50)) # SERVICE, REPAIR, INSPECTION, ACCIDENT, PLATE_CHANGE
|
||||
|
||||
odometer_at_event = Column(Numeric(12, 2))
|
||||
hours_at_event = Column(Numeric(12, 2))
|
||||
|
||||
description = Column(String)
|
||||
cost = Column(Numeric(12, 2))
|
||||
currency = Column(String(3), default="EUR")
|
||||
|
||||
is_verified = Column(Boolean, default=False)
|
||||
attachments = Column(JSON, default=[]) # Számlák, fotók linkjei
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
asset = relationship("Asset", back_populates="events")
|
||||
|
||||
# 4. EMOCIONÁLIS ÉRTÉKELÉS
|
||||
class AssetRating(Base):
|
||||
__tablename__ = "asset_ratings"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
asset_id = Column(UUID(as_uuid=True), ForeignKey("data.assets.id"))
|
||||
user_id = Column(Integer, ForeignKey("data.users.id"))
|
||||
|
||||
comfort_score = Column(Integer) # 1-10
|
||||
experience_score = Column(Integer) # 1-10
|
||||
practicality_score = Column(Integer) # 1-10
|
||||
comment = Column(String)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
asset = relationship("Asset", back_populates="ratings")
|
||||
|
||||
# 5. SZOLGÁLTATÓK (Szerelők, Partnerek)
|
||||
class ServiceProvider(Base):
|
||||
__tablename__ = "service_providers"
|
||||
__table_args__ = {"schema": "data"}
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
name = Column(String(255), nullable=False)
|
||||
official_brand_partner = Column(Boolean, default=False)
|
||||
technical_rating_pct = Column(Integer, default=80)
|
||||
location_city = Column(String(100))
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
# --- KOMPATIBILITÁSI RÉTEG ---
|
||||
Vehicle = Asset
|
||||
ServiceRecord = AssetEvent
|
||||
Reference in New Issue
Block a user