mirror of
				https://github.com/aclindsa/moneygo.git
				synced 2025-11-03 18:13:27 -05:00 
			
		
		
		
	Add direct OFX imports
This commit is contained in:
		
							
								
								
									
										12
									
								
								accounts.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								accounts.go
									
									
									
									
									
								
							@@ -225,11 +225,19 @@ func GetTradingAccount(transaction *gorp.Transaction, userid int64, securityid i
 | 
				
			|||||||
func GetImbalanceAccount(transaction *gorp.Transaction, userid int64, securityid int64) (*Account, error) {
 | 
					func GetImbalanceAccount(transaction *gorp.Transaction, userid int64, securityid int64) (*Account, error) {
 | 
				
			||||||
	var imbalanceAccount Account
 | 
						var imbalanceAccount Account
 | 
				
			||||||
	var account Account
 | 
						var account Account
 | 
				
			||||||
 | 
						xxxtemplate := FindSecurityTemplate("XXX", Currency)
 | 
				
			||||||
 | 
						if xxxtemplate == nil {
 | 
				
			||||||
 | 
							return nil, errors.New("Couldn't find XXX security template")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						xxxsecurity, err := ImportGetCreateSecurity(transaction, userid, xxxtemplate)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, errors.New("Couldn't create XXX security")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	imbalanceAccount.UserId = userid
 | 
						imbalanceAccount.UserId = userid
 | 
				
			||||||
	imbalanceAccount.Name = "Imbalances"
 | 
						imbalanceAccount.Name = "Imbalances"
 | 
				
			||||||
	imbalanceAccount.ParentAccountId = -1
 | 
						imbalanceAccount.ParentAccountId = -1
 | 
				
			||||||
	imbalanceAccount.SecurityId = 840 /*USD*/ //FIXME SecurityId shouldn't matter for top-level imbalance account, but maybe we should grab the user's default
 | 
						imbalanceAccount.SecurityId = xxxsecurity.SecurityId
 | 
				
			||||||
	imbalanceAccount.Type = Bank
 | 
						imbalanceAccount.Type = Bank
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Find/create the top-level trading account
 | 
						// Find/create the top-level trading account
 | 
				
			||||||
@@ -238,7 +246,7 @@ func GetImbalanceAccount(transaction *gorp.Transaction, userid int64, securityid
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	security, err := GetSecurity(securityid, userid)
 | 
						security, err := GetSecurityTx(transaction, securityid, userid)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -302,7 +302,7 @@ func GnucashImportHandler(w http.ResponseWriter, r *http.Request) {
 | 
				
			|||||||
	securityMap := make(map[int64]int64)
 | 
						securityMap := make(map[int64]int64)
 | 
				
			||||||
	for _, security := range gnucashImport.Securities {
 | 
						for _, security := range gnucashImport.Securities {
 | 
				
			||||||
		securityId := security.SecurityId // save off because it could be updated
 | 
							securityId := security.SecurityId // save off because it could be updated
 | 
				
			||||||
		s, err := ImportGetCreateSecurity(sqltransaction, user, &security)
 | 
							s, err := ImportGetCreateSecurity(sqltransaction, user.UserId, &security)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			sqltransaction.Rollback()
 | 
								sqltransaction.Rollback()
 | 
				
			||||||
			WriteError(w, 6 /*Import Error*/)
 | 
								WriteError(w, 6 /*Import Error*/)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										193
									
								
								imports.go
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								imports.go
									
									
									
									
									
								
							@@ -1,37 +1,29 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"github.com/aclindsa/ofxgo"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"math/big"
 | 
						"math/big"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					type OFXDownload struct {
 | 
				
			||||||
 * Assumes the User is a valid, signed-in user, but accountid has not yet been validated
 | 
						OFXPassword string
 | 
				
			||||||
 */
 | 
						StartDate   time.Time
 | 
				
			||||||
func AccountImportHandler(w http.ResponseWriter, r *http.Request, user *User, accountid int64, importtype string) {
 | 
						EndDate     time.Time
 | 
				
			||||||
	//TODO branch off for different importtype's
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	multipartReader, err := r.MultipartReader()
 | 
					func (od *OFXDownload) Read(json_str string) error {
 | 
				
			||||||
	if err != nil {
 | 
						dec := json.NewDecoder(strings.NewReader(json_str))
 | 
				
			||||||
		WriteError(w, 3 /*Invalid Request*/)
 | 
						return dec.Decode(od)
 | 
				
			||||||
		return
 | 
					}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// assume there is only one 'part'
 | 
					func ofxImportHelper(r io.Reader, w http.ResponseWriter, user *User, accountid int64) {
 | 
				
			||||||
	part, err := multipartReader.NextPart()
 | 
						itl, err := ImportOFX(r)
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		if err == io.EOF {
 | 
					 | 
				
			||||||
			WriteError(w, 3 /*Invalid Request*/)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			WriteError(w, 999 /*Internal Error*/)
 | 
					 | 
				
			||||||
			log.Print(err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	itl, err := ImportOFX(part)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		//TODO is this necessarily an invalid request (what if it was an error on our end)?
 | 
							//TODO is this necessarily an invalid request (what if it was an error on our end)?
 | 
				
			||||||
@@ -86,14 +78,17 @@ func AccountImportHandler(w http.ResponseWriter, r *http.Request, user *User, ac
 | 
				
			|||||||
	// SecurityIds to the actual SecurityIDs
 | 
						// SecurityIds to the actual SecurityIDs
 | 
				
			||||||
	var securitymap = make(map[int64]*Security)
 | 
						var securitymap = make(map[int64]*Security)
 | 
				
			||||||
	for _, ofxsecurity := range itl.Securities {
 | 
						for _, ofxsecurity := range itl.Securities {
 | 
				
			||||||
		security, err := ImportGetCreateSecurity(sqltransaction, user, &ofxsecurity)
 | 
							// save off since ImportGetCreateSecurity overwrites SecurityId on
 | 
				
			||||||
 | 
							// ofxsecurity
 | 
				
			||||||
 | 
							oldsecurityid := ofxsecurity.SecurityId
 | 
				
			||||||
 | 
							security, err := ImportGetCreateSecurity(sqltransaction, user.UserId, &ofxsecurity)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			sqltransaction.Rollback()
 | 
								sqltransaction.Rollback()
 | 
				
			||||||
			WriteError(w, 999 /*Internal Error*/)
 | 
								WriteError(w, 999 /*Internal Error*/)
 | 
				
			||||||
			log.Print(err)
 | 
								log.Print(err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		securitymap[ofxsecurity.SecurityId] = security
 | 
							securitymap[oldsecurityid] = security
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if account.SecurityId != securitymap[importedAccount.SecurityId].SecurityId {
 | 
						if account.SecurityId != securitymap[importedAccount.SecurityId].SecurityId {
 | 
				
			||||||
@@ -236,3 +231,151 @@ func AccountImportHandler(w http.ResponseWriter, r *http.Request, user *User, ac
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	WriteSuccess(w)
 | 
						WriteSuccess(w)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func OFXImportHandler(w http.ResponseWriter, r *http.Request, user *User, accountid int64) {
 | 
				
			||||||
 | 
						download_json := r.PostFormValue("ofxdownload")
 | 
				
			||||||
 | 
						if download_json == "" {
 | 
				
			||||||
 | 
							log.Print("download_json")
 | 
				
			||||||
 | 
							WriteError(w, 3 /*Invalid Request*/)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var ofxdownload OFXDownload
 | 
				
			||||||
 | 
						err := ofxdownload.Read(download_json)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Print("ofxdownload.Read")
 | 
				
			||||||
 | 
							WriteError(w, 3 /*Invalid Request*/)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						account, err := GetAccount(accountid, user.UserId)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Print("GetAccount")
 | 
				
			||||||
 | 
							WriteError(w, 3 /*Invalid Request*/)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ofxver := ofxgo.OfxVersion203
 | 
				
			||||||
 | 
						if len(account.OFXVersion) != 0 {
 | 
				
			||||||
 | 
							ofxver, err = ofxgo.NewOfxVersion(account.OFXVersion)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Print("NewOfxVersion")
 | 
				
			||||||
 | 
								WriteError(w, 3 /*Invalid Request*/)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var client = ofxgo.Client{
 | 
				
			||||||
 | 
							AppID:       account.OFXAppID,
 | 
				
			||||||
 | 
							AppVer:      account.OFXAppVer,
 | 
				
			||||||
 | 
							SpecVersion: ofxver,
 | 
				
			||||||
 | 
							NoIndent:    account.OFXNoIndent,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var query ofxgo.Request
 | 
				
			||||||
 | 
						query.URL = account.OFXURL
 | 
				
			||||||
 | 
						query.Signon.ClientUID = ofxgo.UID(account.OFXClientUID)
 | 
				
			||||||
 | 
						query.Signon.UserID = ofxgo.String(account.OFXUser)
 | 
				
			||||||
 | 
						query.Signon.UserPass = ofxgo.String(ofxdownload.OFXPassword)
 | 
				
			||||||
 | 
						query.Signon.Org = ofxgo.String(account.OFXORG)
 | 
				
			||||||
 | 
						query.Signon.Fid = ofxgo.String(account.OFXFID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						transactionuid, err := ofxgo.RandomUID()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							WriteError(w, 999 /*Internal Error*/)
 | 
				
			||||||
 | 
							log.Println("Error creating uid for transaction:", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if account.Type == Investment {
 | 
				
			||||||
 | 
							// Investment account
 | 
				
			||||||
 | 
							statementRequest := ofxgo.InvStatementRequest{
 | 
				
			||||||
 | 
								TrnUID: *transactionuid,
 | 
				
			||||||
 | 
								InvAcctFrom: ofxgo.InvAcct{
 | 
				
			||||||
 | 
									BrokerID: ofxgo.String(account.OFXBankID),
 | 
				
			||||||
 | 
									AcctID:   ofxgo.String(account.OFXAcctID),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Include:        true,
 | 
				
			||||||
 | 
								IncludeOO:      true,
 | 
				
			||||||
 | 
								IncludePos:     true,
 | 
				
			||||||
 | 
								IncludeBalance: true,
 | 
				
			||||||
 | 
								Include401K:    true,
 | 
				
			||||||
 | 
								Include401KBal: true,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							query.InvStmt = append(query.InvStmt, &statementRequest)
 | 
				
			||||||
 | 
						} else if account.OFXAcctType == "CC" {
 | 
				
			||||||
 | 
							// Import credit card transactions
 | 
				
			||||||
 | 
							statementRequest := ofxgo.CCStatementRequest{
 | 
				
			||||||
 | 
								TrnUID: *transactionuid,
 | 
				
			||||||
 | 
								CCAcctFrom: ofxgo.CCAcct{
 | 
				
			||||||
 | 
									AcctID: ofxgo.String(account.OFXAcctID),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Include: true,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							query.CreditCard = append(query.CreditCard, &statementRequest)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// Import generic bank transactions
 | 
				
			||||||
 | 
							acctTypeEnum, err := ofxgo.NewAcctType(account.OFXAcctType)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								WriteError(w, 3 /*Invalid Request*/)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							statementRequest := ofxgo.StatementRequest{
 | 
				
			||||||
 | 
								TrnUID: *transactionuid,
 | 
				
			||||||
 | 
								BankAcctFrom: ofxgo.BankAcct{
 | 
				
			||||||
 | 
									BankID:   ofxgo.String(account.OFXBankID),
 | 
				
			||||||
 | 
									AcctID:   ofxgo.String(account.OFXAcctID),
 | 
				
			||||||
 | 
									AcctType: acctTypeEnum,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Include: true,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							query.Bank = append(query.Bank, &statementRequest)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						response, err := client.RequestNoParse(&query)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							// TODO this could be an error talking with the OFX server...
 | 
				
			||||||
 | 
							WriteError(w, 3 /*Invalid Request*/)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer response.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ofxImportHelper(response.Body, w, user, accountid)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func OFXFileImportHandler(w http.ResponseWriter, r *http.Request, user *User, accountid int64) {
 | 
				
			||||||
 | 
						multipartReader, err := r.MultipartReader()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							WriteError(w, 3 /*Invalid Request*/)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// assume there is only one 'part'
 | 
				
			||||||
 | 
						part, err := multipartReader.NextPart()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if err == io.EOF {
 | 
				
			||||||
 | 
								WriteError(w, 3 /*Invalid Request*/)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								WriteError(w, 999 /*Internal Error*/)
 | 
				
			||||||
 | 
								log.Print(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ofxImportHelper(part, w, user, accountid)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Assumes the User is a valid, signed-in user, but accountid has not yet been validated
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					func AccountImportHandler(w http.ResponseWriter, r *http.Request, user *User, accountid int64, importtype string) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch importtype {
 | 
				
			||||||
 | 
						case "ofx":
 | 
				
			||||||
 | 
							OFXImportHandler(w, r, user, accountid)
 | 
				
			||||||
 | 
						case "ofxfile":
 | 
				
			||||||
 | 
							OFXFileImportHandler(w, r, user, accountid)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							WriteError(w, 3 /*Invalid Request*/)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										163
									
								
								js/actions/ImportActions.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								js/actions/ImportActions.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,163 @@
 | 
				
			|||||||
 | 
					var ImportConstants = require('../constants/ImportConstants');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var models = require('../models.js');
 | 
				
			||||||
 | 
					var OFXDownload = models.OFXDownload;
 | 
				
			||||||
 | 
					var Error = models.Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function beginImport() {
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							type: ImportConstants.BEGIN_IMPORT
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function updateProgress(progress) {
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							type: ImportConstants.UPDATE_IMPORT_PROGRESS,
 | 
				
			||||||
 | 
							progress: progress
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function importFinished() {
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							type: ImportConstants.IMPORT_FINISHED
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function importFailed(error) {
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							type: ImportConstants.IMPORT_FAILED,
 | 
				
			||||||
 | 
							error: error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function openModal() {
 | 
				
			||||||
 | 
						return function(dispatch) {
 | 
				
			||||||
 | 
							dispatch({
 | 
				
			||||||
 | 
								type: ImportConstants.OPEN_IMPORT_MODAL
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function closeModal() {
 | 
				
			||||||
 | 
						return function(dispatch) {
 | 
				
			||||||
 | 
							dispatch({
 | 
				
			||||||
 | 
								type: ImportConstants.CLOSE_IMPORT_MODAL
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function importOFX(account, password, startDate, endDate) {
 | 
				
			||||||
 | 
						return function(dispatch) {
 | 
				
			||||||
 | 
							dispatch(beginImport());
 | 
				
			||||||
 | 
							dispatch(updateProgress(50));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var ofxdownload = new OFXDownload();
 | 
				
			||||||
 | 
							ofxdownload.OFXPassword = password;
 | 
				
			||||||
 | 
							ofxdownload.StartDate = startDate;
 | 
				
			||||||
 | 
							ofxdownload.EndDate = endDate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							$.ajax({
 | 
				
			||||||
 | 
								type: "POST",
 | 
				
			||||||
 | 
								dataType: "json",
 | 
				
			||||||
 | 
								url: "account/"+account.AccountId+"/import/ofx",
 | 
				
			||||||
 | 
								data: {ofxdownload: ofxdownload.toJSON()},
 | 
				
			||||||
 | 
								success: function(data, status, jqXHR) {
 | 
				
			||||||
 | 
									var e = new Error();
 | 
				
			||||||
 | 
									e.fromJSON(data);
 | 
				
			||||||
 | 
									if (e.isError()) {
 | 
				
			||||||
 | 
										var errString = e.ErrorString;
 | 
				
			||||||
 | 
										if (e.ErrorId == 3 /* Invalid Request */) {
 | 
				
			||||||
 | 
											errString = "Please check that your password and all other OFX login credentials are correct.";
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										dispatch(importFailed(errString));
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										dispatch(importFinished());
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								error: function(jqXHR, status, error) {
 | 
				
			||||||
 | 
									dispatch(importFailed(error));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function importFile(url, inputElement) {
 | 
				
			||||||
 | 
						return function(dispatch) {
 | 
				
			||||||
 | 
							dispatch(beginImport());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (inputElement.files.length == 0) {
 | 
				
			||||||
 | 
								dispatch(importFailed("No files specified to be imported"))
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (inputElement.files.length > 1) {
 | 
				
			||||||
 | 
								dispatch(importFailed("More than one file specified for import, only one allowed at a time"))
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var file = inputElement.files[0];
 | 
				
			||||||
 | 
							var formData = new FormData();
 | 
				
			||||||
 | 
							formData.append('importfile', file, file.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var handleSetProgress = function(e) {
 | 
				
			||||||
 | 
								if (e.lengthComputable) {
 | 
				
			||||||
 | 
									var pct = Math.round(e.loaded/e.total*100);
 | 
				
			||||||
 | 
									dispatch(updateProgress(pct));
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									dispatch(updateProgress(50));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							$.ajax({
 | 
				
			||||||
 | 
								type: "POST",
 | 
				
			||||||
 | 
								url: url,
 | 
				
			||||||
 | 
								data: formData,
 | 
				
			||||||
 | 
								xhr: function() {
 | 
				
			||||||
 | 
									var xhrObject = $.ajaxSettings.xhr();
 | 
				
			||||||
 | 
									if (xhrObject.upload) {
 | 
				
			||||||
 | 
										xhrObject.upload.addEventListener('progress', handleSetProgress, false);
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										dispatch(importFailed("File upload failed because xhr.upload isn't supported by your browser."));
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return xhrObject;
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								success: function(data, status, jqXHR) {
 | 
				
			||||||
 | 
									var e = new Error();
 | 
				
			||||||
 | 
									e.fromJSON(data);
 | 
				
			||||||
 | 
									if (e.isError()) {
 | 
				
			||||||
 | 
										var errString = e.ErrorString;
 | 
				
			||||||
 | 
										if (e.ErrorId == 3 /* Invalid Request */) {
 | 
				
			||||||
 | 
											errString = "Please check that the file you uploaded is valid and try again.";
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										dispatch(importFailed(errString));
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										dispatch(importFinished());
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								error: function(jqXHR, status, error) {
 | 
				
			||||||
 | 
									dispatch(importFailed(error));
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								// So jQuery doesn't try to process the data or content-type
 | 
				
			||||||
 | 
								cache: false,
 | 
				
			||||||
 | 
								contentType: false,
 | 
				
			||||||
 | 
								processData: false
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function importOFXFile(inputElement, account) {
 | 
				
			||||||
 | 
						url = "account/"+account.AccountId+"/import/ofxfile";
 | 
				
			||||||
 | 
						return importFile(url, inputElement);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function importGnucash(inputElement) {
 | 
				
			||||||
 | 
						url = "import/gnucash";
 | 
				
			||||||
 | 
						return importFile(url, inputElement);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
						openModal: openModal,
 | 
				
			||||||
 | 
						closeModal: closeModal,
 | 
				
			||||||
 | 
						importOFX: importOFX,
 | 
				
			||||||
 | 
						importOFXFile: importOFXFile,
 | 
				
			||||||
 | 
						importGnucash: importGnucash
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -473,25 +473,29 @@ const AddEditTransactionModal = React.createClass({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const ImportType = {
 | 
					const ImportType = {
 | 
				
			||||||
	OFX: 1,
 | 
						OFX: 1,
 | 
				
			||||||
	Gnucash: 2
 | 
						OFXFile: 2,
 | 
				
			||||||
 | 
						Gnucash: 3
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
var ImportTypeList = [];
 | 
					var ImportTypeList = [];
 | 
				
			||||||
for (var type in ImportType) {
 | 
					for (var type in ImportType) {
 | 
				
			||||||
	if (ImportType.hasOwnProperty(type)) {
 | 
						if (ImportType.hasOwnProperty(type)) {
 | 
				
			||||||
		var name = ImportType[type] == ImportType.OFX ? "OFX/QFX" : type; //QFX is a special snowflake
 | 
							var name = ImportType[type] == ImportType.OFX ? "Direct OFX" : type;
 | 
				
			||||||
 | 
							var name = ImportType[type] == ImportType.OFXFile ? "OFX/QFX File" : type; //QFX is a special snowflake
 | 
				
			||||||
		ImportTypeList.push({'TypeId': ImportType[type], 'Name': name});
 | 
							ImportTypeList.push({'TypeId': ImportType[type], 'Name': name});
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ImportTransactionsModal = React.createClass({
 | 
					const ImportTransactionsModal = React.createClass({
 | 
				
			||||||
	getInitialState: function() {
 | 
						getInitialState: function() {
 | 
				
			||||||
		 return {
 | 
							var startDate = new Date();
 | 
				
			||||||
			importing: false,
 | 
							startDate.setMonth(startDate.getMonth() - 1);
 | 
				
			||||||
			imported: false,
 | 
							return {
 | 
				
			||||||
			importFile: "",
 | 
								importFile: "",
 | 
				
			||||||
			importType: ImportType.Gnucash,
 | 
								importType: ImportType.Gnucash,
 | 
				
			||||||
			uploadProgress: -1,
 | 
								startDate: startDate,
 | 
				
			||||||
			error: null};
 | 
								endDate: new Date(),
 | 
				
			||||||
 | 
								password: "",
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	handleCancel: function() {
 | 
						handleCancel: function() {
 | 
				
			||||||
		this.setState(this.getInitialState());
 | 
							this.setState(this.getInitialState());
 | 
				
			||||||
@@ -504,73 +508,36 @@ const ImportTransactionsModal = React.createClass({
 | 
				
			|||||||
	handleTypeChange: function(type) {
 | 
						handleTypeChange: function(type) {
 | 
				
			||||||
		this.setState({importType: type.TypeId});
 | 
							this.setState({importType: type.TypeId});
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
						handlePasswordChange: function() {
 | 
				
			||||||
 | 
							this.setState({password: ReactDOM.findDOMNode(this.refs.password).value});
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						handleStartDateChange: function(date, string) {
 | 
				
			||||||
 | 
							if (date == null)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							this.setState({
 | 
				
			||||||
 | 
								startDate: date
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						handleEndDateChange: function(date, string) {
 | 
				
			||||||
 | 
							if (date == null)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							this.setState({
 | 
				
			||||||
 | 
								endDate: date
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
	handleSubmit: function() {
 | 
						handleSubmit: function() {
 | 
				
			||||||
 | 
							this.setState(this.getInitialState());
 | 
				
			||||||
		if (this.props.onSubmit != null)
 | 
							if (this.props.onSubmit != null)
 | 
				
			||||||
			this.props.onSubmit(this.props.account);
 | 
								this.props.onSubmit(this.props.account);
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	handleSetProgress: function(e) {
 | 
					 | 
				
			||||||
		if (e.lengthComputable) {
 | 
					 | 
				
			||||||
			var pct = Math.round(e.loaded/e.total*100);
 | 
					 | 
				
			||||||
			this.setState({uploadProgress: pct});
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			this.setState({uploadProgress: 50});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	handleImportTransactions: function() {
 | 
						handleImportTransactions: function() {
 | 
				
			||||||
		var file = ReactDOM.findDOMNode(this.refs.importfile).files[0];
 | 
							if (this.state.importType == ImportType.OFX) {
 | 
				
			||||||
		var formData = new FormData();
 | 
								this.props.onImportOFX(this.props.account, this.state.password, this.state.startDate, this.state.endDate);
 | 
				
			||||||
		formData.append('importfile', file, this.state.importFile);
 | 
							} else if (this.state.importType == ImportType.OFXFile) {
 | 
				
			||||||
		var url = ""
 | 
								this.props.onImportOFXFile(ReactDOM.findDOMNode(this.refs.importfile), this.props.account);
 | 
				
			||||||
		if (this.state.importType == ImportType.OFX)
 | 
							} else if (this.state.importType == ImportType.Gnucash) {
 | 
				
			||||||
			url = "account/"+this.props.account.AccountId+"/import/ofx";
 | 
								this.props.onImportGnucash(ReactDOM.findDOMNode(this.refs.importfile));
 | 
				
			||||||
		else if (this.state.importType == ImportType.Gnucash)
 | 
							}
 | 
				
			||||||
			url = "import/gnucash";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.setState({importing: true});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		$.ajax({
 | 
					 | 
				
			||||||
			type: "POST",
 | 
					 | 
				
			||||||
			url: url,
 | 
					 | 
				
			||||||
			data: formData,
 | 
					 | 
				
			||||||
			xhr: function() {
 | 
					 | 
				
			||||||
				var xhrObject = $.ajaxSettings.xhr();
 | 
					 | 
				
			||||||
				if (xhrObject.upload) {
 | 
					 | 
				
			||||||
					xhrObject.upload.addEventListener('progress', this.handleSetProgress, false);
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					console.log("File upload failed because !xhr.upload")
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				return xhrObject;
 | 
					 | 
				
			||||||
			}.bind(this),
 | 
					 | 
				
			||||||
			success: function(data, status, jqXHR) {
 | 
					 | 
				
			||||||
				var e = new Error();
 | 
					 | 
				
			||||||
				e.fromJSON(data);
 | 
					 | 
				
			||||||
				if (e.isError()) {
 | 
					 | 
				
			||||||
					var errString = e.ErrorString;
 | 
					 | 
				
			||||||
					if (e.ErrorId == 3 /* Invalid Request */) {
 | 
					 | 
				
			||||||
						errString = "Please check that the file you uploaded is valid and try again.";
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					this.setState({
 | 
					 | 
				
			||||||
						importing: false,
 | 
					 | 
				
			||||||
						error: errString
 | 
					 | 
				
			||||||
					});
 | 
					 | 
				
			||||||
					return;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				this.setState({
 | 
					 | 
				
			||||||
					uploadProgress: 100,
 | 
					 | 
				
			||||||
					importing: false,
 | 
					 | 
				
			||||||
					imported: true
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
			}.bind(this),
 | 
					 | 
				
			||||||
			error: function(e) {
 | 
					 | 
				
			||||||
				this.setState({importing: false});
 | 
					 | 
				
			||||||
				console.log("error handler", e);
 | 
					 | 
				
			||||||
			}.bind(this),
 | 
					 | 
				
			||||||
			// So jQuery doesn't try to process teh data or content-type
 | 
					 | 
				
			||||||
			cache: false,
 | 
					 | 
				
			||||||
			contentType: false,
 | 
					 | 
				
			||||||
			processData: false
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	render: function() {
 | 
						render: function() {
 | 
				
			||||||
		var accountNameLabel = "Performing global import:"
 | 
							var accountNameLabel = "Performing global import:"
 | 
				
			||||||
@@ -579,45 +546,104 @@ const ImportTransactionsModal = React.createClass({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// Display the progress bar if an upload/import is in progress
 | 
							// Display the progress bar if an upload/import is in progress
 | 
				
			||||||
		var progressBar = [];
 | 
							var progressBar = [];
 | 
				
			||||||
		if (this.state.importing && this.state.uploadProgress == 100) {
 | 
							if (this.props.imports.importing && this.props.imports.uploadProgress == 100) {
 | 
				
			||||||
			progressBar = (<ProgressBar now={this.state.uploadProgress} active label="Importing transactions..." />);
 | 
								progressBar = (<ProgressBar now={this.props.imports.uploadProgress} active label="Importing transactions..." />);
 | 
				
			||||||
		} else if (this.state.importing && this.state.uploadProgress != -1) {
 | 
							} else if (this.props.imports.importing) {
 | 
				
			||||||
			progressBar = (<ProgressBar now={this.state.uploadProgress} active label="Uploading... %(percent)s%" />);
 | 
								progressBar = (<ProgressBar now={this.props.imports.uploadProgress} active label={`Uploading... ${this.props.imports.uploadProgress}%`} />);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Create panel, possibly displaying error or success messages
 | 
							// Create panel, possibly displaying error or success messages
 | 
				
			||||||
		var panel = [];
 | 
							var panel = [];
 | 
				
			||||||
		if (this.state.error != null) {
 | 
							if (this.props.imports.importFailed) {
 | 
				
			||||||
			panel = (<Panel header="Error Importing Transactions" bsStyle="danger">{this.state.error}</Panel>);
 | 
								panel = (<Panel header="Error Importing Transactions" bsStyle="danger">{this.props.imports.errorMessage}</Panel>);
 | 
				
			||||||
		} else if (this.state.imported) {
 | 
							} else if (this.props.imports.importFinished) {
 | 
				
			||||||
			panel = (<Panel header="Successfully Imported Transactions" bsStyle="success">Your import is now complete.</Panel>);
 | 
								panel = (<Panel header="Successfully Imported Transactions" bsStyle="success">Your import is now complete.</Panel>);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Display proper buttons, possibly disabling them if an import is in progress
 | 
							// Display proper buttons, possibly disabling them if an import is in progress
 | 
				
			||||||
		var button1 = [];
 | 
							var button1 = [];
 | 
				
			||||||
		var button2 = [];
 | 
							var button2 = [];
 | 
				
			||||||
		if (!this.state.imported && this.state.error == null) {
 | 
							if (!this.props.imports.importFinished && !this.props.imports.importFailed) {
 | 
				
			||||||
			button1 = (<Button onClick={this.handleCancel} disabled={this.state.importing} bsStyle="warning">Cancel</Button>);
 | 
								var importingDisabled = this.props.imports.importing || (this.state.importType != ImportType.OFX && this.state.importFile == "") || (this.state.importType == ImportType.OFX && this.state.password == "");
 | 
				
			||||||
			button2 = (<Button onClick={this.handleImportTransactions} disabled={this.state.importing || this.state.importFile == ""} bsStyle="success">Import</Button>);
 | 
								button1 = (<Button onClick={this.handleCancel} disabled={this.props.imports.importing} bsStyle="warning">Cancel</Button>);
 | 
				
			||||||
 | 
								button2 = (<Button onClick={this.handleImportTransactions} disabled={importingDisabled} bsStyle="success">Import</Button>);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			button1 = (<Button onClick={this.handleCancel} disabled={this.state.importing} bsStyle="success">OK</Button>);
 | 
								button1 = (<Button onClick={this.handleSubmit} disabled={this.props.imports.importing} bsStyle="success">OK</Button>);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		var inputDisabled = (this.state.importing || this.state.error != null || this.state.imported) ? true : false;
 | 
							var inputDisabled = (this.props.imports.importing || this.props.imports.importFailed || this.props.imports.importFinished) ? true : false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Disable OFX/QFX imports if no account is selected
 | 
							// Disable OFX/QFX imports if no account is selected
 | 
				
			||||||
		var disabledTypes = false;
 | 
							var disabledTypes = false;
 | 
				
			||||||
		if (this.props.account == null)
 | 
							if (this.props.account == null)
 | 
				
			||||||
			disabledTypes = [ImportTypeList[ImportType.OFX - 1]];
 | 
								disabledTypes = [ImportTypeList[ImportType.OFX - 1], ImportTypeList[ImportType.OFXFile - 1]];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var importForm = [];
 | 
				
			||||||
 | 
							if (this.state.importType == ImportType.OFX) {
 | 
				
			||||||
 | 
								importForm = (
 | 
				
			||||||
 | 
									<div>
 | 
				
			||||||
 | 
									<FormGroup>
 | 
				
			||||||
 | 
										<Col componentClass={ControlLabel} xs={2}>OFX Password</Col>
 | 
				
			||||||
 | 
										<Col xs={10}>
 | 
				
			||||||
 | 
											<FormControl type="password"
 | 
				
			||||||
 | 
												value={this.state.password}
 | 
				
			||||||
 | 
												placeholder="Password..."
 | 
				
			||||||
 | 
												ref="password"
 | 
				
			||||||
 | 
												onChange={this.handlePasswordChange} />
 | 
				
			||||||
 | 
										</Col>
 | 
				
			||||||
 | 
									</FormGroup>
 | 
				
			||||||
 | 
									<FormGroup>
 | 
				
			||||||
 | 
										<Col componentClass={ControlLabel} xs={2}>Start Date</Col>
 | 
				
			||||||
 | 
										<Col xs={10}>
 | 
				
			||||||
 | 
										<DateTimePicker
 | 
				
			||||||
 | 
											time={false}
 | 
				
			||||||
 | 
											defaultValue={this.state.startDate}
 | 
				
			||||||
 | 
											onChange={this.handleStartDateChange} />
 | 
				
			||||||
 | 
										</Col>
 | 
				
			||||||
 | 
									</FormGroup>
 | 
				
			||||||
 | 
									<FormGroup>
 | 
				
			||||||
 | 
										<Col componentClass={ControlLabel} xs={2}>End Date</Col>
 | 
				
			||||||
 | 
										<Col xs={10}>
 | 
				
			||||||
 | 
										<DateTimePicker
 | 
				
			||||||
 | 
											time={false}
 | 
				
			||||||
 | 
											defaultValue={this.state.endDate}
 | 
				
			||||||
 | 
											onChange={this.handleEndDateChange} />
 | 
				
			||||||
 | 
										</Col>
 | 
				
			||||||
 | 
									</FormGroup>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								importForm = (
 | 
				
			||||||
 | 
									<FormGroup>
 | 
				
			||||||
 | 
										<Col componentClass={ControlLabel} xs={2}>File</Col>
 | 
				
			||||||
 | 
										<Col xs={10}>
 | 
				
			||||||
 | 
										<FormControl type="file"
 | 
				
			||||||
 | 
											ref="importfile"
 | 
				
			||||||
 | 
											disabled={inputDisabled}
 | 
				
			||||||
 | 
											value={this.state.importFile}
 | 
				
			||||||
 | 
											onChange={this.handleImportChange} />
 | 
				
			||||||
 | 
										<HelpBlock>Select a file to upload.</HelpBlock>
 | 
				
			||||||
 | 
										</Col>
 | 
				
			||||||
 | 
									</FormGroup>
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return (
 | 
							return (
 | 
				
			||||||
			<Modal show={this.props.show} onHide={this.handleCancel} bsSize="small">
 | 
								<Modal show={this.props.show} onHide={this.handleCancel}>
 | 
				
			||||||
				<Modal.Header closeButton>
 | 
									<Modal.Header closeButton>
 | 
				
			||||||
					<Modal.Title>Import Transactions</Modal.Title>
 | 
										<Modal.Title>Import Transactions</Modal.Title>
 | 
				
			||||||
				</Modal.Header>
 | 
									</Modal.Header>
 | 
				
			||||||
				<Modal.Body>
 | 
									<Modal.Body>
 | 
				
			||||||
				<form onSubmit={this.handleImportTransactions}
 | 
									<Form horizontal onSubmit={this.handleImportTransactions}
 | 
				
			||||||
						encType="multipart/form-data"
 | 
											encType="multipart/form-data"
 | 
				
			||||||
						ref="importform">
 | 
											ref="importform">
 | 
				
			||||||
 | 
										<FormGroup>
 | 
				
			||||||
 | 
										<Col xs={12}>
 | 
				
			||||||
 | 
											<ControlLabel>{accountNameLabel}</ControlLabel>
 | 
				
			||||||
 | 
										</Col>
 | 
				
			||||||
 | 
										</FormGroup>
 | 
				
			||||||
 | 
										<FormGroup>
 | 
				
			||||||
 | 
										<Col componentClass={ControlLabel} xs={2}>Import Type</Col>
 | 
				
			||||||
 | 
										<Col xs={10}>
 | 
				
			||||||
					<DropdownList
 | 
										<DropdownList
 | 
				
			||||||
						data={ImportTypeList}
 | 
											data={ImportTypeList}
 | 
				
			||||||
						valueField='TypeId'
 | 
											valueField='TypeId'
 | 
				
			||||||
@@ -626,16 +652,10 @@ const ImportTransactionsModal = React.createClass({
 | 
				
			|||||||
						defaultValue={this.state.importType}
 | 
											defaultValue={this.state.importType}
 | 
				
			||||||
						disabled={disabledTypes}
 | 
											disabled={disabledTypes}
 | 
				
			||||||
						ref="importtype" />
 | 
											ref="importtype" />
 | 
				
			||||||
					<FormGroup>
 | 
										</Col>
 | 
				
			||||||
						<ControlLabel>{accountNameLabel}</ControlLabel>
 | 
					 | 
				
			||||||
						<FormControl type="file"
 | 
					 | 
				
			||||||
							ref="importfile"
 | 
					 | 
				
			||||||
							disabled={inputDisabled}
 | 
					 | 
				
			||||||
							value={this.state.importFile}
 | 
					 | 
				
			||||||
							onChange={this.handleImportChange} />
 | 
					 | 
				
			||||||
						<HelpBlock>Select a file to upload.</HelpBlock>
 | 
					 | 
				
			||||||
					</FormGroup>
 | 
										</FormGroup>
 | 
				
			||||||
				</form>
 | 
										{importForm}
 | 
				
			||||||
 | 
									</Form>
 | 
				
			||||||
				{progressBar}
 | 
									{progressBar}
 | 
				
			||||||
				{panel}
 | 
									{panel}
 | 
				
			||||||
				</Modal.Body>
 | 
									</Modal.Body>
 | 
				
			||||||
@@ -654,7 +674,6 @@ module.exports = React.createClass({
 | 
				
			|||||||
	displayName: "AccountRegister",
 | 
						displayName: "AccountRegister",
 | 
				
			||||||
	getInitialState: function() {
 | 
						getInitialState: function() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			importingTransactions: false,
 | 
					 | 
				
			||||||
			newTransaction: null,
 | 
								newTransaction: null,
 | 
				
			||||||
			height: 0
 | 
								height: 0
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
@@ -695,16 +714,6 @@ module.exports = React.createClass({
 | 
				
			|||||||
			newTransaction: newTransaction
 | 
								newTransaction: newTransaction
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	handleImportClicked: function() {
 | 
					 | 
				
			||||||
		this.setState({
 | 
					 | 
				
			||||||
			importingTransactions: true
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	handleImportingCancel: function() {
 | 
					 | 
				
			||||||
		this.setState({
 | 
					 | 
				
			||||||
			importingTransactions: false
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	ajaxError: function(jqXHR, status, error) {
 | 
						ajaxError: function(jqXHR, status, error) {
 | 
				
			||||||
		var e = new Error();
 | 
							var e = new Error();
 | 
				
			||||||
		e.ErrorId = 5;
 | 
							e.ErrorId = 5;
 | 
				
			||||||
@@ -725,7 +734,8 @@ module.exports = React.createClass({
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	handleImportComplete: function() {
 | 
						handleImportComplete: function() {
 | 
				
			||||||
		this.setState({importingTransactions: false});
 | 
							this.props.onCloseImportModal();
 | 
				
			||||||
 | 
							this.props.onFetchAllAccounts();
 | 
				
			||||||
		this.props.onFetchTransactionPage(this.props.accounts[this.props.selectedAccount], this.props.pageSize, this.props.transactionPage.page);
 | 
							this.props.onFetchTransactionPage(this.props.accounts[this.props.selectedAccount], this.props.pageSize, this.props.transactionPage.page);
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	handleDeleteTransaction: function(transaction) {
 | 
						handleDeleteTransaction: function(transaction) {
 | 
				
			||||||
@@ -810,11 +820,16 @@ module.exports = React.createClass({
 | 
				
			|||||||
					onDelete={this.handleDeleteTransaction}
 | 
										onDelete={this.handleDeleteTransaction}
 | 
				
			||||||
					securities={this.props.securities} />
 | 
										securities={this.props.securities} />
 | 
				
			||||||
				<ImportTransactionsModal
 | 
									<ImportTransactionsModal
 | 
				
			||||||
					show={this.state.importingTransactions}
 | 
										imports={this.props.imports}
 | 
				
			||||||
 | 
										show={this.props.imports.showModal}
 | 
				
			||||||
					account={this.props.accounts[this.props.selectedAccount]}
 | 
										account={this.props.accounts[this.props.selectedAccount]}
 | 
				
			||||||
					accounts={this.props.accounts}
 | 
										accounts={this.props.accounts}
 | 
				
			||||||
					onCancel={this.handleImportingCancel}
 | 
										onCancel={this.props.onCloseImportModal}
 | 
				
			||||||
					onSubmit={this.handleImportComplete}/>
 | 
										onHide={this.props.onCloseImportModal}
 | 
				
			||||||
 | 
										onSubmit={this.handleImportComplete}
 | 
				
			||||||
 | 
										onImportOFX={this.props.onImportOFX}
 | 
				
			||||||
 | 
										onImportOFXFile={this.props.onImportOFXFile}
 | 
				
			||||||
 | 
										onImportGnucash={this.props.onImportGnucash} />
 | 
				
			||||||
				<div className="transactions-register-toolbar">
 | 
									<div className="transactions-register-toolbar">
 | 
				
			||||||
				Transactions for '{name}'
 | 
									Transactions for '{name}'
 | 
				
			||||||
				<ButtonToolbar className="pull-right">
 | 
									<ButtonToolbar className="pull-right">
 | 
				
			||||||
@@ -835,7 +850,7 @@ module.exports = React.createClass({
 | 
				
			|||||||
						<Glyphicon glyph='plus-sign' /> New Transaction
 | 
											<Glyphicon glyph='plus-sign' /> New Transaction
 | 
				
			||||||
					</Button>
 | 
										</Button>
 | 
				
			||||||
					<Button
 | 
										<Button
 | 
				
			||||||
							onClick={this.handleImportClicked}
 | 
												onClick={this.props.onOpenImportModal}
 | 
				
			||||||
							bsStyle="primary">
 | 
												bsStyle="primary">
 | 
				
			||||||
						<Glyphicon glyph='import' /> Import
 | 
											<Glyphicon glyph='import' /> Import
 | 
				
			||||||
					</Button>
 | 
										</Button>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -139,7 +139,7 @@ const AddEditAccountModal = React.createClass({
 | 
				
			|||||||
		a.OFXAppID = this.state.ofxappid;
 | 
							a.OFXAppID = this.state.ofxappid;
 | 
				
			||||||
		a.OFXAppVer = this.state.ofxappver;
 | 
							a.OFXAppVer = this.state.ofxappver;
 | 
				
			||||||
		a.OFXVersion = this.state.ofxversion;
 | 
							a.OFXVersion = this.state.ofxversion;
 | 
				
			||||||
		a.OFXNoIndent = this.state.ofxNoIndent;
 | 
							a.OFXNoIndent = this.state.ofxnoindent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (this.props.onSubmit != null)
 | 
							if (this.props.onSubmit != null)
 | 
				
			||||||
			this.props.onSubmit(a);
 | 
								this.props.onSubmit(a);
 | 
				
			||||||
@@ -169,6 +169,7 @@ const AddEditAccountModal = React.createClass({
 | 
				
			|||||||
							ref="ofxaccttype">
 | 
												ref="ofxaccttype">
 | 
				
			||||||
						<option value="CHECKING">Checking</option>
 | 
											<option value="CHECKING">Checking</option>
 | 
				
			||||||
						<option value="SAVINGS">Savings</option>
 | 
											<option value="SAVINGS">Savings</option>
 | 
				
			||||||
 | 
											<option value="CC">Credit Card</option>
 | 
				
			||||||
						<option value="MONEYMRKT">Money Market</option>
 | 
											<option value="MONEYMRKT">Money Market</option>
 | 
				
			||||||
						<option value="CREDITLINE">Credit Line</option>
 | 
											<option value="CREDITLINE">Credit Line</option>
 | 
				
			||||||
						<option value="CD">CD</option>
 | 
											<option value="CD">CD</option>
 | 
				
			||||||
@@ -674,12 +675,19 @@ module.exports = React.createClass({
 | 
				
			|||||||
						securities={this.props.securities}
 | 
											securities={this.props.securities}
 | 
				
			||||||
						transactions={this.props.transactions}
 | 
											transactions={this.props.transactions}
 | 
				
			||||||
						transactionPage={this.props.transactionPage}
 | 
											transactionPage={this.props.transactionPage}
 | 
				
			||||||
 | 
											imports={this.props.imports}
 | 
				
			||||||
 | 
											onFetchAllAccounts={this.props.onFetchAllAccounts}
 | 
				
			||||||
						onCreateTransaction={this.props.onCreateTransaction}
 | 
											onCreateTransaction={this.props.onCreateTransaction}
 | 
				
			||||||
						onUpdateTransaction={this.props.onUpdateTransaction}
 | 
											onUpdateTransaction={this.props.onUpdateTransaction}
 | 
				
			||||||
						onDeleteTransaction={this.props.onDeleteTransaction}
 | 
											onDeleteTransaction={this.props.onDeleteTransaction}
 | 
				
			||||||
						onSelectTransaction={this.props.onSelectTransaction}
 | 
											onSelectTransaction={this.props.onSelectTransaction}
 | 
				
			||||||
						onUnselectTransaction={this.props.onUnselectTransaction}
 | 
											onUnselectTransaction={this.props.onUnselectTransaction}
 | 
				
			||||||
						onFetchTransactionPage={this.props.onFetchTransactionPage}/>
 | 
											onFetchTransactionPage={this.props.onFetchTransactionPage}
 | 
				
			||||||
 | 
											onOpenImportModal={this.props.onOpenImportModal}
 | 
				
			||||||
 | 
											onCloseImportModal={this.props.onCloseImportModal}
 | 
				
			||||||
 | 
											onImportOFX={this.props.onImportOFX}
 | 
				
			||||||
 | 
											onImportOFXFile={this.props.onImportOFXFile}
 | 
				
			||||||
 | 
											onImportGnucash={this.props.onImportGnucash} />
 | 
				
			||||||
				</Col>
 | 
									</Col>
 | 
				
			||||||
			</Row></Grid>
 | 
								</Row></Grid>
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								js/constants/ImportConstants.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								js/constants/ImportConstants.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					var keyMirror = require('keymirror');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = keyMirror({
 | 
				
			||||||
 | 
						OPEN_IMPORT_MODAL: null,
 | 
				
			||||||
 | 
						CLOSE_IMPORT_MODAL: null,
 | 
				
			||||||
 | 
						BEGIN_IMPORT: null,
 | 
				
			||||||
 | 
						UPDATE_IMPORT_PROGRESS: null,
 | 
				
			||||||
 | 
						IMPORT_FINISHED: null,
 | 
				
			||||||
 | 
						IMPORT_FAILED: null
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@@ -2,6 +2,7 @@ var connect = require('react-redux').connect;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var AccountActions = require('../actions/AccountActions');
 | 
					var AccountActions = require('../actions/AccountActions');
 | 
				
			||||||
var TransactionActions = require('../actions/TransactionActions');
 | 
					var TransactionActions = require('../actions/TransactionActions');
 | 
				
			||||||
 | 
					var ImportActions = require('../actions/ImportActions');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var AccountsTab = require('../components/AccountsTab');
 | 
					var AccountsTab = require('../components/AccountsTab');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,12 +19,14 @@ function mapStateToProps(state) {
 | 
				
			|||||||
		security_list: security_list,
 | 
							security_list: security_list,
 | 
				
			||||||
		selectedAccount: state.selectedAccount,
 | 
							selectedAccount: state.selectedAccount,
 | 
				
			||||||
		transactions: state.transactions,
 | 
							transactions: state.transactions,
 | 
				
			||||||
		transactionPage: state.transactionPage
 | 
							transactionPage: state.transactionPage,
 | 
				
			||||||
 | 
							imports: state.imports
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function mapDispatchToProps(dispatch) {
 | 
					function mapDispatchToProps(dispatch) {
 | 
				
			||||||
	return {
 | 
						return {
 | 
				
			||||||
 | 
							onFetchAllAccounts: function() {dispatch(AccountActions.fetchAll())},
 | 
				
			||||||
		onCreateAccount: function(account) {dispatch(AccountActions.create(account))},
 | 
							onCreateAccount: function(account) {dispatch(AccountActions.create(account))},
 | 
				
			||||||
		onUpdateAccount: function(account) {dispatch(AccountActions.update(account))},
 | 
							onUpdateAccount: function(account) {dispatch(AccountActions.update(account))},
 | 
				
			||||||
		onDeleteAccount: function(account) {dispatch(AccountActions.remove(account))},
 | 
							onDeleteAccount: function(account) {dispatch(AccountActions.remove(account))},
 | 
				
			||||||
@@ -34,6 +37,11 @@ function mapDispatchToProps(dispatch) {
 | 
				
			|||||||
		onSelectTransaction: function(transactionId) {dispatch(TransactionActions.select(transactionId))},
 | 
							onSelectTransaction: function(transactionId) {dispatch(TransactionActions.select(transactionId))},
 | 
				
			||||||
		onUnselectTransaction: function() {dispatch(TransactionActions.unselect())},
 | 
							onUnselectTransaction: function() {dispatch(TransactionActions.unselect())},
 | 
				
			||||||
		onFetchTransactionPage: function(account, pageSize, page) {dispatch(TransactionActions.fetchPage(account, pageSize, page))},
 | 
							onFetchTransactionPage: function(account, pageSize, page) {dispatch(TransactionActions.fetchPage(account, pageSize, page))},
 | 
				
			||||||
 | 
							onOpenImportModal: function() {dispatch(ImportActions.openModal())},
 | 
				
			||||||
 | 
							onCloseImportModal: function() {dispatch(ImportActions.closeModal())},
 | 
				
			||||||
 | 
							onImportOFX: function(account, password, startDate, endDate) {dispatch(ImportActions.importOFX(account, password, startDate, endDate))},
 | 
				
			||||||
 | 
							onImportOFXFile: function(inputElement, account) {dispatch(ImportActions.importOFXFile(inputElement, account))},
 | 
				
			||||||
 | 
							onImportGnucash: function(inputElement) {dispatch(ImportActions.importGnucash(inputElement))},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										15
									
								
								js/models.js
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								js/models.js
									
									
									
									
									
								
							@@ -573,6 +573,20 @@ Report.prototype.mapReduceSeries = function(mapFn, reduceFn) {
 | 
				
			|||||||
	return this.mapReduceChildren(mapFn, reduceFn);
 | 
						return this.mapReduceChildren(mapFn, reduceFn);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function OFXDownload() {
 | 
				
			||||||
 | 
						this.OFXPassword = "";
 | 
				
			||||||
 | 
						this.StartDate = new Date();
 | 
				
			||||||
 | 
						this.EndDate = new Date();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OFXDownload.prototype.toJSON = function() {
 | 
				
			||||||
 | 
						var json_obj = {};
 | 
				
			||||||
 | 
						json_obj.OFXPassword = this.OFXPassword;
 | 
				
			||||||
 | 
						json_obj.StartDate = this.StartDate.toJSON();
 | 
				
			||||||
 | 
						json_obj.EndDate = this.EndDate.toJSON();
 | 
				
			||||||
 | 
						return JSON.stringify(json_obj);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = models = {
 | 
					module.exports = models = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Classes
 | 
						// Classes
 | 
				
			||||||
@@ -583,6 +597,7 @@ module.exports = models = {
 | 
				
			|||||||
	Split: Split,
 | 
						Split: Split,
 | 
				
			||||||
	Transaction: Transaction,
 | 
						Transaction: Transaction,
 | 
				
			||||||
	Report: Report,
 | 
						Report: Report,
 | 
				
			||||||
 | 
						OFXDownload: OFXDownload,
 | 
				
			||||||
	Error: Error,
 | 
						Error: Error,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Enums, Lists
 | 
						// Enums, Lists
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										47
									
								
								js/reducers/ImportReducer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								js/reducers/ImportReducer.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					var assign = require('object-assign');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var ImportConstants = require('../constants/ImportConstants');
 | 
				
			||||||
 | 
					var UserConstants = require('../constants/UserConstants');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const initialState = {
 | 
				
			||||||
 | 
						showModal: false,
 | 
				
			||||||
 | 
						importing: false,
 | 
				
			||||||
 | 
						uploadProgress: 0,
 | 
				
			||||||
 | 
						importFinished: false,
 | 
				
			||||||
 | 
						importFailed: false,
 | 
				
			||||||
 | 
						errorMessage: null
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = function(state = initialState, action) {
 | 
				
			||||||
 | 
						switch (action.type) {
 | 
				
			||||||
 | 
							case ImportConstants.OPEN_IMPORT_MODAL:
 | 
				
			||||||
 | 
								return assign({}, initialState, {
 | 
				
			||||||
 | 
									showModal: true
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							case ImportConstants.CLOSE_IMPORT_MODAL:
 | 
				
			||||||
 | 
							case UserConstants.USER_LOGGEDOUT:
 | 
				
			||||||
 | 
								return initialState;
 | 
				
			||||||
 | 
							case ImportConstants.BEGIN_IMPORT:
 | 
				
			||||||
 | 
								return assign({}, state, {
 | 
				
			||||||
 | 
									importing: true
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							case ImportConstants.UPDATE_IMPORT_PROGRESS:
 | 
				
			||||||
 | 
								return assign({}, state, {
 | 
				
			||||||
 | 
									uploadProgress: action.progress
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							case ImportConstants.IMPORT_FINISHED:
 | 
				
			||||||
 | 
								return assign({}, state, {
 | 
				
			||||||
 | 
									importing: false,
 | 
				
			||||||
 | 
									uploadProgress: 100,
 | 
				
			||||||
 | 
									importFinished: true
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							case ImportConstants.IMPORT_FAILED:
 | 
				
			||||||
 | 
								return assign({}, state, {
 | 
				
			||||||
 | 
									importing: false,
 | 
				
			||||||
 | 
									importFailed: true,
 | 
				
			||||||
 | 
									errorMessage: action.error
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return state;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -11,6 +11,7 @@ var ReportReducer = require('./ReportReducer');
 | 
				
			|||||||
var SelectedReportReducer = require('./SelectedReportReducer');
 | 
					var SelectedReportReducer = require('./SelectedReportReducer');
 | 
				
			||||||
var TransactionReducer = require('./TransactionReducer');
 | 
					var TransactionReducer = require('./TransactionReducer');
 | 
				
			||||||
var TransactionPageReducer = require('./TransactionPageReducer');
 | 
					var TransactionPageReducer = require('./TransactionPageReducer');
 | 
				
			||||||
 | 
					var ImportReducer = require('./ImportReducer');
 | 
				
			||||||
var ErrorReducer = require('./ErrorReducer');
 | 
					var ErrorReducer = require('./ErrorReducer');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = Redux.combineReducers({
 | 
					module.exports = Redux.combineReducers({
 | 
				
			||||||
@@ -25,5 +26,6 @@ module.exports = Redux.combineReducers({
 | 
				
			|||||||
	selectedReport: SelectedReportReducer,
 | 
						selectedReport: SelectedReportReducer,
 | 
				
			||||||
	transactions: TransactionReducer,
 | 
						transactions: TransactionReducer,
 | 
				
			||||||
	transactionPage: TransactionPageReducer,
 | 
						transactionPage: TransactionPageReducer,
 | 
				
			||||||
 | 
						imports: ImportReducer,
 | 
				
			||||||
	error: ErrorReducer
 | 
						error: ErrorReducer
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								ofx.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								ofx.go
									
									
									
									
									
								
							@@ -113,9 +113,11 @@ func (i *OFXImport) importOFXBank(stmt *ofxgo.StatementResponse) error {
 | 
				
			|||||||
		Type:              Bank,
 | 
							Type:              Bank,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, tran := range stmt.BankTranList.Transactions {
 | 
						if stmt.BankTranList != nil {
 | 
				
			||||||
		if err := i.AddTransaction(&tran, &account); err != nil {
 | 
							for _, tran := range stmt.BankTranList.Transactions {
 | 
				
			||||||
			return err
 | 
								if err := i.AddTransaction(&tran, &account); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -139,9 +141,11 @@ func (i *OFXImport) importOFXCC(stmt *ofxgo.CCStatementResponse) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	i.Accounts = append(i.Accounts, account)
 | 
						i.Accounts = append(i.Accounts, account)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, tran := range stmt.BankTranList.Transactions {
 | 
						if stmt.BankTranList != nil {
 | 
				
			||||||
		if err := i.AddTransaction(&tran, &account); err != nil {
 | 
							for _, tran := range stmt.BankTranList.Transactions {
 | 
				
			||||||
			return err
 | 
								if err := i.AddTransaction(&tran, &account); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -96,6 +96,16 @@ func GetSecurity(securityid int64, userid int64) (*Security, error) {
 | 
				
			|||||||
	return &s, nil
 | 
						return &s, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetSecurityTx(transaction *gorp.Transaction, securityid int64, userid int64) (*Security, error) {
 | 
				
			||||||
 | 
						var s Security
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := transaction.SelectOne(&s, "SELECT * from securities where UserId=? AND SecurityId=?", userid, securityid)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &s, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetSecurities(userid int64) (*[]*Security, error) {
 | 
					func GetSecurities(userid int64) (*[]*Security, error) {
 | 
				
			||||||
	var securities []*Security
 | 
						var securities []*Security
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -180,8 +190,8 @@ func DeleteSecurity(s *Security) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ImportGetCreateSecurity(transaction *gorp.Transaction, user *User, security *Security) (*Security, error) {
 | 
					func ImportGetCreateSecurity(transaction *gorp.Transaction, userid int64, security *Security) (*Security, error) {
 | 
				
			||||||
	security.UserId = user.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
 | 
				
			||||||
		err := InsertSecurityTx(transaction, security)
 | 
							err := InsertSecurityTx(transaction, security)
 | 
				
			||||||
@@ -193,7 +203,7 @@ func ImportGetCreateSecurity(transaction *gorp.Transaction, user *User, security
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	var securities []*Security
 | 
						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)
 | 
						_, err := transaction.Select(&securities, "SELECT * from securities where UserId=? AND Type=? AND AlternateId=? AND Precision=?", userid, security.Type, security.AlternateId, security.Precision)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user