zilverline-mt940 1.0 → 2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/Gemfile.lock +1 -1
- data/README.md +5 -4
- data/lib/mt940.rb +1 -6
- data/lib/mt940/version.rb +2 -2
- data/lib/mt940_structured/file_content.rb +46 -0
- data/lib/mt940_structured/header.rb +30 -0
- data/lib/mt940_structured/mt940_structured.rb +8 -0
- data/lib/mt940_structured/parser.rb +18 -0
- data/lib/mt940_structured/parsers/abnamro/abnamro.rb +5 -0
- data/lib/mt940_structured/parsers/abnamro/parser.rb +15 -0
- data/lib/mt940_structured/parsers/abnamro/transaction_parser.rb +55 -0
- data/lib/mt940_structured/parsers/balance_parser.rb +12 -0
- data/lib/mt940_structured/parsers/bank_statement_parser.rb +59 -0
- data/lib/mt940_structured/parsers/base.rb +34 -0
- data/lib/mt940_structured/parsers/date_parser.rb +7 -0
- data/lib/mt940_structured/parsers/default_line61_parser.rb +25 -0
- data/lib/mt940_structured/parsers/iban_support.rb +15 -0
- data/lib/mt940_structured/parsers/ing/ing.rb +7 -0
- data/lib/mt940_structured/parsers/ing/parser.rb +18 -0
- data/lib/mt940_structured/parsers/ing/structured_transaction_parser.rb +50 -0
- data/lib/mt940_structured/parsers/ing/transaction_parser.rb +31 -0
- data/lib/mt940_structured/parsers/ing/types.rb +26 -0
- data/lib/mt940_structured/parsers/parsers.rb +16 -0
- data/lib/mt940_structured/parsers/rabobank/parser.rb +13 -0
- data/lib/mt940_structured/parsers/rabobank/rabobank.rb +8 -0
- data/lib/mt940_structured/parsers/rabobank/structured_transaction_parser.rb +41 -0
- data/lib/mt940_structured/parsers/rabobank/transaction_parser.rb +29 -0
- data/lib/mt940_structured/parsers/rabobank/types.rb +714 -0
- data/lib/mt940_structured/parsers/structured_description_parser.rb +12 -0
- data/lib/mt940_structured/parsers/tridios/parser.rb +14 -0
- data/lib/mt940_structured/parsers/tridios/transaction_parser.rb +23 -0
- data/lib/mt940_structured/parsers/tridios/triodos.rb +5 -0
- data/spec/fixtures/ing/eu_incasso.txt +17 -0
- data/spec/fixtures/ing/eu_incasso_foreign_transaction.txt +17 -0
- data/spec/fixtures/ing/failing.txt +18 -0
- data/spec/mt940_abnamro_spec.rb +18 -6
- data/spec/mt940_ing_spec.rb +78 -2
- data/spec/mt940_rabobank_spec.rb +11 -11
- data/spec/mt940_structured/file_content_spec.rb +77 -0
- data/spec/mt940_structured/header_spec.rb +32 -0
- data/spec/mt940_structured/parsers/rabobank/bank_statement_parser_spec.rb +32 -0
- data/spec/mt940_triodos_spec.rb +1 -1
- data/spec/mt940_two_accounts_spec.rb +1 -1
- metadata +41 -9
- data/lib/mt940/banks/abnamro.rb +0 -76
- data/lib/mt940/banks/ing.rb +0 -84
- data/lib/mt940/banks/rabobank.rb +0 -770
- data/lib/mt940/banks/triodos.rb +0 -20
- data/lib/mt940/base.rb +0 -165
- data/lib/mt940/structured_format.rb +0 -16
- data/spec/mt940_base_spec.rb +0 -48
data/lib/mt940/banks/triodos.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
class MT940::Triodos < MT940::Base
|
2
|
-
|
3
|
-
def self.determine_bank(*args)
|
4
|
-
self if args[0].match(/^:20:/) && args[1] && args[1].match(/^:25:TRIODOSBANK/)
|
5
|
-
end
|
6
|
-
|
7
|
-
def parse_tag_86
|
8
|
-
if !@tag86 && @line.match(/^:86:\s?(.*)\Z/m)
|
9
|
-
@tag86 = true
|
10
|
-
temp_description = $1.gsub(/\n/, ' ').gsub(/>\d{2}/, '').strip
|
11
|
-
if temp_description.match(/^\d+(\d{9})(.*)$/)
|
12
|
-
@transaction.contra_account = $1.rjust(9, '000000000')
|
13
|
-
@transaction.description = $2.strip
|
14
|
-
else
|
15
|
-
@transaction.description = temp_description
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
data/lib/mt940/base.rb
DELETED
@@ -1,165 +0,0 @@
|
|
1
|
-
module MT940
|
2
|
-
|
3
|
-
class Base
|
4
|
-
|
5
|
-
MT_940_TAG_LINE = /^:(\d{2}(F|C)?):/
|
6
|
-
|
7
|
-
attr_accessor :bank, :opening_balance, :opening_date
|
8
|
-
|
9
|
-
def self.parse_mt940(file)
|
10
|
-
file = File.open(file) if file.is_a?(String)
|
11
|
-
if file.is_a?(File) || file.is_a?(Tempfile)
|
12
|
-
first_line = file.readline
|
13
|
-
second_line = file.readline unless file.eof?
|
14
|
-
klass = determine_bank(first_line, second_line)
|
15
|
-
file.rewind
|
16
|
-
instance = klass.new(file)
|
17
|
-
file.close
|
18
|
-
instance.parse
|
19
|
-
else
|
20
|
-
raise ArgumentError.new('No file is given!')
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def parse
|
25
|
-
@tag86 = false
|
26
|
-
@lines.each do |line|
|
27
|
-
@line = line
|
28
|
-
@line.match(MT_940_TAG_LINE) ? send("parse_tag_#{$1}".to_sym) : parse_line
|
29
|
-
end
|
30
|
-
@bank_statements
|
31
|
-
end
|
32
|
-
|
33
|
-
protected
|
34
|
-
def parse_description_after_tag(description_parts, tag)
|
35
|
-
description_start_index = description_parts.index { |part| part == tag }
|
36
|
-
if description_start_index
|
37
|
-
description_parts[description_start_index + 1].gsub(/\r|\n/, '')
|
38
|
-
else
|
39
|
-
''
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def self.determine_bank(*args)
|
47
|
-
Dir.foreach(File.dirname(__FILE__) + '/banks/') do |file|
|
48
|
-
if file.match(/\.rb$/)
|
49
|
-
klass = eval(file.gsub(/\.rb$/, '').capitalize)
|
50
|
-
bank = klass.determine_bank(*args)
|
51
|
-
return bank if bank
|
52
|
-
end
|
53
|
-
end
|
54
|
-
self
|
55
|
-
end
|
56
|
-
|
57
|
-
def initialize(file)
|
58
|
-
@bank_statements = {}
|
59
|
-
@transactions = []
|
60
|
-
@bank = self.class.to_s.split('::').last
|
61
|
-
@bank = 'Unknown' if @bank == 'Base'
|
62
|
-
temp_lines = file.readlines
|
63
|
-
@lines = []
|
64
|
-
index_of_temp_lines = 0
|
65
|
-
index_in_lines = 0
|
66
|
-
while index_of_temp_lines < temp_lines.size do
|
67
|
-
line = temp_lines[index_of_temp_lines].encode('UTF-8', 'binary', :invalid => :replace, :undef => :replace) # remove invalid chars
|
68
|
-
if mt_940_start_line?(line)
|
69
|
-
@lines << line
|
70
|
-
index_in_lines+=1
|
71
|
-
else
|
72
|
-
@lines[index_in_lines-1] += line
|
73
|
-
end
|
74
|
-
index_of_temp_lines+=1
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def mt_940_start_line?(line)
|
79
|
-
line.match /^:?\d{2}(\D?|\d?):?.*$/
|
80
|
-
end
|
81
|
-
|
82
|
-
def parse_tag_25
|
83
|
-
@line.gsub!('.', '')
|
84
|
-
case @line
|
85
|
-
when /^:\d{2}:NL/
|
86
|
-
@bank_account_iban = @line[4, 18]
|
87
|
-
@bank_account = @bank_account_iban.strip.split(//).last(10).join.sub(/^[0]*/,"")
|
88
|
-
@is_structured_format = true
|
89
|
-
when /^:\d{2}:\D*(\d*)/
|
90
|
-
@bank_account = $1.gsub(/\D/, '').gsub(/^0+/, '')
|
91
|
-
@is_structured_format = false
|
92
|
-
else
|
93
|
-
raise "Unknown format for tag 25: #{@line}"
|
94
|
-
end
|
95
|
-
@bank_statements[@bank_account] ||= []
|
96
|
-
@tag86 = false
|
97
|
-
end
|
98
|
-
|
99
|
-
def parse_tag_28
|
100
|
-
@bank_statement = BankStatement.new([], @bank_account, @bank_account_iban, 0, nil, nil)
|
101
|
-
@bank_statements[@bank_account] << @bank_statement
|
102
|
-
end
|
103
|
-
|
104
|
-
alias_method :parse_tag_28C, :parse_tag_28
|
105
|
-
|
106
|
-
def parse_tag_60F
|
107
|
-
@currency = @line[12..14]
|
108
|
-
balance_date = parse_date(@line[6..11])
|
109
|
-
|
110
|
-
type = @line[5] == 'D' ? -1 : 1
|
111
|
-
amount = @line[15..-1].gsub(",", ".").to_f * type
|
112
|
-
@bank_statement.previous_balance = Balance.new(amount, balance_date, @currency)
|
113
|
-
end
|
114
|
-
|
115
|
-
def parse_tag_62F
|
116
|
-
@currency = @line[12..14]
|
117
|
-
balance_date = parse_date(@line[6..11])
|
118
|
-
|
119
|
-
type = @line[5] == 'D' ? -1 : 1
|
120
|
-
amount = @line[15..-1].gsub(",", ".").to_f * type
|
121
|
-
|
122
|
-
@bank_statement.new_balance = Balance.new(amount, balance_date, @currency)
|
123
|
-
@tag86 = false
|
124
|
-
end
|
125
|
-
|
126
|
-
def parse_tag_61
|
127
|
-
if @line.match(/^:61:(\d{6})(C|D)(\d+),(\d{0,2})/)
|
128
|
-
type = $2 == 'D' ? -1 : 1
|
129
|
-
@transaction = MT940::Transaction.new(:bank_account => @bank_account, :amount => type * ($3 + '.' + $4).to_f, :bank => @bank, :currency => @currency)
|
130
|
-
@transaction.date = parse_date($1)
|
131
|
-
@bank_statement.transactions << @transaction
|
132
|
-
@tag86 = false
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def parse_tag_86
|
137
|
-
if !@tag86 && @line.match(/^:86:\s?(.*)\Z/m)
|
138
|
-
@tag86 = true
|
139
|
-
@transaction.description = $1.gsub(/\n/, ' ').gsub(/>\d{2}/, '').strip
|
140
|
-
parse_contra_account
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def parse_line
|
145
|
-
if @tag86 && @transaction.description
|
146
|
-
@transaction.description.lstrip!
|
147
|
-
@transaction.description += ' ' + @line.gsub(/\n/, ' ').gsub(/>\d{2}\s*/, '').gsub(/\-XXX/, '').gsub(/-$/, '').strip
|
148
|
-
@transaction.description.strip!
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def parse_date(string)
|
153
|
-
Date.new(2000 + string[0..1].to_i, string[2..3].to_i, string[4..5].to_i) if string
|
154
|
-
end
|
155
|
-
|
156
|
-
def parse_contra_account
|
157
|
-
end
|
158
|
-
|
159
|
-
#Fail silently
|
160
|
-
def method_missing(*args)
|
161
|
-
end
|
162
|
-
|
163
|
-
end
|
164
|
-
|
165
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module MT940::StructuredFormat
|
2
|
-
def parse_line
|
3
|
-
super unless @skip_parse_line
|
4
|
-
end
|
5
|
-
|
6
|
-
def read_all_description_lines!
|
7
|
-
@skip_parse_line = true
|
8
|
-
index = @lines.index(@line)
|
9
|
-
@lines[index+1..-1].each do |line|
|
10
|
-
break if line.match MT940::Base::MT_940_TAG_LINE
|
11
|
-
@transaction.description.lstrip!
|
12
|
-
@transaction.description += line.gsub(/\n/, '').gsub(/>\d{2}\s*/, '').gsub(/\-XXX/, '').gsub(/-$/, '').strip
|
13
|
-
@transaction.description.strip!
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
data/spec/mt940_base_spec.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
require_relative 'spec_helper'
|
2
|
-
|
3
|
-
describe "Base" do
|
4
|
-
|
5
|
-
context 'MT940::Base' do
|
6
|
-
it 'read the transactions with the filename of the MT940 file' do
|
7
|
-
file_name = File.dirname(__FILE__) + '/fixtures/ing.txt'
|
8
|
-
@transactions = MT940::Base.parse_mt940(file_name)["1234567"].flat_map(&:transactions)
|
9
|
-
@transactions.size.should == 6
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'read the transactions with the handle to the mt940 file itself' do
|
13
|
-
file_name = File.dirname(__FILE__) + '/fixtures/ing.txt'
|
14
|
-
file = File.open(file_name)
|
15
|
-
@transactions = MT940::Base.parse_mt940(file)["1234567"].flat_map(&:transactions)
|
16
|
-
@transactions.size.should == 6
|
17
|
-
end
|
18
|
-
|
19
|
-
#Tempfile is used by Paperclip, so the following will work:
|
20
|
-
#MT940::Base.transactions(@mt940_file.attachment.to_file)
|
21
|
-
it 'read the transactions with the handle of a Tempfile' do
|
22
|
-
file = Tempfile.new('temp')
|
23
|
-
file.write(':940:')
|
24
|
-
file.rewind
|
25
|
-
@transactions = MT940::Base.parse_mt940(file)
|
26
|
-
@transactions.size.should == 0
|
27
|
-
file.unlink
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'raise an exception if the file does not exist' do
|
31
|
-
file_name = File.dirname(__FILE__) + '/fixtures/123.txt'
|
32
|
-
expect {MT940::Base.parse_mt940(file_name)}.to raise_exception Errno::ENOENT
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'raise an ArgumentError if a wrong argument was given' do
|
36
|
-
expect {MT940::Base.parse_mt940(Hash.new)}.to raise_exception ArgumentError
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
context 'Unknown MT940 file' do
|
41
|
-
it 'return its bank' do
|
42
|
-
file_name = File.dirname(__FILE__) + '/fixtures/unknown.txt'
|
43
|
-
@transactions = MT940::Base.parse_mt940(file_name)["1234567"].flat_map(&:transactions)
|
44
|
-
@transactions.first.bank.should == 'Unknown'
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|