mirror of
https://github.com/aclindsa/moneygo.git
synced 2024-12-26 23:42:29 -05:00
Fix Gnucash security imports
Attempt to find an existing security that matches, trying decreasingly specific searches, and create the security if nothing close can be found.
This commit is contained in:
parent
232e4b0682
commit
c0fb04b722
@ -16,7 +16,8 @@ var error_codes = map[int]string{
|
|||||||
2: "Unauthorized Access",
|
2: "Unauthorized Access",
|
||||||
3: "Invalid Request",
|
3: "Invalid Request",
|
||||||
4: "User Exists",
|
4: "User Exists",
|
||||||
// 5: "Connection Failed", //client-side error
|
// 5: "Connection Failed", //reserved for client-side error
|
||||||
|
6: "Import Error",
|
||||||
999: "Internal Error",
|
999: "Internal Error",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
33
gnucash.go
33
gnucash.go
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@ -27,19 +28,29 @@ func (gc *GnucashCommodity) UnmarshalXML(d *xml.Decoder, start xml.StartElement)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
gc.Security.Type = Stock // assumed default
|
|
||||||
if gxc.Type == "ISO4217" {
|
|
||||||
gc.Security.Type = Currency
|
|
||||||
}
|
|
||||||
gc.Name = gxc.Name
|
gc.Name = gxc.Name
|
||||||
gc.Symbol = gxc.Name
|
gc.Symbol = gxc.Name
|
||||||
gc.Description = gxc.Description
|
gc.Description = gxc.Description
|
||||||
gc.AlternateId = gxc.XCode
|
gc.AlternateId = gxc.XCode
|
||||||
|
|
||||||
|
gc.Security.Type = Stock // assumed default
|
||||||
|
if gxc.Type == "ISO4217" {
|
||||||
|
gc.Security.Type = Currency
|
||||||
|
// Get the number from our templates for the AlternateId because
|
||||||
|
// Gnucash uses 'id' (our Name) to supply the string ISO4217 code
|
||||||
|
template := FindSecurityTemplate(gxc.Name, Currency)
|
||||||
|
if template == nil {
|
||||||
|
return errors.New("Unable to find security template for Gnucash ISO4217 commodity")
|
||||||
|
}
|
||||||
|
gc.AlternateId = template.AlternateId
|
||||||
|
gc.Precision = template.Precision
|
||||||
|
} else {
|
||||||
if gxc.Fraction > 0 {
|
if gxc.Fraction > 0 {
|
||||||
gc.Precision = int(math.Ceil(math.Log10(float64(gxc.Fraction))))
|
gc.Precision = int(math.Ceil(math.Log10(float64(gxc.Fraction))))
|
||||||
} else {
|
} else {
|
||||||
gc.Precision = 0
|
gc.Precision = 0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +263,7 @@ func GnucashImportHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// assume there is only one 'part'
|
// Assume there is only one 'part' and it's the one we care about
|
||||||
part, err := multipartReader.NextPart()
|
part, err := multipartReader.NextPart()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@ -281,17 +292,16 @@ func GnucashImportHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
// internal IDs
|
// internal IDs
|
||||||
securityMap := make(map[int64]int64)
|
securityMap := make(map[int64]int64)
|
||||||
for _, security := range gnucashImport.Securities {
|
for _, security := range gnucashImport.Securities {
|
||||||
//TODO FIXME check on AlternateID also, and convert to the case
|
securityId := security.SecurityId // save off because it could be updated
|
||||||
//where users have their own internal securities
|
s, err := ImportGetCreateSecurity(sqltransaction, user, &security)
|
||||||
s, err := GetSecurityByNameAndType(security.Name, security.Type)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//TODO attempt to create security if it doesn't exist
|
|
||||||
sqltransaction.Rollback()
|
sqltransaction.Rollback()
|
||||||
WriteError(w, 999 /*Internal Error*/)
|
WriteError(w, 6 /*Import Error*/)
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
|
log.Print(security)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
securityMap[security.SecurityId] = s.SecurityId
|
securityMap[securityId] = s.SecurityId
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get/create accounts in the database, building a map from Gnucash account
|
// Get/create accounts in the database, building a map from Gnucash account
|
||||||
@ -349,7 +359,6 @@ func GnucashImportHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
split.AccountId = acctId
|
split.AccountId = acctId
|
||||||
fmt.Printf("Setting split AccountId to %d\n", acctId)
|
|
||||||
}
|
}
|
||||||
err := InsertTransactionTx(sqltransaction, &transaction, user)
|
err := InsertTransactionTx(sqltransaction, &transaction, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -22,7 +22,7 @@ const (
|
|||||||
balanceContextKey
|
balanceContextKey
|
||||||
)
|
)
|
||||||
|
|
||||||
const luaTimeoutSeconds time.Duration = 5 // maximum time a lua request can run for
|
const luaTimeoutSeconds time.Duration = 30 // maximum time a lua request can run for
|
||||||
|
|
||||||
type Series struct {
|
type Series struct {
|
||||||
Values []float64
|
Values []float64
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"gopkg.in/gorp.v1"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -77,6 +78,15 @@ func SearchSecurityTemplates(search string, _type int64, limit int64) []*Securit
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FindSecurityTemplate(name string, _type int64) *Security {
|
||||||
|
for _, security := range SecurityTemplates {
|
||||||
|
if name == security.Name && _type == security.Type {
|
||||||
|
return &security
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetSecurity(securityid int64, userid int64) (*Security, error) {
|
func GetSecurity(securityid int64, userid int64) (*Security, error) {
|
||||||
var s Security
|
var s Security
|
||||||
|
|
||||||
@ -105,6 +115,14 @@ func InsertSecurity(s *Security) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InsertSecurityTx(transaction *gorp.Transaction, s *Security) error {
|
||||||
|
err := transaction.Insert(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func UpdateSecurity(s *Security) error {
|
func UpdateSecurity(s *Security) error {
|
||||||
transaction, err := DB.Begin()
|
transaction, err := DB.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -163,6 +181,59 @@ func DeleteSecurity(s *Security) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ImportGetCreateSecurity(transaction *gorp.Transaction, user *User, security *Security) (*Security, error) {
|
||||||
|
security.UserId = user.UserId
|
||||||
|
if len(security.AlternateId) == 0 {
|
||||||
|
// Always create a new local security if we can't match on the AlternateId
|
||||||
|
err := InsertSecurityTx(transaction, security)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return security, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var securities []*Security
|
||||||
|
|
||||||
|
_, err := transaction.Select(&securities, "SELECT * from securities where UserId=? AND Type=? AND AlternateId=? AND Precision=?", user.UserId, security.Type, security.AlternateId, security.Precision)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// First try to find a case insensitive match on the name or symbol
|
||||||
|
upperName := strings.ToUpper(security.Name)
|
||||||
|
upperSymbol := strings.ToUpper(security.Symbol)
|
||||||
|
for _, s := range securities {
|
||||||
|
if (len(s.Name) > 0 && strings.ToUpper(s.Name) == upperName) ||
|
||||||
|
(len(s.Symbol) > 0 && strings.ToUpper(s.Symbol) == upperSymbol) {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if strings.Contains(strings.ToUpper(security.Name), upperSearch) ||
|
||||||
|
|
||||||
|
// Try to find a partial string match on the name or symbol
|
||||||
|
for _, s := range securities {
|
||||||
|
sUpperName := strings.ToUpper(s.Name)
|
||||||
|
sUpperSymbol := strings.ToUpper(s.Symbol)
|
||||||
|
if (len(upperName) > 0 && len(s.Name) > 0 && (strings.Contains(upperName, sUpperName) || strings.Contains(sUpperName, upperName))) ||
|
||||||
|
(len(upperSymbol) > 0 && len(s.Symbol) > 0 && (strings.Contains(upperSymbol, sUpperSymbol) || strings.Contains(sUpperSymbol, upperSymbol))) {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give up and return the first security in the list
|
||||||
|
if len(securities) > 0 {
|
||||||
|
return securities[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there wasn't even one security in the list, make a new one
|
||||||
|
err = InsertSecurityTx(transaction, security)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return security, nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetSecurityByName(name string) (*Security, error) {
|
func GetSecurityByName(name string) (*Security, error) {
|
||||||
return nil, fmt.Errorf("unimplemented")
|
return nil, fmt.Errorf("unimplemented")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user