summary history files

desktop/backend/services/splits_service.go
package services

import (
	"context"
	"database/sql"
	"fmt"
	"math/big"
	"pennyapp/backend/logwrap"
	"pennyapp/backend/model"
	"pennyapp/backend/types"

	"github.com/volatiletech/sqlboiler/v4/boil"
	"github.com/volatiletech/sqlboiler/v4/queries/qm"
)

const DefaultCurrencyDenominator int64 = 100

type splitsService struct {
	ctx    context.Context
	db     *sql.DB
	logger *logwrap.LogWrap
}

var splits *splitsService

func Splits() *splitsService {
	splits = &splitsService{}
	return splits
}

func (s *splitsService) Start(ctx context.Context, db *sql.DB, logger *logwrap.LogWrap) {
	s.ctx = ctx
	s.db = db
	s.logger = logger
	return
}

func (s *splitsService) getSplit(splitID int64) (*model.Split, error) {
	q := []qm.QueryMod{
		qm.Where("splits.id=?", splitID),
		qm.Load("Account.AccountType"),
	}
	return model.Splits(q...).One(s.ctx, s.db)
}

func (s *splitsService) deleteSplit(split *model.Split) error {
	splitsAttribute := model.SplitsAttribute{
		SplitsID: split.ID,
		Name:     "deleted",
		Value:    "true",
	}
	return splitsAttribute.Upsert(s.ctx, s.db, true, []string{"splits_id", "name"}, boil.Whitelist("name"), boil.Infer())
}

func (s *splitsService) undeleteSplit(split *model.Split) error {
	_, err := model.SplitsAttributes(qm.Where("splits_id=? AND name=?", split.ID, "deleted")).DeleteAll(s.ctx, s.db)
	if err != nil {
		return err
	}
	return nil
}

func (s *splitsService) updateSplit(splitID, accountID int64, amount float64) (*model.Split, error) {
	var split *model.Split
	var err error

	split, err = s.getSplit(splitID)
	if err != nil {
		return nil, err
	}

	split.AccountID = accountID

	r := big.NewRat(int64(amount*float64(DefaultCurrencyDenominator)), DefaultCurrencyDenominator)
	split.ValueNum = r.Num().Int64()
	split.ValueDenom = r.Denom().Int64()

	if _, err = split.Update(s.ctx, s.db, boil.Whitelist("transactions_id", "account_id", "value_num", "value_denom")); err != nil {
		return nil, err
	}

	return split, nil
}

func (s *splitsService) UndeleteSplit(id int64) types.SplitResponse {
	var resp types.SplitResponse

	split, err := s.getSplit(id)
	if err != nil {
		resp.Msg = fmt.Sprintf("Failed to find split: %s", err)
		return resp
	}

	if err := s.undeleteSplit(split); err != nil {
		resp.Msg = fmt.Sprintf("Failed to undelete split: %s", err)
		s.logger.Error(resp.Msg)
		return resp
	}

	resp.Msg = "Split undeleted"
	resp.Success = true

	return resp
}

func (s *splitsService) DeleteSplit(id int64) types.SplitResponse {
	var resp types.SplitResponse

	split, err := s.getSplit(id)
	if err != nil {
		resp.Msg = fmt.Sprintf("Failed to find split: %s", err)
		return resp
	}

	if err := s.deleteSplit(split); err != nil {
		resp.Msg = fmt.Sprintf("Failed to delete split: %s", err)
		s.logger.Error(resp.Msg)
		return resp
	}

	resp.Msg = "Split deleted"
	resp.Success = true

	return resp
}

func (s *splitsService) UpdateSplit(splitID, accountID int64, amount float64) types.SplitResponse {
	var resp types.SplitResponse
	var split *model.Split
	var err error

	split, err = s.updateSplit(splitID, accountID, amount)
	if err != nil {
		resp.Msg = err.Error()
		s.logger.Error(resp.Msg)
		return resp
	}

	resp = types.NewSplitResponse(*split)
	resp.Success = true
	return resp
}

func (s *splitsService) CreateSplit(transactionID, accountID int64, amount float64) types.JSResp {
	var resp types.JSResp

	if transactionID == 0 {
		resp.Msg = "Transaction must not be empty"
		s.logger.Error(resp.Msg)
		return resp
	}

	if accountID == 0 {
		resp.Msg = "Account must not be empty"
		s.logger.Error(resp.Msg)
		return resp
	}

	r := big.NewRat(int64(amount*float64(DefaultCurrencyDenominator)), DefaultCurrencyDenominator)

	split := model.Split{
		TransactionsID: transactionID,
		AccountID:      accountID,
		ValueNum:       r.Num().Int64(),
		ValueDenom:     r.Denom().Int64(),
	}

	if err := split.Insert(s.ctx, s.db, boil.Infer()); err != nil {
		resp.Msg = fmt.Sprintf("Failed to insert split: %s", err.Error())
		s.logger.Error(resp.Msg)
		return resp
	}
	resp.Msg = "Created split"
	resp.Success = true

	return resp
}