v1.38.0 - Projeção de Saldo considera transações em atraso + alerta visual
This commit is contained in:
parent
b9592fc915
commit
f4a33b88bc
12
CHANGELOG.md
12
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.38.0] - 2025-12-15
|
||||
|
||||
### Added
|
||||
- **Projeção de Saldo** - Agora considera transações em atraso
|
||||
- Backend busca todas as transações pendentes (incluindo atrasadas)
|
||||
- Processa transações atrasadas ANTES do ponto inicial da projeção
|
||||
- Adiciona informações de transações em atraso no summary da API
|
||||
- Frontend exibe alerta amarelo quando há transações em atraso incluídas
|
||||
- Alerta mostra quantidade de transações e impacto no saldo
|
||||
- Traduções completas: overdueIncluded, overdueTransactions, includedInProjection (pt-BR, en, es)
|
||||
- Melhora precisão da projeção ao incluir todas as pendências
|
||||
|
||||
## [1.37.0] - 2025-12-15
|
||||
|
||||
### Added
|
||||
|
||||
@ -942,29 +942,45 @@ public function projectionChart(Request $request)
|
||||
ORDER BY li.due_date
|
||||
", [$this->userId, $today->toDateString(), $endDate->toDateString()]);
|
||||
|
||||
// Buscar transações agendadas/pendentes
|
||||
// Buscar transações agendadas/pendentes (incluindo atrasadas)
|
||||
$scheduledTransactions = DB::select("
|
||||
SELECT
|
||||
t.effective_date as date,
|
||||
t.amount,
|
||||
t.type,
|
||||
COALESCE(a.currency, 'EUR') as currency
|
||||
COALESCE(a.currency, 'EUR') as currency,
|
||||
CASE WHEN t.effective_date < ? THEN 1 ELSE 0 END as is_overdue
|
||||
FROM transactions t
|
||||
LEFT JOIN accounts a ON t.account_id = a.id
|
||||
WHERE t.user_id = ?
|
||||
AND t.status IN ('pending', 'scheduled')
|
||||
AND t.effective_date >= ?
|
||||
AND t.effective_date <= ?
|
||||
AND t.deleted_at IS NULL
|
||||
ORDER BY t.effective_date
|
||||
", [$this->userId, $today->toDateString(), $endDate->toDateString()]);
|
||||
", [$today->toDateString(), $this->userId, $endDate->toDateString()]);
|
||||
|
||||
// Ponto inicial
|
||||
// Separar transações atrasadas para processar primeiro
|
||||
$overdueTransactions = array_filter($scheduledTransactions, fn($tx) => $tx->is_overdue);
|
||||
$futureTransactions = array_filter($scheduledTransactions, fn($tx) => !$tx->is_overdue);
|
||||
|
||||
// Processar transações atrasadas ANTES do ponto inicial
|
||||
foreach ($overdueTransactions as $tx) {
|
||||
$amount = $this->convertToPrimaryCurrency(abs($tx->amount), $tx->currency);
|
||||
if ($tx->type === 'credit') {
|
||||
$currentBalance += $amount;
|
||||
} else {
|
||||
$currentBalance -= $amount;
|
||||
}
|
||||
$runningBalance = $currentBalance;
|
||||
}
|
||||
|
||||
// Ponto inicial (já inclui o impacto das transações atrasadas)
|
||||
$dataPoints[] = [
|
||||
'date' => $today->toDateString(),
|
||||
'balance' => round($runningBalance, 2),
|
||||
'label' => $today->format('d/m'),
|
||||
'isToday' => true,
|
||||
'has_overdue' => count($overdueTransactions) > 0,
|
||||
];
|
||||
|
||||
// Gerar pontos até a data final
|
||||
@ -1001,8 +1017,8 @@ public function projectionChart(Request $request)
|
||||
}
|
||||
}
|
||||
|
||||
// Somar transações agendadas neste período
|
||||
foreach ($scheduledTransactions as $tx) {
|
||||
// Somar transações agendadas neste período (apenas futuras)
|
||||
foreach ($futureTransactions as $tx) {
|
||||
if ($tx->date > $periodStart && $tx->date <= $periodEnd) {
|
||||
$amount = $this->convertToPrimaryCurrency(abs($tx->amount), $tx->currency);
|
||||
if ($tx->type === 'credit') {
|
||||
@ -1048,6 +1064,11 @@ public function projectionChart(Request $request)
|
||||
'change' => round($finalBalance - $currentBalance, 2),
|
||||
'change_percent' => $currentBalance != 0 ? round((($finalBalance - $currentBalance) / abs($currentBalance)) * 100, 1) : 0,
|
||||
'negative_month' => $negativeMonth,
|
||||
'overdue_count' => count($overdueTransactions),
|
||||
'overdue_impact' => round(array_reduce($overdueTransactions, function($carry, $tx) {
|
||||
$amount = $this->convertToPrimaryCurrency(abs($tx->amount), $tx->currency);
|
||||
return $carry + ($tx->type === 'credit' ? $amount : -$amount);
|
||||
}, 0), 2),
|
||||
],
|
||||
'period' => [
|
||||
'start' => $today->toDateString(),
|
||||
|
||||
@ -228,7 +228,24 @@ const BalanceProjectionChart = () => {
|
||||
<div className="card-body">
|
||||
{/* Summary Stats */}
|
||||
{summary && (
|
||||
<div className="row g-3 mb-4">
|
||||
<>
|
||||
{/* Alert if overdue transactions included */}
|
||||
{summary.overdue_count > 0 && (
|
||||
<div className="alert alert-warning d-flex align-items-center mb-3" role="alert">
|
||||
<i className="bi bi-exclamation-circle-fill me-2"></i>
|
||||
<div>
|
||||
<strong>{t('reports.projectionChart.overdueIncluded') || 'Atenção'}:</strong>{' '}
|
||||
{summary.overdue_count} {t('reports.projectionChart.overdueTransactions') || 'transação(ões) em atraso'}{' '}
|
||||
{t('reports.projectionChart.includedInProjection') || 'já incluída(s) no saldo atual'}.
|
||||
{' '}
|
||||
<span className={summary.overdue_impact < 0 ? 'text-danger fw-bold' : 'text-success fw-bold'}>
|
||||
({summary.overdue_impact >= 0 ? '+' : ''}{currency(summary.overdue_impact, data?.currency)})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="row g-3 mb-4">
|
||||
<div className="col-6 col-md-3">
|
||||
<div className="p-3 rounded bg-slate-700">
|
||||
<small className="text-slate-400 d-block">{t('reports.projectionChart.currentBalance') || 'Saldo Atual'}</small>
|
||||
@ -262,6 +279,7 @@ const BalanceProjectionChart = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Alert if negative balance predicted */}
|
||||
|
||||
@ -1922,7 +1922,10 @@
|
||||
"2months": "2 months",
|
||||
"3months": "3 months",
|
||||
"6months": "6 months",
|
||||
"12months": "12 months"
|
||||
"12months": "12 months",
|
||||
"overdueIncluded": "Overdue Transactions Included",
|
||||
"overdueTransactions": "overdue transaction(s)",
|
||||
"includedInProjection": "already included in balance projection"
|
||||
}
|
||||
},
|
||||
"months": {
|
||||
|
||||
@ -1905,7 +1905,10 @@
|
||||
"2months": "2 meses",
|
||||
"3months": "3 meses",
|
||||
"6months": "6 meses",
|
||||
"12months": "12 meses"
|
||||
"12months": "12 meses",
|
||||
"overdueIncluded": "Transacciones Vencidas Incluidas",
|
||||
"overdueTransactions": "transacción(es) vencida(s)",
|
||||
"includedInProjection": "ya incluida(s) en la proyección del saldo"
|
||||
}
|
||||
},
|
||||
"months": {
|
||||
|
||||
@ -1924,7 +1924,10 @@
|
||||
"2months": "2 meses",
|
||||
"3months": "3 meses",
|
||||
"6months": "6 meses",
|
||||
"12months": "12 meses"
|
||||
"12months": "12 meses",
|
||||
"overdueIncluded": "Transações em Atraso Incluídas",
|
||||
"overdueTransactions": "transação(ões) em atraso",
|
||||
"includedInProjection": "já incluída(s) na projeção do saldo"
|
||||
}
|
||||
},
|
||||
"months": {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user