mirror of
https://github.com/aclindsa/moneygo.git
synced 2024-12-25 23:23:21 -05:00
Split securities into models
This commit is contained in:
parent
3f4d6d15a1
commit
f72c86ef58
@ -37,7 +37,7 @@ func GetDbMap(db *sql.DB, dbtype config.DbType) (*gorp.DbMap, error) {
|
|||||||
dbmap.AddTableWithName(models.User{}, "users").SetKeys(true, "UserId")
|
dbmap.AddTableWithName(models.User{}, "users").SetKeys(true, "UserId")
|
||||||
dbmap.AddTableWithName(models.Session{}, "sessions").SetKeys(true, "SessionId")
|
dbmap.AddTableWithName(models.Session{}, "sessions").SetKeys(true, "SessionId")
|
||||||
dbmap.AddTableWithName(handlers.Account{}, "accounts").SetKeys(true, "AccountId")
|
dbmap.AddTableWithName(handlers.Account{}, "accounts").SetKeys(true, "AccountId")
|
||||||
dbmap.AddTableWithName(handlers.Security{}, "securities").SetKeys(true, "SecurityId")
|
dbmap.AddTableWithName(models.Security{}, "securities").SetKeys(true, "SecurityId")
|
||||||
dbmap.AddTableWithName(handlers.Transaction{}, "transactions").SetKeys(true, "TransactionId")
|
dbmap.AddTableWithName(handlers.Transaction{}, "transactions").SetKeys(true, "TransactionId")
|
||||||
dbmap.AddTableWithName(handlers.Split{}, "splits").SetKeys(true, "SplitId")
|
dbmap.AddTableWithName(handlers.Split{}, "splits").SetKeys(true, "SplitId")
|
||||||
dbmap.AddTableWithName(handlers.Price{}, "prices").SetKeys(true, "PriceId")
|
dbmap.AddTableWithName(handlers.Price{}, "prices").SetKeys(true, "PriceId")
|
||||||
|
@ -3,6 +3,7 @@ package handlers
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/aclindsa/moneygo/internal/models"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@ -214,7 +215,7 @@ func GetTradingAccount(tx *Tx, userid int64, securityid int64) (*Account, error)
|
|||||||
func GetImbalanceAccount(tx *Tx, userid int64, securityid int64) (*Account, error) {
|
func GetImbalanceAccount(tx *Tx, userid int64, securityid int64) (*Account, error) {
|
||||||
var imbalanceAccount Account
|
var imbalanceAccount Account
|
||||||
var account Account
|
var account Account
|
||||||
xxxtemplate := FindSecurityTemplate("XXX", Currency)
|
xxxtemplate := FindSecurityTemplate("XXX", models.Currency)
|
||||||
if xxxtemplate == nil {
|
if xxxtemplate == nil {
|
||||||
return nil, errors.New("Couldn't find XXX security template")
|
return nil, errors.New("Couldn't find XXX security template")
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/aclindsa/moneygo/internal/models"
|
||||||
"github.com/yuin/gopher-lua"
|
"github.com/yuin/gopher-lua"
|
||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Balance struct {
|
type Balance struct {
|
||||||
Security *Security
|
Security *models.Security
|
||||||
Amount *big.Rat
|
Amount *big.Rat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/aclindsa/moneygo/internal/models"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
@ -22,7 +23,7 @@ type GnucashXMLCommodity struct {
|
|||||||
XCode string `xml:"http://www.gnucash.org/XML/cmdty xcode"`
|
XCode string `xml:"http://www.gnucash.org/XML/cmdty xcode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GnucashCommodity struct{ Security }
|
type GnucashCommodity struct{ models.Security }
|
||||||
|
|
||||||
func (gc *GnucashCommodity) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
func (gc *GnucashCommodity) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
var gxc GnucashXMLCommodity
|
var gxc GnucashXMLCommodity
|
||||||
@ -35,12 +36,12 @@ func (gc *GnucashCommodity) UnmarshalXML(d *xml.Decoder, start xml.StartElement)
|
|||||||
gc.Description = gxc.Description
|
gc.Description = gxc.Description
|
||||||
gc.AlternateId = gxc.XCode
|
gc.AlternateId = gxc.XCode
|
||||||
|
|
||||||
gc.Security.Type = Stock // assumed default
|
gc.Security.Type = models.Stock // assumed default
|
||||||
if gxc.Type == "ISO4217" {
|
if gxc.Type == "ISO4217" {
|
||||||
gc.Security.Type = Currency
|
gc.Security.Type = models.Currency
|
||||||
// Get the number from our templates for the AlternateId because
|
// Get the number from our templates for the AlternateId because
|
||||||
// Gnucash uses 'id' (our Name) to supply the string ISO4217 code
|
// Gnucash uses 'id' (our Name) to supply the string ISO4217 code
|
||||||
template := FindSecurityTemplate(gxc.Name, Currency)
|
template := FindSecurityTemplate(gxc.Name, models.Currency)
|
||||||
if template == nil {
|
if template == nil {
|
||||||
return errors.New("Unable to find security template for Gnucash ISO4217 commodity")
|
return errors.New("Unable to find security template for Gnucash ISO4217 commodity")
|
||||||
}
|
}
|
||||||
@ -125,7 +126,7 @@ type GnucashXMLImport struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GnucashImport struct {
|
type GnucashImport struct {
|
||||||
Securities []Security
|
Securities []models.Security
|
||||||
Accounts []Account
|
Accounts []Account
|
||||||
Transactions []Transaction
|
Transactions []Transaction
|
||||||
Prices []Price
|
Prices []Price
|
||||||
@ -143,7 +144,7 @@ func ImportGnucash(r io.Reader) (*GnucashImport, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fixup securities, making a map of them as we go
|
// Fixup securities, making a map of them as we go
|
||||||
securityMap := make(map[string]Security)
|
securityMap := make(map[string]models.Security)
|
||||||
for i := range gncxml.Commodities {
|
for i := range gncxml.Commodities {
|
||||||
s := gncxml.Commodities[i].Security
|
s := gncxml.Commodities[i].Security
|
||||||
s.SecurityId = int64(i + 1)
|
s.SecurityId = int64(i + 1)
|
||||||
@ -169,7 +170,7 @@ func ImportGnucash(r io.Reader) (*GnucashImport, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("Unable to find currency '%s' for price '%s'", price.Currency.Name, price.Id)
|
return nil, fmt.Errorf("Unable to find currency '%s' for price '%s'", price.Currency.Name, price.Id)
|
||||||
}
|
}
|
||||||
if currency.Type != Currency {
|
if currency.Type != models.Currency {
|
||||||
return nil, fmt.Errorf("Currency for imported price isn't actually a currency\n")
|
return nil, fmt.Errorf("Currency for imported price isn't actually a currency\n")
|
||||||
}
|
}
|
||||||
p.PriceId = int64(i + 1)
|
p.PriceId = int64(i + 1)
|
||||||
|
@ -2,6 +2,7 @@ package handlers_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/aclindsa/moneygo/internal/handlers"
|
"github.com/aclindsa/moneygo/internal/handlers"
|
||||||
|
"github.com/aclindsa/moneygo/internal/models"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -94,7 +95,7 @@ func TestImportGnucash(t *testing.T) {
|
|||||||
accountBalanceHelper(t, d.clients[0], groceries, "287.56") // 87.19 from preexisting transactions and 200.37 from Gnucash
|
accountBalanceHelper(t, d.clients[0], groceries, "287.56") // 87.19 from preexisting transactions and 200.37 from Gnucash
|
||||||
accountBalanceHelper(t, d.clients[0], cable, "89.98")
|
accountBalanceHelper(t, d.clients[0], cable, "89.98")
|
||||||
|
|
||||||
var ge *handlers.Security
|
var ge *models.Security
|
||||||
securities, err := getSecurities(d.clients[0])
|
securities, err := getSecurities(d.clients[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error fetching securities: %s\n", err)
|
t.Fatalf("Error fetching securities: %s\n", err)
|
||||||
|
@ -57,7 +57,7 @@ func ofxImportHelper(tx *Tx, r io.Reader, user *models.User, accountid int64) Re
|
|||||||
// Find matching existing securities or create new ones for those
|
// Find matching existing securities or create new ones for those
|
||||||
// referenced by the OFX import. Also create a map from placeholder import
|
// referenced by the OFX import. Also create a map from placeholder import
|
||||||
// SecurityIds to the actual SecurityIDs
|
// SecurityIds to the actual SecurityIDs
|
||||||
var securitymap = make(map[int64]Security)
|
var securitymap = make(map[int64]models.Security)
|
||||||
for _, ofxsecurity := range itl.Securities {
|
for _, ofxsecurity := range itl.Securities {
|
||||||
// save off since ImportGetCreateSecurity overwrites SecurityId on
|
// save off since ImportGetCreateSecurity overwrites SecurityId on
|
||||||
// ofxsecurity
|
// ofxsecurity
|
||||||
|
@ -3,26 +3,27 @@ package handlers
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/aclindsa/moneygo/internal/models"
|
||||||
"github.com/aclindsa/ofxgo"
|
"github.com/aclindsa/ofxgo"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OFXImport struct {
|
type OFXImport struct {
|
||||||
Securities []Security
|
Securities []models.Security
|
||||||
Accounts []Account
|
Accounts []Account
|
||||||
Transactions []Transaction
|
Transactions []Transaction
|
||||||
// Balances map[int64]string // map AccountIDs to ending balances
|
// Balances map[int64]string // map AccountIDs to ending balances
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *OFXImport) GetSecurity(ofxsecurityid int64) (*Security, error) {
|
func (i *OFXImport) GetSecurity(ofxsecurityid int64) (*models.Security, error) {
|
||||||
if ofxsecurityid < 0 || ofxsecurityid > int64(len(i.Securities)) {
|
if ofxsecurityid < 0 || ofxsecurityid > int64(len(i.Securities)) {
|
||||||
return nil, errors.New("OFXImport.GetSecurity: SecurityID out of range")
|
return nil, errors.New("OFXImport.GetSecurity: SecurityID out of range")
|
||||||
}
|
}
|
||||||
return &i.Securities[ofxsecurityid], nil
|
return &i.Securities[ofxsecurityid], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *OFXImport) GetSecurityAlternateId(alternateid string, securityType SecurityType) (*Security, error) {
|
func (i *OFXImport) GetSecurityAlternateId(alternateid string, securityType models.SecurityType) (*models.Security, error) {
|
||||||
for _, security := range i.Securities {
|
for _, security := range i.Securities {
|
||||||
if alternateid == security.AlternateId && securityType == security.Type {
|
if alternateid == security.AlternateId && securityType == security.Type {
|
||||||
return &security, nil
|
return &security, nil
|
||||||
@ -32,18 +33,18 @@ func (i *OFXImport) GetSecurityAlternateId(alternateid string, securityType Secu
|
|||||||
return nil, errors.New("OFXImport.FindSecurity: Unable to find security")
|
return nil, errors.New("OFXImport.FindSecurity: Unable to find security")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *OFXImport) GetAddCurrency(isoname string) (*Security, error) {
|
func (i *OFXImport) GetAddCurrency(isoname string) (*models.Security, error) {
|
||||||
for _, security := range i.Securities {
|
for _, security := range i.Securities {
|
||||||
if isoname == security.Name && Currency == security.Type {
|
if isoname == security.Name && models.Currency == security.Type {
|
||||||
return &security, nil
|
return &security, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template := FindSecurityTemplate(isoname, Currency)
|
template := FindSecurityTemplate(isoname, models.Currency)
|
||||||
if template == nil {
|
if template == nil {
|
||||||
return nil, fmt.Errorf("Failed to find Security for \"%s\"", isoname)
|
return nil, fmt.Errorf("Failed to find Security for \"%s\"", isoname)
|
||||||
}
|
}
|
||||||
var security Security = *template
|
var security models.Security = *template
|
||||||
security.SecurityId = int64(len(i.Securities) + 1)
|
security.SecurityId = int64(len(i.Securities) + 1)
|
||||||
i.Securities = append(i.Securities, security)
|
i.Securities = append(i.Securities, security)
|
||||||
|
|
||||||
@ -186,13 +187,13 @@ func (i *OFXImport) importSecurities(seclist *ofxgo.SecurityList) error {
|
|||||||
} else {
|
} else {
|
||||||
return errors.New("Can't import unrecognized type satisfying ofxgo.Security interface")
|
return errors.New("Can't import unrecognized type satisfying ofxgo.Security interface")
|
||||||
}
|
}
|
||||||
s := Security{
|
s := models.Security{
|
||||||
SecurityId: int64(len(i.Securities) + 1),
|
SecurityId: int64(len(i.Securities) + 1),
|
||||||
Name: string(si.SecName),
|
Name: string(si.SecName),
|
||||||
Description: string(si.Memo),
|
Description: string(si.Memo),
|
||||||
Symbol: string(si.Ticker),
|
Symbol: string(si.Ticker),
|
||||||
Precision: 5, // TODO How to actually determine this?
|
Precision: 5, // TODO How to actually determine this?
|
||||||
Type: Stock,
|
Type: models.Stock,
|
||||||
AlternateId: string(si.SecID.UniqueID),
|
AlternateId: string(si.SecID.UniqueID),
|
||||||
}
|
}
|
||||||
if len(s.Description) == 0 {
|
if len(s.Description) == 0 {
|
||||||
@ -214,10 +215,10 @@ func (i *OFXImport) GetInvTran(invtran *ofxgo.InvTran) Transaction {
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *OFXImport) GetInvBuyTran(buy *ofxgo.InvBuy, curdef *Security, account *Account) (*Transaction, error) {
|
func (i *OFXImport) GetInvBuyTran(buy *ofxgo.InvBuy, curdef *models.Security, account *Account) (*Transaction, error) {
|
||||||
t := i.GetInvTran(&buy.InvTran)
|
t := i.GetInvTran(&buy.InvTran)
|
||||||
|
|
||||||
security, err := i.GetSecurityAlternateId(string(buy.SecID.UniqueID), Stock)
|
security, err := i.GetSecurityAlternateId(string(buy.SecID.UniqueID), models.Stock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -348,10 +349,10 @@ func (i *OFXImport) GetInvBuyTran(buy *ofxgo.InvBuy, curdef *Security, account *
|
|||||||
return &t, nil
|
return &t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *OFXImport) GetIncomeTran(income *ofxgo.Income, curdef *Security, account *Account) (*Transaction, error) {
|
func (i *OFXImport) GetIncomeTran(income *ofxgo.Income, curdef *models.Security, account *Account) (*Transaction, error) {
|
||||||
t := i.GetInvTran(&income.InvTran)
|
t := i.GetInvTran(&income.InvTran)
|
||||||
|
|
||||||
security, err := i.GetSecurityAlternateId(string(income.SecID.UniqueID), Stock)
|
security, err := i.GetSecurityAlternateId(string(income.SecID.UniqueID), models.Stock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -394,10 +395,10 @@ func (i *OFXImport) GetIncomeTran(income *ofxgo.Income, curdef *Security, accoun
|
|||||||
return &t, nil
|
return &t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *OFXImport) GetInvExpenseTran(expense *ofxgo.InvExpense, curdef *Security, account *Account) (*Transaction, error) {
|
func (i *OFXImport) GetInvExpenseTran(expense *ofxgo.InvExpense, curdef *models.Security, account *Account) (*Transaction, error) {
|
||||||
t := i.GetInvTran(&expense.InvTran)
|
t := i.GetInvTran(&expense.InvTran)
|
||||||
|
|
||||||
security, err := i.GetSecurityAlternateId(string(expense.SecID.UniqueID), Stock)
|
security, err := i.GetSecurityAlternateId(string(expense.SecID.UniqueID), models.Stock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -439,7 +440,7 @@ func (i *OFXImport) GetInvExpenseTran(expense *ofxgo.InvExpense, curdef *Securit
|
|||||||
return &t, nil
|
return &t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *OFXImport) GetMarginInterestTran(marginint *ofxgo.MarginInterest, curdef *Security, account *Account) (*Transaction, error) {
|
func (i *OFXImport) GetMarginInterestTran(marginint *ofxgo.MarginInterest, curdef *models.Security, account *Account) (*Transaction, error) {
|
||||||
t := i.GetInvTran(&marginint.InvTran)
|
t := i.GetInvTran(&marginint.InvTran)
|
||||||
|
|
||||||
memo := string(marginint.InvTran.Memo)
|
memo := string(marginint.InvTran.Memo)
|
||||||
@ -478,10 +479,10 @@ func (i *OFXImport) GetMarginInterestTran(marginint *ofxgo.MarginInterest, curde
|
|||||||
return &t, nil
|
return &t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *OFXImport) GetReinvestTran(reinvest *ofxgo.Reinvest, curdef *Security, account *Account) (*Transaction, error) {
|
func (i *OFXImport) GetReinvestTran(reinvest *ofxgo.Reinvest, curdef *models.Security, account *Account) (*Transaction, error) {
|
||||||
t := i.GetInvTran(&reinvest.InvTran)
|
t := i.GetInvTran(&reinvest.InvTran)
|
||||||
|
|
||||||
security, err := i.GetSecurityAlternateId(string(reinvest.SecID.UniqueID), Stock)
|
security, err := i.GetSecurityAlternateId(string(reinvest.SecID.UniqueID), models.Stock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -634,10 +635,10 @@ func (i *OFXImport) GetReinvestTran(reinvest *ofxgo.Reinvest, curdef *Security,
|
|||||||
return &t, nil
|
return &t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *OFXImport) GetRetOfCapTran(retofcap *ofxgo.RetOfCap, curdef *Security, account *Account) (*Transaction, error) {
|
func (i *OFXImport) GetRetOfCapTran(retofcap *ofxgo.RetOfCap, curdef *models.Security, account *Account) (*Transaction, error) {
|
||||||
t := i.GetInvTran(&retofcap.InvTran)
|
t := i.GetInvTran(&retofcap.InvTran)
|
||||||
|
|
||||||
security, err := i.GetSecurityAlternateId(string(retofcap.SecID.UniqueID), Stock)
|
security, err := i.GetSecurityAlternateId(string(retofcap.SecID.UniqueID), models.Stock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -679,10 +680,10 @@ func (i *OFXImport) GetRetOfCapTran(retofcap *ofxgo.RetOfCap, curdef *Security,
|
|||||||
return &t, nil
|
return &t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *OFXImport) GetInvSellTran(sell *ofxgo.InvSell, curdef *Security, account *Account) (*Transaction, error) {
|
func (i *OFXImport) GetInvSellTran(sell *ofxgo.InvSell, curdef *models.Security, account *Account) (*Transaction, error) {
|
||||||
t := i.GetInvTran(&sell.InvTran)
|
t := i.GetInvTran(&sell.InvTran)
|
||||||
|
|
||||||
security, err := i.GetSecurityAlternateId(string(sell.SecID.UniqueID), Stock)
|
security, err := i.GetSecurityAlternateId(string(sell.SecID.UniqueID), models.Stock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -819,7 +820,7 @@ func (i *OFXImport) GetInvSellTran(sell *ofxgo.InvSell, curdef *Security, accoun
|
|||||||
func (i *OFXImport) GetTransferTran(transfer *ofxgo.Transfer, account *Account) (*Transaction, error) {
|
func (i *OFXImport) GetTransferTran(transfer *ofxgo.Transfer, account *Account) (*Transaction, error) {
|
||||||
t := i.GetInvTran(&transfer.InvTran)
|
t := i.GetInvTran(&transfer.InvTran)
|
||||||
|
|
||||||
security, err := i.GetSecurityAlternateId(string(transfer.SecID.UniqueID), Stock)
|
security, err := i.GetSecurityAlternateId(string(transfer.SecID.UniqueID), models.Stock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -858,7 +859,7 @@ func (i *OFXImport) GetTransferTran(transfer *ofxgo.Transfer, account *Account)
|
|||||||
return &t, nil
|
return &t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *OFXImport) AddInvTransaction(invtran *ofxgo.InvTransaction, account *Account, curdef *Security) error {
|
func (i *OFXImport) AddInvTransaction(invtran *ofxgo.InvTransaction, account *Account, curdef *models.Security) error {
|
||||||
if curdef.SecurityId < 1 || curdef.SecurityId > int64(len(i.Securities)) {
|
if curdef.SecurityId < 1 || curdef.SecurityId > int64(len(i.Securities)) {
|
||||||
return errors.New("Internal error: security index not found in OFX import\n")
|
return errors.New("Internal error: security index not found in OFX import\n")
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package handlers_test
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/aclindsa/moneygo/internal/handlers"
|
"github.com/aclindsa/moneygo/internal/handlers"
|
||||||
|
"github.com/aclindsa/moneygo/internal/models"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
@ -63,7 +64,7 @@ func TestImportOFXCreditCard(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func findSecurity(client *http.Client, symbol string, tipe handlers.SecurityType) (*handlers.Security, error) {
|
func findSecurity(client *http.Client, symbol string, tipe models.SecurityType) (*models.Security, error) {
|
||||||
securities, err := getSecurities(client)
|
securities, err := getSecurities(client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -125,7 +126,7 @@ func TestImportOFX401kMutualFunds(t *testing.T) {
|
|||||||
|
|
||||||
// Make sure the security was created and that the trading account has
|
// Make sure the security was created and that the trading account has
|
||||||
// the right value
|
// the right value
|
||||||
security, err := findSecurity(d.clients[0], "VANGUARD TARGET 2045", handlers.Stock)
|
security, err := findSecurity(d.clients[0], "VANGUARD TARGET 2045", models.Stock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error finding VANGUARD TARGET 2045 security: %s\n", err)
|
t.Fatalf("Error finding VANGUARD TARGET 2045 security: %s\n", err)
|
||||||
}
|
}
|
||||||
@ -204,7 +205,7 @@ func TestImportOFXBrokerage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, check := range checks {
|
for _, check := range checks {
|
||||||
security, err := findSecurity(d.clients[0], check.Ticker, handlers.Stock)
|
security, err := findSecurity(d.clients[0], check.Ticker, models.Stock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error finding security: %s\n", err)
|
t.Fatalf("Error finding security: %s\n", err)
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ func GetPrices(tx *Tx, securityid int64) (*[]*Price, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return the latest price for security in currency units before date
|
// Return the latest price for security in currency units before date
|
||||||
func GetLatestPrice(tx *Tx, security, currency *Security, date *time.Time) (*Price, error) {
|
func GetLatestPrice(tx *Tx, security, currency *models.Security, date *time.Time) (*Price, error) {
|
||||||
var p Price
|
var p Price
|
||||||
err := tx.SelectOne(&p, "SELECT * from prices where SecurityId=? AND CurrencyId=? AND Date <= ? ORDER BY Date DESC LIMIT 1", security.SecurityId, currency.SecurityId, date)
|
err := tx.SelectOne(&p, "SELECT * from prices where SecurityId=? AND CurrencyId=? AND Date <= ? ORDER BY Date DESC LIMIT 1", security.SecurityId, currency.SecurityId, date)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -100,7 +100,7 @@ func GetLatestPrice(tx *Tx, security, currency *Security, date *time.Time) (*Pri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return the earliest price for security in currency units after date
|
// Return the earliest price for security in currency units after date
|
||||||
func GetEarliestPrice(tx *Tx, security, currency *Security, date *time.Time) (*Price, error) {
|
func GetEarliestPrice(tx *Tx, security, currency *models.Security, date *time.Time) (*Price, error) {
|
||||||
var p Price
|
var p Price
|
||||||
err := tx.SelectOne(&p, "SELECT * from prices where SecurityId=? AND CurrencyId=? AND Date >= ? ORDER BY Date ASC LIMIT 1", security.SecurityId, currency.SecurityId, date)
|
err := tx.SelectOne(&p, "SELECT * from prices where SecurityId=? AND CurrencyId=? AND Date >= ? ORDER BY Date ASC LIMIT 1", security.SecurityId, currency.SecurityId, date)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -110,7 +110,7 @@ func GetEarliestPrice(tx *Tx, security, currency *Security, date *time.Time) (*P
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return the price for security in currency closest to date
|
// Return the price for security in currency closest to date
|
||||||
func GetClosestPrice(tx *Tx, security, currency *Security, date *time.Time) (*Price, error) {
|
func GetClosestPrice(tx *Tx, security, currency *models.Security, date *time.Time) (*Price, error) {
|
||||||
earliest, _ := GetEarliestPrice(tx, security, currency, date)
|
earliest, _ := GetEarliestPrice(tx, security, currency, date)
|
||||||
latest, err := GetLatestPrice(tx, security, currency, date)
|
latest, err := GetLatestPrice(tx, security, currency, date)
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class Security(object):
|
|||||||
self.type = _type
|
self.type = _type
|
||||||
self.precision = precision
|
self.precision = precision
|
||||||
def unicode(self):
|
def unicode(self):
|
||||||
s = """\tSecurity{
|
s = """\t{
|
||||||
\t\tName: \"%s\",
|
\t\tName: \"%s\",
|
||||||
\t\tDescription: \"%s\",
|
\t\tDescription: \"%s\",
|
||||||
\t\tSymbol: \"%s\",
|
\t\tSymbol: \"%s\",
|
||||||
@ -72,7 +72,7 @@ def process_ccyntry(currency_list, node):
|
|||||||
else:
|
else:
|
||||||
precision = int(n.firstChild.nodeValue)
|
precision = int(n.firstChild.nodeValue)
|
||||||
if nameSet and numberSet:
|
if nameSet and numberSet:
|
||||||
currency_list.add(Security(name, description, number, "Currency", precision))
|
currency_list.add(Security(name, description, number, "models.Currency", precision))
|
||||||
|
|
||||||
def get_currency_list():
|
def get_currency_list():
|
||||||
currency_list = SecurityList("ISO 4217, from http://www.currency-iso.org/en/home/tables/table-a1.html")
|
currency_list = SecurityList("ISO 4217, from http://www.currency-iso.org/en/home/tables/table-a1.html")
|
||||||
@ -97,7 +97,7 @@ def get_cusip_list(filename):
|
|||||||
cusip = row[0]
|
cusip = row[0]
|
||||||
name = row[1]
|
name = row[1]
|
||||||
description = ",".join(row[2:])
|
description = ",".join(row[2:])
|
||||||
cusip_list.add(Security(name, description, cusip, "Stock", 5))
|
cusip_list.add(Security(name, description, cusip, "models.Stock", 5))
|
||||||
return cusip_list
|
return cusip_list
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@ -105,7 +105,10 @@ def main():
|
|||||||
cusip_list = get_cusip_list('cusip_list.csv')
|
cusip_list = get_cusip_list('cusip_list.csv')
|
||||||
|
|
||||||
print("package handlers\n")
|
print("package handlers\n")
|
||||||
print("var SecurityTemplates = []Security{")
|
print("import (")
|
||||||
|
print("\t\"github.com/aclindsa/moneygo/internal/models\"")
|
||||||
|
print(")\n")
|
||||||
|
print("var SecurityTemplates = []models.Security{")
|
||||||
print(currency_list.unicode())
|
print(currency_list.unicode())
|
||||||
print(cusip_list.unicode())
|
print(cusip_list.unicode())
|
||||||
print("}")
|
print("}")
|
||||||
|
@ -3,9 +3,9 @@ package handlers
|
|||||||
//go:generate make
|
//go:generate make
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/aclindsa/moneygo/internal/models"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -13,64 +13,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SecurityType int64
|
func SearchSecurityTemplates(search string, _type models.SecurityType, limit int64) []*models.Security {
|
||||||
|
|
||||||
const (
|
|
||||||
Currency SecurityType = 1
|
|
||||||
Stock = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetSecurityType(typestring string) SecurityType {
|
|
||||||
if strings.EqualFold(typestring, "currency") {
|
|
||||||
return Currency
|
|
||||||
} else if strings.EqualFold(typestring, "stock") {
|
|
||||||
return Stock
|
|
||||||
} else {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Security struct {
|
|
||||||
SecurityId int64
|
|
||||||
UserId int64
|
|
||||||
Name string
|
|
||||||
Description string
|
|
||||||
Symbol string
|
|
||||||
// Number of decimal digits (to the right of the decimal point) this
|
|
||||||
// security is precise to
|
|
||||||
Precision int `db:"Preciseness"`
|
|
||||||
Type SecurityType
|
|
||||||
// AlternateId is CUSIP for Type=Stock, ISO4217 for Type=Currency
|
|
||||||
AlternateId string
|
|
||||||
}
|
|
||||||
|
|
||||||
type SecurityList struct {
|
|
||||||
Securities *[]*Security `json:"securities"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Security) Read(json_str string) error {
|
|
||||||
dec := json.NewDecoder(strings.NewReader(json_str))
|
|
||||||
return dec.Decode(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Security) Write(w http.ResponseWriter) error {
|
|
||||||
enc := json.NewEncoder(w)
|
|
||||||
return enc.Encode(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sl *SecurityList) Read(json_str string) error {
|
|
||||||
dec := json.NewDecoder(strings.NewReader(json_str))
|
|
||||||
return dec.Decode(sl)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sl *SecurityList) Write(w http.ResponseWriter) error {
|
|
||||||
enc := json.NewEncoder(w)
|
|
||||||
return enc.Encode(sl)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SearchSecurityTemplates(search string, _type SecurityType, limit int64) []*Security {
|
|
||||||
upperSearch := strings.ToUpper(search)
|
upperSearch := strings.ToUpper(search)
|
||||||
var results []*Security
|
var results []*models.Security
|
||||||
for i, security := range SecurityTemplates {
|
for i, security := range SecurityTemplates {
|
||||||
if strings.Contains(strings.ToUpper(security.Name), upperSearch) ||
|
if strings.Contains(strings.ToUpper(security.Name), upperSearch) ||
|
||||||
strings.Contains(strings.ToUpper(security.Description), upperSearch) ||
|
strings.Contains(strings.ToUpper(security.Description), upperSearch) ||
|
||||||
@ -86,7 +31,7 @@ func SearchSecurityTemplates(search string, _type SecurityType, limit int64) []*
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindSecurityTemplate(name string, _type SecurityType) *Security {
|
func FindSecurityTemplate(name string, _type models.SecurityType) *models.Security {
|
||||||
for _, security := range SecurityTemplates {
|
for _, security := range SecurityTemplates {
|
||||||
if name == security.Name && _type == security.Type {
|
if name == security.Name && _type == security.Type {
|
||||||
return &security
|
return &security
|
||||||
@ -95,18 +40,18 @@ func FindSecurityTemplate(name string, _type SecurityType) *Security {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindCurrencyTemplate(iso4217 int64) *Security {
|
func FindCurrencyTemplate(iso4217 int64) *models.Security {
|
||||||
iso4217string := strconv.FormatInt(iso4217, 10)
|
iso4217string := strconv.FormatInt(iso4217, 10)
|
||||||
for _, security := range SecurityTemplates {
|
for _, security := range SecurityTemplates {
|
||||||
if security.Type == Currency && security.AlternateId == iso4217string {
|
if security.Type == models.Currency && security.AlternateId == iso4217string {
|
||||||
return &security
|
return &security
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSecurity(tx *Tx, securityid int64, userid int64) (*Security, error) {
|
func GetSecurity(tx *Tx, securityid int64, userid int64) (*models.Security, error) {
|
||||||
var s Security
|
var s models.Security
|
||||||
|
|
||||||
err := tx.SelectOne(&s, "SELECT * from securities where UserId=? AND SecurityId=?", userid, securityid)
|
err := tx.SelectOne(&s, "SELECT * from securities where UserId=? AND SecurityId=?", userid, securityid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -115,8 +60,8 @@ func GetSecurity(tx *Tx, securityid int64, userid int64) (*Security, error) {
|
|||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSecurities(tx *Tx, userid int64) (*[]*Security, error) {
|
func GetSecurities(tx *Tx, userid int64) (*[]*models.Security, error) {
|
||||||
var securities []*Security
|
var securities []*models.Security
|
||||||
|
|
||||||
_, err := tx.Select(&securities, "SELECT * from securities where UserId=?", userid)
|
_, err := tx.Select(&securities, "SELECT * from securities where UserId=?", userid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -125,7 +70,7 @@ func GetSecurities(tx *Tx, userid int64) (*[]*Security, error) {
|
|||||||
return &securities, nil
|
return &securities, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func InsertSecurity(tx *Tx, s *Security) error {
|
func InsertSecurity(tx *Tx, s *models.Security) error {
|
||||||
err := tx.Insert(s)
|
err := tx.Insert(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -133,11 +78,11 @@ func InsertSecurity(tx *Tx, s *Security) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateSecurity(tx *Tx, s *Security) (err error) {
|
func UpdateSecurity(tx *Tx, s *models.Security) (err error) {
|
||||||
user, err := GetUser(tx, s.UserId)
|
user, err := GetUser(tx, s.UserId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
} else if user.DefaultCurrency == s.SecurityId && s.Type != Currency {
|
} else if user.DefaultCurrency == s.SecurityId && s.Type != models.Currency {
|
||||||
return errors.New("Cannot change security which is user's default currency to be non-currency")
|
return errors.New("Cannot change security which is user's default currency to be non-currency")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +105,7 @@ func (e SecurityInUseError) Error() string {
|
|||||||
return e.message
|
return e.message
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteSecurity(tx *Tx, s *Security) error {
|
func DeleteSecurity(tx *Tx, s *models.Security) error {
|
||||||
// First, ensure no accounts are using this security
|
// First, ensure no accounts are using this security
|
||||||
accounts, err := tx.SelectInt("SELECT count(*) from accounts where UserId=? and SecurityId=?", s.UserId, s.SecurityId)
|
accounts, err := tx.SelectInt("SELECT count(*) from accounts where UserId=? and SecurityId=?", s.UserId, s.SecurityId)
|
||||||
|
|
||||||
@ -193,7 +138,7 @@ func DeleteSecurity(tx *Tx, s *Security) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ImportGetCreateSecurity(tx *Tx, userid int64, security *Security) (*Security, error) {
|
func ImportGetCreateSecurity(tx *Tx, userid int64, security *models.Security) (*models.Security, error) {
|
||||||
security.UserId = userid
|
security.UserId = userid
|
||||||
if len(security.AlternateId) == 0 {
|
if len(security.AlternateId) == 0 {
|
||||||
// Always create a new local security if we can't match on the AlternateId
|
// Always create a new local security if we can't match on the AlternateId
|
||||||
@ -204,7 +149,7 @@ func ImportGetCreateSecurity(tx *Tx, userid int64, security *Security) (*Securit
|
|||||||
return security, nil
|
return security, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var securities []*Security
|
var securities []*models.Security
|
||||||
|
|
||||||
_, err := tx.Select(&securities, "SELECT * from securities where UserId=? AND Type=? AND AlternateId=? AND Preciseness=?", userid, security.Type, security.AlternateId, security.Precision)
|
_, err := tx.Select(&securities, "SELECT * from securities where UserId=? AND Type=? AND AlternateId=? AND Preciseness=?", userid, security.Type, security.AlternateId, security.Precision)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -264,7 +209,7 @@ func SecurityHandler(r *http.Request, context *Context) ResponseWriterWriter {
|
|||||||
return PriceHandler(r, context, user, securityid)
|
return PriceHandler(r, context, user, securityid)
|
||||||
}
|
}
|
||||||
|
|
||||||
var security Security
|
var security models.Security
|
||||||
if err := ReadJSON(r, &security); err != nil {
|
if err := ReadJSON(r, &security); err != nil {
|
||||||
return NewError(3 /*Invalid Request*/)
|
return NewError(3 /*Invalid Request*/)
|
||||||
}
|
}
|
||||||
@ -281,7 +226,7 @@ func SecurityHandler(r *http.Request, context *Context) ResponseWriterWriter {
|
|||||||
} else if r.Method == "GET" {
|
} else if r.Method == "GET" {
|
||||||
if context.LastLevel() {
|
if context.LastLevel() {
|
||||||
//Return all securities
|
//Return all securities
|
||||||
var sl SecurityList
|
var sl models.SecurityList
|
||||||
|
|
||||||
securities, err := GetSecurities(context.Tx, user.UserId)
|
securities, err := GetSecurities(context.Tx, user.UserId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -324,7 +269,7 @@ func SecurityHandler(r *http.Request, context *Context) ResponseWriterWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if r.Method == "PUT" {
|
if r.Method == "PUT" {
|
||||||
var security Security
|
var security models.Security
|
||||||
if err := ReadJSON(r, &security); err != nil || security.SecurityId != securityid {
|
if err := ReadJSON(r, &security); err != nil || security.SecurityId != securityid {
|
||||||
return NewError(3 /*Invalid Request*/)
|
return NewError(3 /*Invalid Request*/)
|
||||||
}
|
}
|
||||||
@ -359,17 +304,17 @@ func SecurityHandler(r *http.Request, context *Context) ResponseWriterWriter {
|
|||||||
|
|
||||||
func SecurityTemplateHandler(r *http.Request, context *Context) ResponseWriterWriter {
|
func SecurityTemplateHandler(r *http.Request, context *Context) ResponseWriterWriter {
|
||||||
if r.Method == "GET" {
|
if r.Method == "GET" {
|
||||||
var sl SecurityList
|
var sl models.SecurityList
|
||||||
|
|
||||||
query, _ := url.ParseQuery(r.URL.RawQuery)
|
query, _ := url.ParseQuery(r.URL.RawQuery)
|
||||||
|
|
||||||
var limit int64 = -1
|
var limit int64 = -1
|
||||||
search := query.Get("search")
|
search := query.Get("search")
|
||||||
|
|
||||||
var _type SecurityType = 0
|
var _type models.SecurityType = 0
|
||||||
typestring := query.Get("type")
|
typestring := query.Get("type")
|
||||||
if len(typestring) > 0 {
|
if len(typestring) > 0 {
|
||||||
_type = GetSecurityType(typestring)
|
_type = models.GetSecurityType(typestring)
|
||||||
if _type == 0 {
|
if _type == 0 {
|
||||||
return NewError(3 /*Invalid Request*/)
|
return NewError(3 /*Invalid Request*/)
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ import (
|
|||||||
|
|
||||||
const luaSecurityTypeName = "security"
|
const luaSecurityTypeName = "security"
|
||||||
|
|
||||||
func luaContextGetSecurities(L *lua.LState) (map[int64]*Security, error) {
|
func luaContextGetSecurities(L *lua.LState) (map[int64]*models.Security, error) {
|
||||||
var security_map map[int64]*Security
|
var security_map map[int64]*models.Security
|
||||||
|
|
||||||
ctx := L.Context()
|
ctx := L.Context()
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ func luaContextGetSecurities(L *lua.LState) (map[int64]*Security, error) {
|
|||||||
return nil, errors.New("Couldn't find tx in lua's Context")
|
return nil, errors.New("Couldn't find tx in lua's Context")
|
||||||
}
|
}
|
||||||
|
|
||||||
security_map, ok = ctx.Value(securitiesContextKey).(map[int64]*Security)
|
security_map, ok = ctx.Value(securitiesContextKey).(map[int64]*models.Security)
|
||||||
if !ok {
|
if !ok {
|
||||||
user, ok := ctx.Value(userContextKey).(*models.User)
|
user, ok := ctx.Value(userContextKey).(*models.User)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -31,7 +31,7 @@ func luaContextGetSecurities(L *lua.LState) (map[int64]*Security, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
security_map = make(map[int64]*Security)
|
security_map = make(map[int64]*models.Security)
|
||||||
for i := range *securities {
|
for i := range *securities {
|
||||||
security_map[(*securities)[i].SecurityId] = (*securities)[i]
|
security_map[(*securities)[i].SecurityId] = (*securities)[i]
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ func luaContextGetSecurities(L *lua.LState) (map[int64]*Security, error) {
|
|||||||
return security_map, nil
|
return security_map, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func luaContextGetDefaultCurrency(L *lua.LState) (*Security, error) {
|
func luaContextGetDefaultCurrency(L *lua.LState) (*models.Security, error) {
|
||||||
security_map, err := luaContextGetSecurities(L)
|
security_map, err := luaContextGetSecurities(L)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -107,7 +107,7 @@ func luaRegisterSecurities(L *lua.LState) {
|
|||||||
L.SetGlobal("get_default_currency", getDefaultCurrencyFn)
|
L.SetGlobal("get_default_currency", getDefaultCurrencyFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SecurityToLua(L *lua.LState, security *Security) *lua.LUserData {
|
func SecurityToLua(L *lua.LState, security *models.Security) *lua.LUserData {
|
||||||
ud := L.NewUserData()
|
ud := L.NewUserData()
|
||||||
ud.Value = security
|
ud.Value = security
|
||||||
L.SetMetatable(ud, L.GetTypeMetatable(luaSecurityTypeName))
|
L.SetMetatable(ud, L.GetTypeMetatable(luaSecurityTypeName))
|
||||||
@ -115,9 +115,9 @@ func SecurityToLua(L *lua.LState, security *Security) *lua.LUserData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Checks whether the first lua argument is a *LUserData with *Security and returns this *Security.
|
// Checks whether the first lua argument is a *LUserData with *Security and returns this *Security.
|
||||||
func luaCheckSecurity(L *lua.LState, n int) *Security {
|
func luaCheckSecurity(L *lua.LState, n int) *models.Security {
|
||||||
ud := L.CheckUserData(n)
|
ud := L.CheckUserData(n)
|
||||||
if security, ok := ud.Value.(*Security); ok {
|
if security, ok := ud.Value.(*models.Security); ok {
|
||||||
return security
|
return security
|
||||||
}
|
}
|
||||||
L.ArgError(n, "security expected")
|
L.ArgError(n, "security expected")
|
||||||
|
@ -2,19 +2,20 @@ package handlers_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/aclindsa/moneygo/internal/handlers"
|
"github.com/aclindsa/moneygo/internal/handlers"
|
||||||
|
"github.com/aclindsa/moneygo/internal/models"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createSecurity(client *http.Client, security *handlers.Security) (*handlers.Security, error) {
|
func createSecurity(client *http.Client, security *models.Security) (*models.Security, error) {
|
||||||
var s handlers.Security
|
var s models.Security
|
||||||
err := create(client, security, &s, "/v1/securities/")
|
err := create(client, security, &s, "/v1/securities/")
|
||||||
return &s, err
|
return &s, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSecurity(client *http.Client, securityid int64) (*handlers.Security, error) {
|
func getSecurity(client *http.Client, securityid int64) (*models.Security, error) {
|
||||||
var s handlers.Security
|
var s models.Security
|
||||||
err := read(client, &s, "/v1/securities/"+strconv.FormatInt(securityid, 10))
|
err := read(client, &s, "/v1/securities/"+strconv.FormatInt(securityid, 10))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -22,8 +23,8 @@ func getSecurity(client *http.Client, securityid int64) (*handlers.Security, err
|
|||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSecurities(client *http.Client) (*handlers.SecurityList, error) {
|
func getSecurities(client *http.Client) (*models.SecurityList, error) {
|
||||||
var sl handlers.SecurityList
|
var sl models.SecurityList
|
||||||
err := read(client, &sl, "/v1/securities/")
|
err := read(client, &sl, "/v1/securities/")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -31,8 +32,8 @@ func getSecurities(client *http.Client) (*handlers.SecurityList, error) {
|
|||||||
return &sl, nil
|
return &sl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSecurity(client *http.Client, security *handlers.Security) (*handlers.Security, error) {
|
func updateSecurity(client *http.Client, security *models.Security) (*models.Security, error) {
|
||||||
var s handlers.Security
|
var s models.Security
|
||||||
err := update(client, security, &s, "/v1/securities/"+strconv.FormatInt(security.SecurityId, 10))
|
err := update(client, security, &s, "/v1/securities/"+strconv.FormatInt(security.SecurityId, 10))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -40,7 +41,7 @@ func updateSecurity(client *http.Client, security *handlers.Security) (*handlers
|
|||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteSecurity(client *http.Client, s *handlers.Security) error {
|
func deleteSecurity(client *http.Client, s *models.Security) error {
|
||||||
err := remove(client, "/v1/securities/"+strconv.FormatInt(s.SecurityId, 10))
|
err := remove(client, "/v1/securities/"+strconv.FormatInt(s.SecurityId, 10))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -2,12 +2,13 @@ package handlers_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/aclindsa/moneygo/internal/handlers"
|
"github.com/aclindsa/moneygo/internal/handlers"
|
||||||
|
"github.com/aclindsa/moneygo/internal/models"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSecurityTemplates(t *testing.T) {
|
func TestSecurityTemplates(t *testing.T) {
|
||||||
var sl handlers.SecurityList
|
var sl models.SecurityList
|
||||||
response, err := server.Client().Get(server.URL + "/v1/securitytemplates/?search=USD&type=currency")
|
response, err := server.Client().Get(server.URL + "/v1/securitytemplates/?search=USD&type=currency")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -30,7 +31,7 @@ func TestSecurityTemplates(t *testing.T) {
|
|||||||
num_usd := 0
|
num_usd := 0
|
||||||
if sl.Securities != nil {
|
if sl.Securities != nil {
|
||||||
for _, s := range *sl.Securities {
|
for _, s := range *sl.Securities {
|
||||||
if s.Type != handlers.Currency {
|
if s.Type != models.Currency {
|
||||||
t.Fatalf("Requested Currency-only security templates, received a non-Currency template for %s", s.Name)
|
t.Fatalf("Requested Currency-only security templates, received a non-Currency template for %s", s.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ func TestSecurityTemplates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSecurityTemplateLimit(t *testing.T) {
|
func TestSecurityTemplateLimit(t *testing.T) {
|
||||||
var sl handlers.SecurityList
|
var sl models.SecurityList
|
||||||
response, err := server.Client().Get(server.URL + "/v1/securitytemplates/?search=e&limit=5")
|
response, err := server.Client().Get(server.URL + "/v1/securitytemplates/?search=e&limit=5")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/aclindsa/moneygo/internal/handlers"
|
"github.com/aclindsa/moneygo/internal/handlers"
|
||||||
|
"github.com/aclindsa/moneygo/internal/models"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -36,7 +37,7 @@ type TestData struct {
|
|||||||
initialized bool
|
initialized bool
|
||||||
users []User
|
users []User
|
||||||
clients []*http.Client
|
clients []*http.Client
|
||||||
securities []handlers.Security
|
securities []models.Security
|
||||||
prices []handlers.Price
|
prices []handlers.Price
|
||||||
accounts []handlers.Account // accounts must appear after their parents in this slice
|
accounts []handlers.Account // accounts must appear after their parents in this slice
|
||||||
transactions []handlers.Transaction
|
transactions []handlers.Transaction
|
||||||
@ -170,14 +171,14 @@ var data = []TestData{
|
|||||||
Email: "bbob+moneygo@my-domain.com",
|
Email: "bbob+moneygo@my-domain.com",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
securities: []handlers.Security{
|
securities: []models.Security{
|
||||||
{
|
{
|
||||||
UserId: 0,
|
UserId: 0,
|
||||||
Name: "USD",
|
Name: "USD",
|
||||||
Description: "US Dollar",
|
Description: "US Dollar",
|
||||||
Symbol: "$",
|
Symbol: "$",
|
||||||
Precision: 2,
|
Precision: 2,
|
||||||
Type: handlers.Currency,
|
Type: models.Currency,
|
||||||
AlternateId: "840",
|
AlternateId: "840",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -186,7 +187,7 @@ var data = []TestData{
|
|||||||
Description: "SPDR S&P 500 ETF Trust",
|
Description: "SPDR S&P 500 ETF Trust",
|
||||||
Symbol: "SPY",
|
Symbol: "SPY",
|
||||||
Precision: 5,
|
Precision: 5,
|
||||||
Type: handlers.Stock,
|
Type: models.Stock,
|
||||||
AlternateId: "78462F103",
|
AlternateId: "78462F103",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -195,7 +196,7 @@ var data = []TestData{
|
|||||||
Description: "Euro",
|
Description: "Euro",
|
||||||
Symbol: "€",
|
Symbol: "€",
|
||||||
Precision: 2,
|
Precision: 2,
|
||||||
Type: handlers.Currency,
|
Type: models.Currency,
|
||||||
AlternateId: "978",
|
AlternateId: "978",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -204,7 +205,7 @@ var data = []TestData{
|
|||||||
Description: "Euro",
|
Description: "Euro",
|
||||||
Symbol: "€",
|
Symbol: "€",
|
||||||
Precision: 2,
|
Precision: 2,
|
||||||
Type: handlers.Currency,
|
Type: models.Currency,
|
||||||
AlternateId: "978",
|
AlternateId: "978",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -54,7 +54,7 @@ func InsertUser(tx *Tx, u *models.User) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy the security template and give it our new UserId
|
// Copy the security template and give it our new UserId
|
||||||
var security Security
|
var security models.Security
|
||||||
security = *security_template
|
security = *security_template
|
||||||
security.UserId = u.UserId
|
security.UserId = u.UserId
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ func UpdateUser(tx *Tx, u *models.User) error {
|
|||||||
return err
|
return err
|
||||||
} else if security.UserId != u.UserId || security.SecurityId != u.DefaultCurrency {
|
} else if security.UserId != u.UserId || security.SecurityId != u.DefaultCurrency {
|
||||||
return errors.New("UserId and DefaultCurrency don't match the fetched security")
|
return errors.New("UserId and DefaultCurrency don't match the fetched security")
|
||||||
} else if security.Type != Currency {
|
} else if security.Type != models.Currency {
|
||||||
return errors.New("New DefaultCurrency security is not a currency")
|
return errors.New("New DefaultCurrency security is not a currency")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
62
internal/models/securities.go
Normal file
62
internal/models/securities.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SecurityType int64
|
||||||
|
|
||||||
|
const (
|
||||||
|
Currency SecurityType = 1
|
||||||
|
Stock = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetSecurityType(typestring string) SecurityType {
|
||||||
|
if strings.EqualFold(typestring, "currency") {
|
||||||
|
return Currency
|
||||||
|
} else if strings.EqualFold(typestring, "stock") {
|
||||||
|
return Stock
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Security struct {
|
||||||
|
SecurityId int64
|
||||||
|
UserId int64
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
Symbol string
|
||||||
|
// Number of decimal digits (to the right of the decimal point) this
|
||||||
|
// security is precise to
|
||||||
|
Precision int `db:"Preciseness"`
|
||||||
|
Type SecurityType
|
||||||
|
// AlternateId is CUSIP for Type=Stock, ISO4217 for Type=Currency
|
||||||
|
AlternateId string
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecurityList struct {
|
||||||
|
Securities *[]*Security `json:"securities"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Security) Read(json_str string) error {
|
||||||
|
dec := json.NewDecoder(strings.NewReader(json_str))
|
||||||
|
return dec.Decode(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Security) Write(w http.ResponseWriter) error {
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
return enc.Encode(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *SecurityList) Read(json_str string) error {
|
||||||
|
dec := json.NewDecoder(strings.NewReader(json_str))
|
||||||
|
return dec.Decode(sl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *SecurityList) Write(w http.ResponseWriter) error {
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
return enc.Encode(sl)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user