céges meghívó kezelése,
This commit is contained in:
431
.roo/history.md
431
.roo/history.md
@@ -19,209 +19,362 @@ Backend SQL hibák javítása a GET /admin/users végponton (Address outerjoin,
|
||||
- **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-16 - Fix 404 API Router & Redesign Dashboard Launcher Card
|
||||
## 2026-06-17 - P0 UI Card Cleanup & Custom Tag 500 Fix
|
||||
|
||||
### 🎯 Cél
|
||||
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.
|
||||
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. 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
|
||||
**1. FRONTEND - [`ServiceFinderView.vue`](frontend/src/views/ServiceFinderView.vue:367):**
|
||||
- **"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. 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
|
||||
**2. BACKEND - [`provider_service.py`](backend/app/services/provider_service.py:661):**
|
||||
- **`_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`)
|
||||
|
||||
**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
|
||||
### ✅ 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 Bugfix: Provider Data Persistence & Schema Alignment
|
||||
## 2026-06-17 - P0 UI Overhaul: Plus Code, Universal Categories, Detail Modal
|
||||
|
||||
### 🎯 Cél
|
||||
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.
|
||||
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 - [`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
|
||||
**1. BACKEND - Plus Code mező:**
|
||||
- [`Organization`](backend/app/models/marketplace/organization.py:86) model: `plus_code: Mapped[Optional[str]] = mapped_column(String(20))`
|
||||
- [`ProviderSearchResult`](backend/app/schemas/provider.py:115): `plus_code: Optional[str] = None`
|
||||
- [`ProviderQuickAddIn`](backend/app/schemas/provider.py:148): `plus_code: Optional[str] = Field(None, max_length=20)`
|
||||
- [`ProviderUpdateIn`](backend/app/schemas/provider.py:180): `plus_code: Optional[str] = Field(None, max_length=20)`
|
||||
- [`provider_service.py`](backend/app/services/provider_service.py:262): 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. 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
|
||||
**2. FRONTEND - Univerzális kategóriák:**
|
||||
- [`ProviderEditModal.vue`](frontend/src/components/provider/ProviderEditModal.vue): `allLevel1Categories` computed property, amely a checked Level 0 szülők Level 1 gyermekeit gyűjti össze deduplikálva
|
||||
- [`ProviderQuickAddModal.vue`](frontend/src/components/provider/ProviderQuickAddModal.vue): Ugyanaz a `allLevel1Categories` logika
|
||||
- Plus Code input mező hozzáadva mindkét modal "Alapadatok" tabjához
|
||||
|
||||
**3. BACKEND - [`providers.py`](backend/app/api/v1/endpoints/providers.py:134):**
|
||||
- `PUT /providers/{id}` végpont hozzáadva (authentikált, hibakezeléssel)
|
||||
**3. FRONTEND - Detail Modal Rework:**
|
||||
- [`ProviderDetailModal.vue`](frontend/src/components/provider/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. 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)
|
||||
**4. i18n:**
|
||||
- [`en.ts`](frontend/src/i18n/en.ts): plus_code és detail modal kulcsok
|
||||
- [`hu.ts`](frontend/src/i18n/hu.ts): plus_code és detail modal magyar fordítások
|
||||
|
||||
**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
|
||||
### ✅ 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)
|
||||
|
||||
**6. FRONTEND - [`ServiceFinderView.vue`](frontend/src/views/ServiceFinderView.vue:375):**
|
||||
- `@save` → `@saved` event binding
|
||||
- `handleEditSaved()`: mentés után automatikus keresés újrafuttatás
|
||||
## 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`](backend/app/services/provider_service.py:445):**
|
||||
- `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`](frontend/src/components/provider/ProviderEditModal.vue:617):**
|
||||
- `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ó
|
||||
- `sync_engine` lefuttatva: **1061 elem OK, 0 javítás, 0 shadow data** - rendszer tökéletesen szinkronban
|
||||
- `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 - P1 Critical Align: Atomizált címmezők a Provider sémákban
|
||||
## 2026-06-17 - P0 Ownership & Access Control Fix: OrganizationMember + owner_id
|
||||
|
||||
### 🎯 Cél
|
||||
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.
|
||||
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.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
|
||||
**1. BACKEND - [`provider_service.py`](backend/app/services/provider_service.py:468):**
|
||||
- **`owner_id=None` → `owner_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:
|
||||
```python
|
||||
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 - [`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. 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é
|
||||
|
||||
**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
|
||||
|
||||
**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
|
||||
**2. BACKEND - [`providers.py`](backend/app/api/v1/endpoints/providers.py:311):**
|
||||
- `PermissionError` → `HTTPException(403)` kezelés hozzáadva a `update_service_provider` végponthoz
|
||||
|
||||
### ✅ 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
|
||||
- `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 - Provider Update & Search Fix Csomag (#264)
|
||||
## 2026-06-17 - P0 Critical Bugfix: Quick-Add Provider 422 category_id null
|
||||
|
||||
### 🎯 Cél
|
||||
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
|
||||
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_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`
|
||||
**1. BACKEND - [`provider.py`](backend/app/schemas/provider.py:135):**
|
||||
- `ProviderQuickAddIn.category_id` típusa `int` → `Optional[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: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)
|
||||
**2. BACKEND - [`provider_service.py`](backend/app/services/provider_service.py:468):**
|
||||
- `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. 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
|
||||
**3. FRONTEND - [`ProviderQuickAddModal.vue`](frontend/src/components/provider/ProviderQuickAddModal.vue:781):**
|
||||
- `phone` → `contact_phone` és `email` → `contact_email` mezőnevek javítva (illeszkedés a backend Pydantic sémához)
|
||||
|
||||
**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
|
||||
### ✅ 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
|
||||
|
||||
**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."*
|
||||
## 2026-06-17: Bug investigation - Quick-add provider cégek megjelennek a Cégeim menüben
|
||||
|
||||
### ✅ 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 ✅
|
||||
**Kártya:** #266
|
||||
**Szerepkör:** Architect
|
||||
**Állapot:** Tervezés kész
|
||||
|
||||
## 2026-06-17 - i18n: Hiányzó provider címmező fordítások hozzáadása
|
||||
### 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-186`** — `GET /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-146`** — `companyOrganizations` 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:193`** — `in_([...])` → `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 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.
|
||||
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. 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'`
|
||||
**1. BACKEND - [`organizations.py`](backend/app/api/v1/endpoints/organizations.py:177-228):**
|
||||
- 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 - [`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'`
|
||||
**2. FRONTEND - [`HeaderCompanySwitcher.vue`](frontend/src/components/header/HeaderCompanySwitcher.vue:144-158):**
|
||||
- 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`](frontend/src/types/organization.ts:25-30):**
|
||||
- Ú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`](backend/app/services/provider_service.py:541):**
|
||||
- `owner_id=user_id` → `owner_id=None` — crowdsourcingból felvett szolgáltatónak nincs tulajdonosa
|
||||
|
||||
**2. BACKEND - [`provider_service.py`](backend/app/services/provider_service.py:641):**
|
||||
- `role=OrgUserRole.OWNER` → `role=OrgUserRole.ADMIN` — a felhasználó ADMIN jogot kap (szerkesztheti), de nem tulajdonos
|
||||
|
||||
### ✅ 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
|
||||
- ✅ 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 - "Dunakeszi, Dunakeszi" duplikáció javítása + irányítószám megjelenítés
|
||||
## 2026-06-17 - P1 Garage Selector Implementation & OrganizationMember Audit
|
||||
|
||||
### 🎯 Cél
|
||||
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.
|
||||
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 - [`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.
|
||||
**1. BACKEND - [`organizations.py`](backend/app/api/v1/endpoints/organizations.py:198):**
|
||||
- `get_my_organizations` query javítva: `INNER JOIN` → `LEFT 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. 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.
|
||||
**2. BACKEND - [`organization.py`](backend/app/models/marketplace/organization.py:195):**
|
||||
- `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. 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"`
|
||||
**3. ADATBÁZIS - `sync_engine`:**
|
||||
- 3 hiányzó oszlop sikeresen hozzáadva a `fleet.organization_members` táblához
|
||||
|
||||
### ✅ 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` ✅
|
||||
**4. DOKUMENTÁCIÓ - [`organization_member_audit_report_2026-06-17.md`](docs/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
|
||||
|
||||
## 2026-06-17 - Kártya fő szolgáltatás (category) mindig megjelenítése
|
||||
### ✅ 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 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.
|
||||
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. 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"
|
||||
**1. BACKEND - [`organization.py`](backend/app/models/marketplace/organization.py:187-190):**
|
||||
- `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.DRIVER` → `default=OrgUserRole.MEMBER`
|
||||
|
||||
**2. FRONTEND - [`hu.ts:1237`](frontend/src/i18n/hu.ts:1237):**
|
||||
- `serviceFinder.noCategory`: `'Nincs kategória'`
|
||||
**2. BACKEND - [`organizations.py`](backend/app/api/v1/endpoints/organizations.py:199-218):**
|
||||
- **`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. FRONTEND - [`en.ts:1237`](frontend/src/i18n/en.ts:1237):**
|
||||
- `serviceFinder.noCategory`: `'No category'`
|
||||
**3. BACKEND - [`organizations.py`](backend/app/api/v1/endpoints/organizations.py:326-607):**
|
||||
- **`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
|
||||
|
||||
### ✅ Verifikáció
|
||||
- Frontend build: `npm run build` sikeres, 0 hiba ✅
|
||||
**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/my` → **200 OK** (1 org: Test Company, user_role: ADMIN)
|
||||
- ✅ `GET /api/v1/organizations/1/members` → **200 OK** (1 member: admin@profibot.hu)
|
||||
- ✅ `POST /api/v1/organizations/1/invitations` → **201 Created** (invited_email, expires_at, role=MANAGER)
|
||||
- ✅ `PATCH /api/v1/organizations/1/members/25` → **200 OK** (role MANAGER → ADMIN)
|
||||
- ✅ `DELETE /api/v1/organizations/1/members/25` → **200 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`](backend/app/models/marketplace/organization.py:34):**
|
||||
- **`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`](backend/app/models/__init__.py:1):**
|
||||
- `OrgRole` hozzáadva az importokhoz és az `__all__` listához
|
||||
|
||||
**3. API VÉGPONTOK - [`organizations.py`](backend/app/api/v1/endpoints/organizations.py:1):**
|
||||
- `_()` 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.value` → `str(member.role)`
|
||||
- `timedelta(days=7)` → `timedelta(days=invite_expiry_days)` a `_get_org_settings()`-ből
|
||||
|
||||
**4. LOCALE FÁJLOK - [`hu.json`](backend/static/locales/hu.json:1), [`en.json`](backend/static/locales/en.json:1):**
|
||||
- `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
|
||||
|
||||
Reference in New Issue
Block a user