Files
service-finder/plans/logic_spec_p0_hybrid_vendor_refactor.md

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_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!
# 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

# 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} -> 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)

(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()

  1. Rewrite quick_add_provider() to create ServiceProvider + ServiceProfile only
  2. Update schemas (add service_provider_id)
  3. Update expense creation endpoint

Fázis 3: Data Migration

  1. Create backend/scripts/migrate_providers.py
  2. Run migration

Fázis 4: Verification

  1. Verify GET /admin/organizations returns only 13 garages
  2. 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