class SaltParser::Ofx::Parser::Base
Constants
- ACCOUNT_TYPES
- TRANSACTION_TYPES
Attributes
accounts[R]
body[R]
errors[R]
headers[R]
html[R]
sign_on[R]
Public Class Methods
new(options = {})
click to toggle source
# File lib/ofx/parser/base.rb, line 21 def initialize(options = {}) @headers = options[:headers] @body = options[:body] @html = Nokogiri::HTML.parse(body) @errors = [] build_sign_on build_accounts check_for_errors end
Public Instance Methods
build_accounts()
click to toggle source
# File lib/ofx/parser/base.rb, line 39 def build_accounts @accounts = Ofx::Accounts.new build_bank_account build_credit_card_account build_investments_account end
to_hash()
click to toggle source
# File lib/ofx/parser/base.rb, line 31 def to_hash { :errors => errors, :sign_on => sign_on.to_hash, :accounts => accounts.to_hash } end
Private Instance Methods
build_amount(element)
click to toggle source
# File lib/ofx/parser/base.rb, line 280 def build_amount(element) parse_float(element.search("trnamt", "total").inner_text) rescue TypeError => error raise SaltParser::Ofx::ParseError.new(SaltParser::Ofx::ParseError::AMOUNT) end
build_available_balance(account)
click to toggle source
# File lib/ofx/parser/base.rb, line 209 def build_available_balance(account) return nil unless account.search("availbal").size > 0 if account.search("availbal > balamt").inner_text.match(/[\d]{14}\.[\d]+/) SaltParser::Ofx::Balance.new( :amount => 0.0, :amount_in_pennies => 0, :posted_at => build_date(account.search("availbal > balamt").inner_text) ) else amount = parse_float(account.search("availbal > balamt").inner_text) SaltParser::Ofx::Balance.new( :amount => amount, :amount_in_pennies => ((amount * 100).round 2).to_i, :posted_at => build_date(account.search("availbal > dtasof").inner_text) ) end end
build_balance(account)
click to toggle source
# File lib/ofx/parser/base.rb, line 189 def build_balance(account) return nil unless account.search("ledgerbal > balamt").size > 0 if account.search("ledgerbal > balamt").inner_text.match(/[\d]{14}\.[\d]+/) SaltParser::Ofx::Balance.new( :amount => 0.0, :amount_in_pennies => 0, :posted_at => build_date(account.search("ledgerbal > balamt").inner_text) ) else amount = parse_float(account.search("ledgerbal > balamt").inner_text) SaltParser::Ofx::Balance.new( :amount => amount, :amount_in_pennies => ((amount * 100).round 2).to_i, :posted_at => build_date(account.search("ledgerbal > dtasof").inner_text) ) end end
build_bank_account()
click to toggle source
# File lib/ofx/parser/base.rb, line 57 def build_bank_account html.search("stmttrnrs", "bankacctinfo").each do |account| begin account_id = account.search("bankacctfrom > acctid").inner_text @accounts << SaltParser::Ofx::Account.new( :bank_id => account.search("bankacctfrom > bankid").inner_text, :id => account_id, :name => account.parent.search("desc").inner_text, :type => ACCOUNT_TYPES[account.search("bankacctfrom > accttype").inner_text.to_s.upcase], :transactions => build_transactions(account.search("banktranlist > stmttrn"), account_id), :balance => build_balance(account), :available_balance => build_available_balance(account), :currency => account.search("stmtrs > curdef").inner_text ) rescue SaltParser::Error::ParseError => error errors << error end end end
build_credit_card_account()
click to toggle source
# File lib/ofx/parser/base.rb, line 78 def build_credit_card_account html.search("ccstmttrnrs", "acctinfo").each do |account| begin account_id = account.search("ccacctfrom > acctid").inner_text next if account_id.blank? @accounts << SaltParser::Ofx::Account.new( :id => account_id, :name => account.search("desc").inner_text, :type => ACCOUNT_TYPES["CREDITCARD"], :transactions => build_transactions(account.search("banktranlist > stmttrn"), account_id), :balance => build_balance(account), :currency => account.search("curdef").inner_text ) rescue SaltParser::Error::ParseError => error errors << error end end end
build_date(date)
click to toggle source
# File lib/ofx/parser/base.rb, line 296 def build_date(date) _, year, month, day, hour, minutes, seconds = *date.match(/(\d{4})(\d{2})(\d{2})(?:(\d{2})(\d{2})(\d{2}))?/) date = "#{year}-#{month}-#{day} " date << "#{hour}:#{minutes}:#{seconds}" if hour && minutes && seconds Time.parse(date) rescue TypeError, ArgumentError => error raise SaltParser::Error::ParseError.new(SaltParser::Error::ParseError::TIME) end
build_investment_amount(element)
click to toggle source
# File lib/ofx/parser/base.rb, line 286 def build_investment_amount(element) if element.parent.search("invbuy", "invsell").size > 0 -1 * parse_float(element.search("total").inner_text) else build_amount(element) end rescue TypeError => error raise SaltParser::Error::ParseError.new(SaltParser::Error::ParseError::AMOUNT) end
build_investment_balance(account)
click to toggle source
# File lib/ofx/parser/base.rb, line 229 def build_investment_balance(account) if account.search("invbal > availcash").size > 0 && account.search("invpos > mktval").size < 1 amount = parse_float(account.search("invbal > availcash").inner_text) elsif account.search("invbal > availcash").size < 1 && account.search("invpos > mktval").size > 0 amount = 0 account.search("invpos > mktval").map do |mktval| amount += parse_float(mktval.inner_text) end amount elsif account.search("invbal > availcash").size > 0 && account.search("invpos > mktval").size > 0 amount = 0 account.search("invbal > availcash").map do |availcash| amount += parse_float(availcash.inner_text) end account.search("invpos > mktval").map do |mktval| amount += parse_float(mktval.inner_text) end amount else return nil end SaltParser::Ofx::Balance.new( :amount => amount, :amount_in_pennies => ((amount * 100).round 2).to_i ) end
build_investment_transaction(transaction, account_id)
click to toggle source
# File lib/ofx/parser/base.rb, line 162 def build_investment_transaction(transaction, account_id) SaltParser::Ofx::Transaction.new( :amount => build_investment_amount(transaction), :amount_in_pennies => ((build_investment_amount(transaction) * 100).round 2).to_i, :fit_id => transaction.search("fitid").inner_text, :memo => transaction.search("memo").inner_text, :name => transaction.search("name").inner_text, :posted_at => build_date(transaction.search("dtposted", "dttrade").inner_text), :type => build_type(transaction), :ref_number => transaction.search("refnum", "uniqueid").empty? ? "N/A" : transaction.search("refnum", "uniqueid").inner_text, :account_id => account_id, :units => parse_float(transaction.search("units").inner_text), :unit_price => parse_float(transaction.search("unitprice").inner_text) ) end
build_investment_transactions(transactions_xml, account_id)
click to toggle source
# File lib/ofx/parser/base.rb, line 147 def build_investment_transactions(transactions_xml, account_id) transactions = transactions_xml.search("stmttrn", "invtran") transactions.each_with_object([]) do |transaction, transactions| begin if transaction.name.include?("stmttrn") transactions << build_investment_transaction(transaction, account_id) else transactions << build_investment_transaction(transaction.parent, account_id) end rescue SaltParser::Error::ParseError => error errors << error end end end
build_investments_account()
click to toggle source
# File lib/ofx/parser/base.rb, line 97 def build_investments_account html.search("invstmttrnrs", "acctinfo").each do |account| begin account_id = account.search("invacctfrom > acctid").inner_text broker_id = account.search("invacctfrom > brokerid").inner_text next if broker_id.blank? or account_id.blank? @accounts << SaltParser::Ofx::Account.new( :id => account_id, :broker_id => broker_id, :type => ACCOUNT_TYPES["INVESTMENT"], :transactions => build_investment_transactions(account.search("invtranlist"), account_id), :balance => build_investment_balance(account), :currency => account.search("curdef").inner_text, :units => compute_investment_units(account), :unit_price => compute_investment_unit_price(account) ) rescue SaltParser::Error::ParseError => error errors << error end end end
build_sign_on()
click to toggle source
# File lib/ofx/parser/base.rb, line 178 def build_sign_on @sign_on = SaltParser::Ofx::SignOn.new( :language => html.search("signonmsgsrsv1 > sonrs > language").inner_text, :fi_id => html.search("signonmsgsrsv1 > sonrs > fi > fid").inner_text, :fi_name => html.search("signonmsgsrsv1 > sonrs > fi > org").inner_text, :code => html.search("signonmsgsrsv1 > sonrs > status > code").inner_text, :severity => html.search("signonmsgsrsv1 > sonrs > status > severity").inner_text, :message => html.search("signonmsgsrsv1 > sonrs > status > message").inner_text ) end
build_transaction(transaction, account_id)
click to toggle source
# File lib/ofx/parser/base.rb, line 130 def build_transaction(transaction, account_id) SaltParser::Ofx::Transaction.new( :amount => build_amount(transaction), :amount_in_pennies => ((build_amount(transaction) * 100).round 2).to_i, :fit_id => transaction.search("fitid").inner_text, :memo => transaction.search("memo").inner_text, :name => transaction.search("name").inner_text, :payee => transaction.search("payee").inner_text, :check_number => transaction.search("checknum").inner_text, :ref_number => transaction.search("refnum").inner_text, :posted_at => build_date(transaction.search("dtposted").inner_text), :type => build_type(transaction), :sic => transaction.search("sic").inner_text, :account_id => account_id ) end
build_transactions(transactions, account_id)
click to toggle source
# File lib/ofx/parser/base.rb, line 120 def build_transactions(transactions, account_id) transactions.each_with_object([]) do |transaction, transactions| begin transactions << build_transaction(transaction, account_id) rescue SaltParser::Error::ParseError => error errors << error end end end
build_type(element)
click to toggle source
# File lib/ofx/parser/base.rb, line 276 def build_type(element) TRANSACTION_TYPES[element.search("trntype", "incometype").inner_text.to_s.upcase] end
check_for_errors()
click to toggle source
# File lib/ofx/parser/base.rb, line 48 def check_for_errors statuses = html.search("status").reverse statuses.each do |status| if status.search("severity").inner_text == "ERROR" errors << SaltParser::Error::RequestError.new(["[#{status.search("code").inner_text}]", status.search("message").inner_text].join(" ").strip) end end end
compute_investment_unit_price(account)
click to toggle source
# File lib/ofx/parser/base.rb, line 268 def compute_investment_unit_price(account) if account.search("invpos > unitprice").size == 1 parse_float(account.search("invpos > unitprice").inner_text) else 0.0 end end
compute_investment_units(account)
click to toggle source
# File lib/ofx/parser/base.rb, line 260 def compute_investment_units(account) if account.search("invpos > units").size == 1 parse_float(account.search("invpos > units").inner_text) else 0.0 end end
parse_float(incoming, options={})
click to toggle source
# File lib/ofx/parser/base.rb, line 307 def parse_float(incoming, options={}) return incoming if incoming.is_a?(Float) string = incoming.dup sanitize_float_string!(string) if options[:integral] string.gsub!(",", "") string.gsub!(".", "") return string.to_f end indexes = { "," => string.rindex(","), "." => string.rindex(".") } return string.to_f if indexes["."].nil? && indexes[","].nil? if indexes["."] == nil if string.scan(/,/).size > 1 string.gsub!(",", "") # 123,123,123 else string.gsub!(",", ".") # 123,123 end return string.to_f end if indexes[","] == nil string.gsub!(".", "") if string.scan(/\./).size > 1 # 123.123.123 return string.to_f end if indexes[","] > indexes["."] # comma is decimal separator string.gsub!(".", "") string.gsub!(",", ".") else # dot is decimal separator string.gsub!(",", "") end string.to_f rescue => error raise SaltParser::Error::ParseError.new(SaltParser::Error::ParseError::FLOAT) end
sanitize_float_string!(string)
click to toggle source
# File lib/ofx/parser/base.rb, line 353 def sanitize_float_string!(string) # replace weird minus sign with proper minus string.gsub!(8211.chr(Encoding::UTF_8), "-") # replace an even weirder minus sign with proper minus string.gsub!(8722.chr(Encoding::UTF_8), "-") # remove everything except digits, dots, commas, '+', '-' string.gsub!(/[^0-9\-+.,]/, "") # remove trailing non digits string.gsub!(/[-+.,]+$/, "") end