diff --git a/CHANGELOG.md b/CHANGELOG.md index e29afe2..3b49656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ O formato segue [Keep a Changelog](https://keepachangelog.com/pt-BR/). Este projeto adota [Versionamento Semântico](https://semver.org/pt-BR/). +## [1.43.20] - 2025-12-16 + +### Fixed +- **Projeção de Saldo - Transações Pendentes Vencidas** + - FIX: Projeção agora inclui transações pendentes vencidas (status='pending' e planned_date < hoje) + - Adicionadas transações pendentes futuras (planned_date entre hoje e fim do mês) + - Separação clara entre transações vencidas (overdue) e pendentes (pending_this_month) + - Response JSON atualizado com breakdown completo: + * `overdue.income` e `overdue.expense` (transações vencidas) + * `pending_this_month.pending_income` e `pending_expense` (transações futuras) + - Projeção inteligente agora considera: realizado + recorrências + passivos + vencidos + pendentes + ## [1.43.19] - 2025-12-16 ### Changed diff --git a/VERSION b/VERSION index 3f05860..a07ddf0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.43.19 +1.43.20 diff --git a/backend/app/Http/Controllers/Api/ReportController.php b/backend/app/Http/Controllers/Api/ReportController.php index ef8e067..cbfbfd0 100644 --- a/backend/app/Http/Controllers/Api/ReportController.php +++ b/backend/app/Http/Controllers/Api/ReportController.php @@ -916,8 +916,35 @@ public function projection(Request $request) // ========================================================================= // 5. TRANSAÇÕES EM ATRASO (overdue) // ========================================================================= + $overdueIncome = 0; $overdueExpense = 0; + // Transações pendentes vencidas (status='pending' e planned_date < hoje) + $overduePendingTransactions = DB::select(" + SELECT + t.amount, + t.type, + COALESCE(a.currency, 'EUR') as currency + FROM transactions t + LEFT JOIN accounts a ON t.account_id = a.id + WHERE t.user_id = ? + AND t.status = 'pending' + AND t.planned_date < ? + AND t.deleted_at IS NULL + AND {$this->excludeTransfers()} + ", [$this->userId, $today]); + + foreach ($overduePendingTransactions as $row) { + $amount = abs($row->amount); + $converted = $this->convertToPrimaryCurrency($amount, $row->currency); + + if ($row->type === 'credit') { + $overdueIncome += $converted; + } else { + $overdueExpense += $converted; + } + } + // Parcelas de passivos vencidas $overdueInstallments = DB::select(" SELECT li.installment_amount as amount, la.currency @@ -940,16 +967,48 @@ public function projection(Request $request) } // ========================================================================= - // 6. CÁLCULOS FINAIS DA PROJEÇÃO + // 6. TRANSAÇÕES PENDENTES ATÉ O FIM DO MÊS (planned_date entre hoje e fim do mês) + // ========================================================================= + $pendingIncome = 0; + $pendingExpense = 0; + + $pendingTransactions = DB::select(" + SELECT + t.amount, + t.type, + COALESCE(a.currency, 'EUR') as currency + FROM transactions t + LEFT JOIN accounts a ON t.account_id = a.id + WHERE t.user_id = ? + AND t.status = 'pending' + AND t.planned_date >= ? + AND t.planned_date <= ? + AND t.deleted_at IS NULL + AND {$this->excludeTransfers()} + ", [$this->userId, $today, $endOfMonth]); + + foreach ($pendingTransactions as $row) { + $amount = abs($row->amount); + $converted = $this->convertToPrimaryCurrency($amount, $row->currency); + + if ($row->type === 'credit') { + $pendingIncome += $converted; + } else { + $pendingExpense += $converted; + } + } + + // ========================================================================= + // 7. CÁLCULOS FINAIS DA PROJEÇÃO // ========================================================================= // Projeção simples (extrapolação linear) $simpleProjectedExpense = ($currExpense / $daysElapsed) * $daysInMonth; $simpleProjectedIncome = ($currIncome / $daysElapsed) * $daysInMonth; - // Projeção inteligente: realizado + pendente (recorrências + passivos) - $smartProjectedIncome = $currIncome + $recurringIncome; - $smartProjectedExpense = $currExpense + $recurringExpense + $liabilityExpense; + // Projeção inteligente: realizado + pendente (todos os tipos) + $smartProjectedIncome = $currIncome + $recurringIncome + $overdueIncome + $pendingIncome; + $smartProjectedExpense = $currExpense + $recurringExpense + $liabilityExpense + $overdueExpense + $pendingExpense; return response()->json([ 'historical_average' => [ @@ -968,10 +1027,15 @@ public function projection(Request $request) 'recurring_income' => round($recurringIncome, 2), 'recurring_expense' => round($recurringExpense, 2), 'liability_installments' => round($liabilityExpense, 2), - 'total_pending_expense' => round($recurringExpense + $liabilityExpense, 2), + 'pending_income' => round($pendingIncome, 2), + 'pending_expense' => round($pendingExpense, 2), + 'total_pending_income' => round($recurringIncome + $pendingIncome, 2), + 'total_pending_expense' => round($recurringExpense + $liabilityExpense + $pendingExpense, 2), ], 'overdue' => [ - 'total' => round($overdueExpense, 2), + 'income' => round($overdueIncome, 2), + 'expense' => round($overdueExpense, 2), + 'total' => round($overdueExpense - $overdueIncome, 2), ], 'projection' => [ // Valores principais (usa projeção inteligente)