feat: #44 fatura de cartão com data futura fica pendente até confirmação

- Nova tabela pending_bill_imports (migration 016)
- ImportHandler.Confirm: quando is_credit_card=true e payment_date > hoje,
  salva como pending em vez de inserir transações
- Novos endpoints: GET /pending-bills, POST /pending-bills/:id/confirm,
  DELETE /pending-bills/:id
- Dashboard inclui pending_bill_imports no payload
- Frontend: resultado "fatura salva como pendente" no ImportView
- AccountsView exibe widget de faturas pendentes com ações de confirmar/descartar
- dashboard_test: mock de PendingBillRepo + TransactionRepository

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
2026-05-28 22:26:42 -03:00
co-authored by Claude Sonnet 4.6
parent 9742ae7469
commit 2b8524dcde
16 changed files with 453 additions and 32 deletions
+30 -1
View File
@@ -8,6 +8,21 @@ import (
"financeiro-carvalho/internal/service"
)
type mockImportTxRepo struct{}
func (m *mockImportTxRepo) IsDuplicate(_ context.Context, _, _ string, _ float64) (bool, error) {
return false, nil
}
func (m *mockImportTxRepo) IsExternalIDKnown(_ context.Context, _ string) (bool, error) {
return false, nil
}
func (m *mockImportTxRepo) BulkInsert(_ context.Context, rows []model.ImportRow) (int, error) {
return len(rows), nil
}
func (m *mockImportTxRepo) SaveImportLog(_ context.Context, _, _ string, _, _, _ int) error {
return nil
}
type mockDashboardRepo struct {
income float64
expenses float64
@@ -33,11 +48,25 @@ type mockPatrimony struct{}
func (m *mockPatrimony) TotalPatrimony(_ context.Context) (float64, error) { return 0, nil }
type mockPendingBillRepo struct{}
func (m *mockPendingBillRepo) List(_ context.Context) ([]model.PendingBillImport, error) {
return nil, nil
}
func (m *mockPendingBillRepo) Create(_ context.Context, _, _ string, _ float64, _ []model.ImportRow) (*model.PendingBillImport, error) {
return &model.PendingBillImport{}, nil
}
func (m *mockPendingBillRepo) GetByID(_ context.Context, _ int) (*model.PendingBillImport, error) {
return nil, nil
}
func (m *mockPendingBillRepo) Delete(_ context.Context, _ int) error { return nil }
func newDashboardSvc(income, expenses float64) *service.DashboardService {
repo := &mockAccountRepo{}
recurrSvc := service.NewRecurringService(newMockRecurring(nil), &mockTxRepo{})
billSvc := service.NewCreditBillService(&mockCreditBillRepo{}, repo)
return service.NewDashboardService(&mockDashboardRepo{income: income, expenses: expenses}, recurrSvc, &mockPatrimony{}, billSvc)
pendingBillSvc := service.NewPendingBillService(&mockPendingBillRepo{}, &mockImportTxRepo{})
return service.NewDashboardService(&mockDashboardRepo{income: income, expenses: expenses}, recurrSvc, &mockPatrimony{}, billSvc, pendingBillSvc)
}
func TestDashboard_SavingsPct_40(t *testing.T) {