RABC fejlesztése és beélesítése hibák kijavításával

This commit is contained in:
Roo
2026-06-18 18:09:51 +00:00
parent 611307a24b
commit fe3c32597d
57 changed files with 4689 additions and 655 deletions

View File

@@ -22,16 +22,17 @@ if TYPE_CHECKING:
from ..marketplace.service_request import ServiceRequest
class UserRole(str, enum.Enum):
superadmin = "superadmin"
admin = "admin"
region_admin = "region_admin"
country_admin = "country_admin"
moderator = "moderator"
sales_agent = "sales_agent"
user = "user"
service_owner = "service_owner"
fleet_manager = "fleet_manager"
driver = "driver"
"""Rendszerszintű (System-level) szerepkörök.
Csak a globális platform szerepkörök maradnak itt.
Szervezeti szerepkörök: lásd OrgUserRole a marketplace/organization.py-ban.
"""
SUPERADMIN = "SUPERADMIN"
ADMIN = "ADMIN"
MODERATOR = "MODERATOR"
SALES_REP = "SALES_REP"
SERVICE_MGR = "SERVICE_MGR"
USER = "USER"
class Person(Base):
"""
@@ -133,7 +134,7 @@ class User(Base):
role: Mapped[UserRole] = mapped_column(
PG_ENUM(UserRole, name="userrole", schema="identity"),
default=UserRole.user
default=UserRole.USER
)
person_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("identity.persons.id"))

View File

@@ -25,17 +25,25 @@ class OrgType(str, enum.Enum):
business = "business"
class OrgUserRole(str, enum.Enum):
"""Szervezeti (Organization-level) szerepkörök.
Ezek a szerepkörök a szervezeten/flottán belüli jogosultságokat határozzák meg.
Minden szerepkörhöz egyedi JSON permissions objektum tartozik a fleet.org_roles táblában.
"""
OWNER = "OWNER"
ADMIN = "ADMIN"
MANAGER = "MANAGER"
MEMBER = "MEMBER"
AGENT = "AGENT"
ACCOUNTANT = "ACCOUNTANT"
DRIVER = "DRIVER"
VIEWER = "VIEWER"
class OrgRole(Base):
"""
Dinamikus szerepkör modell (RBAC).
Dinamikus szerepkör modell (RBAC Phase 2).
A szervezeti szerepkörök adatbázis-vezéreltek, nem hardkódolt Enum-ok.
Alapértelmezett szerepkörök: OWNER, ADMIN, MANAGER, MEMBER, AGENT.
Alapértelmezett szerepkörök: OWNER, ADMIN, ACCOUNTANT, DRIVER, VIEWER.
permissions: JSONB oszlop a capability-alapú jogosultságok tárolására.
Példa: {"can_add_expense": True, "can_approve_expense": False}
"""
__tablename__ = "org_roles"
__table_args__ = {"schema": "fleet"}
@@ -48,6 +56,16 @@ class OrgRole(Base):
priority: Mapped[int] = mapped_column(Integer, default=0)
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
# ── RBAC Phase 2: JSONB permissions column ──
# Stores capability flags for this role, e.g.:
# {"can_add_expense": True, "can_approve_expense": True, "can_view_financials": True}
permissions: Mapped[dict] = mapped_column(
JSONB,
nullable=False,
default=dict,
server_default=text("'{}'::jsonb")
)
class Organization(Base):
"""
@@ -119,9 +137,18 @@ class Organization(Base):
is_deleted: Mapped[bool] = mapped_column(Boolean, default=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
# ── 📦 ELŐFIZETÉS / SUBSCRIPTION ──
subscription_plan: Mapped[str] = mapped_column(String(30), server_default=text("'FREE'"), index=True)
base_asset_limit: Mapped[int] = mapped_column(Integer, server_default=text("1"))
purchased_extra_slots: Mapped[int] = mapped_column(Integer, server_default=text("0"))
# P0 FEATURE: Subscription Tier FK — connects this org to a system.subscription_tiers record
subscription_tier_id: Mapped[Optional[int]] = mapped_column(
Integer,
ForeignKey("system.subscription_tiers.id"),
nullable=True,
index=True
)
notification_settings: Mapped[Any] = mapped_column(JSON, server_default=text("'{\"notify_owner\": true, \"alert_days_before\": [30, 15, 7, 1]}'::jsonb"))
external_integration_config: Mapped[Any] = mapped_column(JSON, server_default=text("'{}'::jsonb"))
@@ -215,9 +242,12 @@ class OrganizationMember(Base):
# Meghívó lejárati ideje
expires_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
# Dinamikus szerepkör (String, nem Enum) - az OrgRole táblából validáljuk
# Dinamikus szerepkör - PostgreSQL ENUM típus (fleet.orguserrole)
# A PG_ENUM használata biztosítja, hogy az SQLAlchemy megfelelő típuskényszerítést
# (type cast) alkalmazzon a lekérdezéseknél, elkerülve az
# "operator does not exist: fleet.orguserrole = character varying" hibát.
role: Mapped[str] = mapped_column(
String(50),
PG_ENUM(OrgUserRole, name="orguserrole", schema="fleet", create_type=False),
default="MEMBER",
server_default=text("'MEMBER'")
)

View File

@@ -247,6 +247,13 @@ class AssetFinancials(Base):
asset: Mapped["Asset"] = relationship("Asset", back_populates="financials")
class AssetCostStatusEnum(str, enum.Enum):
"""Költség státuszok a jóváhagyási munkafolyamathoz."""
DRAFT = "DRAFT"
PENDING_APPROVAL = "PENDING_APPROVAL"
APPROVED = "APPROVED"
class AssetCost(Base):
""" II. Üzemeltetés és TCO kimutatás. """
__tablename__ = "asset_costs"
@@ -257,15 +264,35 @@ class AssetCost(Base):
category_id: Mapped[int] = mapped_column(Integer, ForeignKey("vehicle.cost_categories.id"), index=True, nullable=False)
amount_net: Mapped[float] = mapped_column(Numeric(18, 2), nullable=False)
amount_gross: Mapped[Optional[float]] = mapped_column(Numeric(18, 2), nullable=True)
vat_rate: Mapped[Optional[float]] = mapped_column(Numeric(5, 2), nullable=True)
currency: Mapped[str] = mapped_column(String(3), default="HUF")
date: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
invoice_number: Mapped[Optional[str]] = mapped_column(String(100), index=True)
document_id: Mapped[Optional[uuid.UUID]] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("system.documents.id"), nullable=True)
# Státusz a jóváhagyási munkafolyamathoz
status: Mapped[str] = mapped_column(String(30), default="DRAFT", server_default=text("'DRAFT'"), index=True)
# Kétirányú hivatkozás az AssetEvent felé (kettős könyvelés elkerülése)
linked_asset_event_id: Mapped[Optional[uuid.UUID]] = mapped_column(
PG_UUID(as_uuid=True),
ForeignKey("vehicle.asset_events.id"),
nullable=True,
index=True
)
data: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb"))
asset: Mapped["Asset"] = relationship("Asset", back_populates="costs")
organization: Mapped["Organization"] = relationship("Organization")
category: Mapped["CostCategory"] = relationship("CostCategory")
# Kapcsolat a hivatkozott eseményhez
linked_event: Mapped[Optional["AssetEvent"]] = relationship(
"AssetEvent",
foreign_keys=[linked_asset_event_id],
post_update=True
)
class VehicleLogbook(Base):
@@ -396,6 +423,13 @@ class AssetEventTypeEnum(str, enum.Enum):
RECALL = "RECALL" # Visszahívás
class AssetEventStatusEnum(str, enum.Enum):
"""Esemény státuszok a feldolgozási munkafolyamathoz."""
DRAFT = "DRAFT"
MISSING_TECH_DATA = "MISSING_TECH_DATA"
COMPLETED = "COMPLETED"
class AssetEvent(Base):
""" Digitális Szervizkönyv - Szerviz, baleset és egyéb jelentős események. """
__tablename__ = "asset_events"
@@ -411,6 +445,17 @@ class AssetEvent(Base):
description: Mapped[Optional[str]] = mapped_column(Text)
cost_id: Mapped[Optional[uuid.UUID]] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.asset_costs.id"), nullable=True)
# Státusz a feldolgozási munkafolyamathoz
status: Mapped[str] = mapped_column(String(30), default="DRAFT", server_default=text("'DRAFT'"), index=True)
# Kétirányú hivatkozás az AssetCost felé (kettős könyvelés elkerülése)
linked_expense_id: Mapped[Optional[uuid.UUID]] = mapped_column(
PG_UUID(as_uuid=True),
ForeignKey("vehicle.asset_costs.id"),
nullable=True,
index=True
)
event_date: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), onupdate=func.now())
@@ -419,7 +464,15 @@ class AssetEvent(Base):
asset: Mapped["Asset"] = relationship("Asset", back_populates="events")
user: Mapped[Optional["User"]] = relationship("User")
organization: Mapped[Optional["Organization"]] = relationship("Organization")
cost: Mapped[Optional["AssetCost"]] = relationship("AssetCost")
cost: Mapped[Optional["AssetCost"]] = relationship("AssetCost", foreign_keys=[cost_id])
# Kapcsolat a hivatkozott költséghez
linked_expense: Mapped[Optional["AssetCost"]] = relationship(
"AssetCost",
foreign_keys=[linked_expense_id],
post_update=True,
primaryjoin="AssetEvent.linked_expense_id == AssetCost.id"
)
class ExchangeRate(Base):