diff --git a/bank.go b/bank.go index de27ced..32836a1 100644 --- a/bank.go +++ b/bank.go @@ -117,15 +117,17 @@ type Transaction struct { SIC Int `xml:"SIC,omitempty"` // Standard Industrial Code PayeeID String `xml:"PAYEEID,omitempty"` // Note: Servers should provide NAME or PAYEE, but not both - Name String `xml:"NAME,omitempty"` - Payee *Payee `xml:"PAYEE,omitempty"` - ExtdName String `xml:"EXTDNAME,omitempty"` // Extended name of payee or transaction description - BankAcctTo *BankAcct `xml:"BANKACCTTO,omitempty"` // If the transfer was to a bank account we have the account information for - CCAcctTo *CCAcct `xml:"CCACCTTO,omitempty"` // If the transfer was to a credit card account we have the account information for - Memo String `xml:"MEMO,omitempty"` // Extra information (not in NAME) - ImageData []ImageData `xml:"IMAGEDATA,omitempty"` - Currency Currency `xml:"CURRENCY,omitempty"` // If different from CURDEF in STMTTRS - OrigCurrency Currency `xml:"ORIGCURRENCY,omitempty"` // If different from CURDEF in STMTTRS + Name String `xml:"NAME,omitempty"` + Payee *Payee `xml:"PAYEE,omitempty"` + ExtdName String `xml:"EXTDNAME,omitempty"` // Extended name of payee or transaction description + BankAcctTo *BankAcct `xml:"BANKACCTTO,omitempty"` // If the transfer was to a bank account we have the account information for + CCAcctTo *CCAcct `xml:"CCACCTTO,omitempty"` // If the transfer was to a credit card account we have the account information for + Memo String `xml:"MEMO,omitempty"` // Extra information (not in NAME) + ImageData []ImageData `xml:"IMAGEDATA,omitempty"` + + // Only one of Currency and OrigCurrency can ever be Valid() for the same transaction + Currency Currency `xml:"CURRENCY,omitempty"` // Represents the currency of TrnAmt (instead of CURDEF in STMTRS) if Valid + OrigCurrency Currency `xml:"ORIGCURRENCY,omitempty"` // Represents the currency TrnAmt was converted to STMTRS' CURDEF from if Valid Inv401kSource inv401kSource `xml:"INV401KSOURCE,omitempty"` // One of PRETAX, AFTERTAX, MATCH, PROFITSHARING, ROLLOVER, OTHERVEST, OTHERNONVEST (Default if not present is OTHERNONVEST. The following cash source types are subject to vesting: MATCH, PROFITSHARING, and OTHERVEST.) } @@ -202,18 +204,20 @@ func (l TransactionList) Valid(version ofxVersion) (bool, error) { // Transaction, but is not finalized (and may never be). For instance, it lacks // FiTID and DtPosted fields. type PendingTransaction struct { - XMLName xml.Name `xml:"STMTTRNP"` - TrnType trnType `xml:"TRNTYPE"` // One of CREDIT, DEBIT, INT (interest earned or paid. Note: Depends on signage of amount), DIV, FEE, SRVCHG (service charge), DEP (deposit), ATM (Note: Depends on signage of amount), POS (Note: Depends on signage of amount), XFER, CHECK, PAYMENT, CASH, DIRECTDEP, DIRECTDEBIT, REPEATPMT, HOLD, OTHER - DtTran Date `xml:"DTTRAN"` - DtExpire *Date `xml:"DTEXPIRE,omitempty"` // only valid for TrnType==HOLD, the date the hold will expire - TrnAmt Amount `xml:"TRNAMT"` - RefNum String `xml:"REFNUM,omitempty"` - Name String `xml:"NAME,omitempty"` - ExtdName String `xml:"EXTDNAME,omitempty"` // Extended name of payee or transaction description - Memo String `xml:"MEMO,omitempty"` // Extra information (not in NAME) - ImageData []ImageData `xml:"IMAGEDATA,omitempty"` - Currency Currency `xml:"CURRENCY,omitempty"` // If different from CURDEF in STMTTRS - OrigCurrency Currency `xml:"ORIGCURRENCY,omitempty"` // If different from CURDEF in STMTTRS + XMLName xml.Name `xml:"STMTTRNP"` + TrnType trnType `xml:"TRNTYPE"` // One of CREDIT, DEBIT, INT (interest earned or paid. Note: Depends on signage of amount), DIV, FEE, SRVCHG (service charge), DEP (deposit), ATM (Note: Depends on signage of amount), POS (Note: Depends on signage of amount), XFER, CHECK, PAYMENT, CASH, DIRECTDEP, DIRECTDEBIT, REPEATPMT, HOLD, OTHER + DtTran Date `xml:"DTTRAN"` + DtExpire *Date `xml:"DTEXPIRE,omitempty"` // only valid for TrnType==HOLD, the date the hold will expire + TrnAmt Amount `xml:"TRNAMT"` + RefNum String `xml:"REFNUM,omitempty"` + Name String `xml:"NAME,omitempty"` + ExtdName String `xml:"EXTDNAME,omitempty"` // Extended name of payee or transaction description + Memo String `xml:"MEMO,omitempty"` // Extra information (not in NAME) + ImageData []ImageData `xml:"IMAGEDATA,omitempty"` + + // Only one of Currency and OrigCurrency can ever be Valid() for the same transaction + Currency Currency `xml:"CURRENCY,omitempty"` // Represents the currency of TrnAmt (instead of CURDEF in STMTRS) if Valid + OrigCurrency Currency `xml:"ORIGCURRENCY,omitempty"` // Represents the currency TrnAmt was converted to STMTRS' CURDEF from if Valid } // Valid returns (true, nil) if this struct is valid OFX diff --git a/cmd/ofx/banktransactions.go b/cmd/ofx/banktransactions.go index 3c3eea1..5bb7e6e 100644 --- a/cmd/ofx/banktransactions.go +++ b/cmd/ofx/banktransactions.go @@ -79,8 +79,6 @@ func printTransaction(defCurrency ofxgo.CurrSymbol, tran *ofxgo.Transaction) { currency := defCurrency if ok, _ := tran.Currency.Valid(); ok { currency = tran.Currency.CurSym - } else if ok, _ := tran.OrigCurrency.Valid(); ok { - currency = tran.OrigCurrency.CurSym } var name string diff --git a/cmd/ofx/cctransactions.go b/cmd/ofx/cctransactions.go index 5513c2b..3756bfe 100644 --- a/cmd/ofx/cctransactions.go +++ b/cmd/ofx/cctransactions.go @@ -62,8 +62,6 @@ func ccTransactions() { currency := stmt.CurDef if ok, _ := tran.Currency.Valid(); ok { currency = tran.Currency.CurSym - } else if ok, _ := tran.OrigCurrency.Valid(); ok { - currency = tran.OrigCurrency.CurSym } var name string diff --git a/cmd/ofx/invtransactions.go b/cmd/ofx/invtransactions.go index b0555b4..05b91a7 100644 --- a/cmd/ofx/invtransactions.go +++ b/cmd/ofx/invtransactions.go @@ -92,9 +92,7 @@ func invTransactions() { case ofxgo.Income: printInvTran(&tran.InvTran) currency := stmt.CurDef - if tran.Currency != nil { - currency = tran.Currency.CurSym - } else if tran.OrigCurrency != nil { + if ok, _ := tran.Currency.Valid(); ok { currency = tran.Currency.CurSym } fmt.Printf(" %s %s %s (%s %s)\n", tran.IncomeType, tran.Total, currency, tran.SecID.UniqueIDType, tran.SecID.UniqueID) @@ -102,9 +100,7 @@ func invTransactions() { case ofxgo.InvExpense: printInvTran(&tran.InvTran) currency := stmt.CurDef - if tran.Currency != nil { - currency = tran.Currency.CurSym - } else if tran.OrigCurrency != nil { + if ok, _ := tran.Currency.Valid(); ok { currency = tran.Currency.CurSym } fmt.Printf(" %s %s (%s %s)\n", tran.Total, currency, tran.SecID.UniqueIDType, tran.SecID.UniqueID) @@ -119,18 +115,14 @@ func invTransactions() { case ofxgo.MarginInterest: printInvTran(&tran.InvTran) currency := stmt.CurDef - if tran.Currency != nil { - currency = tran.Currency.CurSym - } else if tran.OrigCurrency != nil { + if ok, _ := tran.Currency.Valid(); ok { currency = tran.Currency.CurSym } fmt.Printf(" %s %s\n", tran.Total, currency) case ofxgo.Reinvest: printInvTran(&tran.InvTran) currency := stmt.CurDef - if tran.Currency != nil { - currency = tran.Currency.CurSym - } else if tran.OrigCurrency != nil { + if ok, _ := tran.Currency.Valid(); ok { currency = tran.Currency.CurSym } fmt.Printf(" %s (%s %s)@%s %s (Total: %s)\n", tran.Units, tran.SecID.UniqueIDType, tran.SecID.UniqueID, tran.UnitPrice, currency, tran.Total) @@ -138,9 +130,7 @@ func invTransactions() { case ofxgo.RetOfCap: printInvTran(&tran.InvTran) currency := stmt.CurDef - if tran.Currency != nil { - currency = tran.Currency.CurSym - } else if tran.OrigCurrency != nil { + if ok, _ := tran.Currency.Valid(); ok { currency = tran.Currency.CurSym } fmt.Printf(" %s %s (%s %s)\n", tran.Total, currency, tran.SecID.UniqueIDType, tran.SecID.UniqueID) @@ -158,9 +148,7 @@ func invTransactions() { case ofxgo.Split: printInvTran(&tran.InvTran) currency := stmt.CurDef - if tran.Currency != nil { - currency = tran.Currency.CurSym - } else if tran.OrigCurrency != nil { + if ok, _ := tran.Currency.Valid(); ok { currency = tran.Currency.CurSym } fmt.Printf(" %d/%d %s -> %s shares of %s %s (%s %s for fractional shares)\n", tran.Numerator, tran.Denominator, tran.OldUnits, tran.NewUnits, tran.SecID.UniqueIDType, tran.SecID.UniqueID, tran.FracCash, currency) @@ -181,9 +169,7 @@ func printInvTran(it *ofxgo.InvTran) { func printInvBuy(defCurrency ofxgo.CurrSymbol, ib *ofxgo.InvBuy) { printInvTran(&ib.InvTran) currency := defCurrency - if ok, _ := ib.Currency.CurSym.Valid(); ok { - currency = ib.Currency.CurSym - } else if ok, _ := ib.OrigCurrency.CurSym.Valid(); ok { + if ok, _ := ib.Currency.Valid(); ok { currency = ib.Currency.CurSym } @@ -194,9 +180,7 @@ func printInvBuy(defCurrency ofxgo.CurrSymbol, ib *ofxgo.InvBuy) { func printInvSell(defCurrency ofxgo.CurrSymbol, is *ofxgo.InvSell) { printInvTran(&is.InvTran) currency := defCurrency - if ok, _ := is.Currency.CurSym.Valid(); ok { - currency = is.Currency.CurSym - } else if ok, _ := is.OrigCurrency.CurSym.Valid(); ok { + if ok, _ := is.Currency.Valid(); ok { currency = is.Currency.CurSym } diff --git a/common.go b/common.go index aa763cf..f98d64b 100644 --- a/common.go +++ b/common.go @@ -310,10 +310,14 @@ type InvAcct struct { AcctID String `xml:"ACCTID"` } -// Currency represents one ISO-4217 currency +// Currency represents one ISO-4217 currency. CURRENCY elements signify that +// the transaction containing this Currency struct is in this currency instead +// of being converted to the statement's default. ORIGCURRENCY elements signify +// that the transaction containing this Currency struct was converted to the +// statement's default from the specified currency. type Currency struct { XMLName xml.Name // CURRENCY or ORIGCURRENCY - CurRate Amount `xml:"CURRATE"` // Ratio of currency to currency + CurRate Amount `xml:"CURRATE"` // Ratio of statement's currency (CURDEF) to transaction currency (CURSYM) CurSym CurrSymbol `xml:"CURSYM"` // ISO-4217 3-character currency identifier } diff --git a/invstmt.go b/invstmt.go index 12bba13..9265947 100644 --- a/invstmt.go +++ b/invstmt.go @@ -75,8 +75,8 @@ type InvBuy struct { Fees Amount `xml:"FEES,omitempty"` Load Amount `xml:"LOAD,omitempty"` Total Amount `xml:"TOTAL"` // Transaction total. Buys, sells, etc.:((quan. * (price +/- markup/markdown)) +/-(commission + fees + load + taxes + penalty + withholding + statewithholding)). Distributions, interest, margin interest, misc. expense, etc.: amount. Return of cap: cost basis - Currency *Currency `xml:"CURRENCY,omitempty"` // Overriding currency for UNITPRICE - OrigCurrency *Currency `xml:"ORIGCURRENCY,omitempty"` // Overriding currency for UNITPRICE + Currency Currency `xml:"CURRENCY,omitempty"` // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid() + OrigCurrency Currency `xml:"ORIGCURRENCY,omitempty"` // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid SubAcctSec subAcctType `xml:"SUBACCTSEC"` // Sub-account type for this security. One of CASH, MARGIN, SHORT, OTHER SubAcctFund subAcctType `xml:"SUBACCTFUND"` // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER @@ -107,8 +107,8 @@ type InvSell struct { TaxExempt Boolean `xml:"TAXEXEMPT,omitempty"` // Tax-exempt transaction Total Amount `xml:"TOTAL"` // Transaction total. Buys, sells, etc.:((quan. * (price +/- markup/markdown)) +/-(commission + fees + load + taxes + penalty + withholding + statewithholding)). Distributions, interest, margin interest, misc. expense, etc.: amount. Return of cap: cost basis Gain Amount `xml:"GAIN,omitempty"` // Total gain - Currency *Currency `xml:"CURRENCY,omitempty"` // Overriding currency for UNITPRICE - OrigCurrency *Currency `xml:"ORIGCURRENCY,omitempty"` // Overriding currency for UNITPRICE + Currency Currency `xml:"CURRENCY,omitempty"` // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid() + OrigCurrency Currency `xml:"ORIGCURRENCY,omitempty"` // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid SubAcctSec subAcctType `xml:"SUBACCTSEC"` // Sub-account type for this security. One of CASH, MARGIN, SHORT, OTHER SubAcctFund subAcctType `xml:"SUBACCTFUND"` // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER @@ -211,8 +211,8 @@ type Income struct { SubAcctFund subAcctType `xml:"SUBACCTFUND"` // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER TaxExempt Boolean `xml:"TAXEXEMPT,omitempty"` // Tax-exempt transaction Witholding Amount `xml:"WITHHOLDING,omitempty"` // Federal tax witholdings - Currency *Currency `xml:"CURRENCY,omitempty"` // Overriding currency for UNITPRICE - OrigCurrency *Currency `xml:"ORIGCURRENCY,omitempty"` // Overriding currency for UNITPRICE + Currency Currency `xml:"CURRENCY,omitempty"` // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid() + OrigCurrency Currency `xml:"ORIGCURRENCY,omitempty"` // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid Inv401kSource inv401kSource `xml:"INV401KSOURCE,omitempty"` // Source of money for this transaction. One of PRETAX, AFTERTAX, MATCH, PROFITSHARING, ROLLOVER, OTHERVEST, OTHERNONVEST for 401(k) accounts. Default if not present is OTHERNONVEST. The following cash source types are subject to vesting: MATCH, PROFITSHARING, and OTHERVEST } @@ -230,8 +230,8 @@ type InvExpense struct { Total Amount `xml:"TOTAL"` SubAcctSec subAcctType `xml:"SUBACCTSEC"` // Sub-account type for this security. One of CASH, MARGIN, SHORT, OTHER SubAcctFund subAcctType `xml:"SUBACCTFUND"` // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER - Currency *Currency `xml:"CURRENCY,omitempty"` // Overriding currency for UNITPRICE - OrigCurrency *Currency `xml:"ORIGCURRENCY,omitempty"` // Overriding currency for UNITPRICE + Currency Currency `xml:"CURRENCY,omitempty"` // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid() + OrigCurrency Currency `xml:"ORIGCURRENCY,omitempty"` // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid Inv401kSource inv401kSource `xml:"INV401KSOURCE,omitempty"` // Source of money for this transaction. One of PRETAX, AFTERTAX, MATCH, PROFITSHARING, ROLLOVER, OTHERVEST, OTHERNONVEST for 401(k) accounts. Default if not present is OTHERNONVEST. The following cash source types are subject to vesting: MATCH, PROFITSHARING, and OTHERVEST } @@ -277,8 +277,8 @@ type MarginInterest struct { InvTran InvTran `xml:"INVTRAN"` Total Amount `xml:"TOTAL"` SubAcctFund subAcctType `xml:"SUBACCTFUND"` // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER - Currency *Currency `xml:"CURRENCY,omitempty"` // Overriding currency for UNITPRICE - OrigCurrency *Currency `xml:"ORIGCURRENCY,omitempty"` // Overriding currency for UNITPRICE + Currency Currency `xml:"CURRENCY,omitempty"` // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid() + OrigCurrency Currency `xml:"ORIGCURRENCY,omitempty"` // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid } // TransactionType returns a string representation of this transaction's type @@ -303,8 +303,8 @@ type Reinvest struct { Fees Amount `xml:"FEES,omitempty"` Load Amount `xml:"LOAD,omitempty"` TaxExempt Boolean `xml:"TAXEXEMPT,omitempty"` // Tax-exempt transaction - Currency *Currency `xml:"CURRENCY,omitempty"` // Overriding currency for UNITPRICE - OrigCurrency *Currency `xml:"ORIGCURRENCY,omitempty"` // Overriding currency for UNITPRICE + Currency Currency `xml:"CURRENCY,omitempty"` // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid() + OrigCurrency Currency `xml:"ORIGCURRENCY,omitempty"` // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid Inv401kSource inv401kSource `xml:"INV401KSOURCE,omitempty"` // Source of money for this transaction. One of PRETAX, AFTERTAX, MATCH, PROFITSHARING, ROLLOVER, OTHERVEST, OTHERNONVEST for 401(k) accounts. Default if not present is OTHERNONVEST. The following cash source types are subject to vesting: MATCH, PROFITSHARING, and OTHERVEST } @@ -322,8 +322,8 @@ type RetOfCap struct { Total Amount `xml:"TOTAL"` SubAcctSec subAcctType `xml:"SUBACCTSEC"` // Sub-account type for this security. One of CASH, MARGIN, SHORT, OTHER SubAcctFund subAcctType `xml:"SUBACCTFUND"` // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER - Currency *Currency `xml:"CURRENCY,omitempty"` // Overriding currency for UNITPRICE - OrigCurrency *Currency `xml:"ORIGCURRENCY,omitempty"` // Overriding currency for UNITPRICE + Currency Currency `xml:"CURRENCY,omitempty"` // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid() + OrigCurrency Currency `xml:"ORIGCURRENCY,omitempty"` // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid Inv401kSource inv401kSource `xml:"INV401KSOURCE,omitempty"` // Source of money for this transaction. One of PRETAX, AFTERTAX, MATCH, PROFITSHARING, ROLLOVER, OTHERVEST, OTHERNONVEST for 401(k) accounts. Default if not present is OTHERNONVEST. The following cash source types are subject to vesting: MATCH, PROFITSHARING, and OTHERVEST } @@ -412,8 +412,8 @@ type Split struct { NewUnits Amount `xml:"NEWUNITS"` // number of shares after the split Numerator Int `xml:"NUMERATOR"` // split ratio numerator Denominator Int `xml:"DENOMINATOR"` // split ratio denominator - Currency *Currency `xml:"CURRENCY,omitempty"` // Overriding currency for UNITPRICE - OrigCurrency *Currency `xml:"ORIGCURRENCY,omitempty"` // Overriding currency for UNITPRICE + Currency Currency `xml:"CURRENCY,omitempty"` // Represents the currency this transaction is in (instead of CURDEF in INVSTMTRS) if Valid() + OrigCurrency Currency `xml:"ORIGCURRENCY,omitempty"` // Represents the currency this transaction was converted to INVSTMTRS' CURDEF from if Valid FracCash Amount `xml:"FRACCASH,omitempty"` // cash for fractional units SubAcctFund subAcctType `xml:"SUBACCTFUND,omitempty"` // Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER Inv401kSource inv401kSource `xml:"INV401KSOURCE,omitempty"` // Source of money for this transaction. One of PRETAX, AFTERTAX, MATCH, PROFITSHARING, ROLLOVER, OTHERVEST, OTHERNONVEST for 401(k) accounts. Default if not present is OTHERNONVEST. The following cash source types are subject to vesting: MATCH, PROFITSHARING, and OTHERVEST