Files
carvalho-finances/apps/api/internal/service/dashboard.go
T
Mlcavalho1andClaude Sonnet 4.6 e3b3433703 feat(#37): módulo de cartão de crédito com faturas mensais
- Migration 011: closing_day/due_day em accounts + tabela credit_bills
- CreditBillService: cria/atualiza fatura corrente on-demand no GET /accounts
- Widget FATURA ATUAL no dashboard com total e botão PAGAR
- AccountsView: campos closing_day/due_day para contas credit + painel fatura
- Dashboard: current_bills lista faturas não pagas de todos os cartões

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-27 15:12:53 -03:00

118 lines
2.9 KiB
Go

package service
import (
"context"
"financeiro-carvalho/internal/model"
)
type DashboardRepo interface {
MonthlySummary(ctx context.Context, month string) (income, expenses float64, err error)
ByCategory(ctx context.Context, month string) ([]model.CategoryTotal, error)
MonthlyEvolution(ctx context.Context, month string) ([]model.MonthEvolution, error)
RecentTransactions(ctx context.Context, month string) ([]model.RecentTransaction, error)
}
type PatrimonySource interface {
TotalPatrimony(ctx context.Context) (float64, error)
}
type DashboardService struct {
repo DashboardRepo
recurrSvc *RecurringService
patrimony PatrimonySource
billSvc *CreditBillService
}
func NewDashboardService(repo DashboardRepo, recurrSvc *RecurringService, patrimony PatrimonySource, billSvc *CreditBillService) *DashboardService {
return &DashboardService{repo: repo, recurrSvc: recurrSvc, patrimony: patrimony, billSvc: billSvc}
}
func (s *DashboardService) Get(ctx context.Context, month string) (*model.DashboardData, error) {
income, expenses, err := s.repo.MonthlySummary(ctx, month)
if err != nil {
return nil, err
}
byCategory, err := s.repo.ByCategory(ctx, month)
if err != nil {
return nil, err
}
evolution, err := s.repo.MonthlyEvolution(ctx, month)
if err != nil {
return nil, err
}
recent, err := s.repo.RecentTransactions(ctx, month)
if err != nil {
return nil, err
}
var savingsPct float64
if income > 0 {
savingsPct = (income - expenses) / income * 100
}
statuses, err := s.recurrSvc.MonthlyStatus(ctx, month)
if err != nil {
return nil, err
}
pending := 0
var pendingIncome []model.PendingIncome
for _, st := range statuses {
if st.Type == "income" {
if !st.Covered {
pendingIncome = append(pendingIncome, model.PendingIncome{
ID: st.ID,
Name: st.Name,
ExpectedAmount: st.ExpectedAmount,
DayOfMonth: st.DayOfMonth,
Late: st.Late,
})
}
} else {
if !st.Covered {
pending++
}
}
}
if pendingIncome == nil {
pendingIncome = []model.PendingIncome{}
}
if byCategory == nil {
byCategory = []model.CategoryTotal{}
}
if evolution == nil {
evolution = []model.MonthEvolution{}
}
if recent == nil {
recent = []model.RecentTransaction{}
}
patrimony, err := s.patrimony.TotalPatrimony(ctx)
if err != nil {
return nil, err
}
currentBills, _ := s.billSvc.currentBillsForDashboard(ctx)
if currentBills == nil {
currentBills = []model.CreditBill{}
}
return &model.DashboardData{
Month: month,
TotalIncome: income,
TotalExpenses: expenses,
SavingsPct: savingsPct,
TotalPatrimony: patrimony,
ByCategory: byCategory,
MonthlyEvolution: evolution,
RecentTransactions: recent,
PendingRecurring: pending,
PendingIncomeRecurrings: pendingIncome,
CurrentBills: currentBills,
}, nil
}