clieop 0.2.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore CHANGED
@@ -4,3 +4,4 @@ clieop-*.gem
4
4
  /.bundle/
5
5
  Gemfile.lock
6
6
  *.rbc
7
+ .rvmrc
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.2.2"
7
- s.date = "2011-09-23"
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