ach 0.4.1 → 0.4.2

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