Class: Aranha::Parsers::Ofx::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/aranha/parsers/ofx/parser.rb

Overview

rubocop:disable Metrics/ClassLength

Class Method Summary collapse

Class Method Details

.build_bank(doc) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/MethodLength



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/aranha/parsers/ofx/parser.rb', line 110

def self.build_bank(doc) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
  acct = ::Aranha::Parsers::Ofx::Data::BankAccount.new

  acct.transaction_uid = (doc / 'TRNUID').inner_text.strip
  acct.number = (doc / 'STMTRS/BANKACCTFROM/ACCTID').inner_text
  acct.routing_number = (doc / 'STMTRS/BANKACCTFROM/BANKID').inner_text
  acct.type = (doc / 'STMTRS/BANKACCTFROM/ACCTTYPE').inner_text.strip
  acct.balance = (doc / 'STMTRS/LEDGERBAL/BALAMT').inner_text
  acct.balance_date = parse_datetime((doc / 'STMTRS/LEDGERBAL/DTASOF').inner_text)

  statement = ::Aranha::Parsers::Ofx::Data::Statement.new
  statement.currency = (doc / 'STMTRS/CURDEF').inner_text
  statement.start_date = parse_datetime((doc / 'STMTRS/BANKTRANLIST/DTSTART').inner_text)
  statement.end_date = parse_datetime((doc / 'STMTRS/BANKTRANLIST/DTEND').inner_text)
  acct.statement = statement

  statement.transactions = (doc / 'STMTRS/BANKTRANLIST/STMTTRN').collect do |t|
    build_transaction(t)
  end

  acct
end

.build_credit(doc) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/MethodLength



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/aranha/parsers/ofx/parser.rb', line 133

def self.build_credit(doc) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
  acct = ::Aranha::Parsers::Ofx::Data::CreditAccount.new

  acct.number = (doc / 'CCSTMTRS/CCACCTFROM/ACCTID').inner_text
  acct.transaction_uid = (doc / 'TRNUID').inner_text.strip
  acct.balance = (doc / 'CCSTMTRS/LEDGERBAL/BALAMT').inner_text
  acct.balance_date = parse_datetime((doc / 'CCSTMTRS/LEDGERBAL/DTASOF').inner_text)
  acct.remaining_credit = (doc / 'CCSTMTRS/AVAILBAL/BALAMT').inner_text
  acct.remaining_credit_date = parse_datetime((doc / 'CCSTMTRS/AVAILBAL/DTASOF').inner_text)

  statement = ::Aranha::Parsers::Ofx::Data::Statement.new
  statement.currency = (doc / 'CCSTMTRS/CURDEF').inner_text
  statement.start_date = parse_datetime((doc / 'CCSTMTRS/BANKTRANLIST/DTSTART').inner_text)
  statement.end_date = parse_datetime((doc / 'CCSTMTRS/BANKTRANLIST/DTEND').inner_text)
  acct.statement = statement

  statement.transactions = (doc / 'CCSTMTRS/BANKTRANLIST/STMTTRN').collect do |t|
    build_transaction(t)
  end

  acct
end

.build_info(doc) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/aranha/parsers/ofx/parser.rb', line 97

def self.build_info(doc)
   = []

  (doc / 'ACCTINFO').each do |info_doc|
    acc_info = ::Aranha::Parsers::Ofx::Data::AccountInfo.new
    acc_info.desc = (info_doc / 'DESC').inner_text
    acc_info.number = (info_doc / 'ACCTID').inner_text
     << acc_info
  end

  
end

.build_investment(doc) ⇒ Object



171
# File 'lib/aranha/parsers/ofx/parser.rb', line 171

def self.build_investment(doc); end

.build_signon(doc) ⇒ Object

rubocop:disable Metrics/AbcSize



85
86
87
88
89
90
91
92
93
94
95
# File 'lib/aranha/parsers/ofx/parser.rb', line 85

def self.build_signon(doc) # rubocop:disable Metrics/AbcSize
  sign_on = ::Aranha::Parsers::Ofx::Data::SignOn.new
  sign_on.status = build_status(doc / 'STATUS')
  sign_on.date = parse_datetime((doc / 'DTSERVER').inner_text)
  sign_on.language = (doc / 'LANGUAGE').inner_text

  sign_on.institute = ::Aranha::Parsers::Ofx::Data::Institute.new
  sign_on.institute.name = ((doc / 'FI') / 'ORG').inner_text
  sign_on.institute.id = ((doc / 'FI') / 'FID').inner_text
  sign_on
end

.build_status(doc) ⇒ Object



173
174
175
176
177
178
179
# File 'lib/aranha/parsers/ofx/parser.rb', line 173

def self.build_status(doc)
  status = ::Aranha::Parsers::Ofx::Data::Status.new
  status.code = (doc / 'CODE').inner_text
  status.severity = (doc / 'SEVERITY').inner_text
  status.message = (doc / 'MESSAGE').inner_text
  status
end

.build_transaction(t) ⇒ Object

for credit and bank transactions.



157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/aranha/parsers/ofx/parser.rb', line 157

def self.build_transaction(t) # rubocop:disable Metrics/AbcSize, Naming/MethodParameterName, Metrics/MethodLength
  transaction = ::Aranha::Parsers::Ofx::Data::Transaction.new
  transaction.type = (t / 'TRNTYPE').inner_text
  transaction.date = parse_datetime((t / 'DTPOSTED').inner_text)
  transaction.amount = (t / 'TRNAMT').inner_text
  transaction.fit_id = (t / 'FITID').inner_text
  transaction.payee = (t / 'PAYEE').inner_text + (t / 'NAME').inner_text
  transaction.memo = (t / 'MEMO').inner_text
  transaction.sic = (t / 'SIC').inner_text
  transaction.check_number = (t / 'CHECKNUM').inner_text if transaction.type == :CHECK
  transaction.currate = (t / 'CURRENCY/CURRATE').inner_text
  transaction
end

.parse(ofx) ⇒ Object

Creates and returns an Ofx instance when given a well-formed OFX document, complete with the mandatory key:pair header.



9
10
11
12
13
14
15
16
17
18
19
# File 'lib/aranha/parsers/ofx/parser.rb', line 9

def self.parse(ofx)
  ofx = ofx.respond_to?(:read) ? ofx.read.to_s : ofx.to_s

  return ::Aranha::Parsers::Ofx::Data.new if ofx == ''

  header, body = pre_process(ofx)

  ofx_out = parse_body(body)
  ofx_out.header = header
  ofx_out
end

.parse_body(body) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/MethodLength



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/aranha/parsers/ofx/parser.rb', line 67

def self.parse_body(body) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
  doc = ::Aranha::Parsers::Ofx::BodyParser.new(body).parse

  ofx = ::Aranha::Parsers::Ofx::Data.new

  ofx.sign_on = build_signon(doc / 'SIGNONMSGSRSV1/SONRS')
  ofx. = build_info(doc / 'SIGNUPMSGSRSV1/ACCTINFOTRNRS')
  unless (doc / 'BANKMSGSRSV1').empty?
    ofx. = build_bank(doc / 'BANKMSGSRSV1/STMTTRNRS')
  end
  unless (doc / 'CREDITCARDMSGSRSV1').empty?
    ofx.credit_card = build_credit(doc / 'CREDITCARDMSGSRSV1/CCSTMTTRNRS')
  end
  # build_investment((doc/"SIGNONMSGSRQV1"))

  ofx
end

.parse_datetime(date) ⇒ Object

Takes an OFX datetime string of the format:

  • YYYYMMDDHHMMSS.XXX[gmt offset:tz name]

  • YYYYMMDD

  • YYYYMMDDHHMMSS

  • YYYYMMDDHHMMSS.XXX

Returns a DateTime object. Milliseconds (XXX) are ignored.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/aranha/parsers/ofx/parser.rb', line 46

def self.parse_datetime(date) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
  if /\A\s*
  (\d{4})(\d{2})(\d{2})           # YYYYMMDD            1,2,3
  (?:(\d{2})(\d{2})(\d{2}))?      # HHMMSS  - optional  4,5,6
  (?:\.(\d{3}))?                  # .XXX    - optional  7
  (?:\[([-+]?[.\d]+):\w{3}\])?  # [-n:TZ] - optional  8,9
  \s*\z/ix =~ date
    year = ::Regexp.last_match(1).to_i
    mon = ::Regexp.last_match(2).to_i
    day = ::Regexp.last_match(3).to_i
    hour = ::Regexp.last_match(4).to_i
    min = ::Regexp.last_match(5).to_i
    sec = ::Regexp.last_match(6).to_i
    # DateTime does not support usecs.
    # usec = 0
    # usec = $7.to_f * 1000000 if $7
    off = Rational(::Regexp.last_match(8).to_i, 24) # offset as a fraction of day. :|
    DateTime.civil(year, mon, day, hour, min, sec, off)
  end
end

.pre_process(ofx) ⇒ Object

Designed to make the main OFX body parsable. This means adding closing tags to the SGML to make it parsable by hpricot.

Returns an array of 2 elements:

  • header as a hash,

  • body as an evily pre-processed string ready for parsing by hpricot.



27
28
29
30
31
32
33
34
35
36
37
# File 'lib/aranha/parsers/ofx/parser.rb', line 27

def self.pre_process(ofx)
  header, body = ofx.split(/\n{2,}|:?<OFX>/, 2)
  body = "#{body.gsub("\r\n", "\n").strip}\n"
  body = "<OFX>\n#{body}" unless body.upcase.start_with?('<OFX>')

  header = Hash[*header.gsub(/^\r?\n+/, '').split("\r\n").collect do |e|
    e.split(':', 2)
  end.flatten]

  [header, body]
end