func ImportGnucash(r io.Reader) (*GnucashImport, error) { var gncxml GnucashXMLImport var gncimport GnucashImport // Perform initial parsing of xml into structs decoder := xml.NewDecoder(r) err := decoder.Decode(&gncxml) if err != nil { return nil, err } // Fixup securities, making a map of them as we go securityMap := make(map[string]Security) for i := range gncxml.Commodities { s := gncxml.Commodities[i].Security s.SecurityId = int64(i + 1) securityMap[s.Name] = s // Ignore gnucash's "template" commodity if s.Name != "template" || s.Description != "template" || s.AlternateId != "template" { gncimport.Securities = append(gncimport.Securities, s) } } //find root account, while simultaneously creating map of GUID's to //accounts var rootAccount GnucashAccount accountMap := make(map[string]GnucashAccount) for i := range gncxml.Accounts { gncxml.Accounts[i].accountid = int64(i + 1) if gncxml.Accounts[i].Type == "ROOT" { rootAccount = gncxml.Accounts[i] } else { accountMap[gncxml.Accounts[i].AccountId] = gncxml.Accounts[i] } } //Translate to our account format, figuring out parent relationships for guid := range accountMap { ga := accountMap[guid] var a Account a.AccountId = ga.accountid if ga.ParentAccountId == rootAccount.AccountId { a.ParentAccountId = -1 } else { parent, ok := accountMap[ga.ParentAccountId] if ok { a.ParentAccountId = parent.accountid } else { a.ParentAccountId = -1 // Ugly, but assign to top-level if we can't find its parent } } a.Name = ga.Name security, ok := securityMap[ga.Commodity.Name] if ok { } else { return nil, fmt.Errorf("Unable to find security: %s", ga.Commodity.Name) } a.SecurityId = security.SecurityId //TODO find account types switch ga.Type { default: a.Type = Bank case "ASSET": a.Type = Asset case "BANK": a.Type = Bank case "CASH": a.Type = Cash case "CREDIT", "LIABILITY": a.Type = Liability case "EQUITY": a.Type = Equity case "EXPENSE": a.Type = Expense case "INCOME": a.Type = Income case "PAYABLE": a.Type = Payable case "RECEIVABLE": a.Type = Receivable case "MUTUAL", "STOCK": a.Type = Investment case "TRADING": a.Type = Trading } gncimport.Accounts = append(gncimport.Accounts, a) } //Translate transactions to our format for i := range gncxml.Transactions { gt := gncxml.Transactions[i] t := new(Transaction) t.Description = gt.Description t.Date = gt.DatePosted.Date.Time t.Status = Imported for j := range gt.Splits { gs := gt.Splits[j] s := new(Split) s.Memo = gs.Memo account, ok := accountMap[gs.AccountId] if !ok { return nil, fmt.Errorf("Unable to find account: %s", gs.AccountId) } s.AccountId = account.accountid security, ok := securityMap[account.Commodity.Name] if !ok { return nil, fmt.Errorf("Unable to find security: %s", account.Commodity.Name) } s.SecurityId = -1 var r big.Rat _, ok = r.SetString(gs.Amount) if ok { s.Amount = r.FloatString(security.Precision) } else { return nil, fmt.Errorf("Can't set split Amount: %s", gs.Amount) } t.Splits = append(t.Splits, s) } gncimport.Transactions = append(gncimport.Transactions, *t) } return &gncimport, nil }