mt940_parser 1.0.5 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/.travis.yml +9 -0
- data/VERSION.yml +4 -4
- data/lib/mt940/customer_statement_message.rb +15 -16
- data/lib/mt940.rb +35 -35
- data/mt940_parser.gemspec +6 -4
- data/test/test_mt940.rb +2 -11
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90c03b829e6f715e6255c89672b49fb07564e6de
|
4
|
+
data.tar.gz: 838b67c8f1e8552b2118fe2e224fb24c6f26341e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d062a25b89d4d24d9eaaea209630c4b2cec15b6929e5c17ea35401875c0d87fe89d227b1073c8a82af03402af42a90f4ca60007c00ee7456d7049ab3295a50e2
|
7
|
+
data.tar.gz: ff659e396c6f01a5978b27e513b2254bd6e93cf40d10c2bc9fd08bf82a9b00643dacbcddc11f3780eb83349bed90b682af8c971162fd4ac781afd7ad41dc771d
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p594
|
data/.travis.yml
ADDED
data/VERSION.yml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
---
|
1
|
+
---
|
2
2
|
:major: 1
|
3
|
-
:minor:
|
4
|
-
:
|
5
|
-
:
|
3
|
+
:minor: 2
|
4
|
+
:patch: 0
|
5
|
+
:build:
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# this is a beautification wrapper around the low-level
|
2
|
-
# MT940.parse command. use it in order to make dealing with
|
2
|
+
# MT940.parse command. use it in order to make dealing with
|
3
3
|
# the data easier
|
4
4
|
class MT940
|
5
5
|
|
@@ -10,20 +10,19 @@ class MT940
|
|
10
10
|
def self.parse_file(file)
|
11
11
|
self.parse(File.read(file))
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def self.parse(data)
|
15
15
|
messages = MT940.parse(data)
|
16
16
|
messages.map { |msg| new(msg) }
|
17
17
|
end
|
18
|
-
|
19
|
-
def initialize(
|
20
|
-
@
|
21
|
-
@account = @raw.find { |line| line.class == MT940::Account }
|
18
|
+
|
19
|
+
def initialize(lines)
|
20
|
+
@account = lines.find { |line| line.class == MT940::Account }
|
22
21
|
@statement_lines = []
|
23
|
-
|
22
|
+
lines.each_with_index do |line, i|
|
24
23
|
next unless line.class == MT940::StatementLine
|
25
|
-
ensure_is_info_line!(
|
26
|
-
@statement_lines << StatementLineBundle.new(
|
24
|
+
ensure_is_info_line!(lines[i+1])
|
25
|
+
@statement_lines << StatementLineBundle.new(lines[i], lines[i+1])
|
27
26
|
end
|
28
27
|
end
|
29
28
|
|
@@ -35,16 +34,16 @@ class MT940
|
|
35
34
|
@account.account_number
|
36
35
|
end
|
37
36
|
|
38
|
-
private
|
39
|
-
|
37
|
+
private
|
38
|
+
|
40
39
|
def ensure_is_info_line!(line)
|
41
40
|
unless line.is_a?(MT940::StatementLineInformation)
|
42
|
-
raise StandardError, "Unexpected Structure; expected StatementLineInformation, but was #{line.class}"
|
41
|
+
raise StandardError, "Unexpected Structure; expected StatementLineInformation, but was #{line.class}"
|
43
42
|
end
|
44
43
|
end
|
45
|
-
|
44
|
+
|
46
45
|
end
|
47
|
-
|
46
|
+
|
48
47
|
class StatementLineBundle
|
49
48
|
|
50
49
|
METHOD_MAP = {
|
@@ -71,5 +70,5 @@ class MT940
|
|
71
70
|
end
|
72
71
|
|
73
72
|
end
|
74
|
-
|
75
|
-
end
|
73
|
+
|
74
|
+
end
|
data/lib/mt940.rb
CHANGED
@@ -3,10 +3,10 @@ require 'mt940/customer_statement_message'
|
|
3
3
|
class MT940
|
4
4
|
class Field
|
5
5
|
attr_reader :modifier, :content
|
6
|
-
|
6
|
+
|
7
7
|
DATE = /(\d{2})(\d{2})(\d{2})/
|
8
8
|
SHORT_DATE = /(\d{2})(\d{2})/
|
9
|
-
|
9
|
+
|
10
10
|
class << self
|
11
11
|
|
12
12
|
def for(line)
|
@@ -24,16 +24,16 @@ class MT940
|
|
24
24
|
'65' => FutureValutaBalance,
|
25
25
|
'86' => StatementLineInformation
|
26
26
|
}[number]
|
27
|
-
|
27
|
+
|
28
28
|
raise StandardError, "Field #{number} is not implemented" unless klass
|
29
|
-
|
29
|
+
|
30
30
|
klass.new(modifier, content)
|
31
31
|
else
|
32
32
|
raise StandardError, "Wrong line format: #{line.dump}"
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def initialize(modifier, content)
|
38
38
|
@modifier = modifier
|
39
39
|
parse_content(content)
|
@@ -42,14 +42,14 @@ class MT940
|
|
42
42
|
private
|
43
43
|
def parse_amount_in_cents(amount)
|
44
44
|
# don't use Integer(amount) function, because amount can be "008" - interpreted as octal number ("010" = 8)
|
45
|
-
amount.gsub(',', '').to_i
|
45
|
+
amount.gsub(',', '').to_i
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
def parse_date(date)
|
49
49
|
date.match(DATE)
|
50
50
|
Date.new("20#{$1}".to_i, $2.to_i, $3.to_i)
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
def parse_entry_date(raw_entry_date, value_date)
|
54
54
|
raw_entry_date.match(SHORT_DATE)
|
55
55
|
entry_date = Date.new(value_date.year, $1.to_i, $2.to_i)
|
@@ -63,16 +63,16 @@ class MT940
|
|
63
63
|
# 20
|
64
64
|
class Job < Field
|
65
65
|
attr_reader :reference
|
66
|
-
|
66
|
+
|
67
67
|
def parse_content(content)
|
68
68
|
@reference = content
|
69
69
|
end
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
72
|
# 21
|
73
73
|
class Reference < Job
|
74
74
|
end
|
75
|
-
|
75
|
+
|
76
76
|
# 25
|
77
77
|
class Account < Field
|
78
78
|
attr_reader :bank_code, :account_number, :account_currency
|
@@ -84,13 +84,13 @@ class MT940
|
|
84
84
|
@bank_code, @account_number, @account_currency = $1, $2, $3
|
85
85
|
end
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
# 28
|
89
89
|
class Statement < Field
|
90
90
|
attr_reader :number, :sheet
|
91
91
|
|
92
92
|
CONTENT = /^(0|(\d{5,5})\/(\d{2,5}))$/
|
93
|
-
|
93
|
+
|
94
94
|
def parse_content(content)
|
95
95
|
content.match(CONTENT)
|
96
96
|
if $1 == '0'
|
@@ -100,13 +100,13 @@ class MT940
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
end
|
103
|
-
|
103
|
+
|
104
104
|
# 60
|
105
105
|
class AccountBalance < Field
|
106
106
|
attr_reader :balance_type, :sign, :currency, :amount, :date
|
107
107
|
|
108
108
|
CONTENT = /^(C|D)(\w{6})(\w{3})(\d{1,12},\d{0,2})$/
|
109
|
-
|
109
|
+
|
110
110
|
def parse_content(content)
|
111
111
|
content.match(CONTENT)
|
112
112
|
|
@@ -116,27 +116,27 @@ class MT940
|
|
116
116
|
when 'M'
|
117
117
|
:intermediate
|
118
118
|
end
|
119
|
-
|
119
|
+
|
120
120
|
@sign = case $1
|
121
121
|
when 'C'
|
122
122
|
:credit
|
123
123
|
when 'D'
|
124
124
|
:debit
|
125
125
|
end
|
126
|
-
|
126
|
+
|
127
127
|
raw_date = $2
|
128
128
|
@currency = $3
|
129
129
|
@amount = parse_amount_in_cents($4)
|
130
|
-
|
130
|
+
|
131
131
|
@date = case raw_date
|
132
132
|
when 'ALT', '0'
|
133
133
|
nil
|
134
134
|
when DATE
|
135
|
-
Date.new("20#{$1}".to_i, $2.to_i, $3.to_i)
|
135
|
+
Date.new("20#{$1}".to_i, $2.to_i, $3.to_i)
|
136
136
|
end
|
137
137
|
end
|
138
138
|
end
|
139
|
-
|
139
|
+
|
140
140
|
# 61
|
141
141
|
class StatementLine < Field
|
142
142
|
attr_reader :date, :entry_date, :funds_code, :amount, :swift_code, :reference, :transaction_description
|
@@ -145,7 +145,7 @@ class MT940
|
|
145
145
|
|
146
146
|
def parse_content(content)
|
147
147
|
content.match(CONTENT)
|
148
|
-
|
148
|
+
|
149
149
|
raw_date = $1
|
150
150
|
raw_entry_date = $2
|
151
151
|
@funds_code = case $3
|
@@ -158,25 +158,25 @@ class MT940
|
|
158
158
|
when 'RD'
|
159
159
|
:return_debit
|
160
160
|
end
|
161
|
-
|
161
|
+
|
162
162
|
@amount = parse_amount_in_cents($4)
|
163
163
|
@swift_code = $5
|
164
164
|
@reference = $6
|
165
165
|
@transaction_description = $7
|
166
|
-
|
166
|
+
|
167
167
|
@date = parse_date(raw_date)
|
168
168
|
@entry_date = parse_entry_date(raw_entry_date, @date) if raw_entry_date
|
169
169
|
end
|
170
|
-
|
170
|
+
|
171
171
|
def value_date
|
172
172
|
@date
|
173
173
|
end
|
174
174
|
end
|
175
|
-
|
175
|
+
|
176
176
|
# 62
|
177
177
|
class ClosingBalance < AccountBalance
|
178
178
|
end
|
179
|
-
|
179
|
+
|
180
180
|
# 64
|
181
181
|
class ValutaBalance < AccountBalance
|
182
182
|
end
|
@@ -184,23 +184,23 @@ class MT940
|
|
184
184
|
# 65
|
185
185
|
class FutureValutaBalance < AccountBalance
|
186
186
|
end
|
187
|
-
|
187
|
+
|
188
188
|
# 86
|
189
189
|
class StatementLineInformation < Field
|
190
|
-
attr_reader :code, :transaction_description, :prima_nota, :details, :bank_code, :account_number,
|
190
|
+
attr_reader :code, :transaction_description, :prima_nota, :details, :bank_code, :account_number,
|
191
191
|
:account_holder, :text_key_extension, :not_implemented_fields
|
192
|
-
|
192
|
+
|
193
193
|
def parse_content(content)
|
194
194
|
content.match(/^(\d{3})((.).*)$/)
|
195
195
|
@code = $1.to_i
|
196
|
-
|
196
|
+
|
197
197
|
details = []
|
198
198
|
account_holder = []
|
199
199
|
|
200
200
|
if seperator = $3
|
201
201
|
sub_fields = $2.scan(/#{Regexp.escape(seperator)}(\d{2})([^#{Regexp.escape(seperator)}]*)/)
|
202
|
-
|
203
|
-
|
202
|
+
|
203
|
+
|
204
204
|
sub_fields.each do |(code, content)|
|
205
205
|
case code.to_i
|
206
206
|
when 0
|
@@ -225,19 +225,19 @@ class MT940
|
|
225
225
|
end
|
226
226
|
end
|
227
227
|
end
|
228
|
-
|
228
|
+
|
229
229
|
@details = details.join("\n")
|
230
230
|
@account_holder = account_holder.join("\n")
|
231
231
|
end
|
232
232
|
end
|
233
|
-
|
233
|
+
|
234
234
|
|
235
235
|
class << self
|
236
236
|
def parse(text)
|
237
237
|
raise "Invalid encoding!" unless text.valid_encoding?
|
238
238
|
new_text = text.encode('utf-8').strip
|
239
239
|
new_text << "\r\n" if new_text[-1,1] == '-'
|
240
|
-
raw_sheets = new_text.split(/^-\r\n/).map { |sheet| sheet.gsub(/\r\n(?!:)/, '') }
|
240
|
+
raw_sheets = new_text.split(/^-\s*\r\n/).map { |sheet| sheet.gsub(/\r\n(?!:)/, '') }
|
241
241
|
sheets = raw_sheets.map { |raw_sheet| parse_sheet(raw_sheet) }
|
242
242
|
end
|
243
243
|
|
data/mt940_parser.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "mt940_parser"
|
8
|
-
s.version = "1.0
|
8
|
+
s.version = "1.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Thies C. Arntzen", "Phillip Oertel"]
|
12
|
-
s.date = "
|
12
|
+
s.date = "2015-01-07"
|
13
13
|
s.email = "developers@betterplace.org"
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"LICENSE",
|
@@ -17,7 +17,9 @@ Gem::Specification.new do |s|
|
|
17
17
|
]
|
18
18
|
s.files = [
|
19
19
|
".document",
|
20
|
+
".ruby-version",
|
20
21
|
".specification",
|
22
|
+
".travis.yml",
|
21
23
|
"Gemfile",
|
22
24
|
"Gemfile.lock",
|
23
25
|
"LICENSE",
|
@@ -58,11 +60,11 @@ Gem::Specification.new do |s|
|
|
58
60
|
s.homepage = "http://github.com/betterplace/mt940_parser"
|
59
61
|
s.licenses = ["MIT"]
|
60
62
|
s.require_paths = ["lib"]
|
61
|
-
s.rubygems_version = "
|
63
|
+
s.rubygems_version = "2.0.14"
|
62
64
|
s.summary = "MT940 parses account statements in the SWIFT MT940 format."
|
63
65
|
|
64
66
|
if s.respond_to? :specification_version then
|
65
|
-
s.specification_version =
|
67
|
+
s.specification_version = 4
|
66
68
|
|
67
69
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
68
70
|
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
data/test/test_mt940.rb
CHANGED
@@ -6,18 +6,9 @@ YAML::ENGINE.yamler = 'psych'
|
|
6
6
|
# $DEBUG = true
|
7
7
|
class TestMt940 < Test::Unit::TestCase
|
8
8
|
|
9
|
-
def read_mt940_data(file)
|
10
|
-
MT940.parse(IO.read(file))
|
11
|
-
end
|
12
|
-
|
13
|
-
def writeout(name, data)
|
14
|
-
|
15
|
-
File.open(name, "w") { |f| f.write data }
|
16
|
-
end
|
17
|
-
|
18
9
|
def test_it_should_parse_fixture_files_correctly
|
19
10
|
Dir[File.dirname(__FILE__) + "/fixtures/*.txt"].reject { |f| f =~ /sepa_snippet/ }.each do |file|
|
20
|
-
data =
|
11
|
+
data = MT940.parse(IO.read(file))
|
21
12
|
generated_structure_file = file.gsub(/.txt$/, ".yml")
|
22
13
|
|
23
14
|
assert_equal YAML::load_file(generated_structure_file).to_yaml, data.to_yaml
|
@@ -33,4 +24,4 @@ class TestMt940 < Test::Unit::TestCase
|
|
33
24
|
end
|
34
25
|
end
|
35
26
|
|
36
|
-
end
|
27
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mt940_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thies C. Arntzen
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-01-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -62,7 +62,9 @@ extra_rdoc_files:
|
|
62
62
|
- README.rdoc
|
63
63
|
files:
|
64
64
|
- .document
|
65
|
+
- .ruby-version
|
65
66
|
- .specification
|
67
|
+
- .travis.yml
|
66
68
|
- Gemfile
|
67
69
|
- Gemfile.lock
|
68
70
|
- LICENSE
|
@@ -119,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
121
|
version: '0'
|
120
122
|
requirements: []
|
121
123
|
rubyforge_project:
|
122
|
-
rubygems_version: 2.
|
124
|
+
rubygems_version: 2.0.14
|
123
125
|
signing_key:
|
124
126
|
specification_version: 4
|
125
127
|
summary: MT940 parses account statements in the SWIFT MT940 format.
|