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 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