Files

214 lines
5.3 KiB
Go
Raw Permalink Normal View History

2018-10-19 15:31:41 -04:00
package model
import (
"errors"
"fmt"
"github.com/openaccounting/oa-server/core/model/types"
"github.com/openaccounting/oa-server/core/ws"
"time"
)
type TransactionInterface interface {
CreateTransaction(*types.Transaction) error
UpdateTransaction(string, *types.Transaction) error
GetTransactionsByAccount(string, string, string, *types.QueryOptions) ([]*types.Transaction, error)
GetTransactionsByOrg(string, string, *types.QueryOptions) ([]*types.Transaction, error)
DeleteTransaction(string, string, string) error
}
func (model *Model) CreateTransaction(transaction *types.Transaction) (err error) {
err = model.checkSplits(transaction)
if err != nil {
return
}
if transaction.Id == "" {
return errors.New("id required")
}
transaction.Inserted = time.Now()
transaction.Updated = time.Now()
if transaction.Date.IsZero() {
transaction.Date = transaction.Inserted
}
err = model.db.InsertTransaction(transaction)
if err != nil {
return
}
// Notify web socket subscribers
// TODO only get user ids that have permission to access transaction
userIds, err2 := model.db.GetOrgUserIds(transaction.OrgId)
if err2 == nil {
ws.PushTransaction(transaction, userIds, "create")
}
return
}
func (model *Model) UpdateTransaction(oldId string, transaction *types.Transaction) (err error) {
err = model.checkSplits(transaction)
if err != nil {
return
}
if oldId == "" || transaction.Id == "" {
return errors.New("id required")
}
// Get original transaction
original, err := model.getTransactionById(oldId)
if err != nil {
return
}
transaction.Updated = time.Now()
transaction.Inserted = original.Inserted
// We used to compare splits and if they hadn't changed just do an update
// on the transaction. The problem is then the updated field gets out of sync
// between the tranaction and its splits.
// It needs to be in sync for getTransactionsByOrg() to work correctly with pagination
// Delete old transaction and insert a new one
transaction.Inserted = transaction.Updated
err = model.db.DeleteAndInsertTransaction(oldId, transaction)
if err != nil {
return
}
// Notify web socket subscribers
// TODO only get user ids that have permission to access transaction
userIds, err2 := model.db.GetOrgUserIds(transaction.OrgId)
if err2 == nil {
ws.PushTransaction(original, userIds, "delete")
ws.PushTransaction(transaction, userIds, "create")
}
return
}
func (model *Model) GetTransactionsByAccount(orgId string, userId string, accountId string, options *types.QueryOptions) ([]*types.Transaction, error) {
userAccounts, err := model.GetAccounts(orgId, userId, "")
if err != nil {
return nil, err
}
if !model.accountsContainWriteAccess(userAccounts, accountId) {
return nil, errors.New(fmt.Sprintf("%s %s", "user does not have permission to access account", accountId))
}
return model.db.GetTransactionsByAccount(accountId, options)
}
func (model *Model) GetTransactionsByOrg(orgId string, userId string, options *types.QueryOptions) ([]*types.Transaction, error) {
userAccounts, err := model.GetAccounts(orgId, userId, "")
if err != nil {
return nil, err
}
var accountIds []string
for _, account := range userAccounts {
accountIds = append(accountIds, account.Id)
}
return model.db.GetTransactionsByOrg(orgId, options, accountIds)
}
func (model *Model) DeleteTransaction(id string, userId string, orgId string) (err error) {
transaction, err := model.getTransactionById(id)
if err != nil {
return
}
userAccounts, err := model.GetAccounts(orgId, userId, "")
if err != nil {
return
}
for _, split := range transaction.Splits {
if !model.accountsContainWriteAccess(userAccounts, split.AccountId) {
return errors.New(fmt.Sprintf("%s %s", "user does not have permission to access account", split.AccountId))
}
}
err = model.db.DeleteTransaction(id)
if err != nil {
return
}
// Notify web socket subscribers
// TODO only get user ids that have permission to access transaction
userIds, err2 := model.db.GetOrgUserIds(transaction.OrgId)
if err2 == nil {
ws.PushTransaction(transaction, userIds, "delete")
}
return
}
func (model *Model) getTransactionById(id string) (*types.Transaction, error) {
// TODO if this is made public, make a separate version that checks permission
return model.db.GetTransactionById(id)
}
func (model *Model) checkSplits(transaction *types.Transaction) (err error) {
if len(transaction.Splits) < 2 {
return errors.New("at least 2 splits are required")
}
org, err := model.GetOrg(transaction.OrgId, transaction.UserId)
if err != nil {
return
}
userAccounts, err := model.GetAccounts(transaction.OrgId, transaction.UserId, "")
if err != nil {
return
}
var amount int64 = 0
for _, split := range transaction.Splits {
if !model.accountsContainWriteAccess(userAccounts, split.AccountId) {
return errors.New(fmt.Sprintf("%s %s", "user does not have permission to access account", split.AccountId))
}
account := model.getAccountFromList(userAccounts, split.AccountId)
if account.HasChildren == true {
return errors.New("Cannot use parent account for split")
}
if account.Currency == org.Currency && split.NativeAmount != split.Amount {
return errors.New("nativeAmount must equal amount for native currency splits")
}
amount += split.NativeAmount
}
if amount != 0 {
return errors.New("splits must add up to 0")
}
return
}