Added transaction feature, fixed some mistakes
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"FamilyHub/src/domain"
|
||||
"FamilyHub/src/repositories"
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TransactionService interface {
|
||||
Create(ctx context.Context, req domain.CreateTransactionRequest) (*domain.Transaction, error)
|
||||
GetByID(ctx context.Context, id int64) (*domain.Transaction, error)
|
||||
List(ctx context.Context, filter domain.TransactionListFilter) ([]*domain.Transaction, error)
|
||||
Update(ctx context.Context, id int64, req domain.UpdateTransactionRequest) (*domain.Transaction, error)
|
||||
Delete(ctx context.Context, id int64) error
|
||||
}
|
||||
|
||||
type transactionService struct {
|
||||
repo repositories.TransactionRepository
|
||||
}
|
||||
|
||||
func NewTransactionService(repo repositories.TransactionRepository) TransactionService {
|
||||
return &transactionService{repo: repo}
|
||||
}
|
||||
|
||||
var (
|
||||
ErrTransactionNotFound = errors.New("transaction not found")
|
||||
ErrTransactionPatch = errors.New("empty update payload")
|
||||
ErrReceiptLinkConflict = errors.New("receipt_id and detach_receipt cannot be used together")
|
||||
ErrInvalidTransaction = errors.New("type and category are required")
|
||||
ErrReceiptNotFound = errors.New("receipt not found")
|
||||
)
|
||||
|
||||
func (s *transactionService) Create(ctx context.Context, req domain.CreateTransactionRequest) (*domain.Transaction, error) {
|
||||
if strings.TrimSpace(req.Type) == "" || strings.TrimSpace(req.Category) == "" {
|
||||
return nil, ErrInvalidTransaction
|
||||
}
|
||||
|
||||
transaction := &domain.Transaction{
|
||||
FamilyID: req.FamilyID,
|
||||
Description: req.Description,
|
||||
Type: req.Type,
|
||||
DateTime: req.DateTime,
|
||||
Category: req.Category,
|
||||
Amount: req.Amount,
|
||||
CreatedBy: req.CreatedBy,
|
||||
ReceiptID: req.ReceiptID,
|
||||
}
|
||||
|
||||
if err := s.repo.Create(ctx, transaction); err != nil {
|
||||
if errors.Is(err, repositories.ErrReceiptNotFound) {
|
||||
return nil, ErrReceiptNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return transaction, nil
|
||||
}
|
||||
|
||||
func (s *transactionService) GetByID(ctx context.Context, id int64) (*domain.Transaction, error) {
|
||||
transaction, err := s.repo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if transaction == nil {
|
||||
return nil, ErrTransactionNotFound
|
||||
}
|
||||
|
||||
return transaction, nil
|
||||
}
|
||||
|
||||
func (s *transactionService) List(ctx context.Context, filter domain.TransactionListFilter) ([]*domain.Transaction, error) {
|
||||
if filter.Limit <= 0 {
|
||||
filter.Limit = 50
|
||||
}
|
||||
if filter.Limit > 200 {
|
||||
filter.Limit = 200
|
||||
}
|
||||
if filter.Offset < 0 {
|
||||
filter.Offset = 0
|
||||
}
|
||||
|
||||
return s.repo.List(ctx, filter)
|
||||
}
|
||||
|
||||
func (s *transactionService) Update(ctx context.Context, id int64, req domain.UpdateTransactionRequest) (*domain.Transaction, error) {
|
||||
if req.ReceiptID != nil && req.DetachReceipt {
|
||||
return nil, ErrReceiptLinkConflict
|
||||
}
|
||||
|
||||
existing, err := s.repo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existing == nil {
|
||||
return nil, ErrTransactionNotFound
|
||||
}
|
||||
|
||||
if req.Description == nil &&
|
||||
req.Type == nil &&
|
||||
req.DateTime == nil &&
|
||||
req.Category == nil &&
|
||||
req.Amount == nil &&
|
||||
req.ReceiptID == nil &&
|
||||
!req.DetachReceipt {
|
||||
return nil, ErrTransactionPatch
|
||||
}
|
||||
|
||||
updated := &domain.Transaction{
|
||||
ID: existing.ID,
|
||||
FamilyID: existing.FamilyID,
|
||||
Description: existing.Description,
|
||||
Type: existing.Type,
|
||||
DateTime: existing.DateTime,
|
||||
Category: existing.Category,
|
||||
Amount: existing.Amount,
|
||||
CreatedAt: existing.CreatedAt,
|
||||
CreatedBy: existing.CreatedBy,
|
||||
ReceiptID: existing.ReceiptID,
|
||||
}
|
||||
|
||||
if req.Description != nil {
|
||||
updated.Description = req.Description
|
||||
}
|
||||
if req.Type != nil {
|
||||
updated.Type = *req.Type
|
||||
}
|
||||
if req.DateTime != nil {
|
||||
updated.DateTime = *req.DateTime
|
||||
}
|
||||
if req.Category != nil {
|
||||
updated.Category = *req.Category
|
||||
}
|
||||
if req.Amount != nil {
|
||||
updated.Amount = *req.Amount
|
||||
}
|
||||
|
||||
syncReceipt := false
|
||||
if req.DetachReceipt {
|
||||
updated.ReceiptID = nil
|
||||
syncReceipt = true
|
||||
}
|
||||
if req.ReceiptID != nil {
|
||||
updated.ReceiptID = req.ReceiptID
|
||||
syncReceipt = true
|
||||
}
|
||||
|
||||
if strings.TrimSpace(updated.Type) == "" || strings.TrimSpace(updated.Category) == "" {
|
||||
return nil, ErrInvalidTransaction
|
||||
}
|
||||
|
||||
if err := s.repo.Update(ctx, updated, syncReceipt); err != nil {
|
||||
if errors.Is(err, repositories.ErrReceiptNotFound) {
|
||||
return nil, ErrReceiptNotFound
|
||||
}
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrTransactionNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.repo.GetByID(ctx, id)
|
||||
}
|
||||
|
||||
func (s *transactionService) Delete(ctx context.Context, id int64) error {
|
||||
transaction, err := s.repo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if transaction == nil {
|
||||
return ErrTransactionNotFound
|
||||
}
|
||||
|
||||
return s.repo.Delete(ctx, id)
|
||||
}
|
||||
Reference in New Issue
Block a user