2017-10-04 19:35:59 -04:00
package handlers
2015-06-25 22:36:58 -04:00
2015-06-27 17:46:06 -04:00
import (
"errors"
2017-12-03 06:38:22 -05:00
"github.com/aclindsa/moneygo/internal/models"
2015-06-27 17:46:06 -04:00
"log"
"net/http"
)
2017-12-04 05:55:25 -05:00
func GetAccount ( tx * Tx , accountid int64 , userid int64 ) ( * models . Account , error ) {
var a models . Account
2015-06-27 17:46:06 -04:00
2017-10-14 14:20:50 -04:00
err := tx . SelectOne ( & a , "SELECT * from accounts where UserId=? AND AccountId=?" , userid , accountid )
2015-06-27 17:46:06 -04:00
if err != nil {
return nil , err
}
return & a , nil
}
2017-12-04 05:55:25 -05:00
func GetAccounts ( tx * Tx , userid int64 ) ( * [ ] models . Account , error ) {
var accounts [ ] models . Account
2015-06-27 17:46:06 -04:00
2017-10-14 14:20:50 -04:00
_ , err := tx . Select ( & accounts , "SELECT * from accounts where UserId=?" , userid )
2015-06-27 17:46:06 -04:00
if err != nil {
return nil , err
}
return & accounts , nil
}
2016-02-15 11:28:44 -05:00
// Get (and attempt to create if it doesn't exist). Matches on UserId,
// SecurityId, Type, Name, and ParentAccountId
2017-12-04 05:55:25 -05:00
func GetCreateAccount ( tx * Tx , a models . Account ) ( * models . Account , error ) {
var accounts [ ] models . Account
var account models . Account
2016-02-10 18:36:11 -05:00
// Try to find the top-level trading account
2017-10-14 19:41:13 -04:00
_ , err := tx . Select ( & accounts , "SELECT * from accounts where UserId=? AND SecurityId=? AND Type=? AND Name=? AND ParentAccountId=? ORDER BY AccountId ASC LIMIT 1" , a . UserId , a . SecurityId , a . Type , a . Name , a . ParentAccountId )
2016-02-10 18:36:11 -05:00
if err != nil {
return nil , err
}
if len ( accounts ) == 1 {
account = accounts [ 0 ]
} else {
2016-02-15 11:28:44 -05:00
account . UserId = a . UserId
account . SecurityId = a . SecurityId
account . Type = a . Type
account . Name = a . Name
account . ParentAccountId = a . ParentAccountId
2016-02-10 18:36:11 -05:00
2017-10-14 19:41:13 -04:00
err = tx . Insert ( & account )
2016-02-11 05:53:44 -05:00
if err != nil {
return nil , err
}
}
return & account , nil
}
// Get (and attempt to create if it doesn't exist) the security/currency
2016-02-15 11:28:44 -05:00
// trading account for the supplied security/currency
2017-12-04 05:55:25 -05:00
func GetTradingAccount ( tx * Tx , userid int64 , securityid int64 ) ( * models . Account , error ) {
var tradingAccount models . Account
var account models . Account
2016-02-11 05:53:44 -05:00
2017-10-14 19:41:13 -04:00
user , err := GetUser ( tx , userid )
2017-09-21 21:00:30 -04:00
if err != nil {
return nil , err
}
2016-02-15 11:28:44 -05:00
tradingAccount . UserId = userid
2017-12-04 05:55:25 -05:00
tradingAccount . Type = models . Trading
2016-02-15 11:28:44 -05:00
tradingAccount . Name = "Trading"
2017-09-21 21:00:30 -04:00
tradingAccount . SecurityId = user . DefaultCurrency
2016-02-15 11:28:44 -05:00
tradingAccount . ParentAccountId = - 1
// Find/create the top-level trading account
2017-10-14 19:41:13 -04:00
ta , err := GetCreateAccount ( tx , tradingAccount )
2016-02-11 05:53:44 -05:00
if err != nil {
return nil , err
}
2017-10-14 19:41:13 -04:00
security , err := GetSecurity ( tx , securityid , userid )
2016-10-16 08:19:11 -04:00
if err != nil {
return nil , err
}
2016-02-15 11:28:44 -05:00
account . UserId = userid
account . Name = security . Name
account . ParentAccountId = ta . AccountId
account . SecurityId = securityid
2017-12-04 05:55:25 -05:00
account . Type = models . Trading
2016-02-15 11:28:44 -05:00
2017-10-14 19:41:13 -04:00
a , err := GetCreateAccount ( tx , account )
2016-02-11 05:53:44 -05:00
if err != nil {
return nil , err
}
2016-02-15 11:28:44 -05:00
return a , nil
}
// Get (and attempt to create if it doesn't exist) the security/currency
// imbalance account for the supplied security/currency
2017-12-04 05:55:25 -05:00
func GetImbalanceAccount ( tx * Tx , userid int64 , securityid int64 ) ( * models . Account , error ) {
var imbalanceAccount models . Account
var account models . Account
2017-12-03 06:38:22 -05:00
xxxtemplate := FindSecurityTemplate ( "XXX" , models . Currency )
2017-06-04 16:01:42 -04:00
if xxxtemplate == nil {
return nil , errors . New ( "Couldn't find XXX security template" )
}
2017-10-14 19:41:13 -04:00
xxxsecurity , err := ImportGetCreateSecurity ( tx , userid , xxxtemplate )
2017-06-04 16:01:42 -04:00
if err != nil {
return nil , errors . New ( "Couldn't create XXX security" )
}
2016-02-11 05:53:44 -05:00
2016-02-15 11:28:44 -05:00
imbalanceAccount . UserId = userid
imbalanceAccount . Name = "Imbalances"
imbalanceAccount . ParentAccountId = - 1
2017-06-04 16:01:42 -04:00
imbalanceAccount . SecurityId = xxxsecurity . SecurityId
2017-12-04 05:55:25 -05:00
imbalanceAccount . Type = models . Bank
2016-02-15 11:28:44 -05:00
// Find/create the top-level trading account
2017-10-14 19:41:13 -04:00
ia , err := GetCreateAccount ( tx , imbalanceAccount )
2016-02-11 05:53:44 -05:00
if err != nil {
return nil , err
}
2017-10-14 19:41:13 -04:00
security , err := GetSecurity ( tx , securityid , userid )
2016-10-16 08:19:11 -04:00
if err != nil {
return nil , err
}
2016-02-15 11:28:44 -05:00
account . UserId = userid
account . Name = security . Name
account . ParentAccountId = ia . AccountId
account . SecurityId = securityid
2017-12-04 05:55:25 -05:00
account . Type = models . Bank
2016-02-10 18:36:11 -05:00
2017-10-14 19:41:13 -04:00
a , err := GetCreateAccount ( tx , account )
2016-02-10 18:36:11 -05:00
if err != nil {
return nil , err
}
2016-02-15 11:28:44 -05:00
return a , nil
2016-02-10 18:36:11 -05:00
}
2015-06-27 17:46:06 -04:00
type ParentAccountMissingError struct { }
func ( pame ParentAccountMissingError ) Error ( ) string {
return "Parent account missing"
}
2017-10-12 21:20:13 -04:00
type TooMuchNestingError struct { }
func ( tmne TooMuchNestingError ) Error ( ) string {
return "Too much nesting"
}
type CircularAccountsError struct { }
func ( cae CircularAccountsError ) Error ( ) string {
return "Would result in circular account relationship"
}
2017-12-04 05:55:25 -05:00
func insertUpdateAccount ( tx * Tx , a * models . Account , insert bool ) error {
2017-10-12 21:20:13 -04:00
found := make ( map [ int64 ] bool )
if ! insert {
found [ a . AccountId ] = true
}
parentid := a . ParentAccountId
depth := 0
for parentid != - 1 {
depth += 1
if depth > 100 {
return TooMuchNestingError { }
2015-06-27 17:46:06 -04:00
}
2017-10-12 21:20:13 -04:00
2017-12-04 05:55:25 -05:00
var a models . Account
2017-10-14 14:20:50 -04:00
err := tx . SelectOne ( & a , "SELECT * from accounts where AccountId=?" , parentid )
2017-10-12 21:20:13 -04:00
if err != nil {
2015-06-27 17:46:06 -04:00
return ParentAccountMissingError { }
}
2017-10-12 21:20:13 -04:00
// Insertion by itself can never result in circular dependencies
if insert {
break
}
found [ parentid ] = true
parentid = a . ParentAccountId
if _ , ok := found [ parentid ] ; ok {
return CircularAccountsError { }
}
2015-06-27 17:46:06 -04:00
}
if insert {
2017-10-14 14:20:50 -04:00
err := tx . Insert ( a )
2015-06-27 17:46:06 -04:00
if err != nil {
return err
}
} else {
2017-10-14 19:41:13 -04:00
oldacct , err := GetAccount ( tx , a . AccountId , a . UserId )
2015-07-11 08:58:36 -04:00
if err != nil {
return err
}
2015-08-05 21:25:25 -04:00
a . AccountVersion = oldacct . AccountVersion + 1
2015-07-11 08:58:36 -04:00
2017-10-14 14:20:50 -04:00
count , err := tx . Update ( a )
2015-06-27 17:46:06 -04:00
if err != nil {
return err
}
if count != 1 {
return errors . New ( "Updated more than one account" )
}
}
return nil
}
2017-12-04 05:55:25 -05:00
func InsertAccount ( tx * Tx , a * models . Account ) error {
2017-10-14 14:20:50 -04:00
return insertUpdateAccount ( tx , a , true )
2015-06-27 17:46:06 -04:00
}
2017-12-04 05:55:25 -05:00
func UpdateAccount ( tx * Tx , a * models . Account ) error {
2017-10-14 14:20:50 -04:00
return insertUpdateAccount ( tx , a , false )
2015-06-27 17:46:06 -04:00
}
2017-12-04 05:55:25 -05:00
func DeleteAccount ( tx * Tx , a * models . Account ) error {
2015-07-04 21:11:00 -04:00
if a . ParentAccountId != - 1 {
// Re-parent splits to this account's parent account if this account isn't a root account
2017-10-14 14:20:50 -04:00
_ , err := tx . Exec ( "UPDATE splits SET AccountId=? WHERE AccountId=?" , a . ParentAccountId , a . AccountId )
2015-07-04 21:11:00 -04:00
if err != nil {
return err
}
} else {
// Delete splits if this account is a root account
2017-10-14 14:20:50 -04:00
_ , err := tx . Exec ( "DELETE FROM splits WHERE AccountId=?" , a . AccountId )
2015-07-04 21:11:00 -04:00
if err != nil {
return err
}
2015-06-29 07:25:48 -04:00
}
// Re-parent child accounts to this account's parent account
2017-10-14 14:20:50 -04:00
_ , err := tx . Exec ( "UPDATE accounts SET ParentAccountId=? WHERE ParentAccountId=?" , a . ParentAccountId , a . AccountId )
2015-06-29 07:25:48 -04:00
if err != nil {
return err
}
2017-10-14 14:20:50 -04:00
count , err := tx . Delete ( a )
2015-06-29 07:25:48 -04:00
if err != nil {
return err
}
if count != 1 {
return errors . New ( "Was going to delete more than one account" )
}
return nil
}
2017-11-12 20:17:27 -05:00
func AccountHandler ( r * http . Request , context * Context ) ResponseWriterWriter {
user , err := GetUserFromSession ( context . Tx , r )
2015-06-27 17:46:06 -04:00
if err != nil {
2017-10-14 14:20:50 -04:00
return NewError ( 1 /*Not Signed In*/ )
2015-06-27 17:46:06 -04:00
}
if r . Method == "POST" {
2017-11-12 21:12:49 -05:00
if ! context . LastLevel ( ) {
accountid , err := context . NextID ( )
if err != nil || context . NextLevel ( ) != "imports" {
return NewError ( 3 /*Invalid Request*/ )
2016-02-02 21:46:27 -05:00
}
2017-11-12 21:12:49 -05:00
return AccountImportHandler ( context , r , user , accountid )
2016-02-02 21:46:27 -05:00
}
2017-12-04 05:55:25 -05:00
var account models . Account
2017-11-13 20:48:19 -05:00
if err := ReadJSON ( r , & account ) ; err != nil {
2017-10-14 14:20:50 -04:00
return NewError ( 3 /*Invalid Request*/ )
2015-06-27 17:46:06 -04:00
}
account . AccountId = - 1
account . UserId = user . UserId
2015-08-05 21:25:25 -04:00
account . AccountVersion = 0
2015-06-27 17:46:06 -04:00
2017-11-12 20:17:27 -05:00
security , err := GetSecurity ( context . Tx , account . SecurityId , user . UserId )
2016-10-16 08:19:11 -04:00
if err != nil {
log . Print ( err )
2017-10-14 14:20:50 -04:00
return NewError ( 999 /*Internal Error*/ )
2016-10-16 08:19:11 -04:00
}
if security == nil {
2017-10-14 14:20:50 -04:00
return NewError ( 3 /*Invalid Request*/ )
2015-06-27 17:46:06 -04:00
}
2017-11-12 20:17:27 -05:00
err = InsertAccount ( context . Tx , & account )
2015-06-27 17:46:06 -04:00
if err != nil {
if _ , ok := err . ( ParentAccountMissingError ) ; ok {
2017-10-14 14:20:50 -04:00
return NewError ( 3 /*Invalid Request*/ )
2015-06-27 17:46:06 -04:00
} else {
log . Print ( err )
2017-10-14 14:20:50 -04:00
return NewError ( 999 /*Internal Error*/ )
2015-06-27 17:46:06 -04:00
}
}
2017-10-14 14:20:50 -04:00
return ResponseWrapper { 201 , & account }
2015-06-27 17:46:06 -04:00
} else if r . Method == "GET" {
2017-11-12 21:12:49 -05:00
if context . LastLevel ( ) {
2015-06-27 17:46:06 -04:00
//Return all Accounts
2017-12-04 05:55:25 -05:00
var al models . AccountList
2017-11-12 20:17:27 -05:00
accounts , err := GetAccounts ( context . Tx , user . UserId )
2015-06-27 17:46:06 -04:00
if err != nil {
log . Print ( err )
2017-10-14 14:20:50 -04:00
return NewError ( 999 /*Internal Error*/ )
2015-06-27 17:46:06 -04:00
}
al . Accounts = accounts
2017-10-14 14:20:50 -04:00
return & al
2017-11-12 21:12:49 -05:00
}
accountid , err := context . NextID ( )
if err != nil {
return NewError ( 3 /*Invalid Request*/ )
}
2015-07-11 08:58:36 -04:00
2017-11-12 21:12:49 -05:00
if context . LastLevel ( ) {
2015-06-29 07:25:48 -04:00
// Return Account with this Id
2017-11-12 20:17:27 -05:00
account , err := GetAccount ( context . Tx , accountid , user . UserId )
2015-06-27 17:46:06 -04:00
if err != nil {
2017-10-14 14:20:50 -04:00
return NewError ( 3 /*Invalid Request*/ )
2015-06-27 17:46:06 -04:00
}
2015-07-11 08:58:36 -04:00
2017-10-14 14:20:50 -04:00
return account
2017-11-12 21:12:49 -05:00
} else if context . NextLevel ( ) == "transactions" {
return AccountTransactionsHandler ( context , r , user , accountid )
2015-06-27 17:46:06 -04:00
}
} else {
2017-11-12 21:12:49 -05:00
accountid , err := context . NextID ( )
2015-06-27 17:46:06 -04:00
if err != nil {
2017-10-14 14:20:50 -04:00
return NewError ( 3 /*Invalid Request*/ )
2015-06-27 17:46:06 -04:00
}
if r . Method == "PUT" {
2017-12-04 05:55:25 -05:00
var account models . Account
2017-11-13 20:48:19 -05:00
if err := ReadJSON ( r , & account ) ; err != nil || account . AccountId != accountid {
2017-10-14 14:20:50 -04:00
return NewError ( 3 /*Invalid Request*/ )
2015-06-27 17:46:06 -04:00
}
account . UserId = user . UserId
2017-11-12 20:17:27 -05:00
security , err := GetSecurity ( context . Tx , account . SecurityId , user . UserId )
2016-10-16 08:19:11 -04:00
if err != nil {
log . Print ( err )
2017-10-14 14:20:50 -04:00
return NewError ( 999 /*Internal Error*/ )
2016-10-16 08:19:11 -04:00
}
if security == nil {
2017-10-14 14:20:50 -04:00
return NewError ( 3 /*Invalid Request*/ )
2015-06-27 17:46:06 -04:00
}
2017-10-11 21:19:14 -04:00
if account . ParentAccountId == account . AccountId {
2017-10-14 14:20:50 -04:00
return NewError ( 3 /*Invalid Request*/ )
2017-10-11 21:19:14 -04:00
}
2017-11-12 20:17:27 -05:00
err = UpdateAccount ( context . Tx , & account )
2015-06-27 17:46:06 -04:00
if err != nil {
2017-10-11 21:19:14 -04:00
if _ , ok := err . ( ParentAccountMissingError ) ; ok {
2017-10-14 14:20:50 -04:00
return NewError ( 3 /*Invalid Request*/ )
2017-10-12 21:20:13 -04:00
} else if _ , ok := err . ( CircularAccountsError ) ; ok {
2017-10-14 14:20:50 -04:00
return NewError ( 3 /*Invalid Request*/ )
2017-10-11 21:19:14 -04:00
} else {
log . Print ( err )
2017-10-14 14:20:50 -04:00
return NewError ( 999 /*Internal Error*/ )
2017-10-11 21:19:14 -04:00
}
2015-06-27 17:46:06 -04:00
}
2017-10-14 14:20:50 -04:00
return & account
2015-06-27 17:46:06 -04:00
} else if r . Method == "DELETE" {
2017-11-12 20:17:27 -05:00
account , err := GetAccount ( context . Tx , accountid , user . UserId )
2015-06-27 17:46:06 -04:00
if err != nil {
2017-10-14 14:20:50 -04:00
return NewError ( 3 /*Invalid Request*/ )
2015-06-27 17:46:06 -04:00
}
2017-11-12 20:17:27 -05:00
err = DeleteAccount ( context . Tx , account )
2015-06-29 07:25:48 -04:00
if err != nil {
2015-06-27 17:46:06 -04:00
log . Print ( err )
2017-10-14 14:20:50 -04:00
return NewError ( 999 /*Internal Error*/ )
2015-06-27 17:46:06 -04:00
}
2017-10-14 14:20:50 -04:00
return SuccessWriter { }
2015-06-27 17:46:06 -04:00
}
}
2017-10-14 14:20:50 -04:00
return NewError ( 3 /*Invalid Request*/ )
2015-06-25 22:36:58 -04:00
}