bai2_ruby 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +36 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +100 -0
- data/Rakefile +12 -0
- data/lib/bai2_ruby/account.rb +31 -0
- data/lib/bai2_ruby/file.rb +29 -0
- data/lib/bai2_ruby/group.rb +31 -0
- data/lib/bai2_ruby/parser.rb +131 -0
- data/lib/bai2_ruby/structures/account_header.rb +15 -0
- data/lib/bai2_ruby/structures/account_trailer.rb +14 -0
- data/lib/bai2_ruby/structures/file_header.rb +22 -0
- data/lib/bai2_ruby/structures/file_trailer.rb +15 -0
- data/lib/bai2_ruby/structures/group_header.rb +21 -0
- data/lib/bai2_ruby/structures/group_trailer.rb +15 -0
- data/lib/bai2_ruby/structures/transaction.rb +19 -0
- data/lib/bai2_ruby/transaction.rb +50 -0
- data/lib/bai2_ruby/version.rb +5 -0
- data/lib/bai2_ruby/writer.rb +77 -0
- data/lib/bai2_ruby.rb +23 -0
- data/sig/bai2_ruby.rbs +4 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f0b1d37b3e9c9ca7581b62b7cd7925d3366b570e45012bee9284ec59d44c6903
|
4
|
+
data.tar.gz: 6e6dc98eb2a9a0f394ad54143f2591140dc31152094a02df76f2f7270750ffe3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 883269f713bd6d7036fa287b0c0c27f606638030193690f15cc94966595bd70cc4c47a96120ea50ddd1293c49ba5e9eda60822017303edaccc5aeb7b2629fc4a
|
7
|
+
data.tar.gz: dbef20c4a3ecd2a554c127ca27981ace0a45449fd793619a398a839e28b4e3f7fc7e8d99fbe817894c43aa0d122f1e0f1a3dbe3572f55f636a2104b54149676d
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
AllCops:
|
2
|
+
NewCops: enable
|
3
|
+
SuggestExtensions: false
|
4
|
+
TargetRubyVersion: 3.0
|
5
|
+
Exclude:
|
6
|
+
- 'bai2_ruby.gemspec'
|
7
|
+
|
8
|
+
Layout/LineLength:
|
9
|
+
Exclude:
|
10
|
+
- 'lib/bai2_ruby/writer.rb'
|
11
|
+
|
12
|
+
Metrics/ClassLength:
|
13
|
+
Max: 150
|
14
|
+
|
15
|
+
Metrics/BlockLength:
|
16
|
+
Exclude:
|
17
|
+
- 'spec/**/*.rb'
|
18
|
+
|
19
|
+
Metrics/MethodLength:
|
20
|
+
Max: 15
|
21
|
+
|
22
|
+
Metrics/ParameterLists:
|
23
|
+
Exclude:
|
24
|
+
- 'lib/bai2_ruby/structures/**/*.rb'
|
25
|
+
|
26
|
+
Naming/AccessorMethodName:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Style/Documentation:
|
30
|
+
Enabled: false
|
31
|
+
|
32
|
+
Style/StringLiterals:
|
33
|
+
EnforcedStyle: double_quotes
|
34
|
+
|
35
|
+
Style/StringLiteralsInInterpolation:
|
36
|
+
EnforcedStyle: double_quotes
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 dpaluy
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# BAI2 Ruby
|
2
|
+
|
3
|
+
BAI2 - Bank Administration Institute 2 file format
|
4
|
+
|
5
|
+
BAI2 Ruby is a gem that provides a parser and writer for the BAI2 (Bank Administration Institute) file format. This gem allows you to easily read and write BAI2 files in your Ruby applications.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
- Parse BAI2 files into Ruby objects
|
10
|
+
- Generate BAI2 files from Ruby objects
|
11
|
+
- Supports all standard BAI2 record types
|
12
|
+
- Handles date and time parsing with configurable time zones
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
`gem "bai2_ruby"`
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
### Parsing a BAI2 file
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
require 'bai2_ruby'
|
24
|
+
|
25
|
+
# Read the content of a BAI2 file
|
26
|
+
bai2_content = File.read('path/to/your/bai2_file.bai')
|
27
|
+
|
28
|
+
# Create a parser instance
|
29
|
+
parser = BAI2::Parser.new(bai2_content)
|
30
|
+
|
31
|
+
# Parse the content
|
32
|
+
bai2_file = parser.parse
|
33
|
+
|
34
|
+
# Access the parsed data
|
35
|
+
puts bai2_file.header.sender_id
|
36
|
+
puts bai2_file.header.file_creation_date
|
37
|
+
|
38
|
+
bai2_file.groups.each do |group|
|
39
|
+
puts group.header.originator_id
|
40
|
+
group.accounts.each do |account|
|
41
|
+
puts account.header.customer_account_number
|
42
|
+
account.transactions.each do |transaction|
|
43
|
+
puts "#{transaction.type}: #{transaction.amount}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
### Writing a BAI2 file
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
require 'bai2_ruby'
|
53
|
+
|
54
|
+
# Create BAI2 structures
|
55
|
+
file_header = BAI2::Structures::FileHeader.new(
|
56
|
+
sender_id: 'SENDER1',
|
57
|
+
receiver_id: 'RECEIVER1',
|
58
|
+
file_creation_date: Date.today,
|
59
|
+
file_creation_time: Time.now,
|
60
|
+
file_id: '123456',
|
61
|
+
physical_record_length: 80,
|
62
|
+
block_size: 0,
|
63
|
+
version_number: '2'
|
64
|
+
)
|
65
|
+
|
66
|
+
# ... Create other structures (groups, accounts, transactions) ...
|
67
|
+
|
68
|
+
# Create a BAI2::File object
|
69
|
+
bai2_file = BAI2::File.new(file_header, groups, file_trailer)
|
70
|
+
|
71
|
+
# Create a writer instance
|
72
|
+
writer = BAI2::Writer.new(bai2_file)
|
73
|
+
|
74
|
+
# Generate BAI2 content
|
75
|
+
bai2_content = writer.generate
|
76
|
+
|
77
|
+
# Write to a file
|
78
|
+
File.write('path/to/output.bai', bai2_content)
|
79
|
+
```
|
80
|
+
|
81
|
+
### Configuration
|
82
|
+
|
83
|
+
By default, the parser uses UTC for time parsing. You can specify a different time zone when creating a parser:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
parser = BAI2::Parser.new(bai2_content, time_zone: "-05:00")
|
87
|
+
```
|
88
|
+
|
89
|
+
## Development
|
90
|
+
|
91
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
92
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
93
|
+
|
94
|
+
## Contributing
|
95
|
+
|
96
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/dpaluy/bai2_ruby.
|
97
|
+
|
98
|
+
## License
|
99
|
+
|
100
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BAI2
|
4
|
+
class Account
|
5
|
+
attr_reader :header, :transactions, :trailer
|
6
|
+
|
7
|
+
def initialize(header, transactions, trailer = nil)
|
8
|
+
@header = header
|
9
|
+
@transactions = transactions
|
10
|
+
@trailer = trailer
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_trailer(trailer)
|
14
|
+
raise BAI2::Error, "Trailer has already been set" if @trailer
|
15
|
+
|
16
|
+
@trailer = trailer
|
17
|
+
end
|
18
|
+
|
19
|
+
def total_credits
|
20
|
+
transactions.select(&:credit?).sum(&:amount)
|
21
|
+
end
|
22
|
+
|
23
|
+
def total_debits
|
24
|
+
transactions.select(&:debit?).sum(&:amount)
|
25
|
+
end
|
26
|
+
|
27
|
+
def net_total
|
28
|
+
transactions.sum(&:signed_amount)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BAI2
|
4
|
+
class File
|
5
|
+
attr_reader :header, :groups, :trailer
|
6
|
+
|
7
|
+
def initialize(header, groups, trailer)
|
8
|
+
@header = header
|
9
|
+
@groups = groups
|
10
|
+
@trailer = trailer
|
11
|
+
end
|
12
|
+
|
13
|
+
def total_credits
|
14
|
+
groups.sum(&:total_credits)
|
15
|
+
end
|
16
|
+
|
17
|
+
def total_debits
|
18
|
+
groups.sum(&:total_debits)
|
19
|
+
end
|
20
|
+
|
21
|
+
def net_total
|
22
|
+
groups.sum(&:net_total)
|
23
|
+
end
|
24
|
+
|
25
|
+
def all_transactions
|
26
|
+
groups.flat_map { |group| group.accounts.flat_map(&:transactions) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BAI2
|
4
|
+
class Group
|
5
|
+
attr_reader :header, :accounts, :trailer
|
6
|
+
|
7
|
+
def initialize(header, accounts, trailer = nil)
|
8
|
+
@header = header
|
9
|
+
@accounts = accounts
|
10
|
+
@trailer = trailer
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_trailer(trailer)
|
14
|
+
raise BAI2::Error, "Trailer has already been set" if @trailer
|
15
|
+
|
16
|
+
@trailer = trailer
|
17
|
+
end
|
18
|
+
|
19
|
+
def total_credits
|
20
|
+
accounts.sum(&:total_credits)
|
21
|
+
end
|
22
|
+
|
23
|
+
def total_debits
|
24
|
+
accounts.sum(&:total_debits)
|
25
|
+
end
|
26
|
+
|
27
|
+
def net_total
|
28
|
+
accounts.sum(&:net_total)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BAI2
|
4
|
+
class Parser
|
5
|
+
def initialize(content, time_zone: "+00:00")
|
6
|
+
@lines = content.split("\n")
|
7
|
+
@current_group = nil
|
8
|
+
@current_account = nil
|
9
|
+
@time_zone = time_zone
|
10
|
+
end
|
11
|
+
|
12
|
+
# rubocop:disable Metrics/AbcSize
|
13
|
+
# rubocop:disable Metrics/MethodLength
|
14
|
+
def parse
|
15
|
+
file_header = parse_file_header(@lines.shift)
|
16
|
+
file_trailer = parse_file_trailer(@lines.pop)
|
17
|
+
|
18
|
+
groups = []
|
19
|
+
|
20
|
+
@lines.each do |line|
|
21
|
+
record_code = line[0, 2]
|
22
|
+
case record_code
|
23
|
+
when "02"
|
24
|
+
@current_group = parse_group_header(line)
|
25
|
+
groups << @current_group
|
26
|
+
when "03"
|
27
|
+
@current_account = parse_account_header(line)
|
28
|
+
@current_group.accounts << @current_account
|
29
|
+
when "16"
|
30
|
+
transaction = parse_transaction(line)
|
31
|
+
@current_account.transactions << transaction
|
32
|
+
when "49"
|
33
|
+
parse_account_trailer(line)
|
34
|
+
when "98"
|
35
|
+
parse_group_trailer(line)
|
36
|
+
else
|
37
|
+
raise BAI2::Error, "Unknown record code: #{record_code}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
BAI2::File.new(file_header, groups, file_trailer)
|
42
|
+
end
|
43
|
+
# rubocop:enable Metrics/AbcSize
|
44
|
+
# rubocop:enable Metrics/MethodLength
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def parse_file_header(line)
|
49
|
+
fields = line.split(",")
|
50
|
+
date = parse_date(fields[3])
|
51
|
+
BAI2::Structures::FileHeader.new(
|
52
|
+
sender_id: fields[1],
|
53
|
+
receiver_id: fields[2],
|
54
|
+
file_creation_date: date,
|
55
|
+
file_creation_time: parse_time(fields[4], date),
|
56
|
+
file_id: fields[5],
|
57
|
+
physical_record_length: fields[6].to_i,
|
58
|
+
block_size: fields[7].to_i,
|
59
|
+
version_number: fields[8]
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_file_trailer(line)
|
64
|
+
fields = line.split(",")
|
65
|
+
BAI2::Structures::FileTrailer.new(
|
66
|
+
file_control_total: fields[1].to_f,
|
67
|
+
number_of_groups: fields[2].to_i,
|
68
|
+
number_of_records: fields[3].to_i
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_group_header(line)
|
73
|
+
fields = line.split(",")
|
74
|
+
date = parse_date(fields[4])
|
75
|
+
header = BAI2::Structures::GroupHeader.new(
|
76
|
+
ultimate_receiver_id: fields[1],
|
77
|
+
originator_id: fields[2],
|
78
|
+
group_status: fields[3],
|
79
|
+
as_of_date: date,
|
80
|
+
as_of_time: parse_time(fields[5], date),
|
81
|
+
currency_code: fields[6],
|
82
|
+
as_of_date_modifier: fields[7]
|
83
|
+
)
|
84
|
+
BAI2::Group.new(header, [])
|
85
|
+
end
|
86
|
+
|
87
|
+
def parse_account_header(line)
|
88
|
+
fields = line.split(",")
|
89
|
+
header = BAI2::Structures::AccountHeader.new(
|
90
|
+
customer_account_number: fields[1],
|
91
|
+
currency_code: fields[2],
|
92
|
+
type_code: fields[3]
|
93
|
+
)
|
94
|
+
BAI2::Account.new(header, [])
|
95
|
+
end
|
96
|
+
|
97
|
+
def parse_transaction(line)
|
98
|
+
fields = line.split(",")
|
99
|
+
BAI2::Transaction.new(fields[1], fields[2].to_f, fields[3])
|
100
|
+
end
|
101
|
+
|
102
|
+
def parse_account_trailer(line)
|
103
|
+
fields = line.split(",")
|
104
|
+
trailer = BAI2::Structures::AccountTrailer.new(
|
105
|
+
account_control_total: fields[1].to_f,
|
106
|
+
number_of_records: fields[2].to_i
|
107
|
+
)
|
108
|
+
@current_account.set_trailer(trailer)
|
109
|
+
end
|
110
|
+
|
111
|
+
def parse_group_trailer(line)
|
112
|
+
fields = line.split(",")
|
113
|
+
trailer = BAI2::Structures::GroupTrailer.new(
|
114
|
+
group_control_total: fields[1].to_f,
|
115
|
+
number_of_accounts: fields[2].to_i,
|
116
|
+
number_of_records: fields[3].to_i
|
117
|
+
)
|
118
|
+
@current_group.set_trailer(trailer)
|
119
|
+
end
|
120
|
+
|
121
|
+
def parse_date(date_string)
|
122
|
+
Date.strptime(date_string, "%y%m%d")
|
123
|
+
end
|
124
|
+
|
125
|
+
def parse_time(time_string, date)
|
126
|
+
hours = time_string[0, 2].to_i
|
127
|
+
minutes = time_string[2, 2].to_i
|
128
|
+
Time.new(date.year, date.month, date.day, hours, minutes, 0, @time_zone)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BAI2
|
4
|
+
module Structures
|
5
|
+
class AccountHeader
|
6
|
+
attr_reader :customer_account_number, :currency_code, :type_code
|
7
|
+
|
8
|
+
def initialize(customer_account_number:, currency_code:, type_code:)
|
9
|
+
@customer_account_number = customer_account_number
|
10
|
+
@currency_code = currency_code
|
11
|
+
@type_code = type_code
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BAI2
|
4
|
+
module Structures
|
5
|
+
class AccountTrailer
|
6
|
+
attr_reader :account_control_total, :number_of_records
|
7
|
+
|
8
|
+
def initialize(account_control_total:, number_of_records:)
|
9
|
+
@account_control_total = account_control_total
|
10
|
+
@number_of_records = number_of_records
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BAI2
|
4
|
+
module Structures
|
5
|
+
class FileHeader
|
6
|
+
attr_reader :sender_id, :receiver_id, :file_creation_date, :file_creation_time,
|
7
|
+
:file_id, :physical_record_length, :block_size, :version_number
|
8
|
+
|
9
|
+
def initialize(sender_id:, receiver_id:, file_creation_date:, file_creation_time:,
|
10
|
+
file_id:, physical_record_length:, block_size:, version_number:)
|
11
|
+
@sender_id = sender_id
|
12
|
+
@receiver_id = receiver_id
|
13
|
+
@file_creation_date = file_creation_date
|
14
|
+
@file_creation_time = file_creation_time
|
15
|
+
@file_id = file_id
|
16
|
+
@physical_record_length = physical_record_length
|
17
|
+
@block_size = block_size
|
18
|
+
@version_number = version_number
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BAI2
|
4
|
+
module Structures
|
5
|
+
class FileTrailer
|
6
|
+
attr_reader :file_control_total, :number_of_groups, :number_of_records
|
7
|
+
|
8
|
+
def initialize(file_control_total:, number_of_groups:, number_of_records:)
|
9
|
+
@file_control_total = file_control_total
|
10
|
+
@number_of_groups = number_of_groups
|
11
|
+
@number_of_records = number_of_records
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BAI2
|
4
|
+
module Structures
|
5
|
+
class GroupHeader
|
6
|
+
attr_reader :ultimate_receiver_id, :originator_id, :group_status, :as_of_date,
|
7
|
+
:as_of_time, :currency_code, :as_of_date_modifier
|
8
|
+
|
9
|
+
def initialize(ultimate_receiver_id:, originator_id:, group_status:, as_of_date:,
|
10
|
+
as_of_time:, currency_code:, as_of_date_modifier:)
|
11
|
+
@ultimate_receiver_id = ultimate_receiver_id
|
12
|
+
@originator_id = originator_id
|
13
|
+
@group_status = group_status
|
14
|
+
@as_of_date = as_of_date
|
15
|
+
@as_of_time = as_of_time
|
16
|
+
@currency_code = currency_code
|
17
|
+
@as_of_date_modifier = as_of_date_modifier
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BAI2
|
4
|
+
module Structures
|
5
|
+
class GroupTrailer
|
6
|
+
attr_reader :group_control_total, :number_of_accounts, :number_of_records
|
7
|
+
|
8
|
+
def initialize(group_control_total:, number_of_accounts:, number_of_records:)
|
9
|
+
@group_control_total = group_control_total
|
10
|
+
@number_of_accounts = number_of_accounts
|
11
|
+
@number_of_records = number_of_records
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BAI2
|
4
|
+
module Structures
|
5
|
+
class Transaction
|
6
|
+
attr_reader :type, :amount, :funds_type, :availability, :bank_reference, :customer_reference, :text
|
7
|
+
|
8
|
+
def initialize(type:, amount:, funds_type:, availability:, bank_reference:, customer_reference:, text:)
|
9
|
+
@type = type
|
10
|
+
@amount = amount
|
11
|
+
@funds_type = funds_type
|
12
|
+
@availability = availability
|
13
|
+
@bank_reference = bank_reference
|
14
|
+
@customer_reference = customer_reference
|
15
|
+
@text = text
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BAI2
|
4
|
+
class Transaction
|
5
|
+
attr_reader :type, :amount, :text, :funds_type
|
6
|
+
|
7
|
+
CREDIT_CODES = %w[105 106 107 108 109 110 115 116 118 201 202 206 207 210 221 222 223 224 225 226 227 230 231 263
|
8
|
+
285 415 416 420 490 600 601 616 618 621 623 632 633 634 664 701 702 703 705 706 707 708 709 710
|
9
|
+
711 712 713 714 715 716 717 805 902].freeze
|
10
|
+
DEBIT_CODES = %w[175 176 177 178 179 180 181 185 186 187 188 277 278 405 406 407 408 410 412 413 414 425 465 469
|
11
|
+
470 471 472 477 481 482 484 485 486 487 489 500 501 502 505 506 507 508 509 510 512 513 514 516
|
12
|
+
518 519 522 532 535 536 554 555 556 557 558 560 561 564 566 570 702 703 709 715 720 890].freeze
|
13
|
+
|
14
|
+
def initialize(type, amount, text, funds_type = nil)
|
15
|
+
@type = type
|
16
|
+
@amount = amount.to_f
|
17
|
+
@text = text
|
18
|
+
@funds_type = funds_type
|
19
|
+
end
|
20
|
+
|
21
|
+
def debit?
|
22
|
+
DEBIT_CODES.include?(@type)
|
23
|
+
end
|
24
|
+
|
25
|
+
def credit?
|
26
|
+
CREDIT_CODES.include?(@type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def signed_amount
|
30
|
+
debit? ? -@amount : @amount
|
31
|
+
end
|
32
|
+
|
33
|
+
# rubocop:disable Style/FormatString
|
34
|
+
def to_s
|
35
|
+
"#{credit? ? "Credit" : "Debit"} transaction (#{@type}): $#{"%.2f" % @amount.abs} - #{@text}"
|
36
|
+
end
|
37
|
+
# rubocop:enable Style/FormatString
|
38
|
+
|
39
|
+
def as_json
|
40
|
+
{
|
41
|
+
type: @type,
|
42
|
+
amount: @amount,
|
43
|
+
signed_amount: signed_amount,
|
44
|
+
text: @text,
|
45
|
+
is_debit: debit?,
|
46
|
+
is_credit: credit?
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BAI2
|
4
|
+
class Writer
|
5
|
+
def initialize(file)
|
6
|
+
@file = file
|
7
|
+
end
|
8
|
+
|
9
|
+
# rubocop:disable Metrics/AbcSize
|
10
|
+
def generate
|
11
|
+
content = []
|
12
|
+
content << generate_file_header(@file.header)
|
13
|
+
|
14
|
+
@file.groups.each do |group|
|
15
|
+
content << generate_group_header(group.header)
|
16
|
+
|
17
|
+
group.accounts.each do |account|
|
18
|
+
content << generate_account_header(account.header)
|
19
|
+
|
20
|
+
account.transactions.each do |transaction|
|
21
|
+
content << generate_transaction(transaction)
|
22
|
+
end
|
23
|
+
|
24
|
+
content << generate_account_trailer(account.trailer)
|
25
|
+
end
|
26
|
+
|
27
|
+
content << generate_group_trailer(group.trailer)
|
28
|
+
end
|
29
|
+
|
30
|
+
content << generate_file_trailer(@file.trailer)
|
31
|
+
content.join("\n")
|
32
|
+
end
|
33
|
+
# rubocop:enable Metrics/AbcSize
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def generate_file_header(header)
|
38
|
+
"01,#{header.sender_id},#{header.receiver_id},#{format_date(header.file_creation_date)},#{format_time(header.file_creation_time)},#{header.file_id},#{header.physical_record_length},#{header.block_size},#{header.version_number}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate_group_header(header)
|
42
|
+
"02,#{header.ultimate_receiver_id},#{header.originator_id},#{header.group_status},#{format_date(header.as_of_date)},#{format_time(header.as_of_time)},#{header.currency_code},#{header.as_of_date_modifier}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def generate_account_header(header)
|
46
|
+
"03,#{header.customer_account_number},#{header.currency_code},#{header.type_code}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def generate_transaction(transaction)
|
50
|
+
"16,#{transaction.type},#{format_amount(transaction.amount)},#{transaction.text}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def generate_account_trailer(trailer)
|
54
|
+
"49,#{format_amount(trailer.account_control_total)},#{trailer.number_of_records}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def generate_group_trailer(trailer)
|
58
|
+
"98,#{format_amount(trailer.group_control_total)},#{trailer.number_of_accounts},#{trailer.number_of_records}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def generate_file_trailer(trailer)
|
62
|
+
"99,#{format_amount(trailer.file_control_total)},#{trailer.number_of_groups},#{trailer.number_of_records}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def format_date(date)
|
66
|
+
date.strftime("%y%m%d")
|
67
|
+
end
|
68
|
+
|
69
|
+
def format_time(time)
|
70
|
+
time.strftime("%H%M")
|
71
|
+
end
|
72
|
+
|
73
|
+
def format_amount(amount)
|
74
|
+
format("%.2f", amount)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/bai2_ruby.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
require "time"
|
5
|
+
require_relative "bai2_ruby/version"
|
6
|
+
require_relative "bai2_ruby/structures/file_header"
|
7
|
+
require_relative "bai2_ruby/structures/file_trailer"
|
8
|
+
require_relative "bai2_ruby/structures/group_header"
|
9
|
+
require_relative "bai2_ruby/structures/group_trailer"
|
10
|
+
require_relative "bai2_ruby/structures/account_header"
|
11
|
+
require_relative "bai2_ruby/structures/account_trailer"
|
12
|
+
require_relative "bai2_ruby/structures/transaction"
|
13
|
+
require_relative "bai2_ruby/parser"
|
14
|
+
require_relative "bai2_ruby/writer"
|
15
|
+
require_relative "bai2_ruby/file"
|
16
|
+
require_relative "bai2_ruby/group"
|
17
|
+
require_relative "bai2_ruby/account"
|
18
|
+
require_relative "bai2_ruby/transaction"
|
19
|
+
|
20
|
+
module BAI2
|
21
|
+
class Error < StandardError; end
|
22
|
+
# Your code goes here...
|
23
|
+
end
|
data/sig/bai2_ruby.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bai2_ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- dpaluy
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-09-12 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Bank Administration Institute 2 (BAI2) file format implementation wrapper
|
14
|
+
for Ruby
|
15
|
+
email:
|
16
|
+
- dpaluy@users.noreply.github.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- ".rspec"
|
22
|
+
- ".rubocop.yml"
|
23
|
+
- CHANGELOG.md
|
24
|
+
- LICENSE.txt
|
25
|
+
- README.md
|
26
|
+
- Rakefile
|
27
|
+
- lib/bai2_ruby.rb
|
28
|
+
- lib/bai2_ruby/account.rb
|
29
|
+
- lib/bai2_ruby/file.rb
|
30
|
+
- lib/bai2_ruby/group.rb
|
31
|
+
- lib/bai2_ruby/parser.rb
|
32
|
+
- lib/bai2_ruby/structures/account_header.rb
|
33
|
+
- lib/bai2_ruby/structures/account_trailer.rb
|
34
|
+
- lib/bai2_ruby/structures/file_header.rb
|
35
|
+
- lib/bai2_ruby/structures/file_trailer.rb
|
36
|
+
- lib/bai2_ruby/structures/group_header.rb
|
37
|
+
- lib/bai2_ruby/structures/group_trailer.rb
|
38
|
+
- lib/bai2_ruby/structures/transaction.rb
|
39
|
+
- lib/bai2_ruby/transaction.rb
|
40
|
+
- lib/bai2_ruby/version.rb
|
41
|
+
- lib/bai2_ruby/writer.rb
|
42
|
+
- sig/bai2_ruby.rbs
|
43
|
+
homepage: https://github.com/dpaluy/bai2_ruby
|
44
|
+
licenses:
|
45
|
+
- MIT
|
46
|
+
metadata: {}
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 3.0.0
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubygems_version: 3.5.18
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: BAI2 implementation wrapper for Ruby
|
66
|
+
test_files: []
|