valvat 0.8.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.github/workflows/ruby.yml +35 -0
- data/.gitignore +3 -1
- data/.rubocop.yml +15 -0
- data/CHANGES.md +45 -1
- data/Gemfile +4 -3
- data/Guardfile +6 -4
- data/README.md +99 -47
- data/Rakefile +3 -1
- data/certs/yolk.pem +21 -21
- data/gemfiles/activemodel-5 +4 -5
- data/gemfiles/activemodel-6 +7 -0
- data/gemfiles/standalone +3 -5
- data/lib/active_model/validations/valvat_validator.rb +70 -22
- data/lib/valvat.rb +7 -3
- data/lib/valvat/checksum.rb +14 -13
- data/lib/valvat/checksum/at.rb +3 -1
- data/lib/valvat/checksum/be.rb +3 -1
- data/lib/valvat/checksum/bg.rb +17 -17
- data/lib/valvat/checksum/cy.rb +2 -0
- data/lib/valvat/checksum/de.rb +5 -3
- data/lib/valvat/checksum/dk.rb +3 -1
- data/lib/valvat/checksum/ee.rb +2 -0
- data/lib/valvat/checksum/es.rb +14 -16
- data/lib/valvat/checksum/fi.rb +3 -1
- data/lib/valvat/checksum/fr.rb +4 -2
- data/lib/valvat/checksum/gb.rb +53 -21
- data/lib/valvat/checksum/gr.rb +4 -2
- data/lib/valvat/checksum/hr.rb +2 -0
- data/lib/valvat/checksum/hu.rb +2 -0
- data/lib/valvat/checksum/ie.rb +5 -6
- data/lib/valvat/checksum/it.rb +6 -4
- data/lib/valvat/checksum/lt.rb +2 -0
- data/lib/valvat/checksum/lu.rb +3 -1
- data/lib/valvat/checksum/mt.rb +2 -0
- data/lib/valvat/checksum/nl.rb +9 -2
- data/lib/valvat/checksum/pl.rb +3 -1
- data/lib/valvat/checksum/pt.rb +3 -1
- data/lib/valvat/checksum/ro.rb +2 -0
- data/lib/valvat/checksum/se.rb +5 -3
- data/lib/valvat/checksum/si.rb +4 -2
- data/lib/valvat/error.rb +31 -0
- data/lib/valvat/local.rb +8 -5
- data/lib/valvat/locales/de.yml +2 -1
- data/lib/valvat/locales/en.yml +2 -1
- data/lib/valvat/locales/es.yml +1 -0
- data/lib/valvat/locales/fr.yml +18 -17
- data/lib/valvat/locales/hu.yml +1 -0
- data/lib/valvat/locales/nl.yml +1 -0
- data/lib/valvat/locales/pl.yml +1 -0
- data/lib/valvat/locales/pt.yml +28 -28
- data/lib/valvat/lookup.rb +14 -45
- data/lib/valvat/lookup/fault.rb +31 -0
- data/lib/valvat/lookup/request.rb +37 -11
- data/lib/valvat/lookup/response.rb +37 -0
- data/lib/valvat/syntax.rb +31 -30
- data/lib/valvat/utils.rb +20 -9
- data/lib/valvat/version.rb +3 -1
- data/spec/active_model/validations/valvat_validator_spec.rb +132 -100
- data/spec/spec_helper.rb +8 -8
- data/spec/valvat/checksum/at_spec.rb +8 -6
- data/spec/valvat/checksum/be_spec.rb +8 -6
- data/spec/valvat/checksum/bg_spec.rb +9 -6
- data/spec/valvat/checksum/cy_spec.rb +5 -3
- data/spec/valvat/checksum/de_spec.rb +8 -6
- data/spec/valvat/checksum/dk_spec.rb +8 -6
- data/spec/valvat/checksum/ee_spec.rb +5 -3
- data/spec/valvat/checksum/es_spec.rb +9 -6
- data/spec/valvat/checksum/fi_spec.rb +8 -6
- data/spec/valvat/checksum/fr_spec.rb +8 -6
- data/spec/valvat/checksum/gb_spec.rb +9 -5
- data/spec/valvat/checksum/gr_spec.rb +8 -6
- data/spec/valvat/checksum/hr_spec.rb +5 -3
- data/spec/valvat/checksum/hu_spec.rb +5 -3
- data/spec/valvat/checksum/ie_spec.rb +13 -5
- data/spec/valvat/checksum/it_spec.rb +12 -10
- data/spec/valvat/checksum/lt_spec.rb +5 -3
- data/spec/valvat/checksum/lu_spec.rb +8 -6
- data/spec/valvat/checksum/mt_spec.rb +5 -3
- data/spec/valvat/checksum/nl_spec.rb +9 -7
- data/spec/valvat/checksum/pl_spec.rb +10 -8
- data/spec/valvat/checksum/pt_spec.rb +12 -13
- data/spec/valvat/checksum/ro_spec.rb +5 -3
- data/spec/valvat/checksum/se_spec.rb +10 -8
- data/spec/valvat/checksum/si_spec.rb +12 -10
- data/spec/valvat/checksum_spec.rb +12 -10
- data/spec/valvat/lookup/fault_spec.rb +34 -0
- data/spec/valvat/lookup/request_spec.rb +19 -0
- data/spec/valvat/lookup/response_spec.rb +29 -0
- data/spec/valvat/lookup_spec.rb +202 -84
- data/spec/valvat/syntax_spec.rb +59 -280
- data/spec/valvat/utils_spec.rb +76 -42
- data/spec/valvat_spec.rb +140 -152
- data/valvat.gemspec +18 -17
- metadata +61 -41
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -33
- data/gemfiles/activemodel-3-2 +0 -8
- data/gemfiles/activemodel-4 +0 -8
- data/gemfiles/before-ruby21/activemodel-3-2 +0 -9
- data/gemfiles/before-ruby21/activemodel-4 +0 -9
- data/gemfiles/before-ruby21/standalone +0 -8
- data/lib/valvat/lookup/request_with_id.rb +0 -31
data/lib/valvat/checksum/fi.rb
CHANGED
data/lib/valvat/checksum/fr.rb
CHANGED
@@ -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
|
data/lib/valvat/checksum/gb.rb
CHANGED
@@ -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
|
-
|
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
|
14
|
-
return false if
|
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
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
23
|
-
|
36
|
+
def all_zero?
|
37
|
+
str_wo_country =~ /\A0{9}\Z/ || str_wo_country =~ /\A0{12}\Z/
|
38
|
+
end
|
24
39
|
|
25
|
-
|
26
|
-
|
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
|
-
|
29
|
-
|
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
|
-
|
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
|
data/lib/valvat/checksum/gr.rb
CHANGED
@@ -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
|
data/lib/valvat/checksum/hr.rb
CHANGED
data/lib/valvat/checksum/hu.rb
CHANGED
data/lib/valvat/checksum/ie.rb
CHANGED
@@ -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 =
|
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
|
data/lib/valvat/checksum/it.rb
CHANGED
@@ -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
|
-
|
8
|
-
|
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
|
data/lib/valvat/checksum/lt.rb
CHANGED
data/lib/valvat/checksum/lu.rb
CHANGED
data/lib/valvat/checksum/mt.rb
CHANGED
data/lib/valvat/checksum/nl.rb
CHANGED
@@ -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
|
data/lib/valvat/checksum/pl.rb
CHANGED
data/lib/valvat/checksum/pt.rb
CHANGED
data/lib/valvat/checksum/ro.rb
CHANGED
data/lib/valvat/checksum/se.rb
CHANGED
@@ -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
|
8
|
-
|
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
|
data/lib/valvat/checksum/si.rb
CHANGED
data/lib/valvat/error.rb
ADDED
@@ -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
|
data/lib/valvat/local.rb
CHANGED
@@ -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::
|
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
|
|
data/lib/valvat/locales/de.yml
CHANGED
@@ -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
|
data/lib/valvat/locales/en.yml
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
en:
|
2
2
|
errors:
|
3
3
|
messages:
|
4
|
-
invalid_vat: is not a valid %{country_adjective}
|
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
|
data/lib/valvat/locales/es.yml
CHANGED
@@ -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
|
data/lib/valvat/locales/fr.yml
CHANGED
@@ -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:
|
9
|
+
at: autrichien
|
9
10
|
be: belge
|
10
11
|
bg: bulgare
|
11
12
|
cy: chypriote
|
12
13
|
cz: tchèque
|
13
|
-
de:
|
14
|
-
dk:
|
14
|
+
de: allemand
|
15
|
+
dk: danois
|
15
16
|
ee: estonien
|
16
|
-
es:
|
17
|
-
fi:
|
18
|
-
fr:
|
17
|
+
es: espagnol
|
18
|
+
fi: finlandais
|
19
|
+
fr: français
|
19
20
|
gb: britannique
|
20
|
-
gr:
|
21
|
-
ie:
|
22
|
-
it:
|
23
|
-
lt:
|
24
|
-
lu:
|
25
|
-
lv:
|
21
|
+
gr: grec
|
22
|
+
ie: irlandais
|
23
|
+
it: italien
|
24
|
+
lt: lituanien
|
25
|
+
lu: luxembourgeois
|
26
|
+
lv: letton
|
26
27
|
mt: maltais
|
27
|
-
nl:
|
28
|
-
pl:
|
29
|
-
pt:
|
30
|
-
ro:
|
31
|
-
se:
|
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
|