cremul-parser 0.0.1

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: 8c49cb69e363b32f6967ef41f5caa5084a242337
4
+ data.tar.gz: 076bd033930f02c267935e93a4f72e2a1262eece
5
+ SHA512:
6
+ metadata.gz: 8f648ff6cfd20bf0094adcebd51e32e9dfed926164fa0ba48e6d1fc0d5dd64a634eeed12e9b463cbbee1e2a124bddc3ccd28c76c83524cc473008b9f535a7e77
7
+ data.tar.gz: 1557e5ac8d18b8fc1c65e106534b9dcdf61ff03209264f9af020eb1401e5a5fca4feeec8e18c464c5d878b621bd1a903dc9148fa78b46bc42b5e00fc00899121
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ .ruby-version
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :development, :test do
4
+ gem 'rspec-core'
5
+ gem 'rspec-mocks'
6
+ gem 'minitest'
7
+ end
8
+
9
+ # Specify your gem's dependencies in cremul-parser.gemspec
10
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Per Spilling
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # cremul-parser gem
2
+
3
+ This is a simple parser for CREMUL payment transaction files written in Ruby. It parses
4
+ the CREMUL file (which should be in UTF-8 encoding) and creates a Ruby object structure
5
+ corresponding to the elements in the file.
6
+
7
+ ## References
8
+
9
+ Here are som useful references regarding the CREMUL file format:
10
+
11
+ 1. [CREMUL documentation from bsk.no](http://bsk.no/hovedmeny/gjeldende-standarder.aspx)
12
+ 2. [CREMUL documentation from truugo.com](http://www.truugo.com/edifact/d12b/cremul/)
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ gem 'cremul-parser'
19
+
20
+ And then execute:
21
+
22
+ $ bundle install
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install cremul-parser
27
+
28
+ ## Usage
29
+
30
+ See the `parser_test.rb` file.
31
+
32
+ ## Copyright
33
+
34
+ Copyright (c) 2014 Per Spilling (per@kodemaker.no). See LICENSE.txt for details.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'rake/testtask'
2
+ require 'bundler/gem_tasks'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ Rake::TestTask.new do |t|
7
+ Dir.chdir('test/unit')
8
+ t.libs << "test"
9
+ t.test_files = FileList['*_test.rb']
10
+ end
11
+
12
+ task :default => :test
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cremul/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "cremul-parser"
8
+ gem.version = Cremul::VERSION
9
+ gem.authors = ["Per Spilling"]
10
+ gem.email = ["per@kodemaker.no"]
11
+ gem.summary = %q{A Ruby parser for CREMUL payment transaction files.}
12
+ gem.description = %q{Based on the format specification from BSK: http://bsk.no/media/18244/CREMUL_BSK_v2_13_d96A-201112.pdf}
13
+ gem.homepage = "https://github.com/perspilling/cremul-parser"
14
+ gem.license = "MIT"
15
+
16
+ gem.files = `git ls-files -z`.split("\x0")
17
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_development_dependency 'bundler', '~> 1.5'
22
+ gem.add_development_dependency 'rake', '~> 10'
23
+ end
@@ -0,0 +1,27 @@
1
+ require_relative 'parser_helper'
2
+
3
+ class CremulHeader
4
+ include Cremul::ParserHelper
5
+
6
+ # bf_id : Beneficiary ID. When the beneficiary is an organization this will be the organzation-number,
7
+ # and when the beneficiary is a person this will be the SSN, aka personnummer (NO).
8
+
9
+ #attr_reader :header_segments, :msg_id, :created_date, :bf_id
10
+ attr_reader :msg_id, :created_date, :bf_id
11
+
12
+ # Expects an array with all segments in the CREMUL file
13
+ def initialize(segments)
14
+ #i = next_line_segment_index(segments, 0)
15
+ #@header_segments = {}
16
+ #segments[0,i].each {|s| @header_segments[s[0,3]] = s[3,s.size] }
17
+
18
+ @msg_id = segments[msg_id_segment_index(segments)]
19
+
20
+ d = segments[next_date_segment_index(segments, 0)].split(':')
21
+ @created_date = Date.parse(d[1])
22
+
23
+ nad = segments[next_nad_segment_index(segments, 0)]
24
+ @bf_id = nad.split('+')[2].to_i
25
+ end
26
+
27
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 'parser_helper'
2
+ require_relative 'cremul_reference'
3
+ require_relative 'cremul_money'
4
+ require_relative 'cremul_payment_tx'
5
+
6
+ # Represents a line item in a CREMUL payment transaction message. A line item may have
7
+ # 1-* child-elements with the individual payments. A line item will have an amount field
8
+ # which will be the sum of the amounts of the child payment items.
9
+ class CremulLine
10
+ include Cremul::ParserHelper
11
+
12
+ attr_reader :posting_date, :money, :reference, :bf_account_number, :transactions
13
+
14
+ def initialize(segments, line_segment_pos)
15
+ d = segments[next_date_segment_index(segments, line_segment_pos)].split(':')
16
+ @posting_date = Date.parse(d[1])
17
+
18
+ @reference = CremulReference.new(segments[next_ref_segment_index(segments, line_segment_pos)])
19
+ @money = CremulMoney.new(segments[next_amount_segment_index(segments, line_segment_pos)])
20
+ bf = segments[next_fii_bf_segment_index(segments, line_segment_pos)].split('+')
21
+ @bf_account_number = bf[2]
22
+
23
+ @transactions = []
24
+ n = number_of_transactions_in_line(segments, line_segment_pos)
25
+ tx_index = next_tx_sequence_segment_index(segments, line_segment_pos)
26
+ n.times do
27
+ @transactions << CremulPaymentTx.new(segments, tx_index)
28
+ tx_index = next_tx_sequence_segment_index(segments, tx_index+1)
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'cremul_header'
2
+ require_relative 'cremul_line'
3
+ require_relative 'parser_helper'
4
+
5
+ class CremulMessage
6
+ include Cremul::ParserHelper
7
+
8
+ attr_reader :header, :number_of_lines, :lines
9
+
10
+ def initialize(segments)
11
+ @header = CremulHeader.new(segments)
12
+ @lines = []
13
+ @number_of_lines = number_of_lines_in_message(segments)
14
+
15
+ # instantiate the line items
16
+ line_segment_pos = next_line_segment_index(segments, 0)
17
+ @number_of_lines.times do
18
+ @lines << CremulLine.new(segments, line_segment_pos)
19
+ line_segment_pos = next_line_segment_index(segments, line_segment_pos+1)
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'parser_helper'
2
+
3
+ class CremulMoney
4
+ include Cremul::ParserHelper
5
+
6
+ DEFAULT_CURRENCY = :NOK
7
+
8
+ attr_reader :amount, :currency
9
+
10
+ def initialize(money_segment)
11
+ m = money_segment.split(':')
12
+
13
+ @amount = m[1].to_f
14
+ if m.size == 3
15
+ @currency = m[2].to_sym
16
+ else
17
+ @currency = DEFAULT_CURRENCY
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,20 @@
1
+ require_relative 'parser_helper'
2
+
3
+ class CremulNameAndAddress
4
+ include Cremul::ParserHelper
5
+
6
+ # NAD+party-id+code+code+nad-line1+nad-line2+nad-line3+nad-line4+nad-line5
7
+
8
+ attr_reader :type, :nad_lines
9
+
10
+ def initialize(nad_segment)
11
+ s = nad_segment.split('+')
12
+ @type = s[1].to_sym
13
+
14
+ @nad_lines = []
15
+ 5.times do |i|
16
+ @nad_lines << s[i+4]
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,79 @@
1
+ require_relative 'parser_helper'
2
+ require_relative 'cremul_reference'
3
+ require_relative 'cremul_name_and_address'
4
+
5
+ # Represents an individual payment transaction (SEQ segment) i a CREMUL message
6
+ class CremulPaymentTx
7
+ include Cremul::ParserHelper
8
+
9
+ REF_TYPE_KID = 999 # Norwegian customer invoice reference
10
+ REF_TYPE_INVOICE_NUMBER = 380
11
+
12
+ attr_reader :posting_date, :money, :references, :invoice_ref_type, :invoice_ref, :free_text
13
+ attr_reader :payer_account_number, :payer_nad, :beneficiary_nad
14
+
15
+
16
+ def initialize(segments, tx_segment_pos)
17
+ s = segments[next_date_segment_index(segments, tx_segment_pos)].split(':')
18
+ @posting_date = Date.parse(s[1])
19
+
20
+ s = segments[next_fii_or_segment_index(segments, tx_segment_pos)].split('+')
21
+ @payer_account_number = s[2]
22
+
23
+ init_invoice_ref(segments, tx_segment_pos)
24
+ init_free_text(segments, tx_segment_pos)
25
+
26
+ @money = CremulMoney.new(segments[next_amount_segment_index(segments, tx_segment_pos)])
27
+
28
+ init_refs(segments, tx_segment_pos)
29
+ init_name_and_addresses(segments, tx_segment_pos)
30
+ end
31
+
32
+ private
33
+
34
+ def init_free_text(segments, tx_segment_pos)
35
+ i = payment_details_segment_index(segments, tx_segment_pos)
36
+ unless i.nil?
37
+ s = segments[i].split('+')
38
+ @free_text = s[s.size-1]
39
+ end
40
+ end
41
+
42
+ def init_invoice_ref(segments, tx_segment_pos)
43
+ i = doc_segment_index(segments, tx_segment_pos)
44
+ unless i.nil?
45
+ s = segments[doc_segment_index(segments, tx_segment_pos)].split('+')
46
+ @invoice_ref_type = ref_type(s[1])
47
+ @invoice_ref = s[2]
48
+ end
49
+ end
50
+
51
+ def init_name_and_addresses(segments, tx_segment_pos)
52
+ n = last_segment_index_in_tx(segments, tx_segment_pos)
53
+ nad_index = next_nad_segment_index(segments, tx_segment_pos)
54
+ while nad_index.nil? == false && nad_index <= n
55
+ nad = CremulNameAndAddress.new(segments[nad_index])
56
+ assign_nad(nad)
57
+ nad_index = next_nad_segment_index(segments, nad_index+1)
58
+ end
59
+ end
60
+
61
+ def init_refs(segments, tx_segment_pos)
62
+ @references = []
63
+ n = number_of_references_in_tx(segments, tx_segment_pos)
64
+ ref_segment_index = next_ref_segment_index(segments, tx_segment_pos)
65
+ n.times do
66
+ @references << CremulReference.new(segments[ref_segment_index])
67
+ ref_segment_index = next_ref_segment_index(segments, ref_segment_index+1)
68
+ end
69
+ end
70
+
71
+ def assign_nad(nad)
72
+ if nad.type == :PL
73
+ @payer_nad = nad
74
+ elsif nad.type == :BE
75
+ @beneficiary_nad = nad
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'parser_helper'
2
+
3
+
4
+ # Represents a reference (RFF) segment in a CREMUL message.
5
+ # Type may be:
6
+ # - :ACK - Bank reference - KID in Norwegian
7
+ # - :AII - Bank’s reference number allocated by the bank to different underlaying individual transactions
8
+ # - :CT - AutoGiro agreement ID
9
+ # - :ABO - Originator´s reference – for excample SWIFT reference senders bank
10
+ # - :ACD - Bank reference = Archive reference
11
+ # - :AEK - Payment order number
12
+ # - :AFO - Beneficiary’s reference
13
+ # - :AGN - Payer’s reference, aka 'egenref' (NO)
14
+ # - :AHK - Payer’s reference number, aka 'debetref' (NO)
15
+ # - :RA - Remittance advice number
16
+ # - :TBR - Reference number pre-advice
17
+ class CremulReference
18
+ include Cremul::ParserHelper
19
+
20
+ attr_reader :number, :type
21
+
22
+ def initialize(ref_segment)
23
+ s = ref_segment.split(':')
24
+ @number = s[1]
25
+ s = s[0].split('+')
26
+ @type = s[1].to_sym
27
+ end
28
+ end
@@ -0,0 +1,207 @@
1
+ module Cremul
2
+ module ParserHelper
3
+
4
+ ELEMENT_SEPARATOR = '+'
5
+ DATA_SEPARATOR = ':'
6
+ REPETITION_SEPARATOR = '*'
7
+ SEGMENT_DELIMITER = "'"
8
+
9
+ MESSAGE_HEADER = 'UNH'
10
+ MESSAGE_BEGIN = 'BGM'
11
+
12
+ LINE_ITEM_SEGMENT = 'LIN'
13
+ DATE_SEGMENT = 'DTM'
14
+ BUSINESS_FUNCTION_SEGMENT = 'BUS'
15
+
16
+ REFERENCE_SEGMENT = 'RFF'
17
+ NAME_ADDRESS_SEGMENT = 'NAD'
18
+ SEQUENCE_SEGMENT = 'SEQ'
19
+
20
+ FINANCIAL_INSTITUTION_SEGMENT = 'FII'
21
+ BENEFICIARY = 'BF'
22
+ # Example: FII+BF+12345678901' (Beneficiary bank account number)
23
+
24
+ MONETARY_AMOUNT_SEGMENT = 'MOA'
25
+ CURRENCIES_SEGMENT = 'CUX'
26
+
27
+ PROCESS_TYPE_SEGMENT = 'PRC'
28
+ FREETEXT_SEGMENT = 'FTX'
29
+
30
+ CODES = {
31
+ BUSINESS_FUNCTION_SEGMENT => {
32
+ c230: 'Total amount valid KID',
33
+ c231: 'Total amount invalid KID',
34
+ c232: 'Total amount AutoGiro',
35
+ c233: 'Total amount electronic payments',
36
+ c234: 'Total amount Giro notes',
37
+ c240: 'Total amount structured information'
38
+
39
+ },
40
+ MONETARY_AMOUNT_SEGMENT => {
41
+ c60: 'Final posted amount',
42
+ c346: 'Total credit. Sum of final posted amounts on level C',
43
+ c349: 'Amount that will be posted/amount not confirmed by bank',
44
+ c362: 'Amount for information – can be changed'
45
+ },
46
+ REFERENCE_SEGMENT => {
47
+ ACK: 'Bank reference - KID in Norwegian',
48
+ AII: 'Bank’s reference number allocated by the bank to different underlaying individual transactions',
49
+ CT: 'AutoGiro agreement ID'
50
+ }
51
+ }
52
+
53
+ REF_TYPES = {
54
+ c380: :INVOICE_NUMBER,
55
+ c381: :CREDIT_NOTE,
56
+ c999: :KID,
57
+ c998: :INVOICE_NUMBER
58
+ }
59
+
60
+ # Assumes that the parameter is a string with the code
61
+ def ref_type(type_number)
62
+ type_symbol = ('c' << type_number).to_sym
63
+ REF_TYPES[type_symbol]
64
+ end
65
+
66
+
67
+ def segment_codes
68
+ CODES
69
+ end
70
+
71
+ def msg_id_segment_index(segments)
72
+ segments.index { |x| /UNH.*/.match(x) }
73
+ end
74
+
75
+ def next_date_segment_index(segments, start_pos)
76
+ index = segments[start_pos, segments.size].index { |x| /DTM.*/.match(x) }
77
+ index += start_pos unless index.nil?
78
+ index
79
+ end
80
+
81
+ def next_amount_segment_index(segments, start_pos)
82
+ index = segments[start_pos, segments.size].index { |x| /MOA.*/.match(x) }
83
+ index += start_pos unless index.nil?
84
+ index
85
+ end
86
+
87
+ def next_line_segment_index(segments, start_pos)
88
+ index = segments[start_pos, segments.size].index { |x| /LIN\+\d/.match(x) }
89
+ index += start_pos unless index.nil?
90
+ index
91
+ end
92
+
93
+ def next_tx_sequence_segment_index(segments, start_pos)
94
+ index = segments[start_pos, segments.size].index { |x| /SEQ\+\+\d/.match(x) }
95
+ index += start_pos unless index.nil?
96
+ index
97
+ end
98
+
99
+ def doc_segment_index(segments, start_pos)
100
+ index = segments[start_pos, segments.size].index { |x| /DOC.*/.match(x) }
101
+ index += start_pos unless index.nil?
102
+ index
103
+ end
104
+
105
+ def payment_details_segment_index(segments, start_pos)
106
+ index = segments[start_pos, segments.size].index { |x| /FTX\+PMD.*/.match(x) }
107
+ index += start_pos unless index.nil?
108
+ index
109
+ end
110
+
111
+ # Optional segment with free text info regarding the payment
112
+ def payment_advice_segment_index(segments, start_pos)
113
+ index = segments[start_pos, segments.size].index { |x| /FTX\+AAG.*/.match(x) }
114
+ index += start_pos unless index.nil?
115
+ index
116
+ end
117
+
118
+ def next_ref_segment_index(segments, start_pos)
119
+ index = segments[start_pos, segments.size].index { |x| /RFF.*/.match(x) }
120
+ index += start_pos unless index.nil?
121
+ index
122
+ end
123
+
124
+ def next_fii_bf_segment_index(segments, start_pos)
125
+ index = segments[start_pos, segments.size].index { |x| /FII\+BF.*/.match(x) }
126
+ index += start_pos unless index.nil?
127
+ index
128
+ end
129
+
130
+ def next_fii_or_segment_index(segments, start_pos)
131
+ index = segments[start_pos, segments.size].index { |x| /FII\+OR.*/.match(x) }
132
+ index += start_pos unless index.nil?
133
+ index
134
+ end
135
+
136
+ def next_nad_segment_index(segments, start_pos)
137
+ index = segments[start_pos, segments.size].index { |x| /NAD.*/.match(x) }
138
+ index += start_pos unless index.nil?
139
+ index
140
+ end
141
+
142
+ def line_count_segment_index(segments)
143
+ segments.index { |x| /CNT\+LI:\d/.match(x) }
144
+ end
145
+
146
+ def number_of_lines_in_message(segments)
147
+ s = segments[line_count_segment_index(segments)]
148
+ s[s.index(':')+1, s.size].to_i
149
+ end
150
+
151
+ # Return the number of individual payments transactions in the line item. Expects 'line_segment_pos'
152
+ # to be the index of the current line item.
153
+ def number_of_transactions_in_line(segments, line_segment_pos)
154
+ n = 1 # there must be at least 1 payment tx
155
+ tx_pos = next_tx_sequence_segment_index(segments, line_segment_pos+1)
156
+ # search for payment tx items until next line item or end of message
157
+ next_line_index = next_line_segment_index(segments, line_segment_pos+1)
158
+ loop do
159
+ tx_pos = next_tx_sequence_segment_index(segments, tx_pos+1)
160
+ if tx_pos.nil?
161
+ break
162
+ elsif !next_line_index.nil? && tx_pos > next_line_index
163
+ break
164
+ else
165
+ n += 1
166
+ end
167
+ end
168
+ n
169
+ end
170
+
171
+ def number_of_references_in_tx(segments, tx_segment_pos)
172
+ n = 0
173
+ ref_index = next_ref_segment_index(segments, tx_segment_pos)
174
+ next_tx_index = next_tx_sequence_segment_index(segments, tx_segment_pos+1)
175
+ next_line_index = next_line_segment_index(segments, tx_segment_pos)
176
+ loop do
177
+ if ref_index.nil?
178
+ break
179
+ elsif !next_tx_index.nil? && ref_index > next_tx_index
180
+ break
181
+ elsif !next_line_index.nil? && ref_index > next_line_index
182
+ break
183
+ else
184
+ n += 1
185
+ end
186
+ ref_index = next_ref_segment_index(segments, ref_index+1)
187
+ end
188
+ n
189
+ end
190
+
191
+ def last_segment_index_in_tx(segments, tx_segment_pos)
192
+ next_tx_index = next_tx_sequence_segment_index(segments, tx_segment_pos+1)
193
+ next_line_index = next_line_segment_index(segments, tx_segment_pos)
194
+ cnt_index = line_count_segment_index(segments)
195
+
196
+ if next_tx_index
197
+ next_tx_index - 1
198
+ elsif next_line_index
199
+ next_line_index - 1
200
+ else
201
+ cnt_index - 1
202
+ end
203
+ end
204
+
205
+ end
206
+
207
+ end
@@ -0,0 +1,3 @@
1
+ module Cremul
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative 'cremul/parser_helper'
4
+ require_relative 'cremul/cremul_message'
5
+
6
+ class CremulParser
7
+ include Cremul::ParserHelper
8
+
9
+ attr_reader :segments, :msg
10
+
11
+ def initialize
12
+ end
13
+
14
+ def parse(file)
15
+ file_as_a_string = ''
16
+ file.each do |line|
17
+ line.encode(Encoding::UTF_8)
18
+ file_as_a_string += line.chop
19
+ end
20
+ @segments = file_as_a_string.split("'")
21
+ @msg = CremulMessage.new(@segments)
22
+ end
23
+
24
+ end
@@ -0,0 +1,2 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../lib/cremul_parser'
@@ -0,0 +1,8 @@
1
+ UNA:+,? 'UNB+UNOC:3+00810506482+00975945065+140312:1546+01001386'UNH+1+CREMUL:D:
2
+ 96A:UN'BGM+435+2014031215350976'DTM+137:20140312:102'NAD+MR+00975945065'LIN+1'DT
3
+ M+209:20140312:102'BUS++DO++233:25:124'MOA+349:1394:NOK'RFF+ACK:08012992096'DTM+
4
+ 171:20140312:102'FII+BF+12121212121'SEQ++1'DTM+203:20140312:102'FII+OR+123123123
5
+ 12'RFF+AEK:12072200001'RFF+ACD:180229451'MOA+143:1394'NAD+PL+++Tømrer Morten Rog
6
+ nebær AS+Skjerpåkeren 17+STANGE++2335'NAD+BE+++FOND.FOR REGIONALE VERNEOMBUD++++
7
+ 0000'PRC+11'FTX+PMD+++Tømrer Morten Rognebær AS'CNT+LI:1'UNT+23+1'UNZ+1+01001386
8
+ '
@@ -0,0 +1,8 @@
1
+ UNA:+,? 'UNB+UNOC:3+00810506482+00975945065+140312:1546+01001386'UNH+1+CREMUL:D:
2
+ 96A:UN'BGM+435+2014031215350976'DTM+137:20140312:102'NAD+MR+00975945065'LIN+1'DT
3
+ M+209:20140312:102'BUS++DO++233:25:124'MOA+349:1394:NOK'RFF+ACK:08012992096'DTM+
4
+ 171:20140312:102'FII+BF+12121212121'SEQ++1'DTM+203:20140312:102'FII+OR+123123123
5
+ 12'RFF+AEK:12072200001'RFF+ACD:180229451'MOA+143:1394'NAD+PL+++T�mrer Morten Rog
6
+ neb�r AS+Skjerp�keren 17+STANGE++2335'NAD+BE+++THE BENEFICIARY COMPANY++++
7
+ 0000'PRC+11'FTX+PMD+++T�mrer Morten Rogneb�r AS'CNT+LI:1'UNT+23+1'UNZ+1+01001386
8
+ '
@@ -0,0 +1,6 @@
1
+ UNA:+,? 'UNB+UNOC:3+00810506482+00975945065+130402:1812+01001057'UNH+1+CREMUL:D:
2
+ 96A:UN'BGM+435+2013040217241217'DTM+137:20130402:102'NAD+MR+00975945065'LIN+1'DT
3
+ M+209:20130402:102'BUS++DO++230:25:124'MOA+349:250:NOK'RFF+ACK:08002130826'DTM+1
4
+ 71:20130402:102'FII+BF+12121212121'SEQ++1'DTM+203:20130331:102'FII+OR+1231231231
5
+ 2'RFF+AEK:3900263127'RFF+ACD:303019152'MOA+143:250'NAD+PL+++Haugene Nils Pe'PRC+
6
+ 8'DOC+999+20122124330'MOA+12:250'GIS+37'CNT+LI:1'UNT+24+1'UNZ+1+01001057'
@@ -0,0 +1,6 @@
1
+ UNA:+,? 'UNB+UNOC:3+00810506482+00975945065+130402:1812+01001057'UNH+1+CREMUL:D:
2
+ 96A:UN'BGM+435+2013040217241217'DTM+137:20130402:102'NAD+MR+00975945065'LIN+1'DT
3
+ M+209:20130402:102'BUS++DO++230:25:124'MOA+349:250:NOK'RFF+ACK:08002130826'DTM+1
4
+ 71:20130402:102'FII+BF+12121212121'SEQ++1'DTM+203:20130331:102'FII+OR+1231231231
5
+ 2'RFF+AEK:3900263127'RFF+ACD:303019152'MOA+143:250'NAD+PL+++Haugene Nils Pe'PRC+
6
+ 8'DOC+999+20122124330'MOA+12:250'GIS+37'CNT+LI:1'UNT+24+1'UNZ+1+01001057'
@@ -0,0 +1,77 @@
1
+ UNA:+,? 'UNB+UNOC:3+00810506482+00975945065+130411:1547+01001066'UNH+1+CREMUL:D:
2
+ 96A:UN'BGM+435+2013041115380742'DTM+137:20130411:102'NAD+MR+00975945065'LIN+1'DT
3
+ M+209:20130411:102'BUS++DO++230:25:124'MOA+349:3000:NOK'RFF+ACK:08011116222'DTM+
4
+ 171:20130411:102'FII+BF+70380518552'SEQ++1'DTM+203:20130410:102'FII+OR+123456789
5
+ 01'RFF+ACD:*85290467'MOA+143:250'NAD+PL+++RUNAR NORDLI+KVENNBERGGT 30+KRISTIANSU
6
+ ND N++6515'NAD+BE+++THE BENEFICIARY COMPANY'PRC+8'DOC+999+20132065978'MOA+
7
+ 12:250'GIS+37'SEQ++2'DTM+203:20130410:102'FII+OR+12345678901'RFF+AEK:31000164149
8
+ 'RFF+ACD:362509152'MOA+143:250'NAD+PL+++R S Bygg'PRC+8'DOC+999+20132148105'MOA+1
9
+ 2:250'GIS+37'SEQ++3'DTM+203:20130410:102'FII+OR+12345678901'RFF+AEK:31000257918'
10
+ RFF+ACD:890209152'MOA+143:250'NAD+PL+++B Bolle AS'PRC+8'DOC+999+20132161140'MOA+
11
+ 12:250'GIS+37'SEQ++4'DTM+203:20130410:102'FII+OR+12345678901'RFF+AEK:31000270111
12
+ 'RFF+ACD:890209152'MOA+143:250'NAD+PL+++Jakobsen Kriste'PRC+8'DOC+999+2013053828
13
+ 1'MOA+12:250'GIS+37'SEQ++5'DTM+203:20130410:102'FII+OR+12345678901'RFF+AEK:31000
14
+ 998400'RFF+ACD:936509152'MOA+143:250'NAD+PL+++Kjetil Vestad E'PRC+8'DOC+999+2013
15
+ 0133695'MOA+12:250'GIS+37'SEQ++6'DTM+203:20130410:102'FII+OR+12345678901'RFF+AEK
16
+ :31000352894'RFF+ACD:470109152'MOA+143:250'NAD+PL+++Brunes Service'PRC+8'DOC+999
17
+ +20131095927'MOA+12:250'GIS+37'SEQ++7'DTM+203:20130410:102'FII+OR+12345678901'RF
18
+ F+AEK:31000302531'RFF+ACD:470109151'MOA+143:250'NAD+PL+++STRØM HILMAR JO'PRC+8'D
19
+ OC+999+20130204637'MOA+12:250'GIS+37'SEQ++8'DTM+203:20130410:102'FII+OR+12345678
20
+ 901:INGE KJELL MEEK'RFF+ACD:860185489'MOA+143:250'NAD+PL+++INGE KJELL MEEK+BREMS
21
+ NES+AVERØY++6530+NO'PRC+8'DOC+999+20132050046'MOA+12:250'GIS+37'SEQ++9'DTM+203:2
22
+ 0130410:102'FII+OR+12345678901'RFF+AEK:933920002'RFF+ACD:393019451'MOA+143:250'N
23
+ AD+PL+++We-Bo Graving A'PRC+8'DOC+999+20130982836'MOA+12:250'GIS+37'SEQ++10'DTM+
24
+ 203:20130410:102'FII+OR+12345678901'RFF+AEK:975030000'RFF+ACD:470119451'MOA+143:
25
+ 250'NAD+PL+++2 Snekkers AS'PRC+8'DOC+999+20130888439'MOA+12:250'GIS+37'SEQ++11'D
26
+ TM+203:20130410:102'FII+OR+12345678901'RFF+AEK:31000424164'RFF+ACD:420109152'MOA
27
+ +143:250'NAD+PL+++BRATBERG RUNE'PRC+8'DOC+999+20131099606'MOA+12:250'GIS+37'SEQ+
28
+ +12'DTM+203:20130410:102'FII+OR+12345678901'RFF+AEK:936050001'RFF+ACD:420119451'
29
+ MOA+143:250'NAD+PL+++Murmester Langn'PRC+8'DOC+999+20131268482'MOA+12:250'GIS+37
30
+ 'LIN+2'DTM+209:20130411:102'BUS++DO++230:25:124'MOA+349:1000:NOK'RFF+ACK:0801197
31
+ 9782'DTM+171:20130411:102'FII+BF+70380518552'SEQ++1'DTM+203:20130411:102'FII+OR+
32
+ 12345678901:UAB NORSTATA'RFF+ACD:860195251'MOA+143:250'NAD+PL+++UAB NORSTATA++OP
33
+ PDAL++7341+NO'PRC+8'DOC+999+20131316414'MOA+12:250'GIS+37'SEQ++2'DTM+203:2013041
34
+ 1:102'FII+OR+12345678901'RFF+AEK:31010464264'RFF+ACD:470119152'MOA+143:250'NAD+P
35
+ L+++Røstgård Birger'PRC+8'DOC+999+20130492877'MOA+12:250'GIS+37'SEQ++3'DTM+203:2
36
+ 0130411:102'FII+OR+12345678901'RFF+AEK:31010478197'RFF+ACD:222019152'MOA+143:250
37
+ 'NAD+PL+++LUVENT AS'PRC+8'DOC+999+20130542564'MOA+12:250'GIS+37'SEQ++4'DTM+203:2
38
+ 0130411:102'FII+OR+12345678901'RFF+AEK:3713048'RFF+ACD:797410302'MOA+143:250'NAD
39
+ +PL+++OPGÅRD MASKIN AS+REISTADHAUGEN 8+ALTA++9513'NAD+BE+++THE BENEFICIARY COMPA
40
+ NY+Postboks 22 Sentrum+OSLO++0101'INP+BF+2:SI'FTX+AAG+++OPGÅRD MASKIN AS'PR
41
+ C+8'DOC+999+20130139262'MOA+12:250'GIS+37'LIN+3'DTM+209:20130411:102'BUS++DO++23
42
+ 0:25:124'MOA+349:1894:NOK'RFF+ACK:08011994246'DTM+171:20130411:102'FII+BF+703805
43
+ 18552'SEQ++1'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:31010681540'RFF+ACD
44
+ :393019152'MOA+143:250'NAD+PL+++Gocakov Michail'PRC+8'DOC+999+20131256453'MOA+12
45
+ :250'GIS+37'SEQ++2'DTM+203:20130411:102'FII+OR+12345678901'RFF+ACD:265538979'RFF
46
+ +ABO:9357538979'MOA+143:250'NAD+PL++Jon Adler Torp:Jøransberg 24:3770 Kragerø'PR
47
+ C+8'DOC+999+20132137371'MOA+12:250'GIS+37'SEQ++3'DTM+203:20130411:102'FII+OR+123
48
+ 45678901'RFF+AEK:1113010006'RFF+ACD:890219451'MOA+143:1394'NAD+PL+++Fauske Elekt
49
+ ro'PRC+8'DOC+999+20130142076'MOA+12:1394'GIS+37'LIN+4'DTM+209:20130411:102'BUS++
50
+ DO++230:25:124'MOA+349:3095,61:NOK'RFF+ACK:08011909226'DTM+171:20130411:102'FII+
51
+ BF+70380518552'SEQ++1'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:1195720000
52
+ 'RFF+ACD:470119451'MOA+143:250'NAD+PL+++Jessen Rør AS'PRC+8'DOC+999+20130080284'
53
+ MOA+12:250'GIS+37'SEQ++2'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:227'RFF
54
+ +ACD:797614172'MOA+143:250'NAD+PL++Laurynas Lukosius:Bruveien 9 B::3055 KROKSTAD
55
+ ELVA'NAD+BE++THE BENEFICIARY COMPANY'INP+BF+2:SI'FTX+AAG+++Laurynas Lukosi
56
+ us 20131371971'PRC+8'DOC+999+20131371971'MOA+12:250'GIS+37'SEQ+
57
+ +3'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:31010773517'RFF+ACD:261019152
58
+ 'MOA+143:250'NAD+PL+++NILSEN TORE'PRC+8'DOC+999+20131337626'MOA+12:250'GIS+37'SE
59
+ Q++4'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:10007596'RFF+ACD:393211000'
60
+ MOA+143:250'NAD+PL+++Inge Langnes'PRC+8'DOC+999+20132006675'MOA+12:250'GIS+37'SE
61
+ Q++5'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:34229470001'RFF+ACD:9055194
62
+ 51'MOA+143:250'NAD+PL+++Bravida Norge AS'PRC+8'DOC+999+20132008606'MOA+12:250'GI
63
+ S+37'SEQ++6'DTM+203:20130411:102'FII+OR+12345678901'RFF+ACD:233340798'RFF+ABO:93
64
+ 67540798'MOA+143:250'NAD+PL++Gunvald Haugeplass:Bardølavegen 142:3580 Geilo'PRC+
65
+ 8'DOC+999+20130598905'MOA+12:250'GIS+37'SEQ++7'DTM+203:20130411:102'FII+OR+12345
66
+ 678901'RFF+AEK:3774669'RFF+ACD:797410819'MOA+143:250'NAD+PL+++OLE ROGNDALEN+HAGA
67
+ STUVEIEN 54+HAFSLUNDSØY++1734'NAD+BE+++THE BENEFICIARY COMPANY+Postboks 22
68
+ Sentrum+OSLO++0101'INP+BF+2:SI'FTX+AAG+++OLE ROGNDALEN'PRC+8'DOC+999+20131226563
69
+ 'MOA+12:250'GIS+37'SEQ++8'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:378560
70
+ 1'RFF+ACD:797410452'MOA+143:250'NAD+PL+++HYTTE OG BOLIG PARTNER AS+HO 405:SJØGAT
71
+ A 6+BODØ++8006'NAD+BE+++ +Postboks 22 Sentrum+OSLO++0
72
+ 101'INP+BF+2:SI'FTX+AAG+++HYTTE OG BOLIG PARTNER AS'PRC+8'DOC+999+20132188044'MO
73
+ A+12:250'GIS+37'SEQ++9'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:118454000
74
+ 3'RFF+ACD:108019451'MOA+143:845,61'NAD+PL+++Bris AS'PRC+8'DOC+999+20131223909'MO
75
+ A+12:845,61'GIS+37'SEQ++10'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:11888
76
+ 10001'RFF+ACD:261019451'MOA+143:250'NAD+PL+++Herøya Elektro'PRC+8'DOC+999+201301
77
+ 41516'MOA+12:250'GIS+37'CNT+LI:4'UNT+363+1'UNZ+1+01001066'
@@ -0,0 +1,77 @@
1
+ UNA:+,? 'UNB+UNOC:3+00810506482+00975945065+130411:1547+01001066'UNH+1+CREMUL:D:
2
+ 96A:UN'BGM+435+2013041115380742'DTM+137:20130411:102'NAD+MR+00975945065'LIN+1'DT
3
+ M+209:20130411:102'BUS++DO++230:25:124'MOA+349:3000:NOK'RFF+ACK:08011116222'DTM+
4
+ 171:20130411:102'FII+BF+70380518552'SEQ++1'DTM+203:20130410:102'FII+OR+123456789
5
+ 01'RFF+ACD:*85290467'MOA+143:250'NAD+PL+++RUNAR NORDLI+KVENNBERGGT 30+KRISTIANSU
6
+ ND N++6515'NAD+BE+++THE BENEFICIARY COMPANY'PRC+8'DOC+999+20132065978'MOA+
7
+ 12:250'GIS+37'SEQ++2'DTM+203:20130410:102'FII+OR+12345678901'RFF+AEK:31000164149
8
+ 'RFF+ACD:362509152'MOA+143:250'NAD+PL+++R S Bygg'PRC+8'DOC+999+20132148105'MOA+1
9
+ 2:250'GIS+37'SEQ++3'DTM+203:20130410:102'FII+OR+12345678901'RFF+AEK:31000257918'
10
+ RFF+ACD:890209152'MOA+143:250'NAD+PL+++B Bolle AS'PRC+8'DOC+999+20132161140'MOA+
11
+ 12:250'GIS+37'SEQ++4'DTM+203:20130410:102'FII+OR+12345678901'RFF+AEK:31000270111
12
+ 'RFF+ACD:890209152'MOA+143:250'NAD+PL+++Jakobsen Kriste'PRC+8'DOC+999+2013053828
13
+ 1'MOA+12:250'GIS+37'SEQ++5'DTM+203:20130410:102'FII+OR+12345678901'RFF+AEK:31000
14
+ 998400'RFF+ACD:936509152'MOA+143:250'NAD+PL+++Kjetil Vestad E'PRC+8'DOC+999+2013
15
+ 0133695'MOA+12:250'GIS+37'SEQ++6'DTM+203:20130410:102'FII+OR+12345678901'RFF+AEK
16
+ :31000352894'RFF+ACD:470109152'MOA+143:250'NAD+PL+++Brunes Service'PRC+8'DOC+999
17
+ +20131095927'MOA+12:250'GIS+37'SEQ++7'DTM+203:20130410:102'FII+OR+12345678901'RF
18
+ F+AEK:31000302531'RFF+ACD:470109151'MOA+143:250'NAD+PL+++STR�M HILMAR JO'PRC+8'D
19
+ OC+999+20130204637'MOA+12:250'GIS+37'SEQ++8'DTM+203:20130410:102'FII+OR+12345678
20
+ 901:INGE KJELL MEEK'RFF+ACD:860185489'MOA+143:250'NAD+PL+++INGE KJELL MEEK+BREMS
21
+ NES+AVER�Y++6530+NO'PRC+8'DOC+999+20132050046'MOA+12:250'GIS+37'SEQ++9'DTM+203:2
22
+ 0130410:102'FII+OR+12345678901'RFF+AEK:933920002'RFF+ACD:393019451'MOA+143:250'N
23
+ AD+PL+++We-Bo Graving A'PRC+8'DOC+999+20130982836'MOA+12:250'GIS+37'SEQ++10'DTM+
24
+ 203:20130410:102'FII+OR+12345678901'RFF+AEK:975030000'RFF+ACD:470119451'MOA+143:
25
+ 250'NAD+PL+++2 Snekkers AS'PRC+8'DOC+999+20130888439'MOA+12:250'GIS+37'SEQ++11'D
26
+ TM+203:20130410:102'FII+OR+12345678901'RFF+AEK:31000424164'RFF+ACD:420109152'MOA
27
+ +143:250'NAD+PL+++BRATBERG RUNE'PRC+8'DOC+999+20131099606'MOA+12:250'GIS+37'SEQ+
28
+ +12'DTM+203:20130410:102'FII+OR+12345678901'RFF+AEK:936050001'RFF+ACD:420119451'
29
+ MOA+143:250'NAD+PL+++Murmester Langn'PRC+8'DOC+999+20131268482'MOA+12:250'GIS+37
30
+ 'LIN+2'DTM+209:20130411:102'BUS++DO++230:25:124'MOA+349:1000:NOK'RFF+ACK:0801197
31
+ 9782'DTM+171:20130411:102'FII+BF+70380518552'SEQ++1'DTM+203:20130411:102'FII+OR+
32
+ 12345678901:UAB NORSTATA'RFF+ACD:860195251'MOA+143:250'NAD+PL+++UAB NORSTATA++OP
33
+ PDAL++7341+NO'PRC+8'DOC+999+20131316414'MOA+12:250'GIS+37'SEQ++2'DTM+203:2013041
34
+ 1:102'FII+OR+12345678901'RFF+AEK:31010464264'RFF+ACD:470119152'MOA+143:250'NAD+P
35
+ L+++R�stg�rd Birger'PRC+8'DOC+999+20130492877'MOA+12:250'GIS+37'SEQ++3'DTM+203:2
36
+ 0130411:102'FII+OR+12345678901'RFF+AEK:31010478197'RFF+ACD:222019152'MOA+143:250
37
+ 'NAD+PL+++LUVENT AS'PRC+8'DOC+999+20130542564'MOA+12:250'GIS+37'SEQ++4'DTM+203:2
38
+ 0130411:102'FII+OR+12345678901'RFF+AEK:3713048'RFF+ACD:797410302'MOA+143:250'NAD
39
+ +PL+++OPG�RD MASKIN AS+REISTADHAUGEN 8+ALTA++9513'NAD+BE+++THE BENEFICIARY COMPA
40
+ NY+Postboks 22 Sentrum+OSLO++0101'INP+BF+2:SI'FTX+AAG+++OPG�RD MASKIN AS'PR
41
+ C+8'DOC+999+20130139262'MOA+12:250'GIS+37'LIN+3'DTM+209:20130411:102'BUS++DO++23
42
+ 0:25:124'MOA+349:1894:NOK'RFF+ACK:08011994246'DTM+171:20130411:102'FII+BF+703805
43
+ 18552'SEQ++1'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:31010681540'RFF+ACD
44
+ :393019152'MOA+143:250'NAD+PL+++Gocakov Michail'PRC+8'DOC+999+20131256453'MOA+12
45
+ :250'GIS+37'SEQ++2'DTM+203:20130411:102'FII+OR+12345678901'RFF+ACD:265538979'RFF
46
+ +ABO:9357538979'MOA+143:250'NAD+PL++Jon Adler Torp:J�ransberg 24:3770 Krager�'PR
47
+ C+8'DOC+999+20132137371'MOA+12:250'GIS+37'SEQ++3'DTM+203:20130411:102'FII+OR+123
48
+ 45678901'RFF+AEK:1113010006'RFF+ACD:890219451'MOA+143:1394'NAD+PL+++Fauske Elekt
49
+ ro'PRC+8'DOC+999+20130142076'MOA+12:1394'GIS+37'LIN+4'DTM+209:20130411:102'BUS++
50
+ DO++230:25:124'MOA+349:3095,61:NOK'RFF+ACK:08011909226'DTM+171:20130411:102'FII+
51
+ BF+70380518552'SEQ++1'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:1195720000
52
+ 'RFF+ACD:470119451'MOA+143:250'NAD+PL+++Jessen R�r AS'PRC+8'DOC+999+20130080284'
53
+ MOA+12:250'GIS+37'SEQ++2'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:227'RFF
54
+ +ACD:797614172'MOA+143:250'NAD+PL++Laurynas Lukosius:Bruveien 9 B::3055 KROKSTAD
55
+ ELVA'NAD+BE++THE BENEFICIARY COMPANY'INP+BF+2:SI'FTX+AAG+++Laurynas Lukosi
56
+ us 20131371971'PRC+8'DOC+999+20131371971'MOA+12:250'GIS+37'SEQ+
57
+ +3'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:31010773517'RFF+ACD:261019152
58
+ 'MOA+143:250'NAD+PL+++NILSEN TORE'PRC+8'DOC+999+20131337626'MOA+12:250'GIS+37'SE
59
+ Q++4'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:10007596'RFF+ACD:393211000'
60
+ MOA+143:250'NAD+PL+++Inge Langnes'PRC+8'DOC+999+20132006675'MOA+12:250'GIS+37'SE
61
+ Q++5'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:34229470001'RFF+ACD:9055194
62
+ 51'MOA+143:250'NAD+PL+++Bravida Norge AS'PRC+8'DOC+999+20132008606'MOA+12:250'GI
63
+ S+37'SEQ++6'DTM+203:20130411:102'FII+OR+12345678901'RFF+ACD:233340798'RFF+ABO:93
64
+ 67540798'MOA+143:250'NAD+PL++Gunvald Haugeplass:Bard�lavegen 142:3580 Geilo'PRC+
65
+ 8'DOC+999+20130598905'MOA+12:250'GIS+37'SEQ++7'DTM+203:20130411:102'FII+OR+12345
66
+ 678901'RFF+AEK:3774669'RFF+ACD:797410819'MOA+143:250'NAD+PL+++OLE ROGNDALEN+HAGA
67
+ STUVEIEN 54+HAFSLUNDS�Y++1734'NAD+BE+++THE BENEFICIARY COMPANY+Postboks 22
68
+ Sentrum+OSLO++0101'INP+BF+2:SI'FTX+AAG+++OLE ROGNDALEN'PRC+8'DOC+999+20131226563
69
+ 'MOA+12:250'GIS+37'SEQ++8'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:378560
70
+ 1'RFF+ACD:797410452'MOA+143:250'NAD+PL+++HYTTE OG BOLIG PARTNER AS+HO 405:SJ�GAT
71
+ A 6+BOD�++8006'NAD+BE+++ +Postboks 22 Sentrum+OSLO++0
72
+ 101'INP+BF+2:SI'FTX+AAG+++HYTTE OG BOLIG PARTNER AS'PRC+8'DOC+999+20132188044'MO
73
+ A+12:250'GIS+37'SEQ++9'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:118454000
74
+ 3'RFF+ACD:108019451'MOA+143:845,61'NAD+PL+++Bris AS'PRC+8'DOC+999+20131223909'MO
75
+ A+12:845,61'GIS+37'SEQ++10'DTM+203:20130411:102'FII+OR+12345678901'RFF+AEK:11888
76
+ 10001'RFF+ACD:261019451'MOA+143:250'NAD+PL+++Her�ya Elektro'PRC+8'DOC+999+201301
77
+ 41516'MOA+12:250'GIS+37'CNT+LI:4'UNT+363+1'UNZ+1+01001066'
@@ -0,0 +1,4 @@
1
+ ## File encoding
2
+
3
+ The original DAT files are in iso-8859-1 encoding. They are converted to UTF-8 before being parsed by
4
+ the CremulParser.
@@ -0,0 +1,10 @@
1
+ #!/bin/bash
2
+
3
+ # Converts files presumed to be in ISO-8859-1 (Latin) encoding to UTF-8. The
4
+ # converted files will be given a -utf-8.txt extension
5
+
6
+ for i in $*
7
+ do
8
+ echo converting $i
9
+ cat $i | iconv -f ISO-8859-1 -t UTF-8 > ${i%%.*}-utf-8.txt
10
+ done
@@ -0,0 +1,55 @@
1
+ UNA:+.? '
2
+ UNB+UNOC:1+00810506482+00900831941+990512:1423+1293++++0'
3
+ UNH+1294+CREMUL:D:96A:UN:BSK'
4
+ BGM+455+245'
5
+ DTM+137:20110111:102'
6
+ NAD+MR+00900831941'
7
+ LIN+1'
8
+ DTM+202:20110111:102'
9
+ BUS++DO'
10
+ MOA+60:14637:NOK'
11
+ RFF+ACK:00412107263'
12
+ FII+BF+70580500043'
13
+ SEQ++1'
14
+ FII+OR+82001234567'
15
+ RFF+AEK:8803609752'
16
+ RFF+ACD:*90000000'
17
+ MOA+143:14637:NOK'
18
+ NAD+PL+++NSB BA PERSONTRAFIKK ØST+ØKONOMIKONTORET 5 ETG+OSLO++0048'
19
+ PRC+11'
20
+ FTX+PMD+++VÅR REF DERES REF BELØP 42224 170:14.637,00'
21
+ LIN+2'
22
+ DTM+202:20110111:102'
23
+ BUS++DO'
24
+ MOA+60:15000:NOK'
25
+ RFF+ACK:00412108142'
26
+ FII+BF+70580500043'
27
+ SEQ++1'FII+OR+15502345678'
28
+ RFF+AEK:8803609752'
29
+ RFF+ACD:*90000000'
30
+ MOA+143:15000:NOK'
31
+ NAD+PL+++ARA DAMPSKIPSSELSKAP POSTBOKS 1235+HALDEN++1750'
32
+ PRC+8'
33
+ DOC+380+6128793'
34
+ GIS+37'
35
+ LIN+3'
36
+ DTM+202:20110111:102'
37
+ BUS++DO++234'
38
+ MOA+346:6740,40:NOK'
39
+ RFF+ACK:00412511745'
40
+ FII+BF+70580500043'
41
+ SEQ++1'
42
+ DTM+193:20110110:102'
43
+ FII+OR+24403456789'
44
+ RFF+AEK:6206632921'
45
+ RFF+ACD:*93139354'
46
+ MOA+143:4126,65:NOK'
47
+ SEQ++2'
48
+ DTM+193:20110110:102'
49
+ FII+OR+60397654321'
50
+ RFF+AEK:6206632595'
51
+ RFF+ACD:*97454800'
52
+ MOA+143:2613,75'
53
+ CNT+LI:3'
54
+ UNT+55+1294'
55
+ UNZ+1+1293'
@@ -0,0 +1,154 @@
1
+ require_relative '../test_helper'
2
+
3
+ describe CremulParser do
4
+
5
+ describe 'parsing valid cremul files' do
6
+ before do
7
+ @parser = CremulParser.new
8
+ end
9
+
10
+ it 'should parse a valid cremul file with 1 line item' do
11
+ @parser.parse(File.open('files/CREMUL0001-utf-8.txt'))
12
+ @parser.segments.must_be_instance_of Array
13
+ @parser.msg.must_be_instance_of CremulMessage
14
+
15
+ d2014_03_12 = Date.new(2014,3,12)
16
+
17
+ msg = @parser.msg
18
+ msg.header.must_be_instance_of CremulHeader
19
+ msg.header.msg_id.must_include 'CREMUL'
20
+ msg.header.created_date.must_equal d2014_03_12
21
+ msg.header.bf_id.must_equal 975945065
22
+
23
+ msg.number_of_lines.must_equal 1
24
+
25
+ line = msg.lines[0]
26
+ line.must_be_instance_of CremulLine
27
+ line.posting_date.must_equal d2014_03_12
28
+ line.bf_account_number.must_equal '12121212121'
29
+ line.money.must_be_instance_of CremulMoney
30
+ line.money.amount.must_equal 1394.to_f
31
+ line.money.currency.must_equal :NOK
32
+
33
+ ref = line.reference
34
+ ref.must_be_instance_of CremulReference
35
+ ref.type.must_equal :ACK
36
+ ref.number.must_equal '08012992096'
37
+
38
+ line.transactions.size.must_equal 1
39
+ tx = line.transactions[0]
40
+ tx.must_be_instance_of CremulPaymentTx
41
+ tx.posting_date.must_equal d2014_03_12
42
+ tx.money.amount.must_equal 1394.to_f
43
+ tx.money.currency.must_equal :NOK
44
+ tx.references.size.must_equal 2
45
+ tx.free_text.must_equal 'Tømrer Morten Rognebær AS'
46
+
47
+ ref = tx.references[0]
48
+ ref.must_be_instance_of CremulReference
49
+ ref.type.must_equal :AEK
50
+ ref.number.must_equal '12072200001'
51
+
52
+ ref = tx.references[1]
53
+ ref.type.must_equal :ACD
54
+ ref.number.must_equal '180229451'
55
+
56
+ tx_payer = tx.payer_nad
57
+ tx_payer.must_be_instance_of CremulNameAndAddress
58
+ tx_payer.type.must_equal :PL
59
+ tx_payer.nad_lines[1].must_equal 'Skjerpåkeren 17'
60
+
61
+
62
+ end
63
+
64
+ it 'should parse a valid cremul file with multiple line items' do
65
+ @parser.parse(File.open('files/cremul_multi_lines.txt'))
66
+ @parser.segments.must_be_instance_of Array
67
+ @parser.msg.must_be_instance_of CremulMessage
68
+
69
+ d2011_01_11 = Date.new(2011,1,11)
70
+
71
+ msg = @parser.msg
72
+ msg.header.must_be_instance_of CremulHeader
73
+ msg.header.msg_id.must_include 'CREMUL'
74
+ msg.header.created_date.must_equal d2011_01_11
75
+
76
+ msg.number_of_lines.must_equal 3
77
+
78
+ line1 = msg.lines[0]
79
+ line1.must_be_instance_of CremulLine
80
+ line1.posting_date.must_equal d2011_01_11
81
+ line1.money.must_be_instance_of CremulMoney
82
+ line1.money.amount.must_equal 14637.to_f
83
+ line1.money.currency.must_equal :NOK
84
+
85
+ line1.transactions.size.must_equal 1
86
+ tx = line1.transactions[0]
87
+ tx.must_be_instance_of CremulPaymentTx
88
+ tx.posting_date.must_equal d2011_01_11
89
+ tx.money.amount.must_equal 14637.to_f
90
+ tx.money.currency.must_equal :NOK
91
+ tx.references.size.must_equal 2
92
+
93
+ tx.payer_nad.must_be_instance_of CremulNameAndAddress
94
+ tx.payer_nad.type.must_equal :PL
95
+ tx.payer_nad.nad_lines[1].must_equal 'ØKONOMIKONTORET 5 ETG'
96
+
97
+
98
+ line2 = msg.lines[1]
99
+ line2.must_be_instance_of CremulLine
100
+ line2.posting_date.must_equal d2011_01_11
101
+ line2.money.must_be_instance_of CremulMoney
102
+ line2.money.amount.must_equal 15000.to_f
103
+ line2.money.currency.must_equal :NOK
104
+ line2.transactions.size.must_equal 1
105
+
106
+ line3 = msg.lines[2]
107
+ line3.must_be_instance_of CremulLine
108
+ line3.posting_date.must_equal d2011_01_11
109
+ line3.money.must_be_instance_of CremulMoney
110
+ line3.money.amount.must_equal '6740,40'.to_f
111
+ line3.money.currency.must_equal :NOK
112
+ line3.transactions.size.must_equal 2
113
+
114
+ end
115
+
116
+ it 'should parse a valid long cremul file' do
117
+ @parser.parse(File.open('files/CREMUL0003-utf-8.txt'))
118
+ @parser.segments.must_be_instance_of Array
119
+ @parser.msg.must_be_instance_of CremulMessage
120
+
121
+ d2013_04_11 = Date.new(2013,4,11)
122
+
123
+ msg = @parser.msg
124
+ msg.header.must_be_instance_of CremulHeader
125
+ msg.header.msg_id.must_include 'CREMUL'
126
+ msg.header.created_date.must_equal d2013_04_11
127
+
128
+ msg.number_of_lines.must_equal 4
129
+
130
+
131
+ line1 = msg.lines[0]
132
+ line1.must_be_instance_of CremulLine
133
+ line1.posting_date.must_equal d2013_04_11
134
+ line1.money.must_be_instance_of CremulMoney
135
+ line1.money.amount.must_equal 3000.to_f
136
+ line1.money.currency.must_equal :NOK
137
+
138
+ line1.transactions.size.must_equal 12
139
+
140
+ tx = line1.transactions[0]
141
+ tx.must_be_instance_of CremulPaymentTx
142
+ tx.posting_date.must_equal Date.new(2013,4,10)
143
+ tx.money.amount.must_equal 250.to_f
144
+ tx.money.currency.must_equal :NOK
145
+ tx.payer_account_number.must_equal '12345678901'
146
+ tx.invoice_ref.must_equal '20132065978'
147
+ tx.invoice_ref_type.must_equal :KID
148
+
149
+ end
150
+
151
+
152
+ end
153
+
154
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cremul-parser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Per Spilling
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10'
41
+ description: 'Based on the format specification from BSK: http://bsk.no/media/18244/CREMUL_BSK_v2_13_d96A-201112.pdf'
42
+ email:
43
+ - per@kodemaker.no
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - cremul-parser.gemspec
54
+ - lib/cremul/cremul_header.rb
55
+ - lib/cremul/cremul_line.rb
56
+ - lib/cremul/cremul_message.rb
57
+ - lib/cremul/cremul_money.rb
58
+ - lib/cremul/cremul_name_and_address.rb
59
+ - lib/cremul/cremul_payment_tx.rb
60
+ - lib/cremul/cremul_reference.rb
61
+ - lib/cremul/parser_helper.rb
62
+ - lib/cremul/version.rb
63
+ - lib/cremul_parser.rb
64
+ - test/test_helper.rb
65
+ - test/unit/files/CREMUL0001-utf-8.txt
66
+ - test/unit/files/CREMUL0001.DAT
67
+ - test/unit/files/CREMUL0002-utf-8.txt
68
+ - test/unit/files/CREMUL0002.DAT
69
+ - test/unit/files/CREMUL0003-utf-8.txt
70
+ - test/unit/files/CREMUL0003.DAT
71
+ - test/unit/files/README.md
72
+ - test/unit/files/convert_files_to_utf8.sh
73
+ - test/unit/files/cremul_multi_lines.txt
74
+ - test/unit/parser_test.rb
75
+ homepage: https://github.com/perspilling/cremul-parser
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.2.1
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: A Ruby parser for CREMUL payment transaction files.
99
+ test_files:
100
+ - test/test_helper.rb
101
+ - test/unit/files/CREMUL0001-utf-8.txt
102
+ - test/unit/files/CREMUL0001.DAT
103
+ - test/unit/files/CREMUL0002-utf-8.txt
104
+ - test/unit/files/CREMUL0002.DAT
105
+ - test/unit/files/CREMUL0003-utf-8.txt
106
+ - test/unit/files/CREMUL0003.DAT
107
+ - test/unit/files/README.md
108
+ - test/unit/files/convert_files_to_utf8.sh
109
+ - test/unit/files/cremul_multi_lines.txt
110
+ - test/unit/parser_test.rb