Files
service-finder/.roo/history.md
2026-07-20 11:17:52 +00:00

16 KiB
Raw Blame History

Verifikáció

  1. Build ellenőrzésnpx vite build sikeres (7.08s, 0 error)
  2. Chunk generálva: CostEntryWizard-C_-R4OzC.js (29.95 kB) - refaktorált komponens helyesen fordul
  3. 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_length changed from 23
  • Hibaüzenet frissítve: "min. 3 karakter"

2. Backend: provider_service.py

  • City search changed from StartsWith ({city}%) to ILIKE partial (%{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: 23 characters
  • Empty state condition updated: searchQuery.length >= 3

4. Frontend: ProviderQuickAddModal.vue

  • New prefillName?: string prop added
  • Watch on isOpen now sets form.name = props.prefillName when provided

5. Frontend: SimpleFuelModal.vue (complete rewrite)

Magic Triangle (Auto-calculation):

  • 3 fields: amountGross, unitPrice, quantity
  • Tracks lastEditedField to 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_cost as a hint below the odometer input

Delayed New Provider Flow:

  • If user types a raw vendor name without selecting a provider:
    1. Expense is saved with external_vendor_name set
    2. unit_price and quantity are sent in data JSONB payload
    3. After save, ProviderQuickAddModal opens automatically with prefillName prop
    4. User can create the provider with the name pre-filled

Verifikáció

  1. Sync Enginedocker exec sf_api python -m app.scripts.sync_engine → 1287/1287 elements OK, 0 error
  2. Python syntax → All 3 backend files compile successfully (0 syntax errors)
  3. Vue TypeScriptvue-tsc --noEmit → Only pre-existing errors (Logo.vue.js, logo1.vue.js), no new errors
  4. Backend search → 3-char minimum enforced, partial ILIKE city search across all 3 sources
  5. Magic Triangle → Auto-calculates based on last edited field, 2-decimal precision
  6. 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:

  1. Az FK constraint soha nem sérül meg
  2. A provider referencia nem vész el (átkerül a service_provider_id-ba)
  3. 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_orgvendor_organization_id (FK biztonságos)
  • crowd_addedservice_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:

  1. A @blur csak akkor fut le, amikor a felhasználó elhagyja a mezőt (pl. Tab-bal vagy kattintással)
  2. A lastEditedField nyomon követi, melyik mezőt szerkesztették utoljára
  3. Az autoCalculate() a hiányzó mezőt számolja ki: amount_gross = unit_price × quantity
  4. Nincs végtelen rekurzió, mert a @blur nem 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ó

  1. Python syntax checkexpenses.py: OK, provider_service.py: OK
  2. Sync Engine → 1287/1287 elements OK, 0 fixes needed
  3. 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_mileage mezőt olvassa ki, ami a Source of Truth (automatikusan szinkronizálva a create_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_expense függvény (635-637. sor) már tartalmazza az asset.current_mileage szinkronizá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ó

  1. Python syntax checkexpenses.py: OK
  2. Sync Engine → 1287/1287 elements OK, 0 fixes needed
  3. FrontendfetchLastOdometer() most a /assets/vehicles/{id} végpontot használja, ami a current_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

  • useVehicleStore importálva, fetchUserVehicles() metódus hozzáadva
  • selectedVehicleId ref + userVehicles ref a járműlista kezelésére
  • effectiveVehicle computed: 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)

  • SubCategoryGroup interface a csoportosított struktúrához
  • groupedSubCategories computed: 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

  • odometerHint ref + odometerLoading ref a hint állapot kezelésére
  • fetchLastOdometer(): a /assets/vehicles/{id} végpontról lekéri a current_mileage-t
  • Template: mindig látható km óra input + hint "Előző állás: X km"
  • watch(effectiveVehicle) triggeli a fetchLastOdometer()-t járműváltáskor
  • isFuelCategory(): 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, @clear események
  • handleSubmit(): P0 Hybrid Vendor Refactor szerint source-aware vendor field-eket küld
    • verified_orgvendor_organization_id
    • crowd_addedservice_provider_id
    • raw string → external_vendor_name

Payload kompatibilitás:

  • asset_id, category_id, amount_gross (GROSS-FIRST), currency, date, mileage_at_cost
  • vendor_organization_id, service_provider_id, external_vendor_name root szinten
  • data JSONB: net_amount, vat_rate, invoice_number, invoice_date, payment_method, payment_deadline, liters, mileage_at_cost, vendor meta

Verifikáció

  1. Vite buildCostEntryWizard-KIg7Uvhm.js (31.25 kB) sikeresen lefordult, 0 error
  2. Template → mind a 4 P0 EPIC feature implementálva
  3. 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ó

  1. Seed script futtatás: docker compose exec sf_api python3 /app/backend/app/scripts/seed_expertise_enterprise.py → sikeres, 9 új sor beszúrva
  2. Adatbázis ellenőrzés: SELECT count(*) FROM marketplace.expertise_tags; → 145 total record
  3. 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: ServiceProviderServiceProfileServiceExpertise
  • 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_data mező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ó

  1. Adatbázis: 33 rekord a marketplace.service_providers táblában, mind status=approved, source=api_import
  2. API Search: GET /api/v1/providers/search?query=biztosito visszaadja a biztosítókat (pl. Allianz, Generali, Groupama)
  3. ServiceProfile és ServiceExpertise kapcsolatok létrejöttek minden providerhez