v1.43.24 - FIX CRITICAL: Projeção inclui recurring_instances vencidas
This commit is contained in:
parent
738730b549
commit
83a4d91029
10
CHANGELOG.md
10
CHANGELOG.md
@ -5,6 +5,16 @@ O formato segue [Keep a Changelog](https://keepachangelog.com/pt-BR/).
|
|||||||
Este projeto adota [Versionamento Semântico](https://semver.org/pt-BR/).
|
Este projeto adota [Versionamento Semântico](https://semver.org/pt-BR/).
|
||||||
|
|
||||||
|
|
||||||
|
## [1.43.24] - 2025-12-16
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- 🐛 **Projeção de Saldo - Instâncias Recorrentes Vencidas**
|
||||||
|
- FIX: Widget de projeção agora inclui `recurring_instances` com status pendente e vencidas
|
||||||
|
- Corrigida query para usar `planned_date` ao invés de `effective_date` nas transações pendentes
|
||||||
|
- Adicionada query separada para `recurring_instances` vencidas e futuras
|
||||||
|
- Impacto: Projeção agora reflete corretamente valores como Aluguel (1003.59€) e Bizum Asenergy (94.22€) vencidos
|
||||||
|
- Cálculo: Saldo Atual - Transações Vencidas - Instâncias Recorrentes Vencidas = Projeção Correta
|
||||||
|
|
||||||
## [1.43.23] - 2025-12-16
|
## [1.43.23] - 2025-12-16
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@ -1153,24 +1153,47 @@ public function projectionChart(Request $request)
|
|||||||
// Buscar transações agendadas/pendentes (incluindo atrasadas)
|
// Buscar transações agendadas/pendentes (incluindo atrasadas)
|
||||||
$scheduledTransactions = DB::select("
|
$scheduledTransactions = DB::select("
|
||||||
SELECT
|
SELECT
|
||||||
t.effective_date as date,
|
COALESCE(t.planned_date, t.effective_date) as date,
|
||||||
t.amount,
|
t.amount,
|
||||||
t.type,
|
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
|
CASE WHEN COALESCE(t.planned_date, t.effective_date) < ? THEN 1 ELSE 0 END as is_overdue
|
||||||
FROM transactions t
|
FROM transactions t
|
||||||
LEFT JOIN accounts a ON t.account_id = a.id
|
LEFT JOIN accounts a ON t.account_id = a.id
|
||||||
WHERE t.user_id = ?
|
WHERE t.user_id = ?
|
||||||
AND t.status IN ('pending', 'scheduled')
|
AND t.status IN ('pending', 'scheduled')
|
||||||
AND t.effective_date <= ?
|
AND COALESCE(t.planned_date, t.effective_date) <= ?
|
||||||
AND t.deleted_at IS NULL
|
AND t.deleted_at IS NULL
|
||||||
ORDER BY t.effective_date
|
AND {$this->excludeTransfers()}
|
||||||
|
ORDER BY COALESCE(t.planned_date, t.effective_date)
|
||||||
|
", [$today->toDateString(), $this->userId, $endDate->toDateString()]);
|
||||||
|
|
||||||
|
// Buscar instâncias de recorrências pendentes (incluindo atrasadas)
|
||||||
|
$recurringInstances = DB::select("
|
||||||
|
SELECT
|
||||||
|
ri.due_date as date,
|
||||||
|
ri.planned_amount as amount,
|
||||||
|
rt.type,
|
||||||
|
COALESCE(a.currency, 'EUR') as currency,
|
||||||
|
CASE WHEN ri.due_date < ? THEN 1 ELSE 0 END as is_overdue
|
||||||
|
FROM recurring_instances ri
|
||||||
|
JOIN recurring_templates rt ON ri.recurring_template_id = rt.id
|
||||||
|
LEFT JOIN accounts a ON rt.account_id = a.id
|
||||||
|
WHERE ri.user_id = ?
|
||||||
|
AND ri.status = 'pending'
|
||||||
|
AND ri.due_date <= ?
|
||||||
|
AND ri.deleted_at IS NULL
|
||||||
|
ORDER BY ri.due_date
|
||||||
", [$today->toDateString(), $this->userId, $endDate->toDateString()]);
|
", [$today->toDateString(), $this->userId, $endDate->toDateString()]);
|
||||||
|
|
||||||
// Separar transações atrasadas para processar primeiro
|
// Separar transações atrasadas para processar primeiro
|
||||||
$overdueTransactions = array_filter($scheduledTransactions, fn($tx) => $tx->is_overdue);
|
$overdueTransactions = array_filter($scheduledTransactions, fn($tx) => $tx->is_overdue);
|
||||||
$futureTransactions = array_filter($scheduledTransactions, fn($tx) => !$tx->is_overdue);
|
$futureTransactions = array_filter($scheduledTransactions, fn($tx) => !$tx->is_overdue);
|
||||||
|
|
||||||
|
// Separar instâncias recorrentes atrasadas
|
||||||
|
$overdueRecurringInstances = array_filter($recurringInstances, fn($ri) => $ri->is_overdue);
|
||||||
|
$futureRecurringInstances = array_filter($recurringInstances, fn($ri) => !$ri->is_overdue);
|
||||||
|
|
||||||
// Processar transações atrasadas ANTES do ponto inicial
|
// Processar transações atrasadas ANTES do ponto inicial
|
||||||
foreach ($overdueTransactions as $tx) {
|
foreach ($overdueTransactions as $tx) {
|
||||||
$amount = $this->convertToPrimaryCurrency(abs($tx->amount), $tx->currency);
|
$amount = $this->convertToPrimaryCurrency(abs($tx->amount), $tx->currency);
|
||||||
@ -1182,13 +1205,24 @@ public function projectionChart(Request $request)
|
|||||||
$runningBalance = $currentBalance;
|
$runningBalance = $currentBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Processar instâncias recorrentes atrasadas ANTES do ponto inicial
|
||||||
|
foreach ($overdueRecurringInstances as $ri) {
|
||||||
|
$amount = $this->convertToPrimaryCurrency(abs($ri->amount), $ri->currency);
|
||||||
|
if ($ri->type === 'credit') {
|
||||||
|
$currentBalance += $amount;
|
||||||
|
} else {
|
||||||
|
$currentBalance -= $amount;
|
||||||
|
}
|
||||||
|
$runningBalance = $currentBalance;
|
||||||
|
}
|
||||||
|
|
||||||
// Ponto inicial (já inclui o impacto das transações atrasadas)
|
// Ponto inicial (já inclui o impacto das transações atrasadas)
|
||||||
$dataPoints[] = [
|
$dataPoints[] = [
|
||||||
'date' => $today->toDateString(),
|
'date' => $today->toDateString(),
|
||||||
'balance' => round($runningBalance, 2),
|
'balance' => round($runningBalance, 2),
|
||||||
'label' => $today->format('d/m'),
|
'label' => $today->format('d/m'),
|
||||||
'isToday' => true,
|
'isToday' => true,
|
||||||
'has_overdue' => count($overdueTransactions) > 0,
|
'has_overdue' => count($overdueTransactions) + count($overdueRecurringInstances) > 0,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Gerar pontos até a data final
|
// Gerar pontos até a data final
|
||||||
@ -1237,6 +1271,18 @@ public function projectionChart(Request $request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Somar instâncias recorrentes neste período (apenas futuras)
|
||||||
|
foreach ($futureRecurringInstances as $ri) {
|
||||||
|
if ($ri->date > $periodStart && $ri->date <= $periodEnd) {
|
||||||
|
$amount = $this->convertToPrimaryCurrency(abs($ri->amount), $ri->currency);
|
||||||
|
if ($ri->type === 'credit') {
|
||||||
|
$runningBalance += $amount;
|
||||||
|
} else {
|
||||||
|
$runningBalance -= $amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$dataPoints[] = [
|
$dataPoints[] = [
|
||||||
'date' => $current->toDateString(),
|
'date' => $current->toDateString(),
|
||||||
'balance' => round($runningBalance, 2),
|
'balance' => round($runningBalance, 2),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user