Files
carvalho-finances/apps/api/internal/service/account.go
T

122 lines
3.4 KiB
Go

package service
import (
"context"
"errors"
"log"
"time"
"financeiro-carvalho/internal/model"
"financeiro-carvalho/internal/repository"
)
var (
ErrAccountEmptyName = errors.New("account name cannot be empty")
ErrAccountInvalidType = errors.New("account type must be checking, savings, investment, or credit")
)
var validAccountTypes = map[string]bool{
"checking": true, "savings": true, "investment": true, "credit": true,
}
type AccountRepo interface {
List(ctx context.Context) ([]model.Account, error)
GetByID(ctx context.Context, id int) (*model.Account, error)
Create(ctx context.Context, in model.AccountInput) (*model.Account, error)
Update(ctx context.Context, id int, in model.AccountInput) (*model.Account, error)
Delete(ctx context.Context, id int) error
TotalPatrimony(ctx context.Context) (float64, error)
}
type AccountService struct {
repo repository.AccountRepoWithYield
cdiSvc *CDIYieldService
billSvc *CreditBillService
}
func NewAccountService(repo repository.AccountRepoWithYield, cdiSvc *CDIYieldService, billSvc *CreditBillService) *AccountService {
return &AccountService{repo: repo, cdiSvc: cdiSvc, billSvc: billSvc}
}
func (s *AccountService) validate(in model.AccountInput) error {
if in.Name == "" {
return ErrAccountEmptyName
}
if !validAccountTypes[in.Type] {
return ErrAccountInvalidType
}
return nil
}
// List returns accounts, applying CDI yield on-demand and attaching current bill for credit accounts.
func (s *AccountService) List(ctx context.Context) ([]model.Account, error) {
t0 := time.Now()
accounts, err := s.repo.List(ctx)
if err != nil {
return nil, err
}
log.Printf("[accounts] repo.List #1: %v", time.Since(t0))
yieldApplied := false
for i := range accounts {
if accounts[i].YieldType == "cdi" {
tCDI := time.Now()
_ = s.cdiSvc.ApplyYield(ctx, &accounts[i])
log.Printf("[accounts] ApplyYield account=%d: %v", accounts[i].ID, time.Since(tCDI))
yieldApplied = true
}
}
if yieldApplied {
tList2 := time.Now()
accounts, err = s.repo.List(ctx)
if err != nil {
return nil, err
}
log.Printf("[accounts] repo.List #2 (post-yield): %v", time.Since(tList2))
}
for i := range accounts {
if accounts[i].Type == "credit" {
tBill := time.Now()
closing, due := 1, 10
if accounts[i].ClosingDay != nil {
closing = *accounts[i].ClosingDay
}
if accounts[i].DueDay != nil {
due = *accounts[i].DueDay
}
_ = s.billSvc.EnsureCurrentBill(ctx, accounts[i].ID, closing, due)
bill, _ := s.billSvc.GetCurrent(ctx, accounts[i].ID)
accounts[i].CurrentBill = bill
log.Printf("[accounts] EnsureCurrentBill+GetCurrent account=%d: %v", accounts[i].ID, time.Since(tBill))
}
}
log.Printf("[accounts] List total: %v", time.Since(t0))
return accounts, nil
}
func (s *AccountService) Create(ctx context.Context, in model.AccountInput) (*model.Account, error) {
if err := s.validate(in); err != nil {
return nil, err
}
return s.repo.Create(ctx, in)
}
func (s *AccountService) Update(ctx context.Context, id int, in model.AccountInput) (*model.Account, error) {
if err := s.validate(in); err != nil {
return nil, err
}
return s.repo.Update(ctx, id, in)
}
func (s *AccountService) Delete(ctx context.Context, id int) error {
return s.repo.Delete(ctx, id)
}
func (s *AccountService) TotalPatrimony(ctx context.Context) (float64, error) {
return s.repo.TotalPatrimony(ctx)
}