aba 0.0.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 989126f3d5c6f6c728d31e3330293a907e045d7b
4
- data.tar.gz: 44341ab465bc09d7a059bd7709c39b3cc004dce8
2
+ SHA256:
3
+ metadata.gz: f7d2996812bbfd9d6386cef252e680024545ad59b01eec0d6645d8cf75dafe6a
4
+ data.tar.gz: 584aad9a1c2b922680da873f1a95922dfa4864c5e54ac979d6ed6f5eb01e3094
5
5
  SHA512:
6
- metadata.gz: 626ece57dc5490418f7e55583669c72f03479fb62620e0cc94cfbc9e7d59e134373861dac585a88bf781d2497da44a5f5c632d4f7097dcb8ec577e2488117a1c
7
- data.tar.gz: b9422ab783d663911b8144bcd5d8e14f0ab3b2d5506d19c62927a57843ecd7413e8b1643e2a33a3b4d950a228ad35b4bb3e763c2f3d57b9f9eadb6f663fd9b29
6
+ metadata.gz: 4be17e99ad4fedbb404bb2305e0a310afafc96c0f2b39f57f49d76b198e7018a7fc9997b35441d2209bce37986487d683fcfd5f20d049acea8581804669ecc8b
7
+ data.tar.gz: b09e75f48ce6ddab89dc0fc82f1d9be3a87f573db08591d65158203b2b1a4d1ae6c3ec6cbf34437d1c4d15842e4e93f07f75977bbf5ac063338fbd2dc519a177
data/.gitignore CHANGED
@@ -20,3 +20,4 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
+ .ruby-version
data/.rspec CHANGED
@@ -1 +1,3 @@
1
- --color
1
+ --color
2
+ --require spec_helper
3
+ --format d
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.5.8
4
+ - 2.6.6
5
+ - 2.7.1
6
+ script: bundle exec rspec spec
data/README.md CHANGED
@@ -1,34 +1,125 @@
1
+ [![Build Status](https://travis-ci.org/andrba/aba.svg?branch=master)](https://travis-ci.org/andrba/aba) [![Code Climate](https://codeclimate.com/github/andrba/aba/badges/gpa.svg)](https://codeclimate.com/github/andrba/aba)
2
+
1
3
  # Aba
2
4
 
3
- The purpose of this gem is to generate an ABA (Australian Banking Association) file. It is a format used by banks to allow for batch transaction.
5
+ Generates ABA (Australian Banking Association) file format output
4
6
 
5
7
  ## Usage
6
8
 
7
9
  ```ruby
8
10
  require 'aba'
9
11
 
10
- aba = Aba.new(bsb: "123-345", financial_institution: "WPC", user_name: "John Doe",
11
- user_id: "466364", description: "Payroll", process_at: Time.now)
12
-
13
- # Add transactions
14
- transactions.each do |t|
15
- aba.transactions << Aba::Transaction.new(
16
- :bsb => "342-342",
17
- :account_number => "3244654",
18
- :amount => amount,
19
- :account_name => "John Doe",
20
- :payment_id => "P2345543",
21
- :transaction_code => 53,
22
- :lodgement_reference => "R435564",
23
- :trace_bsb => "453-543",
24
- :trace_account_number => "45656733",
25
- :name_of_remitter => "Remitter"
12
+ # Initialise ABA
13
+ aba = Aba.batch(
14
+ bsb: "123-345", # Optional (Not required by NAB)
15
+ financial_institution: "WPC",
16
+ user_name: "John Doe",
17
+ user_id: "466364",
18
+ description: "Payroll",
19
+ process_at: Time.now.strftime("%d%m%y")
20
+ )
21
+
22
+ # Add transactions...
23
+ 10.times do
24
+ aba.add_transaction(
25
+ {
26
+ bsb: "342-342",
27
+ account_number: "3244654",
28
+ amount: 10000, # Amount in cents
29
+ account_name: "John Doe",
30
+ transaction_code: 53,
31
+ lodgement_reference: "R435564",
32
+ trace_bsb: "453-543",
33
+ trace_account_number: "45656733",
34
+ name_of_remitter: "Remitter"
35
+ }
36
+ )
37
+ end
38
+
39
+ # ...or add returns
40
+ 10.times do
41
+ aba.add_return(
42
+ {
43
+ bsb: '453-543',
44
+ account_number: '45656733',
45
+ amount: 10000,
46
+ account_name: 'John Doe',
47
+ transaction_code: 53,
48
+ lodgement_reference: 'R435564',
49
+ trace_bsb: '342-342',
50
+ trace_account_number: '3244654',
51
+ name_of_remitter: 'Remitter',
52
+ return_code: 8,
53
+ original_user_id: 654321,
54
+ original_processing_day: 12,
55
+ }
26
56
  )
27
57
  end
28
58
 
29
- puts aba.to_s
59
+ puts aba.to_s # View output
60
+ File.write("/Users/me/dd_#{Time.now.to_i}.aba", aba.to_s) # or write output to file
61
+ ```
62
+
63
+ There are a few ways to create a complete set of ABA data:
64
+
65
+ ```ruby
66
+ # Transactions added to the defined ABA object variable
67
+ aba = Aba.batch financial_institution: 'ANZ', user_name: 'Joe Blow', user_id: 123456, process_at: 200615
68
+ aba.add_transaction bsb: '123-456', account_number: '000-123-456', amount: 50000
69
+ aba.add_transaction bsb: '456-789', account_number: '123-456-789', amount: '-10000', transaction_code: 13
70
+
71
+ # Transactions passed individually inside a block
72
+ aba = Aba.batch financial_institution: 'ANZ', user_name: 'Joe Blow', user_id: 123456, process_at: 200615 do |a|
73
+ a.add_transaction bsb: '123-456', account_number: '000-123-456', amount: 50000
74
+ a.add_transaction bsb: '456-789', account_number: '123-456-789', amount: '-10000', transaction_code: 13
75
+ end
76
+
77
+ # Transactions as an array passed to the second param of Aba.batch
78
+ aba = Aba.batch(
79
+ { financial_institution: 'ANZ', user_name: 'Joe Blow', user_id: 123456, process_at: 200615 },
80
+ [
81
+ { bsb: '123-456', account_number: '000-123-456', amount: 50000 },
82
+ { bsb: '456-789', account_number: '123-456-789', amount: '-10000', transaction_code: 13 }
83
+ ]
84
+ )
85
+
86
+ # NOTE: Be careful with negative transaction amounts! transaction_code will not
87
+ # be set to debit automatically!
30
88
  ```
31
89
 
90
+ Validation errors can be caught in several ways:
91
+
92
+ ```ruby
93
+ # Create an ABA object with invalid character in the user_name
94
+ aba = Aba.batch(
95
+ financial_institution: "ANZ",
96
+ user_name: "Jøhn Doe",
97
+ user_id: "123456",
98
+ process_at: Time.now.strftime("%d%m%y")
99
+ )
100
+
101
+ # Add a transaction with a bad BSB
102
+ aba.add_transaction(
103
+ bsb: "abc-123",
104
+ account_number: "000123456"
105
+ )
106
+
107
+ # Is the data valid?
108
+ aba.valid?
109
+ # Returns: false
110
+
111
+ # Return a structured array of errors
112
+ puts aba.errors
113
+ # Returns:
114
+ # {:aba => ["user_name must not contain invalid characters"],
115
+ # :transactions =>
116
+ # {0 => ["bsb format is incorrect", "trace_bsb format is incorrect"]}}
117
+ ```
118
+
119
+ Validation errors will stop parsing of the data to an ABA formatted string using
120
+ `to_s`. `aba.to_s` will raise a `RuntimeError` instead of returning output.
121
+
122
+
32
123
  ## Installation
33
124
 
34
125
  Add this line to your application's Gemfile:
@@ -6,10 +6,10 @@ require 'aba/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "aba"
8
8
  spec.version = Aba::VERSION
9
- spec.authors = ["Andrey Bazhutkin"]
10
- spec.email = ["andrey.bazhutkin@gmail.com"]
11
- spec.summary = ["ABA File Generator"]
12
- spec.description = ["ABA (Australian Bankers Association) File Generator"]
9
+ spec.authors = ["Andrey Bazhutkin", "Trevor Wistaff"]
10
+ spec.email = ["andrey.bazhutkin@gmail.com", "trev@a07.com.au"]
11
+ spec.summary = "ABA File Generator"
12
+ spec.description = "ABA (Australian Bankers Association) File Generator"
13
13
  spec.homepage = "https://github.com/andrba/aba"
14
14
  spec.license = "MIT"
15
15
 
@@ -18,9 +18,10 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.6"
21
+ spec.add_development_dependency "bundler"
22
22
  spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "pry"
23
24
  spec.add_development_dependency "rspec", "~> 3.0"
24
25
 
25
- spec.required_ruby_version = '>= 1.9.2'
26
+ spec.required_ruby_version = '>= 2.5.0'
26
27
  end
data/lib/aba.rb CHANGED
@@ -1,120 +1,12 @@
1
1
  require "aba/version"
2
2
  require "aba/validations"
3
+ require "aba/entry"
4
+ require "aba/batch"
5
+ require "aba/return"
3
6
  require "aba/transaction"
4
7
 
5
8
  class Aba
6
- include Aba::Validations
7
-
8
- attr_accessor :bsb, :account_number, :financial_institution, :user_name, :user_id,
9
- :description, :process_at, :name_of_remitter, :transactions
10
-
11
- validates_presence_of :bsb, :financial_institution, :user_name, :user_id, :description, :process_at
12
-
13
- validates_bsb :bsb
14
-
15
- validates_max_length :account_number, 9
16
- validates_max_length :financial_institution, 3
17
- validates_max_length :user_name, 26
18
- validates_max_length :user_id, 6
19
- validates_max_length :description, 12
20
-
21
- def initialize(attrs = {})
22
- attrs.each do |key, value|
23
- send("#{key}=", value)
24
- end
25
-
26
- self.transactions = []
27
-
28
- yield self if block_given?
29
- end
30
-
31
- def to_s
32
- # Descriptive record
33
- output = "#{descriptive_record}\r\n"
34
-
35
- # Transactions records
36
- output += @transactions.map{ |t| t.to_s }.join("\r\n")
37
-
38
- # Batch control record
39
- output += "\r\n#{batch_control_record}"
40
- end
41
-
42
- private
43
-
44
- def descriptive_record
45
- # Record type
46
- output = "0"
47
-
48
- # Bank/State/Branch number of the funds account with a hyphen in the 4th character position. e.g. 013-999.
49
- output += self.bsb
50
-
51
- # Funds account number.
52
- output += self.account_number.to_s.ljust(9, " ")
53
-
54
- # Reserved
55
- output += " "
56
-
57
- # Sequence number
58
- output += "01"
59
-
60
- # Must contain the bank mnemonic that is associated with the BSB of the funds account. e.g. ‘ANZ’.
61
- output += self.financial_institution[0..2].to_s
62
-
63
- # Reserved
64
- output += " " * 7
65
-
66
- # Name of User supplying File as advised by User's Financial Institution
67
- output += self.user_name.to_s.ljust(26, " ")
68
-
69
- # Direct Entry User ID.
70
- output += self.user_id.to_s.rjust(6, "0")
71
-
72
- # Description of payments in the file (e.g. Payroll, Creditors etc.).
73
- output += self.description.to_s.ljust(12, " ")
74
-
75
- # Date and time on which the payment is to be processed.
76
- output += self.process_at.strftime("%d%m%y%H%M")
77
-
78
- # Reserved
79
- output += " " * 36
80
- end
81
-
82
- def batch_control_record
83
- # Record type
84
- output = "7"
85
-
86
- # BSB Format Filler
87
- output += "999-999"
88
-
89
- # Reserved
90
- output += " " * 12
91
-
92
- net_total_amount = 0
93
- credit_total_amount = 0
94
- debit_total_amount = 0
95
-
96
- @transactions.each do |t|
97
- net_total_amount += t.amount
98
- credit_total_amount += t.amount if t.amount > 0
99
- debit_total_amount += t.amount if t.amount < 0
100
- end
101
-
102
- # Must equal the difference between File Credit & File Debit Total Amounts. Show in cents without punctuation
103
- output += net_total_amount.abs.to_s.rjust(10, "0")
104
-
105
- # Batch Credit Total Amount
106
- output += credit_total_amount.abs.to_s.rjust(10, "0")
107
-
108
- # Batch Debit Total Amount
109
- output += debit_total_amount.abs.to_s.rjust(10, "0")
110
-
111
- # Reserved
112
- output += " " * 24
113
-
114
- # Batch Total Item Count
115
- output += @transactions.size.to_s.rjust(6, "0")
116
-
117
- # Reserved
118
- output += " " * 40
9
+ def self.batch(attrs = {}, transactions = [])
10
+ Aba::Batch.new(attrs, transactions)
119
11
  end
120
- end
12
+ end
@@ -0,0 +1,224 @@
1
+ class Aba
2
+ class Batch
3
+ include Aba::Validations
4
+
5
+ attr_accessor :bsb, :financial_institution, :user_name, :user_id, :description, :process_at, :entries
6
+
7
+ # BSB
8
+ validates_bsb :bsb, allow_blank: true
9
+
10
+ # Financial Institution
11
+ validates_length :financial_institution, 3
12
+
13
+ # User Name
14
+ validates_presence_of :user_name
15
+ validates_max_length :user_name, 26
16
+ validates_becs :user_name
17
+
18
+ # User ID
19
+ validates_presence_of :user_id
20
+ validates_max_length :user_id, 6
21
+ validates_integer :user_id, false
22
+
23
+ # Description
24
+ validates_max_length :description, 12
25
+ validates_becs :description
26
+
27
+ # Process at Date
28
+ validates_length :process_at, 6
29
+ validates_integer :process_at, false
30
+
31
+
32
+ def initialize(attrs = {}, transactions = [])
33
+ attrs.each do |key, value|
34
+ send("#{key}=", value)
35
+ end
36
+
37
+ @entries = []
38
+
39
+ unless transactions.nil? || transactions.empty?
40
+ transactions.to_a.each do |t|
41
+ self.add_transaction(t) unless t.nil? || t.empty?
42
+ end
43
+ end
44
+
45
+ yield self if block_given?
46
+ end
47
+
48
+ def to_s
49
+ raise RuntimeError, 'No entries present - add one using `add_transaction` or `add_return`' if entries.empty?
50
+ raise RuntimeError, 'ABA data is invalid - check the contents of `errors`' unless valid?
51
+
52
+ # Descriptive record
53
+ output = "#{descriptive_record}\r\n"
54
+
55
+ # Transactions records
56
+ output += entries.map { |t| t.to_s }.join("\r\n")
57
+
58
+ # Batch control record
59
+ output += "\r\n#{batch_control_record}"
60
+ end
61
+
62
+ def add_transaction(attrs = {})
63
+ add_entry(Aba::Transaction, attrs)
64
+ end
65
+
66
+ def add_return(attrs = {})
67
+ add_entry(Aba::Return, attrs)
68
+ end
69
+
70
+ def transactions
71
+ entries.select { |entry| entry.instance_of?(Aba::Transaction) }
72
+ end
73
+
74
+ def transactions_valid?
75
+ !transactions.map { |t| t.valid? }.include?(false)
76
+ end
77
+
78
+ def valid?
79
+ !has_errors? && !has_entry_errors?
80
+ end
81
+
82
+ def errors
83
+ # Run validations
84
+ has_errors?
85
+ has_entry_errors?
86
+
87
+ # Build errors
88
+ all_errors = {}
89
+ all_errors[:aba] = self.error_collection unless self.error_collection.empty?
90
+ entry_error_collection = entries.each_with_index.map { |t, i| [i, t.error_collection] }.reject { |e| e[1].nil? || e[1].empty? }.to_h
91
+ all_errors[:entries] = entry_error_collection unless entry_error_collection.empty?
92
+
93
+ all_errors unless all_errors.empty?
94
+ end
95
+
96
+ private
97
+
98
+ def add_entry(type, attrs)
99
+ (attrs.instance_of?(type) ? attrs : type.new(attrs)).tap do |entry|
100
+ entries << entry
101
+ end
102
+ end
103
+
104
+ def has_entry_errors?
105
+ entries.map { |t| t.valid? }.include?(false)
106
+ end
107
+
108
+ def descriptive_record
109
+ # Record type
110
+ # Max: 1
111
+ # Char position: 1
112
+ output = "0"
113
+
114
+ # Optional branch number of the funds account with a hyphen in the 4th character position
115
+ # Char position: 2-18
116
+ # Max: 17
117
+ # Blank filled
118
+ output += self.bsb.nil? ? " " * 17 : self.bsb.to_s.ljust(17)
119
+
120
+ # Sequence number
121
+ # Char position: 19-20
122
+ # Max: 2
123
+ # Zero padded
124
+ output += "01"
125
+
126
+ # Name of user financial instituion
127
+ # Max: 3
128
+ # Char position: 21-23
129
+ output += self.financial_institution.to_s
130
+
131
+ # Reserved
132
+ # Max: 7
133
+ # Char position: 24-30
134
+ output += " " * 7
135
+
136
+ # Name of User supplying File
137
+ # Char position: 31-56
138
+ # Max: 26
139
+ # Full BECS character set valid
140
+ # Blank filled
141
+ output += self.user_name.to_s.ljust(26)
142
+
143
+ # Direct Entry User ID
144
+ # Char position: 57-62
145
+ # Max: 6
146
+ # Zero padded
147
+ output += self.user_id.to_s.rjust(6, "0")
148
+
149
+ # Description of payments in the file (e.g. Payroll, Creditors etc.)
150
+ # Char position: 63-74
151
+ # Max: 12
152
+ # Full BECS character set valid
153
+ # Blank filled
154
+ output += self.description.to_s.ljust(12)
155
+
156
+ # Date on which the payment is to be processed
157
+ # Char position: 75-80
158
+ # Max: 6
159
+ output += self.process_at.to_s.rjust(6, "0")
160
+
161
+ # Reserved
162
+ # Max: 40
163
+ # Char position: 81-120
164
+ output += " " * 40
165
+ end
166
+
167
+ def batch_control_record
168
+ net_total_amount = 0
169
+ credit_total_amount = 0
170
+ debit_total_amount = 0
171
+
172
+ entries.each do |t|
173
+ net_total_amount += t.amount.to_i
174
+ credit_total_amount += t.amount.to_i if t.amount.to_i > 0
175
+ debit_total_amount += t.amount.to_i if t.amount.to_i < 0
176
+ end
177
+
178
+ # Record type
179
+ # Max: 1
180
+ # Char position: 1
181
+ output = "7"
182
+
183
+ # BSB Format Filler
184
+ # Max: 7
185
+ # Char position: 2-8
186
+ output += "999-999"
187
+
188
+ # Reserved
189
+ # Max: 12
190
+ # Char position: 9-20
191
+ output += " " * 12
192
+
193
+ # Net total
194
+ # Max: 10
195
+ # Char position: 21-30
196
+ output += net_total_amount.abs.to_s.rjust(10, "0")
197
+
198
+ # Credit Total Amount
199
+ # Max: 10
200
+ # Char position: 31-40
201
+ output += credit_total_amount.abs.to_s.rjust(10, "0")
202
+
203
+ # Debit Total Amount
204
+ # Max: 10
205
+ # Char position: 41-50
206
+ output += debit_total_amount.abs.to_s.rjust(10, "0")
207
+
208
+ # Reserved
209
+ # Max: 24
210
+ # Char position: 51-74
211
+ output += " " * 24
212
+
213
+ # Total Item Count
214
+ # Max: 6
215
+ # Char position: 75-80
216
+ output += entries.size.to_s.rjust(6, "0")
217
+
218
+ # Reserved
219
+ # Max: 40
220
+ # Char position: 81-120
221
+ output += " " * 40
222
+ end
223
+ end
224
+ end