ibandit 0.8.1 → 0.8.2
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 +4 -0
- data/data/raw/BLZ2.txt +410 -481
- data/lib/ibandit.rb +3 -1
- data/lib/ibandit/iban.rb +34 -6
- data/lib/ibandit/local_details_cleaner.rb +13 -14
- data/lib/ibandit/sweden/bank_lookup.rb +24 -0
- data/lib/ibandit/sweden/local_details_converter.rb +92 -0
- data/lib/ibandit/sweden/validator.rb +75 -0
- data/lib/ibandit/version.rb +1 -1
- data/spec/ibandit/iban_spec.rb +92 -39
- data/spec/ibandit/local_details_cleaner_spec.rb +9 -7
- data/spec/ibandit/{swedish_details_converter_spec.rb → sweden/local_details_converter_spec.rb} +1 -114
- data/spec/ibandit/sweden/validator_spec.rb +223 -0
- metadata +26 -23
- data/lib/ibandit/swedish_details_converter.rb +0 -150
data/lib/ibandit.rb
CHANGED
@@ -4,7 +4,9 @@ require 'ibandit/errors'
|
|
4
4
|
require 'ibandit/constants'
|
5
5
|
require 'ibandit/iban'
|
6
6
|
require 'ibandit/german_details_converter'
|
7
|
-
require 'ibandit/
|
7
|
+
require 'ibandit/sweden/local_details_converter'
|
8
|
+
require 'ibandit/sweden/validator'
|
9
|
+
require 'ibandit/sweden/bank_lookup'
|
8
10
|
require 'ibandit/iban_splitter'
|
9
11
|
require 'ibandit/iban_assembler'
|
10
12
|
require 'ibandit/pseudo_iban_assembler'
|
data/lib/ibandit/iban.rb
CHANGED
@@ -276,19 +276,47 @@ module Ibandit
|
|
276
276
|
def valid_swedish_details?
|
277
277
|
return true unless country_code == 'SE'
|
278
278
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
279
|
+
if branch_code
|
280
|
+
valid_swedish_local_details?
|
281
|
+
else
|
282
|
+
valid_swedish_swift_details?
|
283
|
+
end
|
284
|
+
end
|
283
285
|
|
284
|
-
|
286
|
+
def valid_swedish_swift_details?
|
287
|
+
unless Sweden::Validator.bank_code_exists?(swift_bank_code)
|
285
288
|
bank_code_field = bank_code.nil? ? :account_number : :bank_code
|
286
289
|
@errors[bank_code_field] = Ibandit.translate(:is_invalid)
|
287
290
|
@errors.delete(:bank_code) if bank_code.nil?
|
288
291
|
return false
|
289
292
|
end
|
290
293
|
|
291
|
-
|
294
|
+
length_valid =
|
295
|
+
Sweden::Validator.account_number_length_valid_for_bank_code?(
|
296
|
+
bank_code: swift_bank_code,
|
297
|
+
account_number: swift_account_number
|
298
|
+
)
|
299
|
+
|
300
|
+
unless length_valid
|
301
|
+
@errors[:account_number] = Ibandit.translate(:is_invalid)
|
302
|
+
return false
|
303
|
+
end
|
304
|
+
|
305
|
+
true
|
306
|
+
end
|
307
|
+
|
308
|
+
def valid_swedish_local_details?
|
309
|
+
unless Sweden::Validator.valid_clearing_code_length?(branch_code)
|
310
|
+
@errors[:branch_code] = Ibandit.translate(:is_invalid)
|
311
|
+
return false
|
312
|
+
end
|
313
|
+
|
314
|
+
valid_serial_number = Sweden::Validator.valid_serial_number_length?(
|
315
|
+
clearing_code: branch_code,
|
316
|
+
serial_number: account_number
|
317
|
+
)
|
318
|
+
|
319
|
+
unless valid_serial_number
|
292
320
|
@errors[:account_number] = Ibandit.translate(:is_invalid)
|
293
321
|
return false
|
294
322
|
end
|
@@ -441,20 +441,19 @@ module Ibandit
|
|
441
441
|
end
|
442
442
|
|
443
443
|
def self.clean_se_details(local_details)
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
}
|
444
|
+
if local_details[:bank_code]
|
445
|
+
# If a bank_code was provided without a branch code we're (probably)
|
446
|
+
# dealing with SWIFT details and should just return them.
|
447
|
+
return {
|
448
|
+
swift_account_number: local_details[:account_number],
|
449
|
+
swift_bank_code: local_details[:bank_code]
|
450
|
+
}
|
451
|
+
else
|
452
|
+
Sweden::LocalDetailsConverter.new(
|
453
|
+
branch_code: local_details[:branch_code],
|
454
|
+
account_number: local_details[:account_number]
|
455
|
+
).convert
|
456
|
+
end
|
458
457
|
end
|
459
458
|
|
460
459
|
def self.clean_si_details(local_details)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Ibandit
|
2
|
+
module Sweden
|
3
|
+
class BankLookup
|
4
|
+
def self.for_clearing_code(clearing_code)
|
5
|
+
code = clearing_code.to_s.slice(0, 4).to_i
|
6
|
+
bank_info_table.find { |bank| bank[:range].include?(code) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.for_bank_code(bank_code)
|
10
|
+
bank_info_table.select { |bank| bank[:bank_code] == bank_code.to_i }
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.bank_info_table
|
14
|
+
@swedish_bank_lookup ||=
|
15
|
+
begin
|
16
|
+
relative_path = '../../../../data/raw/swedish_bank_lookup.yml'
|
17
|
+
raw_info = YAML.load_file(File.expand_path(relative_path, __FILE__))
|
18
|
+
|
19
|
+
raw_info.map { |bank| bank.merge(range: Range.new(*bank[:range])) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Ibandit
|
2
|
+
module Sweden
|
3
|
+
class LocalDetailsConverter
|
4
|
+
# Converts local Swedish details into SWIFT details.
|
5
|
+
#
|
6
|
+
# Local details can be provided as either:
|
7
|
+
# - branch_code: clearing number, account_number: serial number
|
8
|
+
# - branch_code: nil, account_number: #{clearing number}#{serial number}
|
9
|
+
#
|
10
|
+
# The reverse conversion (extracting local details from SWIFT details) is
|
11
|
+
# not possible, since the clearing number cannot be derived. You should
|
12
|
+
# NOT pass this class a SWIFT account number, as it will not convert it to
|
13
|
+
# local details successfully.
|
14
|
+
def initialize(branch_code: nil, account_number: nil)
|
15
|
+
@branch_code = branch_code
|
16
|
+
@account_number = account_number
|
17
|
+
end
|
18
|
+
|
19
|
+
def convert
|
20
|
+
if bank_info.nil?
|
21
|
+
return { swift_bank_code: nil,
|
22
|
+
swift_account_number: cleaned_account_number.rjust(17, '0') }
|
23
|
+
end
|
24
|
+
|
25
|
+
{
|
26
|
+
account_number: serial_number,
|
27
|
+
branch_code: clearing_code,
|
28
|
+
swift_bank_code: bank_info.fetch(:bank_code).to_s,
|
29
|
+
swift_account_number: swift_account_number
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def cleaned_account_number
|
36
|
+
# Don't trim leading zeroes if the account number we are given is a
|
37
|
+
# serial number (i.e. if the clearing code is separate).
|
38
|
+
@cleaned_account_number ||= remove_bad_chars(@account_number)
|
39
|
+
end
|
40
|
+
|
41
|
+
def cleaned_branch_code
|
42
|
+
@cleaned_branch_code ||= remove_bad_chars(@branch_code)
|
43
|
+
end
|
44
|
+
|
45
|
+
def remove_bad_chars(number)
|
46
|
+
return if number.nil?
|
47
|
+
number.gsub(/[-.\s]/, '')
|
48
|
+
end
|
49
|
+
|
50
|
+
def bank_info
|
51
|
+
@bank_info ||= Sweden::BankLookup.for_clearing_code(bank_info_key)
|
52
|
+
end
|
53
|
+
|
54
|
+
def bank_info_key
|
55
|
+
(cleaned_branch_code || cleaned_account_number).slice(0, 4)
|
56
|
+
end
|
57
|
+
|
58
|
+
def clearing_code_length
|
59
|
+
bank_info.fetch(:clearing_code_length)
|
60
|
+
end
|
61
|
+
|
62
|
+
def serial_number_length
|
63
|
+
bank_info.fetch(:serial_number_length)
|
64
|
+
end
|
65
|
+
|
66
|
+
def clearing_code
|
67
|
+
cleaned_branch_code ||
|
68
|
+
cleaned_account_number.slice(0, clearing_code_length)
|
69
|
+
end
|
70
|
+
|
71
|
+
def serial_number
|
72
|
+
serial_number = if @branch_code.nil?
|
73
|
+
cleaned_account_number[clearing_code_length..-1]
|
74
|
+
else
|
75
|
+
cleaned_account_number
|
76
|
+
end
|
77
|
+
|
78
|
+
return serial_number unless bank_info.fetch(:zerofill_serial_number)
|
79
|
+
|
80
|
+
serial_number.rjust(serial_number_length, '0')
|
81
|
+
end
|
82
|
+
|
83
|
+
def swift_account_number
|
84
|
+
if bank_info.fetch(:include_clearing_code)
|
85
|
+
(clearing_code + serial_number).rjust(17, '0')
|
86
|
+
else
|
87
|
+
serial_number.rjust(17, '0')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Ibandit
|
2
|
+
module Sweden
|
3
|
+
class Validator
|
4
|
+
###########################
|
5
|
+
# Local detail validators #
|
6
|
+
###########################
|
7
|
+
|
8
|
+
def self.bank_code_exists_for_clearing_code?(clearing_code)
|
9
|
+
!Sweden::BankLookup.for_clearing_code(clearing_code).nil?
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.valid_clearing_code_length?(clearing_code)
|
13
|
+
return unless bank_code_exists_for_clearing_code?(clearing_code)
|
14
|
+
|
15
|
+
bank_info = Sweden::BankLookup.for_clearing_code(clearing_code)
|
16
|
+
bank_info.fetch(:clearing_code_length) == clearing_code.to_s.length
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.valid_serial_number_length?(clearing_code: nil,
|
20
|
+
serial_number: nil)
|
21
|
+
return unless bank_code_exists_for_clearing_code?(clearing_code)
|
22
|
+
|
23
|
+
bank_info = Sweden::BankLookup.for_clearing_code(clearing_code)
|
24
|
+
serial_number_length = bank_info.fetch(:serial_number_length)
|
25
|
+
|
26
|
+
if bank_info.fetch(:zerofill_serial_number)
|
27
|
+
serial_number = serial_number.rjust(serial_number_length, '0')
|
28
|
+
end
|
29
|
+
|
30
|
+
serial_number_length == serial_number.to_s.length
|
31
|
+
end
|
32
|
+
|
33
|
+
###########################
|
34
|
+
# SWIFT detail validators #
|
35
|
+
###########################
|
36
|
+
|
37
|
+
def self.bank_code_exists?(bank_code)
|
38
|
+
Sweden::BankLookup.for_bank_code(bank_code).any?
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.bank_code_possible_for_account_number?(bank_code: nil,
|
42
|
+
account_number: nil)
|
43
|
+
return unless bank_code_exists?(bank_code)
|
44
|
+
|
45
|
+
clearing_code = account_number.gsub(/\A0+/, '').slice(0, 4).to_i
|
46
|
+
Sweden::BankLookup.for_bank_code(bank_code).any? do |bank|
|
47
|
+
!bank[:include_clearing_code] || bank[:range].include?(clearing_code)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.account_number_length_valid_for_bank_code?(bank_code: nil,
|
52
|
+
account_number: nil)
|
53
|
+
bank_code_possible = bank_code_possible_for_account_number?(
|
54
|
+
bank_code: bank_code,
|
55
|
+
account_number: account_number
|
56
|
+
)
|
57
|
+
return unless bank_code_possible
|
58
|
+
|
59
|
+
Sweden::BankLookup.for_bank_code(bank_code).any? do |bank|
|
60
|
+
length = bank.fetch(:serial_number_length)
|
61
|
+
length += bank[:clearing_code_length] if bank[:include_clearing_code]
|
62
|
+
|
63
|
+
cleaned_account_number = account_number.gsub(/\A0+/, '')
|
64
|
+
if bank[:zerofill_serial_number] && !bank[:include_clearing_code]
|
65
|
+
cleaned_account_number =
|
66
|
+
cleaned_account_number.
|
67
|
+
rjust(bank.fetch(:serial_number_length), '0')
|
68
|
+
end
|
69
|
+
|
70
|
+
cleaned_account_number.length == length
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/ibandit/version.rb
CHANGED
data/spec/ibandit/iban_spec.rb
CHANGED
@@ -1353,60 +1353,113 @@ describe Ibandit::IBAN do
|
|
1353
1353
|
describe 'valid_swedish_details?' do
|
1354
1354
|
subject { iban.valid_swedish_details? }
|
1355
1355
|
|
1356
|
-
context 'with
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1356
|
+
context 'with SWIFT details' do
|
1357
|
+
context 'with an account number that is too long' do
|
1358
|
+
let(:arg) do
|
1359
|
+
{
|
1360
|
+
country_code: 'SE',
|
1361
|
+
bank_code: '500',
|
1362
|
+
account_number: '00000543910240391'
|
1363
|
+
}
|
1364
|
+
end
|
1364
1365
|
|
1365
|
-
|
1366
|
+
it { is_expected.to eq(false) }
|
1366
1367
|
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1368
|
+
context 'locale en', locale: :en do
|
1369
|
+
specify do
|
1370
|
+
iban.valid_swedish_details?
|
1371
|
+
expect(iban.errors).to eq(account_number: 'is invalid')
|
1372
|
+
end
|
1371
1373
|
end
|
1372
1374
|
end
|
1373
|
-
end
|
1374
1375
|
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1376
|
+
context "with an account number that doesn't have a bank code" do
|
1377
|
+
let(:arg) do
|
1378
|
+
{
|
1379
|
+
country_code: 'SE',
|
1380
|
+
bank_code: nil,
|
1381
|
+
account_number: '00000000000010011'
|
1382
|
+
}
|
1383
|
+
end
|
1384
|
+
|
1385
|
+
it { is_expected.to eq(false) }
|
1386
|
+
|
1387
|
+
context 'locale en', locale: :en do
|
1388
|
+
specify do
|
1389
|
+
iban.valid?
|
1390
|
+
expect(iban.errors).to include(account_number: 'is invalid')
|
1391
|
+
expect(iban.errors).to_not include(:bank_code)
|
1392
|
+
end
|
1393
|
+
end
|
1382
1394
|
end
|
1383
1395
|
|
1384
|
-
|
1396
|
+
context 'with a bank code that does not match' do
|
1397
|
+
let(:arg) do
|
1398
|
+
{
|
1399
|
+
country_code: 'SE',
|
1400
|
+
bank_code: '902',
|
1401
|
+
account_number: '00000054391024039'
|
1402
|
+
}
|
1403
|
+
end
|
1385
1404
|
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1405
|
+
it { is_expected.to eq(false) }
|
1406
|
+
|
1407
|
+
context 'locale en', locale: :en do
|
1408
|
+
specify do
|
1409
|
+
iban.valid_swedish_details?
|
1410
|
+
expect(iban.errors).to eq(account_number: 'is invalid')
|
1411
|
+
end
|
1391
1412
|
end
|
1392
1413
|
end
|
1393
1414
|
end
|
1394
1415
|
|
1395
|
-
context 'with
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1416
|
+
context 'with local details' do
|
1417
|
+
context 'with good details' do
|
1418
|
+
let(:arg) do
|
1419
|
+
{
|
1420
|
+
country_code: 'SE',
|
1421
|
+
account_number: '5439-0240391'
|
1422
|
+
}
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
it { is_expected.to eq(true) }
|
1402
1426
|
end
|
1403
1427
|
|
1404
|
-
|
1428
|
+
context 'with a clearing code that is too long' do
|
1429
|
+
let(:arg) do
|
1430
|
+
{
|
1431
|
+
country_code: 'SE',
|
1432
|
+
branch_code: '54391',
|
1433
|
+
account_number: '0240391'
|
1434
|
+
}
|
1435
|
+
end
|
1405
1436
|
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1437
|
+
it { is_expected.to eq(false) }
|
1438
|
+
|
1439
|
+
context 'locale en', locale: :en do
|
1440
|
+
specify do
|
1441
|
+
iban.valid_swedish_details?
|
1442
|
+
expect(iban.errors).to eq(branch_code: 'is invalid')
|
1443
|
+
end
|
1444
|
+
end
|
1445
|
+
end
|
1446
|
+
|
1447
|
+
context 'with a serial number that is too long' do
|
1448
|
+
let(:arg) do
|
1449
|
+
{
|
1450
|
+
country_code: 'SE',
|
1451
|
+
branch_code: '5439',
|
1452
|
+
account_number: '024039111'
|
1453
|
+
}
|
1454
|
+
end
|
1455
|
+
|
1456
|
+
it { is_expected.to eq(false) }
|
1457
|
+
|
1458
|
+
context 'locale en', locale: :en do
|
1459
|
+
specify do
|
1460
|
+
iban.valid_swedish_details?
|
1461
|
+
expect(iban.errors).to eq(account_number: 'is invalid')
|
1462
|
+
end
|
1410
1463
|
end
|
1411
1464
|
end
|
1412
1465
|
end
|