2017-10-04 19:35:59 -04:00
|
|
|
package handlers
|
2015-06-25 22:36:58 -04:00
|
|
|
|
|
|
|
import (
|
2015-06-28 23:03:34 -04:00
|
|
|
"errors"
|
2017-12-02 06:14:47 -05:00
|
|
|
"github.com/aclindsa/moneygo/internal/models"
|
2017-12-08 21:27:03 -05:00
|
|
|
"github.com/aclindsa/moneygo/internal/store"
|
2015-06-28 23:03:34 -04:00
|
|
|
"log"
|
2015-06-25 22:36:58 -04:00
|
|
|
"math/big"
|
2015-06-28 23:03:34 -04:00
|
|
|
"net/http"
|
2015-07-11 08:58:36 -04:00
|
|
|
"net/url"
|
|
|
|
"strconv"
|
2015-06-25 22:36:58 -04:00
|
|
|
)
|
|
|
|
|
2016-02-11 06:08:05 -05:00
|
|
|
// Return a map of security ID's to big.Rat's containing the amount that
|
|
|
|
// security is imbalanced by
|
2017-12-09 05:56:45 -05:00
|
|
|
func GetTransactionImbalances(tx store.Tx, t *models.Transaction) (map[int64]big.Rat, error) {
|
2015-08-30 20:34:18 -04:00
|
|
|
sums := make(map[int64]big.Rat)
|
|
|
|
|
2015-06-28 23:03:34 -04:00
|
|
|
if !t.Valid() {
|
2016-02-11 06:08:05 -05:00
|
|
|
return nil, errors.New("Transaction invalid")
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
2016-02-11 06:08:05 -05:00
|
|
|
|
2015-06-28 23:03:34 -04:00
|
|
|
for i := range t.Splits {
|
2016-02-02 21:46:27 -05:00
|
|
|
securityid := t.Splits[i].SecurityId
|
|
|
|
if t.Splits[i].AccountId != -1 {
|
2016-02-15 11:28:44 -05:00
|
|
|
var err error
|
2017-12-04 05:55:25 -05:00
|
|
|
var account *models.Account
|
2017-12-07 20:47:55 -05:00
|
|
|
account, err = tx.GetAccount(t.Splits[i].AccountId, t.UserId)
|
2016-02-02 21:46:27 -05:00
|
|
|
if err != nil {
|
2016-02-11 06:08:05 -05:00
|
|
|
return nil, err
|
2016-02-02 21:46:27 -05:00
|
|
|
}
|
|
|
|
securityid = account.SecurityId
|
2015-08-30 20:34:18 -04:00
|
|
|
}
|
2015-06-28 23:03:34 -04:00
|
|
|
amount, _ := t.Splits[i].GetAmount()
|
2016-02-02 21:46:27 -05:00
|
|
|
sum := sums[securityid]
|
2015-08-30 20:34:18 -04:00
|
|
|
(&sum).Add(&sum, amount)
|
2016-02-02 21:46:27 -05:00
|
|
|
sums[securityid] = sum
|
2015-08-30 20:34:18 -04:00
|
|
|
}
|
2016-02-11 06:08:05 -05:00
|
|
|
return sums, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if all securities contained in this transaction are balanced,
|
|
|
|
// false otherwise
|
2017-12-09 05:56:45 -05:00
|
|
|
func TransactionBalanced(tx store.Tx, t *models.Transaction) (bool, error) {
|
2016-02-11 06:08:05 -05:00
|
|
|
var zero big.Rat
|
|
|
|
|
2017-12-04 05:55:25 -05:00
|
|
|
sums, err := GetTransactionImbalances(tx, t)
|
2016-02-11 06:08:05 -05:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2015-08-30 20:34:18 -04:00
|
|
|
for _, security_sum := range sums {
|
|
|
|
if security_sum.Cmp(&zero) != 0 {
|
2015-08-30 20:41:47 -04:00
|
|
|
return false, nil
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
}
|
2015-08-30 20:41:47 -04:00
|
|
|
return true, nil
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
|
2017-11-12 20:17:27 -05:00
|
|
|
func TransactionHandler(r *http.Request, context *Context) ResponseWriterWriter {
|
|
|
|
user, err := GetUserFromSession(context.Tx, r)
|
2015-06-28 23:03:34 -04:00
|
|
|
if err != nil {
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(1 /*Not Signed In*/)
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if r.Method == "POST" {
|
2017-12-04 05:55:25 -05:00
|
|
|
var transaction models.Transaction
|
2017-11-13 20:48:19 -05:00
|
|
|
if err := ReadJSON(r, &transaction); err != nil {
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(3 /*Invalid Request*/)
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
transaction.TransactionId = -1
|
|
|
|
transaction.UserId = user.UserId
|
|
|
|
|
2017-10-21 06:50:31 -04:00
|
|
|
if len(transaction.Splits) == 0 {
|
|
|
|
return NewError(3 /*Invalid Request*/)
|
|
|
|
}
|
|
|
|
|
2015-06-28 23:03:34 -04:00
|
|
|
for i := range transaction.Splits {
|
|
|
|
transaction.Splits[i].SplitId = -1
|
2017-12-07 20:47:55 -05:00
|
|
|
_, err := context.Tx.GetAccount(transaction.Splits[i].AccountId, user.UserId)
|
2015-06-28 23:03:34 -04:00
|
|
|
if err != nil {
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(3 /*Invalid Request*/)
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-04 05:55:25 -05:00
|
|
|
balanced, err := TransactionBalanced(context.Tx, &transaction)
|
2017-10-16 05:39:41 -04:00
|
|
|
if err != nil {
|
|
|
|
return NewError(999 /*Internal Error*/)
|
|
|
|
}
|
|
|
|
if !transaction.Valid() || !balanced {
|
|
|
|
return NewError(3 /*Invalid Request*/)
|
|
|
|
}
|
|
|
|
|
2017-12-08 21:27:03 -05:00
|
|
|
err = context.Tx.InsertTransaction(&transaction, user)
|
2015-06-28 23:03:34 -04:00
|
|
|
if err != nil {
|
2017-12-08 21:27:03 -05:00
|
|
|
if _, ok := err.(store.AccountMissingError); ok {
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(3 /*Invalid Request*/)
|
2015-06-28 23:03:34 -04:00
|
|
|
} else {
|
|
|
|
log.Print(err)
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(999 /*Internal Error*/)
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
2017-10-04 08:05:51 -04:00
|
|
|
}
|
|
|
|
|
2017-10-14 14:20:50 -04:00
|
|
|
return &transaction
|
2015-06-28 23:03:34 -04:00
|
|
|
} else if r.Method == "GET" {
|
2017-11-12 21:12:49 -05:00
|
|
|
if context.LastLevel() {
|
2015-06-28 23:03:34 -04:00
|
|
|
//Return all Transactions
|
2017-12-04 05:55:25 -05:00
|
|
|
var al models.TransactionList
|
2017-12-08 21:27:03 -05:00
|
|
|
transactions, err := context.Tx.GetTransactions(user.UserId)
|
2015-06-28 23:03:34 -04:00
|
|
|
if err != nil {
|
|
|
|
log.Print(err)
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(999 /*Internal Error*/)
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
al.Transactions = transactions
|
2017-10-14 14:20:50 -04:00
|
|
|
return &al
|
2015-06-28 23:03:34 -04:00
|
|
|
} else {
|
|
|
|
//Return Transaction with this Id
|
2017-11-12 21:12:49 -05:00
|
|
|
transactionid, err := context.NextID()
|
|
|
|
if err != nil {
|
|
|
|
return NewError(3 /*Invalid Request*/)
|
|
|
|
}
|
2017-12-08 21:27:03 -05:00
|
|
|
transaction, err := context.Tx.GetTransaction(transactionid, user.UserId)
|
2015-06-28 23:03:34 -04:00
|
|
|
if err != nil {
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(3 /*Invalid Request*/)
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
2017-10-14 14:20:50 -04:00
|
|
|
return transaction
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
} else {
|
2017-11-12 21:12:49 -05:00
|
|
|
transactionid, err := context.NextID()
|
2015-06-28 23:03:34 -04:00
|
|
|
if err != nil {
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(3 /*Invalid Request*/)
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
if r.Method == "PUT" {
|
2017-12-04 05:55:25 -05:00
|
|
|
var transaction models.Transaction
|
2017-11-13 20:48:19 -05:00
|
|
|
if err := ReadJSON(r, &transaction); err != nil || transaction.TransactionId != transactionid {
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(3 /*Invalid Request*/)
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
transaction.UserId = user.UserId
|
|
|
|
|
2017-12-04 05:55:25 -05:00
|
|
|
balanced, err := TransactionBalanced(context.Tx, &transaction)
|
2015-08-30 20:41:47 -04:00
|
|
|
if err != nil {
|
|
|
|
log.Print(err)
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(999 /*Internal Error*/)
|
2015-08-30 20:41:47 -04:00
|
|
|
}
|
|
|
|
if !transaction.Valid() || !balanced {
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(3 /*Invalid Request*/)
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
|
2017-10-21 06:50:31 -04:00
|
|
|
if len(transaction.Splits) == 0 {
|
|
|
|
return NewError(3 /*Invalid Request*/)
|
|
|
|
}
|
|
|
|
|
2015-06-28 23:03:34 -04:00
|
|
|
for i := range transaction.Splits {
|
2017-12-07 20:47:55 -05:00
|
|
|
_, err := context.Tx.GetAccount(transaction.Splits[i].AccountId, user.UserId)
|
2015-06-28 23:03:34 -04:00
|
|
|
if err != nil {
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(3 /*Invalid Request*/)
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-08 21:27:03 -05:00
|
|
|
err = context.Tx.UpdateTransaction(&transaction, user)
|
2017-10-04 08:05:51 -04:00
|
|
|
if err != nil {
|
|
|
|
log.Print(err)
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(999 /*Internal Error*/)
|
2017-10-04 08:05:51 -04:00
|
|
|
}
|
|
|
|
|
2017-10-14 14:20:50 -04:00
|
|
|
return &transaction
|
2015-06-28 23:03:34 -04:00
|
|
|
} else if r.Method == "DELETE" {
|
2017-12-08 21:27:03 -05:00
|
|
|
transaction, err := context.Tx.GetTransaction(transactionid, user.UserId)
|
2015-06-28 23:03:34 -04:00
|
|
|
if err != nil {
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(3 /*Invalid Request*/)
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
|
2017-12-08 21:27:03 -05:00
|
|
|
err = context.Tx.DeleteTransaction(transaction, user)
|
2015-06-29 07:25:29 -04:00
|
|
|
if err != nil {
|
2015-06-28 23:03:34 -04:00
|
|
|
log.Print(err)
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(999 /*Internal Error*/)
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
|
2017-10-14 14:20:50 -04:00
|
|
|
return SuccessWriter{}
|
2015-06-28 23:03:34 -04:00
|
|
|
}
|
|
|
|
}
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(3 /*Invalid Request*/)
|
2015-06-25 22:36:58 -04:00
|
|
|
}
|
2015-07-11 08:58:36 -04:00
|
|
|
|
2017-12-08 21:27:03 -05:00
|
|
|
func BalanceFromSplits(splits *[]*models.Split) (*big.Rat, error) {
|
2017-02-19 07:54:27 -05:00
|
|
|
var balance, tmp big.Rat
|
2017-12-08 21:27:03 -05:00
|
|
|
for _, s := range *splits {
|
2017-12-04 05:55:25 -05:00
|
|
|
rat_amount, err := models.GetBigAmount(s.Amount)
|
2017-02-19 07:54:27 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
tmp.Add(&balance, rat_amount)
|
|
|
|
balance.Set(&tmp)
|
2017-01-30 21:04:18 -05:00
|
|
|
}
|
|
|
|
|
2017-02-19 07:54:27 -05:00
|
|
|
return &balance, nil
|
2017-01-30 21:04:18 -05:00
|
|
|
}
|
|
|
|
|
2015-07-11 08:58:36 -04:00
|
|
|
// Return only those transactions which have at least one split pertaining to
|
|
|
|
// an account
|
2017-12-02 06:14:47 -05:00
|
|
|
func AccountTransactionsHandler(context *Context, r *http.Request, user *models.User, accountid int64) ResponseWriterWriter {
|
2015-07-11 08:58:36 -04:00
|
|
|
var page uint64 = 0
|
|
|
|
var limit uint64 = 50
|
|
|
|
var sort string = "date-desc"
|
|
|
|
|
|
|
|
query, _ := url.ParseQuery(r.URL.RawQuery)
|
|
|
|
|
|
|
|
pagestring := query.Get("page")
|
|
|
|
if pagestring != "" {
|
|
|
|
p, err := strconv.ParseUint(pagestring, 10, 0)
|
|
|
|
if err != nil {
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(3 /*Invalid Request*/)
|
2015-07-11 08:58:36 -04:00
|
|
|
}
|
|
|
|
page = p
|
|
|
|
}
|
|
|
|
|
|
|
|
limitstring := query.Get("limit")
|
|
|
|
if limitstring != "" {
|
|
|
|
l, err := strconv.ParseUint(limitstring, 10, 0)
|
|
|
|
if err != nil || l > 100 {
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(3 /*Invalid Request*/)
|
2015-07-11 08:58:36 -04:00
|
|
|
}
|
|
|
|
limit = l
|
|
|
|
}
|
|
|
|
|
|
|
|
sortstring := query.Get("sort")
|
|
|
|
if sortstring != "" {
|
|
|
|
if sortstring != "date-asc" && sortstring != "date-desc" {
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(3 /*Invalid Request*/)
|
2015-07-11 08:58:36 -04:00
|
|
|
}
|
|
|
|
sort = sortstring
|
|
|
|
}
|
|
|
|
|
2017-12-08 21:27:03 -05:00
|
|
|
accountTransactions, err := context.Tx.GetAccountTransactions(user, accountid, sort, page, limit)
|
2015-07-11 08:58:36 -04:00
|
|
|
if err != nil {
|
|
|
|
log.Print(err)
|
2017-10-14 14:20:50 -04:00
|
|
|
return NewError(999 /*Internal Error*/)
|
2015-07-11 08:58:36 -04:00
|
|
|
}
|
|
|
|
|
2017-10-14 14:20:50 -04:00
|
|
|
return accountTransactions
|
2015-07-11 08:58:36 -04:00
|
|
|
}
|