# P0 Architecture Refactor: Option C (Hybrid) — Vendor/Expense Domain Separation **Date:** 2026-06-25 **Author:** Rendszer-Architect **Status:** DRAFT — Awaiting approval **Masterbook 2.0 Alignment:** Domain-Driven Design, Schema Separation --- ## 1. Modul Célja és MasterBook 2.0 illeszkedés ### Cél A `quick_add_provider()` jelenleg teljes `Organization` rekordokat hoz létre a `fleet.organizations` táblában minden egyes crowdsource-olt szolgáltató (vendor) számára. Ez **domain-szennyezés**: a könnyűsúlyú beszállítók (szerelők, gumiszervizek) nem érdemelnek teljes szervezeti entitást garázsadminnal, előfizetéssel és tagokkal. ### MasterBook 2.0 illeszkedés - **DDD Szeparáció:** A `marketplace.service_providers` tábla a könnyűsúlyú, közösség által beküldött szolgáltatók otthona. A `fleet.organizations` csak a valódi garázsokat (flottakezelő cégeket) tartalmazza. - **"Dual Entity":** A két entitás (Organization vs ServiceProvider) külön sémában él, eltérő életciklussal és adatigénnyel. - **Twin-technika:** A ServiceProvider egyszerű, mindössze név + cím adatokkal. A teljes garázs-entitásnak (Organization) viszont subscription, lifecycle, member management kell. --- ## 2. Jelenlegi Architektúra (As-Is) ### 2.1. `quick_add_provider()` flow ``` User -> POST /providers/quick-add -> 1. Organization létre (fleet.organizations, org_type=service_provider) -> 2. ServiceProfile létre (marketplace.service_profiles, FK -> organization) -> 3. ServiceExpertise létre (marketplace.service_expertises) -> 4. Branch létre (fleet.branches, FK -> organization) -> 5. OrganizationMember létre (fleet.organization_members) - mar kikommentezve! ``` ### 2.2. AssetCost vendor link ```python # fleet_finance.asset_costs vendor_organization_id -> fleet.organizations.id (a beszallito Organization-re mutat) ``` ### 2.3. Probléma - Minden crowdsource vendor beszennyezi a `fleet.organizations` táblát - Az admin `/admin/organizations` API a valódi garázsok mellett a fake orgokat is visszaadja - A valódi garázsok safe listája: `(15, 21, 43, 45, 47, 48, 49, 50, 66, 67, 69, 70, 71)` — a többi org a quick_add által létrehozott beszállító! --- ## 3. Célarchitektúra (To-Be) — Option C (Hybrid) ### 3.1. Adatmodell változások #### A) `fleet_finance.asset_costs` — ÚJ oszlop ```python # MEGTARTVA (kompatibilitás): vendor_organization_id: Optional[int] -> fleet.organizations.id # HOZZÁADVA (új elsődleges hivatkozás): service_provider_id: Optional[int] -> marketplace.service_providers.id ``` #### B) `marketplace.service_profiles` — ÚJ oszlop ```python # MEGTARTVA (meglévő kapcsolatok miatt, nullable): organization_id: Optional[int] -> fleet.organizations.id # HOZZÁADVA (új elsődleges hivatkozás): service_provider_id: Optional[int] -> marketplace.service_providers.id ``` #### C) `marketplace.service_providers` — Bővítés ```python # MEGLÉVŐ: id, name, address, category, status, source, validation_score, ... # HOZZÁADVA (a quick_add által igényelt mezők): city: Optional[str] address_zip: Optional[str] address_street_name: Optional[str] address_street_type: Optional[str] address_house_number: Optional[str] plus_code: Optional[str] contact_phone: Optional[str] contact_email: Optional[str] website: Optional[str] ``` ### 3.2. Új `quick_add_provider()` flow ``` User -> POST /providers/quick-add -> 1. ServiceProvider létre (marketplace.service_providers) <- ÚJ! -> 2. ServiceProfile létre (marketplace.service_profiles, FK -> service_provider) -> 3. ServiceExpertise létre (marketplace.service_expertises) -> x NINCS Organization létrehozva! -> x NINCS Branch létrehozva! -> x NINCS OrganizationMember létrehozva! ``` ### 3.3. AssetCost vendor választás logika ``` When creating an expense: 1. IF service_provider_id is provided -> save to service_provider_id column 2. IF vendor_organization_id is provided (legacy) -> save to vendor_organization_id column 3. Both can coexist ``` ### 3.4. API response changes - `GET /expenses/{asset_id}` -> add `service_provider_id` and resolved `vendor_name` - `AssetCostResponse` schema -> add `service_provider_id` --- ## 4. Data Migration: Fake Org -> ServiceProvider ### 4.1. Safe list (valódi garázsok) ```sql (15, 21, 43, 45, 47, 48, 49, 50, 66, 67, 69, 70, 71) ``` ### 4.2. Migrációs lépések 1. SELECT all orgs where id NOT IN safe list 2. For each fake org: - INSERT into marketplace.service_providers (name, address, city, etc.) - UPDATE marketplace.service_profiles SET service_provider_id = new_id WHERE organization_id = fake_org_id - UPDATE fleet_finance.asset_costs SET service_provider_id = new_id WHERE vendor_organization_id = fake_org_id 3. DELETE cascade: - DELETE from fleet.branches WHERE organization_id IN (fake org IDs) - DELETE from fleet.organization_members WHERE organization_id IN (fake org IDs) - DELETE from fleet.organizations WHERE id IN (fake org IDs) --- ## 5. Érintett fájlok ### Módosítandó fájlok: | Fájl | Változtatás | |------|-------------| | `backend/app/models/fleet_finance/models.py` | Add `service_provider_id` column + relationship to `AssetCost` | | `backend/app/models/marketplace/service.py` | Add `service_provider_id` column to `ServiceProfile` | | `backend/app/models/identity/social.py` | Add address/contact fields to `ServiceProvider` | | `backend/app/models/__init__.py` | Update imports if needed | | `backend/app/services/provider_service.py` | Rewrite `quick_add_provider` — create ServiceProvider, not Organization | | `backend/app/api/v1/endpoints/expenses.py` | Update `create_expense` to handle `service_provider_id` | | `backend/app/schemas/asset_cost.py` | Add `service_provider_id` field to schemas | | `backend/scripts/migrate_providers.py` | **NEW** — data migration script | --- ## 6. Végrehajtási Sorrend ### Fázis 1: Schema változások 1. Add `service_provider_id` to `AssetCost` model 2. Add `service_provider_id` to `ServiceProfile` model 3. Extend `ServiceProvider` model with address/contact fields 4. Run sync_engine to apply schema changes ### Fázis 2: Refactor `quick_add_provider()` 5. Rewrite `quick_add_provider()` to create ServiceProvider + ServiceProfile only 6. Update schemas (add `service_provider_id`) 7. Update expense creation endpoint ### Fázis 3: Data Migration 8. Create `backend/scripts/migrate_providers.py` 9. Run migration ### Fázis 4: Verification 10. Verify GET /admin/organizations returns only 13 garages 11. Verify quick-add flow no longer pollutes garages list --- ## 7. Kockázatok és Függőségek | Kockázat | Hatás | Mitigáció | |----------|-------|-----------| | Meglévő FK constraint a `vendor_organization_id`-ra | Adatintegritás sérülhet | Továbbra is nullable; a migration átírja a kapcsolatokat | | ServiceProfile.organization_id meglévő NOT NULL constraint | Migration elakadhat | Már nullable (`Optional[int]`), meglévő rekordok megmaradnak | | Frontend küld `vendor_organization_id`-t | Új mező nem töltődik | Backward compat: mindkét mezőt kezeljük | | Branch törlés cascade | Adatvesztés | Csak a fake org-ok Branch-eit töröljük | --- ## 8. Összefoglaló Az Option C (Hybrid) megoldás: - **Megtartja** a `vendor_organization_id`-t a backward compatibility érdekében - **Hozzáad** egy új `service_provider_id`-t, ami a `marketplace.service_providers` táblára hivatkozik - **Átírja** a `quick_add_provider()`-t, hogy ne hozzon létre Organization-t - **Adatmigrációt** végez: a meglévő fake org-okat áthelyezi a ServiceProvider táblába - **Ellenőrzi** a végeredményt: az admin garázslista pontosan a 13 valódi garázst tartalmazza