Files
carvalho-finances/apps/api/internal/service/transaction.go
T
Mlcavalho1andClaude Sonnet 4.6 12e8500827 feat(#19): registro manual de transações e recorrências fixas
Adiciona CRUD manual de transações (income/expense) com filtro mensal, CRUD de
recorrências fixas com verificação mensal de cobertura (±10% por categoria),
endpoint de ignore/unignore e telas Vue para Transações e Configurações.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-26 20:26:48 -03:00

75 lines
2.0 KiB
Go

package service
import (
"context"
"errors"
"strings"
"financeiro-carvalho/internal/model"
"financeiro-carvalho/internal/repository"
)
var (
ErrInvalidTransactionType = errors.New("type must be 'income' or 'expense'")
ErrCannotEditImported = errors.New("imported transactions cannot be edited or deleted")
ErrTransactionEmptyDesc = errors.New("description is required")
ErrTransactionInvalidAmount = errors.New("amount must be greater than zero")
)
type TransactionService struct {
repo repository.ManualTransactionRepository
}
func NewTransactionService(repo repository.ManualTransactionRepository) *TransactionService {
return &TransactionService{repo: repo}
}
func (s *TransactionService) List(ctx context.Context, month string) ([]model.Transaction, error) {
txs, err := s.repo.List(ctx, month)
if txs == nil {
return []model.Transaction{}, err
}
return txs, err
}
func (s *TransactionService) Create(ctx context.Context, t model.Transaction) (*model.Transaction, error) {
if err := validateTransaction(t); err != nil {
return nil, err
}
t.Description = strings.TrimSpace(t.Description)
return s.repo.Create(ctx, t)
}
func (s *TransactionService) Update(ctx context.Context, t model.Transaction) (*model.Transaction, error) {
if err := validateTransaction(t); err != nil {
return nil, err
}
t.Description = strings.TrimSpace(t.Description)
out, err := s.repo.Update(ctx, t)
if errors.Is(err, repository.ErrNotFound) {
return nil, ErrCannotEditImported
}
return out, err
}
func (s *TransactionService) Delete(ctx context.Context, id int) error {
err := s.repo.Delete(ctx, id)
if errors.Is(err, repository.ErrNotFound) {
return ErrCannotEditImported
}
return err
}
func validateTransaction(t model.Transaction) error {
if strings.TrimSpace(t.Description) == "" {
return ErrTransactionEmptyDesc
}
if t.Amount <= 0 {
return ErrTransactionInvalidAmount
}
if t.Type != "income" && t.Type != "expense" {
return ErrInvalidTransactionType
}
return nil
}