szolgáltatók beálltásai, szerkesztése , létrehozása
This commit is contained in:
746
.roo/history.md
746
.roo/history.md
@@ -8,654 +8,220 @@ Backend SQL hibák javítása a GET /admin/users végponton (Address outerjoin,
|
||||
### 🔧 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.
|
||||
- `GET /admin/users` SQL javítás: `Address` outerjoin-ra változtatva (`isouter=True`), hogy a cím nélküli userek is visszajöjjenek
|
||||
- Telefonszám keresés: `phone` mező hozzáadva a `or_()` feltételhez
|
||||
|
||||
**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.
|
||||
**2. FRONTEND - [`AdminLayout.vue`](frontend/src/layouts/AdminLayout.vue:1):**
|
||||
- `HeaderProfile` komponens beillesztve a jobb felső sarokba (belépett user avatar + név)
|
||||
- A `SidebarToggle` gomb fixálva a sidebar szélén
|
||||
|
||||
**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.
|
||||
**3. FRONTEND - [`AdminUsersView.vue`](frontend/src/views/AdminUsersView.vue:1):**
|
||||
- **Sticky Bulk Action Bar:** A tömeges műveletek sáv (`BulkActionBar`) `sticky` pozícionálást kapott, hogy görgetéskor is látszódjon
|
||||
- **Clear/X gomb:** A keresőmezőbe egy `X` gomb került, ami egy kattintással törli a keresési feltételt
|
||||
|
||||
### ✅ 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
|
||||
## 2026-06-16 - Fix 404 API Router & Redesign Dashboard Launcher Card
|
||||
|
||||
### 🎯 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.
|
||||
A Dashboard Service Finder kártya (Card 3) átalakítása elegáns, kétgombos indítópulttá (Launcher), valamint a providers API 404 hiba kivizsgálása és javítása.
|
||||
|
||||
### 🔧 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.
|
||||
**1. BACKEND AUDIT - [`api.py`](backend/app/api/v1/api.py:38):**
|
||||
- Ellenőrizve: `providers` router regisztrálva van (`include_router(providers.router, prefix="/providers")`)
|
||||
- Ellenőrizve: providers végpontokon NINCS trailing slash (`/categories`, `/search`, `/quick-add`)
|
||||
- **Route-ok élőben is ellenőrizve:** `GET /api/v1/providers/categories`, `GET /api/v1/providers/search`, `POST /api/v1/providers/quick-add` mind aktívak
|
||||
|
||||
### ✅ 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.
|
||||
**2. FRONTEND - [`ProviderQuickAddModal.vue`](frontend/src/components/provider/ProviderQuickAddModal.vue:223):**
|
||||
- `fetchCategories()` már rendelkezik try/catch blokkal, 11 hardcoded fallback kategóriával
|
||||
- Nincs szükség módosításra
|
||||
|
||||
## 2026-06-08 - F5 Refresh Logout Bug Fix & HeaderLogo Contextual Navigation
|
||||
**3. FRONTEND - [`DashboardView.vue`](frontend/src/views/DashboardView.vue:187):**
|
||||
- **Card 3 teljes átalakítása:** A 3-lépéses kontextuális kereső űrlap (input, 2 dropdown, keresés gomb) ELTÁVOLÍTVA
|
||||
- **Launcher dizájn:** Két nagy gomb, szépen formázva:
|
||||
- `🔍 Szervizek Keresése` — nagy, zöld gradient gomb, `isSearchModalOpen` modalt nyit
|
||||
- `➕ Új Szolgáltató Rögzítése` — szekunder, dashed border gomb, `isSfQuickAddOpen` modalt nyit (meglévő ProviderQuickAddModal)
|
||||
- **Search Modal (Placeholder):** Teleportált modál, benne: `🔍` ikon, "Részletes kereső térképpel hamarosan..." szöveg, fejlesztés alatt státusz
|
||||
- Régi `sfSearchLocation`, `sfSearchCategory`, `sfSearchVehicleId`, `sfCategories`, `fetchSfCategories()`, `onSfSearch()` eltávolítva
|
||||
|
||||
## 2026-06-17 - P0 Bugfix: Provider Data Persistence & Schema Alignment
|
||||
|
||||
### 🎯 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.
|
||||
Kritikus adatperzisztencia hiba javítása a Service Finder Provider rendszerében. A frontend edit modal nem hívott backend API-t, így a szerkesztett adatok elvesztek. Emellett a contact mezők (phone, email, website, tags) nem kerültek perzisztálásra a quick-add során, és a search nem adta vissza ezeket.
|
||||
|
||||
### 🔧 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.
|
||||
**1. BACKEND - [`provider.py`](backend/app/schemas/provider.py:1) (NEW):**
|
||||
- `ProviderQuickAddIn`: `contact_phone`, `contact_email`, `website`, `tags` mezők hozzáadva
|
||||
- `ProviderSearchResult`: `address_zip`, `contact_phone`, `contact_email`, `website`, `tags` mezők hozzáadva
|
||||
- `ProviderUpdateIn` (NEW): name, city, address_zip, street, contact_phone, contact_email, website, tags
|
||||
- `ProviderUpdateResponse` (NEW): id, name, status, message
|
||||
|
||||
**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é.
|
||||
**2. BACKEND - [`provider_service.py`](backend/app/services/provider_service.py:486):**
|
||||
- `quick_add_provider()`: contact mezők mentése ServiceProfile-ba
|
||||
- `search_providers()`: LEFT JOIN ServiceProfile, contact mezők visszaadása
|
||||
- `update_provider()` (NEW): Organization + ServiceProfile atomi frissítése
|
||||
|
||||
**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
|
||||
**3. BACKEND - [`providers.py`](backend/app/api/v1/endpoints/providers.py:134):**
|
||||
- `PUT /providers/{id}` végpont hozzáadva (authentikált, hibakezeléssel)
|
||||
|
||||
## 2026-06-12 - Dynamic Parameters Check & Centralized Odometer Implementation
|
||||
**4. FRONTEND - [`ProviderEditModal.vue`](frontend/src/components/provider/ProviderEditModal.vue:191):**
|
||||
- **CRITICAL FIX**: `handleSave()` most `api.put()`-et hív, nem csak eventet emitál
|
||||
- `@save` → `@saved` (past tense, API call után)
|
||||
- `address_zip`, `contact_phone`, `contact_email`, `website`, `tags` mezők támogatása
|
||||
- `source` kivéve a payload-ból (backend-only)
|
||||
|
||||
**5. FRONTEND - [`ProviderQuickAddModal.vue`](frontend/src/components/provider/ProviderQuickAddModal.vue:253):**
|
||||
- Telefon, email, weboldal, címkék mezők hozzáadva a formhoz
|
||||
- Tag management (vessző/pontosvessző parsing, remove gomb)
|
||||
- Payload bővítése contact mezőkkel
|
||||
|
||||
**6. FRONTEND - [`ServiceFinderView.vue`](frontend/src/views/ServiceFinderView.vue:375):**
|
||||
- `@save` → `@saved` event binding
|
||||
- `handleEditSaved()`: mentés után automatikus keresés újrafuttatás
|
||||
|
||||
### ✅ Verifikáció
|
||||
- `sync_engine` lefuttatva: **1061 elem OK, 0 javítás, 0 shadow data** - rendszer tökéletesen szinkronban
|
||||
|
||||
## 2026-06-17 - P1 Critical Align: Atomizált címmezők a Provider sémákban
|
||||
|
||||
### 🎯 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).
|
||||
A `street: Optional[str]` mező eltávolítása és helyette atomizált címmezők (`address_street_name`, `address_street_type`, `address_house_number`) bevezetése a Pydantic sémákban, backend service-ben és frontend űrlapokon. A kapcsolatfelvételi adatok (contact_phone, contact_email, website, tags) a ServiceProfile-ba kerülnek.
|
||||
|
||||
### 🔧 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.
|
||||
**1. BACKEND - [`provider.py`](backend/app/schemas/provider.py):**
|
||||
- `street` mező ELTÁVOLÍTVA a `ProviderQuickAddIn` és `ProviderUpdateIn` sémákból
|
||||
- `address_street_name`, `address_street_type`, `address_house_number` mezők HOZZÁADVA mindkét sémához
|
||||
- `ProviderSearchResult` bővítve az atomizált címmezőkkel
|
||||
|
||||
**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.
|
||||
**2. BACKEND - [`provider_service.py`](backend/app/services/provider_service.py):**
|
||||
- `quick_add_provider()`: `data.street` → `data.address_street_name`, `data.address_street_type`, `data.address_house_number` az Organization és Branch táblákban
|
||||
- `update_provider()`: ugyanez az atomizált címkezelés
|
||||
- `search_providers()`: az org SELECT most már tartalmazza az `address_street_name`, `address_street_type`, `address_house_number` mezőket
|
||||
- A Branch létrehozásánál a `street_name`, `street_type`, `house_number` mezők külön-külön töltődnek
|
||||
|
||||
**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.
|
||||
**3. FRONTEND - [`ProviderQuickAddModal.vue`](frontend/src/components/provider/ProviderQuickAddModal.vue):**
|
||||
- A régi egyesített "Cím (Utca, házszám)" mező helyett 3 külön mező: Utca neve (text), Közterület jellege (select/dropdown 15 opcióval), Házszám (text)
|
||||
- Payload az új atomizált kulcsokkal megy a backend felé
|
||||
|
||||
### 🛠️ 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
|
||||
**4. FRONTEND - [`ProviderEditModal.vue`](frontend/src/components/provider/ProviderEditModal.vue):**
|
||||
- Ugyanaz a 3 mezős szétbontás, a form populate az atomizált mezőkből történik
|
||||
- Payload atomizált kulcsokkal
|
||||
|
||||
### ✅ 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
|
||||
**5. FRONTEND - [`ProviderDetailModal.vue`](frontend/src/components/provider/ProviderDetailModal.vue):**
|
||||
- Az intelligens címösszefűzés (`formattedAddress` computed) a `provider.address_street_name + ' ' + provider.address_street_type + ' ' + provider.address_house_number` alapján történik
|
||||
- `hasAddress` computed ellenőrzi az atomizált mezők meglétét
|
||||
|
||||
## 2026-06-12 - Emergency Regression Fix: Vehicle Creation 500 Error (UnboundLocalError)
|
||||
### ✅ Verifikáció
|
||||
- `sync_engine` lefuttatva: **1061 elem OK, 0 javítás, 0 shadow data** - rendszer tökéletesen szinkronban
|
||||
- Python syntax check: minden fájl szintaktikailag helyes
|
||||
|
||||
## 2026-06-17 - Provider Update & Search Fix Csomag (#264)
|
||||
|
||||
### 🎯 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
|
||||
Két kritikus hiba javítása a provider endpointokban:
|
||||
1. **PUT /providers/{id} → 404**: A konténer nem volt újraindítva a providers modul kódváltoztatásai után
|
||||
2. **GET /providers/search → 500**: `.astext` hiba JSONB subscripten + UNION oszlopszám mismatch
|
||||
3. **Multi-source update**: Az `update_provider` csak Organization-ben keresett, de a search 3 forrást használ
|
||||
|
||||
### 🔧 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).
|
||||
**1. BACKEND - [`provider_service.py:207`](backend/app/services/provider_service.py:207):**
|
||||
- `.astext` → `cast()` javítás: `Organization.external_integration_config["source"].astext` → `cast(Organization.external_integration_config["source"], String)`
|
||||
- **Root cause**: JSON oszlop subscript-je `BinaryExpression`-t ad vissza, amelyen nincs `.astext`
|
||||
|
||||
**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.
|
||||
**2. BACKEND - [`provider_service.py:232-274`](backend/app/services/provider_service.py:232):**
|
||||
- UNION oszlopszám mismatch javítva: staging és crowd SELECT-ekhez hozzáadva a hiányzó `contact_phone`, `contact_email`, `website`, `specialization_tags` mezők (14 oszlopra egységesítve)
|
||||
|
||||
### ✅ 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
|
||||
**3. BACKEND - [`provider_service.py:477-622`](backend/app/services/provider_service.py:477):**
|
||||
- Multi-source update logika: `update_provider()` most már mindhárom forrást támogatja:
|
||||
1. `fleet.organizations` (verified orgs) - közvetlen frissítés
|
||||
2. `marketplace.service_staging` (robot adatok) - migrálás Organization-be
|
||||
3. `marketplace.service_providers` (crowdsourced) - migrálás Organization-be
|
||||
|
||||
## 2026-06-12 - B2B/B2C Context Switcher PLG Actions
|
||||
**4. INFRA - [`pre_start.sh`](backend/app/scripts/pre_start.sh):**
|
||||
- Dokumentálva: a `uvicorn` `--reload` nélkül fut, kódváltoztatás után `docker compose restart sf_api` szükséges
|
||||
|
||||
**5. BACKEND - [`provider_service.py:578-596`](backend/app/services/provider_service.py:578):**
|
||||
- **Adatvédelmi javítás (2026-06-17):** A címmezők (`address_city`, `address_zip`, `address_street_name`, `address_street_type`, `address_house_number`) most már **csak akkor íródnak felül**, ha a frontend explicit nem-`null` értéket küld. Ez megakadályozza, hogy a meglévő címadatok véletlenül `null`-ra állítódjanak, amikor a felhasználó csak más mezőket szerkeszt.
|
||||
- **Trigger:** A Gitea kártya visszautasításra került (`denied` státusz) a felhasználó által: *"az adatok tárolása minden esetben bontottan történjen meg és ha hiányzik valamelyik az alap cím tárolási adatból akkor vissza kell tenni."*
|
||||
|
||||
### ✅ Verifikáció
|
||||
- **PUT /providers/58**: 200 OK ✅
|
||||
- **PUT /providers/58 (partial update - csak zip)**: 200 OK ✅ (többi mező nem nullázódik)
|
||||
- **GET /providers/search?q=Dunakeszi**: 200 OK ✅ (2 provider)
|
||||
- **GET /providers/categories**: 200 OK ✅ (11 categories)
|
||||
- **Login**: 200 OK ✅
|
||||
|
||||
## 2026-06-17 - i18n: Hiányzó provider címmező fordítások hozzáadása
|
||||
|
||||
### 🎯 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.
|
||||
A ProviderEditModal.vue 6 darab `provider.*` i18n kulcsa hiányzott mindkét nyelvi modulból (`hu.ts`, `en.ts`), így a felhasználói felületen a kulcsnevek (pl. `provider.streetNameLabel`) jelentek meg a lefordított szöveg helyett.
|
||||
|
||||
### 🔧 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.
|
||||
**1. FRONTEND - [`hu.ts`](frontend/src/i18n/hu.ts:1300):**
|
||||
- `provider.streetNameLabel`: `'Utca neve'`
|
||||
- `provider.streetNamePlaceholder`: `'Pl. Egressy'`
|
||||
- `provider.streetTypeLabel`: `'Közterület jellege'`
|
||||
- `provider.streetTypePlaceholder`: `'Válassz típust...'`
|
||||
- `provider.houseNumberLabel`: `'Házszám'`
|
||||
- `provider.houseNumberPlaceholder`: `'Pl. 4'`
|
||||
|
||||
**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`.
|
||||
**2. FRONTEND - [`en.ts`](frontend/src/i18n/en.ts:1300):**
|
||||
- `provider.streetNameLabel`: `'Street Name'`
|
||||
- `provider.streetNamePlaceholder`: `'e.g. Egressy'`
|
||||
- `provider.streetTypeLabel`: `'Street Type'`
|
||||
- `provider.streetTypePlaceholder`: `'Select type...'`
|
||||
- `provider.houseNumberLabel`: `'House Number'`
|
||||
- `provider.houseNumberPlaceholder`: `'e.g. 4'`
|
||||
|
||||
### ✅ 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.
|
||||
### ✅ Verifikáció
|
||||
- Mindkét i18n fájl szintaktikailag helyes (Node.js require sikeres)
|
||||
- A ProviderEditModal.vue összes `t('provider.*')` hívása le van fedve
|
||||
|
||||
## 2026-06-12 - Clean Odometer & Switcher Logic (Odometer Revert)
|
||||
## 2026-06-17 - "Dunakeszi, Dunakeszi" duplikáció javítása + irányítószám megjelenítés
|
||||
|
||||
### 🎯 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.
|
||||
A szervizkereső oldalon a kártyán "Dunakeszi, Dunakeszi" duplikált városnév jelent meg, mert a backend `address` mezője megegyezett a `city` mezővel. A részletes nézetből hiányzott az irányítószám.
|
||||
|
||||
### 🔧 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.
|
||||
**1. BACKEND - [`provider_service.py`](backend/app/services/provider_service.py:198):**
|
||||
- Az `address` mezőt `Organization.address_city.label("address")`-ről `func.concat(...)`-re változtattuk, ami az összes atomizált címmezőt (irányítószám, város, utca, közterület, házszám) fűzi össze.
|
||||
- Példa eredmény: `"2120 Dunakeszi, Egressy utca 4"` a korábbi `"Dunakeszi"` helyett.
|
||||
|
||||
**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.
|
||||
**2. FRONTEND - [`ServiceFinderView.vue`](frontend/src/views/ServiceFinderView.vue:543):**
|
||||
- Új `formatCardAddress(provider)` metódus, ami a kártyán az atomizált címmezőkből építi fel a címet.
|
||||
- Fallback: ha nincs atomizált adat, a backend által összefűzött `address` mezőt használja.
|
||||
|
||||
**3. [`backend/app/models/vehicle/__init__.py`](backend/app/models/vehicle/__init__.py):**
|
||||
- `VehicleOdometerState` eltávolítva az importokból és `__all__`-ból.
|
||||
**3. FRONTEND - [`ProviderDetailModal.vue`](frontend/src/components/provider/ProviderDetailModal.vue:237):**
|
||||
- A `formattedAddress` computed property most már tartalmazza az `address_zip` mezőt is.
|
||||
- Formátum: `"2120 Dunakeszi, Egressy utca 4"`
|
||||
|
||||
**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.
|
||||
### ✅ Verifikáció
|
||||
- Backend API: `GET /api/v1/providers/search?city=Dunakeszi` → `address` = `"2120 Dunakeszi, Egressy utca 4"` ✅
|
||||
- Backend API: Autónyíri Kft. (id=58) → `address_zip=2120`, `address_street_name=Egressy`, `address_street_type=utca`, `address_house_number=4` ✅
|
||||
- Frontend build: `npm run build` sikeres, 0 hiba ✅
|
||||
- Backend konténer újraindítva: `docker compose restart sf_api` ✅
|
||||
|
||||
**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
|
||||
## 2026-06-17 - Kártya fő szolgáltatás (category) mindig megjelenítése
|
||||
|
||||
### 🎯 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ó.
|
||||
A szervizkereső kártyákon a fő szolgáltatás (category) mindig látszódjon. Ha van, akkor a kategória neve, ha nincs, akkor egy szaggatott vonalú placeholder.
|
||||
|
||||
### 🔧 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.
|
||||
**1. FRONTEND - [`ServiceFinderView.vue:263`](frontend/src/views/ServiceFinderView.vue:263):**
|
||||
- A category badge `v-if="provider.category"` helyett `v-if`/`v-else` szerkezet:
|
||||
- Ha van category: `bg-sf-accent/10` háttér, `text-sf-accent` szín
|
||||
- Ha nincs: szaggatott vonalú (`border-dashed`) placeholder "Nincs kategória" / "No category"
|
||||
|
||||
### ✅ 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.
|
||||
**2. FRONTEND - [`hu.ts:1237`](frontend/src/i18n/hu.ts:1237):**
|
||||
- `serviceFinder.noCategory`: `'Nincs kategória'`
|
||||
|
||||
## 2026-06-13 - Fix Catalog API 404 Mismatch & Real Database Data
|
||||
**3. FRONTEND - [`en.ts:1237`](frontend/src/i18n/en.ts:1237):**
|
||||
- `serviceFinder.noCategory`: `'No category'`
|
||||
|
||||
### 🎯 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
|
||||
### ✅ Verifikáció
|
||||
- Frontend build: `npm run build` sikeres, 0 hiba ✅
|
||||
|
||||
Reference in New Issue
Block a user