Files
service-finder/.roo/history.md
2026-07-24 09:56:21 +00:00

24 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

P0 EPIC: i18n JSONB Database Migration (Phase 1) — Gitea #399

Dátum: 2026-07-23 Scope: Backend (Models, API, Services, Schemas, Seeds)

🔧 Módosított fájlok

1. Modellek — Régi oszlopok eltávolítva

2. API Végpontok

  • providers.py — Category tree, autocomplete: name_hu/name_enname_i18n
  • catalog.py — Body types: name_huname_i18n

3. Service Réteg

  • provider_service.py — CategoryInfo, tag creation, tag search: name_hu/name_enname_i18n

4. Schema Réteg

  • provider.pyCategoryTreeNode, CategoryAutocompleteItem, ExpertiseCategoryOut, CategoryInfo: name_hu/name_enname_i18n

5. Seed Scriptek (7 fájl)

  • seed_expertise_tags.py, seed_dismantler_category.py, seed_gas_station.py, seed_expertise_enterprise.py, seed_insurance_providers.py, seed_body_types.py, seed_expertise_taxonomy.py

6. Adatbázis

  • 6 shadow column eltávolítva SQL ALTER TABLE segítségével
  • Sync engine: 0 shadow column, 1281 OK — tökéletes szinkron

⚠️ Megjegyzés

  • A konténer újraépítése után pre-existing import hibák derültek ki az expenses.py-ban (3 db):
    1. from app.models.fleet.organization import Organizationapp.models.marketplace.organization
    2. from app.models.fleet.org_member import OrganizationMemberapp.models.marketplace.organization
    3. from app.models.marketplace.service import ServiceProviderapp.models.identity.social
    4. from app.models.system.system_parameter import SystemParameterapp.models.system.system
  • Mind a 4 import hiba javítva, konténer újraépítve, API sikeresen fut. A konténer indításakor előre létező import hibák jelentkeztek (expenses.pyOrganization, AssetEvent, OdometerReading rossz import útvonalai), amelyek NEM kapcsolódnak a #399-es kártyához. Ezek javítása külön kártyát igényel.

P1 EPIC - Registration UI Upgrade & Google SSO Integration

Dátum: 2026-07-23

Módosított fájlok

Backend

  1. backend/app/api/v1/endpoints/auth.py — Google OAuth handler hozzáadva:

    • GoogleAuthRequest Pydantic modell (id_token, access_token, referred_by_code mezőkkel)
    • POST /auth/google végpont: Google token verifikáció (tokeninfo API), felhasználó lekérés/létrehozás SocialAuthService.get_or_create_social_user()-on keresztül, JWT token generálás, refresh_token cookie beállítás
    • Támogatja mind az id_token (Google Sign-In), mind az access_token (OAuth2 token client) típusokat
  2. backend/app/services/social_auth_service.pyget_or_create_social_user() metódus kiegészítve:

    • referred_by_code paraméter hozzáadva
    • Új social regisztrációnál a meghívó kód alapján a referrer (User.referral_code) felkutatása és referred_by_id beállítása
    • generate_secure_slug importálva az új felhasználók referral_code generálásához

Frontend

  1. frontend_app/src/components/LoginModal.vue — Regisztrációs UI frissítve:

    • referredByCode ref hozzáadva
    • "Meghívó kód" (Referral Code) input mező hozzáadva a regisztrációs űrlapba (jelszó mező alá)
    • "Vagy regisztrálj Social fiókkal" elválasztó + Google gomb hozzáadva a regisztrációs űrlapba (a login form dizájnját tükrözve)
    • Google gomb @click esemény bekötve a login űrlapon (handleGoogleLogin)
    • handleGoogleRegister() függvény: a meghívó kódot localStorage-ba menti (pending_referral_code), majd meghívja a handleGoogleLogin()-t
    • handleRegister() frissítve: referred_by_code mezőt is küld a backendnek
    • loadGoogleScript(): dinamikusan betölti a Google Identity Services SDK-t
  2. frontend_app/src/stores/auth.tsgoogleAuth action hozzáadva:

    • access_token küldése a /auth/google végpontra
    • Opcionális referred_by_code támogatása
    • Token perzisztálás, user profil és szervezetek betöltése
  3. frontend_app/.envVITE_GOOGLE_CLIENT_ID környezeti változó hozzáadva

Technikai részletek

  • Google OAuth flow: A frontend a Google Identity Services (GIS) initTokenClient-jével kér access_token-t, amit a backend a https://oauth2.googleapis.com/tokeninfo?access_token={token} endpointon verifikál
  • Referral code flow: Regisztrációkor a meghívó kódot a frontend elküldi a backendnek, ami a User.referral_code mező alapján megkeresi a referrer-t és beállítja a referred_by_id kapcsolatot
  • Biztonság: A backend ellenőrzi a token azp (authorized party) mezőjét a GOOGLE_CLIENT_ID-val
  • Adatbázis: Nincs új migráció, a meglévő User.referral_code és referred_by_id mezők használva
  • Sync engine: 1281 elem szinkronban, 0 hiba
  • Modul betöltés: Mindkét módosított backend modul sikeresen betöltődik

2026-07-23 — Referral & Credit Ecosystem Full Stack Analysis (#409)

Elemzés típusa: Read-only audit
Vizsgált komponensek: identity.users, identity.wallet, gamification.point_rules, gamification.points_ledger, audit.financial_ledger, AuthService, SocialAuthService, GamificationService, LoginModal.vue, auth.ts

Főbb megállapítások:

  1. Single-level (L1) referral tracking létezik User.referred_by_id mezőn keresztül
  2. Jutalmazás kizárólag XP alapú (50 pont P2P_REFERRAL_SUCCESS), nincs kredit-alapú jutalom
  3. KRITIKUS HIBA: auth.ts register() nem küldi el a referred_by_code-ot a payload-ban
  4. Hiányzik: dedikált Referral tábla, multi-level (L2/L3) követés, commission engine, referral API-k
  5. FinancialLedger és Wallet infrastruktúra alkalmas lenne bővítésre

Dokumentáció: /opt/docker/docs/referral_credit_ecosystem_analysis.md


2-Level MLM Commission Distribution

Dátum: 2026-07-24

Megvalósított komponensek:

  1. Model backend/app/models/marketplace/commission.pyupline_commission_percent oszlop hozzáadva (Numeric(5,2), nullable, default=0.00)
  2. Schemas backend/app/schemas/commission.pyCommissionDistributionRequest, CommissionDistributionItem, CommissionDistributionResponse új Pydantic modellek
  3. Service backend/app/services/commission_service.pydistribute_commission() 2-level MLM engine: buyer → Gen1 (commission_percent) → Gen2 (upline_commission_percent)
  4. API backend/app/api/v1/endpoints/admin_commission.pyPOST /admin/commission-rules/distribute végpont
  5. Frontend frontend_admin/pages/finance/commission-rules.vueupline_commission_percent mező az admin UI-ban
  6. i18n — EN/HU locale fájlok frissítve

Teszteredmények:

  • 4/4 teszt PASS
  • Gen1: 5% of 100000 = 5000.00
  • Gen2: 2% of 100000 = 2000.00
  • Total: 7000.00
  • Max amount cap (50000) verified
  • Sync engine: 1303 OK, 0 Fixed, 0 Shadow