zilverline-mt940 1.0 → 2.0

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 (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