mt940 0.6.6 → 0.7.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9687025fcee76507ec72ba177dbacfeedd3b755e
4
+ data.tar.gz: 2be9f9cf8bd116eabd1e5e28c1470809fe8f51ca
5
+ SHA512:
6
+ metadata.gz: eb88e8a1b47dee2efa2ea6cf008e92d3bf40a578eb834ddf469a00464b2b868a37f95b0ecfefad789bbae745196b53bf1cd3417621e277e2785477e263c4e0af
7
+ data.tar.gz: 65323779df20a4a8d4ce4f24994406026b70152b72ad5a31b3de95e3e778761bf6637245cd9aaf4ad67e0447ca70354845623a33b84fffc00ba788f97b0de606
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ mt940
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.0
data/.travis.yml CHANGED
@@ -1,10 +1,11 @@
1
1
  rvm:
2
+ - 2.1.0
3
+ - 2.0.0
2
4
  - 1.9.3
3
- - 1.9.2
4
- - 1.8.7
5
5
 
6
6
  branches:
7
7
  only:
8
8
  - master
9
+ - sepa
9
10
 
10
11
  script: "bundle exec rake test"
data/CHANGELOG CHANGED
@@ -1,3 +1,8 @@
1
+ * 0.7.0
2
+ - No support for Ruby 1.8.7, default 2.1.0
3
+ - I was uncomfortable with using class methods, so I introduced the Mt940::Parser and refactored. See Readme
4
+ - Parser now works for mt940 files after SEPA was introduced (with the so called structured export format)
5
+
1
6
  * 0.6.6
2
7
  - Added currency support in transaction (contribution of Bob Forma)
3
8
 
data/Gemfile CHANGED
@@ -1,5 +1,10 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem 'rake', '0.8.7'
3
+ group :development, :test do
4
+ gem 'rake', '~> 10.1.1'
5
+ gem 'shoulda'
6
+ gem 'guard-test'
7
+ gem 'awesome_print'
8
+ end
4
9
 
5
10
  gemspec
data/Gemfile.lock CHANGED
@@ -1,18 +1,52 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mt940 (0.6.5)
4
+ mt940 (0.7.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
8
8
  specs:
9
- rake (0.8.7)
9
+ awesome_print (1.2.0)
10
+ celluloid (0.15.2)
11
+ timers (~> 1.1.0)
12
+ coderay (1.1.0)
13
+ ffi (1.9.3)
14
+ formatador (0.2.4)
15
+ guard (2.2.5)
16
+ formatador (>= 0.2.4)
17
+ listen (~> 2.1)
18
+ lumberjack (~> 1.0)
19
+ pry (>= 0.9.12)
20
+ thor (>= 0.18.1)
21
+ guard-test (2.0.3)
22
+ guard (~> 2.0)
23
+ test-unit (~> 2.2)
24
+ listen (2.4.0)
25
+ celluloid (>= 0.15.2)
26
+ rb-fsevent (>= 0.9.3)
27
+ rb-inotify (>= 0.9)
28
+ lumberjack (1.0.4)
29
+ method_source (0.8.2)
30
+ pry (0.9.12.4)
31
+ coderay (~> 1.0)
32
+ method_source (~> 0.8)
33
+ slop (~> 3.4)
34
+ rake (10.1.1)
35
+ rb-fsevent (0.9.3)
36
+ rb-inotify (0.9.3)
37
+ ffi (>= 0.5.0)
10
38
  shoulda (2.11.3)
39
+ slop (3.4.7)
40
+ test-unit (2.5.5)
41
+ thor (0.18.1)
42
+ timers (1.1.0)
11
43
 
12
44
  PLATFORMS
13
45
  ruby
14
46
 
15
47
  DEPENDENCIES
48
+ awesome_print
49
+ guard-test
16
50
  mt940!
17
- rake (= 0.8.7)
51
+ rake (~> 10.1.1)
18
52
  shoulda
data/Guardfile ADDED
@@ -0,0 +1,10 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :test do
5
+ watch(%r{^lib/mt940/banks/(.+)\.rb$}) {|m| "test/mt940_#{m[1]}_test.rb" }
6
+ watch(%r{^test/.+_test\.rb$})
7
+ watch('test/test_helper.rb') { "test" }
8
+ watch('lib/mt940/base.rb') { "test" }
9
+ watch('lib/mt940/parser.rb') { "test" }
10
+ end
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Frank Oxener - Agile Dovadi BV
1
+ Copyright (c) 2014 Frank Oxener
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -5,9 +5,9 @@ MT940
5
5
  ![http://travis-ci.org/dovadi/mt940](https://secure.travis-ci.org/dovadi/mt940.png)
6
6
  </a>
7
7
 
8
- Basis parser for MT940 files, see [MT940](http://nl.wikipedia.org/wiki/MT940)
8
+ Basis parser for MT940 files, see [MT940](http://nl.wikipedia.org/wiki/MT940). This library is only meant for collection the transactions. No bank statements or balance (previous or new) is determined.
9
9
 
10
- The following Dutch banks are implemented:
10
+ The following Dutch banks are implemented with support for IBAN numbers:
11
11
 
12
12
  * ABN Amro
13
13
  * ING
@@ -21,7 +21,7 @@ With the file name as argument:
21
21
 
22
22
  file_name = '/Users/dovadi/Downloads/ing.940'
23
23
 
24
- @transactions = MT940:::Base.transactions(file_name)
24
+ @transactions = MT940::Parser.new(file_name).transactions
25
25
 
26
26
  or with the file itself:
27
27
 
@@ -29,19 +29,19 @@ or with the file itself:
29
29
 
30
30
  file = File.open(file_name)
31
31
 
32
- @transactions = MT940:::Base.transactions(file)
32
+ @transactions = MT940::Parser.new(file).transactions
33
33
 
34
34
 
35
35
  * Independent of the bank, a transaction always consists of:
36
36
 
37
- - accountnumber
38
- - bank (for example Ing, Rabobank or Unknown)
37
+ - accountnumber (BBAN or IBAN)
38
+ - bank (for example Ing, Rabobank etc )
39
39
  - date
40
40
  - amount (which is negative in case of a withdrawal)
41
41
  - description
42
42
  - contra account
43
43
 
44
- * With the Rabobank its owner is extracted as well.
44
+ (Pre sepa: with the Rabobank the contra_account owner is extracted as well)
45
45
 
46
46
  Running tests
47
47
  =============
@@ -64,5 +64,5 @@ Contributing to MT940
64
64
  Copyright
65
65
  ==========
66
66
 
67
- Copyright (c) 2012 Frank Oxener - Agile Dovadi BV. See LICENSE.txt for further details.
67
+ Copyright (c) 2014 Frank Oxener. See LICENSE.txt for further details.
68
68
 
@@ -0,0 +1,213 @@
1
+ MT940 voorbeeldbestanden 16-4-2013
2
+
3
+ Change History
4
+ - Corrected debit/credit indicator in 1.
5
+ - Corrected end balances
6
+ - Corrected CRLF (are not allowed mid label)
7
+ - Added REMI field
8
+ - 6a changed TRTP content, removed not used INF tag
9
+ - Removed final slashes where applicable
10
+
11
+
12
+ Inhoudsopgave:
13
+
14
+ 1. SCT overboeking credit
15
+ 2. SCT overboeking debet
16
+ 3. SCT batch
17
+ 4. SCT batch salaris
18
+ 5. SCT terugboeking (return en reject)
19
+ 6. SDD core batch incasso
20
+ 7. SDD core incasso (geincasseerde)
21
+ 8. SDD core incasso (cancellation, reversal, refund en refusal)
22
+ 9. SCT iDEAL
23
+ 10. SCT IBAN Acceptgiro
24
+
25
+ 1. SCT overboeking credit
26
+
27
+ ABNANL2A
28
+ 940
29
+ ABNANL2A
30
+ :20:ABN AMRO BANK NV
31
+ :25:123456789
32
+ :28:13501/1
33
+ :60F:C120511EUR5138,61
34
+ :61:1205120514C500,01N654NONREF
35
+ :86:/TRTP/SEPA OVERBOEKING/IBAN/FR12345678901234/BIC/GEFRADAM
36
+ /NAME/QASD JGRED/REMI/Dit zijn de omschrijvingsregels/EREF/NOTPRO
37
+ VIDED
38
+ :62F:C120514EUR5638,62
39
+
40
+ 2. SCT overboeking debet
41
+
42
+ ABNANL2A
43
+ 940
44
+ ABNANL2A
45
+ :20:ABN AMRO BANK NV
46
+ :25:123456789
47
+ :28:14901/1
48
+ :60F:D120525EUR1627,85
49
+ :61:1205280528D135,12N658NONREF
50
+ :86:/TRTP/SEPA OVERBOEKING/IBAN/BE12345678901234/BIC/KREDBEBB
51
+ /NAME/BELEKET/REMI/Omschrijving van de klant/EREF/05844183645
52
+ :62F:C120528EUR1492,73
53
+
54
+ 3. SCT batch
55
+
56
+ ABNANL2A
57
+ 940
58
+ ABNANL2A
59
+ :20:ABN AMRO BANK NV
60
+ :25:123456789
61
+ :28:14901/1
62
+ :60F:D120525EUR1627,85
63
+ :61:1205280528D135,12N655NONREF
64
+ :86:/TRTP/SEPA BATCH/PREF/109180/NRTX/162
65
+ :62F:C120528EUR1492,73
66
+
67
+ 4. SCT batch salaris
68
+
69
+ ABNANL2A
70
+ 940
71
+ ABNANL2A
72
+ :20:ABN AMRO BANK NV
73
+ :25:123456789
74
+ :28:14901/1
75
+ :60F:D120525EUR1627,85
76
+ :61:1205280528D135,12N652NONREF
77
+ :86:/TRTP/SEPA BATCH SALARIS/PREF/109180/NRTX/162
78
+ :62F:C120528EUR1492,73
79
+
80
+ 5. SCT terugboeking (return en reject)
81
+
82
+ ABNANL2A
83
+ 940
84
+ ABNANL2A
85
+ :20:ABN AMRO BANK NV
86
+ :25:123456789
87
+ :28:12901/1
88
+ :60F:C120507EUR5596,98
89
+ :61:1205080508C1,01N657NONREF
90
+ :86:/TRTP/SEPA TERUGBOEKING/IBAN/NL89ABNA1234567890/BIC/ABNANL2A
91
+ /NAME/M. GRSSUGE/RTRN/MS02/REMI/DIT IS EEN TESTBETALING OM TE KIJ
92
+ KEN OF DE BIC WEL GOED AFGEVANGEN WORDT/EREF/TESTBIC
93
+ :62F:C120508EUR5597,99
94
+
95
+ 6a. SDD core batch incasso
96
+
97
+ ABNANL2A
98
+ 940
99
+ ABNANL2A
100
+ :20:ABN AMRO BANK NV
101
+ :25:123456789
102
+ :28:13901/1
103
+ :60F:C120517EUR1945,64
104
+ :61:1112061206C0,05N247NONREF
105
+ :86:/TRTP/SEPA Incasso batch/PREF/109180-911/NRTX/162/PIND/BRUTO
106
+ :62F:C120518EUR1945,69
107
+
108
+ 6b. SDD B2B batch incasso
109
+
110
+ ABNANL2A
111
+ 940
112
+ ABNANL2A
113
+ :20:ABN AMRO BANK NV
114
+ :25:123456789
115
+ :28:13901/1
116
+ :60F:C120517EUR1945,64
117
+ :61:1112061206C0,05N249NONREF
118
+ :86:/TRTP/SEPA Incasso batch bedrijven/PREF/109180-911/NRTX/162
119
+ /PIND/BRUTO
120
+ :62F:C120518EUR1945,69
121
+
122
+ 7. SDD core incasso (geincasseerde)
123
+
124
+ ABNANL2A
125
+ 940
126
+ ABNANL2A
127
+ :20:ABN AMRO BANK NV
128
+ :25:123456789
129
+ :28:13901/1
130
+ :60F:C120517EUR1945,64
131
+ :61:1204240424D233,77N248NONREF
132
+ :86:/TRTP/SEPA Incasso algemeen doorlopend/CSID/NL13ZZZ0606057800
133
+ 00/NAME/Electriciteit leverancier BV/MARF/123456789XXmandaat
134
+ /REMI/Levering maand mei, zie nota 1234556. Uw klantnummer 123455
135
+ 666/IBAN/NL66ABNA1234567890/BIC/ABNANL2A/EREF/1234567X908303803
136
+ :62F:C120518EUR1711,87
137
+
138
+ 8a. SDD core incasso (Return)
139
+
140
+ ABNANL2A
141
+ 940
142
+ ABNANL2A
143
+ :20:ABN AMRO BANK NV
144
+ :25:123456789
145
+ :28:13901/1
146
+ :60F:C120517EUR1945,64
147
+ :61:1112081208D0,05N245NONREF
148
+ :86:/RTYP/SEPA Incasso Terugboeking/MARF/123456789XXmandaat/RTRN/
149
+ AC01/IBAN/ NL71ABNA1234567890/NAME/Debtor waarop geincasseeerd wo
150
+ rdt/REMI/Levering maand mei, zie nota 1234556. Uw klantnummer 123
151
+ 455666/EREF/1234567X908303803
152
+ :62F:C120518EUR1945,59
153
+
154
+ 8b. SDD core incasso (Reject)
155
+
156
+ ABNANL2A
157
+ 940
158
+ ABNANL2A
159
+ :20:ABN AMRO BANK NV
160
+ :25:123456789
161
+ :28:13901/1
162
+ :60F:C120517EUR1945,64
163
+ :61:1112081208D0,06N246NONREF
164
+ :86:/RTYP/SEPA Incasso niet uitgevoerd/MARF/123456789XXmandaat
165
+ /RTRN/MS02/IBAN/NL71ABNA1234567890/NAME/Debtor waarop geincasseee
166
+ rd wordt/REMI/Levering maand mei, zie nota 1234556. Uw klantnumme
167
+ r 123455666/EREF/1234567X908303803
168
+ :62F:C120518EUR1945,58
169
+
170
+ 9. SCT iDEAL
171
+
172
+ ABNANL2A
173
+ 940
174
+ ABNANL2A
175
+ :20:ABN AMRO BANK NV
176
+ :25:123456789
177
+ :28:13901/1
178
+ :60F:C120517EUR1945,64
179
+ :61:1209270927C0,01N944NONREF
180
+ :86:/TRTP/IDEAL/IBAN/NL73ANDL0123456789/BIC/ANDLNL20/NAME/NAAMXXX
181
+ 1/REMI/0030000322071306 18-06-12 21*09/EREF/INNDNL2U200001001348
182
+ 24001/ORDP//ID/1234567890AQCDEFGHIJ1234567890KLMNO
183
+ :62F:C120518EUR1945,65
184
+
185
+ 10a. SCT IBAN Acceptgiro (debet)
186
+
187
+ ABNANL2A
188
+ 940
189
+ ABNANL2A
190
+ :20:ABN AMRO BANK NV
191
+ :25:123456789
192
+ :28:13501/1
193
+ :60F:C120511EUR5138,61
194
+ :61:1205120514d1,01N946NONREF
195
+ :86:/TRTP/SEPA ACCEPTGIRO/IBAN/NL02ABNA0123456789/BIC/ABNANL2A
196
+ /NAME/Naam/REMI/Issuer: CUR Ref: 1234567812345678/EREF/NOTPROVIDE
197
+ D
198
+ :62F:C120514EUR5137,60
199
+
200
+ 10b. SCT IBAN Acceptgiro (credit)
201
+
202
+ ABNANL2A
203
+ 940
204
+ ABNANL2A
205
+ :20:ABN AMRO BANK NV
206
+ :25:123456789
207
+ :28:13501/1
208
+ :60F:C120511EUR5138,61
209
+ :61:1205120514C500,01N946NONREF
210
+ :86:/TRTP/SEPA ACCEPTGIRO/IBAN/NL02ABNA0123456789/BIC/ABNANL2A
211
+ /NAME/Naam/REMI/Issuer: CUR Ref: 1234567812345678/EREF/NOTPROVIDE
212
+ D
213
+ :62F:C120514EUR5538,62
Binary file
@@ -1,29 +1,31 @@
1
1
  class MT940::Abnamro < MT940::Base
2
2
 
3
- def self.determine_bank(*args)
4
- self if args[0].match(/ABNANL/)
5
- end
3
+ private
6
4
 
7
5
  def parse_tag_61
8
- if @line.match(/^:61:(\d{6})\d{4}(C|D)(\d+),(\d{0,2})/)
9
- type = $2 == 'D' ? -1 : 1
10
- @transaction = MT940::Transaction.new(:bank_account => @bank_account, :amount => type * ($3 + '.' + $4).to_f, :bank => @bank, :currency => @currency)
11
- @transaction.date = parse_date($1)
12
- @transactions << @transaction
13
- @tag86 = false
14
- end
6
+ super(/^:61:(\d{6})\d{4}(C|D)(\d+),(\d{0,2})/)
15
7
  end
16
8
 
17
- def parse_contra_account
18
- if @transaction
19
- if @transaction.description.match(/^(GIRO)\s+(\d+)(.+)/)
20
- @transaction.contra_account = $2.rjust(9, '000000000')
21
- @transaction.description = $3
22
- elsif @transaction.description.match(/^(\d{2}.\d{2}.\d{2}.\d{3})(.+)/)
23
- @transaction.description = $2
24
- @transaction.contra_account = $1.gsub('.','')
25
- end
9
+ def parse_line_before_sepa
10
+ @description = @line.gsub(/>\d{2}/,'').strip
11
+ if @description.match(/^(GIRO)\s+(\d+)(.+)/)
12
+ @contra_account = $2.rjust(9, '000000000')
13
+ @description = $3
14
+ elsif @description.match(/^(\d{2}.\d{2}.\d{2}.\d{3})(.+)/)
15
+ @description = $2
16
+ @contra_account = $1.gsub('.','')
26
17
  end
27
18
  end
28
19
 
20
+ def parse_line_after_sepa
21
+ hash = hashify_description(@line)
22
+ @description = hash['REMI']
23
+ @contra_account = hash['IBAN']
24
+ end
25
+
26
+ def sepa?
27
+ @line[0] == '/'
28
+ end
29
+
29
30
  end
31
+
@@ -1,14 +1,24 @@
1
1
  class MT940::Ing < MT940::Base
2
2
 
3
- def self.determine_bank(*args)
4
- self if args[0].match(/INGBNL/)
3
+ private
4
+
5
+ def parse_line_before_sepa
6
+ pattern = Regexp.new "(#{MT940::BBAN_PATTERN})(.+)"
7
+ if @line.match(pattern)
8
+ @description = $2.strip
9
+ @contra_account = $1[/[^0+]\d*/]
10
+ end
11
+ end
12
+
13
+ def parse_line_after_sepa
14
+ if @line.match(MT940::SEPA_PATTERN)
15
+ @contra_account = $2
16
+ @description = $4.strip
17
+ end
5
18
  end
6
19
 
7
- def parse_contra_account
8
- if @transaction && @transaction.description.match(/^\d(\d{9})(.+)/)
9
- @transaction.contra_account = $1
10
- @transaction.description = $2
11
- end
20
+ def sepa?
21
+ @line.match(MT940::SEPA_PATTERN)
12
22
  end
13
23
 
14
24
  end
@@ -1,28 +1,51 @@
1
1
  class MT940::Rabobank < MT940::Base
2
2
 
3
- def self.determine_bank(*args)
4
- self if args[0].match(/^:940:/)
3
+ private
4
+
5
+ def parse_tag_25
6
+ @line.gsub!('.','')
7
+ if @line.match(Regexp.new ":25:(#{MT940::IBAN_PATTERN})")
8
+ @bank_account = $1
9
+ @sepa = true
10
+ else
11
+ @bank_account = $1.gsub(/^0/,'') if @line.match(/^:\d{2}:[^\d]*(\d*)/)
12
+ end
5
13
  end
6
14
 
7
15
  def parse_tag_61
8
- if @line.match(/^:61:(\d{6})(C|D)(\d+),(\d{0,2})\w{4}\w{1}(\d{9}|NONREF)(.+)$/)
9
- type = $2 == 'D' ? -1 : 1
10
- @transaction = MT940::Transaction.new(:bank_account => @bank_account, :amount => type * ($3 + '.' + $4).to_f, :bank => @bank, :currency => @currency)
11
- @transaction.date = parse_date($1)
12
- @transaction.contra_account = $5.strip
13
- @transaction.contra_account_owner = $6.strip
14
- @transactions << @transaction
16
+ match = super(/^:61:(\d{6})(C|D)(\d+),(\d{0,2})\w{4}\w{1}(\d{9}|NONREF|EREF)(.*)$/)
17
+ if match
18
+ if @sepa
19
+ @transaction.contra_account = match[6].strip
20
+ else
21
+ @transaction.contra_account = match[5].strip
22
+ @transaction.contra_account_owner = match[6].strip
23
+ end
15
24
  end
16
25
  end
17
26
 
18
27
  def parse_tag_86
19
28
  if @line.match(/^:86:(.*)$/)
20
- if @transaction.description.nil?
21
- @transaction.description= $1.strip
22
- else
23
- @transaction.description += ' ' +$1.strip
24
- end
29
+ @line = $1.strip
30
+ @sepa ? determine_description_after_sepa : determine_description_before_sepa
31
+ @transaction.description = @description.strip
25
32
  end
26
33
  end
27
34
 
28
- end
35
+ def determine_description_before_sepa
36
+ if @description.nil?
37
+ @description = @line
38
+ else
39
+ @description += ' ' + @line
40
+ end
41
+ end
42
+
43
+ def determine_description_after_sepa
44
+ hash = hashify_description(@line)
45
+ @description = ''
46
+ @description += hash['NAME'] if hash['NAME']
47
+ @description += ' '
48
+ @description += hash['REMI'] if hash['REMI']
49
+ end
50
+
51
+ end
@@ -1,14 +1,45 @@
1
1
  class MT940::Triodos < MT940::Base
2
2
 
3
- def self.determine_bank(*args)
4
- self if args[0].match(/^:20:/) && args[1] && args[1].match(/^:25:TRIODOSBANK/)
3
+ private
4
+
5
+ def parse_tag_86
6
+ if @line.match(/^:86:000(.*)$/)
7
+ processed_description = hashify_description($1)
8
+
9
+ @transaction.contra_account = if sepa?(processed_description)
10
+ processed_description['21']
11
+ else
12
+ processed_description['10'][/[^0+]\d*/]
13
+ end
14
+
15
+ @transaction.description = extract_description(processed_description)
16
+ end
17
+ end
18
+
19
+ def bic_code?(text)
20
+ MT940::BIC_CODES.values.include?(text)
5
21
  end
6
22
 
7
- def parse_contra_account
8
- if @transaction && @transaction.description.match(/\d+(\d{9})$/)
9
- @transaction.contra_account = $1.rjust(9, '000000000')
10
- @transaction.description = ''
23
+ def extract_description(text)
24
+ identifier = sepa?(text) ? 22 : 20
25
+ description = ''
26
+ text.each do |k,v|
27
+ description += v if k.to_i >= identifier && k.to_i < 30
11
28
  end
29
+ description
30
+ end
31
+
32
+ def hashify_description(description)
33
+ hash = {}
34
+ description.split('>').compact.each do |slice|
35
+ next if slice.empty?
36
+ hash[slice[0..1]] = slice[2..-1]
37
+ end
38
+ hash
39
+ end
40
+
41
+ def sepa?(text)
42
+ text['10'] == '0000000000'
12
43
  end
13
44
 
14
- end
45
+ end