- 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]>
74 lines
2.1 KiB
Go
74 lines
2.1 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"financeiro-carvalho/internal/model"
|
|
"financeiro-carvalho/internal/repository"
|
|
)
|
|
|
|
var ErrPendingBillNotFound = errors.New("pending bill not found")
|
|
|
|
type PendingBillService struct {
|
|
repo repository.PendingBillRepository
|
|
txRepo repository.TransactionRepository
|
|
}
|
|
|
|
func NewPendingBillService(repo repository.PendingBillRepository, txRepo repository.TransactionRepository) *PendingBillService {
|
|
return &PendingBillService{repo: repo, txRepo: txRepo}
|
|
}
|
|
|
|
func (s *PendingBillService) List(ctx context.Context) ([]model.PendingBillImport, error) {
|
|
items, err := s.repo.List(ctx)
|
|
if items == nil {
|
|
return []model.PendingBillImport{}, err
|
|
}
|
|
return items, err
|
|
}
|
|
|
|
// Save stores a credit-card import as pending when payment_date is in the future.
|
|
// Returns (true, pendingBill, nil) if saved as pending; (false, nil, nil) if not applicable.
|
|
func (s *PendingBillService) MaybeSaveAsPending(ctx context.Context, filename, paymentDate string, rows []model.ImportRow) (bool, *model.PendingBillImport, error) {
|
|
if paymentDate == "" {
|
|
return false, nil, nil
|
|
}
|
|
t, err := time.Parse("2006-01-02", paymentDate)
|
|
if err != nil || !t.After(time.Now().Truncate(24*time.Hour)) {
|
|
return false, nil, nil
|
|
}
|
|
|
|
var total float64
|
|
newRows := make([]model.ImportRow, 0, len(rows))
|
|
for _, r := range rows {
|
|
if !r.IsDuplicate {
|
|
total += r.Amount
|
|
newRows = append(newRows, r)
|
|
}
|
|
}
|
|
|
|
p, err := s.repo.Create(ctx, filename, paymentDate, total, newRows)
|
|
if err != nil {
|
|
return true, nil, err
|
|
}
|
|
return true, p, nil
|
|
}
|
|
|
|
// Confirm inserts the pending bill's rows as actual transactions and removes the pending record.
|
|
func (s *PendingBillService) Confirm(ctx context.Context, id int) error {
|
|
p, err := s.repo.GetByID(ctx, id)
|
|
if err != nil {
|
|
return ErrPendingBillNotFound
|
|
}
|
|
if _, err := s.txRepo.BulkInsert(ctx, p.Rows); err != nil {
|
|
return err
|
|
}
|
|
return s.repo.Delete(ctx, id)
|
|
}
|
|
|
|
// Discard removes a pending bill import without creating transactions.
|
|
func (s *PendingBillService) Discard(ctx context.Context, id int) error {
|
|
return s.repo.Delete(ctx, id)
|
|
}
|