ibandit 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|