feat(#35): rendimento CDI automático em contas de investimento

- Migration 010: colunas yield_type e last_yield_date em accounts + source='yield' nas transações
- CDIYieldService: busca taxas BCB (serie 12) e cria transação source=yield com rendimento acumulado
- Weekends/feriados sem taxa usam última taxa disponível (AC4)
- GET /api/accounts dispara cálculo CDI on-demand antes de retornar saldos
- AccountsView: seletor yield_type (none/cdi/variable) + badge CDI/VAR na listagem
- Transações source=yield aparecem no histórico com descrição 'Rendimento CDI — {período}'

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
2026-05-27 15:06:05 -03:00
co-authored by Claude Sonnet 4.6
parent 79f4a62a1b
commit ee6cd9f37e
9 changed files with 249 additions and 27 deletions
+19 -3
View File
@@ -5,6 +5,7 @@ import (
"errors"
"financeiro-carvalho/internal/model"
"financeiro-carvalho/internal/repository"
)
var (
@@ -26,11 +27,12 @@ type AccountRepo interface {
}
type AccountService struct {
repo AccountRepo
repo repository.AccountRepoWithYield
cdiSvc *CDIYieldService
}
func NewAccountService(repo AccountRepo) *AccountService {
return &AccountService{repo: repo}
func NewAccountService(repo repository.AccountRepoWithYield, cdiSvc *CDIYieldService) *AccountService {
return &AccountService{repo: repo, cdiSvc: cdiSvc}
}
func (s *AccountService) validate(in model.AccountInput) error {
@@ -43,7 +45,21 @@ func (s *AccountService) validate(in model.AccountInput) error {
return nil
}
// List returns accounts, applying CDI yield on-demand for cdi-type accounts.
func (s *AccountService) List(ctx context.Context) ([]model.Account, error) {
accounts, err := s.repo.List(ctx)
if err != nil {
return nil, err
}
for i := range accounts {
if accounts[i].YieldType == "cdi" {
// Apply CDI yield — errors are non-fatal (BCB API may be unavailable)
_ = s.cdiSvc.ApplyYield(ctx, &accounts[i])
}
}
// Re-fetch after yield transactions may have been created to get updated balances
return s.repo.List(ctx)
}