feat(#36): receitas recorrentes com confirmação de salário até dia 5

- Migration 009: coluna `type` em recurring_expenses + tabela recurring_late
- API Go: tipo income/expense em CRUD; endpoints POST /confirm e /late
- Dashboard: campo pending_income_recurrings na resposta
- Frontend: RecurringView com seções RECEITAS e DESPESAS separadas
- HomeView: widget de confirmação (dia 1-5) e badge SALÁRIO PENDENTE (pós dia 5)
- Testes unitários: 4 novos casos (income covered, late, confirm, reject expense)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
2026-05-27 15:00:28 -03:00
co-authored by Claude Sonnet 4.6
parent 1931271b76
commit 79f4a62a1b
16 changed files with 725 additions and 77 deletions
+10 -9
View File
@@ -24,13 +24,14 @@ type RecentTransaction struct {
}
type DashboardData struct {
Month string `json:"month"`
TotalIncome float64 `json:"total_income"`
TotalExpenses float64 `json:"total_expenses"`
SavingsPct float64 `json:"savings_pct"`
TotalPatrimony float64 `json:"total_patrimony"`
ByCategory []CategoryTotal `json:"by_category"`
MonthlyEvolution []MonthEvolution `json:"monthly_evolution"`
RecentTransactions []RecentTransaction `json:"recent_transactions"`
PendingRecurring int `json:"pending_recurring"`
Month string `json:"month"`
TotalIncome float64 `json:"total_income"`
TotalExpenses float64 `json:"total_expenses"`
SavingsPct float64 `json:"savings_pct"`
TotalPatrimony float64 `json:"total_patrimony"`
ByCategory []CategoryTotal `json:"by_category"`
MonthlyEvolution []MonthEvolution `json:"monthly_evolution"`
RecentTransactions []RecentTransaction `json:"recent_transactions"`
PendingRecurring int `json:"pending_recurring"`
PendingIncomeRecurrings []PendingIncome `json:"pending_income_recurrings"`
}
+15 -3
View File
@@ -8,6 +8,7 @@ type RecurringExpense struct {
ExpectedAmount float64 `json:"expected_amount"`
DayOfMonth int `json:"day_of_month"`
CategoryID *int `json:"category_id"`
Type string `json:"type"` // "income" | "expense"
Active bool `json:"active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
@@ -18,12 +19,23 @@ type RecurringInput struct {
ExpectedAmount float64 `json:"expected_amount"`
DayOfMonth int `json:"day_of_month"`
CategoryID *int `json:"category_id"`
Type string `json:"type"` // "income" | "expense"
}
// RecurringStatus reports whether a recurring expense is covered for a month.
// RecurringStatus reports whether a recurring item is covered/ignored/late for a month.
type RecurringStatus struct {
RecurringExpense
Covered bool `json:"covered"` // has a matching transaction
Ignored bool `json:"ignored"` // user explicitly ignored this month
Covered bool `json:"covered"` // has a matching transaction or was ignored
Ignored bool `json:"ignored"` // user explicitly ignored this month (expense)
Late bool `json:"late"` // user marked income as late this month
Reason string `json:"reason,omitempty"`
}
// PendingIncome is a lightweight summary of an income recurring pending confirmation.
type PendingIncome struct {
ID int `json:"id"`
Name string `json:"name"`
ExpectedAmount float64 `json:"expected_amount"`
DayOfMonth int `json:"day_of_month"`
Late bool `json:"late"`
}