mirror of
				https://github.com/aclindsa/moneygo.git
				synced 2025-10-30 17:33:26 -04:00 
			
		
		
		
	Split securities into models
This commit is contained in:
		| @@ -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) | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user