valvat 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) 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 +1 -0
  6. data/.rubocop.yml +15 -0
  7. data/CHANGES.md +9 -1
  8. data/Gemfile +2 -0
  9. data/Guardfile +6 -4
  10. data/README.md +5 -3
  11. data/Rakefile +3 -1
  12. data/gemfiles/activemodel-5 +1 -1
  13. data/gemfiles/activemodel-6 +1 -1
  14. data/lib/active_model/validations/valvat_validator.rb +65 -32
  15. data/lib/valvat.rb +4 -2
  16. data/lib/valvat/checksum.rb +14 -13
  17. data/lib/valvat/checksum/at.rb +3 -1
  18. data/lib/valvat/checksum/be.rb +3 -1
  19. data/lib/valvat/checksum/bg.rb +17 -17
  20. data/lib/valvat/checksum/cy.rb +2 -0
  21. data/lib/valvat/checksum/de.rb +5 -3
  22. data/lib/valvat/checksum/dk.rb +3 -1
  23. data/lib/valvat/checksum/ee.rb +2 -0
  24. data/lib/valvat/checksum/es.rb +14 -16
  25. data/lib/valvat/checksum/fi.rb +3 -1
  26. data/lib/valvat/checksum/fr.rb +4 -2
  27. data/lib/valvat/checksum/gb.rb +46 -31
  28. data/lib/valvat/checksum/gr.rb +4 -2
  29. data/lib/valvat/checksum/hr.rb +2 -0
  30. data/lib/valvat/checksum/hu.rb +2 -0
  31. data/lib/valvat/checksum/ie.rb +5 -6
  32. data/lib/valvat/checksum/it.rb +6 -4
  33. data/lib/valvat/checksum/lt.rb +2 -0
  34. data/lib/valvat/checksum/lu.rb +3 -1
  35. data/lib/valvat/checksum/mt.rb +2 -0
  36. data/lib/valvat/checksum/nl.rb +5 -3
  37. data/lib/valvat/checksum/pl.rb +3 -1
  38. data/lib/valvat/checksum/pt.rb +3 -1
  39. data/lib/valvat/checksum/ro.rb +2 -0
  40. data/lib/valvat/checksum/se.rb +5 -3
  41. data/lib/valvat/checksum/si.rb +4 -2
  42. data/lib/valvat/error.rb +4 -1
  43. data/lib/valvat/local.rb +6 -4
  44. data/lib/valvat/locales/de.yml +2 -1
  45. data/lib/valvat/locales/en.yml +2 -1
  46. data/lib/valvat/locales/es.yml +1 -0
  47. data/lib/valvat/locales/fr.yml +1 -0
  48. data/lib/valvat/locales/hu.yml +1 -0
  49. data/lib/valvat/locales/nl.yml +1 -0
  50. data/lib/valvat/locales/pl.yml +1 -0
  51. data/lib/valvat/lookup.rb +5 -4
  52. data/lib/valvat/lookup/fault.rb +17 -16
  53. data/lib/valvat/lookup/request.rb +21 -16
  54. data/lib/valvat/lookup/response.rb +11 -10
  55. data/lib/valvat/syntax.rb +31 -30
  56. data/lib/valvat/utils.rb +12 -9
  57. data/lib/valvat/version.rb +3 -1
  58. data/spec/active_model/validations/valvat_validator_spec.rb +99 -93
  59. data/spec/spec_helper.rb +8 -8
  60. data/spec/valvat/checksum/at_spec.rb +6 -4
  61. data/spec/valvat/checksum/be_spec.rb +6 -4
  62. data/spec/valvat/checksum/bg_spec.rb +7 -4
  63. data/spec/valvat/checksum/cy_spec.rb +3 -1
  64. data/spec/valvat/checksum/de_spec.rb +6 -4
  65. data/spec/valvat/checksum/dk_spec.rb +6 -4
  66. data/spec/valvat/checksum/ee_spec.rb +3 -1
  67. data/spec/valvat/checksum/es_spec.rb +7 -4
  68. data/spec/valvat/checksum/fi_spec.rb +6 -4
  69. data/spec/valvat/checksum/fr_spec.rb +6 -4
  70. data/spec/valvat/checksum/gb_spec.rb +4 -2
  71. data/spec/valvat/checksum/gr_spec.rb +6 -4
  72. data/spec/valvat/checksum/hr_spec.rb +3 -1
  73. data/spec/valvat/checksum/hu_spec.rb +3 -1
  74. data/spec/valvat/checksum/ie_spec.rb +8 -6
  75. data/spec/valvat/checksum/it_spec.rb +10 -8
  76. data/spec/valvat/checksum/lt_spec.rb +3 -1
  77. data/spec/valvat/checksum/lu_spec.rb +6 -4
  78. data/spec/valvat/checksum/mt_spec.rb +3 -1
  79. data/spec/valvat/checksum/nl_spec.rb +9 -7
  80. data/spec/valvat/checksum/pl_spec.rb +8 -6
  81. data/spec/valvat/checksum/pt_spec.rb +10 -11
  82. data/spec/valvat/checksum/ro_spec.rb +3 -1
  83. data/spec/valvat/checksum/se_spec.rb +8 -6
  84. data/spec/valvat/checksum/si_spec.rb +10 -8
  85. data/spec/valvat/checksum_spec.rb +11 -9
  86. data/spec/valvat/lookup/fault_spec.rb +34 -0
  87. data/spec/valvat/lookup/request_spec.rb +19 -0
  88. data/spec/valvat/lookup/response_spec.rb +29 -0
  89. data/spec/valvat/lookup_spec.rb +138 -153
  90. data/spec/valvat/syntax_spec.rb +59 -280
  91. data/spec/valvat/utils_spec.rb +62 -47
  92. data/spec/valvat_spec.rb +140 -152
  93. data/valvat.gemspec +17 -17
  94. metadata +24 -27
  95. metadata.gz.sig +0 -0
  96. data/.travis.yml +0 -47
  97. data/gemfiles/activemodel-3-2 +0 -7
  98. data/gemfiles/activemodel-4 +0 -7
  99. data/gemfiles/before-ruby21/activemodel-3-2 +0 -8
  100. data/gemfiles/before-ruby21/activemodel-4 +0 -8
  101. data/gemfiles/before-ruby21/standalone +0 -7
  102. data/spec/valvat/lockup/fault_spec.rb +0 -32
  103. data/spec/valvat/lockup/request_spec.rb +0 -15
  104. data/spec/valvat/lockup/response_spec.rb +0 -27
@@ -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?
@@ -36,11 +38,11 @@ class Valvat
36
38
  end
37
39
 
38
40
  def inspect
39
- "#<Valvat #{[raw, iso_country_code].compact.join(" ")}>"
41
+ "#<Valvat #{[raw, iso_country_code].compact.join(' ')}>"
40
42
  end
41
43
  end
42
44
 
43
- def Valvat(vat)
45
+ def Valvat(vat) # rubocop:disable Naming/MethodName
44
46
  vat.is_a?(Valvat) ? vat : Valvat.new(vat)
45
47
  end
46
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,6 +2,7 @@ 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
@@ -2,6 +2,7 @@ hu:
2
2
  errors:
3
3
  messages:
4
4
  invalid_vat: a közösségi adószám %{country_adjective} helytelen
5
+ vies_down: "Az adószám ellenőrzése sikertelen: a VIES szolgáltatás nem elérhető. Kérjük próbálja újra később."
5
6
  valvat:
6
7
  country_adjectives:
7
8
  eu: európai
@@ -2,6 +2,7 @@ nl:
2
2
  errors:
3
3
  messages:
4
4
  invalid_vat: is geen geldig %{country_adjective} btw nummer
5
+ vies_down: "We kunnen uw btw-nummer niet bevestigen: de VIES-service is momenteel niet beschikbaar. Probeer het later opnieuw."
5
6
  valvat:
6
7
  country_adjectives:
7
8
  eu: Europees
@@ -2,6 +2,7 @@ pl:
2
2
  errors:
3
3
  messages:
4
4
  invalid_vat: nie jest prawidłowym %{country_adjective} numerem VAT
5
+ vies_down: "Nie można zweryfikować numeru VAT: serwis VIES nie działa. Spróbuj ponownie później."
5
6
  valvat:
6
7
  country_adjectives:
7
8
  eu: europejskim
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  class Lookup
3
-
4
- def initialize(vat, options={})
5
+ def initialize(vat, options = {})
5
6
  @vat = Valvat(vat)
6
7
  @options = options || {}
7
8
  @options[:requester] ||= @options[:requester_vat]
@@ -15,7 +16,7 @@ class Valvat
15
16
  end
16
17
 
17
18
  class << self
18
- def validate(vat, options={})
19
+ def validate(vat, options = {})
19
20
  new(vat, options).validate
20
21
  end
21
22
  end
@@ -35,7 +36,7 @@ class Valvat
35
36
  end
36
37
 
37
38
  def handle_vies_error(error)
38
- if ViesMaintenanceError === error
39
+ if error.is_a?(ViesMaintenanceError)
39
40
  raise error if @options[:raise_error]
40
41
  else
41
42
  raise error unless @options[:raise_error] == false
@@ -1,30 +1,31 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  class Lookup
3
5
  class Fault < Response
4
- private
5
-
6
6
  def self.cleanup(hash)
7
7
  fault = hash[:fault][:faultstring]
8
- return {valid: false} if fault == "INVALID_INPUT"
9
- {error: fault_to_error(fault)}
8
+ return { valid: false } if fault == 'INVALID_INPUT'
9
+
10
+ { error: fault_to_error(fault) }
10
11
  end
11
12
 
12
13
  FAULTS = {
13
- "SERVICE_UNAVAILABLE" => ServiceUnavailable,
14
- "MS_UNAVAILABLE" => MemberStateUnavailable,
15
- "INVALID_REQUESTER_INFO" => InvalidRequester,
16
- "TIMEOUT" => Timeout,
17
- "VAT_BLOCKED" => BlockedError,
18
- "IP_BLOCKED" => BlockedError,
19
- "GLOBAL_MAX_CONCURRENT_REQ" => RateLimitError,
20
- "GLOBAL_MAX_CONCURRENT_REQ_TIME" => RateLimitError,
21
- "MS_MAX_CONCURRENT_REQ" => RateLimitError,
22
- "MS_MAX_CONCURRENT_REQ_TIME" => RateLimitError
23
- }
14
+ 'SERVICE_UNAVAILABLE' => ServiceUnavailable,
15
+ 'MS_UNAVAILABLE' => MemberStateUnavailable,
16
+ 'INVALID_REQUESTER_INFO' => InvalidRequester,
17
+ 'TIMEOUT' => Timeout,
18
+ 'VAT_BLOCKED' => BlockedError,
19
+ 'IP_BLOCKED' => BlockedError,
20
+ 'GLOBAL_MAX_CONCURRENT_REQ' => RateLimitError,
21
+ 'GLOBAL_MAX_CONCURRENT_REQ_TIME' => RateLimitError,
22
+ 'MS_MAX_CONCURRENT_REQ' => RateLimitError,
23
+ 'MS_MAX_CONCURRENT_REQ_TIME' => RateLimitError
24
+ }.freeze
24
25
 
25
26
  def self.fault_to_error(fault)
26
27
  (FAULTS[fault] || UnknownViesError).new(fault)
27
28
  end
28
29
  end
29
30
  end
30
- end
31
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'savon'
2
4
 
3
5
  class Valvat
@@ -12,13 +14,11 @@ class Valvat
12
14
  end
13
15
 
14
16
  def perform
15
- begin
16
- Response.new(
17
- client.call(action, message: message, message_tag: message_tag)
18
- )
19
- rescue Savon::SOAPFault => fault
20
- Fault.new(fault)
21
- end
17
+ Response.new(
18
+ client.call(action, message: message, message_tag: message_tag)
19
+ )
20
+ rescue Savon::SOAPFault => e
21
+ Fault.new(e)
22
22
  end
23
23
 
24
24
  private
@@ -30,14 +30,10 @@ class Valvat
30
30
  end
31
31
 
32
32
  def message
33
- {
34
- country_code: @vat.vat_country_code,
35
- vat_number: @vat.to_s_wo_country
36
- }.merge(@requester ? {
37
- requester_country_code: @requester.vat_country_code,
38
- requester_vat_number: @requester.to_s_wo_country
39
- } : {}
40
- )
33
+ add_requester({
34
+ country_code: @vat.vat_country_code,
35
+ vat_number: @vat.to_s_wo_country
36
+ })
41
37
  end
42
38
 
43
39
  def message_tag
@@ -47,6 +43,15 @@ class Valvat
47
43
  def action
48
44
  @requester ? :check_vat_approx : :check_vat
49
45
  end
46
+
47
+ def add_requester(message)
48
+ return message unless @requester
49
+
50
+ message[:requester_country_code] = @requester.vat_country_code
51
+ message[:requester_vat_number] = @requester.to_s_wo_country
52
+
53
+ message
54
+ end
50
55
  end
51
56
  end
52
- end
57
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  class Lookup
3
5
  class Response
@@ -10,27 +12,26 @@ class Valvat
10
12
  end
11
13
 
12
14
  def to_hash
13
- @hash ||= self.class.cleanup(@raw.to_hash)
15
+ @to_hash ||= self.class.cleanup(@raw.to_hash)
14
16
  end
15
17
 
16
- private
17
-
18
18
  def self.cleanup(hash)
19
- (hash[:check_vat_approx_response] || hash[:check_vat_response] || {}).inject({}) do |hash, (key, value)|
20
- hash[cleanup_key(key)] = cleanup_value(value) unless key == :"@xmlns"
21
- hash
19
+ (
20
+ hash[:check_vat_approx_response] || hash[:check_vat_response] || {}
21
+ ).each_with_object({}) do |(key, value), result|
22
+ result[cleanup_key(key)] = cleanup_value(value) unless key == :"@xmlns"
22
23
  end
23
24
  end
24
25
 
25
- TRADER_PREFIX = /\Atrader_/
26
+ TRADER_PREFIX = /\Atrader_/.freeze
26
27
 
27
28
  def self.cleanup_key(key)
28
- key.to_s.sub(TRADER_PREFIX, "").to_sym
29
+ key.to_s.sub(TRADER_PREFIX, '').to_sym
29
30
  end
30
31
 
31
32
  def self.cleanup_value(value)
32
- value == "---" ? nil : value
33
+ value == '---' ? nil : value
33
34
  end
34
35
  end
35
36
  end
36
- end
37
+ end
@@ -1,36 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
4
  module Syntax
3
-
4
5
  VAT_PATTERNS = {
5
- 'AT' => /\AATU[0-9]{8}\Z/, # Austria
6
- 'BE' => /\ABE0[0-9]{9}\Z/, # Belgium
7
- 'BG' => /\ABG[0-9]{9,10}\Z/, # Bulgaria
8
- 'CY' => /\ACY(?!12)[0-59][0-9]{7}[A-Z]\Z/, # Cyprus
9
- 'CZ' => /\ACZ[0-9]{8,10}\Z/, # Czech Republic
10
- 'DE' => /\ADE[0-9]{9}\Z/, # Germany
11
- 'DK' => /\ADK[0-9]{8}\Z/, # Denmark
12
- 'EE' => /\AEE10[0-9]{7}\Z/, # Estonia
13
- 'GR' => /\AEL[0-9]{9}\Z/, # Greece
14
- 'ES' => /\AES([A-Z][0-9]{8}|[0-9]{8}[A-Z]|[A-Z][0-9]{7}[A-Z])\Z/, # Spain
15
- 'FI' => /\AFI[0-9]{8}\Z/, # Finland
16
- 'FR' => /\AFR[A-Z0-9]{2}[0-9]{9}\Z/, # France
17
- 'GB' => /\AGB([0-9]{9}|[0-9]{12}|(HA|GD)[0-9]{3})\Z/, # United Kingdom
18
- 'HR' => /\AHR[0-9]{11}\Z/, # Croatia
19
- 'HU' => /\AHU[0-9]{8}\Z/, # Hungary
20
- 'IE' => /\AIE([0-9][A-Z][0-9]{5}|[0-9]{7}[A-Z]?)[A-Z]\Z/, # Ireland
21
- 'IT' => /\AIT[0-9]{11}\Z/, # Italy
22
- 'LT' => /\ALT([0-9]{7}1[0-9]|[0-9]{10}1[0-9])\Z/, # Lithuania
23
- 'LU' => /\ALU[0-9]{8}\Z/, # Luxembourg
24
- 'LV' => /\ALV[0-9]{11}\Z/, # Latvia
25
- 'MT' => /\AMT[0-9]{8}\Z/, # Malta
26
- 'NL' => /\ANL[0-9]{9}B[0-9]{2}\Z/, # Netherlands
27
- 'PL' => /\APL[0-9]{10}\Z/, # Poland
28
- 'PT' => /\APT[0-9]{9}\Z/, # Portugal
29
- 'RO' => /\ARO[1-9][0-9]{1,9}\Z/, # Romania
30
- 'SE' => /\ASE[0-9]{10}01\Z/, # Sweden
31
- 'SI' => /\ASI[0-9]{8}\Z/, # Slovenia
32
- 'SK' => /\ASK[0-9]{10}\Z/ # Slovakia
33
- }
6
+ 'AT' => /\AATU[0-9]{8}\Z/, # Austria
7
+ 'BE' => /\ABE0[0-9]{9}\Z/, # Belgium
8
+ 'BG' => /\ABG[0-9]{9,10}\Z/, # Bulgaria
9
+ 'CY' => /\ACY(?!12)[0-59][0-9]{7}[A-Z]\Z/, # Cyprus
10
+ 'CZ' => /\ACZ[0-9]{8,10}\Z/, # Czech Republic
11
+ 'DE' => /\ADE[0-9]{9}\Z/, # Germany
12
+ 'DK' => /\ADK[0-9]{8}\Z/, # Denmark
13
+ 'EE' => /\AEE10[0-9]{7}\Z/, # Estonia
14
+ 'GR' => /\AEL[0-9]{9}\Z/, # Greece
15
+ 'ES' => /\AES([A-Z][0-9]{8}|[0-9]{8}[A-Z]|[A-Z][0-9]{7}[A-Z])\Z/, # Spain
16
+ 'FI' => /\AFI[0-9]{8}\Z/, # Finland
17
+ 'FR' => /\AFR[A-Z0-9]{2}[0-9]{9}\Z/, # France
18
+ 'GB' => /\A(GB|XI)([0-9]{9}|[0-9]{12}|(HA|GD)[0-9]{3})\Z/, # United Kingdom
19
+ 'HR' => /\AHR[0-9]{11}\Z/, # Croatia
20
+ 'HU' => /\AHU[0-9]{8}\Z/, # Hungary
21
+ 'IE' => /\AIE([0-9][A-Z][0-9]{5}|[0-9]{7}[A-Z]?)[A-Z]\Z/, # Ireland
22
+ 'IT' => /\AIT[0-9]{11}\Z/, # Italy
23
+ 'LT' => /\ALT([0-9]{7}1[0-9]|[0-9]{10}1[0-9])\Z/, # Lithuania
24
+ 'LU' => /\ALU[0-9]{8}\Z/, # Luxembourg
25
+ 'LV' => /\ALV[0-9]{11}\Z/, # Latvia
26
+ 'MT' => /\AMT[0-9]{8}\Z/, # Malta
27
+ 'NL' => /\ANL[0-9]{9}B[0-9]{2}\Z/, # Netherlands
28
+ 'PL' => /\APL[0-9]{10}\Z/, # Poland
29
+ 'PT' => /\APT[0-9]{9}\Z/, # Portugal
30
+ 'RO' => /\ARO[1-9][0-9]{1,9}\Z/, # Romania
31
+ 'SE' => /\ASE[0-9]{10}01\Z/, # Sweden
32
+ 'SI' => /\ASI[0-9]{8}\Z/, # Slovenia
33
+ 'SK' => /\ASK[0-9]{10}\Z/ # Slovakia
34
+ }.freeze
34
35
 
35
36
  def self.validate(vat)
36
37
  vat = Valvat(vat)
@@ -1,31 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'date'
2
4
 
3
5
  class Valvat
4
6
  module Utils
5
-
6
- EU_MEMBER_STATES = %w(AT BE BG CY CZ DE DK EE ES FI FR GR HR HU IE IT LT LU LV MT NL PL PT RO SE SI SK)
7
- SUPPORTED_STATES = EU_MEMBER_STATES + %w(GB)
7
+ EU_MEMBER_STATES = %w[AT BE BG CY CZ DE DK EE ES FI FR GR HR HU IE IT LT LU LV MT NL PL PT RO SE SI SK].freeze
8
+ SUPPORTED_STATES = EU_MEMBER_STATES + %w[GB]
8
9
  EU_COUNTRIES = EU_MEMBER_STATES # TODO: Remove constant
9
- COUNTRY_PATTERN = /\A([A-Z]{2})(.+)\Z/
10
- NORMALIZE_PATTERN = /[[:space:][:punct:][:cntrl:]]+/
10
+ COUNTRY_PATTERN = /\A([A-Z]{2})(.+)\Z/.freeze
11
+ NORMALIZE_PATTERN = /[[:space:][:punct:][:cntrl:]]+/.freeze
12
+ CONVERT_VAT_TO_ISO_COUNTRY = { 'EL' => 'GR', 'XI' => 'GB' }.freeze
13
+ CONVERT_ISO_TO_VAT_COUNTRY = CONVERT_VAT_TO_ISO_COUNTRY.invert.freeze
11
14
 
12
15
  def self.split(vat)
13
16
  COUNTRY_PATTERN =~ vat
14
- result = [$1, $2]
17
+ result = [Regexp.last_match(1), Regexp.last_match(2)]
15
18
  iso_country = vat_country_to_iso_country(result[0])
16
19
  country_is_supported?(iso_country) ? result : [nil, nil]
17
20
  end
18
21
 
19
22
  def self.normalize(vat)
20
- vat.to_s.upcase.gsub(NORMALIZE_PATTERN, "")
23
+ vat.to_s.upcase.gsub(NORMALIZE_PATTERN, '')
21
24
  end
22
25
 
23
26
  def self.vat_country_to_iso_country(vat_country)
24
- vat_country == "EL" ? "GR" : vat_country
27
+ CONVERT_VAT_TO_ISO_COUNTRY[vat_country] || vat_country
25
28
  end
26
29
 
27
30
  def self.iso_country_to_vat_country(iso_country)
28
- iso_country == "GR" ? "EL" : iso_country
31
+ CONVERT_ISO_TO_VAT_COUNTRY[iso_country] || iso_country
29
32
  end
30
33
 
31
34
  def self.country_is_supported?(iso_country)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Valvat
2
- VERSION = "1.0.1"
4
+ VERSION = '1.1.0'
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  if defined?(ActiveModel)
@@ -6,15 +8,15 @@ if defined?(ActiveModel)
6
8
  end
7
9
 
8
10
  class InvoiceWithLookup < ModelBase
9
- validates :vat_number, valvat: {lookup: true}
11
+ validates :vat_number, valvat: { lookup: true }
10
12
  end
11
13
 
12
14
  class InvoiceWithLookupAndFailIfDown < ModelBase
13
- validates :vat_number, valvat: {lookup: :fail_if_down}
15
+ validates :vat_number, valvat: { lookup: :fail_if_down }
14
16
  end
15
17
 
16
18
  class InvoiceAllowBlank < ModelBase
17
- validates :vat_number, valvat: {allow_blank: true}
19
+ validates :vat_number, valvat: { allow_blank: true }
18
20
  end
19
21
 
20
22
  class InvoiceAllowBlankOnAll < ModelBase
@@ -22,7 +24,7 @@ if defined?(ActiveModel)
22
24
  end
23
25
 
24
26
  class InvoiceCheckCountry < ModelBase
25
- validates :vat_number, valvat: {match_country: :country}
27
+ validates :vat_number, valvat: { match_country: :country }
26
28
 
27
29
  def country
28
30
  @attributes[:country]
@@ -30,7 +32,7 @@ if defined?(ActiveModel)
30
32
  end
31
33
 
32
34
  class InvoiceCheckCountryWithLookup < ModelBase
33
- validates :vat_number, valvat: {match_country: :country, lookup: true}
35
+ validates :vat_number, valvat: { match_country: :country, lookup: true }
34
36
 
35
37
  def country
36
38
  @attributes[:country]
@@ -38,7 +40,7 @@ if defined?(ActiveModel)
38
40
  end
39
41
 
40
42
  class InvoiceWithChecksum < ModelBase
41
- validates :vat_number, valvat: {checksum: true}
43
+ validates :vat_number, valvat: { checksum: true }
42
44
  end
43
45
 
44
46
  describe Invoice do
@@ -46,190 +48,194 @@ if defined?(ActiveModel)
46
48
  I18n.locale = :en
47
49
  end
48
50
 
49
- context "with valid VAT number" do
50
- it "should be valid" do
51
- expect(Invoice.new(vat_number: "DE259597697")).to be_valid
51
+ context 'with valid VAT number' do
52
+ it 'is valid' do
53
+ expect(described_class.new(vat_number: 'DE259597697')).to be_valid
52
54
  end
53
55
  end
54
56
 
55
- context "with invalid VAT number" do
56
- let(:invoice) { Invoice.new(vat_number: "DE259597697123") }
57
+ context 'with invalid VAT number' do
58
+ let(:invoice) { described_class.new(vat_number: 'DE259597697123') }
57
59
 
58
- it "should not be valid" do
60
+ it 'is not valid' do
59
61
  expect(invoice).not_to be_valid
60
62
  end
61
63
 
62
- it "should add default (country specific) error message" do
64
+ it 'adds default (country specific) error message' do
63
65
  invoice.valid?
64
- expect(invoice.errors[:vat_number]).to eql(["is not a valid German VAT number"])
66
+ expect(invoice.errors[:vat_number]).to eql(['is not a valid German VAT number'])
65
67
  end
66
68
 
67
- context "on DE locale" do
69
+ context 'with DE locale' do
68
70
  before do
69
71
  I18n.locale = :de
70
72
  end
71
73
 
72
- it "should add translated error message" do
74
+ it 'adds translated error message' do
73
75
  invoice.valid?
74
- expect(invoice.errors[:vat_number]).to eql(["ist keine gültige deutsche USt-IdNr."])
76
+ expect(invoice.errors[:vat_number]).to eql(['ist keine gültige deutsche USt-IdNr.'])
75
77
  end
76
78
  end
77
79
 
78
- context "on PT locale" do
80
+ context 'with PT locale' do
79
81
  before do
80
82
  I18n.locale = :pt
81
83
  end
82
84
 
83
- it "should add translated error message" do
85
+ it 'adds translated error message' do
84
86
  invoice.valid?
85
- expect(invoice.errors[:vat_number]).to eql(["O NIF alemão não é válido."])
87
+ expect(invoice.errors[:vat_number]).to eql(['O NIF alemão não é válido.'])
86
88
  end
87
89
  end
88
90
 
89
- context "with i18n translation in place" do
91
+ context 'with i18n translation in place' do
90
92
  before do
91
93
  I18n.backend.store_translations(:en, activemodel: {
92
- errors: {models: {invoice: {invalid_vat: "is ugly."}}}
93
- })
94
+ errors: { models: { invoice: { invalid_vat: 'is ugly.' } } }
95
+ })
94
96
  end
95
97
 
96
98
  after { I18n.reload! }
97
99
 
98
- it "should use translation" do
100
+ it 'uses translation' do
99
101
  invoice.valid?
100
- expect(invoice.errors[:vat_number]).to eql(["is ugly."])
102
+ expect(invoice.errors[:vat_number]).to eql(['is ugly.'])
101
103
  end
102
104
  end
103
105
 
104
- context "with i18n translation with country adjective placeholder in place" do
106
+ context 'with i18n translation with country_adjective placeholder in place' do
105
107
  before do
106
- I18n.backend.store_translations(:en, activemodel: {
107
- errors: {models: {invoice: {invalid_vat: "is not a %{country_adjective} vat"}}}
108
- })
108
+ msg = 'is not a %{country_adjective} vat' # rubocop:disable Style/FormatStringToken
109
+ translation = { activemodel: { errors: { models: { invoice: { invalid_vat: msg } } } } }
110
+ I18n.backend.store_translations(:en, translation)
109
111
  end
110
112
 
111
113
  after { I18n.reload! }
112
114
 
113
- it "should replace country adjective placeholder" do
114
- invoice = Invoice.new(vat_number: "IE123")
115
+ it 'replaces country_adjective placeholder' do
116
+ invoice = described_class.new(vat_number: 'IE123')
115
117
  invoice.valid?
116
- expect(invoice.errors[:vat_number]).to eql(["is not a Irish vat"])
118
+ expect(invoice.errors[:vat_number]).to eql(['is not a Irish vat'])
117
119
  end
118
120
 
119
- it "should fall back to 'European' if country is missing" do
120
- invoice = Invoice.new(vat_number: "XX123")
121
+ it "falls back to 'European' if country is missing" do
122
+ invoice = described_class.new(vat_number: 'XX123')
121
123
  invoice.valid?
122
- expect(invoice.errors[:vat_number]).to eql(["is not a European vat"])
124
+ expect(invoice.errors[:vat_number]).to eql(['is not a European vat'])
123
125
  end
124
126
  end
125
127
  end
126
128
 
127
- context "with blank VAT number" do
128
- it "should not be valid" do
129
- expect(Invoice.new(vat_number: "")).not_to be_valid
130
- expect(Invoice.new(vat_number: nil)).not_to be_valid
129
+ context 'with blank VAT number' do
130
+ it 'is not valid' do
131
+ expect(described_class.new(vat_number: '')).not_to be_valid
132
+ expect(described_class.new(vat_number: nil)).not_to be_valid
131
133
  end
132
134
  end
133
135
  end
134
136
 
135
137
  describe InvoiceWithLookup do
136
- context "with valid but not existing VAT number" do
138
+ context 'with valid but not existing VAT number' do
137
139
  before do
138
140
  allow(Valvat::Syntax).to receive_messages(validate: true)
139
141
  allow(Valvat::Lookup).to receive_messages(validate: false)
140
142
  end
141
143
 
142
- it "should not be valid" do
143
- expect(InvoiceWithLookup.new(vat_number: "DE123")).not_to be_valid
144
+ it 'is not valid' do
145
+ expect(described_class.new(vat_number: 'DE123')).not_to be_valid
144
146
  end
145
147
  end
146
148
 
147
- context "with valid and existing VAT number" do
149
+ context 'with valid and existing VAT number' do
148
150
  before do
149
151
  allow(Valvat::Syntax).to receive_messages(validate: true)
150
152
  allow(Valvat::Lookup).to receive_messages(validate: true)
151
153
  end
152
154
 
153
- it "should be valid" do
154
- expect(InvoiceWithLookup.new(vat_number: "DE123")).to be_valid
155
+ it 'is valid' do
156
+ expect(described_class.new(vat_number: 'DE123')).to be_valid
155
157
  end
156
158
  end
157
159
 
158
- context "with valid VAT number and VIES country service down" do
160
+ context 'with valid VAT number and VIES country service down' do
159
161
  before do
160
162
  allow(Valvat::Syntax).to receive_messages(validate: true)
161
163
  allow(Valvat::Lookup).to receive_messages(validate: nil)
162
164
  end
163
165
 
164
- it "should be valid" do
165
- expect(InvoiceWithLookup.new(vat_number: "DE123")).to be_valid
166
+ it 'is valid' do
167
+ expect(described_class.new(vat_number: 'DE123')).to be_valid
166
168
  end
167
169
  end
168
170
  end
169
171
 
170
172
  describe InvoiceWithLookupAndFailIfDown do
171
- context "with valid VAT number and VIES country service down" do
173
+ let(:record) { described_class.new }
174
+
175
+ context 'with valid VAT number and VIES country service down' do
172
176
  before do
173
177
  allow(Valvat::Syntax).to receive_messages(validate: true)
174
178
  allow(Valvat::Lookup).to receive_messages(validate: nil)
175
179
  end
176
180
 
177
- it "should not be valid" do
178
- expect(InvoiceWithLookupAndFailIfDown.new(vat_number: "DE123")).not_to be_valid
181
+ it 'is not valid' do
182
+ expect(record).not_to be_valid
183
+ msg = 'Unable to validate your VAT number: the VIES service is down. Please try again later.'
184
+ expect(record.errors[:vat_number]).to eql([msg])
179
185
  end
180
186
  end
181
187
  end
182
188
 
183
189
  describe InvoiceAllowBlank do
184
- context "with blank VAT number" do
185
- it "should be valid" do
186
- expect(InvoiceAllowBlank.new(vat_number: "")).to be_valid
187
- expect(InvoiceAllowBlank.new(vat_number: nil)).to be_valid
190
+ context 'with blank VAT number' do
191
+ it 'is valid' do
192
+ expect(described_class.new(vat_number: '')).to be_valid
193
+ expect(described_class.new(vat_number: nil)).to be_valid
188
194
  end
189
195
  end
190
196
  end
191
197
 
192
198
  describe InvoiceAllowBlankOnAll do
193
- context "with blank VAT number" do
194
- it "should be valid" do
195
- expect(InvoiceAllowBlankOnAll.new(vat_number: "")).to be_valid
196
- expect(InvoiceAllowBlankOnAll.new(vat_number: nil)).to be_valid
199
+ context 'with blank VAT number' do
200
+ it 'is valid' do
201
+ expect(described_class.new(vat_number: '')).to be_valid
202
+ expect(described_class.new(vat_number: nil)).to be_valid
197
203
  end
198
204
  end
199
205
  end
200
206
 
201
207
  describe InvoiceCheckCountry do
202
- it "should be not valid on blank country" do
203
- expect(InvoiceCheckCountry.new(country: nil, vat_number: "DE259597697")).not_to be_valid
204
- expect(InvoiceCheckCountry.new(country: "", vat_number: "DE259597697")).not_to be_valid
208
+ it 'is not valid on blank country' do
209
+ expect(described_class.new(country: nil, vat_number: 'DE259597697')).not_to be_valid
210
+ expect(described_class.new(country: '', vat_number: 'DE259597697')).not_to be_valid
205
211
  end
206
212
 
207
- it "should be not valid on wired country" do
208
- expect(InvoiceCheckCountry.new(country: "XAXXX", vat_number: "DE259597697")).not_to be_valid
209
- expect(InvoiceCheckCountry.new(country: "ZO", vat_number: "DE259597697")).not_to be_valid
213
+ it 'is not valid on wired country' do
214
+ expect(described_class.new(country: 'XAXXX', vat_number: 'DE259597697')).not_to be_valid
215
+ expect(described_class.new(country: 'ZO', vat_number: 'DE259597697')).not_to be_valid
210
216
  end
211
217
 
212
- it "should be not valid on mismatching (eu) country" do
213
- expect(InvoiceCheckCountry.new(country: "FR", vat_number: "DE259597697")).not_to be_valid
214
- expect(InvoiceCheckCountry.new(country: "AT", vat_number: "DE259597697")).not_to be_valid
215
- expect(InvoiceCheckCountry.new(country: "DE", vat_number: "ATU65931334")).not_to be_valid
218
+ it 'is not valid on mismatching (eu) country' do
219
+ expect(described_class.new(country: 'FR', vat_number: 'DE259597697')).not_to be_valid
220
+ expect(described_class.new(country: 'AT', vat_number: 'DE259597697')).not_to be_valid
221
+ expect(described_class.new(country: 'DE', vat_number: 'ATU65931334')).not_to be_valid
216
222
  end
217
223
 
218
- it "should be valid on matching country" do
219
- expect(InvoiceCheckCountry.new(country: "DE", vat_number: "DE259597697")).to be_valid
220
- expect(InvoiceCheckCountry.new(country: "AT", vat_number: "ATU65931334")).to be_valid
224
+ it 'is valid on matching country' do
225
+ expect(described_class.new(country: 'DE', vat_number: 'DE259597697')).to be_valid
226
+ expect(described_class.new(country: 'AT', vat_number: 'ATU65931334')).to be_valid
221
227
  end
222
228
 
223
- it "should give back error message with country from :country_match" do
224
- invoice = InvoiceCheckCountry.new(country: "FR", vat_number: "DE259597697")
229
+ it 'gives back error message with country from :country_match' do
230
+ invoice = described_class.new(country: 'FR', vat_number: 'DE259597697')
225
231
  invoice.valid?
226
- expect(invoice.errors[:vat_number]).to eql(["is not a valid French VAT number"])
232
+ expect(invoice.errors[:vat_number]).to eql(['is not a valid French VAT number'])
227
233
  end
228
234
 
229
- it "should give back error message with country from :country_match even on invalid VAT number" do
230
- invoice = InvoiceCheckCountry.new(country: "FR", vat_number: "DE259597697123")
235
+ it 'gives back error message with country from :country_match even on invalid VAT number' do
236
+ invoice = described_class.new(country: 'FR', vat_number: 'DE259597697123')
231
237
  invoice.valid?
232
- expect(invoice.errors[:vat_number]).to eql(["is not a valid French VAT number"])
238
+ expect(invoice.errors[:vat_number]).to eql(['is not a valid French VAT number'])
233
239
  end
234
240
  end
235
241
 
@@ -239,30 +245,30 @@ if defined?(ActiveModel)
239
245
  allow(Valvat::Lookup).to receive_messages(validate: true)
240
246
  end
241
247
 
242
- it "avoids lookup or syntax check on failed because of mismatching country" do
243
- expect(Valvat::Syntax).not_to receive(:validate)
244
- expect(Valvat::Lookup).not_to receive(:validate)
245
- InvoiceCheckCountryWithLookup.new(country: "FR", vat_number: "DE259597697").valid?
248
+ it 'avoids lookup or syntax check on failed because of mismatching country' do
249
+ described_class.new(country: 'FR', vat_number: 'DE259597697').valid?
250
+ expect(Valvat::Syntax).not_to have_received(:validate)
251
+ expect(Valvat::Lookup).not_to have_received(:validate)
246
252
  end
247
253
 
248
- it "check syntax and looup on matching country" do
249
- expect(Valvat::Syntax).to receive(:validate).and_return(true)
250
- expect(Valvat::Lookup).to receive(:validate).and_return(true)
251
- InvoiceCheckCountryWithLookup.new(country: "DE", vat_number: "DE259597697").valid?
254
+ it 'check syntax and looup on matching country' do
255
+ described_class.new(country: 'DE', vat_number: 'DE259597697').valid?
256
+ expect(Valvat::Syntax).to have_received(:validate)
257
+ expect(Valvat::Lookup).to have_received(:validate)
252
258
  end
253
259
  end
254
260
 
255
261
  describe InvoiceWithChecksum do
256
- context "with valid VAT number" do
257
- it "should be valid" do
258
- expect(InvoiceWithChecksum.new(vat_number: "DE259597697")).to be_valid
262
+ context 'with valid VAT number' do
263
+ it 'is valid' do
264
+ expect(described_class.new(vat_number: 'DE259597697')).to be_valid
259
265
  end
260
266
  end
261
267
 
262
- context "with invalid VAT number" do
263
- it "should not be valid" do
264
- expect(InvoiceWithChecksum.new(vat_number: "DE259597687")).not_to be_valid
268
+ context 'with invalid VAT number' do
269
+ it 'is not valid' do
270
+ expect(described_class.new(vat_number: 'DE259597687')).not_to be_valid
265
271
  end
266
272
  end
267
273
  end
268
- end
274
+ end