# 🏗️ Logic Spec: P1 Garage Selector & OrganizationMember Audit ## 1. Modul Célja és Masterbook 2.0 Illeszkedés **Cél:** A Dashboard "Garage Selector" (cégválasztó) pontosítása, hogy a bejelentkezett felhasználó: - a) Szerepeljen azon Organization rekordok között, ahol technikai tulajdonos (`owner_id`) - b) Szerepeljen azon Organization rekordok között, ahol aktív `OrganizationMember` Emellett az `OrganizationMember` tábla és a meghívási folyamat teljes auditja. **Masterbook 2.0 illeszkedés:** B2B szervezetkezelés (11-es Epic), B2B szerepkörök és szervezeti hierarchia. --- ## 2. Adatmodell Elemzés ### 2.1 OrganizationMember tábla (aktuális séma) | Oszlop | Típus | Kötelező | Megjegyzés | |--------|-------|----------|------------| | `id` | integer PK | YES | Auto-increment | | `organization_id` | integer FK -> fleet.organizations.id | YES | | | `user_id` | integer FK -> identity.users.id | NO | Lehet NULL (pending invite) | | `person_id` | bigint FK -> identity.persons.id | NO | | | `role` | ENUM (OrgUserRole) | YES | OWNER, ADMIN, FLEET_MANAGER, DRIVER, MECHANIC, RECEPTIONIST | | `permissions` | JSONB | NO (default `{}`) | | | `is_permanent` | boolean | NO (default false) | | | `is_verified` | boolean | NO (default false) | | ### 2.2 🔴 HIÁNYZÓ OSZLOPOK (Kritikus) 1. **`status` VARCHAR** - A kód használja (`status="pending"`, `status="active"` a organizations.py:421,481 sorokban), **de az adatbázisban NINCS ilyen oszlop!** Ez futási hibát okoz az invite/join folyamatokban. 2. **`joined_at` TIMESTAMP** - Nincs nyomon követve, mikor csatlakozott a tag. 3. **`expires_at` / `valid_until` TIMESTAMP** - Nincs lejárati dátum (pl. ideiglenes hozzáféréshez). 4. **`created_at` TIMESTAMP** - Hiányzik a tag rekord létrehozási dátuma. 5. **`updated_at` TIMESTAMP** - Hiányzik a módosítás dátuma. ### 2.3 🟢 MEGLÉVŐ, HELYESEN MŰKÖDŐ - `role` ENUM - Jól definiált, 6 szerepkörrel. - `user_id` + `person_id` kettős hivatkozás - Támogatja a "Dual Entity" modellt. - `is_permanent` - Alkalmas az állandó vs. ideiglenes tagság megkülönböztetésére. --- ## 3. Backend Módosítás: `get_my_organizations` Fix ### 3.1 Probléma Jelenleg a `GET /api/v1/organizations/my` lekérdezés (`organizations.py:192`) csak `INNER JOIN`-t használ az `OrganizationMember` táblával. Ez kizárja azokat az eseteket, ahol a felhasználó `owner_id` (technikai tulajdonos), de az `OrganizationMember` rekord nem található. ### 3.2 Megoldás Használjunk `LEFT JOIN`-t `OR` feltétellel: ```python from sqlalchemy import or_ stmt = ( select(Organization) .outerjoin(OrganizationMember, OrganizationMember.organization_id == Organization.id) .where( or_( Organization.owner_id == current_user.id, OrganizationMember.user_id == current_user.id ) ) .where(Organization.org_type.notin_([OrgType.service_provider, OrgType.service])) .where(Organization.is_deleted == False) .distinct() ) ``` ### 3.3 Válasz bővítése A frontend számára szükséges a `user_role` mező: ```python user_role = None for member in o.members: if member.user_id == current_user.id: user_role = member.role.value if hasattr(member.role, 'value') else str(member.role) break ``` --- ## 4. Frontend Módosítások ### 4.1 HeaderCompanySwitcher.vue - Jelenlegi állapot A komponens már használja az `authStore.myOrganizations` adatokat, és szűri a `companyOrganizations` computed property-ben az `individual`, `service_provider`, `service` típusokat. Ez rendben van. ### 4.2 Szükséges változtatások 1. **Nincs változtatás szükséges** a frontend logikában - a `fetchMyOrganizations()` már meghívja a `/organizations/my` végpontot, és a `companyOrganizations` computed property megfelelően szűr. 2. **OrganizationItem típus** (`frontend/src/types/organization.ts`) - már tartalmazza a `user_role` mezőt (nem kötelező). ### 4.3 Aktív szervezet kiválasztás A `switchOrganization` metódus (`frontend/src/stores/auth.ts:631`) már implementálva van, a `PATCH /users/me/active-organization` hívással. A frontend globális állapota frissül a `user.value.active_organization_id` mezővel. --- ## 5. Audit Jelentés: OrganizationMember & Invite Flow ### 5.1 Meglévő API Végpontok | Végpont | Metódus | Státusz | Leírás | |---------|---------|---------|--------| | POST /organizations/{org_id}/invitations | POST | Letezik | Meghívó küldése email címre | | POST /organizations/invitations/{token}/accept | POST | Letevezik | Meghívó elfogadása token alapján | | POST /organizations/{org_id}/join-request | POST | Letevezik | Csatlakozási kérelem (ha van admin) | | POST /organizations/{org_id}/claim/request | POST | Letevezik | Árva cég átvételi kérelem | | POST /organizations/{org_id}/claim/verify | POST | Letevezik | Árva cég átvétel OTP-vel | ### 5.2 🔴 HIÁNYZÓ VÉGPONTOK (Kritikus) | Végpont | Hiány | Hatás | |---------|-------|-------| | GET /organizations/{org_id}/members | Nincs | Nincs lehetőség a tagok listázására | | PATCH /organizations/{org_id}/members/{member_id}/role | Nincs | Nincs lehetőség a szerepkör módosítására | | DELETE /organizations/{org_id}/members/{member_id} | Nincs | Nincs lehetőség a tag eltávolítására | | GET /organizations/{org_id}/invitations | Nincs | Nincs lehetőség a függő meghívók listázására | | DELETE /organizations/{org_id}/invitations/{invitation_id} | Nincs | Nincs lehetőség a meghívó visszavonására | ### 5.3 🔴 Kritikus Adatbázis Probléma Az `OrganizationMember` modellben és az adatbázisban **HIÁNYZIK** a `status` oszlop, de a kód (`organizations.py:421` és `organizations.py:481`) hivatkozik rá (`status="pending"`, `status="active"`). Ez a meghívási folyamatban futási hibát okoz! ### 5.4 Meghívási Folyamat Ábrája ``` OWNER/ADMIN -> POST /invitations -> Van-e user? |-> Igen -> Letrehoz OrganizationMember (user_id=target) |-> Nem -> Letrehoz VerificationToken (token_type=org_invite) |-> Email ertesites | Regisztracio utan -> POST /invitations/{token}/accept -> Token validalas -> Letezik-e mar tag? |-> Igen -> Frissiti a szerepkört |-> Nem -> Letrehozza a tag rekordot -> Done ``` ### 5.5 Javasolt Javítási Sorrend 1. **P0 - AZONNAL:** `status` oszlop hozzáadása az `OrganizationMember` modellhez és az adatbázishoz (sync_engine). 2. **P0 - AZONNAL:** Dátum oszlopok (`created_at`, `updated_at`) hozzáadása. 3. **P1 - JELEN FELADAT:** `get_my_organizations` javítása `OR` logikára. 4. **P2 - KOVETKEZO:** Tagkezelő API végpontok implementálása (list, role change, remove). 5. **P2 - KOVETKEZO:** Meghívókezelő API végpontok (list pending, revoke). --- ## 6. Végrehajtási Terv ### 6.1 Backend változtatások (Code mód) 1. `backend/app/models/marketplace/organization.py` - `OrganizationMember` modellbe hozzáadni: `status`, `created_at`, `updated_at` 2. `backend/app/api/v1/endpoints/organizations.py` - `get_my_organizations` query javítása `OR` logikára + `DISTINCT` + `user_role` visszaadása ### 6.2 Frontend változtatások (Code mód) - Nincs szükség változtatásra - a frontend már helyesen használja az API-t. ### 6.3 Adatbázis szinkron - Futtatni: `docker exec sf_api python3 -m app.scripts.sync_engine` --- ## 7. Jóváhagyás **Kérem a felhasználó jóváhagyását a fenti tervhez!** A jóváhagyás után: 1. Létrehozom a Gitea feladatkártyákat 2. Váltok Code módba a megvalósításhoz