@extends('layouts.app') @section('title', 'Cash Flow Ledger — Financial OS') @push('styles') {{-- Tom Select CSS for searchable dropdowns --}} @endpush @section('content') @php $user = Auth::user(); $canModify = $canModify ?? true; $userCurrency = $user->base_currency ?? 'USD'; $currencySymbols = [ 'USD' => '$', 'EUR' => '€', 'GBP' => '£', 'OMR' => 'ر.ع.', 'AED' => 'د.إ', 'SAR' => 'ر.س', 'PKR' => '₨', 'INR' => '₹' ]; $currencySymbol = $currencySymbols[$userCurrency] ?? $userCurrency; $totalIncome = $incomeTransactions->sum('amount') ?? 0; $totalExpenses = $expenseTransactions->sum('amount') ?? 0; $totalLiabilities= $liabilityTransactions->sum('amount') ?? 0; $netCashFlow = $totalIncome - $totalExpenses; $savingsRate = $totalIncome > 0 ? (($totalIncome - $totalExpenses) / $totalIncome) * 100 : 0; $defaultCategoryId = $defaultCategoryId ?? null; $defaultAccountId = $defaultAccountId ?? null; $defaultAmount = $defaultAmount ?? ''; $defaultDescription= $defaultDescription?? ''; $defaultMerchant = $defaultMerchant ?? ''; $activeTab = $activeTab ?? 'expense'; $prevMonth = ''; // ── FIX 4: Build a correct running balance per-account lookup ── // We load the transactions once; the table iterates them in DESC date order. // We calculate the running balance going BACKWARDS from current_balance. // current_balance already reflects all transactions up to NOW. // As we walk backwards (newest → oldest), we un-apply each transaction // to get the balance BEFORE that transaction. // We display the balance AFTER the transaction (i.e. the running total at that point). // Group current balances by account id $accountBalances = $financialAccounts->pluck('current_balance', 'id')->toArray(); // We'll track per-account running balance in a plain PHP array (no static) $runningBalances = []; // [ account_id => float ] $balanceInitialized = []; // [ account_id => bool ] @endphp @php $isEditing = request()->has('edit') || (isset($editMode) && $editMode && isset($editTransactionId)); $editingId = request()->get('edit') ?? ($editTransactionId ?? null); @endphp {{-- TOASTS --}} @if(session('success'))
{{ session('success') }}
@endif @if(session('error'))
{{ session('error') }}
@endif @if(session('info'))
{{ session('info') }}
@endif {{-- KPI CARDS --}}
Total Inflow
{{ $currencySymbol }}{{ number_format($totalIncome,2) }}
{{ $userCurrency }}
INCOME
Total Outflow
{{ $currencySymbol }}{{ number_format($totalExpenses,2) }}
{{ $userCurrency }}
EXPENSES
Total Liability
{{ $currencySymbol }}{{ number_format($totalLiabilities,2) }}
{{ $userCurrency }}
LIABILITIES
Net Position
{{ $currencySymbol }}{{ number_format($netCashFlow,2) }}
Savings: {{ number_format($savingsRate,1) }}%
{{ $netCashFlow>=0?'SURPLUS':'DEFICIT' }}
{{-- EDIT MODE BANNER --}} @if($isEditing && $editingId)
EDIT MODE ACTIVE Editing Transaction #{{ str_pad($editingId,6,'0',STR_PAD_LEFT) }}
Cancel Edit
@endif {{-- ═══════════════════════════════════════════════════ NEW / EDIT TRANSACTION FORM ════════════════════════════════════════════════════ --}} @if($canModify && ($incomeCategories->count() > 0 || $expenseCategories->count() > 0))

{{ $isEditing ? 'Edit Transaction' : 'New Transaction' }}

{{-- ── Tab Switcher ── --}}
{{-- Regular Transaction Form --}}
@csrf @if($isEditing && $editingId) @method('PUT') @endif {{-- Category - Column 1 --}}
{{-- Account - Column 2 --}}
{{-- Amount - Column 1 --}}
@foreach([10,50,100,500,1000] as $qa) @endforeach
{{-- Date - Column 2 --}}
{{-- Description - Column 1 --}}
{{-- Merchant / Payee - Column 2 --}}
{{-- Recurring toggle - Full width --}}
{{-- ── FIX 3: Transfer Form – searchable accounts + auto-today date ── --}}
@csrf {{-- From Account – searchable, NO credit cards --}}
{{-- To Account – searchable, NO credit cards --}}
{{-- Amount --}}
@foreach([10,50,100,500,1000] as $qa) @endforeach
{{-- FIX 3: Auto-today date --}}
{{-- Description --}}
{{-- Recurring toggle --}}
{{-- end coa-card-body --}}
@endif {{-- FILTERS --}}
All Inflows Outflows Transfers Liabilities
Clear
{{-- ═══════════════════════════════════════════════════ LEDGER TABLE ════════════════════════════════════════════════════ --}}
@forelse($transactions as $tx) @php $mon = $tx->transaction_date->format('F Y'); $account = $financialAccounts->firstWhere('id', $tx->financial_account_id); $accountCurrency = $account->currency ?? $userCurrency; $txSymbol = $currencySymbols[$accountCurrency] ?? $accountCurrency; $isLiability= $tx->transaction_type === 'credit_card_payment'; $isExpense = $tx->transaction_type === 'expense'; $isIncome = $tx->transaction_type === 'income'; $isTransfer = $tx->transaction_type === 'transfer'; // ── FIX 4: Correct running balance ── // Transactions are ordered DESC (newest first). // We start from current_balance and walk backwards, // REVERSING each transaction to get the pre-tx balance. // We show the post-tx balance (i.e. after tx applied). $acctId = $account->id ?? null; if ($acctId) { // Initialise from live account balance on first encounter of this account if (!isset($runningBalances[$acctId])) { $runningBalances[$acctId] = (float)($accountBalances[$acctId] ?? 0); } // The balance AFTER this transaction = current running value $displayBalance = $runningBalances[$acctId]; // Now reverse this transaction to get the balance BEFORE it // (so the next older transaction knows where to start) if ($isIncome) { // Income added to balance → reverse: subtract $runningBalances[$acctId] -= (float)$tx->amount; } elseif ($isExpense || $isLiability) { // Expense/liability reduced balance → reverse: add back $runningBalances[$acctId] += (float)$tx->amount; } elseif ($isTransfer) { if (str_contains($tx->description ?? '', 'Transfer to')) { // Source side: reduced balance → add back $runningBalances[$acctId] += (float)$tx->amount; } elseif (str_contains($tx->description ?? '', 'Transfer from')) { // Destination side: increased balance → subtract $runningBalances[$acctId] -= (float)$tx->amount; } } } else { $displayBalance = 0; } // Badge if ($isIncome) { $typeClass='badge-income'; $displayType='INCOME'; } elseif ($isExpense) { $typeClass='badge-expense'; $displayType='EXPENSE'; } elseif ($isTransfer) { $typeClass='badge-transfer'; $displayType='TRANSFER'; } elseif ($isLiability) { $typeClass='badge-liability'; $displayType='LIABILITY'; } else { $typeClass='badge-expense'; $displayType=strtoupper(str_replace('_',' ',$tx->transaction_type)); } @endphp @if($mon != $prevMonth) @php $prevMonth = $mon; @endphp @endif @empty @endforelse
Date Ref Category Account Type Outflow Inflow Liability Transfer Balance Actions
{{ $mon }}
{{ $tx->transaction_date->format('d M Y') }} #{{ str_pad($tx->id,6,'0',STR_PAD_LEFT) }} {{ $tx->category?->name ?? '—' }} @if($tx->description)
{{ Str::limit(e($tx->description),35) }}
@endif
{{ $account->account_name ?? '—' }}
{{ $txSymbol }}{{ number_format($account->current_balance ?? 0, 2) }}
{{ $displayType }} {{ (!$isIncome && !$isTransfer && !$isLiability) ? $txSymbol.number_format($tx->amount,2) : '—' }} {{ $isIncome ? $txSymbol.number_format($tx->amount,2) : '—' }} {{ $isLiability ? $txSymbol.number_format($tx->amount,2) : '—' }} {{ $isTransfer ? $txSymbol.number_format($tx->amount,2) : '—' }} {{ $txSymbol }}{{ number_format($displayBalance,2) }}
{{-- Edit --}} {{-- Audit --}} {{-- Repeat --}} {{-- FIX 4: PDF export – use window.location so session/auth cookie is sent, not target=_blank which can drop cookie in some browsers --}} {{-- FIX 2: Pass isTransfer flag so modal shows correct warning and delete route --}}

No transactions found.

@if(($transactions ?? collect())->hasPages()) @endif {{-- DELETE MODAL --}} {{-- ═══════════════════════════════════════════════════ JAVASCRIPT ════════════════════════════════════════════════════ --}} @endsection