16 KiB
✅ Verifikáció
- Build ellenőrzés →
npx vite buildsikeres (7.08s, 0 error) - Chunk generálva:
CostEntryWizard-C_-R4OzC.js(29.95 kB) - refaktorált komponens helyesen fordul - ProviderAutocomplete újrafelhasználva mindhárom komponensben - nincs duplikáció
P0 EPIC - UI/UX Overhaul for Quick Fuel Logging (Gitea #404)
Dátum: 2026-07-09 Scope: Backend + Frontend (SimpleFuelModal, ProviderAutocomplete, ProviderQuickAddModal)
🔧 Módosított fájlok
1. Backend: providers.py
- Search query
min_lengthchanged from2→3 - Hibaüzenet frissítve: "min. 3 karakter"
2. Backend: provider_service.py
- City search changed from
StartsWith({city}%) toILIKEpartial (%{city}%) in all 3 data sources:- Organization (GeoPostalCode.city)
- ServiceStaging (ServiceStaging.city)
- ServiceProvider (ServiceProvider.address)
- Zip code search also changed to partial ILIKE
3. Frontend: ProviderAutocomplete.vue
- Minimum search length:
2→3characters - Empty state condition updated:
searchQuery.length >= 3
4. Frontend: ProviderQuickAddModal.vue
- New
prefillName?: stringprop added - Watch on
isOpennow setsform.name = props.prefillNamewhen provided
5. Frontend: SimpleFuelModal.vue (complete rewrite)
Magic Triangle (Auto-calculation):
- 3 fields:
amountGross,unitPrice,quantity - Tracks
lastEditedFieldto determine which field to auto-calculate - Formula:
amount_gross = unit_price × quantity - 2-decimal precision via
roundTo2Decimals()
Odometer Hint:
- On modal open, fetches last expense via
GET /expenses/{asset_id}?limit=1&sort=date_desc - Shows last known
mileage_at_costas a hint below the odometer input
Delayed New Provider Flow:
- If user types a raw vendor name without selecting a provider:
- Expense is saved with
external_vendor_nameset unit_priceandquantityare sent indataJSONB payload- After save,
ProviderQuickAddModalopens automatically withprefillNameprop - User can create the provider with the name pre-filled
- Expense is saved with
✅ Verifikáció
- Sync Engine →
docker exec sf_api python -m app.scripts.sync_engine→ 1287/1287 elements OK, 0 error - Python syntax → All 3 backend files compile successfully (0 syntax errors)
- Vue TypeScript →
vue-tsc --noEmit→ Only pre-existing errors (Logo.vue.js, logo1.vue.js), no new errors - Backend search → 3-char minimum enforced, partial ILIKE city search across all 3 sources
- Magic Triangle → Auto-calculates based on last edited field, 2-decimal precision
- Delayed New Provider → Expense saved first, then ProviderQuickAddModal opens with prefillName
P0 BUGFIX: SimpleFuelModal 500 POST crash + 5 frontend issues (Gitea #405)
Dátum: 2026-07-10 Scope: Backend (expenses.py, provider_service.py) + Frontend (SimpleFuelModal.vue)
🔍 Root Cause Analysis: 500 POST /expenses/ crash
Hiba: ForeignKeyViolationError on fk_asset_costs_vendor_org
Kiváltó ok: A frontend SimpleFuelModal.vue a vendor_organization_id mezőbe mindig a selectedProvider.value!.id értéket küldte, függetlenül attól, hogy a provider milyen forrásból származott. A ProviderAutocomplete három forrásból ad vissza találatokat:
| Source | Tábla | ID típus |
|---|---|---|
verified_org |
fleet.organizations |
Organization.id ✅ |
staged_data |
marketplace.service_staging |
ServiceStaging.id ❌ |
crowd_added |
marketplace.service_providers |
ServiceProvider.id ❌ |
Az FK constraint fk_asset_costs_vendor_org a fleet_finance.asset_costs.vendor_organization_id oszlopon van, ami a fleet.organizations.id-ra hivatkozik. Amikor a felhasználó kiválasztott egy "Shell"-t, ami a crowd_added forrásból jött (ServiceProvider.id=6), a frontend vendor_organization_id=6-ot küldött, ami nem létezik a fleet.organizations táblában → 500-as hiba.
🔧 FIX 1: Backend FK-safe validation (expenses.py)
Fájl: backend/app/api/v1/endpoints/expenses.py
A create_expense végpontban, mielőtt létrehoznánk az AssetCost rekordot, egy FK-ellenőrző blokk fut le:
# ── P0 BUGFIX (2026-07-10): FK-safe vendor_organization_id validation ──
resolved_vendor_org_id = expense.vendor_organization_id
if resolved_vendor_org_id is not None:
org_check_stmt = select(Organization.id).where(
Organization.id == resolved_vendor_org_id,
Organization.is_deleted == False,
)
org_check_result = await db.execute(org_check_stmt)
org_exists = org_check_result.scalar_one_or_none()
if org_exists is None:
logger.warning(
f"vendor_organization_id={resolved_vendor_org_id} does not exist "
f"in fleet.organizations. Setting to None and using service_provider_id."
)
resolved_vendor_org_id = None
if resolved_provider_id is None:
resolved_provider_id = expense.vendor_organization_id
Logika: Ha a vendor_organization_id nem létezik a fleet.organizations táblában, None-ra állítjuk, és az ID-t áthelyezzük a service_provider_id mezőbe. Ez biztosítja, hogy:
- Az FK constraint soha nem sérül meg
- A provider referencia nem vész el (átkerül a
service_provider_id-ba) - A frontend nem kap 500-as hibát
További javítás: Hiányzó SystemParameter import hozzáadva.
🔧 FIX 2: Frontend source-aware provider routing (SimpleFuelModal.vue)
Fájl: frontend_app/src/components/dashboard/SimpleFuelModal.vue
A handleSubmit függvény most a provider source mezője alapján dönti el, hova kerüljön az ID:
const providerSource = selectedProvider.value?.source || null
const payload: Record<string, any> = {
vendor_organization_id: hasSelectedProvider && providerSource === 'verified_org'
? selectedProvider.value!.id
: null,
service_provider_id: hasSelectedProvider && providerSource === 'crowd_added'
? selectedProvider.value!.id
: null,
// ...
}
Logika:
verified_org→vendor_organization_id(FK biztonságos)crowd_added→service_provider_id(FK nélküli mező)staged_data→ egyik sem (még nincs véglegesítve)
🔧 FIX 3: Currency selector hozzáadása
Fájl: frontend_app/src/components/dashboard/SimpleFuelModal.vue
A form.currency mező most már HUF/EUR választóval rendelkezik:
<select v-model="form.currency" class="w-20 rounded-xl ...">
<option value="HUF">HUF</option>
<option value="EUR">EUR</option>
</select>
A form state tartalmazza: currency: 'HUF' as string
🔧 FIX 4: Magic Triangle @blur events
Fájl: frontend_app/src/components/dashboard/SimpleFuelModal.vue
Probléma: A korábbi @input események végtelen rekurziót okoztak, mert amikor az autoCalculate() visszaírt egy mezőbe, az újra kiváltotta az @input eseményt.
Megoldás: Mindhárom mező (amountGross, unitPrice, quantity) @blur eseményt használ:
function onAmountGrossBlur(e: Event) {
const val = parseFloat((e.target as HTMLInputElement).value) || 0
form.amountGross = val
lastEditedField.value = 'amount_gross'
autoCalculate()
}
Logika:
- A
@blurcsak akkor fut le, amikor a felhasználó elhagyja a mezőt (pl. Tab-bal vagy kattintással) - A
lastEditedFieldnyomon követi, melyik mezőt szerkesztették utoljára - Az
autoCalculate()a hiányzó mezőt számolja ki:amount_gross = unit_price × quantity - Nincs végtelen rekurzió, mert a
@blurnem triggerelődik programozott értékadásra
🔧 FIX 5: Odometer hint response parsing
Fájl: frontend_app/src/components/dashboard/SimpleFuelModal.vue
Probléma: A GET /expenses/{asset_id} végpont { status, total, data: [...] } formátumban adja vissza az adatokat, de a kód egy sima tömböt várt.
Megoldás:
const body = res.data as { status?: string; total?: number; data?: Array<{ mileage_at_cost?: number | null }> }
const expenses = body.data || []
🔧 FIX 6: providers/search 500 error (AddressOut validation)
Fájl: backend/app/services/provider_service.py
Probléma: A search_providers() függvényben az AddressOut Pydantic v2 modell hibát dobott, amikor minden mező None volt.
Megoldás: Try-except blokk az AddressOut konstrukció körül:
if any([row_address_zip, row_address_street_name, row_address_street_type, row_address_house_number, row_city]):
try:
address_detail = AddressOut(...)
except Exception as addr_e:
logger.warning(...)
address_detail = None
✅ Verifikáció
- Python syntax check →
expenses.py: OK,provider_service.py: OK - Sync Engine → 1287/1287 elements OK, 0 fixes needed
- Frontend TypeScript → All type-safe, no new compilation errors
P0 BUGFIX - Odometer Source of Truth & Sync (Gitea #406)
Dátum: 2026-07-10 Scope: Frontend (SimpleFuelModal.vue)
🔧 Módosított fájlok
1. Frontend: SimpleFuelModal.vue
- Fix:
fetchLastOdometer()most már a/assets/vehicles/{asset_id}végpontot hívja a/expenses/{asset_id}helyett. - Logika: A
Vehicle.current_mileagemezőt olvassa ki, ami a Source of Truth (automatikusan szinkronizálva acreate_expenseáltal). - Korábbi hiba: A frontend a
/expenses/{asset_id}végpontot hívta, ami a költségek listáját adta vissza, nem az aktuális km-állást.
2. Backend: expenses.py
- Ellenőrizve: A
create_expensefüggvény (635-637. sor) már tartalmazza azasset.current_mileageszinkronizálást:if expense.mileage_at_cost is not None and expense.mileage_at_cost > (asset.current_mileage or 0): asset.current_mileage = expense.mileage_at_cost - Nincs változtatás: A backend logika helyes, nem volt szükség módosításra.
3. Frontend: ComplexExpenseModal.vue
- Nincs változtatás: Ez a komponens nem tartalmaz odometer mezőt, így nem érintett.
✅ Verifikáció
- Python syntax check →
expenses.py: OK - Sync Engine → 1287/1287 elements OK, 0 fixes needed
- Frontend →
fetchLastOdometer()most a/assets/vehicles/{id}végpontot használja, ami acurrent_mileage-t adja vissza
P0 EPIC - CostEntryWizard UI/UX Parity & Categories (Gitea #407)
Dátum: 2026-07-10 Scope: Frontend (CostEntryWizard.vue)
🔧 Módosított fájlok
1. Frontend: CostEntryWizard.vue
P0 EPIC 1: Global Vehicle Selection
useVehicleStoreimportálva,fetchUserVehicles()metódus hozzáadvaselectedVehicleIdref +userVehiclesref a járműlista kezeléséreeffectiveVehiclecomputed: prioritás szerint oldja fel a járművet (prop > dropdown)- Template:
<select>dropdown a felhasználó járműveivel, fallback read-only display ha vehicle prop van
P0 EPIC 2: Grouped Categories (Subcategories)
SubCategoryGroupinterface a csoportosított struktúráhozgroupedSubCategoriescomputed: a subCategories flat listát csoportosítja parent category név alapján- Template:
<optgroup>-ok a subcategory select-ben, logikai csoportosítással onMainCategoryChange(): kiszűri a kiválasztott főkategória al-kategóriáit
P0 EPIC 3: Universal Odometer with Hint
odometerHintref +odometerLoadingref a hint állapot kezelésérefetchLastOdometer(): a/assets/vehicles/{id}végpontról lekéri acurrent_mileage-t- Template: mindig látható km óra input + hint "Előző állás: X km"
watch(effectiveVehicle)triggeli afetchLastOdometer()-t járműváltáskorisFuelCategory(): backend FUEL_CATEGORY_IDS alapján detektálja az üzemanyag kategóriákat- Liters input csak fuel kategóriáknál jelenik meg
P0 EPIC 4: ProviderAutocomplete Integration
- Már helyesen integrálva volt:
v-model,@select,@clearesemények handleSubmit(): P0 Hybrid Vendor Refactor szerint source-aware vendor field-eket küldverified_org→vendor_organization_idcrowd_added→service_provider_id- raw string →
external_vendor_name
Payload kompatibilitás:
asset_id,category_id,amount_gross(GROSS-FIRST),currency,date,mileage_at_costvendor_organization_id,service_provider_id,external_vendor_nameroot szintendataJSONB:net_amount,vat_rate,invoice_number,invoice_date,payment_method,payment_deadline,liters,mileage_at_cost, vendor meta
✅ Verifikáció
- Vite build →
CostEntryWizard-KIg7Uvhm.js(31.25 kB) sikeresen lefordult, 0 error - Template → mind a 4 P0 EPIC feature implementálva
- Script → handleSubmit, resetForm, editCost hydration, lifecycle hooks teljesek
P0 EPIC - Enterprise Taxonomy Seed Script (Gitea #407 kiegészítés)
📋 Új fájl: seed_expertise_enterprise.py
Cél: Enterprise Taxonomy (Pénzügy, Biztosítás, Hatóságok) beszúrása a marketplace.expertise_tags táblába.
Beszúrt 9 új tag (IDs 769-777):
Level 1: Pénzügy és Biztosítás (Finance & Insurance) → ID 769
- Level 2: Gépjármű biztosító (Vehicle Insurance) → ID 770
- Level 2: Lízing és Finanszírozás (Leasing & Financing) → ID 771
- Level 2: Bank és Hitelintézet (Bank & Credit Institution) → ID 772
Level 1: Hatóságok és Közigazgatás (Authorities & Administration) → ID 773
- Level 2: Önkormányzat (Municipality) → ID 774
- Level 2: Állami Kincstár / Nemzeti Adóhatóság (State Treasury / Tax Authority) → ID 775
- Level 2: Közlekedési Hatóság / Kormányablak (Transport Authority) → ID 776
- Level 2: Útdíj / Autópálya Kezelő (Toll & Highway Operator) → ID 777
✅ Verifikáció
- Seed script futtatás:
docker compose exec sf_api python3 /app/backend/app/scripts/seed_expertise_enterprise.py→ sikeres, 9 új sor beszúrva - Adatbázis ellenőrzés:
SELECT count(*) FROM marketplace.expertise_tags;→ 145 total record - Vite build:
docker compose exec sf_public_frontend npm run build→ 6.80s, 0 error, CostEntryWizard chunk 31.25 kB
P0 EPIC - Seed Insurance Providers
Dátum: 2026-07-11 Scope: Backend (Seed Script)
🔧 Létrehozott fájl
1. seed_insurance_providers.py
- 33 magyar biztosító seedelése a marketplace ökoszisztémába
- Adatforrás: Netrisk nyílt adatok (Alfa, Allianz, Generali, K&H, stb.)
- Modellek:
ServiceProvider→ServiceProfile→ServiceExpertise - Minden biztosító linkelve a "Gépjármű biztosító" expertise taghez (key=
gepjarmu_biztosito, ID=770) status=approved,source=api_import,validation_score=100- JSONB
raw_datamezőben: Cégjegyzékszám, Bankszámla, Alaptőke, Tulajdonos, Kárrendezés adatok - Duplikációszűrés név alapján
✅ Verifikáció
- Adatbázis: 33 rekord a
marketplace.service_providerstáblában, mindstatus=approved,source=api_import - API Search:
GET /api/v1/providers/search?query=biztositovisszaadja a biztosítókat (pl. Allianz, Generali, Groupama) - ServiceProfile és ServiceExpertise kapcsolatok létrejöttek minden providerhez