admin felület fejlesztése garázs, előfizeztési csomagok
This commit is contained in:
189
plans/logic_spec_p0_hybrid_vendor_refactor.md
Normal file
189
plans/logic_spec_p0_hybrid_vendor_refactor.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user