7.6 KiB
7.6 KiB
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_providerstábla a könnyűsúlyú, közösség által beküldött szolgáltatók otthona. Afleet.organizationscsak 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
# 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.organizationstáblát - Az admin
/admin/organizationsAPI 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
# 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
# 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
# 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}-> addservice_provider_idand resolvedvendor_nameAssetCostResponseschema -> addservice_provider_id
4. Data Migration: Fake Org -> ServiceProvider
4.1. Safe list (valódi garázsok)
(15, 21, 43, 45, 47, 48, 49, 50, 66, 67, 69, 70, 71)
4.2. Migrációs lépések
- SELECT all orgs where id NOT IN safe list
- 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
- 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
- Add
service_provider_idtoAssetCostmodel - Add
service_provider_idtoServiceProfilemodel - Extend
ServiceProvidermodel with address/contact fields - Run sync_engine to apply schema changes
Fázis 2: Refactor quick_add_provider()
- Rewrite
quick_add_provider()to create ServiceProvider + ServiceProfile only - Update schemas (add
service_provider_id) - Update expense creation endpoint
Fázis 3: Data Migration
- Create
backend/scripts/migrate_providers.py - Run migration
Fázis 4: Verification
- Verify GET /admin/organizations returns only 13 garages
- 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 amarketplace.service_providerstá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