ach 0.4.7 → 0.4.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +2 -0
- data/lib/ach/ach_file.rb +13 -4
- data/lib/ach/ach_file.rb.orig +150 -0
- data/lib/ach/batch.rb +1 -1
- data/lib/ach/records/batch_control.rb +1 -2
- data/lib/ach/records/batch_header.rb +1 -1
- data/lib/ach/records/entry_detail.rb +25 -24
- data/lib/ach/version.rb +1 -1
- metadata +13 -12
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NzUzNDkyMzc0ZDk4ODk2MzhjZTY0MGE5Y2ZlMmFkNmE0NjZiNjVjNw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YzQ4ZGQ1MTAwNGYwOTAxZTFlZDk1OGFkMjJhNDgzNGNiNzBjNWJiYQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MjUwMTUwM2U4NmFkZDI3ZGVjMTgzMDRhZWZkNWMzY2IwYTJiNjU4MjA3YjU2
|
10
|
+
MzYwYjVhYzdkMDVmZDVlMzEyZDEwNWM0YmY4YTU3ODhiMjhmZTgxMWYwOGQ5
|
11
|
+
NmE0NjQxN2RjNTNhODIyYTg4ZWJjYTE5N2NlZTJiNmRhYjYyOGI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ODFmY2MzYmM3NTlkMWMwNWU2OTAzYmU1ZTRlZDgyMGMzNzVlZmE5YjE0YzBj
|
14
|
+
NmZjNjhlN2VlNTI0MjNiYzU5NDZhZDU3YWNmYWU1MDFlODBmMGQzMmE4MGQ0
|
15
|
+
ZWQxZjI0ZWQxNjkwNTJlOWUyMjEwZDg5MmQ2OTViYzJiNDEzODY=
|
data/README.md
CHANGED
@@ -70,6 +70,8 @@ ach.batches.first.entries.first.addenda.first.payment_data
|
|
70
70
|
=> "C05992222220280489 1211403932 1211"
|
71
71
|
```
|
72
72
|
|
73
|
+
**Note:** When adding an amount to your ach file, it needs to be in cents. So you'll want to multiply any dollar amounts by 100
|
74
|
+
|
73
75
|
##Copyright
|
74
76
|
|
75
77
|
Copyright (c) 2008-2009 Jared E Morgan, released under the MIT license
|
data/lib/ach/ach_file.rb
CHANGED
@@ -14,7 +14,11 @@ module ACH
|
|
14
14
|
@control = Records::FileControl.new
|
15
15
|
|
16
16
|
if data
|
17
|
-
|
17
|
+
if (data.encode(Encoding.find('ASCII'),ENCODING_OPTIONS) =~ /\n|\r\n/).nil?
|
18
|
+
parse_fixed(data)
|
19
|
+
else
|
20
|
+
parse(data)
|
21
|
+
end
|
18
22
|
end
|
19
23
|
end
|
20
24
|
|
@@ -37,13 +41,12 @@ module ACH
|
|
37
41
|
@control.entry_hash = 0
|
38
42
|
|
39
43
|
@batches.each do | batch |
|
40
|
-
@control.entry_count += batch.entries.
|
44
|
+
@control.entry_count += batch.entries.inject(0) { |total, entry| total + entry.records_count }
|
41
45
|
@control.debit_total += batch.control.debit_total
|
42
46
|
@control.credit_total += batch.control.credit_total
|
43
47
|
@control.entry_hash += batch.control.entry_hash
|
44
48
|
end
|
45
49
|
|
46
|
-
|
47
50
|
records.collect { |r| r.to_ach }.join("\r\n") + "\r\n"
|
48
51
|
end
|
49
52
|
|
@@ -66,6 +69,12 @@ module ACH
|
|
66
69
|
lines.join("\r\n")
|
67
70
|
end
|
68
71
|
|
72
|
+
def parse_fixed data
|
73
|
+
# replace with a space to preserve the record-lengths
|
74
|
+
encoded_data = data.encode(Encoding.find('ASCII'),{:invalid => :replace, :undef => :replace, :replace => ' '})
|
75
|
+
parse encoded_data.scan(/.{94}/).join("\n")
|
76
|
+
end
|
77
|
+
|
69
78
|
def parse data
|
70
79
|
trace_number = 0
|
71
80
|
fh = self.header
|
@@ -89,7 +98,7 @@ module ACH
|
|
89
98
|
batch = ACH::Batch.new
|
90
99
|
bh = batch.header
|
91
100
|
bh.company_name = line[4..19].strip
|
92
|
-
bh.company_identification = line[40..49].
|
101
|
+
bh.company_identification = line[40..49].gsub(/\A1/, '')
|
93
102
|
bh.standard_entry_class_code = line[50..52].strip
|
94
103
|
bh.company_entry_description = line[53..62].strip
|
95
104
|
bh.company_descriptive_date = Date.parse(line[63..68]) rescue nil # this can be various formats
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module ACH
|
4
|
+
class ACHFile
|
5
|
+
include FieldIdentifiers
|
6
|
+
|
7
|
+
attr_reader :batches
|
8
|
+
attr_reader :header
|
9
|
+
attr_reader :control
|
10
|
+
|
11
|
+
def initialize data=nil
|
12
|
+
@batches = []
|
13
|
+
@header = Records::FileHeader.new
|
14
|
+
@control = Records::FileControl.new
|
15
|
+
|
16
|
+
if data
|
17
|
+
if (data =~ /\n|\r\n/).nil?
|
18
|
+
parse_fixed(data)
|
19
|
+
else
|
20
|
+
parse(data)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
records = []
|
27
|
+
records << @header
|
28
|
+
@batches.each { |b| records += b.to_ach }
|
29
|
+
records << @control
|
30
|
+
|
31
|
+
nines_needed = 10 - (records.length % 10)
|
32
|
+
nines_needed = nines_needed % 10
|
33
|
+
nines_needed.times { records << Records::Nines.new() }
|
34
|
+
|
35
|
+
@control.batch_count = @batches.length
|
36
|
+
@control.block_count = (records.length / 10).ceil
|
37
|
+
|
38
|
+
@control.entry_count = 0
|
39
|
+
@control.debit_total = 0
|
40
|
+
@control.credit_total = 0
|
41
|
+
@control.entry_hash = 0
|
42
|
+
|
43
|
+
@batches.each do | batch |
|
44
|
+
@control.entry_count += batch.entries.length
|
45
|
+
@control.debit_total += batch.control.debit_total
|
46
|
+
@control.credit_total += batch.control.credit_total
|
47
|
+
@control.entry_hash += batch.control.entry_hash
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
records.collect { |r| r.to_ach }.join("\r\n") + "\r\n"
|
52
|
+
end
|
53
|
+
|
54
|
+
def report
|
55
|
+
to_s # To ensure correct records
|
56
|
+
lines = []
|
57
|
+
|
58
|
+
@batches.each do | batch |
|
59
|
+
batch.entries.each do | entry |
|
60
|
+
lines << left_justify(entry.individual_name + ": ", 25) +
|
61
|
+
sprintf("% 7d.%02d", entry.amount / 100, entry.amount % 100)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
lines << ""
|
65
|
+
lines << left_justify("Debit Total: ", 25) +
|
66
|
+
sprintf("% 7d.%02d", @control.debit_total / 100, @control.debit_total % 100)
|
67
|
+
lines << left_justify("Credit Total: ", 25) +
|
68
|
+
sprintf("% 7d.%02d", @control.credit_total / 100, @control.credit_total % 100)
|
69
|
+
|
70
|
+
lines.join("\r\n")
|
71
|
+
end
|
72
|
+
|
73
|
+
def parse_fixed data
|
74
|
+
parse data.scan(/.{94}/).join("\n")
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse data
|
78
|
+
trace_number = 0
|
79
|
+
fh = self.header
|
80
|
+
batch = nil
|
81
|
+
bh = nil
|
82
|
+
ed = nil
|
83
|
+
|
84
|
+
data.strip.split(/\n|\r\n/).each do |line|
|
85
|
+
type = line[0].chr
|
86
|
+
case type
|
87
|
+
when '1'
|
88
|
+
fh.immediate_destination = line[03..12].strip
|
89
|
+
fh.immediate_origin = line[13..22].strip
|
90
|
+
fh.transmission_datetime = Time.utc('20'+line[23..24], line[25..26], line[27..28], line[29..30], line[31..32])
|
91
|
+
fh.file_id_modifier = line[33..33]
|
92
|
+
fh.immediate_destination_name = line[40..62].strip
|
93
|
+
fh.immediate_origin_name = line[63..85].strip
|
94
|
+
fh.reference_code = line[86..93].strip
|
95
|
+
when '5'
|
96
|
+
self.batches << batch unless batch.nil?
|
97
|
+
batch = ACH::Batch.new
|
98
|
+
bh = batch.header
|
99
|
+
bh.company_name = line[4..19].strip
|
100
|
+
<<<<<<< HEAD
|
101
|
+
bh.company_identification = line[40..49].strip.gsub(/\A1/, '')
|
102
|
+
=======
|
103
|
+
bh.company_identification = line[41..49]# .strip - do not strip need to preserve trailing spaces
|
104
|
+
>>>>>>> 2dd27ff1c45679b6544a088c4a21f2689123ddf8
|
105
|
+
bh.standard_entry_class_code = line[50..52].strip
|
106
|
+
bh.company_entry_description = line[53..62].strip
|
107
|
+
bh.company_descriptive_date = Date.parse(line[63..68]) rescue nil # this can be various formats
|
108
|
+
bh.effective_entry_date = Date.parse(line[69..74])
|
109
|
+
bh.originating_dfi_identification = line[79..86].strip
|
110
|
+
when '6'
|
111
|
+
ed = ACH::CtxEntryDetail.new
|
112
|
+
ed.transaction_code = line[1..2]
|
113
|
+
ed.routing_number = line[3..11]
|
114
|
+
ed.account_number = line[12..28].strip
|
115
|
+
ed.amount = line[29..38].to_i # cents
|
116
|
+
ed.individual_id_number = line[39..53].strip
|
117
|
+
ed.individual_name = line[54..75].strip
|
118
|
+
ed.originating_dfi_identification = line[79..86]
|
119
|
+
ed.trace_number = line[87..93].to_i
|
120
|
+
batch.entries << ed
|
121
|
+
when '7'
|
122
|
+
type_code = line[1..2]
|
123
|
+
ad = case type_code
|
124
|
+
when '98'
|
125
|
+
ACH::Addendum::NotificationOfChange.new
|
126
|
+
when '99'
|
127
|
+
ACH::Addendum::Return.new
|
128
|
+
else
|
129
|
+
ACH::Addendum.new
|
130
|
+
end
|
131
|
+
ad.type_code = type_code
|
132
|
+
ad.payment_data = line[3..82].strip
|
133
|
+
ad.sequence_number = line[83..86].strip.to_i
|
134
|
+
ad.entry_detail_sequence_number = line[87..93].to_i
|
135
|
+
ed.addenda << ad
|
136
|
+
when '8'
|
137
|
+
# skip
|
138
|
+
when '9'
|
139
|
+
# skip
|
140
|
+
else
|
141
|
+
raise "Didn't recognize type code #{type} for this line:\n#{line}"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
self.batches << batch unless batch.nil?
|
146
|
+
batch.entries.each{ |entry| entry.trace_number = (trace_number += 1) }
|
147
|
+
to_s
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/ach/batch.rb
CHANGED
@@ -14,8 +14,7 @@ module ACH::Records
|
|
14
14
|
field :debit_total, Integer, lambda { |f| sprintf('%012d', f)}
|
15
15
|
field :credit_total, Integer, lambda { |f| sprintf('%012d', f)}
|
16
16
|
field :company_identification, String,
|
17
|
-
lambda { |f| f.length == 10 ? f : "1#{f}" }, nil, /\A
|
18
|
-
|
17
|
+
lambda { |f| f.length == 10 ? f : "1#{f}" }, nil, /\A.{9,10}\z/
|
19
18
|
field :message_authentication_code, String,
|
20
19
|
lambda { |f| left_justify(f, 19)}, ''
|
21
20
|
|
@@ -12,7 +12,7 @@ module ACH::Records
|
|
12
12
|
field :company_discretionary_data, String,
|
13
13
|
lambda { |f| left_justify(f, 20)}, ''
|
14
14
|
field :company_identification, String,
|
15
|
-
lambda { |f| f.length == 10 ? f : "1#{f}" }, nil, /\A
|
15
|
+
lambda { |f| f.length == 10 ? f : "1#{f}" }, nil, /\A.{9,10}\z/
|
16
16
|
# TODO This should be used to determine whether other records are valid for
|
17
17
|
# this code. Should there be a Class for each code?
|
18
18
|
# The default of PPD is purely for my benefit (Jared Morgan)
|
@@ -23,6 +23,12 @@ module ACH::Records
|
|
23
23
|
field :trace_number, Integer, lambda { |f| sprintf('%07d', f)}, nil,
|
24
24
|
lambda { |n| n.to_s.length <= 7 }
|
25
25
|
|
26
|
+
attr_reader :addenda
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@addenda = []
|
30
|
+
end
|
31
|
+
|
26
32
|
def credit?
|
27
33
|
CREDIT_RECORD_TRANSACTION_CODE_ENDING_DIGITS.include?(@transaction_code[1..1])
|
28
34
|
end
|
@@ -35,35 +41,13 @@ module ACH::Records
|
|
35
41
|
return self.amount
|
36
42
|
end
|
37
43
|
|
38
|
-
end
|
39
|
-
|
40
|
-
class CtxEntryDetail < EntryDetail
|
41
|
-
|
42
|
-
@fields = EntryDetail.fields.slice(0, 6)
|
43
|
-
field :number_of_addenda_records, Integer, lambda { |f| sprintf('%04d', f)}, 0
|
44
|
-
field :individual_name, String, lambda { |f| left_justify(f, 16)}
|
45
|
-
const_field :reserved, ' '
|
46
|
-
field :discretionary_data, String, lambda { |f| left_justify(f, 2)}, ' '
|
47
|
-
field :addenda_record_indicator, Integer,
|
48
|
-
lambda { |f| sprintf('%01d', f)}
|
49
|
-
field :originating_dfi_identification, String,
|
50
|
-
nil, nil, /\A\d{8}\z/
|
51
|
-
field :trace_number, Integer, lambda { |f| sprintf('%07d', f)}
|
52
|
-
|
53
|
-
|
54
|
-
attr_reader :addenda
|
55
|
-
|
56
|
-
def initialize
|
57
|
-
@addenda = []
|
58
|
-
end
|
59
|
-
|
60
44
|
def addenda_records?
|
61
45
|
return !self.addenda.empty?
|
62
46
|
end
|
63
47
|
|
64
48
|
def to_ach
|
65
|
-
self.addenda_record_indicator = (self.addenda.empty? ? 0 : 1)
|
66
|
-
self.number_of_addenda_records = self.addenda.length
|
49
|
+
self.addenda_record_indicator = (self.addenda.empty? ? 0 : 1) if self.respond_to?(:addenda_record_indicator)
|
50
|
+
self.number_of_addenda_records = self.addenda.length if self.respond_to?(:number_of_addenda_records)
|
67
51
|
|
68
52
|
ach_string = super
|
69
53
|
|
@@ -74,5 +58,22 @@ module ACH::Records
|
|
74
58
|
return ach_string
|
75
59
|
end
|
76
60
|
|
61
|
+
# @return [Integer] Length of addenda plus 1, used by Batch#entry_count
|
62
|
+
def records_count
|
63
|
+
1 + self.addenda.length
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class CtxEntryDetail < EntryDetail
|
68
|
+
@fields = EntryDetail.fields.slice(0, 6)
|
69
|
+
field :number_of_addenda_records, Integer, lambda { |f| sprintf('%04d', f)}, 0
|
70
|
+
field :individual_name, String, lambda { |f| left_justify(f, 16)}
|
71
|
+
const_field :reserved, ' '
|
72
|
+
field :discretionary_data, String, lambda { |f| left_justify(f, 2)}, ' '
|
73
|
+
field :addenda_record_indicator, Integer,
|
74
|
+
lambda { |f| sprintf('%01d', f)}
|
75
|
+
field :originating_dfi_identification, String,
|
76
|
+
nil, nil, /\A\d{8}\z/
|
77
|
+
field :trace_number, Integer, lambda { |f| sprintf('%07d', f)}
|
77
78
|
end
|
78
79
|
end
|
data/lib/ach/version.rb
CHANGED
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.
|
4
|
+
version: 0.4.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jared Morgan
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-04-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -47,21 +47,22 @@ extensions: []
|
|
47
47
|
extra_rdoc_files:
|
48
48
|
- README.md
|
49
49
|
files:
|
50
|
+
- MIT-LICENSE
|
51
|
+
- README.md
|
50
52
|
- lib/ach.rb
|
51
|
-
- lib/ach/
|
53
|
+
- lib/ach/ach_file.rb
|
54
|
+
- lib/ach/ach_file.rb.orig
|
52
55
|
- lib/ach/batch.rb
|
53
|
-
- lib/ach/
|
54
|
-
- lib/ach/records/
|
55
|
-
- lib/ach/records/file_control.rb
|
56
|
+
- lib/ach/field_identifiers.rb
|
57
|
+
- lib/ach/records/addendum.rb
|
56
58
|
- lib/ach/records/batch_control.rb
|
59
|
+
- lib/ach/records/batch_header.rb
|
57
60
|
- lib/ach/records/entry_detail.rb
|
61
|
+
- lib/ach/records/file_control.rb
|
62
|
+
- lib/ach/records/file_header.rb
|
58
63
|
- lib/ach/records/nines.rb
|
59
|
-
- lib/ach/records/addendum.rb
|
60
64
|
- lib/ach/records/record.rb
|
61
|
-
- lib/ach/
|
62
|
-
- lib/ach/ach_file.rb
|
63
|
-
- MIT-LICENSE
|
64
|
-
- README.md
|
65
|
+
- lib/ach/version.rb
|
65
66
|
homepage: https://github.com/jm81/ach
|
66
67
|
licenses: []
|
67
68
|
metadata: {}
|
@@ -81,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
82
|
version: '0'
|
82
83
|
requirements: []
|
83
84
|
rubyforge_project:
|
84
|
-
rubygems_version: 2.
|
85
|
+
rubygems_version: 2.2.0
|
85
86
|
signing_key:
|
86
87
|
specification_version: 4
|
87
88
|
summary: Helper for building ACH files
|