Files
service-finder/plans/bugfix_quad_blueprint_2026-07-26.md
2026-07-27 08:39:18 +00:00

7.0 KiB

🔧 Code Mode Hand-off Blueprint: 4-Bug Fix Bundle

Created: 2026-07-26 | Mode: Architect → Code
Target Branch: main


📊 Root Cause Analysis Summary

# Bug Root Cause File Severity
1 Logo navigation traps user in org scope HeaderLogo.vue:33 Medium
2 GET /wallet/balance → 500 for user 106 billing_engine.py:625-633 Critical
3 "705,000" shown for all users (N/A — no mock found) N/A — see analysis below Info
4 [intlify] Not found 'subscription.vehicles' SubscriptionStatusWidget.vue:67,81 Low

🐛 Bug 1: Logo Navigation — Always Redirect to /dashboard

File

frontend_app/src/components/header/HeaderLogo.vue

Current Behavior (Lines 32-38)

const targetRoute = computed(() => {
  if (route.path.startsWith('/organization/')) {
    const orgId = route.params.id
    return orgId ? `/organization/${orgId}` : route.path
  }
  return '/dashboard'
})

When user is on /organization/5/vehicles, route.path.startsWith('/organization/') is true, so the logo navigates to /organization/5 — the org root — NOT to /dashboard. This traps the user; there is no way to return to the private garage dashboard via the logo.

Root Cause

The logo's targetRoute logic intentionally scopes the user to the org when on org pages, but this prevents escaping back to /dashboard.

Fix

Replace the entire targetRoute computed with:

const targetRoute = computed(() => '/dashboard')

Rationale: The HeaderCompanySwitcher component already provides organization context switching. The logo should always be a "global home" escape hatch. This follows the web standard: clicking the brand logo always returns to the root dashboard.

Lines to Change

  • DELETE: Lines 32-38
  • REPLACE WITH: Single line const targetRoute = computed(() => '/dashboard')

Verification

  1. Log in as any user
  2. Navigate to /organization/:id/vehicles
  3. Click the HeaderLogo → must navigate to /dashboard (not stay on org pages)

🐛 Bug 2: Backend 500 Error — float(None) on Wallet Balance

File

backend/app/services/billing_engine.pyAtomicTransactionManager.get_wallet_summary()

Current Behavior (Lines 622-635)

return {
    "wallet_id": wallet.id,
    "balances": {
        "earned": float(wallet.earned_credits),        # 💥 CRASHES if None
        "purchased": float(wallet.purchased_credits),   # 💥 CRASHES if None
        "service_coins": float(wallet.service_coins),    # 💥 CRASHES if None
        "voucher": float(voucher_balance),
        "total": float(
            wallet.earned_credits +   # 💥 CRASHES if None
            wallet.purchased_credits +
            wallet.service_coins +
            voucher_balance
        )
    },

Root Cause

The Wallet model defines credit columns as:

earned_credits: Mapped[float] = mapped_column(Numeric(18, 4), server_default=text("0"))

server_default only applies at the DB level on INSERT. If a wallet row was created with explicit None values, or if the DB column contains NULL (e.g., from a migration or manual SQL), then wallet.earned_credits will be Python None. Calling float(None) raises TypeError, which falls through to the generic Exception handler returning HTTP 500.

Also: if the wallet row does NOT exist at all, get_wallet_summary() raises ValueError(f"Wallet not found for user_id={user_id}") → which IS caught as 404. So the 500 specifically means: wallet row EXISTS but has NULL credit columns.

Fix

Option A (Defensive — Preferred): Wrap all float() calls with or 0

Change lines 625-634 to:

"earned": float(wallet.earned_credits or 0),
"purchased": float(wallet.purchased_credits or 0),
"service_coins": float(wallet.service_coins or 0),
"voucher": float(voucher_balance or 0),
"total": float(
    (wallet.earned_credits or 0) +
    (wallet.purchased_credits or 0) +
    (wallet.service_coins or 0) +
    (voucher_balance or 0)
)

Option B (Root Fix — also recommended): Run a DB repair script to fix NULL columns

UPDATE identity.wallets SET earned_credits = 0 WHERE earned_credits IS NULL;
UPDATE identity.wallets SET purchased_credits = 0 WHERE purchased_credits IS NULL;
UPDATE identity.wallets SET service_coins = 0 WHERE service_coins IS NULL;

BOTH Option A and Option B should be applied.

Verification

  1. Find NULL wallets: SELECT id, user_id FROM identity.wallets WHERE earned_credits IS NULL OR purchased_credits IS NULL OR service_coins IS NULL;
  2. Apply both fixes
  3. GET /api/v1/billing/wallet/balance with JWT for user 106 → must return 200

🐛 Bug 3: "705,000" Mock Data — NOT a Code Issue

Finding

No mock/hardcoded data was found in any frontend file. Searched all .vue, .ts, and .json files for 705000 — zero results.

The finance store properly fetches from the API and has zero-fallback:

const totalCredits = computed(() => balance.value?.total ?? 0)

The "705,000" value is from a real database record — likely the admin test user's wallet. User 106 sees an error because their wallet has NULL credits. Once Bug 2 is fixed, user 106 will see 0 (either no wallet → creates one, or NULL columns → returned as 0).

Action

No code change needed for Bug 3. Fixing Bug 2 resolves the 500 error cascade.


🐛 Bug 4: Missing i18n Keys in SubscriptionStatusWidget

File

frontend_app/src/components/dashboard/SubscriptionStatusWidget.vue

Missing Keys (Lines 52, 53, 67, 81)

  • subscription.vehicles (Line 67, 81)
  • subscription.unlimited (Line 81)
  • subscription.timeRemaining (Line 52)
  • subscription.days (Line 53)

None of these exist in hu.ts or en.ts.

Fix

Add to BOTH frontend_app/src/i18n/hu.ts AND frontend_app/src/i18n/en.ts inside the subscription block, after indefinite:

hu.ts — add:

vehicles: 'jármű',
unlimited: 'korlátlan',
timeRemaining: 'Hátralévő idő',
days: 'nap',

en.ts — add:

vehicles: 'vehicles',
unlimited: 'unlimited',
timeRemaining: 'Time Remaining',
days: 'days',

📋 Execution Order (Code Mode)

  1. Bug 1 — HeaderLogo.vue: Change targetRoute to always return /dashboard
  2. Bug 4 — hu.ts + en.ts: Add 4 missing i18n keys
  3. Bug 2 — billing_engine.py: Add or 0 to all float() conversions
  4. Bug 2 (DB) — Run SQL to NULL→0 on identity.wallets credit columns
  5. Bug 3 — Verify fix, no code change needed

🔗 Files Summary

File Bug Operation
frontend_app/src/components/header/HeaderLogo.vue #1 Replace lines 32-38
frontend_app/src/i18n/hu.ts #4 Add 4 keys after line 83
frontend_app/src/i18n/en.ts #4 Add 4 keys after line 83
backend/app/services/billing_engine.py #2 Modify lines 625-634
identity.wallets (DB) #2 SQL UPDATE NULL→0

Blueprint ready for Code Mode hand-off.