clieop 0.2.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.rdoc +2 -0
- data/clieop.gemspec +6 -6
- data/doc/VERWINFO_NL_4.1_11-2009.pdf +0 -0
- data/lib/clieop/payment/batch.rb +128 -0
- data/lib/clieop/payment/file.rb +45 -0
- data/lib/clieop/payment/record.rb +124 -0
- data/lib/clieop/process_info/batch.rb +27 -0
- data/lib/clieop/process_info/file.rb +51 -0
- data/lib/clieop/process_info/record.rb +221 -0
- data/lib/clieop/process_info/transaction.rb +24 -0
- data/lib/clieop.rb +13 -4
- data/lib/core_ext/hash.rb +13 -0
- data/lib/core_ext/string.rb +19 -0
- data/spec/clieop/{batch_spec.rb → payment/batch_spec.rb} +55 -12
- data/spec/clieop/{file_spec.rb → payment/file_spec.rb} +1 -1
- data/spec/clieop/{record_spec.rb → payment/record_spec.rb} +27 -13
- data/spec/clieop/process_info/batch_spec.rb +26 -0
- data/spec/clieop/process_info/file_spec.rb +35 -0
- data/spec/clieop/process_info/record_spec.rb +32 -0
- data/spec/clieop/process_info/transaction_spec.rb +22 -0
- data/spec/clieop_spec.rb +2 -2
- data/spec/files/VERWINFO.txt +97 -0
- metadata +57 -20
- data/lib/clieop/batch.rb +0 -112
- data/lib/clieop/file.rb +0 -43
- data/lib/clieop/record.rb +0 -110
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
= CLIEOP
|
2
2
|
|
3
|
+
{<img src="https://secure.travis-ci.org/wvanbergen/clieop.png?branch=master" alt="Build Status" />}[http://travis-ci.org/wvanbergen/clieop]
|
4
|
+
|
3
5
|
This library is a pure Ruby implementation of the *CLIEOP03* format. Currently,
|
4
6
|
it only supports writing CLIEOP files with accounts receivable transactions.
|
5
7
|
Accounts payable transactions, and reading existing CLIEOP files is planned to be
|
data/clieop.gemspec
CHANGED
@@ -3,14 +3,14 @@ Gem::Specification.new do |s|
|
|
3
3
|
|
4
4
|
# Do not set version and date yourself, this will be done automatically
|
5
5
|
# by the gem release script.
|
6
|
-
s.version = "0.
|
7
|
-
s.date = "
|
6
|
+
s.version = "1.0.0"
|
7
|
+
s.date = "2012-07-20"
|
8
8
|
|
9
9
|
s.summary = "A pure Ruby implementation to write CLIEOP files"
|
10
10
|
s.description = "This library is a pure Ruby, MIT licensed implementation of the CLIEOP03 transaction format. CLIEOP03 can be used to communicate direct debt transactions with your (Dutch) bank."
|
11
11
|
|
12
|
-
s.authors = ['Willem van Bergen', 'Leon Berenschot']
|
13
|
-
s.email = ['willem@vanbergen.org', 'LeipeLeon@gmail.com']
|
12
|
+
s.authors = ['Willem van Bergen', 'Leon Berenschot', 'Reinier de Lange']
|
13
|
+
s.email = ['willem@vanbergen.org', 'LeipeLeon@gmail.com', 'r.j.delange@nedforce.nl']
|
14
14
|
s.homepage = 'http://github.com/wvanbergen/clieop/wiki'
|
15
15
|
|
16
16
|
s.add_development_dependency('rake')
|
@@ -19,6 +19,6 @@ Gem::Specification.new do |s|
|
|
19
19
|
|
20
20
|
# Do not set files and test_files yourself, this will be done automatically
|
21
21
|
# by the gem release script.
|
22
|
-
s.files = %w(.gitignore .infinity_test .rspec Gemfile MIT-LICENSE README.rdoc Rakefile autotest/discover.rb clieop.gemspec doc/clieop03.pdf lib/clieop.rb lib/clieop/batch.rb lib/clieop/file.rb lib/clieop/record.rb spec/clieop/batch_spec.rb spec/clieop/file_spec.rb spec/clieop/record_spec.rb spec/clieop_spec.rb spec/spec_helper.rb tasks/github-gem.rake)
|
23
|
-
s.test_files = %w(spec/clieop/batch_spec.rb spec/clieop/file_spec.rb spec/clieop/record_spec.rb spec/clieop_spec.rb)
|
22
|
+
s.files = %w(.gitignore .infinity_test .rspec Gemfile MIT-LICENSE README.rdoc Rakefile autotest/discover.rb clieop.gemspec doc/VERWINFO_NL_4.1_11-2009.pdf doc/clieop03.pdf lib/clieop.rb lib/clieop/payment/batch.rb lib/clieop/payment/file.rb lib/clieop/payment/record.rb lib/clieop/process_info/batch.rb lib/clieop/process_info/file.rb lib/clieop/process_info/record.rb lib/clieop/process_info/transaction.rb lib/core_ext/hash.rb lib/core_ext/string.rb spec/clieop/payment/batch_spec.rb spec/clieop/payment/file_spec.rb spec/clieop/payment/record_spec.rb spec/clieop/process_info/batch_spec.rb spec/clieop/process_info/file_spec.rb spec/clieop/process_info/record_spec.rb spec/clieop/process_info/transaction_spec.rb spec/clieop_spec.rb spec/files/VERWINFO.txt spec/spec_helper.rb tasks/github-gem.rake)
|
23
|
+
s.test_files = %w(spec/clieop/payment/batch_spec.rb spec/clieop/payment/file_spec.rb spec/clieop/payment/record_spec.rb spec/clieop/process_info/batch_spec.rb spec/clieop/process_info/file_spec.rb spec/clieop/process_info/record_spec.rb spec/clieop/process_info/transaction_spec.rb spec/clieop_spec.rb)
|
24
24
|
end
|
Binary file
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Clieop
|
2
|
+
module Payment
|
3
|
+
|
4
|
+
class Batch
|
5
|
+
|
6
|
+
attr_accessor :transactions, :batch_info
|
7
|
+
|
8
|
+
def initialize(batch_info)
|
9
|
+
raise "Required: :description, :account_nr, :account_owner" unless ([:account_nr, :account_owner] - batch_info.keys).empty?
|
10
|
+
@transactions = []
|
11
|
+
@batch_info = batch_info
|
12
|
+
end
|
13
|
+
|
14
|
+
# Adds a transaction to the batch
|
15
|
+
#
|
16
|
+
# :amount The amount involved with this transaction
|
17
|
+
# :account_nr The bank account from the other party
|
18
|
+
# :account_owner The name of the owner of the bank account
|
19
|
+
# :reference_number A reference number to identify this transaction
|
20
|
+
# :description A description for this transaction (4 lines max)
|
21
|
+
def add_transaction (transaction)
|
22
|
+
raise "No :account_nr given" if transaction[:account_nr].nil?
|
23
|
+
raise "No :amount given" if transaction[:amount].nil?
|
24
|
+
raise "No :account_owner given" if transaction[:account_owner].nil?
|
25
|
+
@transactions << transaction
|
26
|
+
end
|
27
|
+
|
28
|
+
alias_method :<<, :add_transaction
|
29
|
+
|
30
|
+
def to_clieop
|
31
|
+
|
32
|
+
# generate batch header records
|
33
|
+
batch_data = ""
|
34
|
+
batch_data << Clieop::Payment::Record.new(:batch_header,
|
35
|
+
:transaction_group => @batch_info[:transaction_group] || 0,
|
36
|
+
:acount_nr => @batch_info[:account_nr] || 0,
|
37
|
+
:serial_nr => @batch_info[:serial_nr] || 1,
|
38
|
+
:currency => @batch_info[:currency] || "EUR").to_clieop
|
39
|
+
|
40
|
+
# split batch discription into lines and make a record for the first 4 lines
|
41
|
+
unless @batch_info[:description].nil? || @batch_info[:description] == ''
|
42
|
+
@batch_info[:description].split(/\r?\n/)[0, 4].each do |line| # FIXME raise warning when line is longer than 32 chars/description longer than 4 lines
|
43
|
+
batch_data << Clieop::Payment::Record.new(:batch_description, :description => line.strip).to_s unless line == ''
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
batch_data << Clieop::Payment::Record.new(:batch_owner,
|
48
|
+
:process_date => @batch_info[:process_date] || 0,
|
49
|
+
:owner => @batch_info[:account_owner]).to_clieop
|
50
|
+
|
51
|
+
# initialize checksums
|
52
|
+
total_account = @batch_info[:account_nr].to_i * @transactions.length
|
53
|
+
total_amount = 0
|
54
|
+
|
55
|
+
# add transactions to batch
|
56
|
+
@transactions.each do |tr|
|
57
|
+
|
58
|
+
# prepare data for this transaction's records
|
59
|
+
transaction_type = tr[:transaction_type] || (transaction_is_payment? ? 1002 : 0)
|
60
|
+
to_account = transaction_is_payment? ? @batch_info[:account_nr] : tr[:account_nr]
|
61
|
+
from_account = transaction_is_payment? ? tr[:account_nr] : @batch_info[:account_nr]
|
62
|
+
amount_in_cents = (tr[:amount] * 100).round.to_i
|
63
|
+
|
64
|
+
# update checksums
|
65
|
+
total_account += tr[:account_nr].to_i
|
66
|
+
total_amount += amount_in_cents
|
67
|
+
|
68
|
+
# generate transaction record
|
69
|
+
batch_data << Clieop::Payment::Record.new(:transaction_info,
|
70
|
+
:transaction_type => transaction_type, :amount => amount_in_cents,
|
71
|
+
:to_account => to_account, :from_account => from_account).to_clieop
|
72
|
+
|
73
|
+
# generate record with transaction information
|
74
|
+
if transaction_is_payment?
|
75
|
+
batch_data << Clieop::Payment::Record.new(:invoice_name, :name => tr[:account_owner]).to_clieop
|
76
|
+
batch_data << Clieop::Payment::Record.new(:invoice_city, :city => tr[:account_city]).to_clieop unless tr[:account_city].nil?
|
77
|
+
end
|
78
|
+
|
79
|
+
batch_data << Clieop::Payment::Record.new(:transaction_reference, :reference_number => tr[:reference_number]).to_clieop unless tr[:reference_number].nil?
|
80
|
+
|
81
|
+
# split discription into lines and make a record for the first 4 lines
|
82
|
+
unless tr[:description].nil? || tr[:description] == ''
|
83
|
+
tr[:description].split(/\r?\n/)[0, 4].each do |line| # FIXME raise warning when line is longer than 32 chars/description longer than 4 lines
|
84
|
+
batch_data << Clieop::Payment::Record.new(:transaction_description, :description => line.strip).to_s unless line == ''
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
unless transaction_is_payment?
|
89
|
+
batch_data << Clieop::Payment::Record.new(:payment_name, :name => tr[:account_owner]).to_clieop
|
90
|
+
batch_data << Clieop::Payment::Record.new(:payment_city, :city => tr[:account_city]).to_clieop unless tr[:account_city].nil?
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
# generate batch footer record including some checks
|
96
|
+
batch_data << Clieop::Payment::Record.new(:batch_footer, :transaction_count => @transactions.length,
|
97
|
+
:total_amount => total_amount, :account_checksum => account_checksum(total_account)).to_clieop
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
alias_method :to_s, :to_clieop
|
102
|
+
|
103
|
+
# creates a batch for payments from a given account
|
104
|
+
def self.payment_batch(batch_info = {})
|
105
|
+
batch_info[:transaction_group] ||= 0
|
106
|
+
Clieop::Payment::Batch.new(batch_info)
|
107
|
+
end
|
108
|
+
|
109
|
+
# creates a batch for invoices to a given account
|
110
|
+
def self.invoice_batch(batch_info = {})
|
111
|
+
batch_info[:transaction_group] ||= 10
|
112
|
+
Clieop::Payment::Batch.new(batch_info)
|
113
|
+
end
|
114
|
+
|
115
|
+
# the checksum on the total of accounts is in fact the last 10 chars of total amount
|
116
|
+
def account_checksum(total)
|
117
|
+
total.to_s.split('').last(10).join('') # ruby 1.8.6
|
118
|
+
# total.to_s.chars.last(10).join('') # ruby 1.8.7
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def transaction_is_payment?
|
124
|
+
@batch_info[:transaction_group] == 10
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Clieop
|
2
|
+
module Payment
|
3
|
+
|
4
|
+
class File
|
5
|
+
|
6
|
+
attr_accessor :batches
|
7
|
+
attr_reader :file_info
|
8
|
+
|
9
|
+
def initialize(file_info = {})
|
10
|
+
file_info[:date] = Date.today unless file_info[:date]
|
11
|
+
file_info[:date] = file_info[:date].strftime('%d%m%y') if file_info[:date].respond_to?(:strftime)
|
12
|
+
@file_info = file_info
|
13
|
+
@batches = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# renders this file as a CLIEOP03 formatted string
|
17
|
+
def to_clieop
|
18
|
+
clieop_data = Clieop::Payment::Record.new(:file_header, @file_info).to_clieop
|
19
|
+
@batches.each { |batch| clieop_data << batch.to_clieop }
|
20
|
+
clieop_data << Clieop::Payment::Record.new(:file_footer).to_clieop
|
21
|
+
end
|
22
|
+
|
23
|
+
# Alias for to_clieop
|
24
|
+
alias_method :to_s, :to_clieop
|
25
|
+
|
26
|
+
def payment_batch(options)
|
27
|
+
@batches << Clieop::Payment::Batch.payment_batch(options)
|
28
|
+
yield(@batches.last) if block_given?
|
29
|
+
return @batches.last
|
30
|
+
end
|
31
|
+
|
32
|
+
def invoice_batch(options)
|
33
|
+
@batches << Clieop::Payment::Batch.invoice_batch(options)
|
34
|
+
yield(@batches.last) if block_given?
|
35
|
+
return @batches.last
|
36
|
+
end
|
37
|
+
|
38
|
+
def save(filename)
|
39
|
+
::File.open(filename, 'w') do |f|
|
40
|
+
f.write(self.to_clieop)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Clieop
|
2
|
+
module Payment
|
3
|
+
|
4
|
+
class Record
|
5
|
+
|
6
|
+
LINE_SEPARATOR = "\r\n"
|
7
|
+
|
8
|
+
TYPE_DEFINITIONS = {
|
9
|
+
:file_header => [
|
10
|
+
[:record_code, :numeric, 4, 1],
|
11
|
+
[:record_variant, :alpha, 1, 'A'],
|
12
|
+
[:date, :numeric, 6],
|
13
|
+
[:filename, :alpha, 8, 'CLIEOP03'],
|
14
|
+
[:sender_identification, :alpha, 5],
|
15
|
+
[:file_identification, :alpha, 4],
|
16
|
+
[:duplicate_code, :numeric, 1, 1]
|
17
|
+
],
|
18
|
+
:file_footer => [
|
19
|
+
[:record_code, :numeric, 4, 9999],
|
20
|
+
[:record_variant, :alpha, 1, 'A'],
|
21
|
+
],
|
22
|
+
:batch_header => [
|
23
|
+
[:record_code, :numeric, 4, 10],
|
24
|
+
[:record_variant, :alpha, 1, 'B'],
|
25
|
+
[:transaction_group, :numeric, 2],
|
26
|
+
[:acount_nr, :numeric, 10],
|
27
|
+
[:serial_nr, :numeric, 4],
|
28
|
+
[:currency, :alpha, 3, 'EUR']
|
29
|
+
],
|
30
|
+
:batch_footer => [
|
31
|
+
[:record_code, :numeric, 4, 9990],
|
32
|
+
[:record_variant, :alpha, 1, 'A'],
|
33
|
+
[:total_amount, :numeric, 18],
|
34
|
+
[:account_checksum, :numeric, 10],
|
35
|
+
[:transaction_count, :numeric, 7],
|
36
|
+
],
|
37
|
+
:batch_description => [
|
38
|
+
[:record_code, :numeric, 4, 20],
|
39
|
+
[:record_variant, :alpha, 1, 'A'],
|
40
|
+
[:description, :alpha, 32]
|
41
|
+
],
|
42
|
+
:batch_owner => [
|
43
|
+
[:record_code, :numeric, 4, 30],
|
44
|
+
[:record_variant, :alpha, 1, 'B'],
|
45
|
+
[:naw_code, :numeric, 1, 1],
|
46
|
+
[:process_date, :numeric, 6, 0],
|
47
|
+
[:owner, :alpha, 35],
|
48
|
+
[:test, :alpha, 1, 'P']
|
49
|
+
],
|
50
|
+
:transaction_info => [
|
51
|
+
[:record_code, :numeric, 4, 100],
|
52
|
+
[:record_variant, :alpha, 1, 'A'],
|
53
|
+
[:transaction_type, :numeric, 4, 1002],
|
54
|
+
[:amount, :numeric, 12],
|
55
|
+
[:from_account, :numeric, 10],
|
56
|
+
[:to_account, :numeric, 10],
|
57
|
+
],
|
58
|
+
:invoice_name => [
|
59
|
+
[:record_code, :numeric, 4, 110],
|
60
|
+
[:record_variant, :alpha, 1, 'B'],
|
61
|
+
[:name, :alpha, 35],
|
62
|
+
],
|
63
|
+
:invoice_city => [
|
64
|
+
[:record_code, :numeric, 4, 113],
|
65
|
+
[:record_variant, :alpha, 1, 'B'],
|
66
|
+
[:city, :alpha, 35],
|
67
|
+
],
|
68
|
+
:transaction_reference => [
|
69
|
+
[:record_code, :numeric, 4, 150],
|
70
|
+
[:record_variant, :alpha, 1, 'A'],
|
71
|
+
[:reference_number, :alpha, 16],
|
72
|
+
],
|
73
|
+
:transaction_description => [
|
74
|
+
[:record_code, :numeric, 4, 160],
|
75
|
+
[:record_variant, :alpha, 1, 'A'],
|
76
|
+
[:description, :alpha, 32],
|
77
|
+
],
|
78
|
+
:payment_name =>[
|
79
|
+
[:record_code, :numeric, 4, 170],
|
80
|
+
[:record_variant, :alpha, 1, 'B'],
|
81
|
+
[:name, :alpha, 35],
|
82
|
+
],
|
83
|
+
:payment_city =>[
|
84
|
+
[:record_code, :numeric, 4, 173],
|
85
|
+
[:record_variant, :alpha, 1, 'B'],
|
86
|
+
[:city, :alpha, 35],
|
87
|
+
],
|
88
|
+
}
|
89
|
+
|
90
|
+
attr_accessor :definition, :data
|
91
|
+
|
92
|
+
def initialize record_type, record_data = {}
|
93
|
+
|
94
|
+
# load record definition
|
95
|
+
raise "Unknown record type" unless Clieop::Payment::Record::TYPE_DEFINITIONS[record_type.to_sym]
|
96
|
+
@definition = Clieop::Payment::Record::TYPE_DEFINITIONS[record_type.to_sym]
|
97
|
+
|
98
|
+
# set default values according to definition
|
99
|
+
@data = @definition.inject({}) { |memo, field| memo[field[0]] = field[3] if field[3] ; memo }
|
100
|
+
|
101
|
+
# set values for all the provided data
|
102
|
+
record_data.each { |field, value| @data[field] = value }
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_clieop
|
106
|
+
line = ""
|
107
|
+
# format each field
|
108
|
+
@definition.each do |field, type, length, content|
|
109
|
+
fmt = '%'
|
110
|
+
fmt << (type == :numeric ? '0' : '-')
|
111
|
+
fmt << (length.to_s)
|
112
|
+
fmt << (type == :numeric ? 'd' : 's')
|
113
|
+
raw_data = (type == :numeric) ? @data[field].to_i : @data[field]
|
114
|
+
value = sprintf(fmt, raw_data)
|
115
|
+
line << (type == :numeric ? value[0 - length, length] : value[0, length])
|
116
|
+
end
|
117
|
+
# fill each line with spaces up to 50 characters and close with a CR/LF
|
118
|
+
line.ljust(50) + LINE_SEPARATOR
|
119
|
+
end
|
120
|
+
|
121
|
+
alias_method :to_s, :to_clieop
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Clieop
|
2
|
+
module ProcessInfo
|
3
|
+
|
4
|
+
class Batch
|
5
|
+
|
6
|
+
attr_accessor :info, :transactions
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
self.transactions = []
|
10
|
+
self.info = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_record record, current_transaction = nil
|
14
|
+
if [:batch_header, :batch_info, :batch_footer].include?(record.type)
|
15
|
+
info.merge!(record.data.except(:record_code, :filler))
|
16
|
+
elsif current_transaction
|
17
|
+
if record == current_transaction.record
|
18
|
+
transactions << current_transaction
|
19
|
+
else
|
20
|
+
current_transaction.add_record(record)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Clieop
|
2
|
+
module ProcessInfo
|
3
|
+
|
4
|
+
# Reads VERWINFO 4.1 process info, see record.rb for details
|
5
|
+
class File
|
6
|
+
|
7
|
+
attr_accessor :records, :batches, :info
|
8
|
+
|
9
|
+
def initialize(verwinfo_string, whiny_mode = false)
|
10
|
+
self.records = verwinfo_string.split(Clieop::ProcessInfo::Record::LINE_SEPARATOR).map do |record_line|
|
11
|
+
unless record_line.size < 3 # No record code available
|
12
|
+
begin
|
13
|
+
Clieop::ProcessInfo::Record.new(record_line)
|
14
|
+
rescue Exception => e
|
15
|
+
whiny_mode ? raise(e) : nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end.compact
|
19
|
+
|
20
|
+
raise 'No valid records found in VERWINFO data' unless @records.any?
|
21
|
+
|
22
|
+
self.batches = []
|
23
|
+
self.info = records.first.data.merge(records.last.data).except(:record_code, :filler) # Get file header and footer
|
24
|
+
|
25
|
+
current_batch = nil; current_transaction = nil
|
26
|
+
records.each do |record|
|
27
|
+
case record.type
|
28
|
+
when :batch_header
|
29
|
+
current_batch = Clieop::ProcessInfo::Batch.new
|
30
|
+
current_transaction = nil
|
31
|
+
batches << current_batch
|
32
|
+
when :transaction_info, :changed_account_info
|
33
|
+
current_transaction = Clieop::ProcessInfo::Transaction.new(record)
|
34
|
+
end
|
35
|
+
|
36
|
+
current_batch.add_record(record, current_transaction) if current_batch
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.from_file(verwinfo_file, whiny_mode = false)
|
41
|
+
verwinfo_file = File.open(verwinfo_file) if verwinfo_file.is_a?(String)
|
42
|
+
self.from_string(verwinfo_file.read, whiny_mode)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.from_string(verwinfo_string, whiny_mode = false)
|
46
|
+
self.new(verwinfo_string, whiny_mode)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'bigdecimal'
|
3
|
+
|
4
|
+
module Clieop
|
5
|
+
module ProcessInfo
|
6
|
+
|
7
|
+
# Reads VERWINFO 4.1 process info
|
8
|
+
# Docs: http://www.equens.com/Images/VERWINFO%20NL%204.1_11-2009.pdf
|
9
|
+
class Record
|
10
|
+
|
11
|
+
attr_accessor :type, :raw_line, :definition, :data
|
12
|
+
|
13
|
+
LINE_SEPARATOR = "\r\n"
|
14
|
+
|
15
|
+
TYPE_DEFINITIONS = {
|
16
|
+
:file_header => [
|
17
|
+
[:record_code, :numeric, 3, 10],
|
18
|
+
[:filename, :alpha, 8, 'VERWINFO'],
|
19
|
+
[:filler, :alpha, 1, 'A'],
|
20
|
+
[:file_version, :alpha, 3, '4.1'],
|
21
|
+
[:date, :date, 6],
|
22
|
+
[:run_number, :numeric, 4],
|
23
|
+
[:account_nr, :numeric, 10],
|
24
|
+
[:serial_nr, :numeric, 4],
|
25
|
+
[:file_nr, :numeric, 2]
|
26
|
+
],
|
27
|
+
:file_footer => [
|
28
|
+
[:record_code, :numeric, 3, 990],
|
29
|
+
[:batches_count, :numeric, 6],
|
30
|
+
[:next_file_nr, :numeric, 2]
|
31
|
+
],
|
32
|
+
:batch_header => [
|
33
|
+
[:record_code, :numeric, 3, 50],
|
34
|
+
[:account_nr, :numeric, 10],
|
35
|
+
[:filler, :alpha, 3],
|
36
|
+
[:total_amount, :amount, 18],
|
37
|
+
[:transaction_count, :numeric, 7],
|
38
|
+
[:test_code, :alpha, 1], # P(roduction), T(est)
|
39
|
+
[:batch_type, :alpha, 1],
|
40
|
+
[:period_type, :alpha, 1],
|
41
|
+
[:period_length, :numeric, 2],
|
42
|
+
[:period_nr, :numeric, 3]
|
43
|
+
],
|
44
|
+
:batch_info => [
|
45
|
+
[:record_code, :numeric, 3, 51],
|
46
|
+
[:currency, :alpha, 3],
|
47
|
+
[:batch_identifier, :alpha, 16]
|
48
|
+
],
|
49
|
+
:batch_footer => [
|
50
|
+
[:record_code, :numeric, 3, 950],
|
51
|
+
[:total_rejected, :numeric, 7],
|
52
|
+
[:total_reversed, :numeric, 7],
|
53
|
+
[:transaction_count, :numeric, 7],
|
54
|
+
[:total_amount, :amount, 18]
|
55
|
+
],
|
56
|
+
:transaction_info => [
|
57
|
+
[:record_code, :numeric, 3, 100],
|
58
|
+
[:amount, :amount, 13],
|
59
|
+
[:from_account, :numeric, 10],
|
60
|
+
[:to_account, :numeric, 10],
|
61
|
+
[:entry_account, :numeric, 10],
|
62
|
+
|
63
|
+
# from_account_verification_nr - Controlecijfer rekeningnummer betaler
|
64
|
+
# 0 – 9 als Rekeningnummer betaler een rekeningnummer met maximaal 7 cijfers bevat
|
65
|
+
# spatie als Rekeningnummer betaler een gewoon rekeningnummer bevat
|
66
|
+
[:from_account_verification_nr, :numeric, 1],
|
67
|
+
|
68
|
+
# transaction_reference_ok - Betalingskenmerkgoed
|
69
|
+
# J controlecijfer juist
|
70
|
+
# N controlecijfer onjuist
|
71
|
+
# spatie geen controlecijfer aanwezig
|
72
|
+
[:transaction_reference_ok, :alpha, 1]
|
73
|
+
],
|
74
|
+
:euro_record => [
|
75
|
+
[:record_code, :numeric, 3, 101],
|
76
|
+
[:currency, :alpha, 3],
|
77
|
+
[:amount_nlg, :amount, 13],
|
78
|
+
[:amount_eur, :amount, 13]
|
79
|
+
],
|
80
|
+
:reverse_info => [
|
81
|
+
[:record_code, :numeric, 3, 105],
|
82
|
+
[:transaction_reference, :alpha, 16],
|
83
|
+
[:inquiry_identifier, :alpha, 19],
|
84
|
+
|
85
|
+
# reason - Redenstorno Signaalcode Signaaltekst
|
86
|
+
#
|
87
|
+
# 01 0010 Administratieve reden
|
88
|
+
# 02 0011 Rekeningnummer vervallen
|
89
|
+
# 03 0001 Rekeningnummer onbekend
|
90
|
+
# 05 0012 Geen incassomachtiging verstrekt
|
91
|
+
# 06 0013 Niet akkoord met afschrijving
|
92
|
+
# 07 0014 Dubbel betaald
|
93
|
+
# 08 0005 Naam/nummer stemmen niet overeen
|
94
|
+
# 09 0007 Rekeningnummer geblokkeerd
|
95
|
+
# 10 0008 Selektieve incasso blokkade
|
96
|
+
# 11 0009 Rekeningnummer WKA
|
97
|
+
[:reason, :numeric, 2],
|
98
|
+
|
99
|
+
[:original_to_account, :numeric, 10]
|
100
|
+
],
|
101
|
+
:transaction_description => [
|
102
|
+
[:record_code, :numeric, 3, 110],
|
103
|
+
[:description, :alpha, 32]
|
104
|
+
],
|
105
|
+
:bank_info => [
|
106
|
+
[:record_code, :numeric, 3, 115],
|
107
|
+
[:description, :alpha, 30]
|
108
|
+
],
|
109
|
+
:settle_info => [
|
110
|
+
[:record_code, :numeric, 3, 500],
|
111
|
+
|
112
|
+
# status - Poststatus
|
113
|
+
# 00 niet van toepassing (Batchsoorten B en C)
|
114
|
+
# 01 geweigerde post
|
115
|
+
# 02 teruggebogen post
|
116
|
+
[:status, :numeric, 2],
|
117
|
+
|
118
|
+
[:original_settle_date, :date, 6],
|
119
|
+
[:filler, :alpha, 14],
|
120
|
+
[:run_nr, :numeric, 4],
|
121
|
+
[:settle_date, :date, 6],
|
122
|
+
|
123
|
+
# transaction_type - Transactiesoort
|
124
|
+
# 0000 crediteuren betalingen
|
125
|
+
# 0001 door bank geconverteerde opdrachten
|
126
|
+
# 0002 periodieke overboekingen
|
127
|
+
# 0003 salarissen
|
128
|
+
# 0006 iDEAL internet betalingen
|
129
|
+
# 0009 Digitale Nota betalingen
|
130
|
+
# 0092 EBALink betaling
|
131
|
+
# 0220 doorlopende machtiging algemeen
|
132
|
+
# 0221 eenmalige machtiging
|
133
|
+
# 0222 doorlopende machtiging bedrijven
|
134
|
+
# 0223 doorlopende machtiging kansspelen
|
135
|
+
# 0227 doorlopende machtiging bedrijven zonder terugboekingsrecht debiteur
|
136
|
+
# 0228 doorlopende telefonische machtiging
|
137
|
+
# 0229 eenmalige telefonische machtiging
|
138
|
+
# 0284 doorlopende telefonische machtiging kansspelen
|
139
|
+
# 0285 overheidsvordering
|
140
|
+
# 0330 terugboeking op doorlopende machtiging algemeen
|
141
|
+
# 0331 terugboeking op eenmalige machtiging
|
142
|
+
# 0332 terugboeking op doorlopende machtiging bedrijven
|
143
|
+
# 0333 terugboeking op doorlopende machtiging kansspelen
|
144
|
+
# 0337 terugboeking op doorlopende machtiging bedrijven zonder terugboekingsrecht debiteur
|
145
|
+
# 0338 terugboeking op doorlopende telefonische machtiging
|
146
|
+
# 0339 terugboeking op eenmalige telefonische machtiging
|
147
|
+
# 0394 terugboeking op doorlopende telefonische machtiging kansspelen
|
148
|
+
# 0395 terugboeking op Overheidsvordering
|
149
|
+
# 1010 betaalautomaat transactie
|
150
|
+
# 1144 acceptgiro zonder bedrag in coderegel
|
151
|
+
# 1145 acceptgiro met bedrag in coderegel
|
152
|
+
[:transaction_type, :numeric, 4]
|
153
|
+
],
|
154
|
+
:changed_account_info => [
|
155
|
+
[:record_code, :numeric, 3, 503],
|
156
|
+
[:account_nr, :numeric, 10],
|
157
|
+
[:original_account_nr, :numeric, 10]
|
158
|
+
],
|
159
|
+
:name_info => [
|
160
|
+
[:record_code, :numeric, 3, 505],
|
161
|
+
[:name, :alpha, 35]
|
162
|
+
],
|
163
|
+
:street_info => [
|
164
|
+
[:record_code, :numeric, 3, 510],
|
165
|
+
[:street, :alpha, 35]
|
166
|
+
],
|
167
|
+
:city_info => [
|
168
|
+
[:record_code, :numeric, 3, 515],
|
169
|
+
[:city, :alpha, 35]
|
170
|
+
],
|
171
|
+
|
172
|
+
# See 'Bijlage 1'
|
173
|
+
:reject_info => [
|
174
|
+
[:record_code, :numeric, 3, 600],
|
175
|
+
[:code, :numeric, 4],
|
176
|
+
[:description, :alpha, 32]
|
177
|
+
]
|
178
|
+
}
|
179
|
+
|
180
|
+
TYPE_FOR_RECORD_CODE = TYPE_DEFINITIONS.inject({}){|m, (k,v)| m[v.first.last] = k; m }
|
181
|
+
|
182
|
+
def initialize record_line
|
183
|
+
self.raw_line = record_line
|
184
|
+
self.type = TYPE_FOR_RECORD_CODE[raw_line[0,3].to_i]
|
185
|
+
self.data = {}
|
186
|
+
self.definition = TYPE_DEFINITIONS[type]
|
187
|
+
|
188
|
+
skip = 0
|
189
|
+
definition.each do |attribute_definition|
|
190
|
+
# Get the right substring
|
191
|
+
attribute_line = raw_line[skip, attribute_definition[2]]
|
192
|
+
# Parse the attribute
|
193
|
+
data[attribute_definition[0]] = attribute_line.to_s.present? ? self.class.send("parse_#{attribute_definition[1]}_value", attribute_line) : nil
|
194
|
+
# Update the skip
|
195
|
+
skip += attribute_definition[2]
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def self.parse_alpha_value attribute_line
|
202
|
+
attribute_line.strip
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.parse_numeric_value attribute_line
|
206
|
+
attribute_line.to_i
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.parse_amount_value attribute_line
|
210
|
+
(BigDecimal(attribute_line) / 100).round(2)
|
211
|
+
end
|
212
|
+
|
213
|
+
# Format: jjmmdd
|
214
|
+
def self.parse_date_value attribute_line
|
215
|
+
Time.parse(attribute_line).send(:to_date)
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Clieop
|
2
|
+
module ProcessInfo
|
3
|
+
|
4
|
+
class Transaction
|
5
|
+
|
6
|
+
attr_accessor :info, :descriptions, :record
|
7
|
+
|
8
|
+
def initialize(record)
|
9
|
+
self.record = record
|
10
|
+
self.info = record.data.except(:record_code)
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_record record
|
14
|
+
unless record.type == :transaction_description
|
15
|
+
info[record.type] = record.data.except(:record_code, :filler)
|
16
|
+
else
|
17
|
+
info[:transaction_descriptions] ||= []
|
18
|
+
info[:transaction_descriptions] << record.data[:description]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|