ach 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,8 +2,6 @@ require 'example_helper'
2
2
 
3
3
  describe ACH::Batch do
4
4
  before(:each) do
5
- @batch = ACH::Batch.new
6
-
7
5
  @credit = ACH::EntryDetail.new
8
6
  @credit.transaction_code = ACH::CHECKING_CREDIT
9
7
  @credit.routing_number = "000000000"
@@ -30,7 +28,7 @@ describe ACH::Batch do
30
28
  return batch
31
29
  end
32
30
 
33
- describe '#to_s' do
31
+ describe '#to_ach' do
34
32
  it 'should determine BatchHeader#service_class_code if not set' do
35
33
  debits = new_batch
36
34
  debits.entries << @debit << @debit
@@ -86,6 +84,28 @@ describe ACH::Batch do
86
84
  debits.header.service_class_code.should == 225
87
85
  end
88
86
 
87
+ it 'should set BatchControl#company_identification_code_designator from BatchHeader' do
88
+ batch = new_batch
89
+ batch.header.company_identification_code_designator = "3"
90
+ batch.control.company_identification_code_designator.should be_nil # default values are only set when calling to_ach
91
+ batch.to_ach
92
+ batch.control.company_identification_code_designator.should == "3"
93
+ end
94
+
95
+ it 'should set BatchControl#company_identification from BatchHeader' do
96
+ batch = new_batch
97
+ batch.control.company_identification.should be_nil
98
+ batch.to_ach
99
+ batch.control.company_identification.should == "123456789"
100
+ end
101
+
102
+ it 'should set BatchControl#originating_dfi_identification from BatchHeader' do
103
+ batch = new_batch
104
+ batch.control.originating_dfi_identification.should be_nil
105
+ batch.to_ach
106
+ batch.control.originating_dfi_identification.should == "00000000"
107
+ end
108
+
89
109
  it 'should not override BatchHeader#service_class_code if already set' do
90
110
  # Granted that I can't imagine this every being used...
91
111
  batch = new_batch
@@ -94,5 +114,10 @@ describe ACH::Batch do
94
114
  batch.to_ach
95
115
  batch.control.service_class_code.should == 200
96
116
  end
117
+
118
+ it 'should truncate fields that exceed the length in left_justify' do
119
+ @credit.individual_name = "Employee Name That Is Much Too Long"
120
+ @credit.individual_name_to_ach.should == "Employee Name That Is "
121
+ end
97
122
  end
98
123
  end
@@ -2,6 +2,7 @@ require 'example_helper'
2
2
 
3
3
  describe ACH::FieldIdentifiers do
4
4
  describe 'setter' do
5
+
5
6
  before(:each) do
6
7
  @klass = Class.new(ACH::Records::Record)
7
8
  @klass.instance_variable_set(:@fields, [])
@@ -41,5 +42,6 @@ describe ACH::FieldIdentifiers do
41
42
  record.sample = 'abcde'
42
43
  record.instance_variable_get(:@sample).should == 'abcde'
43
44
  end
45
+
44
46
  end
45
47
  end
@@ -0,0 +1,39 @@
1
+ require 'example_helper'
2
+
3
+ describe ACH::Records::FileHeader do
4
+ before(:each) do
5
+ @header = ACH::Records::FileHeader.new
6
+ @header.immediate_destination_name = 'destination'
7
+ @header.immediate_destination = '123456789'
8
+ @header.immediate_origin_name = 'origin'
9
+ @header.immediate_origin = '123456789'
10
+ end
11
+
12
+ describe '#to_ach' do
13
+ it 'has 94 characters' do
14
+ @header.to_ach.should have(94).characters
15
+ end
16
+ end
17
+
18
+ describe '#immediate_origin_to_ach' do
19
+ it 'adds a leading space when only 9 digits' do
20
+ @header.immediate_origin_to_ach.should == ' 123456789'
21
+ end
22
+
23
+ it 'does not add a leading space when 10 digits' do
24
+ @header.immediate_origin = '1234567890'
25
+ @header.immediate_origin_to_ach.should == '1234567890'
26
+ end
27
+ end
28
+
29
+ describe '#immediate_origin_to_ach' do
30
+ it 'adds a leading space when only 9 digits' do
31
+ @header.immediate_destination_to_ach.should == ' 123456789'
32
+ end
33
+
34
+ it 'does not add a leading space when 10 digits' do
35
+ @header.immediate_destination = '1234567890'
36
+ @header.immediate_destination_to_ach.should == '1234567890'
37
+ end
38
+ end
39
+ end
data/lib/ach.rb CHANGED
@@ -4,7 +4,7 @@ module ACH
4
4
  CHECKING_DEBIT = '27'
5
5
  CHECKING_CREDIT_PRENOTE = '23'
6
6
  CHECKING_DEBIT_PRENOTE = '28'
7
-
7
+
8
8
  SAVING_CREDIT = '32'
9
9
  SAVING_DEBIT = '37'
10
10
  SAVING_CREDIT_PRENOTE = '33'
@@ -12,7 +12,7 @@ module ACH
12
12
 
13
13
  LOAN_CREDIT = '52'
14
14
  LOAN_CREDIT_PRENOTE = '53'
15
-
15
+
16
16
  # Valid service class codes
17
17
  SERVICE_CLASS_CODES = [
18
18
  200, # ACH Entries Mixed Debits and Credits
@@ -36,6 +36,6 @@ end
36
36
 
37
37
  # Include Records module to simplify accessing Records classes.
38
38
  module ACH
39
- VERSION = '0.4.0'
39
+ VERSION = '0.4.2'
40
40
  include Records
41
41
  end
@@ -1,57 +1,62 @@
1
- module ACH
2
- class Batch
3
- attr_reader :entries
4
- attr_reader :addendas
5
- attr_reader :header
6
- attr_reader :control
7
-
8
- def initialize
9
- @entries = []
10
- @addendas = []
11
- @header = Records::BatchHeader.new
12
- @control = Records::BatchControl.new
13
- end
14
-
15
- def to_ach
16
- @control.entry_count = @entries.length
17
- @control.debit_total = 0
18
- @control.credit_total = 0
19
- @control.entry_hash = 0
20
- has_debits = false
21
- has_credits = false
22
-
23
- @entries.each do |e|
24
- if e.debit?
25
- @control.debit_total += e.amount
26
- has_debits = true
27
- else
28
- @control.credit_total += e.amount
29
- has_credits = true
30
- end
31
- @control.entry_hash +=
32
- (e.routing_number.to_i / 10) # Last digit is not part of Receiving DFI
33
- end
34
-
35
- # Set service class codes if needed
36
- if @header.service_class_code.nil?
37
- if has_debits && has_credits
38
- @header.service_class_code = 200
39
- elsif has_debits
40
- @header.service_class_code = 225
41
- else
42
- @header.service_class_code = 220
43
- end
44
- end
45
-
46
- if @control.service_class_code.nil?
47
- @control.service_class_code = @header.service_class_code
48
- end
49
-
50
- @control.company_identification = @header.company_identification
51
- @control.originating_dfi_identification = @header.originating_dfi_identification
52
- @control.batch_number = @header.batch_number
53
-
54
- [@header] + @entries + @addendas + [@control]
55
- end
56
- end
57
- end
1
+ module ACH
2
+ class Batch
3
+ attr_reader :entries
4
+ attr_reader :addendas
5
+ attr_reader :header
6
+ attr_reader :control
7
+
8
+ def initialize
9
+ @entries = []
10
+ @addendas = []
11
+ @header = Records::BatchHeader.new
12
+ @control = Records::BatchControl.new
13
+ end
14
+
15
+ def to_ach
16
+ @control.entry_count = @entries.length
17
+ @control.debit_total = 0
18
+ @control.credit_total = 0
19
+ @control.entry_hash = 0
20
+ has_debits = false
21
+ has_credits = false
22
+
23
+ @entries.each do |e|
24
+ if e.debit?
25
+ @control.debit_total += e.amount
26
+ has_debits = true
27
+ else
28
+ @control.credit_total += e.amount
29
+ has_credits = true
30
+ end
31
+ @control.entry_hash +=
32
+ (e.routing_number.to_i / 10) # Last digit is not part of Receiving DFI
33
+ end
34
+
35
+ # Set service class codes if needed
36
+ if @header.service_class_code.nil?
37
+ if has_debits && has_credits
38
+ @header.service_class_code = 200
39
+ elsif has_debits
40
+ @header.service_class_code = 225
41
+ else
42
+ @header.service_class_code = 220
43
+ end
44
+ end
45
+
46
+ if @control.service_class_code.nil?
47
+ @control.service_class_code = @header.service_class_code
48
+ end
49
+
50
+ if ! @header.company_identification_code_designator.nil?
51
+ @control.company_identification_code_designator =
52
+ @header.company_identification_code_designator
53
+ end
54
+
55
+ @control.company_identification = @header.company_identification
56
+ @control.originating_dfi_identification = @header.originating_dfi_identification
57
+ @control.batch_number = @header.batch_number
58
+
59
+ [@header] + @entries + @addendas + [@control]
60
+ end
61
+ end
62
+ end
@@ -1,79 +1,68 @@
1
- module ACH
2
- module FieldIdentifiers
3
- def field(name, klass, stringify = nil, default = nil, validate = nil, msg ='')
4
- fields << name
5
-
6
- # getter
7
- define_method name do
8
- instance_variable_get( "@#{name}" )
9
- end
10
-
11
- # setter (includes validations)
12
- define_method "#{name}=" do | val |
13
- if validate.kind_of?(Regexp)
14
- unless val =~ validate
15
- raise RuntimeError, "#{val} does not match Regexp #{validate} for field #{name}"
16
- end
17
- elsif validate.respond_to?(:call) # Proc with value as argument
18
- unless validate.call(val)
19
- raise RuntimeError, "#{val} does not pass validation Proc for field #{name}"
20
- end
21
- end
22
-
23
- instance_variable_set( "@#{name}", val )
24
- end
25
-
26
- # to_ach
27
- define_method "#{name}_to_ach" do
28
- val = instance_variable_get( "@#{name}" )
29
-
30
- if val.nil?
31
- if default.kind_of?(Proc)
32
- val = default.call
33
- elsif default
34
- val = default
35
- else
36
- raise RuntimeError, "val of #{name} is nil"
37
- end
38
- end
39
-
40
- if stringify.nil?
41
- return val
42
- else
43
- stringify.call(val)
44
- end
45
- end
46
- end
47
-
48
- def const_field(name, val)
49
- fields << name
50
-
51
- # to_ach
52
- define_method "#{name}_to_ach" do
53
- val
54
- end
55
- end
56
-
57
- def left_justify(val, length)
58
- val_length = val.length
59
- if val_length > length
60
- val = val[0..(length - 1)]
61
- else
62
- val = val + (' ' * (length - val_length))
63
- end
64
- end
65
-
66
- # A routing number, usually, a string consisting of exactly nine digits.
67
- # Represented by 'bTTTTAAAAC'.
68
- def routing_field(sym)
69
- field sym, String, lambda {|f| ' ' + f}, nil, /\A\d{9}\Z/,
70
- 'A string consisting of exactly nine digits'
71
- end
72
-
73
- # A routing number without leading space
74
- def spaceless_routing_field(sym)
75
- field sym, String, lambda {|f| f}, nil, /\A\d{9}\Z/,
76
- 'A string consisting of exactly nine digits'
77
- end
78
- end
79
- end
1
+ module ACH
2
+ module FieldIdentifiers
3
+ # NOTE: the msg parameter is unused and should be removed when the API can change
4
+ def field(name, klass, stringify = nil, default = nil, validate = nil, msg ='')
5
+ fields << name
6
+
7
+ # getter
8
+ define_method name do
9
+ instance_variable_get( "@#{name}" )
10
+ end
11
+
12
+ # setter (includes validations)
13
+ define_method "#{name}=" do | val |
14
+ if validate.kind_of?(Regexp)
15
+ unless val =~ validate
16
+ raise RuntimeError, "#{val} does not match Regexp #{validate} for field #{name}"
17
+ end
18
+ elsif validate.respond_to?(:call) # Proc with value as argument
19
+ unless validate.call(val)
20
+ raise RuntimeError, "#{val} does not pass validation Proc for field #{name}"
21
+ end
22
+ end
23
+
24
+ instance_variable_set( "@#{name}", val )
25
+ end
26
+
27
+ # to_ach
28
+ define_method "#{name}_to_ach" do
29
+ val = instance_variable_get( "@#{name}" )
30
+
31
+ if val.nil?
32
+ if default.kind_of?(Proc)
33
+ val = default.call
34
+ elsif default
35
+ val = default
36
+ else
37
+ raise RuntimeError, "val of #{name} is nil"
38
+ end
39
+ end
40
+
41
+ if stringify.nil?
42
+ return val
43
+ else
44
+ stringify.call(val)
45
+ end
46
+ end
47
+ end
48
+
49
+ def const_field(name, val)
50
+ fields << name
51
+
52
+ # to_ach
53
+ define_method "#{name}_to_ach" do
54
+ val
55
+ end
56
+ end
57
+
58
+ # Left justify value and truncate to length if needed
59
+ def left_justify(val, length)
60
+ val[0..(length - 1)].ljust(length)
61
+ end
62
+
63
+ # A routing number without leading space
64
+ def spaceless_routing_field(sym)
65
+ field sym, String, nil, nil, /\A\d{9}\z/
66
+ end
67
+ end
68
+ end
@@ -3,7 +3,7 @@ module ACH::Records
3
3
  @fields = []
4
4
 
5
5
  const_field :record_type, '7'
6
- field :type_code, String, nil, '05', /\A\d{2}\Z/
6
+ field :type_code, String, nil, '05', /\A\d{2}\z/
7
7
  field :payment_data, String, lambda { |f| left_justify(f, 80)}
8
8
  field :sequence_number, Integer, lambda { |f| sprintf('%04d', f)}
9
9
  field :entry_detail_sequence_number, Integer, lambda { |f| sprintf('%07d', f)}
@@ -1,31 +1,31 @@
1
- module ACH::Records
2
- class BatchControl < Record
3
- @fields = []
4
-
5
- const_field :record_type, '8'
6
-
7
- # Many of the fields are calculated in Batch.to_ach
8
- field :service_class_code, String,
9
- lambda { |f| f.to_s }, '200',
10
- lambda { |f| ACH::SERVICE_CLASS_CODES.include?(f.to_i) }
11
- field :entry_count, Integer, lambda { |f| sprintf('%06d', f)}
12
- field :entry_hash, Integer, lambda { |f| sprintf('%010d', f % (10 ** 10))}
13
- field :debit_total, Integer, lambda { |f| sprintf('%012d', f)}
14
- field :credit_total, Integer, lambda { |f| sprintf('%012d', f)}
15
- field :company_identification_code_designator, String, lambda {|f| f}, '1',
16
- /\A(1|3){1}\Z/
17
- field :company_identification, String,
18
- lambda {|f| f}, nil, /\A\d{9}\Z/,
19
- 'Company Tax ID'
20
-
21
- field :message_authentication_code, String,
22
- lambda { |f| left_justify(f, 19)}, ''
23
-
24
- const_field :reserved, (' ' * 6)
25
-
26
- field :originating_dfi_identification, String,
27
- lambda {|f| f}, nil, /\A\d{8}\Z/
28
-
29
- field :batch_number, Integer, lambda { |f| sprintf('%07d', f)}, 1
30
- end
31
- end
1
+ module ACH::Records
2
+ class BatchControl < Record
3
+ @fields = []
4
+
5
+ const_field :record_type, '8'
6
+
7
+ # TODO: many of the fields are calculated in Batch.to_ach, so the defaults
8
+ # should be removed
9
+ field :service_class_code, String,
10
+ lambda { |f| f.to_s }, '200',
11
+ lambda { |f| ACH::SERVICE_CLASS_CODES.include?(f.to_i) }
12
+ field :entry_count, Integer, lambda { |f| sprintf('%06d', f)}
13
+ field :entry_hash, Integer, lambda { |f| sprintf('%010d', f % (10 ** 10))}
14
+ field :debit_total, Integer, lambda { |f| sprintf('%012d', f)}
15
+ field :credit_total, Integer, lambda { |f| sprintf('%012d', f)}
16
+ field :company_identification_code_designator, String, nil, '1',
17
+ /\A[0-9 ]\z/
18
+ field :company_identification, String,
19
+ nil, nil, /\A\d{9}\z/
20
+
21
+ field :message_authentication_code, String,
22
+ lambda { |f| left_justify(f, 19)}, ''
23
+
24
+ const_field :reserved, (' ' * 6)
25
+
26
+ field :originating_dfi_identification, String,
27
+ nil, nil, /\A\d{8}\z/
28
+
29
+ field :batch_number, Integer, lambda { |f| sprintf('%07d', f)}, 1
30
+ end
31
+ end
@@ -1,38 +1,37 @@
1
- module ACH::Records
2
- class BatchHeader < Record
3
- @fields = []
4
-
5
- const_field :record_type, '5'
6
-
7
- # TODO: This needs to be changed to reflect whether credits, debits or both.
8
- field :service_class_code, String,
9
- lambda { |f| f.to_s }, '200',
10
- lambda { |f| ACH::SERVICE_CLASS_CODES.include?(f.to_i) }
11
- field :company_name, String, lambda { |f| left_justify(f, 16)}
12
- field :company_discretionary_data, String,
13
- lambda { |f| left_justify(f, 20)}, ''
14
- field :company_identification_code_designator, String, lambda {|f| f}, '1',
15
- /\A(1|3){1}\Z/
16
- field :company_identification, String,
17
- lambda {|f| f}, nil, /\A\d{9}\Z/,
18
- 'Company Tax ID'
19
- # TODO This should be used to determine whether other records are valid for
20
- # for this code. Should there be a Class for each code?
21
- # The default of PPD is purely for my benefit (Jared Morgan)
22
- field :standard_entry_class_code, String,
23
- lambda { |f| f.upcase }, 'PPD', /\A\w{3}\Z/
24
- field :company_entry_description, String,
25
- lambda { |f| left_justify(f, 10)}
26
- field :company_descriptive_date, Time,
27
- lambda { |f| f.strftime('%y%m%d')},
28
- lambda { Time.now }
29
- field :effective_entry_date, Time,
30
- lambda { |f| f.strftime('%y%m%d')}
31
- const_field :settlement_date, ' '
32
- const_field :originator_status_code, '1'
33
- field :originating_dfi_identification, String,
34
- lambda {|f| f}, nil, /\A\d{8}\Z/
35
-
36
- field :batch_number, Integer, lambda { |f| sprintf('%07d', f)}, 1
37
- end
38
- end
1
+ module ACH::Records
2
+ class BatchHeader < Record
3
+ @fields = []
4
+
5
+ const_field :record_type, '5'
6
+
7
+ # TODO: this is calculated in Batch.to_ach, so the default should be removed
8
+ field :service_class_code, String,
9
+ lambda { |f| f.to_s }, '200',
10
+ lambda { |f| ACH::SERVICE_CLASS_CODES.include?(f.to_i) }
11
+ field :company_name, String, lambda { |f| left_justify(f, 16)}
12
+ field :company_discretionary_data, String,
13
+ lambda { |f| left_justify(f, 20)}, ''
14
+ field :company_identification_code_designator, String, nil, '1',
15
+ /\A[0-9 ]\z/
16
+ field :company_identification, String,
17
+ nil, nil, /\A\d{9}\z/
18
+ # TODO This should be used to determine whether other records are valid for
19
+ # this code. Should there be a Class for each code?
20
+ # The default of PPD is purely for my benefit (Jared Morgan)
21
+ field :standard_entry_class_code, String,
22
+ lambda { |f| f.upcase }, 'PPD', /\A\w{3}\z/
23
+ field :company_entry_description, String,
24
+ lambda { |f| left_justify(f, 10)}
25
+ field :company_descriptive_date, Time,
26
+ lambda { |f| f.strftime('%y%m%d')},
27
+ lambda { Time.now }
28
+ field :effective_entry_date, Time,
29
+ lambda { |f| f.strftime('%y%m%d')}
30
+ const_field :settlement_date, ' '
31
+ const_field :originator_status_code, '1'
32
+ field :originating_dfi_identification, String,
33
+ nil, nil, /\A\d{8}\z/
34
+
35
+ field :batch_number, Integer, lambda { |f| sprintf('%07d', f)}, 1
36
+ end
37
+ end
@@ -1,77 +1,77 @@
1
- module ACH::Records
2
- class EntryDetail < Record
3
- CREDIT_RECORD_TRANSACTION_CODE_ENDING_DIGITS = ["0", "1", "2", "3", "4"]
4
-
5
- @fields = []
6
-
7
- attr_accessor :sorter
8
-
9
- const_field :record_type, '6'
10
- field :transaction_code, String,
11
- lambda {|f| f}, nil, /\A\d{2}\Z/
12
- spaceless_routing_field :routing_number # Receiving DFI Identification
13
- # and Check Digit
14
- field :account_number, String, lambda { |f| left_justify(f, 17)}
15
- field :amount, Integer, lambda { |f| sprintf('%010d', f)}
16
- field :individual_id_number, String, lambda { |f| left_justify(f, 15)}
17
- field :individual_name, String, lambda { |f| left_justify(f, 22)}
18
- field :discretionary_data, String, lambda { |f| left_justify(f, 2)}, ' '
19
- field :addenda_record_indicator, Integer,
20
- lambda { |f| sprintf('%01d', f)}, 0
21
- field :originating_dfi_identification, String,
22
- lambda {|f| f}, nil, /\A\d{8}\Z/
23
- field :trace_number, Integer, lambda { |f| sprintf('%07d', f)}
24
-
25
- def credit?
26
- CREDIT_RECORD_TRANSACTION_CODE_ENDING_DIGITS.include?(@transaction_code[1..1])
27
- end
28
-
29
- def debit?
30
- !credit?
31
- end
32
-
33
- def amount_value
34
- return self.amount
35
- end
36
-
37
- end
38
-
39
- class CtxEntryDetail < EntryDetail
40
-
41
- @fields = EntryDetail.fields.slice(0, 6)
42
- field :number_of_addenda_records, Integer, lambda { |f| sprintf('%04d', f)}, 0
43
- field :individual_name, String, lambda { |f| left_justify(f, 16)}
44
- const_field :reserved, ' '
45
- field :discretionary_data, String, lambda { |f| left_justify(f, 2)}, ' '
46
- field :addenda_record_indicator, Integer,
47
- lambda { |f| sprintf('%01d', f)}
48
- field :originating_dfi_identification, String,
49
- lambda {|f| f}, nil, /\A\d{8}\Z/
50
- field :trace_number, Integer, lambda { |f| sprintf('%07d', f)}
51
-
52
-
53
- attr_reader :addenda
54
-
55
- def initialize
56
- @addenda = []
57
- end
58
-
59
- def addenda_records?
60
- return !self.addenda.empty?
61
- end
62
-
63
- def to_ach
64
- self.addenda_record_indicator = (self.addenda.empty? ? 0 : 1)
65
- self.number_of_addenda_records = self.addenda.length
66
-
67
- ach_string = super
68
-
69
- self.addenda.each {|a|
70
- a.entry_detail_sequence_number = self.trace_number
71
- ach_string << "\r\n" + a.to_ach
72
- }
73
- return ach_string
74
- end
75
-
76
- end
77
- end
1
+ module ACH::Records
2
+ class EntryDetail < Record
3
+ CREDIT_RECORD_TRANSACTION_CODE_ENDING_DIGITS = ["0", "1", "2", "3", "4"]
4
+
5
+ @fields = []
6
+
7
+ attr_accessor :sorter
8
+
9
+ const_field :record_type, '6'
10
+ field :transaction_code, String,
11
+ nil, nil, /\A\d{2}\z/
12
+ spaceless_routing_field :routing_number # Receiving DFI Identification
13
+ # and Check Digit
14
+ field :account_number, String, lambda { |f| left_justify(f, 17)}
15
+ field :amount, Integer, lambda { |f| sprintf('%010d', f)}
16
+ field :individual_id_number, String, lambda { |f| left_justify(f, 15)}
17
+ field :individual_name, String, lambda { |f| left_justify(f, 22)}
18
+ field :discretionary_data, String, lambda { |f| left_justify(f, 2)}, ' '
19
+ field :addenda_record_indicator, Integer,
20
+ lambda { |f| sprintf('%01d', f)}, 0
21
+ field :originating_dfi_identification, String,
22
+ nil, nil, /\A\d{8}\z/
23
+ field :trace_number, Integer, lambda { |f| sprintf('%07d', f)}
24
+
25
+ def credit?
26
+ CREDIT_RECORD_TRANSACTION_CODE_ENDING_DIGITS.include?(@transaction_code[1..1])
27
+ end
28
+
29
+ def debit?
30
+ !credit?
31
+ end
32
+
33
+ def amount_value
34
+ return self.amount
35
+ end
36
+
37
+ end
38
+
39
+ class CtxEntryDetail < EntryDetail
40
+
41
+ @fields = EntryDetail.fields.slice(0, 6)
42
+ field :number_of_addenda_records, Integer, lambda { |f| sprintf('%04d', f)}, 0
43
+ field :individual_name, String, lambda { |f| left_justify(f, 16)}
44
+ const_field :reserved, ' '
45
+ field :discretionary_data, String, lambda { |f| left_justify(f, 2)}, ' '
46
+ field :addenda_record_indicator, Integer,
47
+ lambda { |f| sprintf('%01d', f)}
48
+ field :originating_dfi_identification, String,
49
+ nil, nil, /\A\d{8}\z/
50
+ field :trace_number, Integer, lambda { |f| sprintf('%07d', f)}
51
+
52
+
53
+ attr_reader :addenda
54
+
55
+ def initialize
56
+ @addenda = []
57
+ end
58
+
59
+ def addenda_records?
60
+ return !self.addenda.empty?
61
+ end
62
+
63
+ def to_ach
64
+ self.addenda_record_indicator = (self.addenda.empty? ? 0 : 1)
65
+ self.number_of_addenda_records = self.addenda.length
66
+
67
+ ach_string = super
68
+
69
+ self.addenda.each {|a|
70
+ a.entry_detail_sequence_number = self.trace_number
71
+ ach_string << "\r\n" + a.to_ach
72
+ }
73
+ return ach_string
74
+ end
75
+
76
+ end
77
+ end
@@ -1,17 +1,17 @@
1
- module ACH::Records
2
- class FileControl < Record
3
- @fields = []
4
-
5
- const_field :record_type, '9'
6
- # Many of the fields are calculated in ACHFile.to_ach
7
- field :batch_count, Integer, lambda { |f| sprintf('%06d', f)}
8
- field :block_count, Integer, lambda { |f| sprintf('%06d', f)}
9
- field :entry_count, Integer, lambda { |f| sprintf('%08d', f)}
10
- field :entry_hash, Integer, lambda { |f| sprintf('%010d', f % (10 ** 10))}
11
-
12
- field :debit_total, Integer, lambda { |f| sprintf('%012d', f)}
13
- field :credit_total, Integer, lambda { |f| sprintf('%012d', f)}
14
- const_field :reserved, (' ' * 39)
15
- end
16
- end
17
-
1
+ module ACH::Records
2
+ class FileControl < Record
3
+ @fields = []
4
+
5
+ const_field :record_type, '9'
6
+ # Many of the fields are calculated in ACHFile.to_ach
7
+ field :batch_count, Integer, lambda { |f| sprintf('%06d', f)}
8
+ field :block_count, Integer, lambda { |f| sprintf('%06d', f)}
9
+ field :entry_count, Integer, lambda { |f| sprintf('%08d', f)}
10
+ field :entry_hash, Integer, lambda { |f| sprintf('%010d', f % (10 ** 10))}
11
+
12
+ field :debit_total, Integer, lambda { |f| sprintf('%012d', f)}
13
+ field :credit_total, Integer, lambda { |f| sprintf('%012d', f)}
14
+ const_field :reserved, (' ' * 39)
15
+ end
16
+ end
17
+
@@ -1,21 +1,21 @@
1
- module ACH::Records
2
- class FileHeader < Record
3
- @fields = []
4
-
5
- const_field :record_type, '1'
6
- const_field :priority_code, '01'
7
- field :immediate_destination, String, nil, /\A\s?\d{9}\Z/
8
- field :immediate_origin, String, nil, /\A\d{9,10}\Z/
9
- field :transmission_datetime, Time,
10
- lambda { |f| f.strftime('%y%m%d%H%M')},
11
- lambda { Time.now }
12
- field :file_id_modifier, String, nil, 'A', /\A\w\Z/
13
- const_field :record_size, '094'
14
- const_field :blocking_factor, '10'
15
- const_field :format_code, '1'
16
- field :immediate_destination_name, String, lambda { |f| left_justify(f, 23)}
17
- field :immediate_origin_name, String, lambda { |f| left_justify(f, 23)}
18
- field :reference_code, String, lambda { |f| left_justify(f, 8)}, ''
19
- end
20
- end
21
-
1
+ module ACH::Records
2
+ class FileHeader < Record
3
+ @fields = []
4
+
5
+ const_field :record_type, '1'
6
+ const_field :priority_code, '01'
7
+ field :immediate_destination, String, lambda { |f| f.rjust(10) }, nil, /\A\d{9,10}\z/
8
+ field :immediate_origin, String, lambda { |f| f.rjust(10) }, nil, /\A\d{9,10}\z/
9
+ field :transmission_datetime, Time,
10
+ lambda { |f| f.strftime('%y%m%d%H%M')},
11
+ lambda { Time.now }
12
+ field :file_id_modifier, String, nil, 'A', /\A\w\z/
13
+ const_field :record_size, '094'
14
+ const_field :blocking_factor, '10'
15
+ const_field :format_code, '1'
16
+ field :immediate_destination_name, String, lambda { |f| left_justify(f, 23)}
17
+ field :immediate_origin_name, String, lambda { |f| left_justify(f, 23)}
18
+ field :reference_code, String, lambda { |f| left_justify(f, 8)}, ''
19
+ end
20
+ end
21
+
@@ -1,15 +1,15 @@
1
- module ACH::Records
2
- # The number of records must be a multiple of ten (that is, complete 940
3
- # character blocks). If needed, rows that consist of the digit "9" may be
4
- # used to pad the the file.
5
- #
6
- # "Nines" records thus consist of a single constant field containing the digit
7
- # "9" 94 times.
8
- #
9
- # See 2008 ACH Rules, Appx 1, Sec 1.5; Appx 2, Sec 2.3
10
- class Nines < Record
11
- @fields = []
12
-
13
- const_field :record_type, ('9' * 94)
14
- end
15
- end
1
+ module ACH::Records
2
+ # The number of records must be a multiple of ten (that is, complete 940
3
+ # character blocks). If needed, rows that consist of the digit "9" may be
4
+ # used to pad the the file.
5
+ #
6
+ # "Nines" records thus consist of a single constant field containing the digit
7
+ # "9" 94 times.
8
+ #
9
+ # See 2008 ACH Rules, Appx 1, Sec 1.5; Appx 2, Sec 2.3
10
+ class Nines < Record
11
+ @fields = []
12
+
13
+ const_field :record_type, ('9' * 94)
14
+ end
15
+ end
@@ -1,19 +1,19 @@
1
- module ACH
2
- module Records
3
- class Record
4
- @fields = []
5
-
6
- class << self
7
- def fields
8
- @fields
9
- end
10
- end
11
-
12
- extend(FieldIdentifiers)
13
-
14
- def to_ach
15
- self.class.fields.collect { |f| send("#{f}_to_ach") }.join('').upcase
16
- end
17
- end
18
- end
19
- end
1
+ module ACH
2
+ module Records
3
+ class Record
4
+ @fields = []
5
+
6
+ class << self
7
+ def fields
8
+ @fields
9
+ end
10
+ end
11
+
12
+ extend(FieldIdentifiers)
13
+
14
+ def to_ach
15
+ self.class.fields.collect { |f| send("#{f}_to_ach") }.join('').upcase
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module ACH
2
+ VERSION = '0.4.2'.freeze
3
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ach
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,51 +10,56 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-12-10 00:00:00.000000000 Z
14
- dependencies: []
15
- description: ! 'ach is a Ruby helper for builder ACH files. In particular, it helps
16
- with field
17
-
18
- order and alignment, and adds padding lines to end of file.
19
-
20
- '
13
+ date: 2013-01-12 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: micronaut
17
+ requirement: &71760730 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: *71760730
26
+ description: ach is a Ruby helper for building and parsing ACH files. In particular,
27
+ it helps with field order and alignment, and adds padding lines to end of file.
21
28
  email: jmorgan@morgancreative.net
22
29
  executables: []
23
30
  extensions: []
24
31
  extra_rdoc_files:
25
32
  - README.md
26
33
  files:
27
- - .autotest
34
+ - lib/ach/records/file_header.rb
35
+ - lib/ach/records/batch_control.rb
36
+ - lib/ach/records/batch_header.rb
37
+ - lib/ach/records/entry_detail.rb
38
+ - lib/ach/records/nines.rb
39
+ - lib/ach/records/file_control.rb
40
+ - lib/ach/records/record.rb
41
+ - lib/ach/records/addendum.rb
42
+ - lib/ach/ach_file.rb
43
+ - lib/ach/field_identifiers.rb
44
+ - lib/ach/version.rb
45
+ - lib/ach/batch.rb
46
+ - lib/ach.rb
28
47
  - MIT-LICENSE
29
48
  - README.md
30
- - Rakefile
31
- - VERSION
32
- - ach.gemspec
33
- - examples/ach/ach_file_example.rb
34
- - examples/ach/batch_example.rb
35
- - examples/ach/field_identifiers_example.rb
49
+ - examples/example_helper.rb
36
50
  - examples/ach/fixtures/return_noc.txt
37
- - examples/ach/parse_example.rb
38
51
  - examples/ach/records/batch_control_example.rb
39
- - examples/ach/records/batch_header_example.rb
40
52
  - examples/ach/records/ctx_entry_detail_example.rb
53
+ - examples/ach/records/file_header_example.rb
41
54
  - examples/ach/records/ctx_entry_detail_test.rb
42
55
  - examples/ach/records/nines_example.rb
43
56
  - examples/ach/records/shared/batch_summaries.rb
44
- - examples/example_helper.rb
45
- - lib/ach.rb
46
- - lib/ach/ach_file.rb
47
- - lib/ach/batch.rb
48
- - lib/ach/field_identifiers.rb
49
- - lib/ach/records/addendum.rb
50
- - lib/ach/records/batch_control.rb
51
- - lib/ach/records/batch_header.rb
52
- - lib/ach/records/entry_detail.rb
53
- - lib/ach/records/file_control.rb
54
- - lib/ach/records/file_header.rb
55
- - lib/ach/records/nines.rb
56
- - lib/ach/records/record.rb
57
- homepage: http://github.com/jm81/ach
57
+ - examples/ach/records/batch_header_example.rb
58
+ - examples/ach/parse_example.rb
59
+ - examples/ach/ach_file_example.rb
60
+ - examples/ach/batch_example.rb
61
+ - examples/ach/field_identifiers_example.rb
62
+ homepage: https://github.com/jm81/ach
58
63
  licenses: []
59
64
  post_install_message:
60
65
  rdoc_options: []
@@ -77,5 +82,19 @@ rubyforge_project:
77
82
  rubygems_version: 1.8.10
78
83
  signing_key:
79
84
  specification_version: 3
80
- summary: Helper for building ACH files in Ruby
81
- test_files: []
85
+ summary: Helper for building ACH files
86
+ test_files:
87
+ - examples/example_helper.rb
88
+ - examples/ach/fixtures/return_noc.txt
89
+ - examples/ach/records/batch_control_example.rb
90
+ - examples/ach/records/ctx_entry_detail_example.rb
91
+ - examples/ach/records/file_header_example.rb
92
+ - examples/ach/records/ctx_entry_detail_test.rb
93
+ - examples/ach/records/nines_example.rb
94
+ - examples/ach/records/shared/batch_summaries.rb
95
+ - examples/ach/records/batch_header_example.rb
96
+ - examples/ach/parse_example.rb
97
+ - examples/ach/ach_file_example.rb
98
+ - examples/ach/batch_example.rb
99
+ - examples/ach/field_identifiers_example.rb
100
+ has_rdoc:
data/.autotest DELETED
@@ -1,46 +0,0 @@
1
- require 'autotest'
2
-
3
- Autotest.add_hook :initialize do |at|
4
- at.clear_mappings
5
- at.failed_results_re = /^\d+\)\n(?:\e\[\d*m)?(?:.*?Error in )?'([^\n]*)'(?: FAILED)?(?:\e\[\d*m)?\n(.*?)\n\n/m
6
- at.completed_re = /\n(?:\e\[\d*m)?\d* examples?/m
7
-
8
- at.add_mapping(%r%^examples/.*_example.rb$%) { |filename, _|
9
- filename
10
- }
11
-
12
- at.add_mapping(%r%^lib/(.*)\.rb$%) { |filename, m|
13
- ["examples/lib/#{m[1]}_example.rb"]
14
- }
15
-
16
- at.add_mapping(%r%^examples/(example_helper|shared/.*)\.rb$%) {
17
- at.files_matching %r%^examples/.*_example\.rb$%
18
- }
19
- end
20
-
21
- class Autotest
22
- remove_method :consolidate_failures if instance_methods(false).include?("consolidate_failures")
23
- remove_method :make_test_cmd if instance_methods(false).include?("make_test_cmd")
24
-
25
- def consolidate_failures(failed)
26
- filters = new_hash_of_arrays
27
- failed.each do |spec, trace|
28
- if trace =~ /\n(\.\/)?(.*example\.rb):[\d]+:\Z?/
29
- filters[$2] << spec
30
- end
31
- end
32
- return filters
33
- end
34
-
35
- def make_test_cmd(files_to_test)
36
- puts 'command'
37
- return '' if files_to_test.empty?
38
-
39
- examples = files_to_test.keys.flatten
40
-
41
- examples.map! {|f| %Q(require "#{f}")}
42
-
43
- return "#{ruby} -Ilib -Iexamples -e '#{examples.join("; ")}'"
44
- end
45
-
46
- end
data/Rakefile DELETED
@@ -1,47 +0,0 @@
1
- require 'rubygems'
2
- require 'rake'
3
-
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = "ach"
8
- gem.summary = %{Helper for building ACH files in Ruby}
9
- gem.description = <<EOF
10
- ach is a Ruby helper for builder ACH files. In particular, it helps with field
11
- order and alignment, and adds padding lines to end of file.
12
- EOF
13
- gem.email = "jmorgan@morgancreative.net"
14
- gem.homepage = "http://github.com/jm81/ach"
15
- gem.authors = ["Jared Morgan", "Josh Puetz"]
16
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
- end
18
- Jeweler::GemcutterTasks.new
19
- rescue LoadError
20
- puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
21
- end
22
-
23
- require 'micronaut/rake_task'
24
- Micronaut::RakeTask.new(:examples) do |examples|
25
- examples.pattern = './examples/**/*_example.rb'
26
- examples.ruby_opts << '-Ilib -Iexamples'
27
- end
28
-
29
- Micronaut::RakeTask.new(:rcov) do |examples|
30
- examples.pattern = 'examples/**/*_example.rb'
31
- examples.rcov_opts = '-Ilib -Iexamples'
32
- examples.rcov = true
33
- end
34
-
35
- task :default => :examples
36
-
37
- require 'rdoc/task'
38
- Rake::RDocTask.new do |rdoc|
39
- version = File.exist?('VERSION') ? File.read('VERSION') : ''
40
- rdoc.rdoc_dir = 'rdoc'
41
- rdoc.title = "ACH #{version}"
42
- rdoc.rdoc_files.include('README*')
43
- rdoc.rdoc_files.include('lib/**/*.rb')
44
- end
45
-
46
-
47
-
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.4.1
@@ -1,64 +0,0 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
5
-
6
- Gem::Specification.new do |s|
7
- s.name = "ach"
8
- s.version = "0.4.1"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Jared Morgan", "Josh Puetz"]
12
- s.date = "2012-12-10"
13
- s.description = "ach is a Ruby helper for builder ACH files. In particular, it helps with field\norder and alignment, and adds padding lines to end of file.\n"
14
- s.email = "jmorgan@morgancreative.net"
15
- s.extra_rdoc_files = [
16
- "README.md"
17
- ]
18
- s.files = [
19
- ".autotest",
20
- "MIT-LICENSE",
21
- "README.md",
22
- "Rakefile",
23
- "VERSION",
24
- "ach.gemspec",
25
- "examples/ach/ach_file_example.rb",
26
- "examples/ach/batch_example.rb",
27
- "examples/ach/field_identifiers_example.rb",
28
- "examples/ach/fixtures/return_noc.txt",
29
- "examples/ach/parse_example.rb",
30
- "examples/ach/records/batch_control_example.rb",
31
- "examples/ach/records/batch_header_example.rb",
32
- "examples/ach/records/ctx_entry_detail_example.rb",
33
- "examples/ach/records/ctx_entry_detail_test.rb",
34
- "examples/ach/records/nines_example.rb",
35
- "examples/ach/records/shared/batch_summaries.rb",
36
- "examples/example_helper.rb",
37
- "lib/ach.rb",
38
- "lib/ach/ach_file.rb",
39
- "lib/ach/batch.rb",
40
- "lib/ach/field_identifiers.rb",
41
- "lib/ach/records/addendum.rb",
42
- "lib/ach/records/batch_control.rb",
43
- "lib/ach/records/batch_header.rb",
44
- "lib/ach/records/entry_detail.rb",
45
- "lib/ach/records/file_control.rb",
46
- "lib/ach/records/file_header.rb",
47
- "lib/ach/records/nines.rb",
48
- "lib/ach/records/record.rb"
49
- ]
50
- s.homepage = "http://github.com/jm81/ach"
51
- s.require_paths = ["lib"]
52
- s.rubygems_version = "1.8.10"
53
- s.summary = "Helper for building ACH files in Ruby"
54
-
55
- if s.respond_to? :specification_version then
56
- s.specification_version = 3
57
-
58
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
59
- else
60
- end
61
- else
62
- end
63
- end
64
-