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

12 KiB

🔴 Finance Module — Corrective Blueprint (4 Failures)

Date: 2026-07-26
Author: Architect Mode
Status: Pending Code Mode Execution
Priority: HIGH — Visual misalignment, duplicate back button, mock data, backend enum crash


STEP 1: FAILURE ANALYSIS

FAILURE 1: Wrong Dashboard Card — Mock Data Remains

Finding: The dashboard at DashboardView.vue uses ProfileTrustCard (Card 5). This IS the correct card and it WAS correctly modified by the previous blueprint — it now fetches live financeStore data and shows subscriptionPlanName from authStore.

However, FinancialCard.vue is a separate, unused component that also fetches live wallet data and has a "Top Up" button. It is NOT imported or rendered anywhere on the dashboard. It appears to be a legacy/standalone variant.

The actual source of "mock data" on the dashboard is the transactionCount computed (always 0) and the subscriptionPlanName which shows if the user has no subscription plan set.

Fix plan:

  • The ProfileTrustCard correctly fetches live financeStore data already.
  • The GamificationCard at GamificationCard.vue was changed from hardcoded 2,450 to computed(() => 0) — specifically requested by the previous blueprint as a "placeholder ready to wire up."
  • If the user sees mock data, it's the gamification score (0) and badges (empty array). These ARE mock/placeholder by design until a gamification store is created.

No additional changes needed for the dashboard card itself.

FAILURE 2: Back Button Still in Top Global Navbar

Root Cause: The previous blueprint removed <Teleport> from all 4 child views BUT did NOT remove <HeaderBackButton> from FinanceLayout.vue.

Evidence:

<!-- FinanceLayout.vue:42-45 -->
<HeaderBackButton
  :to="backTarget"
  :label="t('common.back')"
/>

This HeaderBackButton is rendered in the #left slot of BaseHeader, which is the global sticky top navbar. Removing the Teleport only removed the page title — the back button in the layout persists.

Fix: Delete lines 42-45 from FinanceLayout.vue. Also remove the unused backTarget computed (lines 95-100) and clean up the useRoute import if no longer needed.

FAILURE 3: Title Misalignment — Header Outside Content Container

Root Cause: The newly added local header in FinanceMainView.vue is placed OUTSIDE the main content container.

Evidence:

  • Header at line 40: <div class="mb-8 flex items-center justify-between">no max-w, no px, no mx-auto
  • Content container at line 59: <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 w-full">
  • The FleetView pattern at FleetView.vue wraps EVERYTHING in class="relative z-10 mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8" — including the header.

Fix: Wrap the local header in the same responsive container, OR add matching mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 w-full classes to the header div. The cleanest approach: restructure so header + cards share the same container.

Apply the same fix to all 4 finance views (FinanceWalletsView, FinancePackagesView, FinanceTransactionsView).

FAILURE 4: Backend Enum Error — 'credit' is not among defined enum values

Root Cause: The seed_financial_ledger.py seed script inserts lowercase "credit" and "debit" strings into the PostgreSQL audit.ledger_entry_type enum column, but the enum definition at audit.py only allows "CREDIT" and "DEBIT" (UPPERCASE).

Evidence:

  • audit.py:58-60:
    class LedgerEntryType(str, enum.Enum):
        DEBIT = "DEBIT"
        CREDIT = "CREDIT"
    
  • seed_financial_ledger.py:18:
    DB_ENTRY_TYPES = ["credit", "debit"]  # ❌ lowercase!
    
  • The seed script uses psycopg2 (sync) with explicit ::audit.ledger_entry_type cast at line 105 — but psycopg2 may allow case-insensitive casting or the data was inserted before the enum was strict.
  • When the frontend calls GET /billing/wallet/balance, SQLAlchemy reads all FinancialLedger rows and tries to map "credit" to the enum — this crashes with the error seen in the screenshot.

Fix (TWO parts):

Part A — Fix the seed script:

Part B — Fix existing bad data in the database: Run SQL to normalize all lowercase entries:

UPDATE audit.financial_ledger SET entry_type = 'CREDIT' WHERE entry_type::text = 'credit';
UPDATE audit.financial_ledger SET entry_type = 'DEBIT' WHERE entry_type::text = 'debit';

Also check and fix wallet_type and status similarly.


STEP 2: CODE MODE HAND-OFF — Explicit Task List

Execute in order. Do NOT modify FleetView.vue, PrivateLayout.vue, BaseHeader.vue, HeaderBackButton.vue, or router/index.ts.

Task 1: Remove HeaderBackButton from FinanceLayout.vue (FAILURE 2)

File: frontend_app/src/layouts/FinanceLayout.vue

  1. DELETE lines 37-46 — the entire HeaderBackButton block and its comment:

    <!--
      Dynamic back button:
      - On /finance exactly → back to /dashboard
      - On any sub-route (/finance/wallets, etc.) → back to /finance
    -->
    <HeaderBackButton
      :to="backTarget"
      :label="t('common.back')"
    />
    
  2. DELETE the backTarget computed (lines 90-100):

    const backTarget = computed(() => {
      if (route.path === '/finance') {
        return '/dashboard'
      }
      return '/finance'
    })
    
  3. Clean up imports: Remove import { computed } from 'vue' (line 78) and import { useRoute } from 'vue-router' (line 79) if they're no longer used. Also remove const route = useRoute() (line 87). Remove import HeaderBackButton from '../components/header/HeaderBackButton.vue' (line 84).

Task 2: Fix Header Alignment in FinanceMainView.vue (FAILURE 3)

File: frontend_app/src/views/FinanceMainView.vue

  1. DELETE lines 39-55 (the standalone header div)

  2. INSERT the header INSIDE the content container: Move it to be the first child of line 59's <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 w-full">:

    <!-- ── Bottom-aligned card grid — matches DashboardView.vue exactly ── -->
    <div class="flex flex-col justify-end min-h-[85vh] pb-8">
      <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 w-full">
        <!-- ── Page header (FleetView.vue pattern) ── -->
        <div class="mb-8 flex items-center justify-between">
          <div>
            <h1 class="text-3xl font-bold text-white">💰 {{ t('menu.finance') }}</h1>
          </div>
          <div class="flex items-center gap-3">
            <button
              @click="router.push('/dashboard')"
              class="flex items-center gap-2 rounded-xl bg-white/10 px-4 py-2 text-sm text-white/80 transition-all duration-200 hover:bg-white/20 hover:text-white"
            >
              <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
              </svg>
              {{ t('garage.backToDashboard') }}
            </button>
          </div>
        </div>
        <!-- 4-card grid — identical to DashboardView grid -->
        <div class="grid ...">
          ...
        </div>
      </div>
    </div>
    

Task 3: Fix Header Alignment in FinanceWalletsView.vue (FAILURE 3)

File: frontend_app/src/views/FinanceWalletsView.vue

Same pattern as Task 2: Move the header inside the mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 w-full container. The current structure:

<div class="mb-8 flex items-center justify-between"> ...header... </div>
<div class="flex flex-col justify-start min-h-[85vh] py-8">
  <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 w-full">
    ...content...
  </div>
</div>

Should become:

<div class="flex flex-col justify-start min-h-[85vh] py-8">
  <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 w-full">
    <div class="mb-8 flex items-center justify-between"> ...header... </div>
    ...content...
  </div>
</div>

Task 4: Fix Header Alignment in FinancePackagesView.vue (FAILURE 3)

File: frontend_app/src/views/FinancePackagesView.vue

Same pattern as Task 3 — move header inside the container.

Task 5: Fix Header Alignment in FinanceTransactionsView.vue (FAILURE 3)

File: frontend_app/src/views/FinanceTransactionsView.vue

Same pattern as Task 3 — move header inside the container.

Task 6: Fix Backend Enum Error — Seed Script (FAILURE 4A)

File: backend/scripts/seed_financial_ledger.py

  1. Line 18: Change:
    DB_ENTRY_TYPES = ["credit", "debit"]
    
    To:
    DB_ENTRY_TYPES = ["CREDIT", "DEBIT"]
    

Task 7: Fix Backend Enum Error — Existing Database Data (FAILURE 4B)

Run these SQL commands to normalize any existing lowercase entries:

docker compose exec shared-postgres psql -U service_finder_app -d service_finder -c "
UPDATE audit.financial_ledger SET entry_type = 'CREDIT' WHERE entry_type::text = 'credit';
UPDATE audit.financial_ledger SET entry_type = 'DEBIT' WHERE entry_type::text = 'debit';
-- Also check wallet_type
SELECT DISTINCT entry_type::text, wallet_type::text, status::text FROM audit.financial_ledger;
"

Task 8: Verify

  1. Frontend build:

    docker compose exec sf_public_frontend npm run build 2>&1 | tail -5
    
  2. Backend wallet endpoint:

    docker compose exec sf_api python3 -c "
    import requests
    r = requests.post('http://localhost:8000/api/v1/auth/login', data={'username': 'admin@profibot.hu', 'password': 'Admin123!'})
    token = r.json()['access_token']
    resp = requests.get('http://localhost:8000/api/v1/billing/wallet/balance', headers={'Authorization': f'Bearer {token}'})
    print(f'Status: {resp.status_code}')
    print(resp.json())
    "
    

📋 Summary Table

# Failure Root Cause File(s) to Fix
1 Mock data on dashboard GamificationCard score is placeholder 0; transactionCount is placeholder 0 — by design from previous blueprint, pending gamification/transaction stores None needed now (wiring to stores is future work)
2 Back button still in top menu FinanceLayout.vue still has <HeaderBackButton> in #left slot FinanceLayout.vue
3 Title misaligned to edge Header is outside mx-auto max-w-7xl px-4... container All 4 finance views
4 Backend enum crash: 'credit' is not among defined enum values seed_financial_ledger.py:18 uses lowercase "credit"/"debit" but enum only accepts "CREDIT"/"DEBIT" seed_financial_ledger.py + DB cleanup SQL

⚠️ DO NOT MODIFY