# Service Finder Fejlesztési Történet ## 2026-06-21 - P0 Deep Audit: Database Consistency & Zombie API Hunt ### 🎯 Cél Teljes körű adatbázis konzisztencia ellenőrzés és zombie API végpontok felderítése a `vehicle`, `finance`, `fleet_finance` sémákban, mielőtt a frontend fejlesztés elkezdődik. ### 🔧 Eredmények **1. ADATBÁZIS TISZTASÁG:** ✅ PASS ### ✅ Verifikáció - Backend Python szintaxis: OK - Sync Engine: 1297 elem OK, 0 hiba, teljes szinkronban - API Test 1: `PATCH is_always_open=True` → `{"always_open": true, "days": {"monday": {"open": "00:00", "close": "23:59"}, ...}}` ✅ - API Test 2: `PATCH is_always_open=False` → `{"monday": {"open": "09:00", "close": "17:00"}}` (always_open metadata stripped) ✅ ## 2026-07-01 - Unified Address API Refactoring (Gitea #386-#391) ### 🎯 Cél Teljes körű cím API egységesítés a backend összes endpointján. Az audit során 4 különböző címformátumot és 5 kritikus inkonzisztenciát azonosítottunk. A refaktoring 3 fázisban (P1/P2/P3) 6 Gitea kártyán keresztül valósult meg. ### 🔧 Eredmények **1. Audit & Tervezés:** ✅ - Audit dokumentum: `docs/address_api_consistency_audit.md` - Logic spec: `plans/logic_spec_unified_address_refactoring.md` - 6 Gitea kártya létrehozva (#386-#391) **2. P1 #386 — Admin endpointok (admin.py, admin_persons.py):** ✅ - `admin.py list_users`: Flat dict → `AddressOut.model_validate(address)` nested object - `admin_persons.py`: `AddressResponse` → `AddressIn`/`AddressOut`, `PersonAddressUpdate` törölve - 3 `AddressResponse(...)` → `AddressOut.model_validate(person.address)` - Address update field mapping javítva (address_zip→zip, address_hrsz→parcel_id) **3. P2 #387 — User endpointok (users.py, schemas/user.py):** ✅ - `PersonUpdate`: 9 flat `address_*` field → `address: Optional[AddressIn]` - `_build_user_response`: Manual dict → `AddressOut.model_validate(person.address)` - `.dict()` → `.model_dump()` (Pydantic v2) **4. P2 #388 — Admin providers (admin_providers.py):** ✅ - `ProviderDetail`: Flat fields → `address_detail: Optional[AddressOut]` - `ProviderUpdateInput`: Flat fields → `address_detail: Optional[AddressIn]` - STEP 0: address_detail extraction + flat field mapping (zip→address_zip, city→city, stb.) **5. P2 #389 — Admin Organizations (admin_organizations.py):** ✅ - `GarageDetailsResponse`: 15 flat address fields → `address_detail: Optional[AddressOut]`, `billing_address_detail: Optional[AddressOut]`, `notification_address_detail: Optional[AddressOut]` (3 AddressOut nested objects) - `OrganizationUpdate`: 15 flat address fields → `address_detail: Optional[AddressIn]`, `billing_address_detail: Optional[AddressIn]`, `notification_address_detail: Optional[AddressIn]` (3 AddressIn nested objects) - `get_org_detail`: AddressOut construction from denormalized org fields for all 3 address types - `update_organization`: STEP 0 with 3 address_detail extraction + flat field mapping blocks (primary/billing/notification) - Response includes `address_detail` as unified AddressOut object **6. P3 #390 — Public providers (schemas/provider.py, provider_service.py):** ✅ - `ProviderSearchResult`: `address_detail: Optional[AddressOut]` - `ProviderQuickAddIn`: `address_detail: Optional[AddressIn]` - `ProviderUpdateIn`: `address_detail: Optional[AddressIn]` - `search_providers`: AddressOut construction from flat SQL columns **7. P3 #391 — Gamification (gamification.py):** ✅ - `submit_new_service` uses `city`/`address` as plain `Body()` string params - Maps directly to `ServiceStaging.city`/`ServiceStaging.address_line1` - Simple crowdsourcing — AddressIn conversion would be over-engineering **8. Phase 4 — AddressResponse removal:** ✅ - `AddressResponse` class completely removed from `schemas/user.py` - `PersonResponse.address`: `Optional[AddressResponse]` → `Optional[AddressOut]` ### ✅ Verifikáció - Backend Python szintaxis: OK (all files pass py_compile) - Sync Engine: 1274 OK, 0 Fixed, 26 Shadow Data (pre-existing) - All 6 Gitea cards (#386-#391): CLOSED ✅ ## 2026-07-01 - Service Provider Auto-Discovery (Gitea #348, #347) ### 🎯 Cél Implementálni a `find_or_create_provider_by_name()` service függvényt (#348) és az auto-discovery hook-ot az expense creation-ben (#347) a logic_spec alapján. ### 🔧 Eredmények **1. find_or_create_provider_by_name() (#348):** ✅ - **Exact match:** ILIKE keresés a ServiceProvider.name mezőn - **Fuzzy match:** pg_trgm similarity > 0.3 threshold, legjobb találat kiválasztása - **Create new:** Ha nincs találat, új ServiceProvider létrehozása `pending` státusszal, `source=SourceType.api_import` ("api_import"), `validation_score=0` - **Gamification:** PROVIDER_DISCOVERY (300 XP), PROVIDER_CONFIRMATION (150 XP), PROVIDER_VERIFIED_USE (100 XP), USE_UNVERIFIED_PROVIDER (200 XP) **2. Auto-discovery hook expense creation-ben (#347):** ✅ - Már implementálva volt a `POST /expenses/` végpontban - Ha `external_vendor_name` meg van adva, de `service_provider_id` nincs, meghívja a `find_or_create_provider_by_name()`-t - Hiba esetén warning log, nem blokkolja a költség létrehozását **3. Adatbázis módosítások:** - `pg_trgm` extension telepítve - `marketplace.source_type` enum kiegészítve `'api_import'` értékkel ### ✅ Verifikáció - Sync Engine: 1297 elem OK, 0 hiba, teljes szinkronban - Test `test_use_unverified_provider.py`: ALL TESTS PASSED ✅ - PROVIDER_DISCOVERY (300 XP): ✅ User A created provider - PROVIDER_CONFIRMATION (150 XP): ✅ User A reused own provider - USE_UNVERIFIED_PROVIDER (200 XP): ✅ User B used pending provider ## 2026-07-01 - P0 ARCHITECTURE CLEANUP: Final Address Refactor (Ghost Columns & AddressManager) ### 🎯 Cél A `docs/p0_address_manager_usage_audit_2026-07-01.md` auditban feltárt P0 kritikus hiba és P1 hiányosságok javítása. ### 🔧 Eredmények **1. P0 FIX — `admin.py:search_organizations_by_name`:** ✅ - `Organization.address_city` → `GeoPostalCode.city` (JOIN `Address` → `GeoPostalCode`) - Hozzáadva: `outerjoin(Address, Organization.address_id == Address.id)` - Hozzáadva: `outerjoin(GeoPostalCode, Address.postal_code_id == GeoPostalCode.id)` - Változás: `row.address_city` → `row.city` a response builderben **2. P1 FIX — `admin_persons.py:update_person`:** ✅ - 50+ sor direkt Address mező írás → egyetlen `AddressManager.create_or_update()` hívás - Import hozzáadva: `from app.services.address_manager import AddressManager` **3. P1 FIX — `admin_organizations.py:list_organizations`:** ✅ - `org.address_city` → `org.address.city if org.address else None` (relationship-based) **4. P1 FIX — `OrganizationUpdate` séma:** ✅ - Minden flat address field `[DEPRECATED]` prefixet kapott a description-ben **5. SQL Cleanup Script:** ✅ - `docs/sql/cleanup_ghost_columns_2026_07.sql` — 15 DROP COLUMN utasítás `IF EXISTS` védelemmel ### ✅ Verifikáció - Backend Python szintaxis: OK (all 3 files pass py_compile) - Import verifikáció: `Organization.address_id`=True, `Organization.address_city`=False (confirmed removed) - Sync Engine: 1274 OK, 0 Fixed, 26 Shadow Data (pre-existing ghost columns confirmed) ## 2026-07-01 - P0 CRITICAL HOTFIX: Fix 500 Error on /admin/providers List View ### 🎯 Cél A GET /api/v1/admin/providers végpont 500-as hibát dobott a denormalizált cím oszlopok eltávolítása után. ### 🔧 Root Cause & Fix **Root Cause 1 — Wrong ServiceStaging import:** Az `admin_providers.py` a `ServiceStaging`-et az `app.models.marketplace.service` modulból importálta, amely NEM tartalmazza a `full_address` és `city` mezőket. A helyes import az `app.models.marketplace.staged_data` modulból származik, amely rendelkezik ezekkel a mezőkkel. **Root Cause 2 — AddressOut missing required `id` field:** A `get_provider_detail` és `update_provider` végpontok `AddressOut(...)`-ot konstruáltak a ServiceProvider flat address mezőiből, de az `AddressOut` séma `id: uuid.UUID` mezője kötelező (nem Optional). Mivel a ServiceProvider nem rendelkezik Address kapcsolattal, az `address_detail` mezőt `None`-ra kell állítani. ### 🔧 Változtatások 1. **`backend/app/api/v1/endpoints/admin_providers.py:41`** — Import csere: `from app.models.marketplace.service import ServiceStaging` → `from app.models.marketplace.staged_data import ServiceStaging` 2. **`backend/app/api/v1/endpoints/admin_providers.py:568`** — `address_detail=AddressOut(...)` → `address_detail=None` (get_provider_detail) 3. **`backend/app/api/v1/endpoints/admin_providers.py:1340`** — `address_detail=AddressOut(...)` → `address_detail=None` (update_provider) ### ✅ Verifikáció - `GET /api/v1/admin/providers` (default): 200 ✅ (50 items) - `GET /api/v1/admin/providers?status=pending`: 200 ✅ (50 items) - `GET /api/v1/admin/providers/4`: 200 ✅ (address_detail=None) - Sorting by name/validation_score/status: 200 ✅ - Search: 200 ✅ (4 results) ## 2026-07-01 - P0 BUGFIX: Organization Details 500 Error & Provider Raw Data ### 🎯 Cél Két P0 hibajavítás: 1. **Organization Details 500 error** - A `fleet.organizations` táblából fizikailag eltávolított denormalizált cím oszlopok (15 db: address_city, billing_zip, notification_street_name, stb.) miatt a `PUT /{org_id}` végpont `setattr`-nál 500-as hibát dobott, ha a frontend elküldte ezeket a mezőket. 2. **Provider Raw Data hiány** - A `GET /admin/providers/{id}` végpont ServiceProvider rekordoknál nem adta vissza a `raw_data` mezőt, így a frontend "Nyers adatok" tab üresen/mock adatként jelent meg. ### 🔧 Eredmények **1. `admin_organizations.py` - Deprecated address field filter** (1341-1361. sor) - Bevezetve a `DEPRECATED_ADDRESS_FIELDS` halmaz, amely tartalmazza mind a 15 ghost oszlopot - A `update_organization` függvény `setattr` ciklusa előtt ezek a mezők kiszűrésre kerülnek `logger.warning` kíséretében - A GET végpont (`get_organization_details`) már korábban is helyesen, relationship-ekből töltötte a denormalizált mezőket (1114-1139. sor) **2. `admin_providers.py` - Raw data hozzáadása ServiceProvider válaszhoz** (589-591. sor) - A `get_provider_detail` ServiceProvider ágához hozzáadva: `raw_data=sp.raw_data or {}` - Ezzel a frontend `RawDataViewer` komponense valós adatokat kap, nem üres/mock állapotot ### ✅ Verifikáció - Mindkét fájl Python szintaxis ellenőrzése: **OK** - `sync_engine.py` futtatás: **1274 elem OK, 0 hiba** - teljes szinkronban --- ## 2026-07-02: Bugfix - Provider validációs érték és research_in_progress státusz (Gitea #393) ### 🎯 Cél Provider 1752 (Carnanny) esetén a validációs érték üres volt (validation_score=0), és a `research_in_progress` státusz nem jelent meg helyesen a frontend státuszablakában. ### 🔧 Root Cause & Fix - 4 bug javítva **Bug 1 - Backend: `validation_score` hardcoded to 0 for staging providers** - **Hely:** [`admin_providers.py:613`](backend/app/api/v1/endpoints/admin_providers.py:613) - **Ok:** A `get_provider_detail` ServiceStaging ágában `validation_score=0` volt keménykódolva - **Fix:** `validation_score=ss.trust_score or 0` - így a trust_score értéke jelenik meg validation_score-ként **Bug 2 - Frontend: `research_in_progress` hiányzott a statusLabel/statusDescription függvényekből** - **Hely:** [`index.vue:869-887`](frontend_admin/pages/providers/[id]/index.vue:869) - **Ok:** A switch csak 'approved', 'pending', 'rejected' eseteket kezelte - **Fix:** Hozzáadva: `case 'research_in_progress': return 'Kutatás folyamatban'` és magyarázó leírás **Bug 3 - Frontend: `research_in_progress` hiányzott a CSS computed property-kből** - **Hely:** [`index.vue:774-808`](frontend_admin/pages/providers/[id]/index.vue:774) - **Ok:** A `statusBannerClass`, `statusIconClass`, `statusTextClass`, `statusSubtextClass` nem kezelték ezt a státuszt → piros (elutasított) stílust kapott - **Fix:** Mind a 4 computed property-hez hozzáadva a `research_in_progress` case kék színű (blue-500) Tailwind osztályokkal **Bug 4 - Frontend: Moderációs műveletek rossz ágba kerültek** - **Hely:** [`index.vue:283`](frontend_admin/pages/providers/[id]/index.vue:283) - **Ok:** `v-if="provider.status === 'pending'"` csak a pending státuszt kezelte, a `research_in_progress` a `v-else` ágba esett ("elutasításra került") - **Fix:** `v-if="provider.status === 'pending' || provider.status === 'research_in_progress'"` - így a research_in_progress státuszú provider-ek is látják az Approve/Reject gombokat ### 📊 Adatbázis megállapítás - A `service_staging.trust_score` mező értéke 0 a DB-ben, de a `raw_data` JSON tartalmazza: `"trust_score": 20` - Ez egy OSM scout bot enrichment pipeline hiányosság - a bot nem extractálja a `trust_score`-t a `raw_data`-ból a staging oszlopba - A fix biztosítja, hogy AMIKOR a trust_score populálva lesz, az helyesen megjelenjen validation_score-ként ### ✅ Verifikáció - Backend Python szintaxis: **OK** - API konténer restart: **OK** - API hívás provider 1752-re: **200 OK** - status=research_in_progress, validation_score=0 (trust_score=0 miatt), raw_data jelen van - Frontend: mind a 4 Vue komponens módosítás érvényben