RABC fejlesztése és beélesítése hibák kijavításával
This commit is contained in:
505
.roo/history.md
505
.roo/history.md
@@ -446,3 +446,508 @@ Két P0 szintű probléma javítása: (1) "Univerzális Szolgáltatások" blokkb
|
||||
- Seed script: ✅ PASS (134 rekord, ebből 12 univerzális: 4 Level 1 + 8 Level 2)
|
||||
- Autocomplete API `q=Benzinkút`: ✅ PASS (1 találat: Benzinkút, level=2)
|
||||
- Autocomplete API `q=Étterem`: ✅ PASS (3 találat: Étterem, Gyorsétterem / Büfé, Étterem / Büfé)
|
||||
|
||||
## 2026-06-18 - P0 Critical: Financial Math Reversal (Gross-First Accounting)
|
||||
|
||||
### 🎯 Cél
|
||||
A Phase 2 Financial Architecture felülvizsgálata során a Vezető Tervező (Architect) jelezte, hogy a pénzügyi logika fordítva van: az EU-s számvitelben a BRUTTÓ (Gross) az elsődleges forrásigazság (Source of Truth), nem a Nettó. A teljes matematika megfordítása: `_calculate_gross_from_net()` → `_calculate_net_from_gross()`, `amount_gross` kötelező, `amount_net` opcionális.
|
||||
|
||||
### 🔧 Változtatások
|
||||
|
||||
**1. SCHEMA - [`asset_cost.py`](backend/app/schemas/asset_cost.py:9):**
|
||||
- `amount_gross` áthelyezve kötelező mezővé (`Field(..., description="Bruttó összeg — KÖTELEZŐ, elsődleges forrás")`)
|
||||
- `amount_net` opcionális (`Optional[Decimal] = Field(None, ...)`)
|
||||
- `@model_validator(mode='after')` hozzáadva: ha `amount_net=None` és `vat_rate=None`, akkor `amount_net = amount_gross` és `vat_rate = 0`
|
||||
|
||||
**2. SCHEMA - [`asset_event.py`](backend/app/schemas/asset_event.py:69):**
|
||||
- `model_validate` frissítve: `cost_amount` elsődlegesen `amount_gross`-ból, fallback `amount_net`-ből
|
||||
|
||||
**3. SCHEMA - [`asset.py`](backend/app/schemas/asset.py:415):**
|
||||
- `model_validate` frissítve: `cost_amount` elsődlegesen `amount_gross`-ból, fallback `amount_net`-ből
|
||||
|
||||
**4. ENDPOINT - [`expenses.py`](backend/app/api/v1/endpoints/expenses.py:49):**
|
||||
- `_calculate_gross_from_net()` → `_calculate_net_from_gross(amount_gross, vat_rate)` átnevezve
|
||||
- Képlet megfordítva: `net = gross / (1 + vat_rate/100)`
|
||||
- VAT handling logika átírva: ha `vat_rate` adott és `amount_net` nincs, net kiszámítása gross-ból
|
||||
- Ha `amount_net` és `vat_rate` is adott, `vat_rate` felülírva a back-számított értékkel
|
||||
- Ha egyik sincs, `amount_net = amount_gross`, `vat_rate = 0`
|
||||
|
||||
**5. ENDPOINT - [`assets.py`](backend/app/api/v1/endpoints/assets.py):**
|
||||
- `create_maintenance_record`: `cost` mező gross-ként kezelve, `amount_gross` és `amount_net` is a cost értékkel töltve
|
||||
- `create_asset_event`: `cost_amount` gross-ként kezelve
|
||||
- `list_asset_costs`: response-ban `amount_gross` az `amount_net` előtt
|
||||
|
||||
### ✅ Verifikáció
|
||||
- Python szintaxis ellenőrzés: ✅ 5/5 fájl OK
|
||||
- Sync engine: ✅ 1087/1087 OK, 0 fix, 0 extra
|
||||
- Unit math tesztek: ✅ 5/5 passed (Gross=12700→Net=10000 27% VAT, Gross=10500→Net=10000 5% VAT, stb.)
|
||||
- E2E API tesztek: ✅ 3/3 passed (Gross only, Gross+27%, Gross+5%)
|
||||
- A teljes Gross-first matematika helyesen működik: a bruttó összeg a Source of Truth, a nettó és ÁFA visszaszámolva
|
||||
|
||||
## 2026-06-18 - P0 Frontend Cost Payload Audit & Fix (Gross-First Compliance)
|
||||
|
||||
### 🎯 Cél
|
||||
A backend már szigorúan Gross-first rendszer a pénzügyi adatokra, de a frontend űrlapok még `amount_net`-et küldtek a payloadban, ami `amount_gross: null`-t eredményezett az adatbázisban. Teljes audit és javítás.
|
||||
|
||||
### 🔧 Változtatások
|
||||
|
||||
**1. FRONTEND - [`cost.ts`](frontend/src/stores/cost.ts:8):**
|
||||
- `AddExpensePayload` interface: `amount_net` → `amount_gross` (elsődleges mező)
|
||||
- `ExpenseResponse` interface: `amount_net` → `amount_gross`
|
||||
- `addExpense()` body builder: `amount_net: payload.amount_net` → `amount_gross: payload.amount_gross`
|
||||
|
||||
**2. FRONTEND - [`ComplexExpenseModal.vue`](frontend/src/components/dashboard/ComplexExpenseModal.vue:268):**
|
||||
- Payload `amount_net: form.amount` → `amount_gross: form.amount`
|
||||
|
||||
**3. FRONTEND - [`SimpleFuelModal.vue`](frontend/src/components/dashboard/SimpleFuelModal.vue:193):**
|
||||
- Payload `amount_net: form.amount` → `amount_gross: form.amount`
|
||||
|
||||
**4. BACKEND - [`asset_cost.py`](backend/app/schemas/asset_cost.py:27):**
|
||||
- `@model_validator(mode='before')` safety net hozzáadva: ha a bejövő JSON `amount_net`-et tartalmaz, de `amount_gross` hiányzik, automatikusan átmásolja az `amount_net` értékét az `amount_gross`-ba. Ez véd a régebbi cache-elt böngészők ellen.
|
||||
|
||||
### ✅ Verifikáció
|
||||
- Vite build: ✅ Sikeres (216 module transformed, 0 error)
|
||||
- Backend schema import: ✅ Sikeres
|
||||
- Safety net tesztek: ✅ 3/3 passed
|
||||
- `amount_net` only → `amount_gross` auto-copy: ✅
|
||||
- Both fields present → no interference: ✅
|
||||
- `amount_gross` only → no change: ✅
|
||||
|
||||
### 🔍 Utólagos Adatjavítás (Data Fix)
|
||||
A felhasználó jelentése alapján az UOK795 (Honda CB1000R) járműnél az utolsó 2 rögzített költség 0 Ft-ot mutatott. Vizsgálat után kiderült:
|
||||
|
||||
**Gyökér ok:** A P0 audit előtt rögzített rekordoknál a frontend `amount_net`-et küldött, de a backend `amount_gross`-t 0-ra állította (a `validate_gross_first` `mode='after'` validátor `None` → `Decimal("0")`-ra állította a hiányzó gross mezőt).
|
||||
|
||||
**Érintett rekordok (országosan 2 db):**
|
||||
1. `3468e81a` - UOK795 (Honda CB1000R) - 12 000 Ft tankolás → `amount_gross` javítva 0→12000
|
||||
2. `ea0395ea` - QWE123 (APRILIA af1) - 4 000 Ft → `amount_gross` javítva 0→4000
|
||||
|
||||
**Javítás:** `UPDATE vehicle.asset_costs SET amount_gross = amount_net, vat_rate = 0.00 WHERE amount_gross=0 AND amount_net>0`
|
||||
|
||||
## 2026-06-18 - Jogosultságkezelés Audit (Permission System Audit)
|
||||
|
||||
### 🎯 Cél
|
||||
Teljes körű audit a jogosultsági szintek kezeléséről, kiterjedve az adatbázis táblákra, a backend kódra és a frontend rétegre.
|
||||
|
||||
### 🔍 Vizsgált Területek
|
||||
1. **Adatbázis (6 tábla):** identity.users, fleet.org_roles, fleet.organization_members, system.system_parameters, system.pending_actions, system.subscription_tiers
|
||||
2. **Backend:** UserRole enum, DEFAULT_RANK_MAP, RBAC osztály, get_current_admin, scoped RBAC, Dual Control (SecurityService), JWT token
|
||||
3. **Frontend:** authStore.ts (UserRole típus), Router Guard
|
||||
|
||||
### 🔴 Feltárt Kritikus Problémák
|
||||
- **P1:** fleet.org_roles tábla ÜRES - dinamikus szerepkör rendszer nem seedelt
|
||||
- **P2:** DEFAULT_RANK_MAP inkonzisztens a UserRole enum-mal (6 rang soha nem kerül kiosztásra)
|
||||
- **P3:** Enum duplikáció az adatbázisban (kis/nagybetű variánsok)
|
||||
- **P4:** Frontend/backend UserRole eltérés (5 vs 10 role)
|
||||
- **P5:** parameter_scope enum 3 különböző sémában definiálva
|
||||
|
||||
### 📄 Dokumentáció
|
||||
- [`docs/permission_system_audit_2026-06-18.md`](docs/permission_system_audit_2026-06-18.md) - Teljes audit jelentés Mermaid diagrammal
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-18 - RBAC Phase 1: Foundation & Seeding (P0 Critical)
|
||||
|
||||
### 🎯 Cél
|
||||
RBAC Phase 1 migráció Fine-Grained, Capability-Driven architektúrára. System-level (UserRole) és Organization-level (OrgUserRole) szerepkörök szétválasztása, SYSTEM_CAPABILITIES_MATRIX létrehozása, fleet.org_roles tábla seedelése.
|
||||
|
||||
### 🔧 Változtatások
|
||||
|
||||
**1. [`UserRole`](backend/app/models/identity/identity.py:24) enum tisztítás:**
|
||||
- 10 régi érték helyett 6 system-level role: `SUPERADMIN, ADMIN, MODERATOR, SALES_REP, SERVICE_MGR, USER`
|
||||
- Eltávolítva: `region_admin, country_admin, sales_agent, service_owner, fleet_manager, driver`
|
||||
- Hozzáadva: `SALES_REP, SERVICE_MGR`
|
||||
- Értékek UPPERCASE-re változtatva
|
||||
|
||||
**2. [`SYSTEM_CAPABILITIES_MATRIX`](backend/app/core/capabilities.py:1) létrehozása:**
|
||||
- Új fájl: `backend/app/core/capabilities.py`
|
||||
- `Capability` osztály konstansokkal (~26 capability key)
|
||||
- `SYSTEM_CAPABILITIES_MATRIX` dict minden role-hoz boolean capability mátrix
|
||||
- Segédfüggvények: `get_capabilities_for_role()`, `role_has_capability()`
|
||||
|
||||
**3. [`OrgUserRole`](backend/app/models/marketplace/organization.py:27) enum frissítés:**
|
||||
- Új értékek: `OWNER, ADMIN, ACCOUNTANT, DRIVER, VIEWER`
|
||||
- Eltávolítva: `MANAGER, MEMBER, AGENT`
|
||||
|
||||
**4. [`DEFAULT_RANK_MAP`](backend/app/core/security.py:17) tisztítás:**
|
||||
- Csak az új 6 system-level role + GUEST maradt
|
||||
- SERVICE_MGR(40), SALES_REP(30) hozzáadva
|
||||
|
||||
**5. Függő kódok frissítése:**
|
||||
- [`deps.py`](backend/app/api/deps.py:106) - `check_resource_access()` és `get_current_admin()` frissítve
|
||||
- [`rbac.py`](backend/app/core/rbac.py:7) - `UserRole.SUPERADMIN` referencia javítva
|
||||
- [`billing_engine.py`](backend/app/services/billing_engine.py:37) - `RBAC_DISCOUNTS` tisztítva
|
||||
|
||||
**6. PostgreSQL enum migráció:**
|
||||
- [`migrate_rbac_phase1_enums.py`](backend/app/scripts/migrate_rbac_phase1_enums.py) - CREATE TYPE → ALTER COLUMN → DROP TYPE minta
|
||||
- Régi értékek mapelése: `superadmin→SUPERADMIN, admin→ADMIN, moderator→MODERATOR, sales_agent→SALES_REP, user/service_owner/fleet_manager/driver→USER`
|
||||
- ✅ Sikeres: `identity.userrole` enum 6 értékkel, meglévő user role-ok helyesen átkonvertálva
|
||||
|
||||
**7. [`seed_org_roles.py`](backend/app/scripts/seed_org_roles.py) - fleet.org_roles seedelés:**
|
||||
- 5 org role: OWNER(100), ADMIN(80), ACCOUNTANT(60), DRIVER(40), VIEWER(20)
|
||||
- Minden role JSON permissions objektummal
|
||||
- ✅ Sikeres: 5 rekord beszúrva
|
||||
|
||||
### ✅ Verifikáció
|
||||
- Schema sync: **1087 items OK, 0 fixed, 0 shadow data**
|
||||
- `identity.userrole` enum: `SUPERADMIN, ADMIN, MODERATOR, SALES_REP, SERVICE_MGR, USER`
|
||||
- `fleet.org_roles`: 5 rekord (OWNER, ADMIN, ACCOUNTANT, DRIVER, VIEWER)
|
||||
- Meglévő user role-ok: `USER, SUPERADMIN, ADMIN` - helyesen konvertálva
|
||||
|
||||
## 2026-06-18 - P0 EMERGENCY: Superadmin User ID 1 Recreated from Scratch
|
||||
|
||||
### 🎯 Cél
|
||||
A hard-deletelt User ID 1 rekord fizikai újraélesztése: SUPERADMIN joggal, a meglévő Person ID 1-hez kapcsolva, és a Profibot Test Fleet (Organization ID 15) garázshoz OWNER szerepkörrel hozzárendelve.
|
||||
|
||||
### 🔧 Változtatások
|
||||
|
||||
**1. Jelszó hash generálás:**
|
||||
- [`security.py`](backend/app/core/security.py:14): `get_password_hash('Superadmin123!')` használatával bcrypt hash generálva
|
||||
- Hash: `$2b$12$ozXD07/qiFl4eKg.RbadDeGcnMukY3tOpBPEgOza7VsqcbuUW7iq2`
|
||||
- Verifikáció: `verify_password('Superadmin123!', hash)` → `True`
|
||||
|
||||
**2. Email konfliktus feloldása:**
|
||||
- User ID 29 (korábbi `superadmin@profibot.hu`) átnevezve → `superadmin_old@profibot.hu`
|
||||
|
||||
**3. User ID 1 létrehozása (`identity.users`):**
|
||||
- `INSERT INTO identity.users (id=1, email='superadmin@profibot.hu', role='SUPERADMIN', person_id=1, ...)`
|
||||
- Kapcsolat a Person ID 1-hez (Super Admin személy)
|
||||
|
||||
**4. Garázs hozzárendelés (`fleet.organization_members`):**
|
||||
- `INSERT INTO fleet.organization_members (organization_id=15, user_id=1, person_id=1, role='OWNER', ...)`
|
||||
- Organization: "Profibot Test Fleet" (ID 15)
|
||||
|
||||
### ✅ Verifikáció
|
||||
- `identity.users WHERE id=1`: `email=superadmin@profibot.hu, role=SUPERADMIN, person_id=1, is_active=true`
|
||||
- `fleet.organization_members WHERE user_id=1`: `org_id=15 (Profibot Test Fleet), role=OWNER, status=active`
|
||||
- `identity.persons WHERE id=1`: `last_name=Admin, first_name=Super` (meglévő személy)
|
||||
- Jelszó hash verifikáció: `True`
|
||||
- Belépési adatok: `superadmin@profibot.hu` / `Superadmin123!`
|
||||
|
||||
## 2026-06-18: P0 CRITICAL DEBUG - 500 Internal Server Error on POST /api/v1/auth/login (UserRole UPPERCASE migration fix)
|
||||
|
||||
### 🎯 Cél
|
||||
A Phase 1 RBAC Enum UPPERCASE migráció után a `/api/v1/auth/login` endpoint 500-as hibát dobott `LookupError: 'ADMIN' is not among the defined enum values` üzenettel. A régi konténer cache-elt SQLAlchemy metaadatokat használt, és a `billing_engine.py`-ben lévő `UserRole.user` (kisbetűs) hivatkozás miatt az új konténer sem indult el.
|
||||
|
||||
### 🔧 Változtatások (25 javítás 14 fájlban)
|
||||
|
||||
**Kritikus javítások (startup-blokkoló):**
|
||||
- [`backend/app/services/billing_engine.py:70`](backend/app/services/billing_engine.py:70) — `UserRole.user` → `UserRole.USER` (2 helyen)
|
||||
- [`backend/app/services/social_auth_service.py:33`](backend/app/services/social_auth_service.py:33) — `UserRole.user` → `UserRole.USER`
|
||||
- [`backend/app/services/search_service.py:41`](backend/app/services/search_service.py:41) — `UserRole.superadmin, UserRole.admin` → `UserRole.SUPERADMIN, UserRole.ADMIN`
|
||||
- [`backend/app/services/auth_service.py:110`](backend/app/services/auth_service.py:110) — `UserRole.user` → `UserRole.USER`
|
||||
- [`backend/app/api/v1/endpoints/system_parameters.py:125`](backend/app/api/v1/endpoints/system_parameters.py:125) — `UserRole.superadmin, UserRole.admin` → `UserRole.SUPERADMIN, UserRole.ADMIN`
|
||||
- [`backend/app/schemas/admin.py:23`](backend/app/schemas/admin.py:23) — `UserRole.admin, UserRole.superadmin` → `UserRole.ADMIN, UserRole.SUPERADMIN`
|
||||
- [`backend/app/api/v1/endpoints/finance_admin.py:27`](backend/app/api/v1/endpoints/finance_admin.py:27) — `UserRole.superadmin, UserRole.admin` → `UserRole.SUPERADMIN, UserRole.ADMIN`
|
||||
- [`backend/app/api/v1/endpoints/admin.py:195`](backend/app/api/v1/endpoints/admin.py:195) — `UserRole.superadmin` → `UserRole.SUPERADMIN` (2 helyen)
|
||||
- [`backend/app/api/v1/endpoints/security.py:39`](backend/app/api/v1/endpoints/security.py:39) — `UserRole.admin, UserRole.superadmin` → `UserRole.ADMIN, UserRole.SUPERADMIN` (5 helyen)
|
||||
- [`backend/app/core/security.py:56`](backend/app/core/security.py:56) — `DEFAULT_RANK_MAP` már helyes (UPPERCASE kulcsok)
|
||||
- [`backend/app/tests/e2e/test_admin_security.py:21`](backend/app/tests/e2e/test_admin_security.py:21) — `UserRole.user` → `UserRole.USER`, `UserRole.admin` → `UserRole.ADMIN`
|
||||
- [`backend/app/scripts/seed_integration_data.py:158`](backend/app/scripts/seed_integration_data.py:158) — `UserRole.admin` → `UserRole.ADMIN`, `UserRole.superadmin` → `UserRole.SUPERADMIN`
|
||||
|
||||
### ✅ Verifikáció
|
||||
- `POST /api/v1/auth/login` with `admin@profibot.hu` / `Admin123!` → **200 OK**
|
||||
- JWT token payload: `{"sub": "2", "role": "ADMIN", "rank": 90, "scope_level": "system", "scope_id": "2"}`
|
||||
- Konténer indítás: sikeres (nem omlik össze `AttributeError`-rel)
|
||||
- 3 db seed script nem javítható (EACCES jogosultsági hiba) — nem kritikus
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-18 - RBAC Phase 2: Capability Dependencies (Kapuőrök)
|
||||
|
||||
### 🎯 Cél
|
||||
P0 kritikus feladat: JSONB-alapú capability dependency-k bevezetése a hardcoded szerepkör-ellenőrzések helyett. Két új FastAPI dependency: `RequireSystemCapability` (system-level) és `RequireOrgCapability` (org-level).
|
||||
|
||||
### 🔧 Változtatások
|
||||
|
||||
**1. MODELL - [`organization.py`](backend/app/models/marketplace/organization.py:63):**
|
||||
- `permissions` JSONB oszlop hozzáadva az `OrgRole` modellhez (`server_default='{}'::jsonb`)
|
||||
|
||||
**2. DEPENDENCY - [`deps.py`](backend/app/api/deps.py:184):**
|
||||
- [`RequireSystemCapability(capability_name: str)`](backend/app/api/deps.py:184) — Ellenőrzi a `SYSTEM_CAPABILITIES_MATRIX`-ot a system-level role-okhoz. SUPERADMIN automatikusan átmegy.
|
||||
- [`RequireOrgCapability(capability_name: str)`](backend/app/api/deps.py:221) — Lekérdezi a `fleet.org_roles.permissions` JSONB oszlopot. Fallback: `OrganizationMember.permissions`. SUPERADMIN bypass.
|
||||
|
||||
**3. EXPENSES - [`expenses.py`](backend/app/api/v1/endpoints/expenses.py:46):**
|
||||
- `AUTO_APPROVED_ROLES = {"OWNER", "ADMIN"}` eltávolítva
|
||||
- `_check_org_capability()` helper hozzáadva — JSONB-ből olvassa a `can_approve_expense` flag-et
|
||||
- Költség státusz meghatározása: `can_approve` → APPROVED, fuel (category_id=1) → APPROVED, egyéb → PENDING_APPROVAL
|
||||
|
||||
**4. ASSETS - [`assets.py`](backend/app/api/v1/endpoints/assets.py:46):**
|
||||
- `AUTO_APPROVED_ROLES = {"OWNER", "ADMIN"}` eltávolítva
|
||||
- `_check_org_capability()` helper hozzáadva
|
||||
- `create_asset_event` végpontban az esemény státusz JSONB-alapú ellenőrzésre váltva
|
||||
|
||||
**5. SEED - [`seed_org_roles.py`](backend/app/scripts/seed_org_roles.py:154):**
|
||||
- `permissions` paraméter hozzáadva az `OrgRole` konstruktorhoz
|
||||
- Meglévő role-ok permissions mezőjének frissítése (RBAC Phase 2 update loop)
|
||||
|
||||
### 🗄️ Adatbázis
|
||||
- `sync_engine` futtatva: `permissions` JSONB oszlop létrehozva a `fleet.org_roles` táblában
|
||||
- 5 org role permissions adata feltöltve: OWNER, ADMIN, ACCOUNTANT, DRIVER, VIEWER
|
||||
|
||||
### ✅ Verifikáció
|
||||
- Backend indítás: sikeres (nincs import error)
|
||||
- `POST /api/v1/expenses/` ADMIN userrel → **201 CREATED**, `expense_status: "APPROVED"` (mert ADMIN rendelkezik `can_approve_expense: True` képességgel)
|
||||
- `fleet.org_roles` JSONB permissions: mind az 5 role helyesen tárolva
|
||||
|
||||
## 2026-06-18: RBAC Phase 3 - Frontend Reactivity & UI Capabilities Integration
|
||||
|
||||
### 🎯 Cél
|
||||
P0 kritikus feladat: Frontend reaktivitás javítása (F5 Bug) és UI képesség-alapú megjelenítés bevezetése.
|
||||
|
||||
### 🔧 Változtatások
|
||||
|
||||
**Task 1 - F5 Bug javítás (Direct Mutation / Explicit Fetch pattern):**
|
||||
- [`CostsActionsCard.vue`](frontend/src/components/dashboard/CostsActionsCard.vue:110): `cost-saved` event emit hozzáadva, ami a költség mentése után értesíti a szülőt
|
||||
- [`DashboardView.vue`](frontend/src/views/DashboardView.vue:206): `refreshCostsTrigger` ref (számláló) és `onCostSaved()` handler hozzáadva
|
||||
- [`DashboardView.vue`](frontend/src/views/DashboardView.vue:130): `:refresh-trigger="refreshCostsTrigger"` prop átadva a VehicleDetailModal-nak
|
||||
- [`VehicleDetailModal.vue`](frontend/src/components/vehicle/VehicleDetailModal.vue:778): `refreshTrigger` prop és `watch` hozzáadva a költségek újratöltéséhez
|
||||
|
||||
**Task 2 - Backend /users/me exposure:**
|
||||
- [`schemas/user.py`](backend/app/schemas/user.py:70): `system_capabilities: Dict[str, bool]` és `org_capabilities: Dict[str, Dict[str, bool]]` mezők hozzáadva a `UserResponse`-hoz
|
||||
- [`endpoints/users.py`](backend/app/api/v1/endpoints/users.py:106): `_build_user_response()` kiegészítve `system_capabilities` feloldással a `SYSTEM_CAPABILITIES_MATRIX`-ból
|
||||
- [`endpoints/users.py`](backend/app/api/v1/endpoints/users.py:205): `read_users_me()` kiegészítve `org_capabilities` aszinkron feloldásával az aktív org tagságokból
|
||||
|
||||
**Task 3 - Frontend Auth Store capability helpers:**
|
||||
- [`stores/auth.ts`](frontend/src/stores/auth.ts:73): `UserProfile` interface kiegészítve `system_capabilities` és `org_capabilities` mezőkkel
|
||||
- [`stores/auth.ts`](frontend/src/stores/auth.ts:111): `hasSystemCapability(capability)` helper - ellenőrzi a rendszerszintű képességet
|
||||
- [`stores/auth.ts`](frontend/src/stores/auth.ts:122): `hasOrgCapability(orgId, capability)` helper - ellenőrzi a szervezeti képességet
|
||||
- [`stores/auth.ts`](frontend/src/stores/auth.ts:133): `getOrgCapabilities(orgId)` helper - visszaadja az összes szervezeti képességet
|
||||
|
||||
**Task 4 - UI Integration Pilot:**
|
||||
- [`VehicleDetailModal.vue`](frontend/src/components/vehicle/VehicleDetailModal.vue:741): "Szerkesztés" gomb `v-if`-je kiegészítve `authStore.hasOrgCapability(orgId, 'can_approve_expense')` ellenőrzéssel
|
||||
|
||||
### ✅ Verifikáció
|
||||
- Backend Python szintaxis: `user.py` és `users.py` hibátlanul lefordul
|
||||
- Frontend TypeScript: `vue-tsc --noEmit` sikeres (csak pre-existing `.vue.js` file hibák)
|
||||
- Event propagation chain teljes: Cost modal → CostsActionsCard → DashboardView → VehicleDetailModal
|
||||
|
||||
## 2026-06-18 - P0 Legacy Garage Migration (RBAC Phase 3)
|
||||
|
||||
### 🎯 Cél
|
||||
Adatbázis backfill szkript elkészítése és futtatása, amely a legacy `person_id`-alapú ownership struktúrát áttelepíti az új `organization_id`/`organization_members` RBAC struktúrába. Három feladat: (A) hiányzó személyes garázsok létrehozása, (B) userek bindolása a garázsukhoz OWNER role-lal, (C) gazdátlan járművek megmentése.
|
||||
|
||||
### 🔧 Változtatások
|
||||
|
||||
**1. BACKEND - [`migrate_legacy_garages.py`](backend/app/scripts/migrate_legacy_garages.py:1):**
|
||||
- **Task A:** Lekérdezi a `identity.users` táblában lévő usereket, akiknek nincs `INDIVIDUAL` típusú személyes garázsuk (`fleet.organizations`). Létrehozza a garázst minden szükséges oszloppal.
|
||||
- **Task B:** Minden userhez hozzárendeli a személyes garázsát `organization_members` táblában `OWNER` szerepkörrel. Frissíti a `scope_id` mezőt, ha az NULL.
|
||||
- **Task C:** Megkeresi a gazdátlan járműveket (`vehicle.assets` táblában ahol `owner_person_id` kitöltött, de `current_organization_id` vagy `owner_org_id` NULL), és átvezeti őket a tulajdonos személyes garázsába.
|
||||
- **Technikai részletek:** `asyncpg`-t használ közvetlen adatbázis kapcsolattal. Dinamikus schema discovery (`information_schema.columns`) a NOT NULL constraint-ek kezelésére.
|
||||
|
||||
**2. BACKEND - [`verify_migration.py`](backend/app/scripts/verify_migration.py:1):**
|
||||
- Verifikációs szkript, amely a migráció után lekérdezi a kulcs metrikákat.
|
||||
|
||||
### ✅ Eredmény
|
||||
- **6 új személyes garázs** létrehozva (Task A)
|
||||
- **16 member** hozzáadva `organization_members`-hez (Task B)
|
||||
- **10 scope_id** frissítve (Task B)
|
||||
- **1 gazdátlan jármű** megmentve (Task C)
|
||||
- **0 user** maradt személyes garázs nélkül
|
||||
- **0 user** NULL scope_id-vel
|
||||
- **0 orphaned jármű** maradt
|
||||
- **17** total INDIVIDUAL garázs
|
||||
- **22** organization member OWNER role-lal
|
||||
- **38/41** jármű organization-höz rendelve
|
||||
|
||||
## 2026-06-18 - P0 Feature: Subscription & Package Assignment Bridge
|
||||
|
||||
### 🎯 Cél
|
||||
Subscription Tier hozzárendelés szervezetekhez (fleet.organizations) a `subscription_tier_id` FK oszlopon keresztül. Teljes admin API + frontend integráció a csomagok kezeléséhez.
|
||||
|
||||
### 🔧 Változtatások
|
||||
|
||||
**1. ADATBÁZIS - [`Organization`](backend/app/models/marketplace/organization.py:146):**
|
||||
- `subscription_tier_id` FK oszlop hozzáadva (nullable, FK → `system.subscription_tiers.id`)
|
||||
- Sync engine sikeresen lefuttatva → oszlop létrejött
|
||||
|
||||
**2. CAPABILITIES - [`capabilities.py`](backend/app/core/capabilities.py:61):**
|
||||
- `CAN_MANAGE_SUBSCRIPTIONS = "can_manage_subscriptions"` új capability
|
||||
- SUPERADMIN és ADMIN mátrixba bevéve
|
||||
|
||||
**3. API ENDPOINT - [`organizations.py`](backend/app/api/v1/endpoints/organizations.py:730):**
|
||||
- `PUT /{org_id}/subscription` végpont `RequireSystemCapability(Capability.CAN_MANAGE_SUBSCRIPTIONS)` RBAC-cal
|
||||
- `SubscriptionAssignIn` Pydantic schema a body-hoz
|
||||
|
||||
**4. BILLING ENGINE - [`billing_engine.py`](backend/app/services/billing_engine.py:805):**
|
||||
- `upgrade_org_subscription()` függvény: beállítja a `subscription_tier_id`-t, frissíti a `subscription_plan` és `base_asset_limit` mezőket, létrehozza az `OrganizationSubscription` audit rekordot
|
||||
|
||||
**5. QUOTA ENGINE - [`evidence.py`](backend/app/api/v1/endpoints/evidence.py:17):**
|
||||
- scan-registration végpont most az org `subscription_tier_id` → `SubscriptionTier.rules.allowances.max_vehicles` értéket olvassa
|
||||
- Fallback: `org.base_asset_limit` ha nincs tier hozzárendelve
|
||||
|
||||
**6. SCHEMA - [`organization.py`](backend/app/schemas/organization.py:66):**
|
||||
- `subscription_tier_id: Optional[int]` hozzáadva `OrganizationUpdate` és `OrganizationResponse` modellekhez
|
||||
|
||||
**7. FRONTEND TYPES - [`organization.ts`](frontend/src/types/organization.ts:5):**
|
||||
- `subscription_tier_id?: number | null` hozzáadva `OrganizationItem` interfészhez
|
||||
|
||||
**8. FRONTEND UI - [`OrganizationSettingsModal.vue`](frontend/src/components/organization/OrganizationSettingsModal.vue:1):**
|
||||
- Subscription tier dropdown (select + assign gomb)
|
||||
- `availableTiers` betöltése `GET /admin/packages/`-ből
|
||||
- `assignSubscription()` hívás `PUT /organizations/{id}/subscription`-ra
|
||||
|
||||
### ✅ Verifikáció
|
||||
- `subscription_tier_id` oszlop létezik (`information_schema` ellenőrizve)
|
||||
- Org 45: `corp_premium_plus_v1` (tier_id=17, max_vehicles=50) ✅
|
||||
- Org 49: `corp_premium_v1` (tier_id=16, max_vehicles=20) ✅
|
||||
- 8 subscription tier elérhető a `system.subscription_tiers` táblában
|
||||
|
||||
## 2026-06-18 - P0 Critical: Admin Button Fix & Orphaned Vehicle Rescue
|
||||
|
||||
### 🎯 Cél
|
||||
5-lépéses P0 kritikus feladat: (1) Törött Admin/Vezérlőpult gomb javítása a frontenden, (2) Adatbázis felderítés: gazdátlan járművek keresése, (3) Admin felhasználó garázsainak ellenőrzése, (4) Gazdátlan járművek megmentése (UPDATE), (5) Verifikáció és jelentés.
|
||||
|
||||
### 🔧 Változtatások
|
||||
|
||||
**1. FRONTEND - [`auth.ts`](frontend/src/stores/auth.ts:97):**
|
||||
- **Root Cause:** A `ModeSwitcher.vue` a `stores/auth`-ból importálja az `authStore.isAdmin`-et, de a store-ból hiányzott az `isAdmin` computed getter. Mindig `undefined`-et adott vissza, így a gomb sosem jelent meg.
|
||||
- **Fix:** `isAdmin` computed property hozzáadva, amely ellenőrzi a `superadmin`, `admin`, `region_admin`, `country_admin`, `moderator` szerepköröket (kisbetűs összehasonlítással).
|
||||
|
||||
**2. ADATBÁZIS - Gazdátlan járművek felderítése:**
|
||||
- **3 db orphaned vehicle** találva: TEST-API-01, ABB112, ABC-123 (mind `archived` státuszú, `owner_person_id = NULL`, `current_organization_id = NULL`)
|
||||
- **Admin user** (admin@profibot.hu, user_id=2, person_id=2, role=ADMIN) nem rendelkezik Corporate (fleet_owner/business) garázzsal OWNER joggal - csak "Admin Garázsa" (Org 67, individual típus)
|
||||
- Admin 17 járműve már helyesen a Test Company (Org 1, fleet_owner) alá van rendelve
|
||||
|
||||
**3. ADATBÁZIS - Rescue UPDATE:**
|
||||
- 3 gazdátlan jármű átvezetve az Admin Garázsába (Org 67): `owner_person_id=2, owner_org_id=67, current_organization_id=67`
|
||||
- Parancs: `docker compose exec -T shared-postgres psql ... UPDATE vehicle.assets SET ... WHERE current_organization_id IS NULL AND owner_person_id IS NULL`
|
||||
- Eredmény: `UPDATE 3`
|
||||
|
||||
### ✅ Verifikáció
|
||||
- `SELECT ... WHERE current_organization_id IS NULL` → **üres eredmény** (0 gazdátlan jármű) ✅
|
||||
- 3 rescued vehicle: TEST-API-01, ABB112, ABC-123 → Admin Garázsa (Org 67) ✅
|
||||
- Admin 17 egyéb járműve: Test Company (Org 1) alatt maradt ✅
|
||||
|
||||
## 2026-06-18 - P0 CRITICAL: Fix Admin Panel Case Sensitivity (SUPERADMIN/ADMIN)
|
||||
|
||||
### 🎯 Cél
|
||||
A backend UPPERCASE role értékei (SUPERADMIN, ADMIN) és a frontend lowercase összehasonlításai közötti mismatch javítása.
|
||||
|
||||
### 🔧 Változtatások
|
||||
- [`frontend/src/stores/auth.ts`](frontend/src/stores/auth.ts:99) — `isAdmin` getter: lowercase `[superadmin,admin,...]` → UPPERCASE `[SUPERADMIN,ADMIN,MODERATOR]` + `role.toUpperCase()`
|
||||
- [`frontend/src/router/index.ts`](frontend/src/router/index.ts:131) — Router guard: ugyanez a javítás a `requiresAdmin` meta ellenőrzésben
|
||||
- [`frontend/src/stores/authStore.ts`](frontend/src/stores/authStore.ts:27) — Secondary auth store `isAdmin`/`isSuperAdmin` getterek: `role.toUpperCase()`
|
||||
- [`frontend/src/components/header/HeaderProfile.vue`](frontend/src/components/header/HeaderProfile.vue:145) — Inline `isAdmin` computed: lowercase → UPPERCASE + `toUpperCase()`
|
||||
- [`frontend/src/views/admin/AdminUsersView.vue`](frontend/src/views/admin/AdminUsersView.vue:130,299) — Két inline `=== superadmin` check → `=== SUPERADMIN` + `toUpperCase()`
|
||||
|
||||
### ✅ Verifikáció
|
||||
- Backend admin ping endpoint: `role: "ADMIN"` (UPPERCASE) ✅
|
||||
- Frontend Vite HMR: minden módosított fájl újratöltve ✅
|
||||
- Admin felület minden ponton (store getter, router guard, header button, users view) case-insensitive módon ellenőrzi a SUPERADMIN/ADMIN szerepköröket ✅
|
||||
|
||||
## 2026-06-18 - P0 Critical: Precise Subscription Backfill & Legacy Cleanup
|
||||
|
||||
### 🎯 Cél
|
||||
Admin garázsok VIP csomag hozzárendelése, tömeges free tier fallback, legacy string oszlopok kivezetése a kvóta logikából.
|
||||
|
||||
### 🔧 Változtatások
|
||||
1. **Adatbázis backfill** ([`backend/app/scripts/p0_subscription_backfill.py`](backend/app/scripts/p0_subscription_backfill.py)):
|
||||
- VIP: Admin Garázsa (org_id=67) → `private_test_v01` (tier_id=19, max_vehicles=100) ✅
|
||||
- VIP: Test Company (org_id=1) → `org_test_v01` (tier_id=20, max_vehicles=100) ✅
|
||||
- Free Tier Fallback: 14 individual org → `private_free_v1`, 13 other org → `corp_free_v1` ✅
|
||||
- Eredmény: 31/31 org assigned, 0 NULL ✅
|
||||
|
||||
2. **Kvóta Motor refaktor** ([`backend/app/services/asset_service.py`](backend/app/services/asset_service.py:491)):
|
||||
- `get_user_vehicle_limit()`: Eltávolítva a legacy `subscription_plan` string lookup
|
||||
- Single Source of Truth: `SubscriptionTier.rules['allowances']['max_vehicles']` JSONB
|
||||
- Fallback lánc: user subscription → org subscription tier → org.base_asset_limit → config
|
||||
|
||||
3. **Dokumentum helper cleanup** ([`backend/app/api/v1/endpoints/documents.py`](backend/app/api/v1/endpoints/documents.py:90)):
|
||||
- `_check_premium_or_admin()`: Eltávolítva a `subscription_plan` string alapú premium check
|
||||
|
||||
### ✅ Verifikáció
|
||||
- Schema szinkron: sync_engine → 1089/1089 OK ✅
|
||||
- 0 org with NULL subscription_tier_id ✅
|
||||
- Distribution: private_free_v1(14), corp_free_v1(13), corp_premium_v1(1), private_test_v01(1), corp_premium_plus_v1(1), org_test_v01(1) ✅
|
||||
|
||||
## 2026-06-18 - P0 CRITICAL: PATCH Organization 500 Error Fix
|
||||
|
||||
### 🎯 Cél
|
||||
A `PATCH /api/v1/organizations/{id}` végpont 500 Internal Server Error hibájának javítása, amely a subscription backfill után jelentkezett.
|
||||
|
||||
### 🔍 Root Cause Analysis
|
||||
|
||||
**Kiváltó ok #1:** PostgreSQL ENUM típus mismatch.
|
||||
|
||||
A `fleet.organization_members.role` oszlop az adatbázisban `fleet.orguserrole` ENUM típusként volt definiálva, de a SQLAlchemy modellben [`OrganizationMember.role`](backend/app/models/marketplace/organization.py:246) `String(50)`-ként. Amikor a `PATCH /organizations/{id}` végpont [`update_organization`](backend/app/api/v1/endpoints/organizations.py:672) függvénye lefutott, az SQLAlchemy a következő SQL-t generálta:
|
||||
|
||||
```sql
|
||||
WHERE fleet.organization_members.role IN ('OWNER', 'ADMIN')
|
||||
```
|
||||
|
||||
PostgreSQL nem tud implicit módon összehasonlítani egy ENUM típust (`fleet.orguserrole`) egy string literállal (`character varying`), ezért dobta:
|
||||
|
||||
```
|
||||
operator does not exist: fleet.orguserrole = character varying
|
||||
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
|
||||
```
|
||||
|
||||
**Kiváltó ok #2:** Hiányzó `OrganizationMember` rekord az `owner_id` alapú tulajdonosoknak.
|
||||
|
||||
A subscription backfill során számos szervezetnél az `owner_id` mező be lett állítva, de a `fleet.organization_members` táblába nem került be a megfelelő OWNER szerepkörű rekord. A PATCH végpont csak a `OrganizationMember` táblában keresett, így a jogos tulajdonos is 403-as hibát kapott.
|
||||
|
||||
**Érintett fájlok:**
|
||||
- [`backend/app/models/marketplace/organization.py`](backend/app/models/marketplace/organization.py:246) - `OrganizationMember.role` oszlop típusa
|
||||
- [`backend/app/api/v1/endpoints/organizations.py`](backend/app/api/v1/endpoints/organizations.py:668) - A PATCH végpont RBAC ellenőrzése
|
||||
|
||||
### 🔧 Javítások
|
||||
|
||||
**1. MODELL JAVÍTÁS** ([`organization.py`](backend/app/models/marketplace/organization.py:246)):
|
||||
- `OrganizationMember.role` oszlop típusa `String(50)` → `PG_ENUM(OrgUserRole, name="orguserrole", schema="fleet", create_type=False)`-re változtatva
|
||||
- `create_type=False` paraméter biztosítja, hogy nem próbáljuk újra létrehozni a már meglévő ENUM típust
|
||||
|
||||
**2. VÉGPONT JAVÍTÁS** ([`organizations.py`](backend/app/api/v1/endpoints/organizations.py:668)):
|
||||
- Az RBAC ellenőrzés kiegészítve: ha a user nem található `OrganizationMember`-ként, de `Organization.owner_id`-ként szerepel, akkor is engedélyezve van a módosítás
|
||||
- A szervezet lekérése előrébb került (az RBAC ellenőrzés előtt), hogy elérhető legyen az `owner_id` mező
|
||||
- Ezzel elkerültük a duplikált SQL lekérdezést is
|
||||
|
||||
### ✅ Verifikáció
|
||||
- `PATCH /api/v1/organizations/44` → **200 OK** ✅ (korábban 500, majd 403)
|
||||
- Nincs több `UndefinedFunctionError` a logokban ✅
|
||||
- Az SQLAlchemy által generált SQL helyesen kezeli az ENUM összehasonlítást ✅
|
||||
- A tulajdonos (owner_id) sikeresen tudja módosítani a szervezet adatait ✅
|
||||
|
||||
## 2026-06-18: Address fields missing from OrganizationResponse and GET /my (#3-as probléma)
|
||||
|
||||
### 🔍 Root Cause Analysis
|
||||
|
||||
A PATCH `/api/v1/organizations/{id}` végpont 200 OK-val tért vissza, és az adatok az adatbázisba is bekerültek, de a frontend UI-ban nem jelentek meg a cím adatok. A probléma **három rétegben** jelentkezett:
|
||||
|
||||
**1. Backend `OrganizationResponse` séma hiányossága** ([`organization.py`](backend/app/schemas/organization.py:70)):
|
||||
- A `OrganizationResponse` Pydantic osztály nem tartalmazta a cím mezőket (`address_zip`, `address_city`, `address_street_name`, `address_street_type`, `address_house_number`, `address_hrsz`)
|
||||
- A PATCH végpont `response_model=OrganizationResponse`-et használ, így a válaszból kimaradtak a cím adatok
|
||||
|
||||
**2. Backend `GET /my` végpont hiányossága** ([`organizations.py`](backend/app/api/v1/endpoints/organizations.py:210)):
|
||||
- A `get_my_organizations` függvény által visszaadott dictionary-ből hiányoztak a cím mezők
|
||||
- A frontend a PATCH után `authStore.fetchMyOrganizations()`-t hív, ami ezt a végpontot használja
|
||||
|
||||
**3. Frontend `OrganizationItem` típus és `onMounted` hiányossága** ([`OrganizationSettingsModal.vue`](frontend/src/components/organization/OrganizationSettingsModal.vue:222)):
|
||||
- A TypeScript `OrganizationItem` interfészből hiányoztak a cím mezők
|
||||
- Az `onMounted` hook nem töltötte be a cím mezőket a `form` objektumba
|
||||
|
||||
**További probléma:** A `OrganizationUpdate` séma tartalmazott olyan mezőket (`address_stairwell`, `address_floor`, `address_door`), amelyek nem léteznek az `Organization` SQLAlchemy modellben. Ezeket a PATCH végpont csendben figyelmen kívül hagyta, de a `GET /my` végpont AttributeError-t dobott volna rájuk.
|
||||
|
||||
### 🔧 Javítások
|
||||
|
||||
**1. `OrganizationResponse` séma bővítése** ([`organization.py`](backend/app/schemas/organization.py:88)):
|
||||
- Hozzáadva: `address_zip`, `address_city`, `address_street_name`, `address_street_type`, `address_house_number`, `address_hrsz`
|
||||
- Eltávolítva a nem létező mezők: `address_stairwell`, `address_floor`, `address_door`
|
||||
|
||||
**2. `OrganizationUpdate` séma tisztítása** ([`organization.py`](backend/app/schemas/organization.py:54)):
|
||||
- Eltávolítva a nem létező mezők: `address_stairwell`, `address_floor`, `address_door`
|
||||
|
||||
**3. `GET /my` végpont bővítése** ([`organizations.py`](backend/app/api/v1/endpoints/organizations.py:227)):
|
||||
- Hozzáadva a cím mezők a visszaadott dictionary-hez
|
||||
|
||||
**4. Frontend `OrganizationItem` típus bővítése** ([`organization.ts`](frontend/src/types/organization.ts:33)):
|
||||
- Hozzáadva a cím mezők az interfészhez
|
||||
|
||||
**5. Frontend `OrganizationSettingsModal.vue` javítása** ([`OrganizationSettingsModal.vue`](frontend/src/components/organization/OrganizationSettingsModal.vue:222)):
|
||||
- `form` objektum bővítve a cím mezőkkel
|
||||
- `onMounted` hook kiegészítve a cím mezők betöltésével (mindkét ágban)
|
||||
|
||||
### ✅ Verifikáció
|
||||
- `GET /api/v1/organizations/my` → **200 OK**, tartalmazza a cím mezőket ✅
|
||||
- `PATCH /api/v1/organizations/{id}` → **200 OK**, a válasz tartalmazza a cím mezőket ✅
|
||||
- A cím adatok elérhetők a frontend `OrganizationSettingsModal` form-jában ✅
|
||||
|
||||
Reference in New Issue
Block a user