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,43 +1,54 @@
from pydantic import BaseModel, Field
from typing import Optional
from pydantic import BaseModel, ConfigDict, Field
from typing import Optional, Dict, Any, List
from uuid import UUID
from datetime import datetime
class AssetCreate(BaseModel):
# Alapadatok
make: str = Field(..., example="Ford")
model: str = Field(..., example="Mondeo")
vin: str = Field(..., min_length=17, max_length=17, description="Alvázszám")
license_plate: Optional[str] = Field(None, max_length=20, example="RRR-555")
# Nemzetközi és Admin szempontok
vehicle_class: str = Field("land", description="land, sea, air - Admin által bővíthető")
fuel_type: str = Field(..., example="Diesel", description="Admin által definiált üzemanyag típusok")
# Technikai adatok
engine_description: Optional[str] = Field(None, example="2.0 TDCI")
year_of_manufacture: int = Field(..., ge=1900, le=2100)
# Kezdő állapot
current_reading: int = Field(..., ge=0, description="Kezdő km/üzemóra állás")
reading_unit: str = Field("km", description="km, miles, hours - Nemzetközi beállítás")
# Felhasználói adatok
name: Optional[str] = Field(None, description="Egyedi elnevezés")
# --- KATALÓGUS SÉMÁK (Gyári adatok) ---
class AssetCatalogBase(BaseModel):
make: str
model: str
generation: Optional[str] = None
year_from: Optional[int] = None
year_to: Optional[int] = None
vehicle_class: Optional[str] = None
fuel_type: Optional[str] = None
engine_code: Optional[str] = None
class AssetResponse(BaseModel):
id: UUID
catalog_id: Optional[int]
vin: str
license_plate: Optional[str] = None # JAVÍTVA: Lehet None a válaszban
class AssetCatalogResponse(AssetCatalogBase):
id: int
factory_data: Optional[Dict[str, Any]] = None # A robot által gyűjtött adatok
model_config = ConfigDict(from_attributes=True)
# --- JÁRMŰ SÉMÁK (Asset) ---
class AssetBase(BaseModel):
vin: str = Field(..., min_length=17, max_length=17)
license_plate: str
name: Optional[str] = None
fuel_type: str
vehicle_class: str
is_verified: bool
year_of_manufacture: int
system_mileage: int
quality_index: float
created_at: datetime
year_of_manufacture: Optional[int] = None
class Config:
from_attributes = True
class AssetCreate(AssetBase):
# A létrehozáshoz kellenek a katalógus infók is
make: str
model: str
vehicle_class: Optional[str] = "land"
fuel_type: Optional[str] = None
current_reading: Optional[int] = 0
class AssetResponse(AssetBase):
id: UUID
catalog_id: int
is_verified: bool
status: str
model_config = ConfigDict(from_attributes=True)
# --- DIGITÁLIS IKER (Full Profile) ---
# Ez a séma felel a 9 pontos költség és a mélységi szerviz adatok átadásáért
class AssetFullProfile(BaseModel):
identity: Dict[str, Any]
telemetry: Dict[str, Any]
financial_summary: Dict[str, Any]
service_history: List[Dict[str, Any]]
model_config = ConfigDict(from_attributes=True)

View File

@@ -0,0 +1,35 @@
from pydantic import BaseModel, Field
from typing import Optional, Dict, Any
from datetime import datetime
from decimal import Decimal
from uuid import UUID
class AssetCostBase(BaseModel):
"""Alap költség adatok (Frontendről érkező bevitel)."""
cost_type: str = Field(..., description="fuel, service, fine, insurance, toll, etc.")
amount_local: Decimal = Field(..., description="A fizetett bruttó összeg helyi devizában")
currency_local: str = Field("HUF", min_length=3, max_length=3)
date: datetime = Field(default_factory=datetime.now)
mileage_at_cost: Optional[int] = Field(None, description="Kilométeróra állása a költség rögzítésekor")
description: Optional[str] = None
net_amount_local: Optional[Decimal] = None
vat_rate: Optional[Decimal] = Field(27.0, description="ÁFA kulcs (pl. 27.0)")
data: Optional[Dict[str, Any]] = Field(default_factory=dict, description="Extra adatok (pl. helyszín, számlaszám)")
class AssetCostCreate(AssetCostBase):
"""Költség rögzítésekor használt séma."""
asset_id: UUID
organization_id: int
class AssetCostResponse(AssetCostBase):
"""Visszatérő adat modell a frontend felé."""
id: UUID
asset_id: UUID
organization_id: int
driver_id: Optional[int]
amount_eur: Decimal
exchange_rate_used: Decimal
created_at: Optional[datetime] = None
class Config:
from_attributes = True

View File

@@ -1,5 +1,5 @@
from pydantic import BaseModel, EmailStr, Field
from typing import Optional, Dict
from typing import Optional, Dict, Any
from datetime import date
# --- STEP 1: LITE REGISTRATION ---
@@ -9,6 +9,8 @@ class UserLiteRegister(BaseModel):
first_name: str
last_name: str
region_code: str = "HU"
lang: str = Field("hu", description="Választott nyelv kódja")
timezone: str = Field("Europe/Budapest", description="Felhasználó időzónája")
class UserLogin(BaseModel):
email: EmailStr
@@ -30,15 +32,15 @@ class UserKYCComplete(BaseModel):
birth_date: date
mothers_last_name: str
mothers_first_name: str
# Hibrid Címmezők
address_zip: str
address_city: str
address_street_name: str
address_street_type: str
address_house_number: str
address_hrsz: Optional[str] = None # Helyrajzi szám
address_hrsz: Optional[str] = None
identity_docs: Dict[str, DocumentDetail]
ice_contact: ICEContact
preferred_currency: Optional[str] = Field("HUF", max_length=3)
# --- COMMON & SECURITY ---
class PasswordResetRequest(BaseModel):
@@ -53,4 +55,13 @@ class PasswordResetConfirm(BaseModel):
class Token(BaseModel):
access_token: str
token_type: str
is_active: bool
is_active: bool
class TokenPayload(BaseModel):
"""JWT Token payload struktúrája validációhoz."""
sub: Optional[str] = None
role: Optional[str] = None
rank: Optional[int] = 0
scope_level: Optional[str] = None
scope_id: Optional[str] = None
region: Optional[str] = None

View File

@@ -1,46 +0,0 @@
from pydantic import BaseModel, EmailStr, Field, field_validator
from typing import Optional, List
from datetime import date
class UserRegister(BaseModel):
# --- AUTH ---
email: EmailStr = Field(..., example="teszt.user@profibot.hu")
password: Optional[str] = Field(None, min_length=8, description="Social login esetén üres maradhat")
# --- IDENTITY (KYC Step 2) ---
last_name: str = Field(..., min_length=2)
first_name: str = Field(..., min_length=2)
mothers_name: str = Field(..., description="Anyja születési neve")
birth_place: Optional[str] = None
birth_date: Optional[date] = None
# --- OKMÁNYOK (Banki szint) ---
id_card_number: Optional[str] = None
id_card_expiry: Optional[date] = None
driver_license_number: Optional[str] = None
driver_license_expiry: Optional[date] = None
driver_license_categories: List[str] = Field(default_factory=list, example=["B", "A"])
# --- SPECIÁLIS ENGEDÉLYEK ---
boat_license_number: Optional[str] = None
pilot_license_number: Optional[str] = None
# --- SYSTEM ---
region_code: str = Field(default="HU")
invite_token: Optional[str] = None
social_provider: Optional[str] = None
social_id: Optional[str] = None
@field_validator('region_code')
@classmethod
def validate_region(cls, v: str) -> str:
return v.upper() if v else "HU"
class Token(BaseModel):
access_token: str
token_type: str
class UserLogin(BaseModel):
email: EmailStr
password: str

View File

@@ -15,6 +15,8 @@ class CorpOnboardIn(BaseModel):
tax_number: str
country_code: str = "HU"
language: str = Field("hu", description="A szervezet alapértelmezett nyelve")
default_currency: str = Field("HUF", description="A szervezet alapértelmezett pénzneme")
reg_number: Optional[str] = None
# Atomizált Címkezelés