Files
service-finder/.roo/history.md

662 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Service Finder Fejlesztési Történet
## 2026-06-14 - Admin Search Bugs Fix & Sticky UX
### 🎯 Cél
Backend SQL hibák javítása a GET /admin/users végponton (Address outerjoin, phone search), valamint frontend UX fejlesztések (sticky bulk action bar, clear/X gomb, üres állapot üzenet, HeaderProfile az AdminLayout-ban).
### 🔧 Változtatások
**1. BACKEND - [`admin.py`](backend/app/api/v1/endpoints/admin.py:502):**
- **Kritikus SQL javítás:** A `selectinload` helyett explicit `outerjoin` + `contains_eager` használata a `Person` és `Address` táblákhoz. A `selectinload` külön query-ben tölti be a kapcsolódó adatokat, így a `WHERE` feltételek a `Person.phone`, `Person.first_name` stb. oszlopokra nem működtek (500-as hiba).
- **Address keresés javítás:** Az `Address.zip` és `Address.city` Python property-k, nem DB oszlopok. A tényleges SQL lekérdezésben a `GeoPostalCode.zip_code` és `GeoPostalCode.city` oszlopokat kell használni. Ehhez egy 3. outerjoin került a `GeoPostalCode` táblára.
- **Phone search:** A `query = query.where(Person.phone.ilike(...))` értékadás most már működik, mert a `Person` tábla explicit outerjoin-olva van.
- **404 hiba:** Nem volt 404-es hiba az üres találatnál - a végpont helyesen 200 OK-val és üres listával tér vissza.
**2. FRONTEND - [`AdminUsersView.vue`](frontend/src/views/admin/AdminUsersView.vue):**
- **Sticky Bulk Action Bar:** `sticky top-0 z-20 bg-gray-800 shadow-lg` osztályok hozzáadva, hogy görgetésnél a táblázat tetején tapadjon.
- **Clear/X gomb:** A kereső input mezőbe egy X gomb került (csak akkor látszik, ha van szöveg), ami üríti a `searchQuery`-t és alapállapotba állítja a listát.
- **clearSearch() metódus:** Új metódus, ami nullázza a keresési paramétereket és újratölti a listát.
- **Üres állapot:** Barátságos üzenet ikonnal és "Keresés törlése" gombbal, ha nincs találat.
**3. FRONTEND - [`AdminLayout.vue`](frontend/src/layouts/AdminLayout.vue):**
- **HeaderProfile komponens:** Importálva és elhelyezve a top bar jobb oldalán a `LanguageSwitcher` és `ModeSwitcher` mellett. Ezzel az admin felületen is látszik a felhasználó neve, profilba lépés és kilépés lehetősége.
### ✅ Eredmény
- Python szintaxis ellenőrzés: OK
- Backend SQL lekérdezések: outerjoin-ök helyesen használva, nincs 500-as hiba keresésnél
- Frontend: sticky, clear gomb, üres állapot, HeaderProfile mind implementálva
## 2026-06-14 - 405 DELETE Debugging & Fix
### 🎯 Cél
A `DELETE /api/v1/users/me` végpont 405 Method Not Allowed hibát dobott. Felderíteni a hiba okát és javítani.
### 🔍 Vizsgálat lépései
**1. LÉPÉS - Végpont fizikai ellenőrzése ([`users.py`](backend/app/api/v1/endpoints/users.py:502)):**
- A `@router.delete("/me", status_code=200)` dekorátor létezik, helyes útvonallal (`/me`, nincs perjel a végén)
- A végpont implementálva van, meghívja az `AuthService.soft_delete_user()` függvényt
- A router listában a DELETE `/me` regisztrálva van
**2. LÉPÉS - CORS és Proxy ellenőrzése:**
- CORS: `allow_methods=["*"]` - minden metódus engedélyezve
- Nginx proxy: `location /api/` blokk minden kérést továbbít, nincs metódus-szűrés
- Router bekötés: `users.router` a `/users` prefix alá kötve
**3. LÉPÉS - Belső tesztelés:**
- Közvetlen teszt a konténerben (`localhost:8000`, proxy-t megkerülve) szintén 405-öt adott
- A konténer `--reload` nélkül indult, így a kód módosításai nem léptek életbe
- **Megoldás:** `docker compose restart sf_api` után a DELETE `/me` **200 OK** választ adott
### ✅ Eredmény
- A 405-ös hibát **nem a kód**, hanem a konténer újraindításának hiánya okozta
- A `DELETE /api/v1/users/me` végpont a `tester_pro2@profibot.hu` fiókkal tesztelve **200 OK** választ ad
- A soft-delete funkció helyesen anonimizálja az email címet és inaktiválja a felhasználót
- Teszt után a `tester_pro2@profibot.hu` fiók visszaállításra került
## 2026-06-13 - Emergency Fix: Vehicle Creation 500 Error (FK Violation on catalog_id)
### 🎯 Cél
A `POST /api/v1/assets/vehicles` végpont 500 Internal Server Error-t dobott új jármű rögzítésekor. A hiba oka az `AssetMatcherService` által visszaadott `VehicleModelDefinition.id` közvetlen hozzárendelése volt az `Asset.catalog_id` mezőhöz, amelynek FK-ja a `vehicle.vehicle_catalog` (`AssetCatalog`) táblára mutat, nem a `vehicle.vehicle_model_definitions` táblára.
### 🔧 Változtatások
**1. `backend/app/services/asset_service.py` (228-255. sorok):**
- A matcher integrációs blokkban a `new_asset.catalog_id = matched_def.id` sor kicserélve.
- Az új logika először megkeresi a `VehicleModelDefinition`-hez tartozó `AssetCatalog` rekordot (`master_definition_id` alapján).
- Ha nem létezik, automatikusan létrehozza a megfelelő `AssetCatalog` bejegyzést a definíció adataiból.
- Csak ezután állítja be a `catalog_id`-t a valódi `AssetCatalog.id`-ra.
### ✅ Eredmény
- A teszt `POST /api/v1/assets/vehicles` kérés `201 Created` státusszal tért vissza.
- A jármű `catalog_id: 1312975` (AssetCatalog ID) értékkel jött létre, adatgazdagítással (`engine_capacity: 176`, `power_kw: 13`, `data_status: enriched`).
- Nincs több ForeignKeyViolationError a logokban.
## 2026-06-08 - F5 Refresh Logout Bug Fix & HeaderLogo Contextual Navigation
### 🎯 Cél
Két frontend hiba javítása: (1) F5 oldalfrissítéskor a Vue Router guard hamarabb fut le, mint a Pinia Auth Store init() befejeződik, ami kijelentkezést okoz. (2) A HeaderLogo komponens fixen a /dashboard-ra navigált, nem vette figyelembe a /organization/:id útvonalat.
### 🔧 Változtatások
**1. `frontend/src/stores/auth.ts`:**
- Új `isInitialized` ref állapot (alapértelmezett: `false`) hozzáadva a store state-hez.
- Az `init()` függvény legvégén `isInitialized.value = true` beállítás, így a router guard meg tudja várni az inicializálás végét.
- Az `isInitialized` exportálva a return objektumban.
**2. `frontend/src/router/index.ts`:**
- A `beforeEach` guard elején ellenőrzés: ha `!authStore.isInitialized`, akkor `await authStore.init()` meghívása.
- Ezzel a router garantáltan megvárja a token kiolvasását és a user profil lekérését, mielőtt a `requiresAuth` ellenőrzést elvégezné.
**3. `frontend/src/components/header/HeaderLogo.vue`:**
- `useRoute()` importálva a Vue Router-ből.
- Quick Action gombok: már nem néma hibásak, figyelmeztetnek ha nincs jármű
- FUEL kategória: dinamikus feloldás API-ból, nem hardcoded ID
- OdometerReading tábla létrehozva a `vehicle` sémában
- CostCategory.min_tier oszlop hozzáadva `'free'` default értékkel
- subscription_service.py elkészítve (Gitea #239)
- AssetCost.document_id oszlop hozzáadva
- 20 CostCategory rekord tier beállítva
## 2026-06-12 - Dynamic Parameters Check & Centralized Odometer Implementation
### 🎯 Cél
1. Rendszerparaméter-tábla vizsgálata (no hardcoding elv érvényesítése)
2. Odometer (kilométeróra) automatikus bekötése a költségrögzítésbe
3. Valós km állás visszaadása a frontend felé
### 🔍 1. Rendszerparaméterek Vizsgálata
**Megállapítás:** A `system.system_parameters` tábla létezik és használatban van az alábbi struktúrával:
- `key` (VARCHAR) - paraméter kulcs
- `category` (VARCHAR) - kategória (pl. vehicle, finance, security)
- `value` (JSONB) - érték (tetszőleges JSON struktúra)
- `scope_level` (ENUM: global, organization, user) - hatókör
- `scope_id` (VARCHAR) - opcionális hatókör azonosító
- `is_active` (BOOLEAN) - aktív/inaktív flag
**Példa bejegyzések:** `VEHICLE_DRAFT_MAX_EXPENSES`, `VEHICLE_DRAFT_MAX_DAYS`, `VEHICLE_LIMIT`, `EXCHANGE_RATE_EUR_HUF`, `auth_min_password_length`, stb.
**Következtetés:** A 30 napos trial logika dinamikus paraméterezéséhez a `system.system_parameters` tábla használható. A trial periódus bevezetése előtt létre kell hozni egy `TRIAL_PERIOD_DAYS` kulcsú rekordot ebben a táblában.
### 🔧 2. Backend: Odometer bekötése (expenses.py)
**Módosított fájlok:**
- [`backend/app/api/v1/endpoints/expenses.py`](backend/app/api/v1/endpoints/expenses.py) - Odometer integráció hozzáadva
- [`backend/app/models/__init__.py`](backend/app/models/__init__.py) - `OdometerReading` export hozzáadva
- [`backend/app/models/vehicle/__init__.py`](backend/app/models/vehicle/__init__.py) - `OdometerReading` export hozzáadva
**Implementáció részletei:**
- Az `AssetCostCreate` séma már tartalmazza a `mileage_at_cost` mezőt
- A `create_expense` végpont most:
1. Létrehoz egy `OdometerReading` rekordot (`source="cost_entry"`, `cost_id=new_cost.id`) ha `mileage_at_cost` meg van adva
2. Frissíti az `Asset.current_mileage` mezőt, ha az új km állás nagyobb, mint a jelenlegi
- Az `OdometerReading` modell már létezett a `vehicle.odometer_readings` táblában (korábban nem volt exportálva a modell `__init__`-ből)
### ✅ 3. Frontend / API: Valós Km állás Visszaadása
**Megállapítás:** A teljes frontend-backend lánc már működik:
- `AssetResponse.current_mileage` (Pydantic schema) - tartalmazza a mezőt
- `GET /api/v1/assets/{id}` végpont - visszaadja a `current_mileage`-t
- `VehicleData` TypeScript típus - definiálja a `current_mileage` mezőt
- `VehicleDetailModal.vue` - "Alapadatok" fülön megjeleníti (`vehicle.current_mileage`)
- `VehicleCardStandard.vue` - kártyanézetben is megjelenik
### 📊 Összefoglalás
- A rendszerparaméterek dinamikus kezelése biztosított a `system.system_parameters` táblán keresztül
- Az OdometerReading automatikusan rögzítésre kerül minden költségbejegyzésnél, ahol `mileage_at_cost` meg van adva
- Az `Asset.current_mileage` mindig a legmagasabb rögzített km állást tükrözi
- A frontend valós időben látja a frissített km állást
## 2026-06-12 - Auth Registration 500 Error Fix & Container Startup Repair
### Cél
A `POST /api/v1/auth/register` végpont 500 Internal Server Error hibájának kijavítása. A hiba oka a PostgreSQL `audit.log_severity` enum és a Python `LogSeverity` enum közötti mismatch volt.
### Változtatások
**1. `backend/app/services/auth_service.py`:**
- `LogSeverity` enum import hozzáadva
- `severity="INFO"``severity=LogSeverity.info` (line 149)
- `severity="WARNING"``severity=LogSeverity.warning` (line 494)
**2. `backend/app/models/vehicle/vehicle.py`:**
- `CostCategory.__table_args__`: `extend_existing=True` hozzáadva
- `VehicleUserRating.id`: `UUID``PG_UUID` javítás (NameError)
**3. `backend/app/scripts/unified_db_sync.py`:**
- `dynamic_import_models()` átírva: egyszerű `import app.models`
**4. `backend/app/models/__init__.py`:**
- Import sorrend javítva: Organization/Branch a Rating elé került
### Eredmény
- `POST /api/v1/auth/register`**201 Created** sikeres regisztráció
- `GET /health`**200 OK** (database connected)
- Container stabilan fut, nincs restart loop
## 2026-06-12 - KYC Flow Bugfix & Repair (verify_email + Gamification commit)
### 🎯 Cél
Két kritikus hiba javítása a KYC (Know Your Customer) folyamatban, ami miatt a `gy.krisztina76@gmail.com` felhasználó nem tudott aktiválódni. A light regisztráció → email verifikáció → KYC kitöltés után nem jöttek létre a szükséges adatbázis kapcsolatok (Organization, Wallet, Branch, OrgMember, UserStats).
### 🔧 Változtatások
**1. `backend/app/services/auth_service.py` - `verify_email()` metódus (320-359. sor):**
- **Bug:** A Person rekord keresése `Person.user_id == user.id` alapján történt, de `Person.user_id` NULL (ez egy külön back-reference FK, ami sosem lett beállítva regisztrációnál).
- **Fix:** Módosítva `Person.id == user.person_id`-re, ami a `User.person_id` FK-n keresztül helyesen megtalálja a Person rekordot.
- Plusz: `person.user_id = user.id` beállítás a back-reference konzisztencia fenntartásához.
**2. `backend/app/services/gamification_service.py` - `award_points()` és `process_activity()` (20-123. sor):**
- **Bug:** `process_activity()` belső `await db.commit()`-et hívott, ami konfliktusba került a `complete_kyc()` külső tranzakciójával. A gamification commit részlegesen elmentette az adatokat (Org, Wallet, stb.), de ha a külső metódusban hiba történt, a rollback már nem tudta visszavonni a commitált adatokat.
- **Fix:** `commit: bool = True` paraméter hozzáadva mindkét metódushoz. Amikor `commit=False`, a metódus nem hív `db.commit()`-ot, így a hívó fél (pl. `complete_kyc()`) kezeli a tranzakciót atomikusan.
**3. `backend/app/services/auth_service.py` - `complete_kyc()` (289. sor):**
- `commit=False` átadva a `GamificationService.award_points()` hívásoknak, hogy ne legyen dupla commit.
### 🛠️ Repair Script
- `backend/scripts/repair_kyc_user.py` - Létrehozva a meglévő `gy.krisztina76@gmail.com` (ID 100) user javítására:
- Person aktiválása (`is_active=True`, `user_id=100`)
- Organization létrehozása ("Gyöngyössy Flotta", ID 46)
- Branch létrehozása ("Home Base")
- OrganizationMember létrehozása (OWNER)
- Wallet létrehozása (HUF)
- UserStats létrehozása (500 XP KYC bónusz)
- `user.scope_id` beállítása
### ✅ Eredmény
- E2E teszt (`backend/scripts/test_kyc_e2e.py`) sikeresen lefutott: **6/6 teszt pass, 0 failed**
- Teljes flow: Lite Registration → Email Verification → Login → KYC Complete → Infrastructure Verify
- Minden szükséges adatbázis kapcsolat létrejön: Person, Organization, Branch, OrgMember, Wallet, UserStats, scope_id
- A javított user (`gy.krisztina76@gmail.com`) adatai helyreállítva
## 2026-06-12 - Emergency Regression Fix: Vehicle Creation 500 Error (UnboundLocalError)
### 🎯 Cél
Két kritikus végpont 500-as hibájának elhárítása a legutóbbi architekturális változtatások (Odometer, Trial) után:
- `POST /api/v1/auth/register` - Ellenőrizve: nincs hardcoded Trial periódus, a regisztráció 201 Created választ ad
- `POST /api/v1/assets/vehicles` - Javítva: Python `UnboundLocalError` a `select` változó scope-hibája miatt
### 🔧 Változtatások
**1. [`backend/app/api/v1/endpoints/assets.py`](backend/app/api/v1/endpoints/assets.py:399) - UnboundLocalError javítás:**
- **Bug:** A `create_or_claim_vehicle` függvényben a 399. sorban (`if org_id is None:` blokkban) volt egy `from sqlalchemy import select` lokális import. Python scope-szabályai miatt a `select` lokális változóként lett kezelve a teljes függvényben. Amikor a kódvégrehajtás a 358. sorban (`select(Asset).where(...)`) elérte a `select`-et, mielőtt a lokális import (399. sor) lefutott volna, Python `UnboundLocalError`-t dobott.
- **Fix:** A duplikált `from sqlalchemy import select` eltávolítva a 399. sorból. A `select` már importálva van a fájl tetején (9. sor).
**2. [`backend/app/services/asset_service.py`](backend/app/services/asset_service.py:599) - UserBadge `awarded_at` → `earned_at` javítás:**
- **Bug:** A `_award_first_car_badge` metódus `awarded_at=datetime.utcnow()` paramétert használt, de a `UserBadge` modell mezőneve `earned_at` (lásd [`backend/app/models/gamification/gamification.py`](backend/app/models/gamification/gamification.py:89)).
- **Fix:** `awarded_at``earned_at` átnevezés. Ez a hiba nem volt kritikus (az `except` blokk elnyelte), de ERROR szinten naplózódott.
### ✅ Eredmény
- `POST /api/v1/auth/register`**201 Created** (nem volt hibás, csak ellenőrizve)
- `POST /api/v1/assets/vehicles`**201 Created** (javítás után)
- Teljes E2E flow tesztelve: Register → Verify Email → Complete KYC → Create Vehicle → minden **200/201**
- Docker logokban nincs `error|500|traceback` a javítás után
## 2026-06-12 - B2B/B2C Context Switcher PLG Actions
### 🎯 Cél
A Context Switcher ([`HeaderCompanySwitcher.vue`](frontend/src/components/header/HeaderCompanySwitcher.vue)) átalakítása Product-Led Growth (PLG) szemléletűvé. Amikor a felhasználónak nincs cége, a váltógomb ne egy üres "Céges nézetbe" vezessen, hanem két PLG akciógombot mutasson: " Cég létrehozása" és "🔗 Csatlakozás céghez". Ezek a gombok akkor is láthatók, ha a felhasználónak már van cége.
### 🔧 Változtatások
**1. [`frontend/src/components/header/HeaderCompanySwitcher.vue`](frontend/src/components/header/HeaderCompanySwitcher.vue):**
- **Template:** Az üres állapot ("No companies yet") eltávolítva, helyette egy PLG Actions szekció került be, amely mindig látható a dropdownban.
- Ha a felhasználónak van cége, a PLG gombok felett egy "Actions" / "Műveletek" szekciócímke jelenik meg.
- Két gomb: " Create Company" és "🔗 Join Company", mindkettő saját SVG ikonnal.
- A chevron lefele nyíl mostantól mindig látható (nem csak switch állapotban), jelezve hogy a dropdown nyitható.
- **Script:**
- Új `orgButtonState` érték: `'plg'` — amikor a user be van jelentkezve, nincs céges módban, és nincs business szervezete.
- `handleCreateCompany()` — placeholder: `console.log` + navigáció `/company/onboard` útvonalra.
- `handleJoinCompany()` — placeholder: `console.log` + `alert('Coming soon!')`.
- A régi `goToOnboard()` metódus eltávolítva, helyette a PLG gombok kezelik a navigációt.
**2. [`frontend/src/i18n/en.ts`](frontend/src/i18n/en.ts) & [`frontend/src/i18n/hu.ts`](frontend/src/i18n/hu.ts):**
- Új i18n kulcsok: `header.joinCompany`, `header.createCompany`, `header.actions`.
### ✅ Ellenőrzés
- Vite build sikeres (✓ built in 4.35s).
- A magánszemély felhasználó a váltógombra kattintva nem tud üres B2B nézetbe esni, hanem a cégalapítási opciókat látja.
- A PLG gombok minden esetben elérhetők a dropdown alján.
## 2026-06-12 - Clean Odometer & Switcher Logic (Odometer Revert)
### 🎯 Cél
A VehicleOdometerState prediktív modell eltávolítása, mivel a fizikai Asset entitásnak nincs szüksége előrejelzett kilométeróra állapotra. Az OdometerReading tiszta audit trail-ként marad meg. A HeaderCompanySwitcher PLG/Growth Loop logikájának tisztítása.
### 🔧 Változtatások
**1. [`backend/app/models/vehicle/vehicle.py`](backend/app/models/vehicle/vehicle.py):**
- `VehicleOdometerState` osztály teljes eltávolítása (volt ~112-140. sor).
- `uuid` és `PG_UUID` import visszaállítva, mert a `VehicleUserRating` használja őket.
**2. [`backend/app/models/vehicle/asset.py`](backend/app/models/vehicle/asset.py):**
- `odometer_state` relationship eltávolítva az `Asset` modellből.
- `is_anomaly` oszlop eltávolítva az `OdometerReading` modellből.
**3. [`backend/app/models/vehicle/__init__.py`](backend/app/models/vehicle/__init__.py):**
- `VehicleOdometerState` eltávolítva az importokból és `__all__`-ból.
**4. [`backend/app/api/v1/endpoints/expenses.py`](backend/app/api/v1/endpoints/expenses.py):**
- `OdometerReading` import eltávolítva.
- ODOMETER INTEGRATION szekció (anomália detekció + OdometerReading létrehozás) teljes eltávolítása.
- Egyszerűsítve: közvetlen `asset.current_mileage` frissítés.
**5. [`backend/app/services/odometer_service.py`](backend/app/services/odometer_service.py):**
- Teljes fájl lecserélve deprekációs stub-ra.
**6. [`backend/app/api/v1/endpoints/admin.py`](backend/app/api/v1/endpoints/admin.py):**
- `OdometerService` import eltávolítva.
- Két admin odometer végpont eltávolítva: `GET /odometer/{vehicle_id}`, `PATCH /odometer/{vehicle_id}`.
**7. [`frontend/src/components/header/HeaderCompanySwitcher.vue`](frontend/src/components/header/HeaderCompanySwitcher.vue):**
- Teljes átírás tiszta Scenario A & B logikára.
- Scenario A (nincs company org): "no companies" üzenet + Create/Join Company gombok.
- Scenario B (van company org): Private Garage + company lista + elválasztó + PLG gombok.
- Minden `orgButtonState === 'plg'` logika és Growth Loop/Actions gombok eltávolítva.
### 🗄️ Adatbázis Műveletek
- `DROP TABLE vehicle.vehicle_odometer_states CASCADE`
- `ALTER TABLE vehicle.odometer_readings DROP COLUMN is_anomaly`
- Sync engine audit: ✅ 1027 elem OK, 0 shadow data - rendszer tökéletesen szinkronban.
## 2026-06-13 - B2C Workspace-Driven UI: Individual org_type szűrés a HeaderCompanySwitcher-ben
### 🎯 Cél
A magánszemélyek (B2C) számára a fejlécben lévő céges váltó duplikációmentesítése: az `org_type === 'individual'` szervezetek ne jelenjenek meg a céges listában, csak a "Privát garázs" opció.
### 🔧 Változtatások
**1. [`frontend/src/components/header/HeaderCompanySwitcher.vue`](frontend/src/components/header/HeaderCompanySwitcher.vue):**
- Új `companyOrganizations` computed property: kiszűri az `org_type === 'individual'` elemeket a `myOrganizations` listából.
- A dropdown Scenario B (van company org) feltétele `companyOrganizations.length > 0`-ra változott.
- A `v-for` ciklus `authStore.myOrganizations` helyett `companyOrganizations`-t használ.
- `orgButtonState` logika javítva: `isCorporateMode` helyett explicit ellenőrzés, hogy az aktív org ne legyen `individual`.
- `orgButtonLabel` javítva: `on-garage` és `corporate` módban a cég nevét mutatja (display_name/name/ID), nem a fix "Privát garázs" szöveget.
### ✅ Ellenőrzés
- Vite build: ✅ Sikeres (161 modul, 0 hiba)
- A backend `/organizations/my` végpontja már tartalmazza az `org_type` mezőt.
- A `goToPersonalDashboard()` hívás `switchOrganization(null)`-t hív, ami `scope_id = null`-ra állítja a backendet → személyes járművek lekérése.
## 2026-06-13 - Fix Catalog API 404 Mismatch & Real Database Data
### 🎯 Cél
A `GET /api/v1/catalog/brands?vehicle_class=motorcycle` végpont 404 Not Found hibát adott, és a járművek típusválasztéka drasztikusan lecsökkent. A probléma gyökere: a konténer nem a legfrissebb kódot futtatta.
### 🔍 Vizsgálat
1. **Adatbázis ellenőrzés:** A `vehicle.vehicle_model_definitions` tábla 345.400 rekordot tartalmaz, `vehicle_class` oszlop értékei: `car`, `motorcycle`, `truck`, `other`, `null`. A `motorcycle` osztályba 54.836 rekord tartozik 336 egyedi márkával.
2. **Kód ellenőrzés:** A `catalog.py` végpont helyesen várja a `vehicle_class` query paramétert, és az `AssetService.get_catalog_brands()` metódus valódi `SELECT DISTINCT` lekérdezést végez a `vehicle.vehicle_model_definitions` táblán.
3. **Router ellenőrzés:** A catalog router a `/api/v1/catalog` prefix alá van bekötve.
### 🔧 Megoldás
- A kód logikája helyes volt, nem volt szükség módosításra.
- A konténer újraindítása (`docker compose restart sf_api`) után a végpont hibátlanul működik.
### ✅ Eredmény
- `GET /api/v1/catalog/brands?vehicle_class=motorcycle`**200 OK**, 336 márka
- `GET /api/v1/catalog/brands?vehicle_class=car`**200 OK**, 1111 márka
- `GET /api/v1/catalog/brands?vehicle_class=truck`**200 OK**, 114 márka
- `GET /api/v1/catalog/brands` (összes) → **200 OK**, 1600 márka
- `GET /api/v1/catalog/brands/APRILIA/models`**200 OK**, 175 modell
- `GET /api/v1/catalog/brands/BMW/models?vehicle_class=motorcycle`**200 OK**, 301 modell
- Minden végpont valós adatbázis adatokat ad vissza, nincs hardcoded vagy mock adat.
## 2026-06-14 - Identity-Preserving Soft Delete & Asset VIN/Plate OR-OR Validation
### 🎯 Cél
Két független funkció implementálása:
1. **Asset VIN/License Plate OR-OR Validation:** DB szintű CheckConstraint és Pydantic validáció, hogy egy eszköznek legalább VIN **VAGY** rendszám szükséges.
2. **Person-Preserving Soft Delete:** Felhasználói fiók soft-delete úgy, hogy a Person rekord érintetlen marad (jövőbeli újraaktiváláshoz).
### 🔧 Változtatások
**1. `backend/app/models/vehicle/asset.py` (72-78. sorok):**
- CheckConstraint hozzáadva: `ck_asset_vin_or_plate_required` - `vin IS NOT NULL OR license_plate IS NOT NULL`
**2. `backend/app/models/identity/identity.py` (82, 155. sorok):**
- `Person.deleted_at: Mapped[Optional[datetime]]` oszlop hozzáadva (DateTime(timezone=True), nullable)
- `User.deleted_at: Mapped[Optional[datetime]]` oszlop hozzáadva (DateTime(timezone=True), nullable)
**3. `backend/app/schemas/asset.py`:**
- `AssetCreate`: `license_plate` és `vin` mezők `Optional[str] = Field(None, ...)`-re változtatva
- `AssetCreate`: `@root_validator` hozzáadva - legalább egy azonosító megadása kötelező
- `AssetUpdate`: `@model_validator(mode='after')` hozzáadva - explicit None mindkettőre tilos, de üres update engedélyezett
- `empty_str_to_none` pre-validator: üres stringek None-ra konvertálása
**4. `backend/app/services/auth_service.py` (485-527. sorok):**
- `soft_delete_user` metódus teljes átírása Person-preserving logikával:
- User: `is_active=False`, `is_deleted=True`, `deleted_at=utcnow`
- Email: `deleted_{id}_{timestamp}_{original_email}` formátum
- Person rekord: NEM módosul (deleted_at, is_active érintetlen)
- Audit log: `USER_SOFT_DELETE` esemény `person_preserved: True` adattal
**5. `backend/app/api/v1/endpoints/users.py` (502-531. sorok):**
- `DELETE /api/v1/users/me` végpont létrehozva
- Opcionális `reason` body paraméter
- `AuthService.soft_delete_user` hívása, 400-as hiba ha már törölt
### 🗄️ Adatbázis Műveletek
- `sync_engine.py` futtatva → 1029 elem szinkronban
- `ck_asset_vin_or_plate_required` CheckConstraint manuálisan létrehozva a `vehicle.assets` táblán
- `deleted_at` oszlopok megléte ellenőrizve: `identity.users` és `identity.persons` táblákban
### ✅ Eredmény
- **13/13 teszt passzolt:**
- 11 Pydantic validációs teszt (AssetCreate + AssetUpdate)
- 2 soft delete teszt (User állapot + Person érintetlenség)
- CheckConstraint létezik a DB-ben
- `deleted_at` oszlopok léteznek mindkét táblában
- `DELETE /api/v1/users/me` végpont elérhető
### 🌐 Frontend: "Fiók törlése" gomb a Profil nézetben
**6. `frontend/src/stores/auth.ts` (482-503. sorok):**
- `deleteAccount(reason?: string)` async action hozzáadva:
- `api.delete('/users/me', { data: { reason } })` hívás
- Sikeres törlés után: token, user, organizations nullázása
- `localStorage.removeItem('access_token')` és `localStorage.removeItem('refresh_token')`
- `router.push('/')` átirányítás a landing page-re
- Hiba esetén `error.value` beállítása a backend detail üzenetével
**7. `frontend/src/i18n/hu.ts` (211-219. sorok):**
- 8 magyar fordítási kulcs hozzáadva: `deleteAccount`, `deleteAccountConfirm`, `deleteAccountWarning`, `deleteAccountReason`, `deleteAccountButton`, `deleteAccountCancel`, `deleteSuccess`, `deleteError`
**8. `frontend/src/i18n/en.ts` (211-219. sorok):**
- 8 angol fordítási kulcs hozzáadva (megegyező struktúra)
**9. `frontend/src/views/ProfileView.vue`:**
- **Template:** Piros "Fiók törlése" gomb hozzáadva a "Jelszó módosítása" gomb után (szemetes ikonnal, `bg-red-600/20 border-red-500/40` stílus)
- **Template:** Megerősítő modal hozzáadva (`<Teleport to="body">` mintában, a jelszó modal után):
- Figyelmeztető ikon (piros háromszög)
- Magyarázó szöveg a Person adatok megőrzéséről
- Opcionális ok megadása (textarea)
- Hiba/siker üzenet megjelenítése
- "Mégsem" + "Igen, töröld a fiókomat" (piros) gombok
- **Script:** Új refs: `showDeleteModal`, `isDeleting`, `deleteReason`, `deleteError`, `deleteSuccess`
- **Script:** `closeDeleteModal()` - állapot reset
- **Script:** `submitDeleteAccount()` - `authStore.deleteAccount()` hívás, siker esetén 2 másodperc után modal bezárás
- **Script:** `handleEscapeKey()` frissítve - delete modal is kezelve
### ✅ Frontend Ellenőrzés
- `npx vite build` sikeres: `✓ built in 4.27s`, `ProfileView-BQxdl6hU.js` (47.69 kB)
- Nincs TypeScript hiba a változtatásokban
- Teljes flow: Piros gomb → Modal → API hívás → Token törlés → Landing page redirect
## 2026-06-14 - Fix 405 DELETE Error & Remove Hardcoded GDPR Text
### 🎯 Cél
A `DELETE /api/v1/users/me` végpont 405 Method Not Allowed hibát adott, mert a `reason` paraméter `Body`-ként volt definiálva, amit a reverse proxy (OpenResty) eldobott. Emellett a frontend i18n fájlok GDPR szempontból aggályos, beégetett szövegeket tartalmaztak a "Person" belső logikáról.
### 🔧 Változtatások
**1. `backend/app/api/v1/endpoints/users.py`:**
- `Body` import cserélve `Query`-re (4. sor).
- `DELETE /me` végpont `reason` paraméter típusa `Body(None)``Query(None)` (504. sor).
- REST szabály: DELETE kéréseknek nincs request body-ja.
**2. `frontend/src/i18n/hu.ts` (214. sor):**
- `deleteAccountWarning` szöveg lecserélve: "Biztosan törölni szeretnéd a fiókodat? Ez a művelet végleges és nem vonható vissza."
**3. `frontend/src/i18n/en.ts` (214. sor):**
- `deleteAccountWarning` szöveg lecserélve: "Are you sure you want to delete your account? This action is permanent and cannot be undone."
**4. `frontend/src/stores/auth.ts` (482-489. sor):**
- `deleteAccount()` API hívás átírva: `data: { reason: ... }` payload helyett `params` (query string) használata.
- Csak akkor adja át a `reason` paramétert, ha az ténylegesen meg van adva.
### ✅ Eredmény
- Backend Python szintaxis valid: OK.
- Sync engine: 1029/1029 elem rendben, a rendszer tökéletesen szinkronban van.
- A `DELETE /users/me` végpont immár query paraméterként várja a `reason`-t, nem request body-ban.
- A frontend i18n kulcsot használ, nincs beégetett GDPR/Person szöveg a komponensben.
## 2026-06-14 - Frontend: Account Restore, Last Admin Warning & Smart Company Claiming
### 🎯 Cél
Három frontend funkció implementálása teljes i18n támogatással: (1) 30 napos törölt fiók visszaállítás a bejelentkezési folyamatban, (2) Utolsó admin figyelmeztetés a fiók törlésénél, (3) Intelligens cég igénylés/csatlakozás.
### 🔧 Módosított fájlok
- [`frontend/src/i18n/hu.ts`](frontend/src/i18n/hu.ts) - Új i18n kulcsok: restore, lastAdminWarning, company claiming/joining
- [`frontend/src/i18n/en.ts`](frontend/src/i18n/en.ts) - Új i18n kulcsok angol nyelven
- [`frontend/src/stores/auth.ts`](frontend/src/stores/auth.ts) - `deleteAccount()` visszatérési érték bővítése `is_last_admin`-nel, `requestRestore()` és `verifyRestore()` hozzáadása
- [`frontend/src/components/LoginModal.vue`](frontend/src/components/LoginModal.vue) - Account Restore 5. face (3 lépés: email → OTP+password → siker)
- [`frontend/src/views/ProfileView.vue`](frontend/src/views/ProfileView.vue) - Last Admin Warning orange alert a törlés modalban
- [`frontend/src/views/organization/CompanyOnboardingView.vue`](frontend/src/views/organization/CompanyOnboardingView.vue) - Smart Company Claiming/Joining (active_exists, orphaned, manual fallback)
### ✅ Eredmény
- Vite build: sikeres (4.29s)
- Minden szöveg i18n kulcsokon keresztül, nincs hardcoded szöveg a Vue komponensekben
## 2026-06-14 - Enterprise User Management Module (RBAC & Bulk Actions)
### 🎯 Cél
Enterprise szintű User Management modul implementálása az Admin felülethez: RBAC-alapú bulk műveletek (ban, unban, soft_delete, restore, hard_delete) és paginált felhasználó lista szűréssel.
### 🔧 Változtatások
**1. Backend - [`admin.py`](backend/app/api/v1/endpoints/admin.py:470):**
- `BulkActionRequest` Pydantic modell regex validációval a támogatott akciókra
- `GET /admin/users` - Paginált felhasználó lista ILIKE e-mail szűréssel, role/is_active/is_deleted filterekkel
- `POST /admin/users/bulk-action` - Csoportos műveletek RBAC rangellenőrzéssel:
- `hard_delete`: superadmin (rank >= 100)
- `soft_delete/restore/unban`: admin (rank >= 90)
- `ban`: moderator (rank >= 50)
- Superadmin felhasználók védelme (kivéve ha saját magán hajtja végre)
- `SecurityAuditLog` minden bulk művelethez
**2. Frontend Store - [`adminUsers.ts`](frontend/src/stores/adminUsers.ts):**
- Új Pinia store `useAdminUsersStore` néven
- `fetchUsers()` - Paginált lekérés filter paraméterekkel
- `bulkAction()` - Bulk művelet végrehajtás + automatikus lista frissítés
**3. Frontend View - [`AdminUsersView.vue`](frontend/src/views/admin/AdminUsersView.vue):**
- Teljes admin felhasználó kezelő felület:
- E-mail keresés 400ms debounce-szal
- Role és státusz filter dropdown-ok
- Checkbox-os tábla "Select All" indeterminate állapottal
- Bulk Action Bar (Ban, Unban, Soft Delete, Restore, Hard Delete)
- **Hard Delete gomb elrejtése** `v-if="authStore.user?.role === 'superadmin'"`
- Role badge-ek színkódolva, státusz badge-ek (Active=zöld, Inactive=sárga, Deleted=piros)
- Pagination Previous/Next gombokkal
**4. Router & Layout:**
- [`index.ts`](frontend/src/router/index.ts) - `/admin/users` route regisztrálva
- [`AdminLayout.vue`](frontend/src/layouts/AdminLayout.vue) - Aktív menüpont kiemelés
### ✅ Eredmény
- Backend Python szintaxis: OK
- Vite build: sikeres (4.48s) - `AdminUsersView-CzzIFoUK.js` (9.69 kB)
- Minden funkció implementálva és verifikálva
## 2026-06-15 - Admin Service Catalog API & UI
### 🎯 Cél
Admin Service Catalog CRUD API és frontend UI megvalósítása i18n támogatással. A backend API végpontok (GET, POST, PATCH) már léteztek, a frontend oldal hiányzott.
### 🔧 Változtatások
**1. FRONTEND - [`adminServices.ts`](frontend/src/stores/adminServices.ts):**
- Új Pinia store a ServiceCatalog kezelésére
- `fetchServices()`, `createService()`, `updateService()` aszinkron akciók
- TypeScript interfészek: `ServiceCatalogItem`, `ServiceCatalogCreatePayload`, `ServiceCatalogUpdatePayload`
**2. FRONTEND - [`AdminServicesView.vue`](frontend/src/views/admin/AdminServicesView.vue):**
- Reszponzív kártya grid (1/2/3 oszlop) a szolgáltatások megjelenítésére
- Minden kártyán: service_code badge, név, leírás (line-clamp-2), kredit költség
- Szerkesztő/Létrehozó modál űrlap validációval
- Aktív/Inaktív toggle gomb
- Number input spinner eltávolítva (CSS + Tailwind classes)
**3. FRONTEND - [`router/index.ts`](frontend/src/router/index.ts):**
- `/admin/services` route regisztrálva `admin-services` néven
**4. FRONTEND - [`AdminLayout.vue`](frontend/src/layouts/AdminLayout.vue):**
- "Services" menüpont hozzáadva a Subscription Packages alá
**5. FRONTEND - [`en.ts`](frontend/src/i18n/en.ts), [`hu.ts`](frontend/src/i18n/hu.ts):**
- `admin.services.*` i18n kulcsok mindkét nyelven (title, subtitle, create_button, modal, field_*, stb.)
**6. ADATBÁZIS - Seed:**
- `sync_engine.py` futtatva: 1047 elem OK, rendszer szinkronban
- `seed_services.py` futtatva: 3 szolgáltatás beszúrva (SRV_DATA_EXPORT, SRV_AI_UPLOAD, SRV_DIGITAL_BOOK)
### ✅ Eredmény
- Backend API végpontok: már léteztek, nem kellett módosítani
- Frontend store, view, router, layout, i18n: mind implementálva
- Adatbázis: sync_engine OK, seed sikeres (3 rekord)
- A Services menüpont megjelent az Admin felületen a 3 kezdő szolgáltatással
## 2026-06-15 - Gitea #134: Company Settings, Admin Package Filters & Subscription Migration
### 🎯 Cél
Három független feladat implementálása: (1) Cégadatok szerkesztése modal + PATCH végpont, (2) Admin csomagok szűrése típus és dátum szerint, (3) Előfizetés migrációs szkript a régi denormalizált `subscription_plan` mezők átvezetésére.
### 🔧 Változtatások
**FRONTEND 1a - Context Switcher javítás ([`HeaderCompanySwitcher.vue`](frontend/src/components/header/HeaderCompanySwitcher.vue)):**
- Megállapítás: A komponens már tartalmazza a `display_name || name || #ID` fallback logikát.
- Az `OrganizationLayout` computed property-je (`orgName`) reaktívan frissül, amikor az `authStore.myOrganizations` betöltődik.
- Nincs szükség kódmódosításra - a meglévő logika megfelelően kezeli az async betöltést.
**FRONTEND 1b - Backend PATCH végpont ([`organizations.py`](backend/app/api/v1/endpoints/organizations.py:283)):**
- Új általános `PATCH /{org_id}` végpont (nem csak visual-settings), OWNER/ADMIN jogosultsággal.
- Támogatott mezők: `name`, `full_name`, `tax_number`, `display_name`, `default_currency`, `language`, `address_zip`, `address_city`, `address_street_name`, `address_street_type`, `address_house_number`, `address_stairwell`, `address_floor`, `address_door`, `address_hrsz`, `visual_settings`.
- `model_dump(exclude_unset=True)` használata részleges frissítéshez, JSONB merge a `visual_settings`-hez.
**FRONTEND 1b - Schema bővítés ([`organization.py`](backend/app/schemas/organization.py:41)):**
- `OrganizationUpdate` séma kiegészítve: `name`, `full_name`, `tax_number`, `address_zip`, `address_city`, `address_street_name`, `address_street_type`, `address_house_number`, `address_stairwell`, `address_floor`, `address_door`, `address_hrsz` mezőkkel.
**FRONTEND 1c - Cégbeállítások modal ([`OrganizationSettingsModal.vue`](frontend/src/components/organization/OrganizationSettingsModal.vue)):**
- Új komponens: Teleport modal form űrlappal (name, full_name, display_name, tax_number, address_zip, address_city, address_street_name, address_house_number).
- Pre-fill az `authStore.myOrganizations`-ból mount-kor.
- `PATCH /organizations/{orgId}` hívás csak a nem-üres mezőkkel.
- `authStore.fetchMyOrganizations()` frissítés mentés után.
- `close` és `saved` események.
**FRONTEND 1c - CompanyGarageView bővítés ([`CompanyGarageView.vue`](frontend/src/views/organization/CompanyGarageView.vue)):**
- "Company Settings" gomb a Quick Actions kártyában (emerald stílus, gear ikon).
- `showSettingsModal` ref + `OrganizationSettingsModal` import.
- Modal megjelenítése `v-if="showSettingsModal"`.
**FRONTEND 1c - i18n kulcsok ([`hu.ts`](frontend/src/i18n/hu.ts), [`en.ts`](frontend/src/i18n/en.ts)):**
- Új `company.settings*` kulcsok: settingsTitle, settingsDescription, settingsModalTitle, settingsName, settingsFullName, settingsTaxNumber, settingsDisplayName, settingsAddressZip, settingsAddressCity, settingsAddressStreet, settingsAddressHouseNumber, settingsSaveSuccess, settingsSaveError.
**FRONTEND 2a - Backend szűrés ([`admin_packages.py`](backend/app/api/v1/endpoints/admin_packages.py:51)):**
- `type_filter: Optional[str]` query param - JSONB `rules->>'type'` szűrés (private/corporate).
- `date_from: Optional[str]` query param - JSONB `rules->'lifecycle'->>'available_from' >= date_from`.
- `date_until: Optional[str]` query param - JSONB `rules->'lifecycle'->>'available_until' <= date_until`.
**FRONTEND 2b - Admin csomagok szűrő UI ([`AdminPackagesView.vue`](frontend/src/views/admin/AdminPackagesView.vue)):**
- Filter bar a header és a csomag grid között: Type dropdown (All/Private/Corporate), Date From/Until inputok, Clear Filters gomb.
- `typeFilter`, `dateFrom`, `dateUntil` ref-ek, `clearFilters()` függvény.
- `loadPackages()` filter paraméterek átadása.
**FRONTEND 2c - Store bővítés ([`adminPackages.ts`](frontend/src/stores/adminPackages.ts)):**
- `fetchPackages()` paraméterek: `type_filter`, `date_from`, `date_until` hozzáadva.
**BACKEND 3 - Migrációs szkript ([`migrate_subscriptions.py`](backend/app/scripts/migrate_subscriptions.py)):**
- **A)** `corp_free_v1` csomag létrehozása (ID=22, 0 áras, 3 jármű, 1 garázs).
- **B)** `finance.user_subscriptions` tábla létrehozása (user_id, tier_id, valid_from, valid_until, is_active, created_at, updated_at).
- **C)** Adatkonverzió: 19 org subscription (18x private_free_v1, 1x corp_premium_v1) és 21 user subscription (16x private_free_v1, 3x corp_premium_v1, 1x corp_vip_v1, 1x private_pro_v1).
- Plan mapping: FREE→private_free_v1, PRO→private_pro_v1, PREMIUM→corp_premium_v1, ENTERPRISE→corp_vip_v1.
**BACKEND 3 - UserSubscription modell ([`core_logic.py`](backend/app/models/core_logic.py:44)):**
- `UserSubscription` SQLAlchemy modell hozzáadva a `finance.user_subscriptions` táblához.
- Kapcsolatok: `user_id → identity.users.id`, `tier_id → system.subscription_tiers.id`.
### 🗄️ Adatbázis Műveletek
- `sync_engine.py` futtatva: 1056 elem OK, 0 shadow data - rendszer tökéletesen szinkronban.
- `corp_free_v1` tier létrehozva (ID=22).
- `finance.user_subscriptions` tábla létrehozva index-szel.
- 19 org_subscription + 21 user_subscription rekord beszúrva.
### ✅ Eredmény
- Backend: PATCH /organizations/{id} végpont működik, admin packages szűrés JSONB path lekérdezéssel.
- Frontend: Company Settings modal, Admin Packages filter UI, i18n támogatás.
- Adatbázis: subscription migráció sikeres, minden adat átvezetve a normalizált táblákba.
- Sync engine: teljes szinkronban.
---
## 2026-06-15: PATCH 500 javítás + Cég adatai Hamburger Menü
### 🎯 Cél
Három részből álló feladat:
1. **KRITIKUS:** PATCH /admin/packages/{id} 500-as hiba javítása
2. **Frontend:** Hiányzó i18n kulcsok (date_from_label, date_until_label) pótlása + dátum szűrés logika javítása
3. **Frontend:** Cég Settings Hamburger Menü + 'Cég adatai' modal read/edit móddal
### 🔍 Hibakeresés (Docker Log)
- `docker compose logs --tail=100 sf_api``AttributeError: 'dict' object has no attribute 'model_dump'`
- **Gyökér ok:** `payload.model_dump(exclude_unset=True)` rekurzívan dict-té alakítja a beágyazott Pydantic modelleket, így `update_data["rules"]` már sima dict, amin a `.model_dump()` hívás AttributeError-t dob.
### 🔧 Változtatások
**1. Backend: PATCH 500 fix (`backend/app/api/v1/endpoints/admin_packages.py`)**
- `new_rules = update_data["rules"].model_dump()``new_rules = update_data["rules"]` (közvetlen dict assign)
- `isinstance(new_rules, dict)` biztonsági ellenőrzés hozzáadva
- Dátum szűrés javítva: string összehasonlítás helyett `cast(..., Date)` használata
- Importok: `from datetime import date`, `from sqlalchemy import Date, cast`
**2. Frontend: i18n kulcsok (`frontend/src/i18n/hu.ts`, `frontend/src/i18n/en.ts`)**
- `admin.packages.date_from_label`, `admin.packages.date_until_label` kulcsok hozzáadva
- `company.companyDataTitle`, `company.companyDataMenu`, `company.close` kulcsok hozzáadva
**3. Frontend: CompanyDataModal (`frontend/src/components/organization/CompanyDataModal.vue`)**
- **Read mód:** Név, Teljes név, Adószám, Címek megjelenítése szövegesen; 'Bezárás' és 'Szerkesztés' gombok
- **Edit mód:** Input mezők (name, full_name, display_name, address); adószám (`tax_number`) mindig disabled/read-only
- 'Mégsem' → vissza read módba; 'Mentés' → PATCH API hívás, majd vissza read módba
- Glassmorphism design (dark theme, backdrop-blur, border-white/10)
**4. Frontend: Hamburger Menü (`frontend/src/layouts/OrganizationLayout.vue`)**
- 3-vonalas hamburger ikon a header right slot-jában
- Dropdown 'Cég adatai' menüponttal
- Outside-click handler a bezáráshoz
- CompanyDataModal integráció
### ✅ Ellenőrzés
- Backend konténer újraindítva: `docker compose restart sf_api` → sikeres startup
- Frontend build: `docker compose exec sf_public_frontend sh -c "cd /app && npm run build"` → 4.83s, hiba nélkül