valvat 0.8.2 → 1.1.0

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 +45 -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 +14 -13
  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 +6 -4
  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 +9 -2
  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 +18 -17
  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 +12 -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 -42
  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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class FI < Base
@@ -10,4 +12,4 @@ class Valvat
10
12
  end
11
13
  end
12
14
  end
13
- end
15
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class FR < Base
@@ -5,10 +7,10 @@ class Valvat
5
7
  siren = str_wo_country[2..-1].to_i
6
8
  (12 + (3 * siren) % 97) % 97
7
9
  end
8
-
10
+
9
11
  def given_check_digit
10
12
  str_wo_country[0..1].to_i
11
13
  end
12
14
  end
13
15
  end
14
- end
16
+ end
@@ -1,34 +1,66 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class GB < Base
4
- OLD_FORMAT_FORBIDDEN_RANGES = [(100_000..999_999), (9_490_001..9_700_000), (9_990_001..9_999_999)]
5
- NEW_FORMAT_FORBIDDEN_RANGES = [(1..100_000), (100_001..1_000_000)]
6
-
7
- def validate
8
- vat_number = vat.to_s_wo_country
9
- check_sum = vat.to_s_wo_country[7..8].to_i
10
- vat_base = vat_number[0..6]
6
+ OLD_FORMAT_FORBIDDEN_RANGES = [(100_000..999_999), (9_490_001..9_700_000), (9_990_001..9_999_999)].freeze
7
+ NEW_FORMAT_FORBIDDEN_RANGES = [(1..100_000), (100_001..1_000_000)].freeze
8
+ GOV_NUMBER = /\A(GD[0-4]{1}\d{2})\Z/.freeze
9
+ HEALTH_NUMBER = /\A(HA[5-9]{1}\d{2})\Z/.freeze
11
10
 
11
+ def validate # rubocop:disable Metrics/CyclomaticComplexity
12
12
  # government departments and health authorities, so no checksum
13
- return true if vat_number =~ /\A(GD[0-4]{1}\d{2})\Z/ || vat_number =~ /\A(HA[5-9]{1}\d{2})\Z/
14
- return false if vat_number =~ /\A0{9}\Z/ || vat_number =~ /\A0{12}\Z/
13
+ return true if gov_or_health?
14
+ return false if all_zero?
15
+
16
+ return false if old_format_remainder.zero? && fobidden_in_old_format?
17
+ return false if new_format_remainder.zero? && fobidden_in_new_format?
18
+
19
+ old_format_remainder.zero? || new_format_remainder.zero?
20
+ end
21
+
22
+ private
23
+
24
+ def checksum
25
+ @checksum ||= str_wo_country[7..8].to_i
26
+ end
27
+
28
+ def vat_base
29
+ @vat_base ||= str_wo_country[0..6]
30
+ end
15
31
 
16
- vat_base_sum = vat_base.split('').
17
- map(&:to_i).
18
- zip([8, 7, 6, 5, 4, 3, 2]).
19
- map { |vat_number_digit, multiplier| vat_number_digit * multiplier }.
20
- inject(:+)
32
+ def gov_or_health?
33
+ str_wo_country =~ GOV_NUMBER || str_wo_country =~ HEALTH_NUMBER
34
+ end
21
35
 
22
- old_format_remainder = (vat_base_sum + check_sum).modulo(97)
23
- new_format_remainder = (vat_base_sum + 55 + check_sum).modulo(97)
36
+ def all_zero?
37
+ str_wo_country =~ /\A0{9}\Z/ || str_wo_country =~ /\A0{12}\Z/
38
+ end
24
39
 
25
- return false if old_format_remainder == 0 &&
26
- OLD_FORMAT_FORBIDDEN_RANGES.any? { |range| range.include? vat_base.to_i }
40
+ def vat_base_sum
41
+ @vat_base_sum ||= vat_base.split('')
42
+ .map(&:to_i)
43
+ .zip([8, 7, 6, 5, 4, 3, 2])
44
+ .map { |vat_number_digit, multiplier| vat_number_digit * multiplier }
45
+ .inject(:+)
46
+ end
27
47
 
28
- return false if new_format_remainder == 0 &&
29
- NEW_FORMAT_FORBIDDEN_RANGES.any? { |range| range.include? vat_base.to_i }
48
+ def old_format_remainder
49
+ @old_format_remainder ||= (vat_base_sum + checksum).modulo(97)
50
+ end
51
+
52
+ def new_format_remainder
53
+ @new_format_remainder ||= (vat_base_sum + 55 + checksum).modulo(97)
54
+ end
55
+
56
+ def fobidden_in_new_format?
57
+ vat_base_int = vat_base.to_i
58
+ NEW_FORMAT_FORBIDDEN_RANGES.any? { |range| range.include?(vat_base_int) }
59
+ end
30
60
 
31
- old_format_remainder == 0 || new_format_remainder == 0
61
+ def fobidden_in_old_format?
62
+ vat_base_int = vat_base.to_i
63
+ OLD_FORMAT_FORBIDDEN_RANGES.any? { |range| range.include?(vat_base_int) }
32
64
  end
33
65
  end
34
66
  end
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class GR < Base
4
6
  def check_digit
5
7
  chk = sum_figures_by do |fig, i|
6
- fig*(2**(i+1))
8
+ fig * (2**(i + 1))
7
9
  end.modulo(11)
8
10
  chk > 9 ? 0 : chk
9
11
  end
10
12
  end
11
13
  end
12
- end
14
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class HR < Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class HU < Base
@@ -1,17 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class IE < Base
4
6
  def check_digit
5
7
  total = sum_figures_by do |fig, i|
6
- fig*(i+2)
7
- end
8
- if str_wo_country.size == 9
9
- total += (CHARS.index(str_wo_country[8]) * 9)
8
+ fig * (i + 2)
10
9
  end
10
+ total += ((CHARS.index(str_wo_country[8]) || 0) * 9) if str_wo_country.size == 9
11
11
  total.modulo(23)
12
12
  end
13
13
 
14
- CHARS = "WABCDEFGHIJKLMNOPQRSTUV".split("")
14
+ CHARS = 'WABCDEFGHIJKLMNOPQRSTUV'.split('')
15
15
 
16
16
  def given_check_digit
17
17
  if str_wo_country.size == 9
@@ -38,7 +38,6 @@ class Valvat
38
38
  super
39
39
  end
40
40
  end
41
-
42
41
  end
43
42
  end
44
43
  end
@@ -1,17 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class IT < Base
4
6
  def validate
5
7
  y = figures_str[7..9].to_i
6
8
  y >= 1 && (y <= 100 || [120, 121].include?(y)) &&
7
- figures_str[0..6] != "0000000" &&
8
- super
9
+ figures_str[0..6] != '0000000' &&
10
+ super
9
11
  end
10
12
 
11
13
  def check_digit
12
- chk = 10 - sum_of_figures_for_at_es_it_se(true).modulo(10)
14
+ chk = 10 - sum_of_figures_for_at_es_it_se(reverse_ints: true).modulo(10)
13
15
  chk == 10 ? 0 : chk
14
16
  end
15
17
  end
16
18
  end
17
- end
19
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class LT < Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class LU < 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 MT < Base
@@ -1,9 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class NL < Base
6
+ def validate
7
+ vat.to_s.gsub(/[A-Z]/) { |let| (let.ord - 55).to_s }.to_i % 97 == 1 ||
8
+ super
9
+ end
10
+
4
11
  def check_digit
5
12
  sum_figures_by do |fig, i|
6
- fig*(i+2)
13
+ fig * (i + 2)
7
14
  end.modulo(11)
8
15
  end
9
16
 
@@ -12,4 +19,4 @@ class Valvat
12
19
  end
13
20
  end
14
21
  end
15
- end
22
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class PL < Base
@@ -9,4 +11,4 @@ class Valvat
9
11
  end
10
12
  end
11
13
  end
12
- end
14
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class PT < Base
@@ -7,4 +9,4 @@ class Valvat
7
9
  end
8
10
  end
9
11
  end
10
- end
12
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class RO < Base
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class SE < Base
4
6
  check_digit_length 0
5
7
 
6
8
  def validate
7
- vat.to_s_wo_country[-2..-1].to_i > 0 &&
8
- super
9
+ vat.to_s_wo_country[-2..-1].to_i.positive? &&
10
+ super
9
11
  end
10
12
 
11
13
  private
@@ -23,4 +25,4 @@ class Valvat
23
25
  end
24
26
  end
25
27
  end
26
- end
28
+ end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Checksum
3
5
  class SI < Base
4
6
  def validate
5
- figures_str.to_i > 999999 &&
6
- super
7
+ figures_str.to_i > 999_999 &&
8
+ super
7
9
  end
8
10
 
9
11
  def check_digit
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valvat
4
+ Error = Class.new(RuntimeError)
5
+
6
+ class ViesError < Error
7
+ def initialize(faultstring = 'UNKNOWN')
8
+ @faultstring = faultstring
9
+ super
10
+ end
11
+
12
+ def to_s
13
+ "The VIES web service returned the error '#{@faultstring}'."
14
+ end
15
+
16
+ def eql?(other)
17
+ to_s.eql?(other.to_s)
18
+ end
19
+ end
20
+ ViesMaintenanceError = Class.new(ViesError)
21
+
22
+ ServiceUnavailable = Class.new(ViesMaintenanceError)
23
+ MemberStateUnavailable = Class.new(ViesMaintenanceError)
24
+
25
+ Timeout = Class.new(ViesError)
26
+ InvalidRequester = Class.new(ViesError)
27
+ BlockedError = Class.new(ViesError)
28
+ RateLimitError = Class.new(ViesError)
29
+
30
+ UnknownViesError = Class.new(ViesError)
31
+ end
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  def initialize(raw)
3
- @raw = Valvat::Utils.normalize(raw || "")
5
+ @raw = Valvat::Utils.normalize(raw || '')
4
6
  @vat_country_code, @to_s_wo_country = to_a
5
7
  end
6
8
 
7
9
  attr_reader :raw, :vat_country_code, :to_s_wo_country
8
10
 
9
11
  def blank?
10
- raw.nil? || raw.strip == ""
12
+ raw.nil? || raw.strip == ''
11
13
  end
12
14
 
13
15
  def valid?
@@ -22,8 +24,9 @@ class Valvat
22
24
  Valvat::Utils.vat_country_to_iso_country(vat_country_code)
23
25
  end
24
26
 
27
+ # TODO: Remove method / not in use
25
28
  def european?
26
- Valvat::Utils::EU_COUNTRIES.include?(iso_country_code)
29
+ Valvat::Utils::EU_MEMBER_STATES.include?(iso_country_code)
27
30
  end
28
31
 
29
32
  def to_a
@@ -35,11 +38,11 @@ class Valvat
35
38
  end
36
39
 
37
40
  def inspect
38
- "#<Valvat #{[raw, iso_country_code].compact.join(" ")}>"
41
+ "#<Valvat #{[raw, iso_country_code].compact.join(' ')}>"
39
42
  end
40
43
  end
41
44
 
42
- def Valvat(vat)
45
+ def Valvat(vat) # rubocop:disable Naming/MethodName
43
46
  vat.is_a?(Valvat) ? vat : Valvat.new(vat)
44
47
  end
45
48
 
@@ -1,7 +1,8 @@
1
1
  de:
2
2
  errors:
3
3
  messages:
4
- invalid_vat: ist keine gültige %{country_adjective} USt-IdNr.
4
+ invalid_vat: "ist keine gültige %{country_adjective} USt-IdNr."
5
+ vies_down: "Die USt-IdNr. kann nicht überprüft werden: der VIES-Webservice ist nicht verfügbar. Bitte später noch einmal versuchen."
5
6
  valvat:
6
7
  country_adjectives:
7
8
  eu: europäische
@@ -1,7 +1,8 @@
1
1
  en:
2
2
  errors:
3
3
  messages:
4
- invalid_vat: is not a valid %{country_adjective} vat number
4
+ invalid_vat: "is not a valid %{country_adjective} VAT number"
5
+ vies_down: "Unable to validate your VAT number: the VIES service is down. Please try again later."
5
6
  valvat:
6
7
  country_adjectives:
7
8
  eu: European
@@ -2,6 +2,7 @@ es:
2
2
  errors:
3
3
  messages:
4
4
  invalid_vat: no es un número de IVA intracomunitario %{country_adjective} válido
5
+ vies_down: "No se ha podido validar su número de IVA: El servicio VIES no está disponible, por favor inténtelo más tarde."
5
6
  valvat:
6
7
  country_adjectives:
7
8
  eu: europeo
@@ -2,32 +2,33 @@ fr:
2
2
  errors:
3
3
  messages:
4
4
  invalid_vat: "Numéro de TVA %{country_adjective} invalide"
5
+ vies_down: "Impossible de valider votre numéro de TVA : le service de validation VIES est indisponible. Merci de réessayer ultérieurement."
5
6
  valvat:
6
7
  country_adjectives:
7
8
  eu: intracommunautaire
8
- at: autrichienne
9
+ at: autrichien
9
10
  be: belge
10
11
  bg: bulgare
11
12
  cy: chypriote
12
13
  cz: tchèque
13
- de: allemande
14
- dk: danoise
14
+ de: allemand
15
+ dk: danois
15
16
  ee: estonien
16
- es: espagnole
17
- fi: finlandaise
18
- fr: française
17
+ es: espagnol
18
+ fi: finlandais
19
+ fr: français
19
20
  gb: britannique
20
- gr: grèque
21
- ie: irlandaise
22
- it: italienne
23
- lt: lituanienne
24
- lu: luxembourgeoise
25
- lv: lettone
21
+ gr: grec
22
+ ie: irlandais
23
+ it: italien
24
+ lt: lituanien
25
+ lu: luxembourgeois
26
+ lv: letton
26
27
  mt: maltais
27
- nl: hollandaise
28
- pl: polonaise
29
- pt: portugaise
30
- ro: roumaine
31
- se: suédoise
28
+ nl: hollandais
29
+ pl: polonais
30
+ pt: portugais
31
+ ro: roumain
32
+ se: suédois
32
33
  si: slovène
33
34
  sk: slovaque