ofx 0.3.2 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c152721af7763671b8e2c6f912f85e3e3a0b7f44
4
- data.tar.gz: 8e5f68901a6215bf71079cf67ff756c50630a7fb
2
+ SHA256:
3
+ metadata.gz: f010daf75c910a87b19c737c27a0970a4a410f5e5b6ba5916f4546e2bb5f9abb
4
+ data.tar.gz: abf6b0090855e5c063ce2d627e8f2b4907be64a71289cb520d4ae4bf7687371e
5
5
  SHA512:
6
- metadata.gz: 4eb56b361cf81ec4b091509fec2272a335514fe697fc7dd060d18e8bafa7a255cf2a55f4e0ecd0438fe3303aee2178ca591510d07767365ea310000e01c61d2c
7
- data.tar.gz: 3629da082dc5e93a97220e378ac8e8811db2b5f651594ecfe887cce923455325e23981845cc4b82ac434b330d4c01b1c5550e01dbc7db2c03de94d4d73bfc69a
6
+ metadata.gz: f3ccb6719848c32e3047243715a3ec2aabbc80b2f241bfd2ff7dcbda819bf1baeb3086ee751459a28643345d4272bf4b4481dd56ba727f9ebf94ea6a680d4cf0
7
+ data.tar.gz: 48076b4428d20fa368544099691ebe1bf877b724b49cfce83265545a84b62b07e45759e00ae0febaf254090d53c157a1f9b7b6340f5c9d59237aa1847e590ca5
data/README.rdoc CHANGED
@@ -1,5 +1,8 @@
1
1
  = OFX
2
2
 
3
+ {<img src="https://badge.fury.io/rb/ofx.png" alt="Gem Version" />}[http://badge.fury.io/rb/ofx]
4
+ {<img src="https://travis-ci.org/annacruz/ofx.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/annacruz/ofx]
5
+
3
6
  A simple OFX (Open Financial Exchange) parser built on top of Nokogiri. Currently supports both OFX 1.0.2 and 2.1.1.
4
7
 
5
8
  Works on both ruby 1.9 and 2.0.
@@ -14,6 +17,8 @@ Works on both ruby 1.9 and 2.0.
14
17
  p account.transactions
15
18
  end
16
19
 
20
+ Invalid files will raise an OFX::UnsupportedFileError.
21
+
17
22
  == Creator
18
23
 
19
24
  * Nando Vieira - http://simplesideias.com.br
@@ -1,20 +1,31 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OFX
2
4
  module Parser
3
5
  class OFX102
4
- VERSION = "1.0.2"
6
+ VERSION = '1.0.2'
5
7
 
6
8
  ACCOUNT_TYPES = {
7
- "CHECKING" => :checking
8
- }
9
-
10
- TRANSACTION_TYPES = [
11
- 'ATM', 'CASH', 'CHECK', 'CREDIT', 'DEBIT', 'DEP', 'DIRECTDEBIT', 'DIRECTDEP', 'DIV',
12
- 'FEE', 'INT', 'OTHER', 'PAYMENT', 'POS', 'REPEATPMT', 'SRVCHG', 'XFER'
13
- ].inject({}) { |hash, tran_type| hash[tran_type] = tran_type.downcase.to_sym; hash }
14
-
15
- attr_reader :headers
16
- attr_reader :body
17
- attr_reader :html
9
+ 'CHECKING' => :checking,
10
+ 'SAVINGS' => :savings,
11
+ 'CREDITLINE' => :creditline,
12
+ 'MONEYMRKT' => :moneymrkt
13
+ }.freeze
14
+
15
+ TRANSACTION_TYPES = %w[
16
+ ATM CASH CHECK CREDIT DEBIT DEP DIRECTDEBIT DIRECTDEP DIV
17
+ FEE INT OTHER PAYMENT POS REPEATPMT SRVCHG XFER
18
+ ].each_with_object({}) do |tran_type, hash|
19
+ hash[tran_type] = tran_type.downcase.to_sym
20
+ end
21
+
22
+ SEVERITY = {
23
+ 'INFO' => :info,
24
+ 'WARN' => :warn,
25
+ 'ERROR' => :error
26
+ }.freeza
27
+
28
+ attr_reader :headers, :body, :html
18
29
 
19
30
  def initialize(options = {})
20
31
  @headers = options[:headers]
@@ -22,8 +33,17 @@ module OFX
22
33
  @html = Nokogiri::HTML.parse(body)
23
34
  end
24
35
 
36
+ def statements
37
+ @statements ||= html.search('stmttrnrs, ccstmttrnrs').collect { |node| build_statement(node) }
38
+ end
39
+
40
+ def accounts
41
+ @accounts ||= html.search('stmttrnrs, ccstmttrnrs').collect { |node| build_account(node) }
42
+ end
43
+
44
+ # DEPRECATED: kept for legacy support
25
45
  def account
26
- @account ||= build_account
46
+ @account ||= build_account(html.search('stmttrnrs, ccstmttrnrs').first)
27
47
  end
28
48
 
29
49
  def sign_on
@@ -32,106 +52,153 @@ module OFX
32
52
 
33
53
  def self.parse_headers(header_text)
34
54
  # Change single CR's to LF's to avoid issues with some banks
35
- header_text.gsub!(/\r(?!\n)/, "\n")
55
+ header_text.gsub!(/\r(?!\n)/, '\n')
36
56
 
37
57
  # Parse headers. When value is NONE, convert it to nil.
38
- headers = header_text.to_enum(:each_line).inject({}) do |memo, line|
58
+ headers = header_text.to_enum(:each_line).each_with_object({}) do |line, memo|
39
59
  _, key, value = *line.match(/^(.*?):(.*?)\s*(\r?\n)*$/)
40
60
 
41
61
  unless key.nil?
42
- memo[key] = value == "NONE" ? nil : value
62
+ memo[key] = value == 'NONE' ? nil : value
43
63
  end
44
-
45
- memo
46
64
  end
47
65
 
48
66
  return headers unless headers.empty?
49
67
  end
50
68
 
51
69
  private
52
- def build_account
70
+
71
+ def build_statement(node)
72
+ stmrs_node = node.search('stmtrs, ccstmtrs')
73
+ account = build_account(node)
74
+ OFX::Statement.new(
75
+ currency: stmrs_node.search('curdef').inner_text,
76
+ start_date: build_date(stmrs_node.search('banktranlist > dtstart').inner_text),
77
+ end_date: build_date(stmrs_node.search('banktranlist > dtend').inner_text),
78
+ account: account,
79
+ transactions: account.transactions,
80
+ balance: account.balance,
81
+ available_balance: account.available_balance
82
+ )
83
+ end
84
+
85
+ def build_account(node)
53
86
  OFX::Account.new({
54
- :bank_id => html.search("bankacctfrom > bankid").inner_text,
55
- :id => html.search("bankacctfrom > acctid, ccacctfrom > acctid").inner_text,
56
- :type => ACCOUNT_TYPES[html.search("bankacctfrom > accttype").inner_text.to_s.upcase],
57
- :transactions => build_transactions,
58
- :balance => build_balance,
59
- :available_balance => build_available_balance,
60
- :currency => html.search("bankmsgsrsv1 > stmttrnrs > stmtrs > curdef, " +
61
- "creditcardmsgsrsv1 > ccstmttrnrs > ccstmtrs > curdef").inner_text
62
- })
87
+ bank_id: node.search('bankacctfrom > bankid').inner_text,
88
+ id: node.search('bankacctfrom > acctid, ccacctfrom > acctid').inner_text,
89
+ type: ACCOUNT_TYPES[node.search('bankacctfrom > accttype').inner_text.to_s.upcase],
90
+ transactions: build_transactions(node),
91
+ balance: build_balance(node),
92
+ available_balance: build_available_balance(node),
93
+ currency: node.search('stmtrs > curdef, ccstmtrs > curdef').inner_text
94
+ })
95
+ end
96
+
97
+ def build_status(node)
98
+ OFX::Status.new({
99
+ code: node.search('code').inner_text.to_i,
100
+ severity: SEVERITY[node.search('severity').inner_text],
101
+ message: node.search('message').inner_text
102
+ })
63
103
  end
64
104
 
65
105
  def build_sign_on
66
106
  OFX::SignOn.new({
67
- :language => html.search("signonmsgsrsv1 > sonrs > language").inner_text,
68
- :fi_id => html.search("signonmsgsrsv1 > sonrs > fi > fid").inner_text,
69
- :fi_name => html.search("signonmsgsrsv1 > sonrs > fi > org").inner_text
70
- })
107
+ language: html.search('signonmsgsrsv1 > sonrs > language').inner_text,
108
+ fi_id: html.search('signonmsgsrsv1 > sonrs > fi > fid').inner_text,
109
+ fi_name: html.search('signonmsgsrsv1 > sonrs > fi > org').inner_text,
110
+ status: build_status(html.search('signonmsgsrsv1 > sonrs > status'))
111
+ })
71
112
  end
72
113
 
73
- def build_transactions
74
- html.search("banktranlist > stmttrn").collect do |element|
114
+ def build_transactions(node)
115
+ node.search('banktranlist > stmttrn').collect do |element|
75
116
  build_transaction(element)
76
117
  end
77
118
  end
78
119
 
79
120
  def build_transaction(element)
121
+ occurred_at = begin
122
+ build_date(element.search('dtuser').inner_text)
123
+ rescue StandardError
124
+ nil
125
+ end
126
+
80
127
  OFX::Transaction.new({
81
- :amount => build_amount(element),
82
- :amount_in_pennies => (build_amount(element) * 100).to_i,
83
- :fit_id => element.search("fitid").inner_text,
84
- :memo => element.search("memo").inner_text,
85
- :name => element.search("name").inner_text,
86
- :payee => element.search("payee").inner_text,
87
- :check_number => element.search("checknum").inner_text,
88
- :ref_number => element.search("refnum").inner_text,
89
- :posted_at => build_date(element.search("dtposted").inner_text),
90
- :type => build_type(element),
91
- :sic => element.search("sic").inner_text
92
- })
128
+ amount: build_amount(element),
129
+ amount_in_pennies: (build_amount(element) * 100).to_i,
130
+ fit_id: element.search('fitid').inner_text,
131
+ memo: element.search('memo').inner_text,
132
+ name: element.search('name').inner_text,
133
+ payee: element.search('payee').inner_text,
134
+ check_number: element.search('checknum').inner_text,
135
+ ref_number: element.search('refnum').inner_text,
136
+ posted_at: build_date(element.search('dtposted').inner_text),
137
+ occurred_at:,
138
+ type: build_type(element),
139
+ sic: element.search('sic').inner_text
140
+ })
93
141
  end
94
142
 
95
143
  def build_type(element)
96
- TRANSACTION_TYPES[element.search("trntype").inner_text.to_s.upcase]
144
+ TRANSACTION_TYPES[element.search('trntype').inner_text.to_s.upcase]
97
145
  end
98
146
 
99
147
  def build_amount(element)
100
- BigDecimal.new(element.search("trnamt").inner_text)
148
+ to_decimal(element.search('trnamt').inner_text)
101
149
  end
102
150
 
151
+ # Input format is `YYYYMMDDHHMMSS.XXX[gmt offset[:tz name]]`
103
152
  def build_date(date)
104
- _, year, month, day, hour, minutes, seconds = *date.match(/(\d{4})(\d{2})(\d{2})(?:(\d{2})(\d{2})(\d{2}))?/)
153
+ tz_pattern = /(?:\[([+-]?\d{1,4}):\S{3}\])?\z/
154
+
155
+ # Timezone offset handling
156
+ date.sub!(tz_pattern, '')
157
+ offset = Regexp.last_match(1)
158
+
159
+ if offset
160
+ # Offset padding
161
+ _, hours, mins = *offset.match(/\A([+-]?\d{1,2})(\d{0,2})?\z/)
162
+ offset = format('%+03d%02d', hours.to_i, mins.to_i)
163
+ else
164
+ offset = '+0000'
165
+ end
105
166
 
106
- date = "#{year}-#{month}-#{day} "
107
- date << "#{hour}:#{minutes}:#{seconds}" if hour && minutes && seconds
167
+ date << ' #{offset}'
108
168
 
109
169
  Time.parse(date)
110
170
  end
111
171
 
112
- def build_balance
113
- amount = html.search("ledgerbal > balamt").inner_text.to_f
172
+ def build_balance(node)
173
+ amount = to_decimal(node.search('ledgerbal > balamt').inner_text)
174
+ posted_at = begin
175
+ build_date(node.search('ledgerbal > dtasof').inner_text)
176
+ rescue StandardError
177
+ nil
178
+ end
114
179
 
115
180
  OFX::Balance.new({
116
- :amount => amount,
117
- :amount_in_pennies => (amount * 100).to_i,
118
- :posted_at => build_date(html.search("ledgerbal > dtasof").inner_text)
119
- })
181
+ amount:,
182
+ amount_in_pennies: (amount * 100).to_i,
183
+ posted_at:
184
+ })
120
185
  end
121
186
 
122
- def build_available_balance
123
- if html.search("availbal").size > 0
124
- amount = html.search("availbal > balamt").inner_text.to_f
187
+ def build_available_balance(node)
188
+ if node.search('availbal').size > 0
189
+ amount = to_decimal(node.search('availbal > balamt').inner_text)
125
190
 
126
191
  OFX::Balance.new({
127
- :amount => amount,
128
- :amount_in_pennies => (amount * 100).to_i,
129
- :posted_at => build_date(html.search("availbal > dtasof").inner_text)
130
- })
131
- else
132
- return nil
192
+ amount:,
193
+ amount_in_pennies: (amount * 100).to_i,
194
+ posted_at: build_date(node.search('availbal > dtasof').inner_text)
195
+ })
133
196
  end
134
197
  end
198
+
199
+ def to_decimal(amount)
200
+ BigDecimal(amount.to_s.gsub(',', '.'))
201
+ end
135
202
  end
136
203
  end
137
204
  end
@@ -0,0 +1,7 @@
1
+ module OFX
2
+ module Parser
3
+ class OFX103 < OFX102
4
+ VERSION = '1.0.3'
5
+ end
6
+ end
7
+ end
data/lib/ofx/parser.rb CHANGED
@@ -12,14 +12,16 @@ module OFX
12
12
  begin
13
13
  @content = convert_to_utf8(resource.read)
14
14
  @headers, @body = prepare(content)
15
- rescue Exception
15
+ rescue
16
16
  raise OFX::UnsupportedFileError
17
17
  end
18
18
 
19
19
  case headers["VERSION"]
20
20
  when /102/ then
21
21
  @parser = OFX102.new(:headers => headers, :body => body)
22
- when /200|211/ then
22
+ when /103/ then
23
+ @parser = OFX103.new(:headers => headers, :body => body)
24
+ when /200|202|211|220/ then
23
25
  @parser = OFX211.new(:headers => headers, :body => body)
24
26
  else
25
27
  raise OFX::UnsupportedFileError
@@ -32,7 +34,7 @@ module OFX
32
34
  else
33
35
  open(resource)
34
36
  end
35
- rescue Exception
37
+ rescue
36
38
  StringIO.new(resource)
37
39
  end
38
40
 
data/lib/ofx/sign_on.rb CHANGED
@@ -3,5 +3,6 @@ module OFX
3
3
  attr_accessor :language
4
4
  attr_accessor :fi_id
5
5
  attr_accessor :fi_name
6
+ attr_accessor :status
6
7
  end
7
8
  end
@@ -0,0 +1,11 @@
1
+ module OFX
2
+ class Statement < Foundation
3
+ attr_accessor :account
4
+ attr_accessor :available_balance
5
+ attr_accessor :balance
6
+ attr_accessor :currency
7
+ attr_accessor :start_date
8
+ attr_accessor :end_date
9
+ attr_accessor :transactions
10
+ end
11
+ end
data/lib/ofx/status.rb ADDED
@@ -0,0 +1,12 @@
1
+ module OFX
2
+ # Error Reporting Aggregate
3
+ class Status < Foundation
4
+ attr_accessor :code # Error code
5
+ attr_accessor :severity # Severity of the error
6
+ attr_accessor :message # Textual explanation
7
+
8
+ def success?
9
+ code == 0
10
+ end
11
+ end
12
+ end
@@ -8,6 +8,7 @@ module OFX
8
8
  attr_accessor :name
9
9
  attr_accessor :payee
10
10
  attr_accessor :posted_at
11
+ attr_accessor :occurred_at
11
12
  attr_accessor :ref_number
12
13
  attr_accessor :type
13
14
  attr_accessor :sic
data/lib/ofx/version.rb CHANGED
@@ -2,7 +2,7 @@ module OFX
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 3
5
- PATCH = 2
5
+ PATCH = 4
6
6
  STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
7
  end
8
8
  end
data/lib/ofx.rb CHANGED
@@ -1,19 +1,24 @@
1
- require "open-uri"
2
- require "nokogiri"
3
- require "bigdecimal"
1
+ # frozen_string_literal: true
4
2
 
5
- require "kconv"
3
+ require 'open-uri'
4
+ require 'nokogiri'
5
+ require 'bigdecimal'
6
6
 
7
- require "ofx/errors"
8
- require "ofx/parser"
9
- require "ofx/parser/ofx102"
10
- require "ofx/parser/ofx211"
11
- require "ofx/foundation"
12
- require "ofx/balance"
13
- require "ofx/account"
14
- require "ofx/sign_on"
15
- require "ofx/transaction"
16
- require "ofx/version"
7
+ require 'kconv'
8
+
9
+ require 'ofx/errors'
10
+ require 'ofx/parser'
11
+ require 'ofx/parser/ofx102'
12
+ require 'ofx/parser/ofx103'
13
+ require 'ofx/parser/ofx211'
14
+ require 'ofx/foundation'
15
+ require 'ofx/balance'
16
+ require 'ofx/account'
17
+ require 'ofx/sign_on'
18
+ require 'ofx/status'
19
+ require 'ofx/statement'
20
+ require 'ofx/transaction'
21
+ require 'ofx/version'
17
22
 
18
23
  def OFX(resource, &block)
19
24
  parser = OFX::Parser::Base.new(resource).parser
@@ -11,49 +11,49 @@ describe OFX::Account do
11
11
  it "should return currency" do
12
12
  @account.currency.should == "BRL"
13
13
  end
14
-
14
+
15
15
  it "should return bank id" do
16
16
  @account.bank_id.should == "0356"
17
17
  end
18
-
18
+
19
19
  it "should return id" do
20
20
  @account.id.should == "03227113109"
21
21
  end
22
-
22
+
23
23
  it "should return type" do
24
24
  @account.type.should == :checking
25
25
  end
26
-
26
+
27
27
  it "should return transactions" do
28
28
  @account.transactions.should be_a_kind_of(Array)
29
29
  @account.transactions.size.should == 36
30
30
  end
31
-
31
+
32
32
  it "should return balance" do
33
- @account.balance.amount.should == 598.44
33
+ @account.balance.amount.should == BigDecimal('598.44')
34
34
  end
35
-
35
+
36
36
  it "should return balance in pennies" do
37
37
  @account.balance.amount_in_pennies.should == 59844
38
38
  end
39
-
39
+
40
40
  it "should return balance date" do
41
- @account.balance.posted_at.should == Time.parse("2009-11-01")
41
+ @account.balance.posted_at.should == Time.gm(2009,11,1)
42
42
  end
43
-
43
+
44
44
  context "available_balance" do
45
45
  it "should return available balance" do
46
- @account.available_balance.amount.should == 1555.99
46
+ @account.available_balance.amount.should == BigDecimal('1555.99')
47
47
  end
48
-
48
+
49
49
  it "should return available balance in pennies" do
50
50
  @account.available_balance.amount_in_pennies.should == 155599
51
51
  end
52
-
52
+
53
53
  it "should return available balance date" do
54
- @account.available_balance.posted_at.should == Time.parse("2009-11-01")
54
+ @account.available_balance.posted_at.should == Time.gm(2009,11,1)
55
55
  end
56
-
56
+
57
57
  it "should return nil if AVAILBAL not found" do
58
58
  @ofx = OFX::Parser::Base.new("spec/fixtures/utf8.ofx")
59
59
  @parser = @ofx.parser
@@ -61,7 +61,7 @@ describe OFX::Account do
61
61
  @account.available_balance.should be_nil
62
62
  end
63
63
  end
64
-
64
+
65
65
  context "Credit Card" do
66
66
  before do
67
67
  @ofx = OFX::Parser::Base.new("spec/fixtures/creditcard.ofx")
@@ -72,10 +72,60 @@ describe OFX::Account do
72
72
  it "should return id" do
73
73
  @account.id.should == "XXXXXXXXXXXX1111"
74
74
  end
75
-
75
+
76
76
  it "should return currency" do
77
77
  @account.currency.should == "USD"
78
78
  end
79
79
  end
80
+ context "With Issue" do # Bradesco do not provide a valid date in balance
81
+ before do
82
+ @ofx = OFX::Parser::Base.new("spec/fixtures/dtsof_balance_issue.ofx")
83
+ @parser = @ofx.parser
84
+ @account = @parser.account
85
+ end
86
+
87
+ it "should return nil for date balance" do
88
+ @account.balance.posted_at.should be_nil
89
+ end
90
+ end
91
+
92
+ context "Invalid Dates" do
93
+ before do
94
+ @ofx = OFX::Parser::Base.new("spec/fixtures/bradesco.ofx")
95
+ @parser = @ofx.parser
96
+ end
97
+ it "should not raise error when balance has date zero" do
98
+ expect { @parser.account.balance }.to_not raise_error
99
+ end
100
+ it "should return NIL in balance.posted_at when balance date is zero" do
101
+ @parser.account.balance.posted_at.should be_nil
102
+ end
103
+ end
104
+
105
+ context "decimal values using a comma" do
106
+ before do
107
+ @ofx = OFX::Parser::Base.new("spec/fixtures/santander.ofx")
108
+ @parser = @ofx.parser
109
+ @account = @parser.account
110
+ end
111
+
112
+ it "should return balance" do
113
+ @account.balance.amount.should == BigDecimal('348.29')
114
+ end
115
+
116
+ it "should return balance in pennies" do
117
+ @account.balance.amount_in_pennies.should == 34829
118
+ end
119
+
120
+ context "available_balance" do
121
+ it "should return available balance" do
122
+ @account.available_balance.amount.should == BigDecimal('2415.87')
123
+ end
124
+
125
+ it "should return available balance in pennies" do
126
+ @account.available_balance.amount_in_pennies.should == 241587
127
+ end
128
+ end
129
+ end
80
130
  end
81
131
  end
@@ -5,11 +5,11 @@ describe OFX::Parser::OFX102 do
5
5
  @ofx = OFX::Parser::Base.new("spec/fixtures/sample.ofx")
6
6
  @parser = @ofx.parser
7
7
  end
8
-
8
+
9
9
  it "should have a version" do
10
10
  OFX::Parser::OFX102::VERSION.should == "1.0.2"
11
11
  end
12
-
12
+
13
13
  it "should set headers" do
14
14
  @parser.headers.should == @ofx.headers
15
15
  end
@@ -22,7 +22,7 @@ describe OFX::Parser::OFX102 do
22
22
  it "should set body" do
23
23
  @parser.body.should == @ofx.body
24
24
  end
25
-
25
+
26
26
  it "should set account" do
27
27
  @parser.account.should be_a_kind_of(OFX::Account)
28
28
  end
@@ -30,17 +30,38 @@ describe OFX::Parser::OFX102 do
30
30
  it "should set account" do
31
31
  @parser.sign_on.should be_a_kind_of(OFX::SignOn)
32
32
  end
33
+
34
+ it "should set statements" do
35
+ @parser.statements.size.should == 1
36
+ @parser.statements.first.should be_a_kind_of(OFX::Statement)
37
+ end
33
38
 
34
39
  it "should know about all transaction types" do
35
40
  valid_types = [
36
- 'CREDIT', 'DEBIT', 'INT', 'DIV', 'FEE', 'SRVCHG', 'DEP', 'ATM', 'POS', 'XFER',
41
+ 'CREDIT', 'DEBIT', 'INT', 'DIV', 'FEE', 'SRVCHG', 'DEP', 'ATM', 'POS', 'XFER',
37
42
  'CHECK', 'PAYMENT', 'CASH', 'DIRECTDEP', 'DIRECTDEBIT', 'REPEATPMT', 'OTHER'
38
43
  ]
39
44
  valid_types.sort.should == OFX::Parser::OFX102::TRANSACTION_TYPES.keys.sort
40
-
45
+
41
46
  valid_types.each do |transaction_type|
42
47
  transaction_type.downcase.to_sym.should equal OFX::Parser::OFX102::TRANSACTION_TYPES[transaction_type]
43
48
  end
44
49
  end
45
-
50
+
51
+ describe "#build_date" do
52
+ context "without a Time Zone" do
53
+ it "should default to GMT" do
54
+ @parser.send(:build_date, "20170904").should == Time.gm(2017, 9, 4)
55
+ @parser.send(:build_date, "20170904082855").should == Time.gm(2017, 9, 4, 8, 28, 55)
56
+ end
57
+ end
58
+
59
+ context "with a Time Zone" do
60
+ it "should returns the correct date" do
61
+ @parser.send(:build_date, "20150507164333[-0300:BRT]").should == Time.new(2015, 5, 7, 16, 43, 33, "-03:00")
62
+ @parser.send(:build_date, "20180507120000[0:GMT]").should == Time.gm(2018, 5, 7, 12)
63
+ @parser.send(:build_date, "20170904082855[-3:GMT]").should == Time.new(2017, 9, 4, 8, 28, 55, "-03:00")
64
+ end
65
+ end
66
+ end
46
67
  end