céges meghívó kezelése,

This commit is contained in:
Roo
2026-06-17 22:07:55 +00:00
parent bf3a971ff1
commit 127b130401
28 changed files with 5806 additions and 1313 deletions

View File

@@ -1,123 +1,90 @@
# 🔧 Logic Spec: Provider Update & Search Fix Csomag
# 🔧 Fix Plan: Provider Update 500 Error (folder_slug Truncation)
## 🎯 Cél
A szolgáltató cégek adatainak rögzítésében és szerkesztésében fellépő hibák javítása, valamint az adatok részletes rögzíthetőségének biztosítása.
A `PUT /api/v1/providers/{id}` végpont által dobott 500-as hiba kijavítása, amikor a felhasználó egy olyan szolgáltató adatait szerkeszti, amely még **nincs** átmigrálva az `Organization` táblába (csak `ServiceStaging`-ben létezik).
## 📋 Problémák Összefoglalása
## 🔍 Root Cause Analysis
### 1. PUT /api/v1/providers/{id} → 404 Not Found (✅ MEGOLDVA)
- **Root Cause**: A `sf_api` konténer nem volt újraindítva a providers modul kódfrissítése után. A `pre_start.sh` fájlban `uvicorn` `--reload` flag nélkül fut.
- **Javítás**: `docker compose restart sf_api` (végrehajtva)
- **Verifikáció**: PUT /providers/58 → 200 OK
### Hibajelenség
`PUT /api/v1/providers/4859``500 Internal Server Error`
```
value too long for type character varying(12)
```
### 2. GET /api/v1/providers/search → 500 Internal Server Error (❌ JAVÍTANDÓ)
- **Hiba**: `AttributeError: Neither 'BinaryExpression' object nor 'Comparator' object has an attribute 'astext'`
- **Hibás kód**: [`provider_service.py:207`](../backend/app/services/provider_service.py:207)
```python
(Organization.external_integration_config["source"].astext == "crowdsourced", literal("crowd_added")),
```
- **Root Cause**: Az [`external_integration_config`](../backend/app/models/marketplace/organization.py:109) `JSON` típusú oszlop. A `["source"]` subscript `BinaryExpression`-t ad vissza, amelyen NINCS `.astext`.
- **Javítás**: `cast` használata:
```python
(cast(Organization.external_integration_config["source"], String) == "crowdsourced", literal("crowd_added")),
```
### Kiváltó ok
A provider ID=4859 a `marketplace.service_staging` táblában létezik, de **nincs** még `fleet.organizations` rekordja.
### 3. Adat-healing: Régi címformátumú Organization rekordok (❌ JAVÍTANDÓ)
- **Probléma**: Az Organization id=58 (`Autónyíri Kft.`) adatai a régi formátumban:
- `street_name = "Egressy u. 4."` (nem atomizált)
- `address_street_name = NULL`, `address_street_type = NULL`, `address_house_number = NULL`
- `zip = NULL`
- **Következmény**: A frontend DetailModal üres címet mutat.
- **Javítás**: Adat-healing script a `street_name` mezőből atomizált komponensek kinyerésére.
Az [`update_provider()`](backend/app/services/provider_service.py:786) függvény a migrációs ágon (ServiceStaging → Organization) a `folder_slug`-ot az alábbi képlettel generálja:
### 4. Multi-source Update Probléma (❌ JAVÍTANDÓ)
- **Probléma**: Az [`update_provider`](../backend/app/services/provider_service.py:477) CSAK `Organization`-ben keres. A search UNION-nal dolgozza fel a szolgáltatókat 3 forrásból (Organization, ServiceStaging, ServiceProvider).
- **Javítás**: Multi-source update logika.
### 5. Szerver Restart Workflow Hiánya (❌ JAVÍTANDÓ)
- **Probléma**: A [`pre_start.sh`](../backend/app/scripts/pre_start.sh) `--reload` nélkül indul.
- **Javítás**: `--reload` flag fejlesztői módban.
---
## 🗺️ Érintett Fájlok
| Fájl | Változtatás | Prioritás |
|------|-------------|-----------|
| [`provider_service.py:207`](../backend/app/services/provider_service.py:207) | `.astext` → `cast()` | **KRITIKUS** |
| [`provider_service.py:477-562`](../backend/app/services/provider_service.py:477) | Multi-source update | **MAGAS** |
| Új: `heal_provider_addresses.py` | Adat-healing script | **MAGAS** |
| [`pre_start.sh`](../backend/app/scripts/pre_start.sh) | `--reload` dev módban | **ALACSONY** |
---
## 🛠️ Részletes Javítási Terv
### 1. `.astext` → `cast()` javítás
**Fájl**: [`provider_service.py`](../backend/app/services/provider_service.py:207)
**Jelenlegi:**
```python
(Organization.external_integration_config["source"].astext == "crowdsourced", literal("crowd_added")),
folder_slug = f"sp-{staging.id}-{uuid.uuid4().hex[:6]}",
```
**Javított:**
- `"sp-"` = 3 karakter
- `"4859"` = 4 karakter (provider_id hossza)
- `"-"` = 1 karakter
- `"4d45d3"` = 6 karakter (uuid hex)
- **Összesen: 14 karakter**
Az [`Organization`](backend/app/models/marketplace/organization.py:75) modellben a `folder_slug` mező:
```python
(cast(Organization.external_integration_config["source"], String) == "crowdsourced", literal("crowd_added")),
folder_slug: Mapped[str] = mapped_column(String(12), unique=True, index=True)
```
**Csak 12 karaktert enged!** → PostgreSQL `StringDataRightTruncationError`.
**Ugyanez a hiba** a crowd-sourced migrációs ágon is (line 820):
```python
folder_slug = f"cr-{crowd.id}-{uuid.uuid4().hex[:6]}",
```
### 2. Multi-source Update Logika
### Összehasonlítás
- **`quick_add_provider`** (line 501-503): `hashlib.md5(...).hexdigest()[:12]` → pontosan 12 karakter → **MŰKÖDIK**
- **`update_provider` migration** (line 786, 820): `"sp-{id}-{hex}"` / `"cr-{id}-{hex}"`**TÚL HOSSZÚ**
**Fájl**: [`provider_service.py:477`](../backend/app/services/provider_service.py:477)
## 📋 Javítási Terv
Az `update_provider` ellenőrizze mindhárom forrást:
1. `db.get(Organization, provider_id)` → meglévő logika
2. `db.get(ServiceStaging, provider_id)` → migrálás Organization-be
3. `db.get(ServiceProvider, provider_id)` → migrálás Organization-be
4. Ha egyikben sem → ValueError
### 1. Model fix: [`backend/app/models/marketplace/organization.py`](backend/app/models/marketplace/organization.py:75)
**Változtatás:** `folder_slug` oszlop méretének növelése `String(12)``String(24)`
### 3. Adat-healing Script
Indoklás:
- A jelenlegi 12 karakter túl szűk
- A `quick_add_provider` pontosan 12 karaktert használ → kompatibilis
- A migrációs slug-ok (pl. `sp-{id}-{hex6}`) elférnek
- A `unique=True` megszorítás megmarad
**Fájl**: `backend/app/scripts/heal_provider_addresses.py`
### 2. Service fix: [`backend/app/services/provider_service.py`](backend/app/services/provider_service.py:786)
**Változtatás:** A migrációs `folder_slug` generálás módosítása konzisztens `hashlib.md5` alapú generálásra.
1. Lekérdezni Organization rekordokat, ahol `org_type='service_provider'` ÉS `address_street_name IS NULL` ÉS `street_name IS NOT NULL`
2. Regex: `r'^([^\d]+?)\s+(u\.|utca|út|tér|köz|sor|körút|liget|part|fasor|sétány|park|híd|sugárút|rakpart|dűlő|telep|szőlő)\s*(.*)$'`
3. Frissíteni az atomizált mezőket
4. Naplózás
**Staging ág (line 786):**
```python
# EZT:
folder_slug = f"sp-{staging.id}-{uuid.uuid4().hex[:6]}"
# HELYETTE:
folder_slug = hashlib.md5(f"sp-{staging.id}-{uuid.uuid4()}".encode()).hexdigest()[:12]
```
---
**Crowd ág (line 820):**
```python
# EZT:
folder_slug = f"cr-{crowd.id}-{uuid.uuid4().hex[:6]}"
# HELYETTE:
folder_slug = hashlib.md5(f"cr-{crowd.id}-{uuid.uuid4()}".encode()).hexdigest()[:12]
```
## 🧪 Tesztelési Terv
Indoklás:
- Konzisztens a `quick_add_provider` által használt módszerrel
- Mindig pontosan 12 karakter → garantáltan elfér a `String(12)` mezőben is
- Elég nagy entrópia az egyediséghez (`hashlib.md5(...)` → 32 hex, `[:12]` → 48 bit)
### Teszt 1: search működés
### 3. Adatbázis szinkronizáció
Mivel az oszlop mérete változik (`String(12)``String(24)`), futtatni kell:
```bash
docker compose exec sf_api python3 -c "
import httpx, asyncio
async def t():
async with httpx.AsyncClient(base_url='http://localhost:8000') as c:
r = await c.post('/api/v1/auth/login', data={'username': 'admin@profibot.hu', 'password': 'Admin123!'})
t = r.json()['access_token']
r2 = await c.get('/api/v1/providers/search', params={'q': 'Dunakeszi', 'limit': 5}, headers={'Authorization': f'Bearer {t}'})
print(f'Search: {r2.status_code}')
print(r2.text[:500])
asyncio.run(t())
"
docker exec sf_api python -m app.scripts.sync_engine
```
### Teszt 2: PUT működés
```bash
docker compose exec sf_api python3 -c "
import httpx, asyncio
async def t():
async with httpx.AsyncClient(base_url='http://localhost:8000') as c:
r = await c.post('/api/v1/auth/login', data={'username': 'admin@profibot.hu', 'password': 'Admin123!'})
t = r.json()['access_token']
r2 = await c.put('/api/v1/providers/58',
json={'address_zip': '2120', 'city': 'Dunakeszi', 'name': 'Autónyíri Kft.'},
headers={'Authorization': f'Bearer {t}'})
print(f'PUT: {r2.status_code}')
print(r2.text)
asyncio.run(t())
"
```
## ✅ Elfogadási kritériumok
1. `PUT /api/v1/providers/4859` sikeresen lefut (200 OK)
2. A migrált Organization `folder_slug` pontosan 12 karakter hosszú
3. Meglévő provider-ek (`quick_add`-ból) továbbra is működnek
4. A `folder_slug` `unique` megszorítás nem sérül
5. A logokban nincs `StringDataRightTruncationError`