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.
- data/README.rdoc +38 -0
- data/Rakefile +141 -0
- data/bin/coop_to_ofx +60 -0
- data/lib/coop_scraper/base.rb +8 -0
- data/lib/coop_scraper/credit_card.rb +94 -0
- data/lib/coop_scraper/current_account.rb +92 -0
- data/lib/coop_scraper/version.rb +12 -0
- data/lib/coop_scraper.rb +2 -0
- data/lib/ofx/statement/base.rb +53 -0
- data/lib/ofx/statement/credit_card.rb +15 -0
- data/lib/ofx/statement/current_account.rb +14 -0
- data/lib/ofx/statement/output/base.rb +131 -0
- data/lib/ofx/statement/output/builder.rb +76 -0
- data/lib/ofx/statement/output/credit_card.rb +31 -0
- data/lib/ofx/statement/output/current_account.rb +29 -0
- data/lib/ofx/statement/transaction.rb +52 -0
- data/lib/ofx/statement.rb +3 -0
- data/lib/ofx.rb +1 -0
- data/spec/coop_scraper/base_spec.rb +15 -0
- data/spec/coop_scraper/credit_card_spec.rb +229 -0
- data/spec/coop_scraper/current_account_spec.rb +154 -0
- data/spec/fixtures/credit_card/cc_statement_fixture.html +927 -0
- data/spec/fixtures/credit_card/foreign_transaction_fixture.html +447 -0
- data/spec/fixtures/credit_card/interest_transaction_fixture.html +438 -0
- data/spec/fixtures/credit_card/maybe.txt +43 -0
- data/spec/fixtures/credit_card/merchandise_interest_fixture.html +0 -0
- data/spec/fixtures/credit_card/normal_transaction_fixture.html +439 -0
- data/spec/fixtures/credit_card/overlimit_charge_fixture.html +446 -0
- data/spec/fixtures/credit_card/payment_in_transaction_fixture.html +439 -0
- data/spec/fixtures/credit_card/simple_cc_statement.ofx +43 -0
- data/spec/fixtures/credit_card/statement_with_interest_line_fixture.html +452 -0
- data/spec/fixtures/current_account/cash_point_transaction_fixture.html +372 -0
- data/spec/fixtures/current_account/current_account_fixture.html +420 -0
- data/spec/fixtures/current_account/current_account_fixture.ofx +83 -0
- data/spec/fixtures/current_account/debit_interest_transaction_fixture.html +372 -0
- data/spec/fixtures/current_account/no_transactions_fixture.html +364 -0
- data/spec/fixtures/current_account/normal_transaction_fixture.html +372 -0
- data/spec/fixtures/current_account/payment_in_transaction_fixture.html +372 -0
- data/spec/fixtures/current_account/service_charge_transaction_fixture.html +372 -0
- data/spec/fixtures/current_account/transfer_transaction_fixture.html +372 -0
- data/spec/ofx/statement/base_spec.rb +116 -0
- data/spec/ofx/statement/credit_card_spec.rb +20 -0
- data/spec/ofx/statement/current_account_spec.rb +20 -0
- data/spec/ofx/statement/output/base_spec.rb +249 -0
- data/spec/ofx/statement/output/builder_spec.rb +38 -0
- data/spec/ofx/statement/output/credit_card_spec.rb +84 -0
- data/spec/ofx/statement/output/current_account_spec.rb +81 -0
- data/spec/ofx/statement/transaction_spec.rb +76 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +36 -0
- 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
|
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
|