valvat 0.9.1 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.github/workflows/ruby.yml +35 -0
  5. data/.gitignore +3 -1
  6. data/.rubocop.yml +15 -0
  7. data/CHANGES.md +36 -1
  8. data/Gemfile +4 -3
  9. data/Guardfile +6 -4
  10. data/README.md +99 -47
  11. data/Rakefile +3 -1
  12. data/certs/yolk.pem +21 -21
  13. data/gemfiles/activemodel-5 +4 -5
  14. data/gemfiles/activemodel-6 +7 -0
  15. data/gemfiles/standalone +3 -5
  16. data/lib/active_model/validations/valvat_validator.rb +70 -22
  17. data/lib/valvat.rb +7 -3
  18. data/lib/valvat/checksum.rb +24 -28
  19. data/lib/valvat/checksum/at.rb +3 -1
  20. data/lib/valvat/checksum/be.rb +3 -1
  21. data/lib/valvat/checksum/bg.rb +17 -17
  22. data/lib/valvat/checksum/cy.rb +2 -0
  23. data/lib/valvat/checksum/de.rb +5 -3
  24. data/lib/valvat/checksum/dk.rb +3 -1
  25. data/lib/valvat/checksum/ee.rb +2 -0
  26. data/lib/valvat/checksum/es.rb +14 -16
  27. data/lib/valvat/checksum/fi.rb +3 -1
  28. data/lib/valvat/checksum/fr.rb +4 -2
  29. data/lib/valvat/checksum/gb.rb +53 -21
  30. data/lib/valvat/checksum/gr.rb +4 -2
  31. data/lib/valvat/checksum/hr.rb +2 -0
  32. data/lib/valvat/checksum/hu.rb +2 -0
  33. data/lib/valvat/checksum/ie.rb +5 -6
  34. data/lib/valvat/checksum/it.rb +7 -5
  35. data/lib/valvat/checksum/lt.rb +2 -0
  36. data/lib/valvat/checksum/lu.rb +3 -1
  37. data/lib/valvat/checksum/mt.rb +2 -0
  38. data/lib/valvat/checksum/nl.rb +5 -3
  39. data/lib/valvat/checksum/pl.rb +3 -1
  40. data/lib/valvat/checksum/pt.rb +3 -1
  41. data/lib/valvat/checksum/ro.rb +2 -0
  42. data/lib/valvat/checksum/se.rb +5 -3
  43. data/lib/valvat/checksum/si.rb +4 -2
  44. data/lib/valvat/error.rb +31 -0
  45. data/lib/valvat/local.rb +8 -5
  46. data/lib/valvat/locales/de.yml +2 -1
  47. data/lib/valvat/locales/en.yml +2 -1
  48. data/lib/valvat/locales/es.yml +1 -0
  49. data/lib/valvat/locales/fr.yml +1 -0
  50. data/lib/valvat/locales/hu.yml +1 -0
  51. data/lib/valvat/locales/nl.yml +1 -0
  52. data/lib/valvat/locales/pl.yml +1 -0
  53. data/lib/valvat/locales/pt.yml +28 -28
  54. data/lib/valvat/lookup.rb +14 -45
  55. data/lib/valvat/lookup/fault.rb +31 -0
  56. data/lib/valvat/lookup/request.rb +37 -11
  57. data/lib/valvat/lookup/response.rb +37 -0
  58. data/lib/valvat/syntax.rb +31 -30
  59. data/lib/valvat/utils.rb +20 -9
  60. data/lib/valvat/version.rb +3 -1
  61. data/spec/active_model/validations/valvat_validator_spec.rb +132 -100
  62. data/spec/spec_helper.rb +8 -8
  63. data/spec/valvat/checksum/at_spec.rb +8 -6
  64. data/spec/valvat/checksum/be_spec.rb +8 -6
  65. data/spec/valvat/checksum/bg_spec.rb +9 -6
  66. data/spec/valvat/checksum/cy_spec.rb +5 -3
  67. data/spec/valvat/checksum/de_spec.rb +8 -6
  68. data/spec/valvat/checksum/dk_spec.rb +8 -6
  69. data/spec/valvat/checksum/ee_spec.rb +5 -3
  70. data/spec/valvat/checksum/es_spec.rb +9 -6
  71. data/spec/valvat/checksum/fi_spec.rb +8 -6
  72. data/spec/valvat/checksum/fr_spec.rb +8 -6
  73. data/spec/valvat/checksum/gb_spec.rb +9 -5
  74. data/spec/valvat/checksum/gr_spec.rb +8 -6
  75. data/spec/valvat/checksum/hr_spec.rb +5 -3
  76. data/spec/valvat/checksum/hu_spec.rb +5 -3
  77. data/spec/valvat/checksum/ie_spec.rb +13 -5
  78. data/spec/valvat/checksum/it_spec.rb +12 -10
  79. data/spec/valvat/checksum/lt_spec.rb +5 -3
  80. data/spec/valvat/checksum/lu_spec.rb +8 -6
  81. data/spec/valvat/checksum/mt_spec.rb +5 -3
  82. data/spec/valvat/checksum/nl_spec.rb +9 -7
  83. data/spec/valvat/checksum/pl_spec.rb +10 -8
  84. data/spec/valvat/checksum/pt_spec.rb +12 -13
  85. data/spec/valvat/checksum/ro_spec.rb +5 -3
  86. data/spec/valvat/checksum/se_spec.rb +10 -8
  87. data/spec/valvat/checksum/si_spec.rb +12 -10
  88. data/spec/valvat/checksum_spec.rb +25 -10
  89. data/spec/valvat/lookup/fault_spec.rb +34 -0
  90. data/spec/valvat/lookup/request_spec.rb +19 -0
  91. data/spec/valvat/lookup/response_spec.rb +29 -0
  92. data/spec/valvat/lookup_spec.rb +202 -84
  93. data/spec/valvat/syntax_spec.rb +59 -280
  94. data/spec/valvat/utils_spec.rb +76 -43
  95. data/spec/valvat_spec.rb +140 -152
  96. data/valvat.gemspec +18 -17
  97. metadata +61 -41
  98. metadata.gz.sig +0 -0
  99. data/.travis.yml +0 -33
  100. data/gemfiles/activemodel-3-2 +0 -8
  101. data/gemfiles/activemodel-4 +0 -8
  102. data/gemfiles/before-ruby21/activemodel-3-2 +0 -9
  103. data/gemfiles/before-ruby21/activemodel-4 +0 -9
  104. data/gemfiles/before-ruby21/standalone +0 -8
  105. data/lib/valvat/lookup/request_with_id.rb +0 -31
@@ -1,8 +1,7 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
 
3
- gem "savon", ">=2.3.0"
4
- gem "activemodel", "~> 5.0.2"
3
+ gem "activemodel", "~> 5.0"
5
4
 
6
- gem 'rspec', '~> 3.0'
7
5
  gem 'rack'
8
- gem 'rake'
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "activemodel", "~> 6.0"
4
+
5
+ gem 'rack'
6
+
7
+ gemspec path: "../"
data/gemfiles/standalone CHANGED
@@ -1,7 +1,5 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
 
3
- gem "savon", ">=2.3.0"
4
-
5
- gem 'rspec', '~> 3.0'
6
3
  gem 'rack', '< 2'
7
- gem 'rake'
4
+
5
+ gemspec path: "../"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_model'
2
4
  require 'valvat/syntax'
3
5
  require 'valvat/lookup'
@@ -5,47 +7,93 @@ require 'valvat/lookup'
5
7
  module ActiveModel
6
8
  module Validations
7
9
  class ValvatValidator < EachValidator
10
+ def initialize(options)
11
+ normalize_options(options)
12
+ super
13
+ end
14
+
8
15
  def validate_each(record, attribute, value)
9
16
  vat = Valvat(value)
10
17
  iso_country_code = vat.iso_country_code
11
18
 
12
- if country_does_not_match?(record, iso_country_code)
13
- iso_country_code = iso_country_code_of(record)
14
- elsif vat.valid? && valid_checksum?(vat) && vat_exists?(vat)
15
- return
16
- end
19
+ is_valid = if country_does_not_match?(record, iso_country_code)
20
+ iso_country_code = iso_country_code_of(record)
21
+ false
22
+ else
23
+ vat_valid?(vat)
24
+ end
17
25
 
18
- iso_country_code = "eu" if iso_country_code.blank?
19
- record.errors.add(attribute, :invalid_vat,
20
- :message => options[:message],
21
- :country_adjective => I18n.t(
22
- :"valvat.country_adjectives.#{iso_country_code.downcase}",
23
- :default => [:"valvat.country_adjectives.eu", "european"]
24
- )
25
- )
26
+ iso_country_code = 'eu' if iso_country_code.blank?
27
+
28
+ add_error(is_valid, record, attribute, iso_country_code)
26
29
  end
27
30
 
28
31
  private
29
32
 
33
+ def vat_valid?(vat)
34
+ vat.valid? && valid_checksum?(vat) && vat_exists?(vat)
35
+ end
36
+
37
+ def valid_checksum?(vat)
38
+ return true unless options[:checksum]
39
+
40
+ vat.valid_checksum?
41
+ end
42
+
43
+ def vat_exists?(vat)
44
+ return true unless options[:lookup]
45
+
46
+ exists = vat.exists?(options[:lookup])
47
+
48
+ return true if exists.nil? && !options[:lookup][:fail_if_down]
49
+
50
+ exists
51
+ end
52
+
30
53
  def country_does_not_match?(record, iso_country_code)
31
54
  return false unless options[:match_country]
55
+
32
56
  iso_country_code_of(record) != iso_country_code
33
57
  end
34
58
 
35
59
  def iso_country_code_of(record)
36
- (record.send(options[:match_country]) || "").upcase
60
+ (record.send(options[:match_country]) || '').upcase
37
61
  end
38
62
 
39
- def valid_checksum?(vat)
40
- return true unless options[:checksum]
41
- vat.valid_checksum?
63
+ def add_error(is_valid, record, attribute, iso_country_code)
64
+ case is_valid
65
+ when false
66
+ add_invalid_vat_error(record, attribute, iso_country_code)
67
+ when nil
68
+ add_vies_down_error(record, attribute)
69
+ end
42
70
  end
43
71
 
44
- def vat_exists?(vat)
45
- return true unless options[:lookup]
46
- is_valid = vat.exists?
47
- return is_valid unless is_valid.nil?
48
- options[:lookup] != :fail_if_down
72
+ def add_invalid_vat_error(record, attribute, iso_country_code)
73
+ record.errors.add(attribute, :invalid_vat,
74
+ message: options[:message],
75
+ country_adjective: I18n.t(
76
+ :"valvat.country_adjectives.#{iso_country_code.downcase}",
77
+ default: [:"valvat.country_adjectives.eu", 'european']
78
+ ))
79
+ end
80
+
81
+ def add_vies_down_error(record, attribute)
82
+ record.errors.add(attribute, :vies_down,
83
+ message: options[:vies_down_message])
84
+ end
85
+
86
+ def normalize_options(options)
87
+ return unless options[:lookup]
88
+
89
+ options[:lookup] = case options[:lookup]
90
+ when :fail_if_down
91
+ { fail_if_down: true }
92
+ when Hash
93
+ options[:lookup]
94
+ else
95
+ {}
96
+ end
49
97
  end
50
98
  end
51
99
  end
data/lib/valvat.rb CHANGED
@@ -1,12 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'valvat/error'
1
4
  require 'valvat/local'
2
5
  require 'valvat/lookup'
3
6
  require 'valvat/lookup/request'
4
- require 'valvat/lookup/request_with_id'
7
+ require 'valvat/lookup/response'
8
+ require 'valvat/lookup/fault'
5
9
  require 'active_model/validations/valvat_validator' if defined?(ActiveModel)
6
10
 
7
11
  class Valvat
8
- def exists?(options={})
12
+ def exists?(options = {})
9
13
  Valvat::Lookup.validate(self, options)
10
14
  end
11
- alias_method :exist?, :exists?
15
+ alias exist? exists?
12
16
  end
@@ -1,21 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
- ALGORITHMS = {}
4
-
5
- def self.validate(vat)
6
- vat = Valvat(vat)
7
- algo = ALGORITHMS[vat.iso_country_code]
8
- Valvat::Syntax.validate(vat) && !!(algo.nil? || algo.new(vat).validate)
9
- end
10
-
11
5
  class Base
12
- def self.inherited(klass)
13
- ALGORITHMS[klass.name.split(/::/).last] = klass
14
- end
15
-
16
6
  attr_reader :vat
17
7
 
18
- def self.check_digit_length(len=nil)
8
+ def self.check_digit_length(len = nil)
19
9
  @check_digit_length = len if len
20
10
  @check_digit_length || 1
21
11
  end
@@ -39,44 +29,50 @@ class Valvat
39
29
  end
40
30
 
41
31
  def figures_str
42
- str_wo_country[0..-(self.class.check_digit_length+1)]
32
+ str_wo_country[0..-(self.class.check_digit_length + 1)]
43
33
  end
44
34
 
45
35
  def figures
46
- figures_str.split("").map(&:to_i)
36
+ figures_str.split('').map(&:to_i)
47
37
  end
48
38
 
49
39
  def str_wo_country
50
40
  vat.to_s_wo_country
51
41
  end
52
42
 
53
- def sum_figures_by
54
- figures.reverse.each_with_index.map do |fig, i|
55
- yield(fig, i)
56
- end.inject(:+)
43
+ def sum_figures_by(&block)
44
+ figures.reverse.each_with_index.map(&block).inject(:+)
57
45
  end
58
46
 
59
47
  def sum_of_squares(num)
60
- num.to_s.split("").map(&:to_i).inject(:+)
48
+ num.to_s.split('').map(&:to_i).inject(:+)
61
49
  end
62
50
 
63
- def sum_of_figures_for_at_es_it_se(reverse_ints=false)
51
+ def sum_of_figures_for_at_es_it_se(reverse_ints: false)
64
52
  ints = reverse_ints ? [2, 1] : [1, 2]
65
53
  sum_figures_by do |fig, i|
66
- sum_of_squares(fig*(i.modulo(2) == 0 ? ints[0] : ints[1]))
54
+ sum_of_squares(fig * (i.modulo(2).zero? ? ints[0] : ints[1]))
67
55
  end
68
56
  end
69
57
 
70
58
  def sum_of_figues_for_pt_si
71
59
  11 - sum_figures_by do |fig, i|
72
- fig*(i+2)
60
+ fig * (i + 2)
73
61
  end.modulo(11)
74
62
  end
75
63
  end
64
+
65
+ def self.validate(vat)
66
+ vat = Valvat(vat)
67
+ algo = ALGORITHMS[vat.iso_country_code]
68
+ Valvat::Syntax.validate(vat) && !!(algo.nil? || algo.new(vat).validate)
69
+ end
70
+
71
+ ALGORITHMS = Dir[File.join(__dir__, 'checksum', '*.rb')].each_with_object({}) do |path, algos|
72
+ require path.gsub(/\.rb$/, '')
73
+
74
+ classname = File.basename(path, '.rb').upcase
75
+ algos[classname] = ['Valvat', 'Checksum', classname].inject(Object, :const_get)
76
+ end.freeze
76
77
  end
77
78
  end
78
-
79
- Dir[File.join(File.dirname(__FILE__), 'checksum', '*.rb')].each do |path|
80
- # On Ruby 2.1.0 ActiveSupport goes mad if you pass it paths with .rb at the end
81
- require path.gsub(/\.rb$/, '')
82
- end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class AT < Base
@@ -11,4 +13,4 @@ class Valvat
11
13
  end
12
14
  end
13
15
  end
14
- end
16
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class BE < Base
@@ -8,4 +10,4 @@ class Valvat
8
10
  end
9
11
  end
10
12
  end
11
- end
13
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class BG < Base
@@ -9,6 +11,7 @@ class Valvat
9
11
  local_person_chk = check_digit_local_natural_person
10
12
 
11
13
  return local_person_chk if given_check_digit == local_person_chk
14
+
12
15
  check_digit_foreign_natural_person
13
16
  end
14
17
 
@@ -19,43 +22,40 @@ class Valvat
19
22
  end.inject(:+).modulo(11)
20
23
 
21
24
  return chk if chk < 10
22
- return 0
25
+
26
+ 0
23
27
  end
24
28
 
25
29
  def check_digit_foreign_natural_person
26
30
  weight = [21, 19, 17, 13, 11, 9, 7, 3, 1]
27
31
 
28
- chk = figures.map do |fig|
32
+ figures.map do |fig|
29
33
  fig * weight.shift
30
34
  end.inject(:+).modulo(10)
31
-
32
- chk
33
35
  end
34
36
 
35
37
  def check_digit_legal_person
36
- prod = 0
37
- figures.each_with_index do |fig, index|
38
- prod += (index + 1) * fig.to_i
39
- end
40
-
41
- chk = prod % 11
38
+ chk = build_prod % 11
42
39
 
43
40
  if chk == 10
44
- prod = 0
45
- figures.each_with_index do |fig, index|
46
- prod += (index + 3) * fig.to_i
47
- end
48
-
49
- chk = prod % 11
41
+ chk = build_prod(3) % 11
50
42
  chk = 0 if chk == 10
51
43
  end
52
44
 
53
45
  chk
54
46
  end
55
47
 
48
+ def build_prod(add = 1)
49
+ prod = 0
50
+ figures.each_with_index do |fig, index|
51
+ prod += (index + add) * fig.to_i
52
+ end
53
+ prod
54
+ end
55
+
56
56
  def natural_person?
57
57
  vat.to_s_wo_country.length == 10
58
58
  end
59
59
  end
60
60
  end
61
- end
61
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class CY < Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class DE < Base
@@ -8,12 +10,12 @@ class Valvat
8
10
  prod = M
9
11
  figures.each do |fig|
10
12
  sum = (prod + fig).modulo(M)
11
- sum = M if sum == 0
12
- prod = (2*sum).modulo(N)
13
+ sum = M if sum.zero?
14
+ prod = (2 * sum).modulo(N)
13
15
  end
14
16
  chk = N - prod
15
17
  chk == 10 ? 0 : chk
16
18
  end
17
19
  end
18
20
  end
19
- end
21
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class DK < Base
@@ -15,4 +17,4 @@ class Valvat
15
17
  end
16
18
  end
17
19
  end
18
- end
20
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class EE < Base
@@ -1,33 +1,31 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class ES < Base
4
- NATURAL_PERSON_CHARS = %w(T R W A G M Y F P D X B N J Z S Q V H L C K E)
5
- NATURAL_PERSON_EXP = /\A([\d]{8}[ABCDEFGHJKLMNPQRSTVWXYZ]|[KLMXYZ][\d]{7}[ABCDEFGHJKLMNPQRSTVWXYZ])\Z/
6
- LEGAL_PERSON_CHARS = [false] + %w(A B C D E F G H I J)
7
- LEGAL_PERSON_EXP = /\A[NPQRSW][\d]{7}[ABCDEFGHIJ]\Z/
6
+ NATURAL_PERSON_CHARS = %w[T R W A G M Y F P D X B N J Z S Q V H L C K E].freeze
7
+ NATURAL_PERSON_EXP = /\A(\d{8}[ABCDEFGHJKLMNPQRSTVWXYZ]|[KLMXYZ]\d{7}[ABCDEFGHJKLMNPQRSTVWXYZ])\Z/.freeze
8
+ LEGAL_PERSON_CHARS = [false] + %w[A B C D E F G H I J]
9
+ LEGAL_PERSON_EXP = /\A[NPQRSW]\d{7}[ABCDEFGHIJ]\Z/.freeze
10
+ NIE_DIGIT_BY_LETTER = %w[X Y Z].freeze
8
11
 
9
12
  def check_digit
10
13
  natural_person? ? check_digit_natural_person : check_digit_legal_person
11
14
  end
12
15
 
13
16
  def check_digit_natural_person
14
- nie_digit = nil
15
17
  letter = vat.to_s_wo_country[0]
16
- if letter == 'X'
17
- nie_digit = 0
18
- elsif letter == 'Y'
19
- nie_digit = 1
20
- elsif letter == 'Z'
21
- nie_digit = 2
22
- end
18
+ nie_digit = NIE_DIGIT_BY_LETTER.index(letter)
23
19
  NATURAL_PERSON_CHARS["#{nie_digit}#{figures_str}".to_i.modulo(23)]
24
20
  end
25
21
 
26
22
  def check_digit_legal_person
27
- chk = 10 - sum_of_figures_for_at_es_it_se(true).modulo(10)
28
- legal_foreign_person? ?
29
- LEGAL_PERSON_CHARS[chk] :
23
+ chk = 10 - sum_of_figures_for_at_es_it_se(reverse_ints: true).modulo(10)
24
+ if legal_foreign_person?
25
+ LEGAL_PERSON_CHARS[chk]
26
+ else
30
27
  (chk == 10 ? 0 : chk)
28
+ end
31
29
  end
32
30
 
33
31
  def given_check_digit
@@ -52,4 +50,4 @@ class Valvat
52
50
  end
53
51
  end
54
52
  end
55
- end
53
+ end