You've already forked openaccounting-server
mirror of
https://github.com/openaccounting/oa-server.git
synced 2025-12-09 00:50:59 +13:00
214 lines
5.3 KiB
Go
214 lines
5.3 KiB
Go
|
|
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
|
||
|
|
}
|