zilverline-mt940 1.0 → 2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +8 -8
  2. data/Gemfile.lock +1 -1
  3. data/README.md +5 -4
  4. data/lib/mt940.rb +1 -6
  5. data/lib/mt940/version.rb +2 -2
  6. data/lib/mt940_structured/file_content.rb +46 -0
  7. data/lib/mt940_structured/header.rb +30 -0
  8. data/lib/mt940_structured/mt940_structured.rb +8 -0
  9. data/lib/mt940_structured/parser.rb +18 -0
  10. data/lib/mt940_structured/parsers/abnamro/abnamro.rb +5 -0
  11. data/lib/mt940_structured/parsers/abnamro/parser.rb +15 -0
  12. data/lib/mt940_structured/parsers/abnamro/transaction_parser.rb +55 -0
  13. data/lib/mt940_structured/parsers/balance_parser.rb +12 -0
  14. data/lib/mt940_structured/parsers/bank_statement_parser.rb +59 -0
  15. data/lib/mt940_structured/parsers/base.rb +34 -0
  16. data/lib/mt940_structured/parsers/date_parser.rb +7 -0
  17. data/lib/mt940_structured/parsers/default_line61_parser.rb +25 -0
  18. data/lib/mt940_structured/parsers/iban_support.rb +15 -0
  19. data/lib/mt940_structured/parsers/ing/ing.rb +7 -0
  20. data/lib/mt940_structured/parsers/ing/parser.rb +18 -0
  21. data/lib/mt940_structured/parsers/ing/structured_transaction_parser.rb +50 -0
  22. data/lib/mt940_structured/parsers/ing/transaction_parser.rb +31 -0
  23. data/lib/mt940_structured/parsers/ing/types.rb +26 -0
  24. data/lib/mt940_structured/parsers/parsers.rb +16 -0
  25. data/lib/mt940_structured/parsers/rabobank/parser.rb +13 -0
  26. data/lib/mt940_structured/parsers/rabobank/rabobank.rb +8 -0
  27. data/lib/mt940_structured/parsers/rabobank/structured_transaction_parser.rb +41 -0
  28. data/lib/mt940_structured/parsers/rabobank/transaction_parser.rb +29 -0
  29. data/lib/mt940_structured/parsers/rabobank/types.rb +714 -0
  30. data/lib/mt940_structured/parsers/structured_description_parser.rb +12 -0
  31. data/lib/mt940_structured/parsers/tridios/parser.rb +14 -0
  32. data/lib/mt940_structured/parsers/tridios/transaction_parser.rb +23 -0
  33. data/lib/mt940_structured/parsers/tridios/triodos.rb +5 -0
  34. data/spec/fixtures/ing/eu_incasso.txt +17 -0
  35. data/spec/fixtures/ing/eu_incasso_foreign_transaction.txt +17 -0
  36. data/spec/fixtures/ing/failing.txt +18 -0
  37. data/spec/mt940_abnamro_spec.rb +18 -6
  38. data/spec/mt940_ing_spec.rb +78 -2
  39. data/spec/mt940_rabobank_spec.rb +11 -11
  40. data/spec/mt940_structured/file_content_spec.rb +77 -0
  41. data/spec/mt940_structured/header_spec.rb +32 -0
  42. data/spec/mt940_structured/parsers/rabobank/bank_statement_parser_spec.rb +32 -0
  43. data/spec/mt940_triodos_spec.rb +1 -1
  44. data/spec/mt940_two_accounts_spec.rb +1 -1
  45. metadata +41 -9
  46. data/lib/mt940/banks/abnamro.rb +0 -76
  47. data/lib/mt940/banks/ing.rb +0 -84
  48. data/lib/mt940/banks/rabobank.rb +0 -770
  49. data/lib/mt940/banks/triodos.rb +0 -20
  50. data/lib/mt940/base.rb +0 -165
  51. data/lib/mt940/structured_format.rb +0 -16
  52. data/spec/mt940_base_spec.rb +0 -48
@@ -0,0 +1,12 @@
1
+ module MT940Structured::Parsers
2
+ module StructuredDescriptionParser
3
+ def parse_description_after_tag(description_parts, tag)
4
+ description_start_index = description_parts.index { |part| part == tag }
5
+ if description_start_index
6
+ description_parts[description_start_index + 1].strip
7
+ else
8
+ ''
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module MT940Structured::Parsers::Triodos
2
+ class Parser < MT940Structured::Parsers::Base
3
+ def initialize
4
+ super "Triodos", TransactionParsers.new
5
+ end
6
+ end
7
+
8
+ class TransactionParsers
9
+ def for_format(_)
10
+ TransactionParser.new
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,23 @@
1
+ module MT940Structured::Parsers::Triodos
2
+ class TransactionParser
3
+ include MT940Structured::Parsers::DateParser
4
+ include MT940Structured::Parsers::DefaultLine61Parser
5
+
6
+ def get_regex_for_line_61
7
+ /^:61:(\d{6})(C|D)(\d+),(\d{0,2})/
8
+ end
9
+
10
+ def enrich_transaction(transaction, line_86)
11
+ if line_86.match(/^:86:\s?(.*)\Z/m)
12
+ temp_description = $1.gsub(/\n/, ' ').gsub(/>\d{2}/, '').strip
13
+ if temp_description.match(/^\d+(\d{9})(.*)$/)
14
+ transaction.contra_account = $1.rjust(9, '000000000')
15
+ transaction.description = $2.strip
16
+ else
17
+ transaction.description = temp_description
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ module MT940Structured::Parsers::Triodos
2
+ end
3
+
4
+ require_relative 'transaction_parser'
5
+ require_relative 'parser'
@@ -0,0 +1,17 @@
1
+ 0000 01INGBNL2AXXXX00001
2
+ 0000 01INGBNL2AXXXX00001
3
+ 940 00
4
+ :20:MPBZ
5
+ :25:0001234567
6
+ :28C:000
7
+ :60F:C131231EUR1000,00
8
+ :61:131231D50,00NIC MARF
9
+ :86:Europese Incasso, doorlopend NL58INGB0000003000 INGBNL2A JAAPJAAP
10
+ FIETS PAPIER QWDFDFGGASDFGDSFGS NV NL10XXX100020000000 SEPA 01000
11
+ 00000000 000000000000-AAAA12345678 Premie xxxxxxxxxxxxxxxxxxxxxxx
12
+ :62F:C131231EUR950,00
13
+ :64:C131231EUR950,00
14
+ :65:C131231EUR950,00
15
+ :86:D000046C000017D10000,18C20000,00
16
+ -XXX
17
+
@@ -0,0 +1,17 @@
1
+ 0000 01INGBNL2AXXXX00001
2
+ 0000 01INGBNL2AXXXX00001
3
+ 940 00
4
+ :20:MPBZ
5
+ :25:0001234567
6
+ :28C:000
7
+ :60F:C131231EUR1000,00
8
+ :61:131231D050,97NIC MARF
9
+ :86:Europese Incasso, doorlopend BB123456789876567898 CITIATWX Google
10
+ Ireland Limited GB40G01SDDCITI00000011091334 SEPA 9087653421 NL0
11
+ 001MKXD ADWORDS:3455667788:NL0001MKXD
12
+ :62F:C131231EUR950,00
13
+ :64:C131231EUR950,00
14
+ :65:C131231EUR950,00
15
+ :86:D000046C000017D10000,18C20000,00
16
+ -XXX
17
+
@@ -0,0 +1,18 @@
1
+ 0000 01INGBNL2AXXXX00001
2
+ 0000 01INGBNL2AXXXX00001
3
+ 940 00
4
+ :20:MPBZ
5
+ :25:0001234567
6
+ :28C:000
7
+ :60F:C131231EUR1000,00
8
+ :61:140122D50,00NIC MARF
9
+ :86:Europese Incasso, doorlopend NL80RABO0123456789 RABONL2U BOB NL72
10
+ BOB998877665544 SEPA BOB213654789387485940392049 1234567898765432
11
+ Kenmerk: 3333.1111.2222.3333 Omschrijving: 987654321 01-01-2012
12
+ 3 MND 9878878787 Servicecontract
13
+ :62F:C131231EUR950,00
14
+ :64:C131231EUR950,00
15
+ :65:C131231EUR950,00
16
+ :86:D000046C000017D10000,18C20000,00
17
+ -XXX
18
+
@@ -1,11 +1,11 @@
1
1
  require_relative 'spec_helper'
2
2
 
3
- describe "MT940::Base" do
3
+ describe MT940Structured::Parser do
4
4
 
5
- context 'classis mt940' do
5
+ context 'classic mt940' do
6
6
  before :each do
7
7
  @file_name = File.dirname(__FILE__) + '/fixtures/abnamro.txt'
8
- @bank_statements = MT940::Base.parse_mt940(@file_name)["517852257"]
8
+ @bank_statements = MT940Structured::Parser.parse_mt940(@file_name)["517852257"]
9
9
  @transactions = @bank_statements.flat_map(&:transactions)
10
10
  @transaction = @transactions.first
11
11
  end
@@ -67,10 +67,11 @@ describe "MT940::Base" do
67
67
  end
68
68
  end
69
69
  end
70
+
70
71
  context 'sepa mt940' do
71
72
  before :each do
72
73
  @file_name = File.dirname(__FILE__) + '/fixtures/abnamro_structured.txt'
73
- @bank_statements = MT940::Base.parse_mt940(@file_name)["123212321"]
74
+ @bank_statements = MT940Structured::Parser.parse_mt940(@file_name)["123212321"]
74
75
  @transactions = @bank_statements.flat_map(&:transactions)
75
76
  end
76
77
 
@@ -88,6 +89,15 @@ describe "MT940::Base" do
88
89
  @bank_statements.last.new_balance.date.should == Date.new(2014, 1, 27)
89
90
  end
90
91
 
92
+ context 'nonref' do
93
+ let(:transaction) { @transactions.first }
94
+
95
+ it 'has a NONREF when contraaccount is unknown' do
96
+ transaction.contra_account.should == 'NONREF'
97
+ end
98
+
99
+ end
100
+
91
101
  context 'Transaction' do
92
102
 
93
103
  let(:transaction) { @transactions[1] }
@@ -131,7 +141,7 @@ describe "MT940::Base" do
131
141
  end
132
142
 
133
143
  it 'have the correct description in case of a regular bank' do
134
- transaction.description.should == "SAVINGS 3798473"
144
+ transaction.description.should == "SAVINGS"
135
145
  end
136
146
 
137
147
  it 'have a date' do
@@ -171,7 +181,7 @@ describe "MT940::Base" do
171
181
  end
172
182
 
173
183
  it 'have the correct description in case of a regular bank' do
174
- transaction.description.should == %Q{4851430136 0030000 735822580 NS E-TICKET(S)KENMERK: 26-01-2014 18:14 003000 0735822580}
184
+ transaction.description.should == %Q{4851430136 0030000 735822580 NS E-TICKET(S) KENMERK: 26-01-2014 18:14 003000 0735822580}
175
185
  end
176
186
 
177
187
  it 'have a date' do
@@ -240,5 +250,7 @@ describe "MT940::Base" do
240
250
  end
241
251
 
242
252
  end
253
+
254
+ pending 'SEPA INCASSO ALGEMEEN DOORLOPEND'
243
255
  end
244
256
  end
@@ -5,7 +5,7 @@ describe "ING" do
5
5
  context 'old mt940' do
6
6
  before :each do
7
7
  @file_name = File.dirname(__FILE__) + '/fixtures/ing.txt'
8
- @bank_statements = MT940::Base.parse_mt940(@file_name)["1234567"]
8
+ @bank_statements = MT940Structured::Parser.parse_mt940(@file_name)["1234567"]
9
9
  @transactions = @bank_statements.flat_map(&:transactions)
10
10
  @transaction = @transactions.first
11
11
  end
@@ -66,7 +66,7 @@ describe "ING" do
66
66
 
67
67
  before :each do
68
68
  @file_name = File.dirname(__FILE__) + '/fixtures/ing_structured.txt'
69
- @bank_statements = MT940::Base.parse_mt940(@file_name)["1234567"]
69
+ @bank_statements = MT940Structured::Parser.parse_mt940(@file_name)["1234567"]
70
70
  @transactions = @bank_statements.flat_map(&:transactions)
71
71
  @transaction = @transactions.first
72
72
  end
@@ -224,4 +224,80 @@ describe "ING" do
224
224
  end
225
225
  end
226
226
  end
227
+
228
+ context 'europese incasso' do
229
+ before :each do
230
+ @file_name = File.dirname(__FILE__) + '/fixtures/ing/eu_incasso.txt'
231
+ @bank_statements = MT940Structured::Parser.parse_mt940(@file_name)["1234567"]
232
+ @transactions = @bank_statements.flat_map(&:transactions)
233
+ @transaction = @transactions.first
234
+ end
235
+
236
+ it 'has a description' do
237
+ @transaction.description.should == 'NL10XXX100020000000 01000 00000000 000000000000-AAAA12345678 Premie xxxxxxxxxxxxxxxxxxxxxxx'
238
+ end
239
+
240
+ it 'has a contra account' do
241
+ @transaction.contra_account.should == "3000"
242
+ end
243
+
244
+ it 'has a contra account iban' do
245
+ @transaction.contra_account_iban.should == "NL58INGB0000003000"
246
+ end
247
+
248
+ it 'has a contra account owner' do
249
+ @transaction.contra_account_owner.should == "JAAPJAAP FIETS PAPIER QWDFDFGGASDFGDSFGS NV"
250
+ end
251
+ end
252
+
253
+ context 'foreign transaction' do
254
+ before :each do
255
+ @file_name = File.dirname(__FILE__) + '/fixtures/ing/eu_incasso_foreign_transaction.txt'
256
+ @bank_statements = MT940Structured::Parser.parse_mt940(@file_name)["1234567"]
257
+ @transactions = @bank_statements.flat_map(&:transactions)
258
+ @transaction = @transactions.first
259
+ end
260
+
261
+ it 'has a description' do
262
+ @transaction.description.should == 'GB40G01SDDCITI00000011091334 9087653421 NL0 001MKXD ADWORDS:3455667788:NL0001MKXD'
263
+ end
264
+
265
+ it 'has a contra account' do
266
+ @transaction.contra_account.should == "BB123456789876567898"
267
+ end
268
+
269
+ it 'has a contra account iban' do
270
+ @transaction.contra_account_iban.should == "BB123456789876567898"
271
+ end
272
+
273
+ it 'has a contra account owner' do
274
+ @transaction.contra_account_owner.should == "Google Ireland Limited"
275
+ end
276
+ end
277
+
278
+ describe 'new line in reference after company name' do
279
+ before :each do
280
+ @file_name = File.dirname(__FILE__) + '/fixtures/ing/failing.txt'
281
+ @bank_statements = MT940Structured::Parser.parse_mt940(@file_name)["1234567"]
282
+ @transactions = @bank_statements.flat_map(&:transactions)
283
+ @transaction = @transactions.first
284
+ end
285
+
286
+ it 'has a description' do
287
+ @transaction.description.should == 'NL72 BOB998877665544 BOB213654789387485940392049 1234567898765432 Kenmerk: 3333.1111.2222.3333 Omschrijving: 987654321 01-01-2012 3 MND 9878878787 Servicecontract'
288
+ end
289
+
290
+ it 'has a contra account' do
291
+ @transaction.contra_account.should == "123456789"
292
+ end
293
+
294
+ it 'has a contra account iban' do
295
+ @transaction.contra_account_iban.should == "NL80RABO0123456789"
296
+ end
297
+
298
+ it 'has a contra account owner' do
299
+ @transaction.contra_account_owner.should == "BOB"
300
+ end
301
+
302
+ end
227
303
  end
@@ -4,7 +4,7 @@ describe "Rabobank" do
4
4
 
5
5
  context "parse whole file" do
6
6
  let(:file_name) { File.dirname(__FILE__) + '/fixtures/rabobank.txt' }
7
- let(:bank_statements) { MT940::Base.parse_mt940(file_name) }
7
+ let(:bank_statements) { MT940Structured::Parser.parse_mt940(file_name) }
8
8
 
9
9
  it "should have the correct number of bank account's" do
10
10
  bank_statements.keys.size.should == 1
@@ -155,7 +155,7 @@ describe "Rabobank" do
155
155
 
156
156
  context "deposit from savings account" do
157
157
  let(:file_name) { File.dirname(__FILE__) + '/fixtures/rabobank_mt940_structured_to_savings_account.txt' }
158
- let(:bank_statements) { MT940::Base.parse_mt940(file_name) }
158
+ let(:bank_statements) { MT940Structured::Parser.parse_mt940(file_name) }
159
159
 
160
160
  it "should have the correct contra account number" do
161
161
  bank_statement = bank_statements["123456789"][0]
@@ -168,7 +168,7 @@ describe "Rabobank" do
168
168
 
169
169
  context "savings account" do
170
170
  let(:file_name) { File.dirname(__FILE__) + '/fixtures/rabobank_mt940_structured_savings_account.txt' }
171
- let(:bank_statements) { MT940::Base.parse_mt940(file_name) }
171
+ let(:bank_statements) { MT940Structured::Parser.parse_mt940(file_name) }
172
172
 
173
173
  it "should have the correct accountnumber" do
174
174
  bank_statements["9123456789"].size.should == 1
@@ -178,7 +178,7 @@ describe "Rabobank" do
178
178
 
179
179
  context "structured betalingskenmerk" do
180
180
  let(:file_name) { File.dirname(__FILE__) + '/fixtures/rabobank_mt940_structured_dutch_tax.txt' }
181
- let(:bank_statements) { MT940::Base.parse_mt940(file_name) }
181
+ let(:bank_statements) { MT940Structured::Parser.parse_mt940(file_name) }
182
182
 
183
183
  it "should put a structuted betalingskenmerk in the description" do
184
184
  bank_statement = bank_statements["123456789"][0]
@@ -189,19 +189,19 @@ describe "Rabobank" do
189
189
 
190
190
  context "structured multiline description" do
191
191
  let(:file_name) { File.dirname(__FILE__) + '/fixtures/rabobank_mt940_structured_multi_line.txt' }
192
- let(:bank_statements) { MT940::Base.parse_mt940(file_name) }
192
+ let(:bank_statements) { MT940Structured::Parser.parse_mt940(file_name) }
193
193
 
194
194
  it "handles multiline in the description" do
195
195
  bank_statement = bank_statements["123456789"][0]
196
196
  transaction = bank_statement.transactions.first
197
- transaction.description.should == "Factuur 2014-002"
197
+ transaction.description.should == "Factuur 20 14-002"
198
198
  end
199
199
 
200
200
  end
201
201
 
202
202
  context "mt 940 structured" do
203
203
  let(:file_name) { File.dirname(__FILE__) + '/fixtures/rabobank_mt940_structured.txt' }
204
- let(:bank_statements) { MT940::Base.parse_mt940(file_name) }
204
+ let(:bank_statements) { MT940Structured::Parser.parse_mt940(file_name) }
205
205
 
206
206
  it "should have the correct number of bank account's" do
207
207
  bank_statements.keys.size.should == 1
@@ -269,7 +269,7 @@ describe "Rabobank" do
269
269
  end
270
270
 
271
271
  it "should have a contra account owner" do
272
- transaction.contra_account_owner.should == "Nespresso Nederland B.V."
272
+ transaction.contra_account_owner.should == "Nespresso Nede rland B.V."
273
273
  end
274
274
 
275
275
  it "should have a bank" do
@@ -304,7 +304,7 @@ describe "Rabobank" do
304
304
  end
305
305
 
306
306
  it "should have the correct contra account" do
307
- transaction.contra_account.should == "NONREF"
307
+ transaction.contra_account.should == "663616476"
308
308
  end
309
309
 
310
310
  it "should have the correct contra account iban" do
@@ -325,7 +325,7 @@ describe "Rabobank" do
325
325
  end
326
326
 
327
327
  it "should have the correct contra account iban" do
328
- transaction.contra_account_iban.should == "4500018"
328
+ transaction.contra_account_iban.should == nil
329
329
  end
330
330
 
331
331
  it "should have the correct contra account owner" do
@@ -362,7 +362,7 @@ describe "Rabobank" do
362
362
 
363
363
  it "should be able to handle a debet current balance" do
364
364
  debet_file_name = File.dirname(__FILE__) + '/fixtures/rabobank_with_debet_previous_balance.txt'
365
- bank_statement = MT940::Base.parse_mt940(debet_file_name)["129199348"].first
365
+ bank_statement = MT940Structured::Parser.parse_mt940(debet_file_name)["129199348"].first
366
366
 
367
367
  bank_statement.previous_balance.amount.should == -12
368
368
  bank_statement.previous_balance.currency.should == "EUR"
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe MT940Structured::FileContent do
4
+
5
+ subject { MT940Structured::FileContent.new(raw_lines).group_lines }
6
+
7
+ context "ignores all lines up to :20:" do
8
+ let(:raw_lines) { [":940:", ":20:940A121001", ":86:2121.21.211EUR"] }
9
+ its(:size) { should eq 2 }
10
+ its(:first) { should eq ":20:940A121001" }
11
+ its(:last) { should eq ":86:2121.21.211EUR" }
12
+ end
13
+
14
+ context "two :86: lines" do
15
+ let(:raw_lines) { [":20:940A121001", ":86:2121.21.211EUR", "belongs to first :86:", ":61:bla"] }
16
+ it "groups them" do
17
+ expect(subject[1]).to eq(":86:2121.21.211EUR belongs to first :86:")
18
+ end
19
+
20
+ its(:last) { should eq ":61:bla" }
21
+ end
22
+
23
+ context "multiple :86: lines divided by newline" do
24
+ let(:raw_lines) { [":20:940A121001", ":86:2121.21.211EUR", "belongs to first :86:", "also belongs to first :86:", ":61:bla"] }
25
+ it "groups them" do
26
+ expect(subject[1]).to eq(":86:2121.21.211EUR belongs to first :86: also belongs to first :86:")
27
+ end
28
+
29
+ its(:last) { should eq ":61:bla" }
30
+ end
31
+
32
+ context "multiple :86: lines divided by :86:" do
33
+ let(:raw_lines) do
34
+ [
35
+ ":20:940A121008",
36
+ ":25:2121.21.211EUR",
37
+ ":28:00000/00",
38
+ ":60F:C121005EUR000000017351,42",
39
+ ":61:121008D000000000190,14N0600101000731 INSURRANCE",
40
+ ":86:BETALINGSKENM. 490022201282",
41
+ ":86:ARBEIDS ONG. VERZ. 00333333333",
42
+ ":86:PERIODE 06.10.2012 - 06.11.2012",
43
+ ":61:bla"
44
+ ]
45
+ end
46
+ it "groups them" do
47
+ expect(subject[5]).to eq(":86:BETALINGSKENM. 490022201282 ARBEIDS ONG. VERZ. 00333333333 PERIODE 06.10.2012 - 06.11.2012")
48
+ end
49
+
50
+ its(:last) { should eq ":61:bla" }
51
+
52
+ end
53
+
54
+ context "stops at end of file character for ING" do
55
+ let(:raw_lines) { [":20:940A121001", ":86:2121.21.211EUR", "-XXX"] }
56
+ its(:last) { should eq ":86:2121.21.211EUR" }
57
+ end
58
+
59
+ context "stops at end of file character for Rabobank" do
60
+ let(:raw_lines) { [":20:940A121001", ":86:2121.21.211EUR", ":62F:C121031EUR000000006675,99"] }
61
+ its(:last) { should eq ":62F:C121031EUR000000006675,99" }
62
+ end
63
+
64
+ context "stops at end of file character for Abn Amro" do
65
+ let(:raw_lines) { [":20:940A121001", ":86:2121.21.211EUR", "-"] }
66
+ its(:last) { should eq ":86:2121.21.211EUR" }
67
+ end
68
+
69
+ context "custom grouping divider" do
70
+ let(:raw_lines) { [":20:940A121001", ":86:2121.21.211EUR", "belongs to first :86:", ":61:bla"] }
71
+ it "groups them using the custom divider" do
72
+ custom = MT940Structured::FileContent.new(raw_lines, "\n").group_lines
73
+ expect(custom[1]).to eq(":86:2121.21.211EUR\nbelongs to first :86:")
74
+ end
75
+ end
76
+
77
+ end