ibandit 0.7.0 → 0.8.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
2
  SHA1:
3
- metadata.gz: 53b3af5d1ef9ac7b61de43b1cc268380af1e1242
4
- data.tar.gz: 751d53c25ff31c8a44f7cd334f76dac3389d8c0e
3
+ metadata.gz: 0bc26110597f8350ef5b91b0b2fbee330c21bc7b
4
+ data.tar.gz: 3b1d66ea7ceb6b58f275d164e4e873c34416ddd4
5
5
  SHA512:
6
- metadata.gz: a8c6fa27a0efa6909f0783d5257bcce17ade3e21a1030078056854e8f3638e27568c61fe27485ab594f17bb9a0ce818206b521a463821e45f711fb23a50d1af8
7
- data.tar.gz: bf770b81e770a5ed0037ae3b8ef810e84043f9355eadef160fe8cfcde8fd8321c34a8d2be3392a39dc97ebdb15fd879b4907fef7196571a5523b691ec4d1cc79
6
+ metadata.gz: 32ad174ca626b5f73fff4472fc42699146b57d194a871b480cd30765efe7b74d7c918f9f92e75500b2367243521f128282730220818369d6105482ef257f8360
7
+ data.tar.gz: 9440bd30011a7d61f2dd2beec9ee115ab27c0ca9af6cea75b2022d48ac3e25dfc49e3bfcdeb44665e2c0fe3ce1c3978494e9cd4233c80d25a51c280e596938ee
@@ -12,7 +12,7 @@ LineLength:
12
12
  Max: 80
13
13
 
14
14
  ClassLength:
15
- Max: 300
15
+ Max: 400
16
16
 
17
17
  # Avoid single-line methods.
18
18
  SingleLineMethods:
@@ -39,5 +39,9 @@ Style/AsciiComments:
39
39
  Metrics/MethodLength:
40
40
  Max: 25
41
41
 
42
+ # Configuration parameters: CountComments.
43
+ Metrics/ModuleLength:
44
+ Max: 400
45
+
42
46
  Style/DotPosition:
43
47
  EnforcedStyle: 'trailing'
@@ -1,3 +1,7 @@
1
+ ## 0.8.0 - August 14, 2015
2
+ - Return local details for Sweden
3
+ - Introduce pseudo-IBANs for Sweden
4
+
1
5
  ## 0.7.0 - August 11, 2015
2
6
  - Remove all unused `CheckDigit` methods
3
7
 
data/README.md CHANGED
@@ -97,13 +97,13 @@ county combines them:
97
97
  `check_digits`
98
98
  : Two digits calculated using part of the ISO/IEC 7064:2003 standard
99
99
 
100
- `bank_code`
100
+ `swift_bank_code`
101
101
  : The SWIFT identifier for the bank to which the IBAN refers
102
102
 
103
- `branch_code`
103
+ `swift_branch_code`
104
104
  : The SWIFT identifer for the branch to which the IBAN refers (not used in all countries)
105
105
 
106
- `account_number`
106
+ `swift_account_number`
107
107
  : The account number for the account
108
108
 
109
109
  `iban_national_id`
@@ -116,9 +116,9 @@ iban = Ibandit::IBAN.new("GB82 WEST 1234 5698 7654 32")
116
116
 
117
117
  iban.country_code # => "GB"
118
118
  iban.check_digits # => "82"
119
- iban.bank_code # => "WEST"
120
- iban.branch_code # => "123456"
121
- iban.account_number # => "98765432"
119
+ iban.swift_bank_code # => "WEST"
120
+ iban.swift_branch_code # => "123456"
121
+ iban.swift_account_number # => "98765432"
122
122
  iban.iban_national_id # => "WEST123456"
123
123
  ```
124
124
 
@@ -131,6 +131,23 @@ iban = Ibandit::IBAN.new("ES12 1234 5678 9112 3456 7890")
131
131
  iban.local_check_digits # => "91"
132
132
  ```
133
133
 
134
+ In some countries, the SWIFT-defined details differ from the local details that
135
+ customers are familiar with. For this reason, there are also `bank_code`,
136
+ `branch_code` and `account_number` methods on an `IBAN` object. At present,
137
+ these only differ from the `swift_` equivalents for Swedish bank accounts.
138
+
139
+ ```ruby
140
+ iban = Ibandit::IBAN.new(
141
+ country_code: 'SE',
142
+ account_number: '7507-1211203'
143
+ )
144
+ iban.swift_account_number # => "75071211203"
145
+ iban.account_number # => "1211203"
146
+
147
+ iban.swift_branch_code # => nil
148
+ iban.branch_code # => "7507"
149
+ ```
150
+
134
151
  ### Initializing Ibandit
135
152
 
136
153
  The UK and Ireland both use part of the BIC as the `bank_code` in their IBANs.
@@ -420,6 +437,31 @@ iban = Ibandit::IBAN.new(
420
437
  iban.iban # => "GB60BARC20000055779911"
421
438
  ```
422
439
 
440
+ ### Pseudo-IBANs
441
+
442
+ In some countries, it is not possible to recover local banking details from an
443
+ IBAN. For this reason, Ibandit has a concept of a *pseudo-IBAN*. This is a
444
+ string with a similar structure to an IBAN that can be decomposed into local
445
+ banking details. Pseudo-IBANs can be recognized by the fact that they have `ZZ`
446
+ as the third and fourth characters (these would be check digits for a regular
447
+ IBAN).
448
+
449
+ ```
450
+ iban = Ibandit::IBAN.new(
451
+ country_code: 'SE',
452
+ branch_code: '7507',
453
+ account_number: '1211203'
454
+ )
455
+ iban.pseudo_iban # => "SEZZX7507XXX1211203"
456
+
457
+ iban = Ibandit::IBAN.new('SEZZX7507XXX1211203')
458
+ iban.country_code # => "SE"
459
+ iban.branch_code # => "7507"
460
+ iban.account_number # => "1211203"
461
+ ```
462
+
463
+ At present, pseudo-IBANs are only available for Swedish bank accounts.
464
+
423
465
  ## Other libraries
424
466
 
425
467
  Another gem, [iban-tools](https://github.com/alphasights/iban-tools), also
@@ -76,6 +76,10 @@ QA:
76
76
  SA:
77
77
  :bank_code_format: \d{2}
78
78
  :account_number_format: '[A-Z0-9]{18}'
79
+ SE:
80
+ :pseudo_iban_bank_code_length: 0
81
+ :pseudo_iban_branch_code_length: 5
82
+ :pseudo_iban_account_number_length: 10
79
83
  SI:
80
84
  :bank_code_format: \d{5}
81
85
  :account_number_format: \d{8}\d{2}
@@ -736,6 +736,9 @@ SE:
736
736
  :bban_format: \d{3}\d{16}\d{1}
737
737
  :bank_code_format: \d{3}
738
738
  :account_number_format: \d{16}\d{1}
739
+ :pseudo_iban_bank_code_length: 0
740
+ :pseudo_iban_branch_code_length: 5
741
+ :pseudo_iban_account_number_length: 10
739
742
  SI:
740
743
  :bank_code_position: 5
741
744
  :bank_code_length: 5
@@ -1,10 +1,10 @@
1
1
  require File.expand_path('../lib/ibandit/version', __FILE__)
2
2
 
3
3
  Gem::Specification.new do |gem|
4
- gem.add_development_dependency 'rspec', '~> 3.1'
5
- gem.add_development_dependency 'rspec-its', '~> 1.1'
6
- gem.add_development_dependency 'rubocop', '~> 0.30.1'
7
- gem.add_development_dependency 'sax-machine', '~> 1.1'
4
+ gem.add_development_dependency 'rspec', '~> 3.3'
5
+ gem.add_development_dependency 'rspec-its', '~> 1.2'
6
+ gem.add_development_dependency 'rubocop', '~> 0.33.0'
7
+ gem.add_development_dependency 'sax-machine', '~> 1.3'
8
8
  gem.add_development_dependency 'nokogiri', '~> 1.6'
9
9
 
10
10
  gem.add_runtime_dependency 'i18n', '~> 0.7.0'
@@ -1,11 +1,14 @@
1
1
  require 'i18n'
2
2
  require 'ibandit/version'
3
3
  require 'ibandit/errors'
4
+ require 'ibandit/constants'
4
5
  require 'ibandit/iban'
5
6
  require 'ibandit/german_details_converter'
6
7
  require 'ibandit/swedish_details_converter'
7
8
  require 'ibandit/iban_splitter'
8
9
  require 'ibandit/iban_assembler'
10
+ require 'ibandit/pseudo_iban_assembler'
11
+ require 'ibandit/pseudo_iban_splitter'
9
12
  require 'ibandit/local_details_cleaner'
10
13
  require 'ibandit/check_digit'
11
14
 
@@ -0,0 +1,10 @@
1
+ module Ibandit
2
+ module Constants
3
+ SUPPORTED_COUNTRY_CODES = %w(AT BE BG CY CZ DE DK EE ES FI FR GB GR HR HU IE
4
+ IS IT LT LU LV MC MT NL NO PL PT RO SE SI SK
5
+ SM).freeze
6
+
7
+ PSEUDO_IBAN_COUNTRY_CODES = %w(SE).freeze
8
+ PSEUDO_IBAN_CHECK_DIGITS = 'ZZ'.freeze
9
+ end
10
+ end
@@ -2,13 +2,21 @@ require 'yaml'
2
2
 
3
3
  module Ibandit
4
4
  class IBAN
5
- attr_reader :errors, :iban, :country_code, :check_digits, :bank_code,
6
- :branch_code, :account_number
5
+ attr_reader :errors, :iban, :country_code, :check_digits, :bank_code,
6
+ :branch_code, :account_number, :swift_bank_code,
7
+ :swift_branch_code, :swift_account_number
7
8
 
8
9
  def initialize(argument)
9
10
  if argument.is_a?(String)
10
- @iban = argument.to_s.gsub(/\s+/, '').upcase
11
- extract_local_details_from_iban!
11
+ input = argument.to_s.gsub(/\s+/, '').upcase
12
+
13
+ if pseudo_iban?(input)
14
+ local_details = PseudoIBANSplitter.new(input).split
15
+ build_iban_from_local_details(local_details)
16
+ else
17
+ @iban = input
18
+ extract_swift_details_from_iban!
19
+ end
12
20
  elsif argument.is_a?(Hash)
13
21
  build_iban_from_local_details(argument)
14
22
  else
@@ -33,8 +41,8 @@ module Ibandit
33
41
  def iban_national_id
34
42
  return unless decomposable?
35
43
 
36
- national_id = bank_code.to_s
37
- national_id += branch_code.to_s
44
+ national_id = swift_bank_code.to_s
45
+ national_id += swift_branch_code.to_s
38
46
  national_id.slice(0, structure[:iban_national_id_length])
39
47
  end
40
48
 
@@ -51,6 +59,15 @@ module Ibandit
51
59
  iban[4..-1] unless iban.nil?
52
60
  end
53
61
 
62
+ def pseudo_iban
63
+ @pseudo_iban ||= PseudoIBANAssembler.new(
64
+ country_code: country_code,
65
+ bank_code: bank_code,
66
+ branch_code: branch_code,
67
+ account_number: account_number
68
+ ).assemble
69
+ end
70
+
54
71
  ###############
55
72
  # Validations #
56
73
  ###############
@@ -115,12 +132,12 @@ module Ibandit
115
132
  def valid_bank_code_length?
116
133
  return unless valid_country_code?
117
134
 
118
- if bank_code.nil? || bank_code.length == 0
135
+ if swift_bank_code.nil? || swift_bank_code.length == 0
119
136
  @errors[:bank_code] = Ibandit.translate(:is_required)
120
137
  return false
121
138
  end
122
139
 
123
- return true if bank_code.length == structure[:bank_code_length]
140
+ return true if swift_bank_code.length == structure[:bank_code_length]
124
141
 
125
142
  @errors[:bank_code] =
126
143
  Ibandit.translate(:wrong_length, expected: structure[:bank_code_length])
@@ -129,12 +146,14 @@ module Ibandit
129
146
 
130
147
  def valid_branch_code_length?
131
148
  return unless valid_country_code?
132
- return true if branch_code.to_s.length == structure[:branch_code_length]
149
+ if swift_branch_code.to_s.length == structure[:branch_code_length]
150
+ return true
151
+ end
133
152
 
134
153
  if structure[:branch_code_length] == 0
135
154
  @errors[:branch_code] = Ibandit.translate(:not_used_in_country,
136
155
  country_code: country_code)
137
- elsif branch_code.nil? || branch_code.length == 0
156
+ elsif swift_branch_code.nil? || swift_branch_code.length == 0
138
157
  @errors[:branch_code] = Ibandit.translate(:is_required)
139
158
  else
140
159
  @errors[:branch_code] =
@@ -147,12 +166,14 @@ module Ibandit
147
166
  def valid_account_number_length?
148
167
  return unless valid_country_code?
149
168
 
150
- if account_number.nil?
169
+ if swift_account_number.nil?
151
170
  @errors[:account_number] = Ibandit.translate(:is_required)
152
171
  return false
153
172
  end
154
173
 
155
- return true if account_number.length == structure[:account_number_length]
174
+ if swift_account_number.length == structure[:account_number_length]
175
+ return true
176
+ end
156
177
 
157
178
  @errors[:account_number] =
158
179
  Ibandit.translate(:wrong_length,
@@ -187,7 +208,7 @@ module Ibandit
187
208
  def valid_bank_code_format?
188
209
  return unless valid_bank_code_length?
189
210
 
190
- if bank_code =~ Regexp.new(structure[:bank_code_format])
211
+ if swift_bank_code =~ Regexp.new(structure[:bank_code_format])
191
212
  true
192
213
  else
193
214
  @errors[:bank_code] = Ibandit.translate(:is_invalid)
@@ -199,7 +220,7 @@ module Ibandit
199
220
  return unless valid_branch_code_length?
200
221
  return true unless structure[:branch_code_format]
201
222
 
202
- if branch_code =~ Regexp.new(structure[:branch_code_format])
223
+ if swift_branch_code =~ Regexp.new(structure[:branch_code_format])
203
224
  true
204
225
  else
205
226
  @errors[:branch_code] = Ibandit.translate(:is_invalid)
@@ -210,7 +231,7 @@ module Ibandit
210
231
  def valid_account_number_format?
211
232
  return unless valid_account_number_length?
212
233
 
213
- if account_number =~ Regexp.new(structure[:account_number_format])
234
+ if swift_account_number =~ Regexp.new(structure[:account_number_format])
214
235
  true
215
236
  else
216
237
  @errors[:account_number] = Ibandit.translate(:is_invalid)
@@ -242,8 +263,8 @@ module Ibandit
242
263
  begin
243
264
  GermanDetailsConverter.convert(
244
265
  country_code: country_code,
245
- bank_code: bank_code,
246
- account_number: account_number
266
+ bank_code: swift_bank_code,
267
+ account_number: swift_account_number
247
268
  )
248
269
  true
249
270
  rescue UnsupportedAccountDetails
@@ -255,7 +276,10 @@ module Ibandit
255
276
  def valid_swedish_details?
256
277
  return true unless country_code == 'SE'
257
278
 
258
- bank_details = { bank_code: bank_code, account_number: account_number }
279
+ bank_details = {
280
+ bank_code: swift_bank_code,
281
+ account_number: swift_account_number
282
+ }
259
283
 
260
284
  unless SwedishDetailsConverter.valid_bank_code?(bank_details)
261
285
  bank_code_field = bank_code.nil? ? :account_number : :bank_code
@@ -279,28 +303,41 @@ module Ibandit
279
303
  private
280
304
 
281
305
  def decomposable?
282
- [iban, country_code, bank_code, account_number].none?(&:nil?)
306
+ [iban, country_code, swift_bank_code, swift_account_number].none?(&:nil?)
283
307
  end
284
308
 
285
309
  def build_iban_from_local_details(details_hash)
286
310
  local_details = LocalDetailsCleaner.clean(details_hash)
287
311
 
288
- @country_code = try_dup(local_details[:country_code])
289
- @account_number = try_dup(local_details[:account_number])
290
- @branch_code = try_dup(local_details[:branch_code])
291
- @bank_code = try_dup(local_details[:bank_code])
292
- @iban = IBANAssembler.assemble(local_details)
293
- @check_digits = @iban.slice(2, 2) unless @iban.nil?
312
+ @country_code = try_dup(local_details[:country_code])
313
+ @account_number = try_dup(local_details[:account_number])
314
+ @branch_code = try_dup(local_details[:branch_code])
315
+ @bank_code = try_dup(local_details[:bank_code])
316
+
317
+ @swift_account_number = try_dup(local_details[:swift_account_number])
318
+ @swift_branch_code = try_dup(local_details[:swift_branch_code])
319
+ @swift_bank_code = try_dup(local_details[:swift_bank_code])
320
+
321
+ @iban = IBANAssembler.assemble(swift_details)
322
+ @check_digits = @iban.slice(2, 2) unless @iban.nil?
294
323
  end
295
324
 
296
- def extract_local_details_from_iban!
297
- local_details = IBANSplitter.split(@iban)
325
+ def extract_swift_details_from_iban!
326
+ swift_details = IBANSplitter.split(@iban)
327
+
328
+ @country_code = swift_details[:country_code]
329
+ @check_digits = swift_details[:check_digits]
298
330
 
299
- @country_code = local_details[:country_code]
300
- @check_digits = local_details[:check_digits]
301
- @bank_code = local_details[:bank_code]
302
- @branch_code = local_details[:branch_code]
303
- @account_number = local_details[:account_number]
331
+ @swift_bank_code = swift_details[:bank_code]
332
+ @swift_branch_code = swift_details[:branch_code]
333
+ @swift_account_number = swift_details[:account_number]
334
+
335
+ return if Constants::PSEUDO_IBAN_COUNTRY_CODES.
336
+ include?(@country_code)
337
+
338
+ @bank_code = swift_details[:bank_code]
339
+ @branch_code = swift_details[:branch_code]
340
+ @account_number = swift_details[:account_number]
304
341
  end
305
342
 
306
343
  def try_dup(object)
@@ -339,5 +376,18 @@ module Ibandit
339
376
  :bank_code
340
377
  end
341
378
  end
379
+
380
+ def swift_details
381
+ {
382
+ country_code: @country_code,
383
+ account_number: @swift_account_number,
384
+ branch_code: @swift_branch_code,
385
+ bank_code: @swift_bank_code
386
+ }
387
+ end
388
+
389
+ def pseudo_iban?(input)
390
+ input.slice(2, 2) == Constants::PSEUDO_IBAN_CHECK_DIGITS
391
+ end
342
392
  end
343
393
  end
@@ -1,9 +1,5 @@
1
1
  module Ibandit
2
2
  module IBANAssembler
3
- SUPPORTED_COUNTRY_CODES = %w(AT BE BG CY CZ DE DK EE ES FI FR GB GR HR HU IE
4
- IS IT LT LU LV MC MT NL NO PL PT RO SE SI SK
5
- SM).freeze
6
-
7
3
  EXCEPTION_COUNTRY_CODES = %w(IT SM BE).freeze
8
4
 
9
5
  def self.assemble(local_details)
@@ -67,8 +63,11 @@ module Ibandit
67
63
  ##################
68
64
 
69
65
  def self.can_assemble?(local_details)
70
- SUPPORTED_COUNTRY_CODES.include?(local_details[:country_code]) &&
71
- valid_arguments?(local_details)
66
+ supported_country_code?(local_details) && valid_arguments?(local_details)
67
+ end
68
+
69
+ def self.supported_country_code?(local_details)
70
+ Constants::SUPPORTED_COUNTRY_CODES.include?(local_details[:country_code])
72
71
  end
73
72
 
74
73
  def self.valid_arguments?(local_details)
@@ -1,12 +1,12 @@
1
1
  module Ibandit
2
2
  module LocalDetailsCleaner
3
- SUPPORTED_COUNTRY_CODES = %w(AT BE BG CY CZ DE DK EE ES FI FR GB GR HR HU IE
4
- IS IT LT LU LV MC MT NL NO PL PT RO SE SI SK
5
- SM).freeze
6
-
7
3
  def self.clean(local_details)
8
4
  country_code = local_details[:country_code]
9
5
 
6
+ unless explicit_swift_details?(country_code)
7
+ local_details = swift_details_for(local_details).merge(local_details)
8
+ end
9
+
10
10
  return local_details unless can_clean?(country_code, local_details)
11
11
 
12
12
  local_details.merge(
@@ -18,10 +18,14 @@ module Ibandit
18
18
  ###########
19
19
 
20
20
  def self.can_clean?(country_code, local_details)
21
- SUPPORTED_COUNTRY_CODES.include?(country_code) &&
21
+ Constants::SUPPORTED_COUNTRY_CODES.include?(country_code) &&
22
22
  fields_for?(country_code, local_details)
23
23
  end
24
24
 
25
+ def self.explicit_swift_details?(country_code)
26
+ Constants::PSEUDO_IBAN_COUNTRY_CODES.include?(country_code)
27
+ end
28
+
25
29
  def self.fields_for?(country_code, opts)
26
30
  required_fields(country_code).all? { |argument| opts[argument] }
27
31
  end
@@ -149,7 +153,7 @@ module Ibandit
149
153
 
150
154
  {
151
155
  bank_code: bank_code.rjust(4, '0'),
152
- account_number: account_number.gsub('-', '').rjust(10, '0')
156
+ account_number: account_number.delete('-').rjust(10, '0')
153
157
  }
154
158
  end
155
159
 
@@ -437,14 +441,19 @@ module Ibandit
437
441
  end
438
442
 
439
443
  def self.clean_se_details(local_details)
440
- converted_details =
441
- SwedishDetailsConverter.convert(local_details[:account_number])
444
+ converted_details = SwedishDetailsConverter.new(
445
+ branch_code: local_details[:branch_code],
446
+ account_number: local_details[:account_number]
447
+ ).convert
442
448
 
443
- bank_code = local_details[:bank_code] || converted_details[:bank_code]
449
+ bank_code = local_details[:bank_code] ||
450
+ converted_details[:swift_bank_code]
444
451
 
445
452
  {
446
- bank_code: bank_code,
447
- account_number: converted_details[:account_number]
453
+ account_number: converted_details[:account_number],
454
+ branch_code: converted_details[:branch_code],
455
+ swift_bank_code: bank_code,
456
+ swift_account_number: converted_details[:swift_account_number]
448
457
  }
449
458
  end
450
459
 
@@ -477,5 +486,14 @@ module Ibandit
477
486
  hufo + reikningsnumer + kennitala
478
487
  end
479
488
  private_class_method :pad_is_account_number
489
+
490
+ def self.swift_details_for(local_details)
491
+ {
492
+ swift_bank_code: local_details[:bank_code],
493
+ swift_branch_code: local_details[:branch_code],
494
+ swift_account_number: local_details[:account_number]
495
+ }
496
+ end
497
+ private_class_method :swift_details_for
480
498
  end
481
499
  end