ach 0.2.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/MIT-LICENSE +20 -0
- data/README.md +63 -0
- data/Rakefile +44 -0
- data/VERSION +1 -0
- data/ach.gemspec +64 -0
- data/examples/ach/ach_file_example.rb +10 -0
- data/examples/ach/records/batch_control_example.rb +34 -0
- data/examples/ach/records/nines_example.rb +13 -0
- data/examples/example_helper.rb +12 -0
- data/lib/ach.rb +30 -0
- data/lib/ach/ach_file.rb +56 -0
- data/lib/ach/batch.rb +36 -0
- data/lib/ach/field_identifiers.rb +74 -0
- data/lib/ach/records/addendum.rb +0 -0
- data/lib/ach/records/batch_control.rb +29 -0
- data/lib/ach/records/batch_header.rb +30 -0
- data/lib/ach/records/entry_detail.rb +33 -0
- data/lib/ach/records/file_control.rb +17 -0
- data/lib/ach/records/file_header.rb +21 -0
- data/lib/ach/records/nines.rb +15 -0
- data/lib/ach/records/record.rb +19 -0
- metadata +81 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008-2009 Jared E Morgan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
#ACH
|
2
|
+
|
3
|
+
ach is a Ruby helper for builder ACH files. In particular, it helps with field
|
4
|
+
order and alignment, and adds padding lines to end of file.
|
5
|
+
|
6
|
+
**This library has only been used in one production application and for very
|
7
|
+
limited purposes. Please test thoroughly before using in a production
|
8
|
+
environment.**
|
9
|
+
|
10
|
+
See [ACH::Builder](http://search.cpan.org/~tkeefer/ACH-Builder-0.03/lib/ACH/Builder.pm)
|
11
|
+
for a similar Perl library
|
12
|
+
|
13
|
+
##Example
|
14
|
+
|
15
|
+
You should consult a copy of the [ACH Rules](http://www.nacha.org) for details
|
16
|
+
on individual fields. You can probably obtain a copy from your bank.
|
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 = "Company ID"
|
34
|
+
bh.company_entry_description = "DESCRIPTION"
|
35
|
+
bh.company_descriptive_date = Date.today
|
36
|
+
bh.effective_entry_date = (Date.today + 1)
|
37
|
+
bh.originating_dfi_identification = "00000000"
|
38
|
+
|
39
|
+
# Detail Entry
|
40
|
+
ed = ACH::EntryDetail.new
|
41
|
+
ed.transaction_code = ACH::CHECKING_CREDIT
|
42
|
+
ed.routing_number = "000000000"
|
43
|
+
ed.account_number = "00000000000"
|
44
|
+
ed.amount = "100" # In cents
|
45
|
+
ed.individual_id_number = "Employee Name"
|
46
|
+
ed.individual_name = "Employee Name"
|
47
|
+
ed.originating_dfi_identification = '00000000'
|
48
|
+
batch.entries << ed
|
49
|
+
# ... Additional detail entries, possibly including *offsetting entry*, if needed.
|
50
|
+
|
51
|
+
# Insert trace numbers
|
52
|
+
batch.entries.each{ |entry| entry.trace_number = (trace_number += 1) }
|
53
|
+
|
54
|
+
output = ach.to_s
|
55
|
+
File.open("/path/to/ach.txt", 'w') do |f|
|
56
|
+
f.write output
|
57
|
+
end
|
58
|
+
|
59
|
+
p ach.report
|
60
|
+
|
61
|
+
##Copyright
|
62
|
+
|
63
|
+
Copyright (c) 2008-2009 Jared E Morgan, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
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"]
|
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 'rake/rdoctask'
|
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
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/ach.gemspec
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
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 = %q{ach}
|
8
|
+
s.version = "0.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Jared Morgan"]
|
12
|
+
s.date = %q{2009-10-11}
|
13
|
+
s.description = %q{ach is a Ruby helper for builder ACH files. In particular, it helps with field
|
14
|
+
order and alignment, and adds padding lines to end of file.
|
15
|
+
}
|
16
|
+
s.email = %q{jmorgan@morgancreative.net}
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"README.md"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
"MIT-LICENSE",
|
22
|
+
"README.md",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"ach.gemspec",
|
26
|
+
"examples/ach/ach_file_example.rb",
|
27
|
+
"examples/ach/records/batch_control_example.rb",
|
28
|
+
"examples/ach/records/nines_example.rb",
|
29
|
+
"examples/example_helper.rb",
|
30
|
+
"lib/ach.rb",
|
31
|
+
"lib/ach/ach_file.rb",
|
32
|
+
"lib/ach/batch.rb",
|
33
|
+
"lib/ach/field_identifiers.rb",
|
34
|
+
"lib/ach/records/addendum.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/file_control.rb",
|
39
|
+
"lib/ach/records/file_header.rb",
|
40
|
+
"lib/ach/records/nines.rb",
|
41
|
+
"lib/ach/records/record.rb"
|
42
|
+
]
|
43
|
+
s.homepage = %q{http://github.com/jm81/ach}
|
44
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
45
|
+
s.require_paths = ["lib"]
|
46
|
+
s.rubygems_version = %q{1.3.5}
|
47
|
+
s.summary = %q{Helper for building ACH files in Ruby}
|
48
|
+
s.test_files = [
|
49
|
+
"examples/ach/ach_file_example.rb",
|
50
|
+
"examples/ach/records/batch_control_example.rb",
|
51
|
+
"examples/ach/records/nines_example.rb",
|
52
|
+
"examples/example_helper.rb"
|
53
|
+
]
|
54
|
+
|
55
|
+
if s.respond_to? :specification_version then
|
56
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
57
|
+
s.specification_version = 3
|
58
|
+
|
59
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
60
|
+
else
|
61
|
+
end
|
62
|
+
else
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'example_helper'
|
2
|
+
|
3
|
+
describe ACH::Records::BatchControl do
|
4
|
+
before(:each) do
|
5
|
+
@record = ACH::Records::BatchControl.new
|
6
|
+
@record.entry_count = 1
|
7
|
+
@record.entry_hash = 2
|
8
|
+
@record.debit_total = 3
|
9
|
+
@record.credit_total = 4
|
10
|
+
@record.company_identification = '123456789'
|
11
|
+
@record.message_authentication_code = '22345678'
|
12
|
+
@record.originating_dfi_identification = '32345678'
|
13
|
+
@record.batch_number = 5
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#to_ach' do
|
17
|
+
it 'should generate record string' do
|
18
|
+
exp = [
|
19
|
+
'8',
|
20
|
+
'200',
|
21
|
+
'000001',
|
22
|
+
'0000000002',
|
23
|
+
'000000000003',
|
24
|
+
'000000000004',
|
25
|
+
'1123456789',
|
26
|
+
'22345678 ',
|
27
|
+
' ',
|
28
|
+
'32345678',
|
29
|
+
'0000005'
|
30
|
+
]
|
31
|
+
@record.to_ach.should == exp.join('')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'example_helper'
|
2
|
+
|
3
|
+
describe ACH::Records::Nines do
|
4
|
+
before(:each) do
|
5
|
+
@nines = ACH::Records::Nines.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#to_ach' do
|
9
|
+
it 'should generate 94 copies of the digit "9"' do
|
10
|
+
@nines.to_ach.should == ('9' * 94)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'micronaut'
|
3
|
+
require 'ach'
|
4
|
+
|
5
|
+
def not_in_editor?
|
6
|
+
!(ENV.has_key?('TM_MODE') || ENV.has_key?('EMACS') || ENV.has_key?('VIM'))
|
7
|
+
end
|
8
|
+
|
9
|
+
Micronaut.configure do |c|
|
10
|
+
c.color_enabled = not_in_editor?
|
11
|
+
c.filter_run :focused => true
|
12
|
+
end
|
data/lib/ach.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module ACH
|
2
|
+
# transaction codes
|
3
|
+
CHECKING_CREDIT = '22'
|
4
|
+
CHECKING_DEBIT = '27'
|
5
|
+
CHECKING_CREDIT_PRENOTE = '23'
|
6
|
+
CHECKING_DEBIT_PRENOT = '28'
|
7
|
+
|
8
|
+
SAVING_CREDIT = '32'
|
9
|
+
SAVING_DEBIT = '37'
|
10
|
+
SAVING_CREDIT_PRENOTE = '33'
|
11
|
+
SAVING_DEBIT_PRENOT = '38'
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'time'
|
15
|
+
require 'ach/field_identifiers'
|
16
|
+
require 'ach/ach_file'
|
17
|
+
require 'ach/batch'
|
18
|
+
|
19
|
+
# Require records files
|
20
|
+
require 'ach/records/record'
|
21
|
+
|
22
|
+
Dir.new(File.dirname(__FILE__) + '/ach/records').each do |file|
|
23
|
+
require('ach/records/' + File.basename(file)) if File.extname(file) == ".rb"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Include Records module to simplify accessing Records classes.
|
27
|
+
module ACH
|
28
|
+
VERSION = '0.2.0'
|
29
|
+
include Records
|
30
|
+
end
|
data/lib/ach/ach_file.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module ACH
|
2
|
+
class ACHFile
|
3
|
+
include FieldIdentifiers
|
4
|
+
|
5
|
+
attr_reader :batches
|
6
|
+
attr_reader :header
|
7
|
+
attr_reader :control
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@batches = []
|
11
|
+
@header = Records::FileHeader.new
|
12
|
+
@control = Records::FileControl.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
records = []
|
17
|
+
records << @header
|
18
|
+
@batches.each { |b| records += b.to_ach }
|
19
|
+
records << @control
|
20
|
+
|
21
|
+
nines_needed = 10 - (records.length % 10)
|
22
|
+
nines_needed = nines_needed % 10
|
23
|
+
nines_needed.times { records << Records::Nines.new() }
|
24
|
+
|
25
|
+
@control.batch_count = @batches.length
|
26
|
+
@control.block_count = (records.length / 10).ceil
|
27
|
+
|
28
|
+
@control.entry_count = 0
|
29
|
+
@control.debit_total = 0
|
30
|
+
@control.credit_total = 0
|
31
|
+
@control.entry_hash = 0
|
32
|
+
|
33
|
+
@batches.each do | batch |
|
34
|
+
@control.entry_count += batch.entries.length
|
35
|
+
@control.debit_total += batch.control.debit_total
|
36
|
+
@control.credit_total += batch.control.credit_total
|
37
|
+
@control.entry_hash += batch.control.entry_hash
|
38
|
+
end
|
39
|
+
|
40
|
+
records.collect { |r| r.to_ach }.join("\n") + "\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
def report
|
44
|
+
to_s # To ensure correct records
|
45
|
+
lines = []
|
46
|
+
|
47
|
+
@batches.each do | batch |
|
48
|
+
batch.entries.each do | entry |
|
49
|
+
lines << left_justify(entry.individual_name + ": ", 25) +
|
50
|
+
sprintf("% 7d.%02d", entry.amount / 100, entry.amount % 100)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
lines.join("\n")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/ach/batch.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module ACH
|
2
|
+
class Batch
|
3
|
+
attr_reader :entries
|
4
|
+
attr_reader :header
|
5
|
+
attr_reader :control
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@entries = []
|
9
|
+
@header = Records::BatchHeader.new
|
10
|
+
@control = Records::BatchControl.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_ach
|
14
|
+
@control.entry_count = @entries.length
|
15
|
+
@control.debit_total = 0
|
16
|
+
@control.credit_total = 0
|
17
|
+
@control.entry_hash = 0
|
18
|
+
|
19
|
+
@entries.each do |e|
|
20
|
+
if e.debit?
|
21
|
+
@control.debit_total += e.amount
|
22
|
+
else
|
23
|
+
@control.credit_total += e.amount
|
24
|
+
end
|
25
|
+
@control.entry_hash +=
|
26
|
+
(e.routing_number.to_i / 10) # Last digit is not part of Receiving DFI
|
27
|
+
end
|
28
|
+
|
29
|
+
@control.company_identification = @header.company_identification
|
30
|
+
@control.originating_dfi_identification = @header.originating_dfi_identification
|
31
|
+
@control.batch_number = @header.batch_number
|
32
|
+
|
33
|
+
[@header] + @entries + [@control]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,74 @@
|
|
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}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
instance_variable_set( "@#{name}", val )
|
19
|
+
end
|
20
|
+
|
21
|
+
# to_ach
|
22
|
+
define_method "#{name}_to_ach" do
|
23
|
+
val = instance_variable_get( "@#{name}" )
|
24
|
+
|
25
|
+
if val.nil?
|
26
|
+
if default.kind_of?(Proc)
|
27
|
+
val = default.call
|
28
|
+
elsif default
|
29
|
+
val = default
|
30
|
+
else
|
31
|
+
raise RuntimeError, "val is nil"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
if stringify.nil?
|
36
|
+
return val
|
37
|
+
else
|
38
|
+
stringify.call(val)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def const_field(name, val)
|
44
|
+
fields << name
|
45
|
+
|
46
|
+
# to_ach
|
47
|
+
define_method "#{name}_to_ach" do
|
48
|
+
val
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def left_justify(val, length)
|
53
|
+
val_length = val.length
|
54
|
+
if val_length > length
|
55
|
+
val = val[0..(length - 1)]
|
56
|
+
else
|
57
|
+
val = val + (' ' * (length - val_length))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# A routing number, usually, a string consisting of exactly nine digits.
|
62
|
+
# Represented by 'bTTTTAAAAC'.
|
63
|
+
def routing_field(sym)
|
64
|
+
field sym, String, lambda {|f| ' ' + f}, nil, /\A\d{9}\Z/,
|
65
|
+
'A string consisting of exactly nine digits'
|
66
|
+
end
|
67
|
+
|
68
|
+
# A routing number without leading space
|
69
|
+
def spaceless_routing_field(sym)
|
70
|
+
field sym, String, lambda {|f| f}, nil, /\A\d{9}\Z/,
|
71
|
+
'A string consisting of exactly nine digits'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
File without changes
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ACH::Records
|
2
|
+
class BatchControl < Record
|
3
|
+
@fields = []
|
4
|
+
|
5
|
+
const_field :record_type, '8'
|
6
|
+
|
7
|
+
# TODO: This needs to be changed to reflect whether credits, debits or both.
|
8
|
+
const_field :service_class_code, '200'
|
9
|
+
# Many of the fields are calculated in Batch.to_ach
|
10
|
+
field :entry_count, Integer, lambda { |f| sprintf('%06d', f)}
|
11
|
+
field :entry_hash, Integer, lambda { |f| sprintf('%010d', f % (10 ** 10))}
|
12
|
+
field :debit_total, Integer, lambda { |f| sprintf('%012d', f)}
|
13
|
+
field :credit_total, Integer, lambda { |f| sprintf('%012d', f)}
|
14
|
+
|
15
|
+
field :company_identification, String,
|
16
|
+
lambda {|f| '1' + f}, nil, /\A\d{9}\Z/,
|
17
|
+
'Company Tax ID'
|
18
|
+
|
19
|
+
field :message_authentication_code, String,
|
20
|
+
lambda { |f| left_justify(f, 19)}, ''
|
21
|
+
|
22
|
+
const_field :reserved, (' ' * 6)
|
23
|
+
|
24
|
+
field :originating_dfi_identification, String,
|
25
|
+
lambda {|f| f}, nil, /\A\d{8}\Z/
|
26
|
+
|
27
|
+
field :batch_number, Integer, lambda { |f| sprintf('%07d', f)}, 1
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,30 @@
|
|
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
|
+
const_field :service_class_code, '200'
|
9
|
+
field :company_name, String, lambda { |f| left_justify(f, 16)}
|
10
|
+
field :company_discretionary_data, String,
|
11
|
+
lambda { |f| left_justify(f, 20)}, ''
|
12
|
+
field :company_identification, String,
|
13
|
+
lambda {|f| '1' + f}, nil, /\A\d{9}\Z/,
|
14
|
+
'Company Tax ID'
|
15
|
+
const_field :standard_entry_class_code, 'PPD'
|
16
|
+
field :company_entry_description, String,
|
17
|
+
lambda { |f| left_justify(f, 10)}
|
18
|
+
field :company_descriptive_date, Time,
|
19
|
+
lambda { |f| f.strftime('%y%m%d')},
|
20
|
+
lambda { Time.now }
|
21
|
+
field :effective_entry_date, Time,
|
22
|
+
lambda { |f| f.strftime('%y%m%d')}
|
23
|
+
const_field :settlement_date, ' '
|
24
|
+
const_field :originator_status_code, '1'
|
25
|
+
field :originating_dfi_identification, String,
|
26
|
+
lambda {|f| f}, nil, /\A\d{8}\Z/
|
27
|
+
|
28
|
+
field :batch_number, Integer, lambda { |f| sprintf('%07d', f)}, 1
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ACH::Records
|
2
|
+
class EntryDetail < Record
|
3
|
+
@fields = []
|
4
|
+
|
5
|
+
attr_accessor :sorter
|
6
|
+
|
7
|
+
const_field :record_type, '6'
|
8
|
+
field :transaction_code, String,
|
9
|
+
lambda {|f| f}, nil, /\A\d{2}\Z/
|
10
|
+
spaceless_routing_field :routing_number # Receiving DFI Identification
|
11
|
+
# and Check Digit
|
12
|
+
field :account_number, String, lambda { |f| left_justify(f, 17)}
|
13
|
+
field :amount, Integer, lambda { |f| sprintf('%010d', f)}
|
14
|
+
field :individual_id_number, String, lambda { |f| left_justify(f, 15)}
|
15
|
+
field :individual_name, String, lambda { |f| left_justify(f, 22)}
|
16
|
+
field :discretionary_data, String, lambda { |f| left_justify(f, 2)}, ' '
|
17
|
+
field :addenda_record_indicator, Integer,
|
18
|
+
lambda { |f| sprintf('%01d', f)}, 0
|
19
|
+
field :originating_dfi_identification, String,
|
20
|
+
lambda {|f| f}, nil, /\A\d{8}\Z/
|
21
|
+
field :trace_number, Integer, lambda { |f| sprintf('%07d', f)}
|
22
|
+
|
23
|
+
def credit?
|
24
|
+
@transaction_code[1..1] == '2' || @transaction_code[1..1] == '3'
|
25
|
+
end
|
26
|
+
|
27
|
+
def debit?
|
28
|
+
!credit?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Uses '\r\n' (I think)
|
@@ -0,0 +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
|
+
|
@@ -0,0 +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
|
+
routing_field :immediate_destination
|
8
|
+
routing_field :immediate_origin
|
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
|
+
|
@@ -0,0 +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
|
@@ -0,0 +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
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ach
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jared Morgan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-11 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: |
|
17
|
+
ach is a Ruby helper for builder ACH files. In particular, it helps with field
|
18
|
+
order and alignment, and adds padding lines to end of file.
|
19
|
+
|
20
|
+
email: jmorgan@morgancreative.net
|
21
|
+
executables: []
|
22
|
+
|
23
|
+
extensions: []
|
24
|
+
|
25
|
+
extra_rdoc_files:
|
26
|
+
- README.md
|
27
|
+
files:
|
28
|
+
- MIT-LICENSE
|
29
|
+
- README.md
|
30
|
+
- Rakefile
|
31
|
+
- VERSION
|
32
|
+
- ach.gemspec
|
33
|
+
- examples/ach/ach_file_example.rb
|
34
|
+
- examples/ach/records/batch_control_example.rb
|
35
|
+
- examples/ach/records/nines_example.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
|
+
has_rdoc: true
|
50
|
+
homepage: http://github.com/jm81/ach
|
51
|
+
licenses: []
|
52
|
+
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- --charset=UTF-8
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.3.5
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: Helper for building ACH files in Ruby
|
77
|
+
test_files:
|
78
|
+
- examples/ach/ach_file_example.rb
|
79
|
+
- examples/ach/records/batch_control_example.rb
|
80
|
+
- examples/ach/records/nines_example.rb
|
81
|
+
- examples/example_helper.rb
|