bai2 1.0.0 → 2.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +27 -5
- data/lib/bai2.rb +9 -3
- data/lib/bai2/integrity.rb +31 -17
- data/lib/bai2/record.rb +1 -1
- data/lib/bai2/version.rb +1 -1
- data/test/data/daily_with_summary.bai2 +11 -0
- data/test/data/eod_without_as_of_time.bai2 +17 -0
- data/test/data/invalid_checksum_eod.bai2 +17 -0
- data/test/tests/bai2.rb +23 -5
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 975f734808bc96fce7ca38c08e1ba90e8839b8c6
|
4
|
+
data.tar.gz: 99d17eec7002b0b0c6ca70b9a3228f2500efadc5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdd5385fe33114722442f33838c2bab6406cfc41ab7a1e3125bd09bf1d269e351e57008090da8ccb34b6ae11edefd4ec222cf9ae7fad32edcd3fb86a9b18bb6e
|
7
|
+
data.tar.gz: eb2228fdfaaba4856d141508995809e7ecc0235de2b6437206faa774c9382c469486ae9a001be4a2398746882a697b59936fd01aad5cf1713382159c6eed3ed7
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
Changelog
|
2
|
+
|
3
|
+
## 2.0.0
|
4
|
+
- Add parameters for whether or not we use account summaries for account control checksum. We now include it by default. To disable it `account_control_ignores_summary_amounts` should passed into `options` This is the only breaking change.
|
5
|
+
- Allow for optional "as of" times on the Group header.
|
6
|
+
|
7
|
+
## 1.0.0
|
8
|
+
- Initial Release
|
data/README.md
CHANGED
@@ -75,6 +75,26 @@ file.groups.filter {|g| g.destination == YourOrgId }.each do |group|
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
```
|
78
|
+
## Options
|
79
|
+
`Bai2::BaiFile.parse` and `Bai2::BaiFile.new` accept an optional second parameter, `options`.
|
80
|
+
|
81
|
+
* `options[:account_control_ignores_summary_amounts]` (Boolean, Default: False)
|
82
|
+
See [Caveats](#caveats) below. Optionally ignores the amounts in the account summary fields when calculating the account control checksum.
|
83
|
+
This value should be set only if you know that your bank uses this nonstandard calculation for
|
84
|
+
account control values.
|
85
|
+
|
86
|
+
* `options[:num_account_summary_continuation_records]` (Integer, Default: 0)
|
87
|
+
The number of continuation records the account summary.
|
88
|
+
|
89
|
+
|
90
|
+
##### Usage:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
Bai2::BaiFile.new(string_data,
|
94
|
+
account_control_ignores_summary_amounts: true,
|
95
|
+
num_account_summary_continuation_records: 3)
|
96
|
+
```
|
97
|
+
|
78
98
|
|
79
99
|
## Caveats
|
80
100
|
|
@@ -85,11 +105,13 @@ just an SVB quirk. I would love to hear how other banks do this. GitHub Issues
|
|
85
105
|
with more information on this would be greatly appreciated.
|
86
106
|
|
87
107
|
```ruby
|
88
|
-
#
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
108
|
+
# Some banks differ from the spec (*cough* SVB) and do not include
|
109
|
+
# the summary amounts in the control amount.
|
110
|
+
actual_sum = if options[:account_control_ignores_summary_amounts]
|
111
|
+
transaction_amounts_sum
|
112
|
+
else
|
113
|
+
transaction_amounts_sum + summary_amounts_sum
|
114
|
+
end
|
93
115
|
```
|
94
116
|
|
95
117
|
|
data/lib/bai2.rb
CHANGED
@@ -11,12 +11,17 @@ module Bai2
|
|
11
11
|
#
|
12
12
|
class BaiFile
|
13
13
|
|
14
|
+
DEFAULT_OPTIONS = {
|
15
|
+
account_control_ignores_summary_amounts: false,
|
16
|
+
num_account_summary_continuation_records: 0
|
17
|
+
}.freeze
|
18
|
+
|
14
19
|
# Parse a file on disk:
|
15
20
|
#
|
16
21
|
# f = BaiFile.parse('myfile.bai2')
|
17
22
|
#
|
18
|
-
def self.parse(path)
|
19
|
-
self.new(File.read(path))
|
23
|
+
def self.parse(path, options = {})
|
24
|
+
self.new(File.read(path), options)
|
20
25
|
end
|
21
26
|
|
22
27
|
|
@@ -24,9 +29,10 @@ module Bai2
|
|
24
29
|
#
|
25
30
|
# f = BaiFile.new(bai2_data)
|
26
31
|
#
|
27
|
-
def initialize(raw)
|
32
|
+
def initialize(raw, options = {})
|
28
33
|
@raw = raw
|
29
34
|
@groups = []
|
35
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
30
36
|
parse(raw)
|
31
37
|
end
|
32
38
|
|
data/lib/bai2/integrity.rb
CHANGED
@@ -36,12 +36,14 @@ module Bai2
|
|
36
36
|
end
|
37
37
|
|
38
38
|
# Run children assertions, which return number of records. May raise.
|
39
|
-
records = self.groups.map {|g| g.send(:assert_integrity
|
39
|
+
records = self.groups.map {|g| g.send(:assert_integrity!, @options) }.reduce(0, &:+)
|
40
40
|
|
41
|
-
unless expectation[:records] == (
|
41
|
+
unless expectation[:records] == (actual_num_records = records + 2)
|
42
42
|
raise IntegrityError.new(
|
43
|
-
"Record count invalid: file: #{expectation[:records]}, groups: #{
|
43
|
+
"Record count invalid: file: #{expectation[:records]}, groups: #{actual_num_records}")
|
44
44
|
end
|
45
|
+
|
46
|
+
actual_num_records
|
45
47
|
end
|
46
48
|
|
47
49
|
|
@@ -53,7 +55,7 @@ module Bai2
|
|
53
55
|
|
54
56
|
# Asserts integrity of a fully-parsed BaiFile by calculating checksums.
|
55
57
|
#
|
56
|
-
def assert_integrity!
|
58
|
+
def assert_integrity!(options)
|
57
59
|
expectation = {
|
58
60
|
sum: @trailer[:group_control_total],
|
59
61
|
children: @trailer[:number_of_accounts],
|
@@ -77,15 +79,15 @@ module Bai2
|
|
77
79
|
end
|
78
80
|
|
79
81
|
# Run children assertions, which return number of records. May raise.
|
80
|
-
records = self.accounts.map {|a| a.send(:assert_integrity
|
82
|
+
records = self.accounts.map {|a| a.send(:assert_integrity!, options) }.reduce(0, &:+)
|
81
83
|
|
82
|
-
unless expectation[:records] == (
|
84
|
+
unless expectation[:records] == (actual_num_records = records + 2)
|
83
85
|
raise IntegrityError.new(
|
84
|
-
"Record count invalid: group: #{expectation[:records]}, accounts: #{
|
86
|
+
"Record count invalid: group: #{expectation[:records]}, accounts: #{actual_num_records}")
|
85
87
|
end
|
86
88
|
|
87
89
|
# Return record count
|
88
|
-
|
90
|
+
actual_num_records
|
89
91
|
end
|
90
92
|
end
|
91
93
|
|
@@ -93,17 +95,23 @@ module Bai2
|
|
93
95
|
class Account
|
94
96
|
private
|
95
97
|
|
96
|
-
def assert_integrity!
|
98
|
+
def assert_integrity!(options)
|
97
99
|
expectation = {
|
98
100
|
sum: @trailer[:account_control_total],
|
99
101
|
records: @trailer[:number_of_records],
|
100
102
|
}
|
101
103
|
|
102
104
|
# Check sum vs. summary + transaction sums
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
105
|
+
summary_amounts_sum = self.summaries.map {|s| s[:amount] }.reduce(0, &:+)
|
106
|
+
transaction_amounts_sum = self.transactions.map(&:amount).reduce(0, &:+)
|
107
|
+
|
108
|
+
# Some banks differ from the spec (*cough* SVB) and do not include
|
109
|
+
# the summary amounts in the control amount.
|
110
|
+
actual_sum = if options[:account_control_ignores_summary_amounts]
|
111
|
+
transaction_amounts_sum
|
112
|
+
else
|
113
|
+
transaction_amounts_sum + summary_amounts_sum
|
114
|
+
end
|
107
115
|
|
108
116
|
unless expectation[:sum] == actual_sum
|
109
117
|
raise IntegrityError.new(
|
@@ -115,13 +123,19 @@ module Bai2
|
|
115
123
|
tx.instance_variable_get(:@record).physical_record_count
|
116
124
|
end.reduce(0, &:+)
|
117
125
|
|
118
|
-
|
119
|
-
|
120
|
-
|
126
|
+
# Account for the account header and the account trailer records
|
127
|
+
# and any additional summary records (Some banks use continuation records
|
128
|
+
# for account summaries, others put the summary data on the same row as the header)
|
129
|
+
additional_records = 2 + options[:num_account_summary_continuation_records]
|
130
|
+
actual_num_records = records + additional_records
|
131
|
+
|
132
|
+
unless expectation[:records] == actual_num_records
|
133
|
+
raise IntegrityError.new(
|
134
|
+
"Record count invalid: account: #{expectation[:records]}, transactions: #{actual_num_records}")
|
121
135
|
end
|
122
136
|
|
123
137
|
# Return record count
|
124
|
-
|
138
|
+
actual_num_records
|
125
139
|
end
|
126
140
|
end
|
127
141
|
end
|
data/lib/bai2/record.rb
CHANGED
data/lib/bai2/version.rb
CHANGED
@@ -0,0 +1,11 @@
|
|
1
|
+
01,121140399,9999999999,050608,1015,1,80,1,2/
|
2
|
+
02,9999999999,121140399,1,050607,1015,,2/
|
3
|
+
03,1234567890,USD,010,000,,,015,000,,/
|
4
|
+
88,040,000,,,045,000,,/
|
5
|
+
88,072,000,,,074,000,,/
|
6
|
+
88,100,25001,17,,400,000,0,/
|
7
|
+
16,174,25001,Z,,50848,/
|
8
|
+
88,SAMPLE CONTINUATION TEXT,/
|
9
|
+
49,50002,7/
|
10
|
+
98,50002,1,9/
|
11
|
+
99,50002,1,11/
|
@@ -0,0 +1,17 @@
|
|
1
|
+
01,121140399,3333333333,100831,1720,000001,80,1,2/
|
2
|
+
02,3333333333,121140399,1,100831,,USD,4/
|
3
|
+
03,3333333333,USD,,,,/
|
4
|
+
16,195,8325982,,,,FED NO: 20100831L1B77D1CDSDSDJSIO15608310954FT01
|
5
|
+
88,SENDER BNK:=ETRADE BANK
|
6
|
+
88,SENDER ID:=056073573
|
7
|
+
88,ORG:=OPTIONS LINK WIRE CLEARING
|
8
|
+
88,ORG ADDRESS:=1995 SE. 57TH ST. NY, NY 10022
|
9
|
+
88,BNF ID:=3300333333
|
10
|
+
88,BNF NAME:=YOUR NAME HERE INC
|
11
|
+
88,BNF ADDRESS:=185 B ST SAN FRAN, CA 94011
|
12
|
+
88,REC FI:=SIL VLY BK SCLA
|
13
|
+
88,REC ID:=121140399
|
14
|
+
88,OBI:=INVOICE 123456
|
15
|
+
49,8325982,13/
|
16
|
+
98,8325982,1,15/
|
17
|
+
99,8325982,1,17/
|
@@ -0,0 +1,17 @@
|
|
1
|
+
01,121140399,3333333333,100831,1720,000001,80,1,2/
|
2
|
+
02,3333333333,121140399,1,100831,1720,USD,4/
|
3
|
+
03,3333333333,USD,,,,/
|
4
|
+
16,195,8325982,,,,FED NO: 20100831L1B77D1CDSDSDJSIO15608310954FT01
|
5
|
+
88,SENDER BNK:=ETRADE BANK
|
6
|
+
88,SENDER ID:=056073573
|
7
|
+
88,ORG:=OPTIONS LINK WIRE CLEARING
|
8
|
+
88,ORG ADDRESS:=1995 SE. 57TH ST. NY, NY 10022
|
9
|
+
88,BNF ID:=3300333333
|
10
|
+
88,BNF NAME:=YOUR NAME HERE INC
|
11
|
+
88,BNF ADDRESS:=185 B ST SAN FRAN, CA 94011
|
12
|
+
88,REC FI:=SIL VLY BK SCLA
|
13
|
+
88,REC ID:=121140399
|
14
|
+
88,OBI:=INVOICE 123456
|
15
|
+
49,8325983,13/
|
16
|
+
98,8325983,1,15/
|
17
|
+
99,8325983,1,17/
|
data/test/tests/bai2.rb
CHANGED
@@ -6,16 +6,23 @@ class Bai2Test < Minitest::Test
|
|
6
6
|
|
7
7
|
def setup
|
8
8
|
@daily = Bai2::BaiFile.parse(File.expand_path('../../data/daily.bai2', __FILE__))
|
9
|
+
@daily_with_summary = Bai2::BaiFile.parse(File.expand_path('../../data/daily_with_summary.bai2', __FILE__),
|
10
|
+
num_account_summary_continuation_records: 3)
|
11
|
+
|
9
12
|
@eod = Bai2::BaiFile.parse(File.expand_path('../../data/eod.bai2', __FILE__))
|
13
|
+
@eod_no_as_of_time = Bai2::BaiFile.parse(File.expand_path('../../data/eod_without_as_of_time.bai2', __FILE__))
|
14
|
+
|
15
|
+
@all_files = [@daily, @daily_with_summary, @eod, @eod_no_as_of_time]
|
10
16
|
end
|
11
17
|
|
12
18
|
def test_parsing
|
13
|
-
|
14
|
-
|
19
|
+
@all_files.each do |file|
|
20
|
+
assert_kind_of(Bai2::BaiFile, file)
|
21
|
+
end
|
15
22
|
end
|
16
23
|
|
17
24
|
def test_groups
|
18
|
-
|
25
|
+
@all_files.each do |file|
|
19
26
|
assert_kind_of(Array, file.groups)
|
20
27
|
assert_equal(1, file.groups.count)
|
21
28
|
group = file.groups.first
|
@@ -27,8 +34,8 @@ class Bai2Test < Minitest::Test
|
|
27
34
|
end
|
28
35
|
|
29
36
|
def test_accounts
|
30
|
-
|
31
|
-
accounts =
|
37
|
+
@all_files.each do |file|
|
38
|
+
accounts = file.groups.first.accounts
|
32
39
|
assert_kind_of(Array, accounts)
|
33
40
|
assert_equal(1, accounts.count)
|
34
41
|
assert_kind_of(Bai2::BaiFile::Account, accounts.first)
|
@@ -56,4 +63,15 @@ class Bai2Test < Minitest::Test
|
|
56
63
|
})
|
57
64
|
end
|
58
65
|
|
66
|
+
def test_integrity
|
67
|
+
assert_raises Bai2::BaiFile::IntegrityError do
|
68
|
+
# Calling without the options: num_account_summary_continuation_records => 3 should raise an error
|
69
|
+
Bai2::BaiFile.parse(File.expand_path('../../data/daily_with_summary.bai2', __FILE__))
|
70
|
+
end
|
71
|
+
assert_raises Bai2::BaiFile::IntegrityError do
|
72
|
+
# An invalid amount checksum should raise an error
|
73
|
+
Bai2::BaiFile.parse(File.expand_path('../../data/invalid_checksum_eod.bai2', __FILE__))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
59
77
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bai2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kenneth Ballenegger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-05-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -74,6 +74,7 @@ extensions: []
|
|
74
74
|
extra_rdoc_files: []
|
75
75
|
files:
|
76
76
|
- ".gitignore"
|
77
|
+
- CHANGELOG.md
|
77
78
|
- Gemfile
|
78
79
|
- LICENSE.txt
|
79
80
|
- README.md
|
@@ -88,7 +89,10 @@ files:
|
|
88
89
|
- lib/bai2/version.rb
|
89
90
|
- test/autorun.rb
|
90
91
|
- test/data/daily.bai2
|
92
|
+
- test/data/daily_with_summary.bai2
|
91
93
|
- test/data/eod.bai2
|
94
|
+
- test/data/eod_without_as_of_time.bai2
|
95
|
+
- test/data/invalid_checksum_eod.bai2
|
92
96
|
- test/tests/bai2.rb
|
93
97
|
- test/tests/parsing.rb
|
94
98
|
homepage: https://github.com/venturehacks/bai2
|
@@ -111,14 +115,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
115
|
version: '0'
|
112
116
|
requirements: []
|
113
117
|
rubyforge_project:
|
114
|
-
rubygems_version: 2.2.
|
118
|
+
rubygems_version: 2.5.2.3
|
115
119
|
signing_key:
|
116
120
|
specification_version: 4
|
117
121
|
summary: Parse BAI2 files.
|
118
122
|
test_files:
|
119
123
|
- test/autorun.rb
|
120
124
|
- test/data/daily.bai2
|
125
|
+
- test/data/daily_with_summary.bai2
|
121
126
|
- test/data/eod.bai2
|
127
|
+
- test/data/eod_without_as_of_time.bai2
|
128
|
+
- test/data/invalid_checksum_eod.bai2
|
122
129
|
- test/tests/bai2.rb
|
123
130
|
- test/tests/parsing.rb
|
124
|
-
has_rdoc:
|