mirror of
https://github.com/aclindsa/moneygo.git
synced 2024-12-26 15:42:27 -05:00
OFX: Import the most common investment transactions
This is very preliminary support, and is likely to still fail for many cases - even those with nominal support. Most transaction splits end up being filed under 'imbalanced' accounts instead of something that makes more sense.
This commit is contained in:
parent
3727f5b767
commit
89d045a51f
669
ofx.go
669
ofx.go
@ -22,6 +22,16 @@ func (i *OFXImport) GetSecurity(ofxsecurityid int64) (*Security, error) {
|
||||
return &i.Securities[ofxsecurityid], nil
|
||||
}
|
||||
|
||||
func (i *OFXImport) GetSecurityAlternateId(alternateid string, securityType int64) (*Security, error) {
|
||||
for _, security := range i.Securities {
|
||||
if alternateid == security.AlternateId && securityType == security.Type {
|
||||
return &security, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("OFXImport.FindSecurity: Unable to find security")
|
||||
}
|
||||
|
||||
func (i *OFXImport) GetAddCurrency(isoname string) (*Security, error) {
|
||||
for _, security := range i.Securities {
|
||||
if isoname == security.Name && Currency == security.Type {
|
||||
@ -83,6 +93,9 @@ func (i *OFXImport) AddTransaction(tran *ofxgo.Transaction, account *Account) er
|
||||
s1.RemoteId = "ofx:" + tran.FiTID.String()
|
||||
// TODO CorrectFiTID/CorrectAction?
|
||||
|
||||
s1.ImportSplitType = ImportAccount
|
||||
s2.ImportSplitType = ExternalAccount
|
||||
|
||||
security := i.Securities[account.SecurityId-1]
|
||||
s1.Amount = amt.FloatString(security.Precision)
|
||||
s2.Amount = amt.Neg(amt).FloatString(security.Precision)
|
||||
@ -152,12 +165,655 @@ func (i *OFXImport) importOFXCC(stmt *ofxgo.CCStatementResponse) error {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO balance(s)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *OFXImport) importSecurities(seclist *ofxgo.SecurityList) error {
|
||||
for _, security := range seclist.Securities {
|
||||
var si ofxgo.SecInfo
|
||||
if sec, ok := (security).(ofxgo.DebtInfo); ok {
|
||||
si = sec.SecInfo
|
||||
} else if sec, ok := (security).(ofxgo.MFInfo); ok {
|
||||
si = sec.SecInfo
|
||||
} else if sec, ok := (security).(ofxgo.OptInfo); ok {
|
||||
si = sec.SecInfo
|
||||
} else if sec, ok := (security).(ofxgo.OtherInfo); ok {
|
||||
si = sec.SecInfo
|
||||
} else if sec, ok := (security).(ofxgo.StockInfo); ok {
|
||||
si = sec.SecInfo
|
||||
} else {
|
||||
return errors.New("Can't import unrecognized type satisfying ofxgo.Security interface")
|
||||
}
|
||||
s := Security{
|
||||
SecurityId: int64(len(i.Securities) + 1),
|
||||
Name: string(si.SecName),
|
||||
Description: string(si.Memo),
|
||||
Symbol: string(si.Ticker),
|
||||
Precision: 5, // TODO How to actually determine this?
|
||||
Type: Stock,
|
||||
AlternateId: string(si.SecID.UniqueID),
|
||||
}
|
||||
if len(s.Description) == 0 {
|
||||
s.Description = s.Name
|
||||
}
|
||||
if len(s.Symbol) == 0 {
|
||||
s.Symbol = s.Name
|
||||
}
|
||||
|
||||
i.Securities = append(i.Securities, s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *OFXImport) GetInvTran(invtran *ofxgo.InvTran) Transaction {
|
||||
var t Transaction
|
||||
t.Description = string(invtran.Memo)
|
||||
t.Date = invtran.DtTrade.UTC()
|
||||
return t
|
||||
}
|
||||
|
||||
func (i *OFXImport) GetInvBuyTran(buy *ofxgo.InvBuy, curdef *Security, account *Account) (*Transaction, error) {
|
||||
t := i.GetInvTran(&buy.InvTran)
|
||||
|
||||
security, err := i.GetSecurityAlternateId(string(buy.SecID.UniqueID), Stock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memo := string(buy.InvTran.Memo)
|
||||
if len(memo) > 0 {
|
||||
memo += " "
|
||||
}
|
||||
|
||||
var commission, taxes, fees, load, total, tradingTotal big.Rat
|
||||
commission.Set(&buy.Commission.Rat)
|
||||
taxes.Set(&buy.Taxes.Rat)
|
||||
fees.Set(&buy.Fees.Rat)
|
||||
load.Set(&buy.Load.Rat)
|
||||
total.Set(&buy.Total.Rat)
|
||||
|
||||
tradingTotal.Neg(&total)
|
||||
tradingTotal.Sub(&tradingTotal, &commission)
|
||||
tradingTotal.Sub(&tradingTotal, &taxes)
|
||||
tradingTotal.Sub(&tradingTotal, &fees)
|
||||
tradingTotal.Sub(&tradingTotal, &load)
|
||||
|
||||
// Convert amounts to account's currency if Currency is set
|
||||
if ok, _ := buy.Currency.Valid(); ok {
|
||||
commission.Mul(&commission, &buy.Currency.CurRate.Rat)
|
||||
taxes.Mul(&taxes, &buy.Currency.CurRate.Rat)
|
||||
fees.Mul(&fees, &buy.Currency.CurRate.Rat)
|
||||
load.Mul(&load, &buy.Currency.CurRate.Rat)
|
||||
total.Mul(&total, &buy.Currency.CurRate.Rat)
|
||||
tradingTotal.Mul(&tradingTotal, &buy.Currency.CurRate.Rat)
|
||||
}
|
||||
|
||||
if num := commission.Num(); !num.IsInt64() || num.Int64() != 0 {
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: Commission,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + buy.InvTran.FiTID.String(),
|
||||
Memo: memo + "(commission)",
|
||||
Amount: commission.FloatString(curdef.Precision),
|
||||
})
|
||||
}
|
||||
if num := taxes.Num(); !num.IsInt64() || num.Int64() != 0 {
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: Taxes,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + buy.InvTran.FiTID.String(),
|
||||
Memo: memo + "(taxes)",
|
||||
Amount: taxes.FloatString(curdef.Precision),
|
||||
})
|
||||
}
|
||||
if num := fees.Num(); !num.IsInt64() || num.Int64() != 0 {
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: Fees,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + buy.InvTran.FiTID.String(),
|
||||
Memo: memo + "(fees)",
|
||||
Amount: fees.FloatString(curdef.Precision),
|
||||
})
|
||||
}
|
||||
if num := load.Num(); !num.IsInt64() || num.Int64() != 0 {
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: Load,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + buy.InvTran.FiTID.String(),
|
||||
Memo: memo + "(load)",
|
||||
Amount: load.FloatString(curdef.Precision),
|
||||
})
|
||||
}
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: ImportAccount,
|
||||
AccountId: account.AccountId,
|
||||
SecurityId: -1,
|
||||
RemoteId: "ofx:" + buy.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: total.FloatString(curdef.Precision),
|
||||
})
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: TradingAccount,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + buy.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: tradingTotal.FloatString(curdef.Precision),
|
||||
})
|
||||
|
||||
units := big.NewRat(0, 1)
|
||||
units.Set(&buy.Units.Rat)
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: SubAccount,
|
||||
AccountId: -1,
|
||||
SecurityId: security.SecurityId,
|
||||
RemoteId: "ofx:" + buy.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: units.FloatString(security.Precision),
|
||||
})
|
||||
units.Neg(units)
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: TradingAccount,
|
||||
AccountId: -1,
|
||||
SecurityId: security.SecurityId,
|
||||
RemoteId: "ofx:" + buy.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: units.FloatString(security.Precision),
|
||||
})
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (i *OFXImport) GetIncomeTran(income *ofxgo.Income, curdef *Security, account *Account) (*Transaction, error) {
|
||||
t := i.GetInvTran(&income.InvTran)
|
||||
|
||||
security, err := i.GetSecurityAlternateId(string(income.SecID.UniqueID), Stock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memo := string(income.InvTran.Memo)
|
||||
if len(memo) > 0 {
|
||||
memo += " "
|
||||
} else {
|
||||
memo = income.IncomeType.String() + " on " + security.Symbol
|
||||
}
|
||||
|
||||
var total big.Rat
|
||||
total.Set(&income.Total.Rat)
|
||||
if ok, _ := income.Currency.Valid(); ok {
|
||||
total.Mul(&total, &income.Currency.CurRate.Rat)
|
||||
}
|
||||
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: ImportAccount,
|
||||
AccountId: account.AccountId,
|
||||
SecurityId: -1,
|
||||
RemoteId: "ofx:" + income.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: total.FloatString(curdef.Precision),
|
||||
})
|
||||
total.Neg(&total)
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: IncomeAccount,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + income.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: total.FloatString(curdef.Precision),
|
||||
})
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (i *OFXImport) GetReinvestTran(reinvest *ofxgo.Reinvest, curdef *Security, account *Account) (*Transaction, error) {
|
||||
t := i.GetInvTran(&reinvest.InvTran)
|
||||
|
||||
security, err := i.GetSecurityAlternateId(string(reinvest.SecID.UniqueID), Stock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memo := string(reinvest.InvTran.Memo)
|
||||
if len(memo) > 0 {
|
||||
memo += " "
|
||||
}
|
||||
|
||||
var commission, taxes, fees, load, total, tradingTotal big.Rat
|
||||
commission.Set(&reinvest.Commission.Rat)
|
||||
taxes.Set(&reinvest.Taxes.Rat)
|
||||
fees.Set(&reinvest.Fees.Rat)
|
||||
load.Set(&reinvest.Load.Rat)
|
||||
total.Set(&reinvest.Total.Rat)
|
||||
|
||||
tradingTotal.Neg(&total)
|
||||
tradingTotal.Sub(&tradingTotal, &commission)
|
||||
tradingTotal.Sub(&tradingTotal, &taxes)
|
||||
tradingTotal.Sub(&tradingTotal, &fees)
|
||||
tradingTotal.Sub(&tradingTotal, &load)
|
||||
|
||||
// Convert amounts to account's currency if Currency is set
|
||||
if ok, _ := reinvest.Currency.Valid(); ok {
|
||||
commission.Mul(&commission, &reinvest.Currency.CurRate.Rat)
|
||||
taxes.Mul(&taxes, &reinvest.Currency.CurRate.Rat)
|
||||
fees.Mul(&fees, &reinvest.Currency.CurRate.Rat)
|
||||
load.Mul(&load, &reinvest.Currency.CurRate.Rat)
|
||||
total.Mul(&total, &reinvest.Currency.CurRate.Rat)
|
||||
tradingTotal.Mul(&tradingTotal, &reinvest.Currency.CurRate.Rat)
|
||||
}
|
||||
|
||||
if num := commission.Num(); !num.IsInt64() || num.Int64() != 0 {
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: Commission,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + reinvest.InvTran.FiTID.String(),
|
||||
Memo: memo + "(commission)",
|
||||
Amount: commission.FloatString(curdef.Precision),
|
||||
})
|
||||
}
|
||||
if num := taxes.Num(); !num.IsInt64() || num.Int64() != 0 {
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: Taxes,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + reinvest.InvTran.FiTID.String(),
|
||||
Memo: memo + "(taxes)",
|
||||
Amount: taxes.FloatString(curdef.Precision),
|
||||
})
|
||||
}
|
||||
if num := fees.Num(); !num.IsInt64() || num.Int64() != 0 {
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: Fees,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + reinvest.InvTran.FiTID.String(),
|
||||
Memo: memo + "(fees)",
|
||||
Amount: fees.FloatString(curdef.Precision),
|
||||
})
|
||||
}
|
||||
if num := load.Num(); !num.IsInt64() || num.Int64() != 0 {
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: Load,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + reinvest.InvTran.FiTID.String(),
|
||||
Memo: memo + "(load)",
|
||||
Amount: load.FloatString(curdef.Precision),
|
||||
})
|
||||
}
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: ImportAccount,
|
||||
AccountId: account.AccountId,
|
||||
SecurityId: -1,
|
||||
RemoteId: "ofx:" + reinvest.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: total.FloatString(curdef.Precision),
|
||||
})
|
||||
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: IncomeAccount,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + reinvest.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: total.FloatString(curdef.Precision),
|
||||
})
|
||||
total.Neg(&total)
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: ImportAccount,
|
||||
AccountId: account.AccountId,
|
||||
SecurityId: -1,
|
||||
RemoteId: "ofx:" + reinvest.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: total.FloatString(curdef.Precision),
|
||||
})
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: TradingAccount,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + reinvest.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: tradingTotal.FloatString(curdef.Precision),
|
||||
})
|
||||
|
||||
var units big.Rat
|
||||
units.Set(&reinvest.Units.Rat)
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: SubAccount,
|
||||
AccountId: -1,
|
||||
SecurityId: security.SecurityId,
|
||||
RemoteId: "ofx:" + reinvest.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: units.FloatString(security.Precision),
|
||||
})
|
||||
units.Neg(&units)
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: TradingAccount,
|
||||
AccountId: -1,
|
||||
SecurityId: security.SecurityId,
|
||||
RemoteId: "ofx:" + reinvest.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: units.FloatString(security.Precision),
|
||||
})
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (i *OFXImport) GetInvSellTran(sell *ofxgo.InvSell, curdef *Security, account *Account) (*Transaction, error) {
|
||||
t := i.GetInvTran(&sell.InvTran)
|
||||
|
||||
security, err := i.GetSecurityAlternateId(string(sell.SecID.UniqueID), Stock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memo := string(sell.InvTran.Memo)
|
||||
if len(memo) > 0 {
|
||||
memo += " "
|
||||
}
|
||||
|
||||
var commission, taxes, fees, load, total, tradingTotal big.Rat
|
||||
commission.Set(&sell.Commission.Rat)
|
||||
taxes.Set(&sell.Taxes.Rat)
|
||||
fees.Set(&sell.Fees.Rat)
|
||||
load.Set(&sell.Load.Rat)
|
||||
total.Set(&sell.Total.Rat)
|
||||
|
||||
tradingTotal.Neg(&total)
|
||||
tradingTotal.Sub(&tradingTotal, &commission)
|
||||
tradingTotal.Sub(&tradingTotal, &taxes)
|
||||
tradingTotal.Sub(&tradingTotal, &fees)
|
||||
tradingTotal.Sub(&tradingTotal, &load)
|
||||
|
||||
// Convert amounts to account's currency if Currency is set
|
||||
if ok, _ := sell.Currency.Valid(); ok {
|
||||
commission.Mul(&commission, &sell.Currency.CurRate.Rat)
|
||||
taxes.Mul(&taxes, &sell.Currency.CurRate.Rat)
|
||||
fees.Mul(&fees, &sell.Currency.CurRate.Rat)
|
||||
load.Mul(&load, &sell.Currency.CurRate.Rat)
|
||||
total.Mul(&total, &sell.Currency.CurRate.Rat)
|
||||
tradingTotal.Mul(&tradingTotal, &sell.Currency.CurRate.Rat)
|
||||
}
|
||||
|
||||
if num := commission.Num(); !num.IsInt64() || num.Int64() != 0 {
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: Commission,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + sell.InvTran.FiTID.String(),
|
||||
Memo: memo + "(commission)",
|
||||
Amount: commission.FloatString(curdef.Precision),
|
||||
})
|
||||
}
|
||||
if num := taxes.Num(); !num.IsInt64() || num.Int64() != 0 {
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: Taxes,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + sell.InvTran.FiTID.String(),
|
||||
Memo: memo + "(taxes)",
|
||||
Amount: taxes.FloatString(curdef.Precision),
|
||||
})
|
||||
}
|
||||
if num := fees.Num(); !num.IsInt64() || num.Int64() != 0 {
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: Fees,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + sell.InvTran.FiTID.String(),
|
||||
Memo: memo + "(fees)",
|
||||
Amount: fees.FloatString(curdef.Precision),
|
||||
})
|
||||
}
|
||||
if num := load.Num(); !num.IsInt64() || num.Int64() != 0 {
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: Load,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + sell.InvTran.FiTID.String(),
|
||||
Memo: memo + "(load)",
|
||||
Amount: load.FloatString(curdef.Precision),
|
||||
})
|
||||
}
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: ImportAccount,
|
||||
AccountId: account.AccountId,
|
||||
SecurityId: -1,
|
||||
RemoteId: "ofx:" + sell.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: total.FloatString(curdef.Precision),
|
||||
})
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: TradingAccount,
|
||||
AccountId: -1,
|
||||
SecurityId: curdef.SecurityId,
|
||||
RemoteId: "ofx:" + sell.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: tradingTotal.FloatString(curdef.Precision),
|
||||
})
|
||||
|
||||
var units big.Rat
|
||||
units.Set(&sell.Units.Rat)
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: SubAccount,
|
||||
AccountId: -1,
|
||||
SecurityId: security.SecurityId,
|
||||
RemoteId: "ofx:" + sell.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: units.FloatString(security.Precision),
|
||||
})
|
||||
units.Neg(&units)
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: TradingAccount,
|
||||
AccountId: -1,
|
||||
SecurityId: security.SecurityId,
|
||||
RemoteId: "ofx:" + sell.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: units.FloatString(security.Precision),
|
||||
})
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (i *OFXImport) GetTransferTran(transfer *ofxgo.Transfer, account *Account) (*Transaction, error) {
|
||||
t := i.GetInvTran(&transfer.InvTran)
|
||||
|
||||
security, err := i.GetSecurityAlternateId(string(transfer.SecID.UniqueID), Stock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memo := string(transfer.InvTran.Memo)
|
||||
|
||||
var units big.Rat
|
||||
if transfer.TferAction == ofxgo.TferActionIn {
|
||||
units.Set(&transfer.Units.Rat)
|
||||
} else {
|
||||
units.Neg(&transfer.Units.Rat)
|
||||
}
|
||||
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: SubAccount,
|
||||
AccountId: -1,
|
||||
SecurityId: security.SecurityId,
|
||||
RemoteId: "ofx:" + transfer.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: units.FloatString(security.Precision),
|
||||
})
|
||||
units.Neg(&units)
|
||||
t.Splits = append(t.Splits, &Split{
|
||||
// TODO ReversalFiTID?
|
||||
Status: Imported,
|
||||
ImportSplitType: ExternalAccount,
|
||||
AccountId: -1,
|
||||
SecurityId: security.SecurityId,
|
||||
RemoteId: "ofx:" + transfer.InvTran.FiTID.String(),
|
||||
Memo: memo,
|
||||
Amount: units.FloatString(security.Precision),
|
||||
})
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (i *OFXImport) AddInvTransaction(invtran *ofxgo.InvTransaction, account *Account, curdef *Security) error {
|
||||
if curdef.SecurityId < 1 || curdef.SecurityId > int64(len(i.Securities)) {
|
||||
return errors.New("Internal error: security index not found in OFX import\n")
|
||||
}
|
||||
|
||||
var t *Transaction
|
||||
var err error
|
||||
if tran, ok := (*invtran).(ofxgo.BuyDebt); ok {
|
||||
t, err = i.GetInvBuyTran(&tran.InvBuy, curdef, account)
|
||||
} else if tran, ok := (*invtran).(ofxgo.BuyMF); ok {
|
||||
t, err = i.GetInvBuyTran(&tran.InvBuy, curdef, account)
|
||||
} else if tran, ok := (*invtran).(ofxgo.BuyOpt); ok {
|
||||
t, err = i.GetInvBuyTran(&tran.InvBuy, curdef, account)
|
||||
} else if tran, ok := (*invtran).(ofxgo.BuyOther); ok {
|
||||
t, err = i.GetInvBuyTran(&tran.InvBuy, curdef, account)
|
||||
} else if tran, ok := (*invtran).(ofxgo.BuyStock); ok {
|
||||
t, err = i.GetInvBuyTran(&tran.InvBuy, curdef, account)
|
||||
// TODO implementme
|
||||
// } else if tran, ok := (*invtran).(ofxgo.ClosureOpt); ok {
|
||||
} else if tran, ok := (*invtran).(ofxgo.Income); ok {
|
||||
t, err = i.GetIncomeTran(&tran, curdef, account)
|
||||
// } else if tran, ok := (*invtran).(ofxgo.InvExpense); ok {
|
||||
// } else if tran, ok := (*invtran).(ofxgo.JrnlFund); ok {
|
||||
// } else if tran, ok := (*invtran).(ofxgo.JrnlSec); ok {
|
||||
// } else if tran, ok := (*invtran).(ofxgo.MarginInterest); ok {
|
||||
} else if tran, ok := (*invtran).(ofxgo.Reinvest); ok {
|
||||
t, err = i.GetReinvestTran(&tran, curdef, account)
|
||||
// } else if tran, ok := (*invtran).(ofxgo.RetOfCap); ok {
|
||||
} else if tran, ok := (*invtran).(ofxgo.SellDebt); ok {
|
||||
t, err = i.GetInvSellTran(&tran.InvSell, curdef, account)
|
||||
} else if tran, ok := (*invtran).(ofxgo.SellMF); ok {
|
||||
t, err = i.GetInvSellTran(&tran.InvSell, curdef, account)
|
||||
} else if tran, ok := (*invtran).(ofxgo.SellOpt); ok {
|
||||
t, err = i.GetInvSellTran(&tran.InvSell, curdef, account)
|
||||
} else if tran, ok := (*invtran).(ofxgo.SellOther); ok {
|
||||
t, err = i.GetInvSellTran(&tran.InvSell, curdef, account)
|
||||
} else if tran, ok := (*invtran).(ofxgo.SellStock); ok {
|
||||
t, err = i.GetInvSellTran(&tran.InvSell, curdef, account)
|
||||
// TODO implementme
|
||||
// } else if tran, ok := (*invtran).(ofxgo.Split); ok {
|
||||
} else if tran, ok := (*invtran).(ofxgo.Transfer); ok {
|
||||
t, err = i.GetTransferTran(&tran, account)
|
||||
} else {
|
||||
return errors.New("Unrecognized type satisfying ofxgo.InvTransaction interface: " + (*invtran).TransactionType())
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.Transactions = append(i.Transactions, *t)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *OFXImport) importOFXInv(stmt *ofxgo.InvStatementResponse) error {
|
||||
// TODO
|
||||
return errors.New("unimplemented")
|
||||
security, err := i.GetAddCurrency(stmt.CurDef.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
account := Account{
|
||||
AccountId: int64(len(i.Accounts) + 1),
|
||||
ExternalAccountId: stmt.InvAcctFrom.AcctID.String(),
|
||||
SecurityId: security.SecurityId,
|
||||
ParentAccountId: -1,
|
||||
Type: Investment,
|
||||
}
|
||||
i.Accounts = append(i.Accounts, account)
|
||||
|
||||
if stmt.InvTranList != nil {
|
||||
for _, invtran := range stmt.InvTranList.InvTransactions {
|
||||
if err := i.AddInvTransaction(&invtran, &account, security); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, bt := range stmt.InvTranList.BankTransactions {
|
||||
// TODO Should we do something different for the value of
|
||||
// bt.SubAcctFund?
|
||||
for _, tran := range bt.Transactions {
|
||||
if err := i.AddTransaction(&tran, &account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO InvPosList
|
||||
// TODO InvBal
|
||||
// TODO Inv401K and INV401kBal???
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ImportOFX(r io.Reader) (*OFXImport, error) {
|
||||
@ -191,6 +847,15 @@ func ImportOFX(r io.Reader) (*OFXImport, error) {
|
||||
return &i, nil
|
||||
}
|
||||
}
|
||||
for _, seclist := range response.SecList {
|
||||
if securitylist, ok := seclist.(*ofxgo.SecurityList); ok {
|
||||
err = i.importSecurities(securitylist)
|
||||
// TODO actually import securities
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, inv := range response.InvStmt {
|
||||
if stmt, ok := inv.(*ofxgo.InvStatementResponse); ok {
|
||||
err = i.importOFXInv(stmt)
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Split.Status
|
||||
const (
|
||||
Imported int64 = 1
|
||||
Entered = 2
|
||||
@ -22,10 +23,25 @@ const (
|
||||
Voided = 5
|
||||
)
|
||||
|
||||
// Split.ImportSplitType
|
||||
const (
|
||||
Default int64 = 0
|
||||
ImportAccount = 1
|
||||
SubAccount = 2
|
||||
ExternalAccount = 3
|
||||
TradingAccount = 4
|
||||
Commission = 5
|
||||
Taxes = 6
|
||||
Fees = 7
|
||||
Load = 8
|
||||
IncomeAccount = 9
|
||||
)
|
||||
|
||||
type Split struct {
|
||||
SplitId int64
|
||||
TransactionId int64
|
||||
Status int64
|
||||
ImportSplitType int64
|
||||
|
||||
// One of AccountId and SecurityId must be -1
|
||||
// In normal splits, AccountId will be valid and SecurityId will be -1. The
|
||||
|
Loading…
Reference in New Issue
Block a user