Files
service-finder/.roo/history.md

71 KiB

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:

  • 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 - AdminLayout.vue:

  • 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 - AdminUsersView.vue:

  • 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

2026-06-17 - P0 UI Card Cleanup & Custom Tag 500 Fix

🎯 Cél

Három kritikus hiba javítása: (1) "Nincs kategória" fantom badge eltüntetése a provider kártyákról, (2) Telefon/Weboldal ikonok megjelenítése a kártyákon, (3) 500-as hiba javítása custom tag mentéskor.

🔧 Változtatások

1. FRONTEND - ServiceFinderView.vue:

  • "Nincs kategória" javítás: A v-if="provider.category" feltétel helyett v-if="provider.categories && provider.categories.length > 0" lett beállítva. Ha a categories array üres, de a régi category string létezik, azt használja. Csak ha egyik sincs, akkor jelenik meg a "Nincs kategória" felirat.
  • Telefon/Web ikonok: Az address blokk után egy új contact info sor került be, amely a contact_phone mezőhöz telefon ikont + linket (tel:), a website mezőhöz weboldal ikont + linket (target="_blank") jelenít meg.
  • TypeScript interface bővítés: A ProviderSearchResult interfész kibővítve a hiányzó mezőkkel: contact_phone, contact_email, website, tags, address_zip, address_street_name, address_street_type, address_house_number.

2. BACKEND - provider_service.py:

  • _slugify() függvény: Új segédfüggvény, amely biztonságos slug-ot generál magyar ékezetes karakterekből (á→a, é→e, í→i, ó→o, ö→o, ő→o, ú→u, ü→u, ű→u), majd nem-alfanumerikus karaktereket alulvonásra cserél.
  • _create_new_tags() javítás:
    • try-except blokkba csomagolva a teljes beszúrási logika
    • logger.error(..., exc_info=True) hibanaplózás hozzáadva
    • parent_id=None és path=None explicit beállítva
    • A kulcs generálás most a _slugify() függvényt használja
    • Egyetlen hibás címke nem szakítja meg a teljes folyamatot (continue)

Eredmény

  • "Nincs kategória" fantom badge megszűnt: a feltétel most a categories tömböt ellenőrzi
  • Telefon és weboldal linkek megjelennek a kártyákon (ha vannak)
  • Custom tag mentés 500 helyett try-except blokkban fut, hibanaplózással
  • Python szintaxis ellenőrzés: OK

2026-06-17 - P0 UI Overhaul: Plus Code, Universal Categories, Detail Modal

🎯 Cél

Három területet fed le: (1) Plus Code integráció a backendben és adatbázisban, (2) Univerzális kategóriák megjelenítése a ProviderEditModal és QuickAdd modalokban, (3) ProviderDetailModal gazdag újraírása kategória chipekkel, kontakt grid-del és navigációs gombbal.

🔧 Változtatások

1. BACKEND - Plus Code mező:

  • Organization model: plus_code: Mapped[Optional[str]] = mapped_column(String(20))
  • ProviderSearchResult: plus_code: Optional[str] = None
  • ProviderQuickAddIn: plus_code: Optional[str] = Field(None, max_length=20)
  • ProviderUpdateIn: plus_code: Optional[str] = Field(None, max_length=20)
  • provider_service.py: plus_code hozzáadva a search SELECT-hez, quick_add Organization creation-höz, és update null-safety checkhez
  • sync_engine sikeresen lefuttatva, a plus_code oszlop létrejött

2. FRONTEND - Univerzális kategóriák:

  • ProviderEditModal.vue: allLevel1Categories computed property, amely a checked Level 0 szülők Level 1 gyermekeit gyűjti össze deduplikálva
  • ProviderQuickAddModal.vue: Ugyanaz a allLevel1Categories logika
  • Plus Code input mező hozzáadva mindkét modal "Alapadatok" tabjához

3. FRONTEND - Detail Modal Rework:

  • ProviderDetailModal.vue: Teljes újraírás
    • Kategória chipek (szintenként eltérő színnel: Level0=lila, Level1=kék, Level2=teal, Level3=szürke)
    • Kontakt grid: Telefon (tel: link), Email (mailto: link), Weboldal (_blank, external icon)
    • Helyszín szekció: cím + plus_code + "Navigáció" gomb (Google Maps URL)
    • Hiányzó adatok esetén i18n fallback szövegek

4. i18n:

  • en.ts: plus_code és detail modal kulcsok
  • hu.ts: plus_code és detail modal magyar fordítások

Eredmény

  • Backend Python import ellenőrzés: OK (provider.py, provider_service.py, providers.py endpoints)
  • Frontend Vite build: OK (87 modules, 6.07s)
  • Adatbázis sync_engine: OK (plus_code oszlop létrejött)

2026-06-17 - P0 Triage Fix: Data Persistence & UNION ALL Column Mismatch

🎯 Cél

Két kritikus hiba javítása a Provider Search/Update folyamatban:

  1. Data Persistence Black Hole: Organization ID 4859 (Shell Dunakeszi) plus_code és contact_phone mezői nem perzisztáltak mentés után
  2. UNION ALL 500 error: CompoundSelect must have identical numbers of columns hiba a search végponton

🔧 Változtatások

1. BACKEND - provider_service.py:

  • plus_code hozzáadva a ProviderSearchResult konstruktorhoz (getattr(row, "plus_code", None)) - a mező SELECT-ben volt, de a Python objektumba nem került át
  • plus_code oszlop (literal(None).label("plus_code")) hozzáadva a UNION ALL második (staging_stmt) és harmadik (crowd_stmt) ágához, hogy a 15 oszlopszám konzisztens legyen
  • P0 CRITICAL BUGFIX: Ha nincs ServiceProfile rekord az Organization-höz, a update_provider most létrehozza azt (fingerprint + location + contact adatokkal), ahelyett hogy csak logolná a hiányt és eldobná a contact/category adatokat
  • ServiceStatus import hozzáadva a provider_service.py-hoz

2. FRONTEND - ProviderEditModal.vue:

  • allLevel1Categories szétbontva vehicleLevel1Categories-re (Level 0 checkboxoktól függő) és universalLevel1Categories-re (mindig látható, categoryTree.value.filter(c => c.level === 1))
  • Új UI blokk "Iparágak és Univerzális Szolgáltatások" címmel, amber színű dashed border-rel

Verifikáció

  • plus_code=8FVX+3W, contact_phone=+36 20 123 4567, contact_email=shell.dunakeszi@example.com sikeresen mentve és visszaolvasva ID 4859-hez
  • API /api/v1/providers/search hibátlanul fut (nincs 500-as UNION ALL hiba)
  • Adatbázis szinten ellenőrizve: fleet.organizations.plus_code és marketplace.service_profiles.contact_phone/contact_email helyesen perzisztálva

2026-06-17 - P0 Ownership & Access Control Fix: OrganizationMember + owner_id

🎯 Cél

Két P0 kritikus hiba javítása a Provider Quick-Add és Update folyamatban:

  1. Ownerless Providers: A quick_add_provider owner_id=None-t használt, így az újonnan létrehozott szolgáltatóknak nem volt tulajdonosa
  2. Missing OrganizationMember: A 6-lépéses folyamat 5. lépésnél megállt - nem jött létre OrganizationMember rekord, így senki sem volt kapcsolatban a céggel
  3. No Access Control: A update_provider végpont bárki által hívható volt - nem ellenőrizte, hogy a hívó user jogosult-e szerkeszteni az adott szervezetet

🔧 Változtatások

1. BACKEND - provider_service.py:

  • owner_id=Noneowner_id=user_id (531. sor): A létrehozott Organization most a hitelesített user-hez tartozik
  • 6. 👤 ORGANIZATION MEMBER LÉTREHOZÁSA (585-600. sor): Teljesen új blokk a gamifikáció előtt:
    member = OrganizationMember(
        organization_id=org.id,
        user_id=user_id,
        role=OrgUserRole.OWNER,
        is_permanent=True,
        is_verified=True,
    )
    db.add(member)
    await db.flush()
    
  • 🔐 ACCESS CONTROL ELLENŐRZÉS (841-863. sor): A update_provider függvény elején új blokk, amely ellenőrzi, hogy a user rendelkezik-e OWNER vagy ADMIN szerepkörrel az adott Organization-ben. Ha nem, PermissionError-t dob.

2. BACKEND - providers.py:

  • PermissionErrorHTTPException(403) kezelés hozzáadva a update_service_provider végponthoz

Verifikáció

  • owner_id=2 (admin user) sikeresen beállítva a létrehozott Organization-ben
  • OrganizationMember(user_id=2, role=OWNER) rekord sikeresen létrejött
  • Admin user sikeresen frissítheti a provider adatokat (200 OK)
  • Érvénytelen token esetén 401 Unauthorized
  • Docker konténer újraépítve: docker compose up -d --build sf_api
  • Tesztadatok (org 59, 60, 61) tisztítva

2026-06-17 - P0 Critical Bugfix: Quick-Add Provider 422 category_id null

🎯 Cél

A POST /api/v1/providers/quick-add endpoint 422-es hibát dobott, amikor a frontend category_id: null-t küldött. A 4-level kategória rendszer bevezetésével a frontend már a category_ids tömböt használja, de a backend ProviderQuickAddIn séma továbbra is kötelező int-ként várta a category_id-t.

🔧 Változtatások

1. BACKEND - provider.py:

  • ProviderQuickAddIn.category_id típusa intOptional[int] = None (opcionális)
  • Új mezők: category_ids: Optional[List[int]] és new_tags: Optional[List[str]] a 4-level kategória rendszer támogatásához

2. BACKEND - provider_service.py:

  • quick_add_provider() most már kezeli a category_id=None esetet (kihagyja az elsődleges kategória lekérdezést)
  • A category_ids és new_tags mezők feldolgozása: ServiceExpertise kapcsolatok létrehozása a 4-level kategória rendszerből
  • Deduplikáció: az összes kategória ID egyesítve, duplikátumok eltávolítva

3. FRONTEND - ProviderQuickAddModal.vue:

  • phonecontact_phone és emailcontact_email mezőnevek javítva (illeszkedés a backend Pydantic sémához)

Teszt eredmény

  • A pontosan reprodukált hiba (Bokebo Kft., category_id=null, category_ids=[149]) HTTP 201 választ ad
  • Adatbázisban: Organization létrejött, ServiceProfile létrejött, ServiceExpertise kapcsolat a 149-es kategóriával létrejött
  • Gamification: 500 pont jóváírva

2026-06-17: Bug investigation - Quick-add provider cégek megjelennek a Cégeim menüben

Kártya: #266 Szerepkör: Architect Állapot: Tervezés kész

Felfedezett hibák

  1. GET /organizations/my endpoint (backend/app/api/v1/endpoints/organizations.py:183-186): Nincs org_type szűrés, minden szervezetet visszaad, ahol a user tag az OrganizationMember táblában.

  2. HeaderCompanySwitcher.vue:142-146: A companyOrganizations computed property csak az individual típust szűri ki, a service_provider típusú szervezeteket nem.

  3. quick_add_provider() (backend/app/services/provider_service.py:638-646): Létrehoz OrganizationMember-et role=OWNER-rel, ami miatt a GET /my visszaadja a szolgáltatót.

Javítási terv

  1. organizations.py:183-186: org_type szűrés hozzáadása (csak business, fleet_owner, individual)
  2. organizations.py:193-206: owner_id mező hozzáadása a response-hoz
  3. HeaderCompanySwitcher.vue:142-146: service_provider és service típusok kiszűrése
  4. Adatbázis audit: hiányzó owner_id-k ellenőrzése a régi service_providereknél

Kapcsolódó fájlok

  • plans/logic_spec_provider_cegeim_bugfix.md - Részletes logic_spec

2026-06-17: Bugfix implementáció - Quick-add provider cégek a Cégeim menüben (#266)

Szerepkör: Code (Fast Coder) Állapot: Implementálva és tesztelve

Végrehajtott módosítások

  1. backend/app/api/v1/endpoints/organizations.py:183-186GET /my endpoint:

    • Hozzáadva org_type szűrés: csak business, fleet_owner, individual típusú szervezetek visszaadása
    • owner_id mező hozzáadva a response dict-hez
  2. frontend/src/components/header/HeaderCompanySwitcher.vue:142-146companyOrganizations computed:

    • service_provider és service típusok kiszűrése (biztonsági réteg)

Verifikáció

  • sync_engine.py — 1065 elem OK, 0 hiba
  • Backend Python import check — OK
  • Frontend npm run build — 5.97s, 0 hiba

🔧 Javítás (v2) — 2026-06-17

Probléma: A whitelist (in_([business, fleet_owner, individual])) túl szigorú volt, kizárta a valódi cégeket (pl. club típus).

Megoldás:

  1. backend/app/api/v1/endpoints/organizations.py:193in_([...])notin_([OrgType.service_provider, OrgType.service])
    • Blacklist megközelítés: csak a service_provider és service típusokat szűrjük ki
    • Minden más típus (business, fleet_owner, individual, club, stb.) megjelenik

Verifikáció

  • sync_engine.py — 1065 elem OK, 0 hiba
  • Backend Python import check — OK
  • notin_ szintaxis ellenőrizve — OK

2026-06-17 - Garázsválasztó Bugfix: service_provider org-ok nem látszottak OWNER-nek sem

🎯 Cél

A saját tulajdonú cégek (service_provider org_type) megjelenítése a garázsválasztó gombban, ha a felhasználó OWNER joggal rendelkezik.

🔧 Változtatások

1. BACKEND - organizations.py:

  • A GET /my végpont most már select(Organization, OrganizationMember.role)-t használ, hogy a felhasználó szerepkörét is lekérdezze
  • A service_provider/service típusú org-ok utólagos szűrése: csak akkor jelennek meg, ha a user role = OWNER
  • A válaszban új user_role mező került visszaadásra

2. FRONTEND - HeaderCompanySwitcher.vue:

  • A companyOrganizations computed property most már a user_role mezőt is figyelembe veszi
  • service_provider/service típus csak user_role === 'OWNER' esetén jelenik meg

3. FRONTEND - organization.ts:

  • Új user_role?: string mező az OrganizationItem interfészben

Verifikáció

  • Backend syntax check — OK
  • API teszt: 4 org visszatér (1 individual + 3 service_provider OWNER)
  • user_role mező jelen van a válaszban (ADMIN, OWNER)
  • Nincs regresszió: non-OWNER role esetén a service_provider org-ok ki vannak szűrve

2026-06-17: Root cause fix - quick_add_provider owner_id és role javítás (#266)

🔍 Felismert probléma

A quick_add_provider függvény a crowdsourcingból felvett szolgáltatóknál owner_id=user_id-t állított be és OWNER szerepkörű tagot hozott létre. Ez hibás, mert:

  • A közösségi jelöléssel felvett cégeknek NINCS tulajdonosa (owner_id=NULL)
  • Aki felvesz egy céget a Service Finder-be, az NEM lesz a cég tulajdonosa
  • Az OWNER szerepkör miatt ezek a cégek megjelentek a "Cégeim" garázsválasztóban

🔧 Végrehajtott módosítások

1. BACKEND - provider_service.py:

  • owner_id=user_idowner_id=None — crowdsourcingból felvett szolgáltatónak nincs tulajdonosa

2. BACKEND - provider_service.py:

  • role=OrgUserRole.OWNERrole=OrgUserRole.ADMIN — a felhasználó ADMIN jogot kap (szerkesztheti), de nem tulajdonos

Verifikáció

  • Sync engine: séma konzisztens (1065 elem OK)
  • API teszt: GET /api/v1/organizations/my → 1 org (Test Company, individual)
  • service_provider org-ok (57, 58, 62) NEM jelennek meg a garázsválasztóban
  • A meglévő adatok (owner_id=2, role=OWNER) a régi kóddal jöttek létre, az új kód már helyesen hozza létre az új provider-eket

2026-06-17 - P1 Garage Selector Implementation & OrganizationMember Audit

🎯 Cél

Dashboard "Garage Selector" (cégválasztó) pontosítása, hogy a bejelentkezett felhasználó azon Organization rekordokat lássa, ahol: a) Technikai tulajdonos (owner_id) VAGY b) Aktív tag (OrganizationMember)

Emellett az OrganizationMember tábla hiányzó oszlopainak pótlása és audit jelentés készítése.

🔧 Változtatások

1. BACKEND - organizations.py:

  • get_my_organizations query javítva: INNER JOINLEFT JOIN + OR feltétel (owner_id VAGY OrganizationMember.user_id)
  • DISTINCT hozzáadva a duplikációk elkerülésére
  • user_role mező visszaadása a frontend számára
  • _get_user_role() segédfüggvény implementálva

2. BACKEND - organization.py:

  • OrganizationMember modellhez hozzáadva: status (String(20)), created_at, updated_at oszlopok
  • Ezek az oszlopok hiányoztak az adatbázisból, de a kód már hivatkozott rájuk

3. ADATBÁZIS - sync_engine:

  • 3 hiányzó oszlop sikeresen hozzáadva a fleet.organization_members táblához

4. DOKUMENTÁCIÓ - organization_member_audit_report_2026-06-17.md:

  • Részletes audit jelentés az OrganizationMember tábláról
  • Hiányzó API végpontok azonosítása (tagkezelés, meghívókezelés)
  • Javasolt javítási sorrend

Eredmény

  • GET /api/v1/organizations/my helyesen adja vissza a tulajdonosi és tagsági viszonyokat
  • user_role mező elérhető a frontend számára
  • Adatbázis séma konzisztens a kóddal
  • Audit jelentés elkészítve a docs mappában

2026-06-17 - P0 Garage Selector Fix & Team Management API Implementation

🎯 Cél

A P0 Garage Selector hiba végleges javítása (a GET /organizations/my 500-as hibát dobott JSON oszlopok miatt) és a hiányzó Team Management API végpontok implementálása.

🔧 Változtatások

1. BACKEND - organization.py:

  • OrganizationMember modellhez hozzáadva: invited_email (String(255)) és expires_at (DateTime(timezone=True)) oszlopok
  • OrgUserRole enum szinkronizálva az adatbázisban lévő értékekkel: OWNER, ADMIN, MANAGER, MEMBER, AGENT
  • default=OrgUserRole.DRIVERdefault=OrgUserRole.MEMBER

2. BACKEND - organizations.py:

  • GET /organizations/my SQL javítás: A DISTINCT a teljes Organization modellen JSON oszlopok miatt hibát dobott (could not identify an equality operator for type json)
  • Megoldás: Subquery approach: először csak az ID-kat szedjük DISTINCT-tel, majd a második query-ben selectinload(Organization.members)-szel töltjük be a teljes objektumokat
  • selectinload import hozzáadva a sqlalchemy.orm-ból

3. BACKEND - organizations.py:

  • GET /{org_id}/members — Tagok listázása (user email + role + status)
  • POST /{org_id}/invitations — Meghívó küldése (invited_email, expires_at=+7 nap)
  • PATCH /{org_id}/members/{member_id} — Szerepkör módosítása (utolsó OWNER védelme)
  • DELETE /{org_id}/members/{member_id} — Tag eltávolítása / meghívó visszavonása
  • _check_org_admin_access() — Segédfüggvény: csak OWNER/ADMIN férhet hozzá
  • Pydantic modellek: MemberResponse, MemberRoleUpdate, InvitationCreate
  • Régi duplikált POST /invitations és POST /invitations/{token}/accept végpontok eltávolítva

4. ADATBÁZIS - sync_engine:

  • 2 új oszlop (invited_email, expires_at) sikeresen hozzáadva a fleet.organization_members táblához

Teszt eredmény

  • GET /api/v1/organizations/my200 OK (1 org: Test Company, user_role: ADMIN)
  • GET /api/v1/organizations/1/members200 OK (1 member: admin@profibot.hu)
  • POST /api/v1/organizations/1/invitations201 Created (invited_email, expires_at, role=MANAGER)
  • PATCH /api/v1/organizations/1/members/25200 OK (role MANAGER → ADMIN)
  • DELETE /api/v1/organizations/1/members/25200 OK (invitation revoked)

2026-06-17 - Team Management Modul Refaktorálás (Dynamic RBAC, Settings, i18n)

🎯 Cél

A Team Management modul teljes refaktorálása: hardcoded OrgUserRole Enum kiváltása adatbázis-vezérelt OrgRole modellel, hardcoded 7 napos meghívási lejárat kiváltása dinamikus Organization.settings JSONB mezővel, és az összes hardcoded magyar szöveg kiváltása i18n kulcsokkal a TranslationService.get_text() segítségével.

🔧 Változtatások

1. MODELL - organization.py:

  • OrgRole új modell létrehozva a fleet.org_roles táblában (id, name_key, display_name, description, is_system, priority, is_active, created_at)
  • Organization.settings JSONB oszlop hozzáadva alapértelmezett értékekkel: {"invite_expiry_days": 7, "max_members": 50, "allow_public_join": false}
  • OrganizationMember.role típusa megváltoztatva PG_ENUM(OrgUserRole)-ról String(50)-re, alapértelmezett "MEMBER"

2. MODELL EXPORT - __init__.py:

  • OrgRole hozzáadva az importokhoz és az __all__ listához

3. API VÉGPONTOK - organizations.py:

  • _() helper függvény hozzáadva a TranslationService.get_text() rövidítésére
  • _get_org_settings() helper hozzáadva a dinamikus beállítások olvasásához
  • Összes hardcoded magyar szöveg kiváltva i18n kulcsokkal (pl. ORGANIZATION.ERROR.INVALID_TAX_FORMAT, ORGANIZATION.SUCCESS.ONBOARDED, ORGANIZATION.CLAIM.OTP_SENT)
  • Összes OrgUserRole.OWNER / OrgUserRole.ADMIN Enum referencia kiváltva string literálokkal ("OWNER", "ADMIN")
  • member.role.valuestr(member.role)
  • timedelta(days=7)timedelta(days=invite_expiry_days) a _get_org_settings()-ből

4. LOCALE FÁJLOK - hu.json, en.json:

  • ORGANIZATION névtér hozzáadva mindkét nyelvhez a következő alkulcsokkal: ERROR.* (11 hibaüzenet), SUCCESS.* (5 sikerüzenet), INVITATION.* (3 meghívó üzenet), JOIN_REQUEST.* (1 csatlakozási üzenet), CLAIM.* (2 átvételi üzenet)

5. ADATBÁZIS SZINKRONIZÁCIÓ:

  • sync_engine.py sikeresen lefuttatva: fleet.org_roles tábla létrehozva, fleet.organizations.settings oszlop hozzáadva

Eredmény

  • Adatbázis séma frissítve (2 új elem: org_roles tábla + settings oszlop)
  • API végpontok mind i18n kulcsokat használnak a TranslationService.get_text()-en keresztül
  • Nincs több hardcoded OrgUserRole Enum a Team Management modulban
  • Nincs több hardcoded 7 napos lejárat
  • Nincs több hardcoded magyar szöveg az API válaszokban
  • Business onboarding teszt: PASS

2026-06-17 - Seed: Master Taxonomy i18n (JSONB kétnyelvű taxonómia)

🎯 Cél

4-szintű, kétnyelvű (HU/EN) autóipari taxonómia seed scriptjének elkészítése JSONB name_translations mezővel, 8 járműtípusra kiterjedően.

🔧 Változtatások

1. MODELL - service.py:

  • name_translations JSONB oszlop hozzáadva az ExpertiseTag modellhez ({"hu": "...", "en": "..."} formátumban)

2. SEED SCRIPT - seed_master_taxonomy_i18n.py:

  • Új seed script létrehozva backend/scripts/seed_master_taxonomy_i18n.py
  • 4-szintű hierarchia: Level 0 (Járműtípus), Level 1 (Iparág), Level 2 (Szakma), Level 3 (Specifikus címke)
  • 8 járműtípus: Személygépjármű, Motorkerékpár, Kishaszonjármű, Kamion, Munkagép, Pótkocsi, Busz, Lakóautó
  • Idempotens: törli a régi is_official=True rekordokat, meghagyja a felhasználóiakat
  • Adjacency List (parent_id) + Materialized Path (path) struktúra

Eredmény

  • 161 hivatalos kategória beszúrva (8 L0, 23 L1, 46 L2, 84 L3)
  • 161 JSONB name_translations kitöltve
  • 12 felhasználói címke megőrizve
  • Adatbázis verifikáció: PASS

2026-06-17 - P0 UI Regression Fix: Universal Categories Restoration

🎯 Cél

Restore the "Universal Categories" (e.g., Benzinkút / Gas Station) independent checkbox block in Provider modals, which was accidentally removed during RBAC/i18n refactoring.

🔧 Változtatások

  • frontend/src/components/provider/ProviderQuickAddModal.vue:
    • Replaced allLevel1Categories computed with two separate computeds: vehicleLevel1Categories (children of checked Level 0) and universalLevel1Categories (root Level 1 categories, independent of vehicle type).
    • Added the amber-colored universal categories UI block with dashed border, globe icon, and independent checkbox styling.
    • Universal categories are now always visible regardless of vehicle type selection.
  • frontend/src/components/provider/ProviderEditModal.vue: Already had correct structure — verified and left unchanged.

Verifikáció

  • Vite build: PASS (exit code 0)
  • TypeScript check: PASS (no new errors)
  • Universal categories IDs are correctly included in selectedCategoryIds array → sent in payload as category_ids.

2026-06-17 - P0 UI Polish: i18n Fix & Master Taxonomy Autocomplete

🎯 Cél

Két P0 szintű probléma javítása: (1) "Univerzális Szolgáltatások" blokkban raw i18n kulcsok (universal_title, universal_hint) megjelenítése a fordítás helyett, (2) "Benzinkút" és "Étterem" hiányzó Level 2 kategóriák felvétele a Master Taxonomy-ba az autocomplete kereshetőséghez.

🔧 Változtatások

1. FRONTEND i18n - hu.ts:

  • categories.universal_title kulcs hozzáadva: "Univerzális Szolgáltatások"
  • categories.universal_hint kulcs hozzáadva: "Járműtípustól független infrastruktúra"

2. FRONTEND i18n - en.ts:

  • categories.universal_title kulcs hozzáadva: "Universal Services"
  • categories.universal_hint kulcs hozzáadva: "Vehicle-independent infrastructure"

3. BACKEND Taxonomy - seed_expertise_taxonomy.py:

  • UNIVERSAL_CATEGORIES bővítve Level 2 gyermek kategóriákkal:
    • uzemanyag_toltoallomasbenzinkut (Benzinkút), elektromos_toltoallomas (EV Charging Station)
    • etel_italetterem (Étterem), gyorsetterem_bufe (Gyorsétterem / Büfé)
    • szallashotel (Hotel), motel_panzio (Motel / Panzió)
    • orzes_parkolasuni_orzott_parkolo (Őrzött Parkoló), uni_garazs (Garázs)
  • Univerzális kategóriák beszúró ciklusa kiegészítve rekurzív gyermek feldolgozással (_flatten_and_insert hívás)

Verifikáció

  • Seed script: PASS (134 rekord, ebből 12 univerzális: 4 Level 1 + 8 Level 2)
  • Autocomplete API q=Benzinkút: PASS (1 találat: Benzinkút, level=2)
  • Autocomplete API q=Étterem: PASS (3 találat: Étterem, Gyorsétterem / Büfé, Étterem / Büfé)

2026-06-18 - P0 Critical: Financial Math Reversal (Gross-First Accounting)

🎯 Cél

A Phase 2 Financial Architecture felülvizsgálata során a Vezető Tervező (Architect) jelezte, hogy a pénzügyi logika fordítva van: az EU-s számvitelben a BRUTTÓ (Gross) az elsődleges forrásigazság (Source of Truth), nem a Nettó. A teljes matematika megfordítása: _calculate_gross_from_net()_calculate_net_from_gross(), amount_gross kötelező, amount_net opcionális.

🔧 Változtatások

1. SCHEMA - asset_cost.py:

  • amount_gross áthelyezve kötelező mezővé (Field(..., description="Bruttó összeg — KÖTELEZŐ, elsődleges forrás"))
  • amount_net opcionális (Optional[Decimal] = Field(None, ...))
  • @model_validator(mode='after') hozzáadva: ha amount_net=None és vat_rate=None, akkor amount_net = amount_gross és vat_rate = 0

2. SCHEMA - asset_event.py:

  • model_validate frissítve: cost_amount elsődlegesen amount_gross-ból, fallback amount_net-ből

3. SCHEMA - asset.py:

  • model_validate frissítve: cost_amount elsődlegesen amount_gross-ból, fallback amount_net-ből

4. ENDPOINT - expenses.py:

  • _calculate_gross_from_net()_calculate_net_from_gross(amount_gross, vat_rate) átnevezve
  • Képlet megfordítva: net = gross / (1 + vat_rate/100)
  • VAT handling logika átírva: ha vat_rate adott és amount_net nincs, net kiszámítása gross-ból
  • Ha amount_net és vat_rate is adott, vat_rate felülírva a back-számított értékkel
  • Ha egyik sincs, amount_net = amount_gross, vat_rate = 0

5. ENDPOINT - assets.py:

  • create_maintenance_record: cost mező gross-ként kezelve, amount_gross és amount_net is a cost értékkel töltve
  • create_asset_event: cost_amount gross-ként kezelve
  • list_asset_costs: response-ban amount_gross az amount_net előtt

Verifikáció

  • Python szintaxis ellenőrzés: 5/5 fájl OK
  • Sync engine: 1087/1087 OK, 0 fix, 0 extra
  • Unit math tesztek: 5/5 passed (Gross=12700→Net=10000 27% VAT, Gross=10500→Net=10000 5% VAT, stb.)
  • E2E API tesztek: 3/3 passed (Gross only, Gross+27%, Gross+5%)
  • A teljes Gross-first matematika helyesen működik: a bruttó összeg a Source of Truth, a nettó és ÁFA visszaszámolva

2026-06-18 - P0 Frontend Cost Payload Audit & Fix (Gross-First Compliance)

🎯 Cél

A backend már szigorúan Gross-first rendszer a pénzügyi adatokra, de a frontend űrlapok még amount_net-et küldtek a payloadban, ami amount_gross: null-t eredményezett az adatbázisban. Teljes audit és javítás.

🔧 Változtatások

1. FRONTEND - cost.ts:

  • AddExpensePayload interface: amount_netamount_gross (elsődleges mező)
  • ExpenseResponse interface: amount_netamount_gross
  • addExpense() body builder: amount_net: payload.amount_netamount_gross: payload.amount_gross

2. FRONTEND - ComplexExpenseModal.vue:

  • Payload amount_net: form.amountamount_gross: form.amount

3. FRONTEND - SimpleFuelModal.vue:

  • Payload amount_net: form.amountamount_gross: form.amount

4. BACKEND - asset_cost.py:

  • @model_validator(mode='before') safety net hozzáadva: ha a bejövő JSON amount_net-et tartalmaz, de amount_gross hiányzik, automatikusan átmásolja az amount_net értékét az amount_gross-ba. Ez véd a régebbi cache-elt böngészők ellen.

Verifikáció

  • Vite build: Sikeres (216 module transformed, 0 error)
  • Backend schema import: Sikeres
  • Safety net tesztek: 3/3 passed
    • amount_net only → amount_gross auto-copy:
    • Both fields present → no interference:
    • amount_gross only → no change:

🔍 Utólagos Adatjavítás (Data Fix)

A felhasználó jelentése alapján az UOK795 (Honda CB1000R) járműnél az utolsó 2 rögzített költség 0 Ft-ot mutatott. Vizsgálat után kiderült:

Gyökér ok: A P0 audit előtt rögzített rekordoknál a frontend amount_net-et küldött, de a backend amount_gross-t 0-ra állította (a validate_gross_first mode='after' validátor NoneDecimal("0")-ra állította a hiányzó gross mezőt).

Érintett rekordok (országosan 2 db):

  1. 3468e81a - UOK795 (Honda CB1000R) - 12 000 Ft tankolás → amount_gross javítva 0→12000
  2. ea0395ea - QWE123 (APRILIA af1) - 4 000 Ft → amount_gross javítva 0→4000

Javítás: UPDATE vehicle.asset_costs SET amount_gross = amount_net, vat_rate = 0.00 WHERE amount_gross=0 AND amount_net>0

2026-06-18 - Jogosultságkezelés Audit (Permission System Audit)

🎯 Cél

Teljes körű audit a jogosultsági szintek kezeléséről, kiterjedve az adatbázis táblákra, a backend kódra és a frontend rétegre.

🔍 Vizsgált Területek

  1. Adatbázis (6 tábla): identity.users, fleet.org_roles, fleet.organization_members, system.system_parameters, system.pending_actions, system.subscription_tiers
  2. Backend: UserRole enum, DEFAULT_RANK_MAP, RBAC osztály, get_current_admin, scoped RBAC, Dual Control (SecurityService), JWT token
  3. Frontend: authStore.ts (UserRole típus), Router Guard

🔴 Feltárt Kritikus Problémák

  • P1: fleet.org_roles tábla ÜRES - dinamikus szerepkör rendszer nem seedelt
  • P2: DEFAULT_RANK_MAP inkonzisztens a UserRole enum-mal (6 rang soha nem kerül kiosztásra)
  • P3: Enum duplikáció az adatbázisban (kis/nagybetű variánsok)
  • P4: Frontend/backend UserRole eltérés (5 vs 10 role)
  • P5: parameter_scope enum 3 különböző sémában definiálva

📄 Dokumentáció


2026-06-18 - RBAC Phase 1: Foundation & Seeding (P0 Critical)

🎯 Cél

RBAC Phase 1 migráció Fine-Grained, Capability-Driven architektúrára. System-level (UserRole) és Organization-level (OrgUserRole) szerepkörök szétválasztása, SYSTEM_CAPABILITIES_MATRIX létrehozása, fleet.org_roles tábla seedelése.

🔧 Változtatások

1. UserRole enum tisztítás:

  • 10 régi érték helyett 6 system-level role: SUPERADMIN, ADMIN, MODERATOR, SALES_REP, SERVICE_MGR, USER
  • Eltávolítva: region_admin, country_admin, sales_agent, service_owner, fleet_manager, driver
  • Hozzáadva: SALES_REP, SERVICE_MGR
  • Értékek UPPERCASE-re változtatva

2. SYSTEM_CAPABILITIES_MATRIX létrehozása:

  • Új fájl: backend/app/core/capabilities.py
  • Capability osztály konstansokkal (~26 capability key)
  • SYSTEM_CAPABILITIES_MATRIX dict minden role-hoz boolean capability mátrix
  • Segédfüggvények: get_capabilities_for_role(), role_has_capability()

3. OrgUserRole enum frissítés:

  • Új értékek: OWNER, ADMIN, ACCOUNTANT, DRIVER, VIEWER
  • Eltávolítva: MANAGER, MEMBER, AGENT

4. DEFAULT_RANK_MAP tisztítás:

  • Csak az új 6 system-level role + GUEST maradt
  • SERVICE_MGR(40), SALES_REP(30) hozzáadva

5. Függő kódok frissítése:

  • deps.py - check_resource_access() és get_current_admin() frissítve
  • rbac.py - UserRole.SUPERADMIN referencia javítva
  • billing_engine.py - RBAC_DISCOUNTS tisztítva

6. PostgreSQL enum migráció:

  • migrate_rbac_phase1_enums.py - CREATE TYPE → ALTER COLUMN → DROP TYPE minta
  • Régi értékek mapelése: superadmin→SUPERADMIN, admin→ADMIN, moderator→MODERATOR, sales_agent→SALES_REP, user/service_owner/fleet_manager/driver→USER
  • Sikeres: identity.userrole enum 6 értékkel, meglévő user role-ok helyesen átkonvertálva

7. seed_org_roles.py - fleet.org_roles seedelés:

  • 5 org role: OWNER(100), ADMIN(80), ACCOUNTANT(60), DRIVER(40), VIEWER(20)
  • Minden role JSON permissions objektummal
  • Sikeres: 5 rekord beszúrva

Verifikáció

  • Schema sync: 1087 items OK, 0 fixed, 0 shadow data
  • identity.userrole enum: SUPERADMIN, ADMIN, MODERATOR, SALES_REP, SERVICE_MGR, USER
  • fleet.org_roles: 5 rekord (OWNER, ADMIN, ACCOUNTANT, DRIVER, VIEWER)
  • Meglévő user role-ok: USER, SUPERADMIN, ADMIN - helyesen konvertálva

2026-06-18 - P0 EMERGENCY: Superadmin User ID 1 Recreated from Scratch

🎯 Cél

A hard-deletelt User ID 1 rekord fizikai újraélesztése: SUPERADMIN joggal, a meglévő Person ID 1-hez kapcsolva, és a Profibot Test Fleet (Organization ID 15) garázshoz OWNER szerepkörrel hozzárendelve.

🔧 Változtatások

1. Jelszó hash generálás:

  • security.py: get_password_hash('Superadmin123!') használatával bcrypt hash generálva
  • Hash: $2b$12$ozXD07/qiFl4eKg.RbadDeGcnMukY3tOpBPEgOza7VsqcbuUW7iq2
  • Verifikáció: verify_password('Superadmin123!', hash)True

2. Email konfliktus feloldása:

  • User ID 29 (korábbi superadmin@profibot.hu) átnevezve → superadmin_old@profibot.hu

3. User ID 1 létrehozása (identity.users):

  • INSERT INTO identity.users (id=1, email='superadmin@profibot.hu', role='SUPERADMIN', person_id=1, ...)
  • Kapcsolat a Person ID 1-hez (Super Admin személy)

4. Garázs hozzárendelés (fleet.organization_members):

  • INSERT INTO fleet.organization_members (organization_id=15, user_id=1, person_id=1, role='OWNER', ...)
  • Organization: "Profibot Test Fleet" (ID 15)

Verifikáció

  • identity.users WHERE id=1: email=superadmin@profibot.hu, role=SUPERADMIN, person_id=1, is_active=true
  • fleet.organization_members WHERE user_id=1: org_id=15 (Profibot Test Fleet), role=OWNER, status=active
  • identity.persons WHERE id=1: last_name=Admin, first_name=Super (meglévő személy)
  • Jelszó hash verifikáció: True
  • Belépési adatok: superadmin@profibot.hu / Superadmin123!

2026-06-18: P0 CRITICAL DEBUG - 500 Internal Server Error on POST /api/v1/auth/login (UserRole UPPERCASE migration fix)

🎯 Cél

A Phase 1 RBAC Enum UPPERCASE migráció után a /api/v1/auth/login endpoint 500-as hibát dobott LookupError: 'ADMIN' is not among the defined enum values üzenettel. A régi konténer cache-elt SQLAlchemy metaadatokat használt, és a billing_engine.py-ben lévő UserRole.user (kisbetűs) hivatkozás miatt az új konténer sem indult el.

🔧 Változtatások (25 javítás 14 fájlban)

Kritikus javítások (startup-blokkoló):

Verifikáció

  • POST /api/v1/auth/login with admin@profibot.hu / Admin123!200 OK
  • JWT token payload: {"sub": "2", "role": "ADMIN", "rank": 90, "scope_level": "system", "scope_id": "2"}
  • Konténer indítás: sikeres (nem omlik össze AttributeError-rel)
  • 3 db seed script nem javítható (EACCES jogosultsági hiba) — nem kritikus

2026-06-18 - RBAC Phase 2: Capability Dependencies (Kapuőrök)

🎯 Cél

P0 kritikus feladat: JSONB-alapú capability dependency-k bevezetése a hardcoded szerepkör-ellenőrzések helyett. Két új FastAPI dependency: RequireSystemCapability (system-level) és RequireOrgCapability (org-level).

🔧 Változtatások

1. MODELL - organization.py:

  • permissions JSONB oszlop hozzáadva az OrgRole modellhez (server_default='{}'::jsonb)

2. DEPENDENCY - deps.py:

3. EXPENSES - expenses.py:

  • AUTO_APPROVED_ROLES = {"OWNER", "ADMIN"} eltávolítva
  • _check_org_capability() helper hozzáadva — JSONB-ből olvassa a can_approve_expense flag-et
  • Költség státusz meghatározása: can_approve → APPROVED, fuel (category_id=1) → APPROVED, egyéb → PENDING_APPROVAL

4. ASSETS - assets.py:

  • AUTO_APPROVED_ROLES = {"OWNER", "ADMIN"} eltávolítva
  • _check_org_capability() helper hozzáadva
  • create_asset_event végpontban az esemény státusz JSONB-alapú ellenőrzésre váltva

5. SEED - seed_org_roles.py:

  • permissions paraméter hozzáadva az OrgRole konstruktorhoz
  • Meglévő role-ok permissions mezőjének frissítése (RBAC Phase 2 update loop)

🗄️ Adatbázis

  • sync_engine futtatva: permissions JSONB oszlop létrehozva a fleet.org_roles táblában
  • 5 org role permissions adata feltöltve: OWNER, ADMIN, ACCOUNTANT, DRIVER, VIEWER

Verifikáció

  • Backend indítás: sikeres (nincs import error)
  • POST /api/v1/expenses/ ADMIN userrel → 201 CREATED, expense_status: "APPROVED" (mert ADMIN rendelkezik can_approve_expense: True képességgel)
  • fleet.org_roles JSONB permissions: mind az 5 role helyesen tárolva

2026-06-18: RBAC Phase 3 - Frontend Reactivity & UI Capabilities Integration

🎯 Cél

P0 kritikus feladat: Frontend reaktivitás javítása (F5 Bug) és UI képesség-alapú megjelenítés bevezetése.

🔧 Változtatások

Task 1 - F5 Bug javítás (Direct Mutation / Explicit Fetch pattern):

  • CostsActionsCard.vue: cost-saved event emit hozzáadva, ami a költség mentése után értesíti a szülőt
  • DashboardView.vue: refreshCostsTrigger ref (számláló) és onCostSaved() handler hozzáadva
  • DashboardView.vue: :refresh-trigger="refreshCostsTrigger" prop átadva a VehicleDetailModal-nak
  • VehicleDetailModal.vue: refreshTrigger prop és watch hozzáadva a költségek újratöltéséhez

Task 2 - Backend /users/me exposure:

  • schemas/user.py: system_capabilities: Dict[str, bool] és org_capabilities: Dict[str, Dict[str, bool]] mezők hozzáadva a UserResponse-hoz
  • endpoints/users.py: _build_user_response() kiegészítve system_capabilities feloldással a SYSTEM_CAPABILITIES_MATRIX-ból
  • endpoints/users.py: read_users_me() kiegészítve org_capabilities aszinkron feloldásával az aktív org tagságokból

Task 3 - Frontend Auth Store capability helpers:

  • stores/auth.ts: UserProfile interface kiegészítve system_capabilities és org_capabilities mezőkkel
  • stores/auth.ts: hasSystemCapability(capability) helper - ellenőrzi a rendszerszintű képességet
  • stores/auth.ts: hasOrgCapability(orgId, capability) helper - ellenőrzi a szervezeti képességet
  • stores/auth.ts: getOrgCapabilities(orgId) helper - visszaadja az összes szervezeti képességet

Task 4 - UI Integration Pilot:

  • VehicleDetailModal.vue: "Szerkesztés" gomb v-if-je kiegészítve authStore.hasOrgCapability(orgId, 'can_approve_expense') ellenőrzéssel

Verifikáció

  • Backend Python szintaxis: user.py és users.py hibátlanul lefordul
  • Frontend TypeScript: vue-tsc --noEmit sikeres (csak pre-existing .vue.js file hibák)
  • Event propagation chain teljes: Cost modal → CostsActionsCard → DashboardView → VehicleDetailModal

2026-06-18 - P0 Legacy Garage Migration (RBAC Phase 3)

🎯 Cél

Adatbázis backfill szkript elkészítése és futtatása, amely a legacy person_id-alapú ownership struktúrát áttelepíti az új organization_id/organization_members RBAC struktúrába. Három feladat: (A) hiányzó személyes garázsok létrehozása, (B) userek bindolása a garázsukhoz OWNER role-lal, (C) gazdátlan járművek megmentése.

🔧 Változtatások

1. BACKEND - migrate_legacy_garages.py:

  • Task A: Lekérdezi a identity.users táblában lévő usereket, akiknek nincs INDIVIDUAL típusú személyes garázsuk (fleet.organizations). Létrehozza a garázst minden szükséges oszloppal.
  • Task B: Minden userhez hozzárendeli a személyes garázsát organization_members táblában OWNER szerepkörrel. Frissíti a scope_id mezőt, ha az NULL.
  • Task C: Megkeresi a gazdátlan járműveket (vehicle.assets táblában ahol owner_person_id kitöltött, de current_organization_id vagy owner_org_id NULL), és átvezeti őket a tulajdonos személyes garázsába.
  • Technikai részletek: asyncpg-t használ közvetlen adatbázis kapcsolattal. Dinamikus schema discovery (information_schema.columns) a NOT NULL constraint-ek kezelésére.

2. BACKEND - verify_migration.py:

  • Verifikációs szkript, amely a migráció után lekérdezi a kulcs metrikákat.

Eredmény

  • 6 új személyes garázs létrehozva (Task A)
  • 16 member hozzáadva organization_members-hez (Task B)
  • 10 scope_id frissítve (Task B)
  • 1 gazdátlan jármű megmentve (Task C)
  • 0 user maradt személyes garázs nélkül
  • 0 user NULL scope_id-vel
  • 0 orphaned jármű maradt
  • 17 total INDIVIDUAL garázs
  • 22 organization member OWNER role-lal
  • 38/41 jármű organization-höz rendelve

2026-06-18 - P0 Feature: Subscription & Package Assignment Bridge

🎯 Cél

Subscription Tier hozzárendelés szervezetekhez (fleet.organizations) a subscription_tier_id FK oszlopon keresztül. Teljes admin API + frontend integráció a csomagok kezeléséhez.

🔧 Változtatások

1. ADATBÁZIS - Organization:

  • subscription_tier_id FK oszlop hozzáadva (nullable, FK → system.subscription_tiers.id)
  • Sync engine sikeresen lefuttatva → oszlop létrejött

2. CAPABILITIES - capabilities.py:

  • CAN_MANAGE_SUBSCRIPTIONS = "can_manage_subscriptions" új capability
  • SUPERADMIN és ADMIN mátrixba bevéve

3. API ENDPOINT - organizations.py:

  • PUT /{org_id}/subscription végpont RequireSystemCapability(Capability.CAN_MANAGE_SUBSCRIPTIONS) RBAC-cal
  • SubscriptionAssignIn Pydantic schema a body-hoz

4. BILLING ENGINE - billing_engine.py:

  • upgrade_org_subscription() függvény: beállítja a subscription_tier_id-t, frissíti a subscription_plan és base_asset_limit mezőket, létrehozza az OrganizationSubscription audit rekordot

5. QUOTA ENGINE - evidence.py:

  • scan-registration végpont most az org subscription_tier_idSubscriptionTier.rules.allowances.max_vehicles értéket olvassa
  • Fallback: org.base_asset_limit ha nincs tier hozzárendelve

6. SCHEMA - organization.py:

  • subscription_tier_id: Optional[int] hozzáadva OrganizationUpdate és OrganizationResponse modellekhez

7. FRONTEND TYPES - organization.ts:

  • subscription_tier_id?: number | null hozzáadva OrganizationItem interfészhez

8. FRONTEND UI - OrganizationSettingsModal.vue:

  • Subscription tier dropdown (select + assign gomb)
  • availableTiers betöltése GET /admin/packages/-ből
  • assignSubscription() hívás PUT /organizations/{id}/subscription-ra

Verifikáció

  • subscription_tier_id oszlop létezik (information_schema ellenőrizve)
  • Org 45: corp_premium_plus_v1 (tier_id=17, max_vehicles=50)
  • Org 49: corp_premium_v1 (tier_id=16, max_vehicles=20)
  • 8 subscription tier elérhető a system.subscription_tiers táblában

2026-06-18 - P0 Critical: Admin Button Fix & Orphaned Vehicle Rescue

🎯 Cél

5-lépéses P0 kritikus feladat: (1) Törött Admin/Vezérlőpult gomb javítása a frontenden, (2) Adatbázis felderítés: gazdátlan járművek keresése, (3) Admin felhasználó garázsainak ellenőrzése, (4) Gazdátlan járművek megmentése (UPDATE), (5) Verifikáció és jelentés.

🔧 Változtatások

1. FRONTEND - auth.ts:

  • Root Cause: A ModeSwitcher.vue a stores/auth-ból importálja az authStore.isAdmin-et, de a store-ból hiányzott az isAdmin computed getter. Mindig undefined-et adott vissza, így a gomb sosem jelent meg.
  • Fix: isAdmin computed property hozzáadva, amely ellenőrzi a superadmin, admin, region_admin, country_admin, moderator szerepköröket (kisbetűs összehasonlítással).

2. ADATBÁZIS - Gazdátlan járművek felderítése:

  • 3 db orphaned vehicle találva: TEST-API-01, ABB112, ABC-123 (mind archived státuszú, owner_person_id = NULL, current_organization_id = NULL)
  • Admin user (admin@profibot.hu, user_id=2, person_id=2, role=ADMIN) nem rendelkezik Corporate (fleet_owner/business) garázzsal OWNER joggal - csak "Admin Garázsa" (Org 67, individual típus)
  • Admin 17 járműve már helyesen a Test Company (Org 1, fleet_owner) alá van rendelve

3. ADATBÁZIS - Rescue UPDATE:

  • 3 gazdátlan jármű átvezetve az Admin Garázsába (Org 67): owner_person_id=2, owner_org_id=67, current_organization_id=67
  • Parancs: docker compose exec -T shared-postgres psql ... UPDATE vehicle.assets SET ... WHERE current_organization_id IS NULL AND owner_person_id IS NULL
  • Eredmény: UPDATE 3

Verifikáció

  • SELECT ... WHERE current_organization_id IS NULLüres eredmény (0 gazdátlan jármű)
  • 3 rescued vehicle: TEST-API-01, ABB112, ABC-123 → Admin Garázsa (Org 67)
  • Admin 17 egyéb járműve: Test Company (Org 1) alatt maradt

2026-06-18 - P0 CRITICAL: Fix Admin Panel Case Sensitivity (SUPERADMIN/ADMIN)

🎯 Cél

A backend UPPERCASE role értékei (SUPERADMIN, ADMIN) és a frontend lowercase összehasonlításai közötti mismatch javítása.

🔧 Változtatások

Verifikáció

  • Backend admin ping endpoint: role: "ADMIN" (UPPERCASE)
  • Frontend Vite HMR: minden módosított fájl újratöltve
  • Admin felület minden ponton (store getter, router guard, header button, users view) case-insensitive módon ellenőrzi a SUPERADMIN/ADMIN szerepköröket

2026-06-18 - P0 Critical: Precise Subscription Backfill & Legacy Cleanup

🎯 Cél

Admin garázsok VIP csomag hozzárendelése, tömeges free tier fallback, legacy string oszlopok kivezetése a kvóta logikából.

🔧 Változtatások

  1. Adatbázis backfill (backend/app/scripts/p0_subscription_backfill.py):

    • VIP: Admin Garázsa (org_id=67) → private_test_v01 (tier_id=19, max_vehicles=100)
    • VIP: Test Company (org_id=1) → org_test_v01 (tier_id=20, max_vehicles=100)
    • Free Tier Fallback: 14 individual org → private_free_v1, 13 other org → corp_free_v1
    • Eredmény: 31/31 org assigned, 0 NULL
  2. Kvóta Motor refaktor (backend/app/services/asset_service.py):

    • get_user_vehicle_limit(): Eltávolítva a legacy subscription_plan string lookup
    • Single Source of Truth: SubscriptionTier.rules['allowances']['max_vehicles'] JSONB
    • Fallback lánc: user subscription → org subscription tier → org.base_asset_limit → config
  3. Dokumentum helper cleanup (backend/app/api/v1/endpoints/documents.py):

    • _check_premium_or_admin(): Eltávolítva a subscription_plan string alapú premium check

Verifikáció

  • Schema szinkron: sync_engine → 1089/1089 OK
  • 0 org with NULL subscription_tier_id
  • Distribution: private_free_v1(14), corp_free_v1(13), corp_premium_v1(1), private_test_v01(1), corp_premium_plus_v1(1), org_test_v01(1)

2026-06-18 - P0 CRITICAL: PATCH Organization 500 Error Fix

🎯 Cél

A PATCH /api/v1/organizations/{id} végpont 500 Internal Server Error hibájának javítása, amely a subscription backfill után jelentkezett.

🔍 Root Cause Analysis

Kiváltó ok #1: PostgreSQL ENUM típus mismatch.

A fleet.organization_members.role oszlop az adatbázisban fleet.orguserrole ENUM típusként volt definiálva, de a SQLAlchemy modellben OrganizationMember.role String(50)-ként. Amikor a PATCH /organizations/{id} végpont update_organization függvénye lefutott, az SQLAlchemy a következő SQL-t generálta:

WHERE fleet.organization_members.role IN ('OWNER', 'ADMIN')

PostgreSQL nem tud implicit módon összehasonlítani egy ENUM típust (fleet.orguserrole) egy string literállal (character varying), ezért dobta:

operator does not exist: fleet.orguserrole = character varying
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.

Kiváltó ok #2: Hiányzó OrganizationMember rekord az owner_id alapú tulajdonosoknak.

A subscription backfill során számos szervezetnél az owner_id mező be lett állítva, de a fleet.organization_members táblába nem került be a megfelelő OWNER szerepkörű rekord. A PATCH végpont csak a OrganizationMember táblában keresett, így a jogos tulajdonos is 403-as hibát kapott.

Érintett fájlok:

🔧 Javítások

1. MODELL JAVÍTÁS (organization.py):

  • OrganizationMember.role oszlop típusa String(50)PG_ENUM(OrgUserRole, name="orguserrole", schema="fleet", create_type=False)-re változtatva
  • create_type=False paraméter biztosítja, hogy nem próbáljuk újra létrehozni a már meglévő ENUM típust

2. VÉGPONT JAVÍTÁS (organizations.py):

  • Az RBAC ellenőrzés kiegészítve: ha a user nem található OrganizationMember-ként, de Organization.owner_id-ként szerepel, akkor is engedélyezve van a módosítás
  • A szervezet lekérése előrébb került (az RBAC ellenőrzés előtt), hogy elérhető legyen az owner_id mező
  • Ezzel elkerültük a duplikált SQL lekérdezést is

Verifikáció

  • PATCH /api/v1/organizations/44200 OK (korábban 500, majd 403)
  • Nincs több UndefinedFunctionError a logokban
  • Az SQLAlchemy által generált SQL helyesen kezeli az ENUM összehasonlítást
  • A tulajdonos (owner_id) sikeresen tudja módosítani a szervezet adatait

2026-06-18: Address fields missing from OrganizationResponse and GET /my (#3-as probléma)

🔍 Root Cause Analysis

A PATCH /api/v1/organizations/{id} végpont 200 OK-val tért vissza, és az adatok az adatbázisba is bekerültek, de a frontend UI-ban nem jelentek meg a cím adatok. A probléma három rétegben jelentkezett:

1. Backend OrganizationResponse séma hiányossága (organization.py):

  • A OrganizationResponse Pydantic osztály nem tartalmazta a cím mezőket (address_zip, address_city, address_street_name, address_street_type, address_house_number, address_hrsz)
  • A PATCH végpont response_model=OrganizationResponse-et használ, így a válaszból kimaradtak a cím adatok

2. Backend GET /my végpont hiányossága (organizations.py):

  • A get_my_organizations függvény által visszaadott dictionary-ből hiányoztak a cím mezők
  • A frontend a PATCH után authStore.fetchMyOrganizations()-t hív, ami ezt a végpontot használja

3. Frontend OrganizationItem típus és onMounted hiányossága (OrganizationSettingsModal.vue):

  • A TypeScript OrganizationItem interfészből hiányoztak a cím mezők
  • Az onMounted hook nem töltötte be a cím mezőket a form objektumba

További probléma: A OrganizationUpdate séma tartalmazott olyan mezőket (address_stairwell, address_floor, address_door), amelyek nem léteznek az Organization SQLAlchemy modellben. Ezeket a PATCH végpont csendben figyelmen kívül hagyta, de a GET /my végpont AttributeError-t dobott volna rájuk.

🔧 Javítások

1. OrganizationResponse séma bővítése (organization.py):

  • Hozzáadva: address_zip, address_city, address_street_name, address_street_type, address_house_number, address_hrsz
  • Eltávolítva a nem létező mezők: address_stairwell, address_floor, address_door

2. OrganizationUpdate séma tisztítása (organization.py):

  • Eltávolítva a nem létező mezők: address_stairwell, address_floor, address_door

3. GET /my végpont bővítése (organizations.py):

  • Hozzáadva a cím mezők a visszaadott dictionary-hez

4. Frontend OrganizationItem típus bővítése (organization.ts):

  • Hozzáadva a cím mezők az interfészhez

5. Frontend OrganizationSettingsModal.vue javítása (OrganizationSettingsModal.vue):

  • form objektum bővítve a cím mezőkkel
  • onMounted hook kiegészítve a cím mezők betöltésével (mindkét ágban)

Verifikáció

  • GET /api/v1/organizations/my200 OK, tartalmazza a cím mezőket
  • PATCH /api/v1/organizations/{id}200 OK, a válasz tartalmazza a cím mezőket
  • A cím adatok elérhetők a frontend OrganizationSettingsModal form-jában

2026-06-18 - P0: Subscription Stacking & Ad Engine Implementation

🎯 Cél

Subscription időhalmozás (stacking) implementálása duration_days segítségével a JSONB rules-ban, valamint a Native Ad Engine adatbázis séma (marketing schema) létrehozása.

🔧 Változtatások

1. Pydantic Schema Bővítés - subscription.py:

  • DurationModel: days (default=30) és allow_stacking (default=True) mezők
  • AdPolicyModel: show_ads, ad_free_grace_days, max_daily_impressions mezők
  • SubscriptionRulesModel: duration: Optional[DurationModel] és ad_policy: Optional[AdPolicyModel] opcionális mezők

2. Stacking Logika - billing_engine.py:

  • upgrade_org_subscription(): duration_days kiolvasása tier.rules-ból, valid_until számítás stackinggel
  • upgrade_subscription(): UserSubscription kezelés stacking logikával
  • Képlet: IF existing.valid_until > NOW → stacked = existing + duration_days ELSE fresh = NOW + duration_days

3. Ad Engine Modellek - marketing.py:

  • 7 tábla a marketing sémában: campaigns, creatives, placements, campaign_creatives, campaign_placements, ad_impressions, ad_clicks
  • String-based enumok (CampaignStatus, CreativeType, PlacementType) CheckConstraint-tel a sync_engine kompatibilitásért
  • Priority + weighted random selection logika a hirdetés kiválasztáshoz

4. Seed Adatok - seed_packages.py:

  • Mind a 6 csomag frissítve duration (30 nap, allow_stacking=true) és ad_policy mezőkkel
  • Free: show_ads=true, max_daily_impressions=50
  • Pro: show_ads=true, grace_days=3, max_impressions=100
  • VIP/Corporate: show_ads=false
  • UPSERT (INSERT ... ON CONFLICT DO UPDATE) használata DELETE helyett FK referenciák megőrzéséért

5. Modell Regisztráció - __init__.py:

  • Marketing modellek importálva és __all__-hoz adva

Verifikáció

  • sync_engine: 1152 elem szinkronban, 7 marketing tábla létrehozva
  • seed_packages: 6 csomag sikeresen frissítve duration/ad_policy mezőkkel
  • Pydantic validáció: DurationModel, AdPolicyModel, SubscriptionRulesModel hibátlan
  • Stacking logika szimuláció: Fresh (30 nap), Stacked (40 nap = 10 + 30), Expired reset (30 nap)
  • Gitea kártya #271 létrehozva és elindítva

2026-06-18 - P0 Debug: GET /api/v1/subscriptions/public 404 Fix

🎯 Cél

A frontend SubscriptionPlansView által hívott GET /api/v1/subscriptions/public végpont 404-es hibájának kivizsgálása és javítása.

🔧 Változtatások

1. ROOT CAUSE - Stale container cache:

  • A subscriptions.py fájl új volt (untracked ?? git statusban)
  • A futó uvicorn konténer a régi modulgráfot cache-elte, nem tudott az új router-ről
  • Fix: docker compose restart sf_api — a konténer újratöltése után a végpont 200 OK-val válaszolt

2. i18n - Missing common.retry key:

  • A hu.ts common szekciójába hozzáadva: retry: 'Újrapróbálkozás'
  • A en.ts common szekciójába hozzáadva: retry: 'Retry'

3. is_public filtering:

  • A get_public_subscriptions végpont most már szűri a csomagokat a rules.lifecycle.is_public JSONB mező alapján
  • Csak azok a tier-ek kerülnek visszaadásra, ahol is_public=True (alapértelmezett: True)

Verifikáció

  • GET /api/v1/subscriptions/public200 OK, 10 tier visszaadva
  • Minden tier is_public=True értékkel rendelkezik
  • A frontend i18n kulcsok (common.retry) mindkét nyelven elérhetők

2026-06-18 - P0 Booster Architecture & Minimalist Subscription UI

🎯 Cél

P0 architektúra frissítés: Booster kvóta rendszer, marketing adatok a subscription tier-ekhez, minimalista előfizetési kártyák és részletes modal.

🔧 Változtatások

1. extra_allowances JSONB oszlop

  • OrganizationSubscription modellhez hozzáadva: extra_allowances: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb"), default=dict)
  • A sync_engine sikeresen létrehozta a finance.org_subscriptions.extra_allowances oszlopot

2. Kvóta motor frissítés

3. Marketing adatok

4. Minimalista előfizetési kártyák

  • SubscriptionPlansView.vue teljes átírása: csak név, badge, subtitle, ár, 3 kulcs statisztika (max_vehicles, max_garages, monthly_free_credits) megjelenítése
  • Gomb szöveg: "Részletek & Vásárlás"
  • h-full + flex flex-col az egyenlő kártyamagasságokért

5. PlanDetailsModal komponens

  • PlanDetailsModal.vue létrehozva: Teleport modal, fejléc highlight színnel, havi/éves váltó, megtakarítás % kijelzés, 3 kulcs statisztika, szolgáltatás lista, rendelés szimuláció gomb
  • i18n kulcsok hozzáadva (hu.ts, en.ts)

Verifikáció

  • extra_allowances oszlop létezik: finance.org_subscriptions.extra_allowances JSONB, default '{}'::jsonb
  • 6 subscription tier rendelkezik marketing adatokkal (private_free, private_pro, private_vip, corp_premium, corp_premium_plus, corp_vip)
  • Minden Python fájl szintaktikailag helyes
  • seed_packages.py sikeresen lefutott, az adatok perzisztálva

2026-06-18 - P0: Multi-Region Pricing & Admin Marketing UI

🎯 Cél

Multi-region pricing (pricing_zones) bevezetése a subscription rendszerbe: a backend dinamikusan feloldja a megfelelő árat a felhasználó országkódja alapján, a frontend pedig megjeleníti a feloldott árat. Admin UI-ban marketing mezők és pricing zones szerkesztő hozzáadása.

🔧 Változtatások

1. BACKEND - Schema subscription.py:

  • PricingZoneModel osztály hozzáadva: monthly_price, yearly_price, currency, credit_price
  • pricing_zones: Optional[Dict[str, PricingZoneModel]] mező a SubscriptionRulesModel-ben
  • MarketingModel osztály hozzáadva: subtitle, badge, highlight_color
  • marketing: Optional[MarketingModel] mező a SubscriptionRulesModel-ben
  • PublicSubscriptionTierResponse séma: resolved_pricing: Optional[PricingZoneModel] mezővel

2. BACKEND - Seed seed_packages.py:

  • pricing_zones() helper függvény: DEFAULT (EUR), HU (HUF), US (USD) zónákkal
  • Mind a 6 fő csomag (private_free, private_pro, private_vip, corp_premium, corp_premium_plus, corp_vip) pricing_zones adatokat kapott

3. BACKEND - Endpoint subscriptions.py:

  • resolve_pricing(rules, country_code) függvény: országkód → pricing_zones → DEFAULT → legacy pricing → None
  • get_user_country_code(current_user, db) aszinkron függvény: aktív org country_code → person address country → "DEFAULT"
  • GET /public végpont frissítve: List[PublicSubscriptionTierResponse] visszatérés, resolved_pricing kitöltve

4. FRONTEND - SubscriptionPlansView.vue:

  • resolved_pricing mező a SubscriptionPlan interfészben
  • resolvedMonthlyPrice(), resolvedYearlyPrice(), resolvedCurrency(), yearlySavingsPercent() helperek
  • Dinamikus Intl.NumberFormat pénznem formázás (HUF=0 tizedes, más=2 tizedes)
  • Havi + éves ár kijelzés megtakarítás %-kal

5. FRONTEND - PlanDetailsModal.vue:

  • resolved_pricing alapján számított resolvedMonthlyPrice, resolvedYearlyPrice, resolvedCurrency computed property-k
  • Dinamikus pénznem formázás

6. FRONTEND - Admin Store adminPackages.ts:

  • PricingZoneModel interfész hozzáadva
  • MarketingModel interfész hozzáadva
  • pricing_zones és marketing mezők a SubscriptionRulesModel-ben

7. FRONTEND - Admin UI PackageEditModal.vue:

  • Marketing szekció: Alcím (Subtitle), Jelvény (Badge), Kiemelő szín (Hex color picker)
  • Pricing Zones szekció: dinamikus lista zónák hozzáadásával/törlésével/szerkesztésével (country code, havi/éves ár, pénznem, kredit ár)
  • buildRules() frissítve: marketing és pricing_zones beszúrása a rules objektumba
  • watch handler frissítve: meglévő marketing és pricing_zones adatok betöltése szerkesztéskor

Verifikáció

  • sync_engine sikeresen lefutott, a séma konzisztens
  • seed_packages.py sikeresen lefutott, mind a 6 fő csomag rendelkezik pricing_zones adatokkal (HU, US, DEFAULT)
  • API végpont (GET /api/v1/subscriptions/public) helyesen adja vissza a resolved_pricing mezőt
  • Frontend TypeScript ellenőrzés: nincs új hiba a módosított fájlokban
  • Admin UI: marketing mezők és pricing zones szerkesztő működik