ach 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -15,51 +15,60 @@ for a similar Perl library
15
15
  You should consult a copy of the [ACH Rules](http://www.nacha.org) for details
16
16
  on individual fields. You can probably obtain a copy from your bank.
17
17
 
18
- # Create ACH file
19
- ach = ACH::ACHFile.new
20
- trace_number = 0
21
-
22
- # File Header
23
- fh = ach.header
24
- fh.immediate_destination = "000000000"
25
- fh.immediate_destination_name = "BANK NAME"
26
- fh.immediate_origin = "000000000"
27
- fh.immediate_origin_name = "BANK NAME"
28
-
29
- # Batch
30
- batch = ACH::Batch.new
31
- bh = batch.header
32
- bh.company_name = "Company Name"
33
- bh.company_identification = "123456789"
34
- bh.standard_entry_class_code = 'PPD'
35
- bh.company_entry_description = "DESCRIPTION"
36
- bh.company_descriptive_date = Date.today
37
- bh.effective_entry_date = (Date.today + 1)
38
- bh.originating_dfi_identification = "00000000"
39
- ach.batches << batch
40
-
41
- # Detail Entry
42
- ed = ACH::EntryDetail.new
43
- ed.transaction_code = ACH::CHECKING_CREDIT
44
- ed.routing_number = "000000000"
45
- ed.account_number = "00000000000"
46
- ed.amount = 100 # In cents
47
- ed.individual_id_number = "Employee Name"
48
- ed.individual_name = "Employee Name"
49
- ed.originating_dfi_identification = '00000000'
50
- batch.entries << ed
51
- # ... Additional detail entries, possibly including *offsetting entry*, if needed.
52
-
53
- # Insert trace numbers
54
- batch.entries.each{ |entry| entry.trace_number = (trace_number += 1) }
55
-
56
-
57
- output = ach.to_s
58
- File.open("ach.txt", 'w') do |f|
59
- f.write output
60
- end
61
-
62
- p ach.report
18
+ ```ruby
19
+ # Create ACH file
20
+ ach = ACH::ACHFile.new
21
+ trace_number = 0
22
+
23
+ # File Header
24
+ fh = ach.header
25
+ fh.immediate_destination = "000000000"
26
+ fh.immediate_destination_name = "BANK NAME"
27
+ fh.immediate_origin = "000000000"
28
+ fh.immediate_origin_name = "BANK NAME"
29
+
30
+ # Batch
31
+ batch = ACH::Batch.new
32
+ bh = batch.header
33
+ bh.company_name = "Company Name"
34
+ bh.company_identification = "123456789"
35
+ bh.standard_entry_class_code = 'PPD'
36
+ bh.company_entry_description = "DESCRIPTION"
37
+ bh.company_descriptive_date = Date.today
38
+ bh.effective_entry_date = (Date.today + 1)
39
+ bh.originating_dfi_identification = "00000000"
40
+ ach.batches << batch
41
+
42
+ # Detail Entry
43
+ ed = ACH::EntryDetail.new
44
+ ed.transaction_code = ACH::CHECKING_CREDIT
45
+ ed.routing_number = "000000000"
46
+ ed.account_number = "00000000000"
47
+ ed.amount = 100 # In cents
48
+ ed.individual_id_number = "Employee Name"
49
+ ed.individual_name = "Employee Name"
50
+ ed.originating_dfi_identification = '00000000'
51
+ batch.entries << ed
52
+ # ... Additional detail entries, possibly including *offsetting entry*, if needed.
53
+
54
+ # Insert trace numbers
55
+ batch.entries.each{ |entry| entry.trace_number = (trace_number += 1) }
56
+
57
+
58
+ output = ach.to_s
59
+ File.open("ach.txt", 'w') do |f|
60
+ f.write output
61
+ end
62
+
63
+ p ach.report
64
+ ```
65
+
66
+ ```ruby
67
+ # Parse an ACH file
68
+ ach = ACH::ACHFile.new(File.read('examples/ach/fixtures/return_noc.txt'))
69
+ ach.batches.first.entries.first.addenda.first.payment_data
70
+ => "C05992222220280489 1211403932 1211"
71
+ ```
63
72
 
64
73
  ##Copyright
65
74
 
data/Rakefile CHANGED
@@ -22,7 +22,7 @@ end
22
22
 
23
23
  require 'micronaut/rake_task'
24
24
  Micronaut::RakeTask.new(:examples) do |examples|
25
- examples.pattern = 'examples/**/*_example.rb'
25
+ examples.pattern = './examples/**/*_example.rb'
26
26
  examples.ruby_opts << '-Ilib -Iexamples'
27
27
  end
28
28
 
@@ -34,7 +34,7 @@ end
34
34
 
35
35
  task :default => :examples
36
36
 
37
- require 'rake/rdoctask'
37
+ require 'rdoc/task'
38
38
  Rake::RDocTask.new do |rdoc|
39
39
  version = File.exist?('VERSION') ? File.read('VERSION') : ''
40
40
  rdoc.rdoc_dir = 'rdoc'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.2
1
+ 0.4.0
data/ach.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "ach"
8
- s.version = "0.3.2"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jared Morgan", "Josh Puetz"]
12
- s.date = "2012-11-02"
12
+ s.date = "2012-11-07"
13
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
14
  s.email = "jmorgan@morgancreative.net"
15
15
  s.extra_rdoc_files = [
@@ -25,6 +25,8 @@ Gem::Specification.new do |s|
25
25
  "examples/ach/ach_file_example.rb",
26
26
  "examples/ach/batch_example.rb",
27
27
  "examples/ach/field_identifiers_example.rb",
28
+ "examples/ach/fixtures/return_noc.txt",
29
+ "examples/ach/parse_example.rb",
28
30
  "examples/ach/records/batch_control_example.rb",
29
31
  "examples/ach/records/batch_header_example.rb",
30
32
  "examples/ach/records/ctx_entry_detail_example.rb",
@@ -47,7 +49,7 @@ Gem::Specification.new do |s|
47
49
  ]
48
50
  s.homepage = "http://github.com/jm81/ach"
49
51
  s.require_paths = ["lib"]
50
- s.rubygems_version = "1.8.11"
52
+ s.rubygems_version = "1.8.10"
51
53
  s.summary = "Helper for building ACH files in Ruby"
52
54
 
53
55
  if s.respond_to? :specification_version then
@@ -10,8 +10,7 @@ describe ACH::FieldIdentifiers do
10
10
  it 'should validate against a Regexp' do
11
11
  @klass.field(:sample, String, nil, nil, /\A\w{5}\Z/)
12
12
  record = @klass.new
13
- msg = "abcd does not match Regexp #{/\A\w{5}\Z/}"
14
- lambda { record.sample = 'abcd' }.should raise_error(RuntimeError, msg)
13
+ lambda { record.sample = 'abcd' }.should raise_error(RuntimeError)
15
14
  record.sample.should be_nil
16
15
  lambda { record.sample = 'abcdef' }.should raise_error(RuntimeError)
17
16
  record.sample.should be_nil
@@ -26,8 +25,7 @@ describe ACH::FieldIdentifiers do
26
25
 
27
26
  @klass.field(:sample, String, nil, nil, block)
28
27
  record = @klass.new
29
- msg = "5 does not pass validation Proc"
30
- lambda { record.sample = 5 }.should raise_error(RuntimeError, msg)
28
+ lambda { record.sample = 5 }.should raise_error(RuntimeError)
31
29
  record.sample.should be_nil
32
30
  lambda { record.sample = 501 }.should raise_error(RuntimeError)
33
31
  record.sample.should be_nil
@@ -0,0 +1,20 @@
1
+ 101 191001234 9922222261210151518A094101Certification Bank-SiliCERTIFICATION BANK-SILIRETURNS
2
+ 5200COMPANY INC DISC DATA 1412345678CORDESCRIPT 1210151210150001992222220000001
3
+ 6211211403993300911569 0000000000A38LTNY2 NAME ONE 1121140390280747
4
+ 798C05992222220280489 1211403932 121140390280747
5
+ 820000000200121140390000000000000000000000001412345678 992222220000001
6
+ 5200COMPANY INC DISC DATA 1412345678PPDDESCRIPT 1210151210150001992222220000002
7
+ 6261211403993300911569 0000002536GKGQT9VK NAME TWO 1121140390280738
8
+ 799R07992222220280393 12114039 121140390280738
9
+ 820000000200121140390000000025360000000000001412345678 992222220000002
10
+ 5200COMPANY INC DISC DATA 1412345678PPDDESCRIPT 1210151210150001992222220000003
11
+ 6261211403993300911569 0000002417KYU341VP NAME THREE 1121140390280729
12
+ 799R03992222220280389 12114039 121140390280729
13
+ 820000000200121140390000000024170000000000001412345678 992222220000003
14
+ 9000003000002000000060036342117000000004953000000000000
15
+ 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
16
+ 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
17
+ 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
18
+ 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
19
+ 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
20
+ 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
@@ -0,0 +1,49 @@
1
+ require 'example_helper'
2
+ require 'date'
3
+
4
+ describe "Parse" do
5
+ describe 'Returns and NOC' do
6
+ before(:each) do
7
+ @data = File.read('examples/ach/fixtures/return_noc.txt')
8
+ end
9
+
10
+ it "should parse return/notification of change file" do
11
+ ach = ACH::ACHFile.new(@data)
12
+ fh = ach.header
13
+ fh.immediate_destination.should == "191001234"
14
+ fh.immediate_origin.should == "992222226"
15
+ fh.transmission_datetime.should == Time.utc(2012, 10, 15, 15, 18)
16
+ fh.immediate_destination_name.should == "Certification Bank-Sili"
17
+ fh.immediate_origin_name.should == "CERTIFICATION BANK-SILI"
18
+
19
+ ach.batches.size.should == 3
20
+
21
+ batch = ach.batches[0]
22
+ batch.entries.size.should == 1
23
+ bh = batch.header
24
+ bh.company_name.should == "COMPANY INC"
25
+ bh.company_identification.should == "412345678"
26
+ bh.standard_entry_class_code.should == 'COR'
27
+ bh.company_entry_description.should == "DESCRIPT"
28
+ bh.company_descriptive_date.should == Date.parse('121015')
29
+ bh.effective_entry_date.should == Date.parse('121015')
30
+ bh.originating_dfi_identification.should == "99222222"
31
+
32
+ ed = batch.entries[0]
33
+ ed.transaction_code.should == "21"
34
+ ed.routing_number.should == "121140399"
35
+ ed.account_number.should == "3300911569"
36
+ ed.amount.should == 0 # In cents
37
+ ed.individual_id_number.should == "A38LTNY2"
38
+ ed.individual_name.should == "NAME ONE"
39
+
40
+ batch = ach.batches[1]
41
+ batch.entries.size.should == 1
42
+ bh = batch.header
43
+ bh.standard_entry_class_code.should == 'PPD'
44
+ ed = batch.entries[0]
45
+ ed.amount.should == 2536
46
+ end
47
+
48
+ end
49
+ end
data/lib/ach/ach_file.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'date'
2
+
1
3
  module ACH
2
4
  class ACHFile
3
5
  include FieldIdentifiers
@@ -6,10 +8,14 @@ module ACH
6
8
  attr_reader :header
7
9
  attr_reader :control
8
10
 
9
- def initialize
11
+ def initialize data=nil
10
12
  @batches = []
11
13
  @header = Records::FileHeader.new
12
14
  @control = Records::FileControl.new
15
+
16
+ if data
17
+ parse(data)
18
+ end
13
19
  end
14
20
 
15
21
  def to_s
@@ -37,6 +43,7 @@ module ACH
37
43
  @control.entry_hash += batch.control.entry_hash
38
44
  end
39
45
 
46
+
40
47
  records.collect { |r| r.to_ach }.join("\r\n") + "\r\n"
41
48
  end
42
49
 
@@ -58,5 +65,66 @@ module ACH
58
65
 
59
66
  lines.join("\r\n")
60
67
  end
68
+
69
+ def parse data
70
+ trace_number = 0
71
+ fh = self.header
72
+ batch = nil
73
+ bh = nil
74
+ ed = nil
75
+
76
+ data.strip.split(/\n|\r\n/).each do |line|
77
+ type = line[0..0]
78
+ if type == '1'
79
+ fh.immediate_destination = line[03..12].strip
80
+ fh.immediate_origin = line[13..22].strip
81
+ fh.transmission_datetime = Time.utc('20'+line[23..24], line[25..26], line[27..28], line[29..30], line[31..32])
82
+ fh.file_id_modifier = line[33..33]
83
+ fh.immediate_destination_name = line[40..62].strip
84
+ fh.immediate_origin_name = line[63..85].strip
85
+ fh.reference_code = line[86..93].strip
86
+ elsif type == '5'
87
+ self.batches << batch unless batch.nil?
88
+ batch = ACH::Batch.new
89
+ bh = batch.header
90
+ bh.company_name = line[4..19].strip
91
+ bh.company_identification = line[41..49].strip
92
+ bh.standard_entry_class_code = line[50..52].strip
93
+ bh.company_entry_description = line[53..62].strip
94
+ bh.company_descriptive_date = Date.parse(line[63..68])
95
+ bh.effective_entry_date = Date.parse(line[69..74])
96
+ bh.originating_dfi_identification = line[79..86].strip
97
+ elsif type == '6'
98
+ ed = ACH::CtxEntryDetail.new
99
+ ed.transaction_code = line[1..2]
100
+ ed.routing_number = line[3..11]
101
+ ed.account_number = line[12..28].strip
102
+ ed.amount = line[29..38].to_i # cents
103
+ ed.individual_id_number = line[39..53].strip
104
+ ed.individual_name = line[54..75].strip
105
+ ed.originating_dfi_identification = line[79..86]
106
+ ed.trace_number = line[87..93].to_i
107
+ batch.entries << ed
108
+ elsif type == '7'
109
+ ad = ACH::Addendum.new
110
+ ad.type_code = line[1..2]
111
+ ad.payment_data = line[3..82].strip
112
+ ad.sequence_number = line[83..86].strip.to_i
113
+ ad.entry_detail_sequence_number = line[87..93].to_i
114
+ ed.addenda << ad
115
+ elsif type == '8'
116
+ # skip
117
+ elsif type == '9'
118
+ # skip
119
+ else
120
+ raise "Didn't recognize type code #{type} for this line:\n#{line}"
121
+ end
122
+ end
123
+
124
+ batch.entries << ed unless ed.nil?
125
+ self.batches << batch unless batch.nil?
126
+ batch.entries.each{ |entry| entry.trace_number = (trace_number += 1) }
127
+ to_s
128
+ end
61
129
  end
62
130
  end
data/lib/ach/batch.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  module ACH
2
2
  class Batch
3
3
  attr_reader :entries
4
+ attr_reader :addendas
4
5
  attr_reader :header
5
6
  attr_reader :control
6
7
 
7
8
  def initialize
8
9
  @entries = []
10
+ @addendas = []
9
11
  @header = Records::BatchHeader.new
10
12
  @control = Records::BatchControl.new
11
13
  end
@@ -49,7 +51,7 @@ module ACH
49
51
  @control.originating_dfi_identification = @header.originating_dfi_identification
50
52
  @control.batch_number = @header.batch_number
51
53
 
52
- [@header] + @entries + [@control]
54
+ [@header] + @entries + @addendas + [@control]
53
55
  end
54
56
  end
55
57
  end
@@ -12,11 +12,11 @@ module ACH
12
12
  define_method "#{name}=" do | val |
13
13
  if validate.kind_of?(Regexp)
14
14
  unless val =~ validate
15
- raise RuntimeError, "#{val} does not match Regexp #{validate}"
15
+ raise RuntimeError, "#{val} does not match Regexp #{validate} for field #{name}"
16
16
  end
17
17
  elsif validate.respond_to?(:call) # Proc with value as argument
18
18
  unless validate.call(val)
19
- raise RuntimeError, "#{val} does not pass validation Proc"
19
+ raise RuntimeError, "#{val} does not pass validation Proc for field #{name}"
20
20
  end
21
21
  end
22
22
 
@@ -33,7 +33,7 @@ module ACH
33
33
  elsif default
34
34
  val = default
35
35
  else
36
- raise RuntimeError, "val is nil"
36
+ raise RuntimeError, "val of #{name} is nil"
37
37
  end
38
38
  end
39
39
 
@@ -4,7 +4,7 @@ module ACH::Records
4
4
  @fields = []
5
5
 
6
6
  const_field :record_type, '7'
7
- const_field :type_code, '05'
7
+ field :type_code, String, lambda {|f| f}, '05', /\A\d{2}\Z/
8
8
  field :payment_data, String, lambda { |f| left_justify(f, 80)}
9
9
  field :sequence_number, Integer, lambda { |f| sprintf('%04d', f)}
10
10
  field :entry_detail_sequence_number, Integer, lambda { |f| sprintf('%07d', f)}
@@ -68,7 +68,7 @@ module ACH::Records
68
68
 
69
69
  self.addenda.each {|a|
70
70
  a.entry_detail_sequence_number = self.trace_number
71
- ach_string << "\r\n" + a.to_ach
71
+ ach_string << "\r\n" + a.to_ach
72
72
  }
73
73
  return ach_string
74
74
  end
@@ -4,8 +4,8 @@ module ACH::Records
4
4
 
5
5
  const_field :record_type, '1'
6
6
  const_field :priority_code, '01'
7
- routing_field :immediate_destination
8
- routing_field :immediate_origin
7
+ field :immediate_destination, String, nil, /\A\s?\d{9}\Z/
8
+ field :immediate_origin, String, nil, /\A\d{9,10}\Z/
9
9
  field :transmission_datetime, Time,
10
10
  lambda { |f| f.strftime('%y%m%d%H%M')},
11
11
  lambda { Time.now }
data/lib/ach.rb CHANGED
@@ -36,6 +36,6 @@ end
36
36
 
37
37
  # Include Records module to simplify accessing Records classes.
38
38
  module ACH
39
- VERSION = '0.3.2'
39
+ VERSION = '0.4.0'
40
40
  include Records
41
41
  end
metadata CHANGED
@@ -1,36 +1,29 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: ach
3
- version: !ruby/object:Gem::Version
4
- hash: 23
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 3
9
- - 2
10
- version: 0.3.2
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Jared Morgan
14
9
  - Josh Puetz
15
10
  autorequire:
16
11
  bindir: bin
17
12
  cert_chain: []
18
-
19
- date: 2012-11-02 00:00:00 Z
13
+ date: 2012-11-07 00:00:00.000000000 Z
20
14
  dependencies: []
15
+ description: ! 'ach is a Ruby helper for builder ACH files. In particular, it helps
16
+ with field
21
17
 
22
- description: |
23
- ach is a Ruby helper for builder ACH files. In particular, it helps with field
24
18
  order and alignment, and adds padding lines to end of file.
25
19
 
20
+ '
26
21
  email: jmorgan@morgancreative.net
27
22
  executables: []
28
-
29
23
  extensions: []
30
-
31
- extra_rdoc_files:
24
+ extra_rdoc_files:
32
25
  - README.md
33
- files:
26
+ files:
34
27
  - .autotest
35
28
  - MIT-LICENSE
36
29
  - README.md
@@ -40,6 +33,8 @@ files:
40
33
  - examples/ach/ach_file_example.rb
41
34
  - examples/ach/batch_example.rb
42
35
  - examples/ach/field_identifiers_example.rb
36
+ - examples/ach/fixtures/return_noc.txt
37
+ - examples/ach/parse_example.rb
43
38
  - examples/ach/records/batch_control_example.rb
44
39
  - examples/ach/records/batch_header_example.rb
45
40
  - examples/ach/records/ctx_entry_detail_example.rb
@@ -61,36 +56,26 @@ files:
61
56
  - lib/ach/records/record.rb
62
57
  homepage: http://github.com/jm81/ach
63
58
  licenses: []
64
-
65
59
  post_install_message:
66
60
  rdoc_options: []
67
-
68
- require_paths:
61
+ require_paths:
69
62
  - lib
70
- required_ruby_version: !ruby/object:Gem::Requirement
63
+ required_ruby_version: !ruby/object:Gem::Requirement
71
64
  none: false
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- hash: 3
76
- segments:
77
- - 0
78
- version: "0"
79
- required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
70
  none: false
81
- requirements:
82
- - - ">="
83
- - !ruby/object:Gem::Version
84
- hash: 3
85
- segments:
86
- - 0
87
- version: "0"
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
88
75
  requirements: []
89
-
90
76
  rubyforge_project:
91
- rubygems_version: 1.8.11
77
+ rubygems_version: 1.8.10
92
78
  signing_key:
93
79
  specification_version: 3
94
80
  summary: Helper for building ACH files in Ruby
95
81
  test_files: []
96
-