absa-h2h 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.gitignore +5 -0
  2. data/.rspec +3 -0
  3. data/Gemfile +7 -0
  4. data/README +0 -0
  5. data/Rakefile +7 -0
  6. data/absa-h2h.gemspec +23 -0
  7. data/lib/absa-h2h/account_holder_verification.rb +22 -0
  8. data/lib/absa-h2h/account_holder_verification_output.rb +22 -0
  9. data/lib/absa-h2h/eft/rejection_code.rb +30 -0
  10. data/lib/absa-h2h/eft.rb +200 -0
  11. data/lib/absa-h2h/eft_output.rb +12 -0
  12. data/lib/absa-h2h/eft_redirect.rb +13 -0
  13. data/lib/absa-h2h/eft_unpaid.rb +13 -0
  14. data/lib/absa-h2h/reply.rb +27 -0
  15. data/lib/absa-h2h/transmission/document.rb +26 -0
  16. data/lib/absa-h2h/transmission/record.rb +18 -0
  17. data/lib/absa-h2h/transmission/set.rb +168 -0
  18. data/lib/absa-h2h/version.rb +5 -0
  19. data/lib/absa-h2h.rb +22 -0
  20. data/lib/config/account_holder_verification.yml +196 -0
  21. data/lib/config/account_holder_verification_output.yml +182 -0
  22. data/lib/config/document.yml +54 -0
  23. data/lib/config/eft.yml +284 -0
  24. data/lib/config/eft_output.yml +65 -0
  25. data/lib/config/eft_redirect.yml +129 -0
  26. data/lib/config/eft_rejection_codes.yml +321 -0
  27. data/lib/config/eft_unpaid.yml +125 -0
  28. data/lib/config/reply.yml +222 -0
  29. data/lib/tmp/test.txt +0 -0
  30. data/spec/examples/ahv_input_file.txt +7 -0
  31. data/spec/examples/ahv_output_file.txt +7 -0
  32. data/spec/examples/eft_input_credit_file.txt +166 -0
  33. data/spec/examples/eft_input_credit_file2.txt +0 -0
  34. data/spec/examples/eft_input_file.txt +20 -0
  35. data/spec/examples/eft_output_file.txt +35 -0
  36. data/spec/examples/reply_file.txt +5 -0
  37. data/spec/examples/transmission_header_file.txt +1 -0
  38. data/spec/lib/account_holder_verification_spec.rb +157 -0
  39. data/spec/lib/eft/rejection_code_spec.rb +37 -0
  40. data/spec/lib/eft_output_spec.rb +9 -0
  41. data/spec/lib/eft_spec.rb +369 -0
  42. data/spec/lib/eft_transaction_standard_spec.rb +88 -0
  43. data/spec/lib/string_spec.rb +9 -0
  44. data/spec/lib/transmission/document_spec.rb +152 -0
  45. data/spec/lib/transmission/header_spec.rb +28 -0
  46. data/spec/lib/transmission/record_spec.rb +24 -0
  47. data/spec/lib/transmission/trailer_spec.rb +26 -0
  48. data/spec/spec_helper.rb +9 -0
  49. metadata +135 -0
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ build.sh
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format documentation
3
+
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in absa-h2h.gemspec
4
+ gemspec
5
+
6
+ gem 'rspec'
7
+ gem 'strata', '0.0.1', :git => 'git://github.com/tehtorq/strata.git'
data/README ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+
6
+ # If you want to make this the default task
7
+ task :default => :spec
data/absa-h2h.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "absa-h2h/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "absa-h2h"
7
+ s.version = Absa::H2h::VERSION
8
+ s.authors = ["Jeffrey van Aswegen, Douglas Anderson"]
9
+ s.email = ["jeffmess@gmail.com, i.am.douglas.anderson@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{A ruby interface to commumicate with the ABSA Host 2 Host platform}
12
+ s.description = %q{The interface supports Account holder verifications, EFT payments, Debit orders, collecting statements.}
13
+
14
+ s.rubyforge_project = "absa-h2h"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency "activesupport"
22
+ s.add_dependency "i18n"
23
+ end
@@ -0,0 +1,22 @@
1
+ module Absa
2
+ module H2h
3
+ module Transmission
4
+ class AccountHolderVerification < Set
5
+ class Header < Record; end
6
+ class Trailer < Record; end
7
+ class InternalAccountDetail < Record; end
8
+ class ExternalAccountDetail < Record; end
9
+
10
+ def validate!
11
+ unless trailer.no_det_recs == transactions.length.to_s
12
+ raise "no_det_recs mismatch: expected #{trailer.no_det_recs}, got #{transactions.length}"
13
+ end
14
+
15
+ unless transactions.map {|t| t.seq_no} == (1..(transactions.length)).map(&:to_s).to_a
16
+ raise "seq_no mismatch: #{transactions.map {|t| t.seq_no}}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module Absa
2
+ module H2h
3
+ module Transmission
4
+ class AccountHolderVerificationOutput < Set
5
+ class Header < Record; end
6
+ class Trailer < Record; end
7
+ class InternalAccountDetail < Record; end
8
+ class ExternalAccountDetail < Record; end
9
+
10
+ def validate!
11
+ unless trailer.no_det_recs == transactions.length.to_s
12
+ raise "no_det_recs mismatch: expected #{trailer.no_det_recs}, got #{transactions.length}"
13
+ end
14
+
15
+ unless transactions.map {|t| t.seq_no} == (1..(transactions.length)).map(&:to_s).to_a
16
+ raise "seq_no mismatch: #{transactions.map {|t| t.seq_no}}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ module Absa
2
+ module H2h
3
+ module Eft
4
+ class RejectionCode
5
+
6
+ def self.reasons
7
+ self.config['reasons']
8
+ end
9
+
10
+ def self.qualifiers
11
+ self.config['qualifiers']
12
+ end
13
+
14
+ def self.reason_for_code(code)
15
+ self.reasons[code]
16
+ end
17
+
18
+ def self.qualifier_for_code(code)
19
+ self.qualifiers[code]
20
+ end
21
+
22
+ def self.config
23
+ file_name = "#{Absa::H2h::CONFIG_DIR}/eft_rejection_codes.yml"
24
+ YAML.load(File.open(file_name))
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,200 @@
1
+ module Absa
2
+ module H2h
3
+ module Transmission
4
+ class Eft < Set
5
+
6
+ def validate_standard_transactions!
7
+ if transactions.first.user_sequence_number != header.first_sequence_number
8
+ raise "user_sequence_number: 1st Standard transactions user sequence number and the headers first sequence number must be equal."
9
+ end
10
+
11
+ raise "user_sequence_number: Duplicate user sequence number. Transactions must have unique sequence numbers!" unless transactions.map(&:user_sequence_number).uniq.length == transactions.length
12
+
13
+ unless transactions.map(&:user_sequence_number) == ((transactions.first.user_sequence_number.to_i)..(transactions.first.user_sequence_number.to_i + transactions.length-1)).map(&:to_s).to_a
14
+ raise "user_sequence_number: Transactions must increment sequentially. Got: #{transactions.map(&:user_sequence_number)}"
15
+ end
16
+
17
+ transactions.each do |transaction|
18
+ first_action_date = Date.strptime(header.first_action_date, "%y%m%d")
19
+ last_action_date = Date.strptime(header.last_action_date, "%y%m%d")
20
+ action_date = Date.strptime(transaction.action_date, "%y%m%d")
21
+
22
+ raise "action_date: Must be within the range of the headers first_action_date and last_action_date" unless (first_action_date..last_action_date).cover?(action_date)
23
+ raise "rec_status: Transaction and Header record status must be equal" if header.rec_status != transaction.rec_status
24
+ end
25
+ end
26
+
27
+ def validate_header_trailer!
28
+ raise "rec_status: Trailer and Header record status must be equal" if header.rec_status != trailer.rec_status
29
+ raise "bankserv_user_code: Trailer and Header user code must be equal." if header.bankserv_user_code != trailer.bankserv_user_code
30
+ raise "first_sequence_number: Trailer and Header sequence number must be equal." if header.first_sequence_number != trailer.first_sequence_number
31
+ raise "first_action_date: Trailer and Header first action date must be equal." if header.first_action_date != trailer.first_action_date
32
+ raise "last_action_date: Trailer and Header last action date must be equal." if header.last_action_date != trailer.last_action_date
33
+ end
34
+
35
+ def validate_contra_records!
36
+ transactions.select{|t| t.contra_record? }.each do |transaction|
37
+ # Loop used to validate contra records against the standard records
38
+ unless calculate_contra_record_total(transaction) == transaction.amount.to_i
39
+ raise "amount: Contra record amount must be the sum amount of all preceeding transactions. Expected #{calculate_contra_record_total(transaction)}. Got #{transaction.amount}."
40
+ end
41
+
42
+ transactions = transactions_for_contra_record(transaction)
43
+
44
+ if transactions.map(&:action_date).uniq.length > 1
45
+ raise "action_date: Contra records action date must be equal to all preceeding standard transactions action date. Got #{transactions_for_contra_record(transaction).map(&:action_date)}."
46
+ end
47
+
48
+ if (transactions.map(&:user_nominated_account).uniq.length > 1) or (transactions.map(&:user_nominated_account).first != transaction.user_nominated_account)
49
+ raise "user_nominated_account: Contra records user nominated account must match all preceeding standard transactions user nominated accounts. Got #{transactions.map(&:user_nominated_account)}."
50
+ end
51
+
52
+ if (transactions.map(&:user_branch).uniq.length > 1) or (transactions.map(&:user_branch).first != transaction.user_branch)
53
+ raise "user_branch_code: Contra records user branch must match all preceeding standard transactions user branch. Got #{transactions.map(&:user_branch)}."
54
+ end
55
+
56
+ raise "user_code: Contra records user code must match the headers users code. Got #{transaction.user_code}. Expected #{header.bankserv_user_code}." unless transaction.user_code == header.bankserv_user_code
57
+ end
58
+ end
59
+
60
+ def validate_trailer_transactions!
61
+ unless trailer.last_sequence_number == transactions.last.user_sequence_number
62
+ raise "last_sequence_number: Trailer records last sequence number must match the last contra records sequence number. Got #{trailer.last_sequence_number}. Expected #{transactions.last.user_sequence_number}"
63
+ end
64
+
65
+ debit_records = transactions.select {|t| t.bankserv_record_identifier.to_i == 50 }
66
+ credit_records = transactions.select {|t| t.bankserv_record_identifier.to_i == 10 }
67
+
68
+ unless trailer.no_debit_records.to_i == self.debit_records.count + self.credit_contra_records.count
69
+ raise "no_debit_records: Trailer records number of debit records must match the number of debit records. Expected #{debit_records.count}. Got #{trailer.no_debit_records}."
70
+ end
71
+
72
+ unless trailer.no_credit_records.to_i == self.credit_records.count + self.debit_contra_records.count
73
+ raise "no_credit_records: Trailer records number of credit records must match the number of credit records and contra debit records. Expected #{self.credit_records.count + self.debit_contra_records.count}. Got #{trailer.no_credit_records}."
74
+ end
75
+
76
+ unless trailer.no_contra_records.to_i == self.contra_records.count
77
+ raise "no_contra_records: Trailer records number of contra records must match the number of contra records. Expected #{self.contra_records.count}. Got #{trailer.no_contra_records}."
78
+ end
79
+
80
+ unless trailer.total_debit_value.to_i == self.total_debit_transactions
81
+ raise "total_debit_value: Trailer records total debit value must equal the sum amount of all transactions and credit contra records. Expected #{self.total_debit_transactions}. Got #{trailer.total_debit_value.to_i}."
82
+ end
83
+
84
+ unless trailer.total_credit_value.to_i == self.total_credit_transactions
85
+ raise "total_credit_value: Trailer records total credit value must equal the sum amount of all transactions and debit contra records. Expected #{self.total_credit_transactions}. Got #{trailer.total_credit_value.to_i}."
86
+ end
87
+
88
+ unless trailer.hash_total_of_homing_account_numbers.to_i == self.homing_numbers_hash_total
89
+ raise "hash_total_of_homing_account_numbers: Trailers hash total of homing account numbers does not match. Expected #{self.homing_numbers_hash_total}. Got #{trailer.hash_total_of_homing_account_numbers}."
90
+ end
91
+ end
92
+
93
+ def validate!
94
+ validate_standard_transactions!
95
+ validate_header_trailer!
96
+ validate_contra_records!
97
+ validate_trailer_transactions!
98
+ end
99
+
100
+ def contra_records
101
+ transactions.select {|t| t.contra_record? }
102
+ end
103
+
104
+ def standard_records
105
+ transactions.select {|t| !t.contra_record? }
106
+ end
107
+
108
+ def debit_records
109
+ # Standard records only
110
+ transactions.select {|t| t.bankserv_record_identifier.to_i == 50 }
111
+ end
112
+
113
+ def credit_records
114
+ # Standard records only
115
+ transactions.select {|t| t.bankserv_record_identifier.to_i == 10 }
116
+ end
117
+
118
+ def debit_contra_records
119
+ transactions.select {|t| t.contra_record? && t.bankserv_record_identifier.to_i == 52}
120
+ end
121
+
122
+ def credit_contra_records
123
+ transactions.select {|t| t.contra_record? && t.bankserv_record_identifier.to_i == 12}
124
+ end
125
+
126
+ def total_debit_transactions
127
+ # including credit contra records
128
+ ccr = self.credit_contra_records == [] ? 0 : self.credit_contra_records.map(&:amount).map(&:to_i).inject(&:+)
129
+ (self.debit_records.map(&:amount).map(&:to_i).inject(&:+) || 0) + ccr
130
+ end
131
+
132
+ def total_credit_transactions
133
+ # including debit contra records
134
+ dcr = self.debit_contra_records == [] ? 0 : self.debit_contra_records.map(&:amount).map(&:to_i).inject(&:+)
135
+ (self.credit_records.map(&:amount).map(&:to_i).inject(&:+) || 0) + dcr
136
+ end
137
+
138
+ def calculate_contra_record_total(contra_record)
139
+ transactions_for_contra_record(contra_record).map(&:amount).map(&:to_i).inject(&:+)
140
+ end
141
+
142
+ def transactions_for_contra_record(contra_record)
143
+ contra_records = self.contra_records.map(&:user_sequence_number)
144
+ sequence = transactions.map(&:user_sequence_number)
145
+
146
+ if contra_records.index(contra_record.user_sequence_number) == 0 # First contra record in user set
147
+ previous_contra_record = contra_record.user_sequence_number
148
+ start_point = 0
149
+ else
150
+ previous_contra_record = contra_records[contra_records.index(contra_record.user_sequence_number)-1]
151
+ start_point = sequence.index(previous_contra_record) + 1
152
+ end
153
+
154
+ end_point = sequence.index(contra_record.user_sequence_number)-1
155
+ transactions[start_point..end_point]
156
+ end
157
+
158
+ def homing_numbers_hash_total
159
+ ns_homing_account_number_total = self.standard_records.map(&:non_standard_homing_account_number).empty? ? 0 : self.standard_records.map(&:non_standard_homing_account_number).map(&:to_i).inject(&:+)
160
+ field9 = transactions.map(&:homing_account_number).map(&:to_i).inject(&:+) + ns_homing_account_number_total
161
+ field9.to_s.reverse[0,12].reverse.to_i
162
+ end
163
+
164
+ class Header < Record; end
165
+ class ContraRecord < Record
166
+
167
+ def contra_record?
168
+ true
169
+ end
170
+
171
+ def validate!(options={})
172
+ super(options)
173
+ raise "homing_branch: Should match the user branch. Got #{@homing_branch}. Expected #{@user_branch}." unless @homing_branch == @user_branch
174
+ raise "homing_account_number: Should match the user nominated account number. Got #{@homing_account_number}. Expected #{@user_nominated_account}." unless @homing_account_number == @user_nominated_account
175
+ raise "user_ref: Position 1 - 10 is compulsory. Please provide users abbreviated name." if @user_ref[0..9].blank?
176
+ raise "user_ref: Position 11 - 16 is compulsory and must be set to 'CONTRA'. Got #{@user_ref[10..15]}" if @user_ref[10..15] != "CONTRA"
177
+ end
178
+
179
+ end
180
+
181
+ class StandardRecord < Record
182
+
183
+ def contra_record?
184
+ false
185
+ end
186
+
187
+ def validate!(options={})
188
+ super(options)
189
+
190
+ raise "user_ref: Position 1 - 10 is compulsory. Please provide users abbreviated name." if @user_ref[0..11].blank?
191
+ raise "homing_account_name: Not to be left blank." if @homing_account_name.blank?
192
+ end
193
+
194
+ end
195
+
196
+ class Trailer < Record; end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,12 @@
1
+ module Absa
2
+ module H2h
3
+ module Transmission
4
+ class EftOutput < Set
5
+
6
+ class Header < Record; end
7
+ class Trailer < Record; end
8
+
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ module Absa
2
+ module H2h
3
+ module Transmission
4
+ class EftRedirect < Set
5
+
6
+ class Header < Record; end
7
+ class Trailer < Record; end
8
+ class Transaction < Record; end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Absa
2
+ module H2h
3
+ module Transmission
4
+ class EftUnpaid < Set
5
+
6
+ class Header < Record; end
7
+ class Trailer < Record; end
8
+ class Transaction < Record; end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ module Absa
2
+ module H2h
3
+ module Transmission
4
+ class Reply < Set
5
+
6
+ class TransmissionStatus < Record; end
7
+ class TransmissionRejectedReason < Record; end
8
+ class EftStatus < Record; end
9
+ class AcceptedReportReply < Record; end
10
+ class RejectedMessage < Record; end
11
+
12
+ def self.hash_from_s(string, transmission_type)
13
+ set_info = {type: self.partial_class_name.underscore, data: []}
14
+
15
+ string.split(/^/).each do |line|
16
+ if Set.for_record(line, transmission_type) == self
17
+ set_info[:data] << self.process_record(line[0, 198])
18
+ end
19
+ end
20
+
21
+ set_info
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ module Absa
2
+ module H2h
3
+ module Transmission
4
+
5
+ class Document < Set
6
+
7
+ class Header < Record; end
8
+ class Trailer < Record; end
9
+
10
+ def self.from_s(string, transmission_type)
11
+ options = self.hash_from_s(string, transmission_type)
12
+ self.build(options[:data])
13
+ end
14
+
15
+ def to_file!(filename)
16
+ File.open(destination, 'w') {|file| file.write(self.to_s) }
17
+ end
18
+
19
+ def from_file!(filename)
20
+ self.from_s(File.open(filename, "rb").read)
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ module Absa::H2h::Transmission
2
+
3
+ class Record
4
+ include Strata::RecordWriter
5
+ extend Strata::RecordWriter::ClassMethods
6
+
7
+ set_record_length 198
8
+ set_delimiter "\r\n"
9
+ set_allowed_characters ('A'..'Z').to_a + ('a'..'z').to_a + (0..9).to_a.map(&:to_s) + ['.','/','-','&','*',',','(',')','<','+','$',';','>','=',"'",' '] # move to config file
10
+
11
+ def initialize(options = {})
12
+ set_layout_variables(options)
13
+ validate! options
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,168 @@
1
+ module Absa::H2h::Transmission
2
+ class Set
3
+
4
+ attr_accessor :records
5
+
6
+ def initialize
7
+ self.records = []
8
+ end
9
+
10
+ def self.build(data)
11
+ set = self.new
12
+
13
+ data.each do |hash|
14
+ puts hash.inspect
15
+ if hash[:data].is_a? Array
16
+ klass = "Absa::H2h::Transmission::#{hash[:type].capitalize.camelize}".constantize
17
+ set.records << klass.build(hash[:data])
18
+ else
19
+ klass = "Absa::H2h::Transmission::#{self.partial_class_name}::#{hash[:type].capitalize.camelize}".constantize
20
+ set.records << klass.new(hash[:data])
21
+ end
22
+ end
23
+
24
+ set.validate!
25
+ set
26
+ end
27
+
28
+ def header
29
+ records[0]
30
+ end
31
+
32
+ def trailer
33
+ records[-1]
34
+ end
35
+
36
+ def transactions
37
+ records[1..-2]
38
+ end
39
+
40
+ def validate!
41
+
42
+ end
43
+
44
+ def to_s
45
+ string = ""
46
+ records.each {|record| string += record.to_s }
47
+ string
48
+ end
49
+
50
+ def self.for_record(record, transmission_type) # move this logic to yml file
51
+ record_id = record[0..2]
52
+
53
+ case record_id
54
+ when '000','999'
55
+ return Absa::H2h::Transmission::Document
56
+ when '030','031','039'
57
+ return (transmission_type == "output") ? Absa::H2h::Transmission::AccountHolderVerificationOutput : Absa::H2h::Transmission::AccountHolderVerification
58
+ when '001', '020'
59
+ return Absa::H2h::Transmission::Eft
60
+ when '010','019'
61
+ return Absa::H2h::Transmission::EftOutput
62
+ when '011','013','014'
63
+ return Absa::H2h::Transmission::EftUnpaid
64
+ when '016','017','018'
65
+ return Absa::H2h::Transmission::EftRedirect
66
+ when '900','901','902','903'
67
+ return Absa::H2h::Transmission::Reply
68
+ end
69
+ end
70
+
71
+ def self.trailer_id(klass)
72
+ case klass.name
73
+ when 'Absa::H2h::Transmission::Document'
74
+ return '999'
75
+ when 'Absa::H2h::Transmission::AccountHolderVerification'
76
+ return '039'
77
+ when 'Absa::H2h::Transmission::AccountHolderVerificationOutput'
78
+ return '039'
79
+ when 'Absa::H2h::Transmission::EftOutput'
80
+ return '019'
81
+ when 'Absa::H2h::Transmission::EftUnpaid'
82
+ return '014'
83
+ when 'Absa::H2h::Transmission::EftRedirect'
84
+ return '018'
85
+ end
86
+ end
87
+
88
+ def self.is_trailer_record?(set, record)
89
+ record_id = record[0,3]
90
+ return true if set == Absa::H2h::Transmission::Eft and (["001","020"].include? record_id) and record[4,2] == "92"
91
+ self.trailer_id(set) == record_id
92
+ end
93
+
94
+ def self.process_record(record)
95
+ record_info = {}
96
+
97
+ self.record_types.each do |record_type|
98
+ klass = "#{self.name}::#{record_type.camelize}".constantize
99
+
100
+ if klass.matches_definition?(record)
101
+ options = klass.string_to_hash(record)
102
+ record_info = {type: record_type, data: options}
103
+ break
104
+ end
105
+ end
106
+
107
+ record_info
108
+ end
109
+
110
+ def self.hash_from_s(string, transmission_type)
111
+ set_info = {type: self.partial_class_name.underscore, data: []}
112
+ lines = string.split(/^/)
113
+
114
+ # look for rec_ids, split into chunks, and pass each related class a piece of string
115
+
116
+ buffer = []
117
+ current_set = nil
118
+ subset = nil
119
+
120
+ lines.each do |line|
121
+ if Set.for_record(line, transmission_type) == self
122
+ if subset && (buffer.length > 0)
123
+ set_info[:data] << subset.hash_from_s(buffer.join, transmission_type)
124
+ buffer = []
125
+ subset = nil
126
+ end
127
+
128
+ record = line[0, 198]
129
+ set_info[:data] << self.process_record(record)
130
+ else
131
+ subset = Set.for_record(line, transmission_type) unless subset
132
+ buffer << line
133
+
134
+ if self.is_trailer_record?(subset, line)
135
+ set_info[:data] << subset.hash_from_s(buffer.join, transmission_type)
136
+ buffer = []
137
+ subset = nil
138
+ end
139
+ end
140
+ end
141
+
142
+ set_info
143
+ end
144
+
145
+ def self.record_types
146
+ self.layout_rules.map {|k,v| k}
147
+ end
148
+
149
+ def self.module_name
150
+ self.name.split("::")[0..-1].join("::")
151
+ end
152
+
153
+ def self.partial_class_name
154
+ self.name.split("::")[-1]
155
+ end
156
+
157
+ def self.layout_rules
158
+ file_name = "#{Absa::H2h::CONFIG_DIR}/#{self.partial_class_name.underscore}.yml"
159
+
160
+ YAML.load(File.open(file_name))
161
+ end
162
+
163
+ def self.record_type(record_type)
164
+ "#{self.name}::#{record_type.camelize}".constantize
165
+ end
166
+
167
+ end
168
+ end
@@ -0,0 +1,5 @@
1
+ module Absa
2
+ module H2h
3
+ VERSION = "0.0.11"
4
+ end
5
+ end
data/lib/absa-h2h.rb ADDED
@@ -0,0 +1,22 @@
1
+ require "absa-h2h/version"
2
+ require "active_support/core_ext/string"
3
+ require "yaml"
4
+ #require 'strata'
5
+
6
+ module Absa
7
+ module H2h
8
+ CONFIG_DIR = File.expand_path(File.dirname(__FILE__)) + "/config"
9
+ end
10
+ end
11
+
12
+ require 'absa-h2h/transmission/set'
13
+ require 'absa-h2h/transmission/record'
14
+ require 'absa-h2h/transmission/document'
15
+ require 'absa-h2h/account_holder_verification'
16
+ require 'absa-h2h/account_holder_verification_output'
17
+ require 'absa-h2h/eft'
18
+ require 'absa-h2h/eft_output'
19
+ require 'absa-h2h/eft_unpaid'
20
+ require 'absa-h2h/eft_redirect'
21
+ require 'absa-h2h/eft/rejection_code'
22
+ require 'absa-h2h/reply'