coop_to_ofx 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. data/README.rdoc +38 -0
  2. data/Rakefile +141 -0
  3. data/bin/coop_to_ofx +60 -0
  4. data/lib/coop_scraper/base.rb +8 -0
  5. data/lib/coop_scraper/credit_card.rb +94 -0
  6. data/lib/coop_scraper/current_account.rb +92 -0
  7. data/lib/coop_scraper/version.rb +12 -0
  8. data/lib/coop_scraper.rb +2 -0
  9. data/lib/ofx/statement/base.rb +53 -0
  10. data/lib/ofx/statement/credit_card.rb +15 -0
  11. data/lib/ofx/statement/current_account.rb +14 -0
  12. data/lib/ofx/statement/output/base.rb +131 -0
  13. data/lib/ofx/statement/output/builder.rb +76 -0
  14. data/lib/ofx/statement/output/credit_card.rb +31 -0
  15. data/lib/ofx/statement/output/current_account.rb +29 -0
  16. data/lib/ofx/statement/transaction.rb +52 -0
  17. data/lib/ofx/statement.rb +3 -0
  18. data/lib/ofx.rb +1 -0
  19. data/spec/coop_scraper/base_spec.rb +15 -0
  20. data/spec/coop_scraper/credit_card_spec.rb +229 -0
  21. data/spec/coop_scraper/current_account_spec.rb +154 -0
  22. data/spec/fixtures/credit_card/cc_statement_fixture.html +927 -0
  23. data/spec/fixtures/credit_card/foreign_transaction_fixture.html +447 -0
  24. data/spec/fixtures/credit_card/interest_transaction_fixture.html +438 -0
  25. data/spec/fixtures/credit_card/maybe.txt +43 -0
  26. data/spec/fixtures/credit_card/merchandise_interest_fixture.html +0 -0
  27. data/spec/fixtures/credit_card/normal_transaction_fixture.html +439 -0
  28. data/spec/fixtures/credit_card/overlimit_charge_fixture.html +446 -0
  29. data/spec/fixtures/credit_card/payment_in_transaction_fixture.html +439 -0
  30. data/spec/fixtures/credit_card/simple_cc_statement.ofx +43 -0
  31. data/spec/fixtures/credit_card/statement_with_interest_line_fixture.html +452 -0
  32. data/spec/fixtures/current_account/cash_point_transaction_fixture.html +372 -0
  33. data/spec/fixtures/current_account/current_account_fixture.html +420 -0
  34. data/spec/fixtures/current_account/current_account_fixture.ofx +83 -0
  35. data/spec/fixtures/current_account/debit_interest_transaction_fixture.html +372 -0
  36. data/spec/fixtures/current_account/no_transactions_fixture.html +364 -0
  37. data/spec/fixtures/current_account/normal_transaction_fixture.html +372 -0
  38. data/spec/fixtures/current_account/payment_in_transaction_fixture.html +372 -0
  39. data/spec/fixtures/current_account/service_charge_transaction_fixture.html +372 -0
  40. data/spec/fixtures/current_account/transfer_transaction_fixture.html +372 -0
  41. data/spec/ofx/statement/base_spec.rb +116 -0
  42. data/spec/ofx/statement/credit_card_spec.rb +20 -0
  43. data/spec/ofx/statement/current_account_spec.rb +20 -0
  44. data/spec/ofx/statement/output/base_spec.rb +249 -0
  45. data/spec/ofx/statement/output/builder_spec.rb +38 -0
  46. data/spec/ofx/statement/output/credit_card_spec.rb +84 -0
  47. data/spec/ofx/statement/output/current_account_spec.rb +81 -0
  48. data/spec/ofx/statement/transaction_spec.rb +76 -0
  49. data/spec/spec.opts +2 -0
  50. data/spec/spec_helper.rb +36 -0
  51. metadata +172 -0
@@ -0,0 +1,76 @@
1
+ module OFX
2
+ module Statement
3
+ module Output
4
+ module Builder
5
+ class OFX2 < ::Builder::XmlMarkup
6
+ def ofx_stanza!
7
+ self.instruct!
8
+ self.instruct! :OFX, :OFXHEADER => "200", :VERSION => "203", :SECURITY => "NONE",
9
+ :OLDFILEUID => "NONE", :NEWFILEUID => "NONE"
10
+ end
11
+ end
12
+
13
+ class OFX1 < ::Builder::XmlMarkup
14
+ def ofx_stanza!
15
+ self.text! <<-EOH
16
+ OFXHEADER:100
17
+ DATA:OFXSGML
18
+ VERSION:103
19
+ SECURITY:NONE
20
+ ENCODING:USASCII
21
+ CHARSET:NONE
22
+ COMPRESSION:NONE
23
+ OLDFILEUID:NONE
24
+ NEWFILEUID:NONE
25
+ EOH
26
+ end
27
+ # Create SGMLish markup based on the name of the method. This method
28
+ # is never invoked directly, but is called for each markup method
29
+ # in the markup block.
30
+ #
31
+ # NB, it's not really SGML, it just doesn't generate empty tags of the form <tag/>
32
+ # Instead it generates <tag>
33
+ # That's it, it's a nasty hack.
34
+ def method_missing(sym, *args, &block)
35
+ text = nil
36
+ attrs = nil
37
+ sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
38
+ args.each do |arg|
39
+ case arg
40
+ when Hash
41
+ attrs ||= {}
42
+ attrs.merge!(arg)
43
+ else
44
+ text ||= ''
45
+ text << arg.to_s
46
+ end
47
+ end
48
+ if block
49
+ unless text.nil?
50
+ raise ArgumentError, "XmlMarkup cannot mix a text argument with a block"
51
+ end
52
+ _indent
53
+ _start_tag(sym, attrs)
54
+ _newline
55
+ _nested_structures(block)
56
+ _indent
57
+ _end_tag(sym)
58
+ _newline
59
+ elsif text.nil?
60
+ _indent
61
+ _start_tag(sym, attrs)
62
+ _newline
63
+ else
64
+ _indent
65
+ _start_tag(sym, attrs)
66
+ text! text
67
+ _end_tag(sym)
68
+ _newline
69
+ end
70
+ @target
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,31 @@
1
+ require 'ofx/statement/output/base'
2
+
3
+ module OFX
4
+ module Statement
5
+ module Output
6
+ class CreditCard < OFX::Statement::Output::Base
7
+ def message_set_block(node)
8
+ return node.CREDITCARDMSGSETV1 unless block_given?
9
+ node.CREDITCARDMSGSETV1 { |child| yield(child) }
10
+ end
11
+
12
+ def statement_block(node, statement)
13
+ node.CCSTMTTRNRS do |ccstmttrnrs|
14
+ ccstmttrnrs.CCSTMTRS do |ccstmtrs|
15
+ ccstmtrs.CURDEF statement.currency
16
+ ccstmtrs.CCACCTFROM do |ccacctfrom|
17
+ ccacctfrom.ACCTID statement.account_number
18
+ end
19
+ transaction_list(ccstmtrs, statement) { |list_node| yield(list_node) if block_given? }
20
+ ledger_balance_block(ccstmtrs, statement)
21
+ ccstmtrs.AVAILBAL do |availbal|
22
+ availbal.BALAMT statement.available_credit
23
+ availbal.DTASOF time_to_ofx_dta(statement.date)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ require 'ofx/statement/output/base'
2
+
3
+ module OFX
4
+ module Statement
5
+ module Output
6
+ class CurrentAccount < OFX::Statement::Output::Base
7
+ def message_set_block(node)
8
+ return node.BANKMSGSETV1 unless block_given?
9
+ node.BANKMSGSETV1 { |child| yield(child) }
10
+ end
11
+
12
+ def statement_block(node, statement)
13
+ node.STMTTRNRS do |stmttrnrs|
14
+ stmttrnrs.STMTRS do |stmtrs|
15
+ stmtrs.CURDEF statement.currency
16
+ stmtrs.BANKACCTFROM do |bankacctfrom|
17
+ bankacctfrom.BANKID statement.sort_code
18
+ bankacctfrom.ACCTID statement.account_number
19
+ bankacctfrom.ACCTTYPE "CHECKING"
20
+ end
21
+ transaction_list(stmtrs, statement) { |list_node| yield(list_node) if block_given? }
22
+ ledger_balance_block(stmtrs, statement)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,52 @@
1
+ module OFX
2
+ module Statement
3
+ class Transaction
4
+ class << self
5
+ def valid_trntypes
6
+ @valid_trntypes ||= OFX::Statement::Output::Base.trntype_hash.keys
7
+ end
8
+ end
9
+
10
+ attr_reader :amount, :date, :name, :memo, :trntype
11
+ attr_accessor :statement
12
+
13
+ def initialize(amount, date, name, options = {})
14
+ @amount = amount
15
+ @date = date
16
+ @name = name
17
+ @memo = options[:memo]
18
+ @trntype = verify_trntype(options[:trntype].nil? ? default_trntype : options[:trntype])
19
+ end
20
+
21
+ def has_memo?
22
+ !(memo.nil? || memo.empty?)
23
+ end
24
+
25
+ def fitid
26
+ statement.fitid_for(self)
27
+ end
28
+
29
+ private
30
+
31
+ def default_trntype
32
+ amount.match(/^-/) ? :debit : :credit
33
+ end
34
+
35
+ def verify_trntype(trntype)
36
+ raise UnknownTrntype, trntype unless self.class.valid_trntypes.include?(trntype)
37
+ trntype
38
+ end
39
+ end
40
+
41
+ class UnknownTrntype < StandardError
42
+ def initialize(trntype)
43
+ super()
44
+ @trntype = trntype
45
+ end
46
+
47
+ def to_s
48
+ "Unknown Trntype #{@trntype.inspect}"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,3 @@
1
+ require 'ofx/statement/credit_card'
2
+ require 'ofx/statement/current_account'
3
+ require 'ofx/statement/transaction'
data/lib/ofx.rb ADDED
@@ -0,0 +1 @@
1
+ require 'ofx/statement'
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe CoopScraper::Base do
4
+ before(:each) do
5
+ klass = Class.new
6
+ klass.class_eval { include CoopScraper::Base }
7
+ @instance = klass.new
8
+ end
9
+
10
+ describe "making Ruby dates from Co-op dates" do
11
+ it "should be able to turn a DD/MM/YYYY string into a YYYYMMDD one" do
12
+ @instance.coop_date_to_time('03/02/2009').should == Time.utc('2009', '2', '3')
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,229 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe CoopScraper::CreditCard do
4
+ def fixture_path(fixture_file_name)
5
+ full_fixture_path('credit_card', fixture_file_name)
6
+ end
7
+
8
+ describe "parsing the html components" do
9
+ def fixture_doc(fixture_file_name = 'cc_statement_fixture.html')
10
+ open(fixture_path(fixture_file_name)) { |f| Hpricot(f) }
11
+ end
12
+
13
+ def fixture_with_interest_line_doc
14
+ fixture_doc('statement_with_interest_line_fixture.html')
15
+ end
16
+
17
+ def normal_transaction_fixture_doc
18
+ fixture_doc('normal_transaction_fixture.html')
19
+ end
20
+
21
+ def payment_in_transaction_fixture_doc
22
+ fixture_doc('payment_in_transaction_fixture.html')
23
+ end
24
+
25
+ def foreign_transaction_fixture_doc
26
+ fixture_doc('foreign_transaction_fixture.html')
27
+ end
28
+
29
+ def interest_transaction_fixture_doc
30
+ fixture_doc('interest_transaction_fixture.html')
31
+ end
32
+
33
+ it "should be able to extract the statement date" do
34
+ CoopScraper::CreditCard.extract_statement_date(fixture_doc).should == Time.utc('2009', '2', '3')
35
+ end
36
+
37
+ it "should be able to extract the account (VISA card) number" do
38
+ CoopScraper::CreditCard.extract_account_number(fixture_doc).should == '1234123412341234'
39
+ end
40
+
41
+ it "should be able to extract the statement balance" do
42
+ CoopScraper::CreditCard.extract_statement_balance(fixture_doc).should == '-123.21'
43
+ end
44
+
45
+ it "should be able to extract the available credit" do
46
+ CoopScraper::CreditCard.extract_available_credit(fixture_doc).should == '1000.00'
47
+ end
48
+
49
+ describe "extracting transactions" do
50
+ it "should find the correct number of transactions" do
51
+ mock_statement = mock('OFX::Statement')
52
+ mock_statement.stubs(:date).returns(Time.utc('2009', '2', '3'))
53
+ CoopScraper::CreditCard.extract_transactions(fixture_doc, mock_statement).size.should == 57
54
+ end
55
+
56
+ it "should create OFX::Statement::Transaction objects for the transactions" do
57
+ mock_statement = mock('OFX::Statement')
58
+ mock_statement.stubs(:date).returns(Time.utc('2009', '2', '3'))
59
+ CoopScraper::CreditCard.extract_transactions(fixture_doc, mock_statement).first.
60
+ should be_instance_of(OFX::Statement::Transaction)
61
+ end
62
+
63
+ describe "processing a normal transaction" do
64
+ before(:all) do
65
+ mock_statement = mock('OFX::Statement')
66
+ mock_statement.stubs(:date).returns(Time.utc('2009', '2', '3'))
67
+ transactions = CoopScraper::CreditCard.extract_transactions(normal_transaction_fixture_doc, mock_statement)
68
+ transactions.size.should == 1
69
+ @transaction = transactions.first
70
+ end
71
+
72
+ it "should pull out the date" do
73
+ @transaction.date.should == Time.utc('2009', '1', '6')
74
+ end
75
+
76
+ it "should pull out the amount" do
77
+ @transaction.amount.should == '-23.00'
78
+ end
79
+
80
+ it "should pull out the details" do
81
+ @transaction.name.should == 'SOME TRANSACTION HERE'
82
+ end
83
+ end
84
+
85
+ describe "processing a transaction where money was put in" do
86
+ before(:all) do
87
+ mock_statement = mock('OFX::Statement')
88
+ mock_statement.stubs(:date).returns(Time.utc('2009', '2', '3'))
89
+ transactions = CoopScraper::CreditCard.extract_transactions(payment_in_transaction_fixture_doc, mock_statement)
90
+ transactions.size.should == 1
91
+ @transaction = transactions.first
92
+ end
93
+
94
+ it "should pull out the date" do
95
+ @transaction.date.should == Time.utc('2009', '1', '23')
96
+ end
97
+
98
+ it "should pull out the amount" do
99
+ @transaction.amount.should == '500.00'
100
+ end
101
+
102
+ it "should pull out the details" do
103
+ @transaction.name.should == 'PAYMENT - THANK YOU GB'
104
+ end
105
+ end
106
+
107
+ describe "processing a transaction with an additional row for currency conversion" do
108
+ before(:all) do
109
+ mock_statement = mock('OFX::Statement')
110
+ mock_statement.stubs(:date).returns(Time.utc('2009', '2', '3'))
111
+ transactions = CoopScraper::CreditCard.extract_transactions(foreign_transaction_fixture_doc, mock_statement)
112
+ transactions.size.should == 1
113
+ @transaction = transactions.first
114
+ end
115
+
116
+ it "should pull out the date" do
117
+ @transaction.date.should == Time.utc('2009', '1', '8')
118
+ end
119
+
120
+ it "should pull out the amount" do
121
+ @transaction.amount.should == '-27.13'
122
+ end
123
+
124
+ it "should pull out the details" do
125
+ @transaction.name.should == 'TUFFMAIL 123-1234567'
126
+ end
127
+
128
+ it "should pull out the conversion details" do
129
+ @transaction.memo.should == '##0000 40.00 USD @ 1.4744'
130
+ end
131
+ end
132
+
133
+ it "should ignore estimated interest lines" do
134
+ mock_statement = mock('OFX::Statement')
135
+ mock_statement.stubs(:date).returns(Time.utc('2009', '2', '3'))
136
+ transactions = CoopScraper::CreditCard.extract_transactions(fixture_with_interest_line_doc, mock_statement)
137
+ transactions.size.should == 1
138
+ transactions.first.should_not have_memo
139
+ end
140
+
141
+ describe "processing an overlimit charge line" do
142
+ before(:all) do
143
+ mock_statement = mock('OFX::Statement')
144
+ mock_statement.stubs(:date).returns(Time.utc('2009', '2', '3'))
145
+ transactions = CoopScraper::CreditCard.extract_transactions(fixture_doc('overlimit_charge_fixture.html'), mock_statement)
146
+ transactions.size.should == 2
147
+ @transaction = transactions.last
148
+ end
149
+
150
+ it "should be the overlimit charge" do
151
+ @transaction.name.should == 'OVERLIMIT CHARGE'
152
+ end
153
+
154
+ it "should pick up the statement date" do
155
+ @transaction.date.should == Time.utc('2009', '2', '3')
156
+ end
157
+
158
+ it "should have the right trntype" do
159
+ @transaction.trntype.should == :service_charge
160
+ end
161
+ end
162
+
163
+ describe "processing a transaction for credit card interest" do
164
+ before(:all) do
165
+ mock_statement = mock('OFX::Statement')
166
+ mock_statement.stubs(:date).returns(Time.utc('2009', '2', '3'))
167
+ transactions = CoopScraper::CreditCard.extract_transactions(interest_transaction_fixture_doc, mock_statement)
168
+ transactions.size.should == 1
169
+ @transaction = transactions.first
170
+ end
171
+
172
+ it "should pull out the date" do
173
+ @transaction.date.should == Time.utc('2009', '5', '5')
174
+ end
175
+
176
+ it "should pull out the amount" do
177
+ @transaction.amount.should == '-40.58'
178
+ end
179
+
180
+ it "should pull out the details" do
181
+ @transaction.name.should == 'MERCHANDISE INTEREST AT 1.667% PER MTH'
182
+ end
183
+
184
+ it "should have the right trntype" do
185
+ @transaction.trntype.should == :interest
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ describe "creating the statement object" do
192
+ before(:each) do
193
+ fixture = open(fixture_path('cc_statement_fixture.html'))
194
+ @statement = CoopScraper::CreditCard.generate_statement(fixture, Time.utc('2009', '3', '6'))
195
+ end
196
+
197
+ it "should have the right server response time" do
198
+ @statement.server_response_time.should == Time.utc('2009', '3', '6')
199
+ end
200
+
201
+ it "should the correct statement date" do
202
+ @statement.date.should == Time.utc('2009', '2', '3')
203
+ end
204
+
205
+ it "should have the correct account number" do
206
+ @statement.account_number.should == "1234123412341234"
207
+ end
208
+
209
+ it "should have the correct statement start date" do
210
+ @statement.start_date.should == Time.utc('2009', '1', '5')
211
+ end
212
+
213
+ it "should have the correct statement end date" do
214
+ @statement.end_date.should == Time.utc('2009', '2', '3')
215
+ end
216
+
217
+ it "should have the correct balance" do
218
+ @statement.ledger_balance.should == "-123.21"
219
+ end
220
+
221
+ it "should have the correct amount of available credit" do
222
+ @statement.available_credit.should == "1000.00"
223
+ end
224
+
225
+ it "should have the right number of transactions" do
226
+ @statement.transactions.size.should == 57
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,154 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe CoopScraper::CurrentAccount do
4
+ def fixture_path(fixture_file_name)
5
+ full_fixture_path('current_account', fixture_file_name)
6
+ end
7
+
8
+ describe "parsing the html components" do
9
+ def fixture_doc(name = 'current_account_fixture.html')
10
+ open(fixture_path(name)) { |f| Hpricot(f) }
11
+ end
12
+
13
+ def normal_transaction_fixture_doc
14
+ fixture_doc('normal_transaction_fixture.html')
15
+ end
16
+
17
+ def payment_in_transaction_fixture_doc
18
+ fixture_doc('payment_in_transaction_fixture.html')
19
+ end
20
+
21
+ def no_transactions_fixture_doc
22
+ fixture_doc('no_transactions_fixture.html')
23
+ end
24
+
25
+ it "should be able to extract the statement date" do
26
+ CoopScraper::CurrentAccount.extract_statement_date(fixture_doc).should == Time.utc('2008', '10', '3')
27
+ end
28
+
29
+ it "should be able to extract the account number" do
30
+ CoopScraper::CurrentAccount.extract_account_number(fixture_doc).should == "12341234"
31
+ end
32
+
33
+ it "should be able to extract the sort code" do
34
+ CoopScraper::CurrentAccount.extract_sort_code(fixture_doc).should == "089273"
35
+ end
36
+
37
+ describe "transactions" do
38
+ it "should find the correct number of transactions" do
39
+ CoopScraper::CurrentAccount.extract_transactions(fixture_doc).size.should == 7
40
+ end
41
+
42
+ it "should create OFX::Statement::Transaction objects for the transactions" do
43
+ CoopScraper::CurrentAccount.extract_transactions(fixture_doc).first.should be_instance_of(OFX::Statement::Transaction)
44
+ end
45
+
46
+ describe "processing a normal transaction" do
47
+ before(:all) do
48
+ transactions = CoopScraper::CurrentAccount.extract_transactions(normal_transaction_fixture_doc)
49
+ transactions.size.should == 1
50
+ @transaction = transactions.first
51
+ end
52
+
53
+ it "should pull out the date" do
54
+ @transaction.date.should == Time.utc('2008', '9', '29')
55
+ end
56
+
57
+ it "should pull out the amount" do
58
+ @transaction.amount.should == '-20.00'
59
+ end
60
+
61
+ it "should pull out the details" do
62
+ @transaction.name.should == 'LINK 10:51SEP28'
63
+ end
64
+ end
65
+
66
+ describe "processing a transaction where money was put in" do
67
+ before(:all) do
68
+ transactions = CoopScraper::CurrentAccount.extract_transactions(payment_in_transaction_fixture_doc)
69
+ transactions.size.should == 1
70
+ @transaction = transactions.first
71
+ end
72
+
73
+ it "should pull out the date" do
74
+ @transaction.date.should == Time.utc('2008', '10', '3')
75
+ end
76
+
77
+ it "should pull out the amount" do
78
+ @transaction.amount.should == '200.00'
79
+ end
80
+
81
+ it "should pull out the details" do
82
+ @transaction.name.should == 'SOME MONEY'
83
+ end
84
+ end
85
+
86
+ describe "processing a statement where no transactions occurred" do
87
+ before(:each) do
88
+ @transactions = CoopScraper::CurrentAccount.extract_transactions(no_transactions_fixture_doc)
89
+ end
90
+
91
+ it "should not find any transactions" do
92
+ @transactions.should be_empty
93
+ end
94
+ end
95
+
96
+ describe "processing transactions with non-default transaction types" do
97
+ it "should be able to pull out a debit interest transaction" do
98
+ transactions = CoopScraper::CurrentAccount.
99
+ extract_transactions(fixture_doc('debit_interest_transaction_fixture.html'))
100
+
101
+ transactions.first.trntype.should == :interest
102
+ end
103
+
104
+ it "should be able to pull out a service charge transaction" do
105
+ transactions = CoopScraper::CurrentAccount.
106
+ extract_transactions(fixture_doc('service_charge_transaction_fixture.html'))
107
+
108
+ transactions.first.trntype.should == :service_charge
109
+ end
110
+
111
+ it "should be able to pull out a transfer transaction" do
112
+ transactions = CoopScraper::CurrentAccount.
113
+ extract_transactions(fixture_doc('transfer_transaction_fixture.html'))
114
+
115
+ transactions.first.trntype.should == :transfer
116
+ end
117
+
118
+ it "should be able to pull out a cash point transaction" do
119
+ transactions = CoopScraper::CurrentAccount.
120
+ extract_transactions(fixture_doc('cash_point_transaction_fixture.html'))
121
+
122
+ transactions.first.trntype.should == :atm
123
+ end
124
+ end
125
+ end
126
+
127
+ it "should be able to extract the closing balance" do
128
+ CoopScraper::CurrentAccount.extract_closing_balance(fixture_doc).should == "219.92"
129
+ end
130
+
131
+ it "should be able to extract the statement start date" do
132
+ CoopScraper::CurrentAccount.extract_statement_start_date(fixture_doc).should == Time.utc('2008', '9', '29')
133
+ end
134
+
135
+ describe "generating the statement" do
136
+ it "should create a statement object okay" do
137
+ fixture = open(fixture_path('current_account_fixture.html'))
138
+ mock_statement = mock('Statement')
139
+ OFX::Statement::CurrentAccount.expects(:new).returns(mock_statement)
140
+
141
+ mock_statement.expects(:server_response_time=).with(Time.utc('2009', '3', '6'))
142
+ mock_statement.expects(:date=).with(Time.utc('2008', '10', '3'))
143
+ mock_statement.expects(:sort_code=).with('089273')
144
+ mock_statement.expects(:account_number=).with('12341234')
145
+ mock_statement.expects(:start_date=).with(Time.utc('2008', '9', '29'))
146
+ mock_statement.expects(:end_date=).with(Time.utc('2008', '10', '3'))
147
+ mock_statement.expects(:ledger_balance=).with("219.92")
148
+ mock_statement.expects(:<<).at_least_once.with { |arg| arg.respond_to?(:amount) }
149
+
150
+ CoopScraper::CurrentAccount.generate_statement(fixture, Time.utc('2009', '3', '6'))
151
+ end
152
+ end
153
+ end
154
+ end