Example #1
0
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
}