20 KiB
Service Finder Fejlesztési Történet
2026-06-21 - P0 Deep Audit: Database Consistency & Zombie API Hunt
🎯 Cél
...
2026-07-07 - P0 ServiceProvider AddressManager Integration
🎯 Cél
...
2. Seed script létrehozása és futtatása
A seed_dismantler_category.py szkript létrehozva (backend/app/scripts/), amely idempotens módon beszúrja az "Autóbontó / Használt alkatrész" kategóriát.
✅ Verifikáció
- Seed script futtatva → ✅ Sikeresen beszúrva az 'Autóbontó / Használt alkatrész' kategória (ID: 767). Összes rekord: 135.
- admin_providers.py szintaxis ellenőrzés →
import app.api.v1.endpoints.admin_providerssikeres (✅ module imports OK) - sync_engine futtatva → 1282 OK, 0 issue, schema fully in sync
2026-07-07 - P0 BUGFIX: Live Provider Advanced Fields & Gas Station Categories
🎯 Cél
P0 kritikus hibajavítás: Live Provider (ID 6, MOL Fót) opening_hours és category_ids mezői elvesztek a backend update_provider() végpontban, mert a ServiceProvider-hez nem tartozott ServiceProfile rekord. Emellett a "Shop / Kisbolt" kategória hiányzott a rendszerből.
🔧 Változtatások
1. Backend fix: admin_providers.py
- P0 BUGFIX (2026-07-07): Ha egy Live Provider-hez nem létezik ServiceProfile, de a PATCH kérésben advanced mezők (
opening_hours,specializations,supported_vehicle_classes,category_ids) érkeznek, a backend MOST automatikusan létrehoz egy ServiceProfile rekordot. - Response serialization fix: A
category_idsvisszaadása előtt a rendszer lekérdezi a tényleges DB állapotot aServiceExpertisetáblából (resolved_category_ids), így a frontend mindig a valóban elmentett adatokat kapja vissza.
2. Seed script: seed_gas_station.py
- Létrehozva a hiányzó "Shop / Kisbolt" (Convenience Store) kategória (ID: 768) az "Üzemanyag és Töltőállomás" (ID=755) szülő alá.
- Meglévő kategóriák (ID=755, 756, 757) érintetlenek.
✅ Verifikáció
- Seed script futtatva → ✅ 'Shop / Kisbolt' (ID: 768) már létezik. Összes rekord: 136.
- sync_engine futtatva → 1282 OK, 0 issue, schema fully in sync
2026-07-07 - P0 CRITICAL HOTFIX: Promotion Engine v2
🎯 Cél
Javítani a Promotion Engine-t a PATCH /admin/providers/{id} végponton, amely ServiceStaging rekordok jóváhagyásakor nem futott le.
🔍 Root Cause Analysis
- Trigger condition too fragile: Az
if is_staging and new_status == "approved":feltétel anew_statusváltozóra támaszkodott, amely aupdate_fields.get("status")-ból származott. Ha a státusz nem volt explicit átadva a PATCH payload-ban (pl. csaknamemezőt módosítottak),new_statusNonevolt, és a promotion blokk teljesen kimaradt. - ID handling bug: Promotion után a
provider_objátállításra került az új ServiceProvider-re, de az AuditLog (target_id=str(provider_id)) és a ServiceProfile lekérdezés (ServiceProfile.service_provider_id == provider_id) továbbra is a régi staging ID-t (pl. 1754) használta, nem az új provider ID-t.
🔧 Javítások
- Trigger condition →
effective_status: A feltétel most aprovider_obj.status-t (tényleges DB állapot) ellenőrzi közvetlenül, nem anew_statuspayload változót. Ez biztosítja, hogy a promotion akkor is lefusson, ha a státusz már "approved" volt a DB-ben, vagy ha nem volt explicit átadva. - ID fix: Minden
provider_idhivatkozás (AuditLog, ServiceProfile lookup)provider_obj.id-ra lett cserélve, ami promotion után az új ServiceProvider ID-ját tartalmazza. - Logolás:
logger.error("P0 PROMOTION TRIGGERED")hozzáadva a promotion blokk elejére a Docker logokban való trace-elhetőségért.
✅ Verifikáció
- Szintaxis ellenőrzés → ✅
py_compilesikeres - Konténer újraindítás → ✅
docker compose restart sf_apisikeres - API elérhető → ✅ Uvicorn fut a 8000-es porton
2026-07-07 - DDD Database Refactoring 1.0 (P0 - 3 Phase)
🎯 Cél
DDD Database Refactoring 1.0 három fázisban: address_id FK bevezetése, admin_providers.py egyszerűsítése, frontend cleanup.
🔧 Változtatások
#396 - Phase 1: address_id FK (Already Complete)
ServiceProvider.address_idésaddress_relmár létezett a modellben- Mind a 7 provider rekord rendelkezik
address_id-vel sync_engine1282 OK, 0 drift - nincs teendő
#397 - Phase 2: admin_providers.py Simplify
_build_unified_providers_query():ServiceProvider.address_idhozzáadva a SELECT-hez (12. oszlop)ServiceStagingésOrganizationUNION ágak:literal(None).label("address_id")placeholder a column parityértlist_providers():address_detailfeloldásaAddressManager.get_normalized()segítségével minden sorban- Ellenőrizve:
list_providersendpoint minden provider esetén visszaadja azaddress_detail-t
#398 - Phase 3: Frontend Cleanup
garages/index.vueline 157:garage.cityfallback eltávolítva → csakgarage.address_detail?.cityproviders/index.vueline 137: máraddress_detail-t használ (nem kellett módosítani)providers/[id]/index.vuelines 144-166: máraddress_detail-t használ (nem kellett módosítani)providers/[id]/edit.vuelines 1133-1148: márdata.address_detail-t használ (nem kellett módosítani)
✅ Verifikáció
- #397 API teszt → ✅
list_providersendpoint 5 provider-t ad visszaaddress_detailmezővel - #398 Frontend regex audit → ✅ Nincs több
provider.addressvagyprovider.cityflat field referencia - Konténer újraindítás → ✅
docker compose restart sf_apisikeres
2026-07-07 - P0 BUGFIX: Category Sync & Parent-Child UX Automation
🎯 Cél
A provider admin felület kategória szinkronizációjának javítása legacy provider-eknél (ahol a ServiceProfile organization_id-n keresztül kapcsolódik), valamint a frontend kategória-választó UX automatizálása.
🔧 Változtatások
Backend: backend/app/api/v1/endpoints/admin_providers.py
get_provider_detail(line 615-618): ServiceProfile lekérdezés kiterjesztve OR-ra:(ServiceProfile.service_provider_id == provider_id) | (ServiceProfile.organization_id == provider_id)update_provider(line 1575-1578): Ugyanez a javítás a PATCH végpontban,provider_obj.id-t használva- Root Cause: Legacy provider-eknél (pl. ID 6) a ServiceProfile
organization_idmezőn keresztül kapcsolódik, nemservice_provider_id-n. Az egyszerű FK keresés miatt a profil nem található ->category_idselveszett a válaszból és a mentésből.
Frontend: frontend_admin/pages/providers/[id]/edit.vue
- Import (line 626):
watchhozzáadva a Vue import-hoz parentLookupMap(line 942): Flat Map<number, number> a kategória fa rekurzív bejárásáhozbuildParentLookupMap()(line 944): Rekurzív fa bejáró, ami minden gyermek node-hoz hozzárendeli a szülő ID-twatch(categoryTree, ...)(line 956): A map újraépítése minden fa betöltéskortoggleCategory()(line 961): Gyermek kategória kiválasztásakor automatikusan hozzáadja a szülő kategória ID-t is aform.category_idstömbhöz
✅ Verifikáció
- Backend szintaxis -> OK:
admin_providersmodul sikeresen betöltve a konténerben - Sync Engine -> OK: 1282 OK, 0 hiba, adatbázis szinkronban
- Frontend -> OK: Import és
toggleCategorymódosítva,watch+parentLookupMapbeépítve
2026-07-07 - P0 BUGFIX: ServiceProfile Auto-Creation on PATCH for New Providers
🎯 Cél
P0 kritikus hibajavítás: PATCH /admin/providers/{id} 500-as hibát dobott, ha a provider-hez nem tartozott ServiceProfile rekord. A _sync_service_expertises() hívás profile.id-t használt, de profile None maradt, mert a ServiceProfile létrehozás feltételes volt (has_advanced_fields).
🔧 Változtatások
1. Import fix: admin_providers.py
ServiceExpertisehozzáadva az import-hoz (hiányzott,NameError-t okozott futásidőben)
2. ServiceProfile unconditional creation: admin_providers.py
- Előtte: ServiceProfile csak akkor jött létre, ha
has_advanced_fieldsTrue volt - Utána: Ha nincs ServiceProfile, MINDIG létrejön egy alapértelmezett profil (fingerprint:
auto-created-{provider_id}, status:active, trust_score: 30) - Debug logolás hozzáadva (
print+logger.info) a sync hívás előtt
✅ Verifikáció
- Sync Engine -> OK: 1282 OK, 0 hiba, adatbázis szinkronban
- E2E teszt -> ✅ PATCH 200 SUCCESS: provider #21 (Test Provider No Address - Fixed v4) sikeresen frissítve
category_ids=[12, 13, 14]-gyel - Docker log -> ✅
P0 BUGFIX: Auto-created ServiceProfile #24 for Live provider #21
2026-07-07 - P0 HOTFIX: Edit Form Chips & Reactivity Deep Clone
🎯 Cél
P0 kritikus hibajavítás: A provider edit űrlapon a kiválasztott kategória chipek nyers ID-kat jelenítettek meg (#760) név helyett, és a "Mentés" gomb disabled maradt kategória toggle után.
🔍 Root Cause Analysis
- Chip Display: A template már
{{ getCategoryName(id) }}-t használt (line 243), és auseCategoriescomposablegetCategoryName()függvénye helyesen visszaadja a nevet, ha acategoryMapbe van töltve. A#760fallback akkor jelenik meg, ha a kategóriák még nem töltődtek be — ez normális átmeneti állapot. - Reactivity Bug (Fő probléma): A
mapProviderToForm()függvényben aform.category_ids = data.category_ids || []direkt referenciát másolt, nem deep clone-t. Mivel aform.category_idsés aprovider.value.category_idsugyanarra a tömbre mutattak, ahasChangescomputed property sosem érzékelte a változást — a kategória toggle mindkét referenciát egyszerre módosította.
🔧 Változtatások
frontend_admin/pages/providers/[id]/edit.vue
mapProviderToForm()(lines 1137-1148): Minden tömb értékadás deep clone-ra javítva:form.supported_vehicle_classes = [...(data.supported_vehicle_classes || [])]form.category_ids = [...(data.category_ids || [])]form.specializations.brands = Array.isArray(spec.brands) ? [...spec.brands] : []form.specializations.propulsion = Array.isArray(spec.propulsion) ? [...spec.propulsion] : []
✅ Verifikáció
- Deep clone ellenőrzés:
form.category_idsésprovider.value.category_idsmost külön referenciák — a toggleCategory() hívás után ahasChangeshelyesentrue-t ad vissza. - Chip display: A template már
{{ getCategoryName(id) }}-t használ (line 243), auseCategories.getCategoryName()pedig acategoryMap-ből adja vissza a nevet, vagy#${id}fallback-et, ha még nem töltődött be.
2026-07-07 - P0 EPIC: i18n JSONB Database Migration (Phase 1 & 2)
🎯 Cél
Architect által jóváhagyott i18n JSONB migrációs terv (plans/logic_spec_i18n_jsonb_migration_phase1.md) első két fázisának implementálása: Séma bővítés (Phase 1) és Adatmigráció (Phase 2).
🔧 Változtatások
Phase 1: Schema Expansion (3 SQLAlchemy model)
-
backend/app/models/marketplace/service.py—ExpertiseTagosztály:name_i18n: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default=text("'{\"hu\": \"\"}'::jsonb"))description_i18n: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)- GIN index:
Index('idx_expertise_tags_name_i18n', 'name_i18n', postgresql_using='gin')
-
backend/app/models/vehicle/vehicle_definitions.py—BodyTypeDictionaryosztály:name_i18n: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default=text("'{\"hu\": \"\"}'::jsonb"))- GIN index:
Index('idx_dict_body_types_name_i18n', 'name_i18n', postgresql_using='gin')
-
backend/app/models/core_logic.py—ServiceCatalogosztály:name_i18n: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default=text("'{\"hu\": \"\"}'::jsonb"))description_i18n: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)- GIN index:
Index('idx_service_catalog_name_i18n', 'name_i18n', postgresql_using='gin')
Phase 2: Data Migration Script
backend/app/scripts/migrate_i18n_jsonb.py— Létrehozva:- Aszinkron migrációs script, amely a régi
name_hu/name_enoszlopokat JSONB struktúrába csomagolja - Kezeli a
name_translationsmeglévő JSONB mező merge-elését is json.dumps()használata az asyncpg JSONB kompatibilitás miatt- Idempotens:
ON CONFLICT DO NOTHING+ meglévő adatok felülírása
- Aszinkron migrációs script, amely a régi
📊 Migrációs Eredmények
| Tábla | Sorok | name_i18n | description_i18n |
|---|---|---|---|
marketplace.expertise_tags |
136 | ✅ {"hu": "...", "en": "..."} |
✅ {"hu": "..."} |
vehicle.dict_body_types |
16 | ✅ {"hu": "Szedán", ...} |
❌ N/A |
system.service_catalog |
3 | ✅ {"hu": "Adat Export"} |
✅ {"hu": "PDF és Excel..."} |
🗄️ GIN Indexek
| Index | Tábla | Típus |
|---|---|---|
idx_expertise_tags_name_i18n |
marketplace.expertise_tags |
GIN on name_i18n |
idx_dict_body_types_name_i18n |
vehicle.dict_body_types |
GIN on name_i18n |
idx_service_catalog_name_i18n |
system.service_catalog |
GIN on name_i18n |
✅ Verifikáció
- sync_engine futtatva → 1287 OK, 0 Fixed, 0 Shadow — schema fully in sync
- 5 új JSONB oszlop létrehozva a DB-ben (3×
name_i18n, 2×description_i18n) - 3 GIN index manuálisan létrehozva (sync_engine nem kezeli az indexeket)
- 155 sor sikeresen migrálva (136 expertise_tags + 16 dict_body_types + 3 service_catalog)
- Régi oszlopok (
name_hu,name_en,name,description) érintetlenek — backward compatibility biztosítva
2026-07-08 - P0 i18n Static Dictionary Cleanup (Admin Frontend)
🎯 Cél
Az admin frontend összes providers oldalának és a layout sidebar menüjének teljes i18n konverziója. Minden hardcoded magyar szöveg $t() / t() hívásokra cserélése, és a hiányzó fordítási kulcsok felvétele a hu.json és en.json fájlokba.
🔧 Változtatások
1. Layout (default.vue)
menuGroupsarray összes label-je i18n kulcsokra cserélve (menu.center,menu.dashboard,menu.providers, stb.)pageTitlecomputed property összes hardcoded string i18n kulcsokra cserélve- Key fix:
menu.ledger→menu.points_ledger,gamification.users.detail→gamification.users.user_detail_title
2. Providers oldalak
- index.vue - Teljes konverzió: template + script (
statusLabel()) - pending.vue - Teljes konverzió: template + script (toast üzenetek)
- rejected.vue - Teljes konverzió: template + script (toast üzenetek)
- [id]/index.vue - Teljes konverzió: template (minden szekció, modal, drawer) + script (tabs, DAYS, statusLabel, statusDescription, vehicleClassLabel, validationTypeLabel, historyActionLabel, toast üzenetek)
- [id]/edit.vue - Teljes konverzió: template (minden tab, szekció, drawer) + script (tabs, VEHICLE_CLASSES, DAYS, copyWeekdays, saveForm toasts)
3. Lokalizációs fájlok
- hu.json - ~200+ új providers kulcs (index, pending, rejected, detail, edit)
- en.json - ~200+ új providers kulcs angol fordítással
✅ Verifikáció
- Build →
npm run buildsikeres (sf_admin_frontend konténerben) ✅ - Nincs fordítási hiba - minden használt i18n kulcs létezik a locale fájlokban ✅
2026-07-08 - P0 Architectural Upgrade: Modular i18n Locales Migration
🎯 Cél
Migrate the Nuxt admin frontend from monolithic i18n locale files (single hu.json, en.json) to Nuxt i18n Modular Locales (multiple domain-specific JSON files per locale).
🔧 Változtatások
-
frontend_admin/nuxt.config.ts(31. sor): ChangedlangDirfrom'i18n/locales/'to'locales/'to avoid path doubling with Nuxt i18n module's automatici18nDirdetection. Updated hu and en locale configs to usefilesarray pattern instead of singlefile. -
frontend_admin/i18n/locales/hu/common.json(CREATED): Extracted allcommon.*keys from old monolithichu.json(loading, saving, error, retry, save, cancel, delete, edit, search, filter, status, permissions, etc.) -
frontend_admin/i18n/locales/en/common.json(CREATED): English equivalents of all common.* keys. -
frontend_admin/i18n/locales/hu/menu.json(CREATED): Extracted allmenu.*keys (center, dashboard, providers, garages, users, finance, packages, gamification, system, etc.) -
frontend_admin/i18n/locales/en/menu.json(CREATED): English equivalents of all menu.* keys. -
frontend_admin/i18n/locales/hu/providers.json(UPDATED): Expanded from ~84 lines to ~280 lines with all provider-related keys from old monolithic file. -
frontend_admin/i18n/locales/en/providers.json(UPDATED): English equivalents, expanded similarly. -
frontend_admin/locales/hu.json(DELETED): Old monolithic Hungarian locale file (1003 lines). -
frontend_admin/locales/en.json(DELETED): Old monolithic English locale file (1003 lines).
🔍 Megjegyzések
- A Nuxt i18n modul automatikusan detektálja az
i18n/könyvtárat a projekt gyökerében, és azt használjai18nDir-ként. Ezért alangDir-t relatívan kell megadni ehhez a könyvtárhoz ('locales/'), nem pedig abszolút módon ('i18n/locales/'). - A többi nyelv (de, fr, ro, cz, sk) továbbra is a régi
frontend_admin/locales/könyvtárban található monolit fájlokat használja. - A konténer újraindítás után sikeresen elindult, a Nitro szerver és a Vite klienst is felépítette.
- A
@intlify/message-compilerInvalid linked formathibát dobott azinfo@example.comemail placeholder értékben lévő@karakter miatt. A javítás:{'@'}Intlify szintaxis használata ahu/providers.jsonésen/providers.jsonfájlokban.
2026-07-08 - P0 CRITICAL i18n Audit & Repair (HU/EN) - Fix #6: Missing persons.json
🎯 Cél
A Nuxt i18n modul computeLocaleHashes függvénye ENOENT hibát dobott, mert a persons.json fájl nem létezett a frontend_admin/i18n/locales/en/ és hu/ könyvtárakban, pedig a nuxt.config.ts files tömbje hivatkozott rá.
🔧 Végrehajtott Javítások
frontend_admin/i18n/locales/en/persons.json(LÉTREHOZVA): Angol nyelvű személykezelő fordítások (3456 byte), 80+ kulcs apersons.*éspersons.details.*névtérben.frontend_admin/i18n/locales/hu/persons.json(LÉTREHOZVA): Magyar nyelvű személykezelő fordítások (3898 byte), azonos struktúrával.
📝 Technikai Részletek
- A
persons.*kulcsokat apages/persons/index.vue(lista nézet) éspages/persons/[id]/index.vue(részletek nézet) template-ek használják. - A hiányzó fájl a
nuxt.config.ts24 moduláris fájljának egyike volt, amelyet a Fix #3 során adtunk hozzá afilestömbhöz, de a fájl ténylegesen nem létezett a lemezen. - A
.nuxtcache törlése (sudo rm -rf frontend_admin/.nuxt/dev) és a konténer újraindítása után a build sikeresen lefutott i18n hibák nélkül.
✅ Verifikáció
- Konténer újraindítva →
docker compose restart sf_admin_frontend✅ - Build logok →
✔ Vite client built in 40ms,✔ Vite server built in 445ms,[nitro] ✔ Nuxt Nitro server built in 1526ms✅ - i18n hibák → Nincs
ERROR, nincsENOENT, nincs i18n compilation error ✅