valvat 0.8.1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.gitignore +2 -1
- data/.travis.yml +30 -16
- data/CHANGES.md +45 -2
- data/Gemfile +2 -3
- data/README.md +96 -46
- data/certs/yolk.pem +24 -20
- data/gemfiles/activemodel-3-2 +3 -4
- data/gemfiles/activemodel-4 +3 -4
- data/gemfiles/activemodel-5 +4 -5
- data/gemfiles/activemodel-6 +7 -0
- data/gemfiles/before-ruby21/activemodel-3-2 +3 -4
- data/gemfiles/before-ruby21/activemodel-4 +3 -4
- data/gemfiles/before-ruby21/standalone +3 -4
- data/gemfiles/standalone +3 -5
- data/lib/active_model/validations/valvat_validator.rb +20 -5
- data/lib/valvat.rb +3 -1
- data/lib/valvat/checksum/gb.rb +30 -13
- data/lib/valvat/checksum/ie.rb +1 -1
- data/lib/valvat/checksum/nl.rb +5 -0
- data/lib/valvat/checksum/si.rb +2 -2
- data/lib/valvat/error.rb +28 -0
- data/lib/valvat/local.rb +2 -1
- data/lib/valvat/locales/en.yml +1 -1
- data/lib/valvat/locales/fr.yml +17 -17
- data/lib/valvat/locales/pt.yml +28 -28
- data/lib/valvat/lookup.rb +11 -43
- data/lib/valvat/lookup/fault.rb +30 -0
- data/lib/valvat/lookup/request.rb +33 -12
- data/lib/valvat/lookup/response.rb +36 -0
- data/lib/valvat/utils.rb +11 -3
- data/lib/valvat/version.rb +1 -1
- data/spec/active_model/validations/valvat_validator_spec.rb +92 -66
- data/spec/valvat/checksum/at_spec.rb +2 -2
- data/spec/valvat/checksum/be_spec.rb +2 -2
- data/spec/valvat/checksum/bg_spec.rb +2 -2
- data/spec/valvat/checksum/cy_spec.rb +2 -2
- data/spec/valvat/checksum/de_spec.rb +2 -2
- data/spec/valvat/checksum/dk_spec.rb +2 -2
- data/spec/valvat/checksum/ee_spec.rb +2 -2
- data/spec/valvat/checksum/es_spec.rb +2 -2
- data/spec/valvat/checksum/fi_spec.rb +2 -2
- data/spec/valvat/checksum/fr_spec.rb +2 -2
- data/spec/valvat/checksum/gb_spec.rb +5 -3
- data/spec/valvat/checksum/gr_spec.rb +2 -2
- data/spec/valvat/checksum/hr_spec.rb +2 -2
- data/spec/valvat/checksum/hu_spec.rb +2 -2
- data/spec/valvat/checksum/ie_spec.rb +8 -2
- data/spec/valvat/checksum/it_spec.rb +4 -4
- data/spec/valvat/checksum/lt_spec.rb +2 -2
- data/spec/valvat/checksum/lu_spec.rb +2 -2
- data/spec/valvat/checksum/mt_spec.rb +2 -2
- data/spec/valvat/checksum/nl_spec.rb +4 -4
- data/spec/valvat/checksum/pl_spec.rb +3 -3
- data/spec/valvat/checksum/pt_spec.rb +2 -2
- data/spec/valvat/checksum/ro_spec.rb +2 -2
- data/spec/valvat/checksum/se_spec.rb +3 -3
- data/spec/valvat/checksum/si_spec.rb +7 -7
- data/spec/valvat/checksum_spec.rb +2 -2
- data/spec/valvat/lockup/fault_spec.rb +32 -0
- data/spec/valvat/lockup/request_spec.rb +15 -0
- data/spec/valvat/lockup/response_spec.rb +27 -0
- data/spec/valvat/lookup_spec.rb +168 -35
- data/spec/valvat/syntax_spec.rb +29 -29
- data/spec/valvat/utils_spec.rb +28 -8
- data/spec/valvat_spec.rb +18 -18
- data/valvat.gemspec +1 -0
- metadata +52 -25
- metadata.gz.sig +0 -0
- data/lib/valvat/lookup/request_with_id.rb +0 -31
data/gemfiles/activemodel-3-2
CHANGED
data/gemfiles/activemodel-4
CHANGED
data/gemfiles/activemodel-5
CHANGED
data/gemfiles/standalone
CHANGED
@@ -5,6 +5,20 @@ require 'valvat/lookup'
|
|
5
5
|
module ActiveModel
|
6
6
|
module Validations
|
7
7
|
class ValvatValidator < EachValidator
|
8
|
+
def initialize(options)
|
9
|
+
if options[:lookup]
|
10
|
+
options[:lookup] = if options[:lookup] == :fail_if_down
|
11
|
+
{fail_if_down: true}
|
12
|
+
elsif options[:lookup].is_a?(Hash)
|
13
|
+
options[:lookup]
|
14
|
+
else
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
8
22
|
def validate_each(record, attribute, value)
|
9
23
|
vat = Valvat(value)
|
10
24
|
iso_country_code = vat.iso_country_code
|
@@ -17,10 +31,10 @@ module ActiveModel
|
|
17
31
|
|
18
32
|
iso_country_code = "eu" if iso_country_code.blank?
|
19
33
|
record.errors.add(attribute, :invalid_vat,
|
20
|
-
:
|
21
|
-
:
|
34
|
+
message: options[:message],
|
35
|
+
country_adjective: I18n.t(
|
22
36
|
:"valvat.country_adjectives.#{iso_country_code.downcase}",
|
23
|
-
:
|
37
|
+
default: [:"valvat.country_adjectives.eu", "european"]
|
24
38
|
)
|
25
39
|
)
|
26
40
|
end
|
@@ -43,9 +57,10 @@ module ActiveModel
|
|
43
57
|
|
44
58
|
def vat_exists?(vat)
|
45
59
|
return true unless options[:lookup]
|
46
|
-
|
60
|
+
|
61
|
+
is_valid = vat.exists?(options[:lookup])
|
47
62
|
return is_valid unless is_valid.nil?
|
48
|
-
options[:lookup]
|
63
|
+
!options[:lookup][:fail_if_down]
|
49
64
|
end
|
50
65
|
end
|
51
66
|
end
|
data/lib/valvat.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
require 'valvat/error'
|
1
2
|
require 'valvat/local'
|
2
3
|
require 'valvat/lookup'
|
3
4
|
require 'valvat/lookup/request'
|
4
|
-
require 'valvat/lookup/
|
5
|
+
require 'valvat/lookup/response'
|
6
|
+
require 'valvat/lookup/fault'
|
5
7
|
require 'active_model/validations/valvat_validator' if defined?(ActiveModel)
|
6
8
|
|
7
9
|
class Valvat
|
data/lib/valvat/checksum/gb.rb
CHANGED
@@ -3,33 +3,50 @@ class Valvat
|
|
3
3
|
class GB < Base
|
4
4
|
OLD_FORMAT_FORBIDDEN_RANGES = [(100_000..999_999), (9_490_001..9_700_000), (9_990_001..9_999_999)]
|
5
5
|
NEW_FORMAT_FORBIDDEN_RANGES = [(1..100_000), (100_001..1_000_000)]
|
6
|
+
GOV_NUMBER = /\A(GD[0-4]{1}\d{2})\Z/
|
7
|
+
HEALTH_NUMBER = /\A(HA[5-9]{1}\d{2})\Z/
|
6
8
|
|
7
9
|
def validate
|
8
10
|
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]
|
11
11
|
|
12
12
|
# government departments and health authorities, so no checksum
|
13
|
-
return true if
|
14
|
-
return false if vat_number
|
13
|
+
return true if gov_or_health?(vat_number)
|
14
|
+
return false if all_zero?(vat_number)
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
inject(:+)
|
16
|
+
checksum = vat_number[7..8].to_i
|
17
|
+
vat_base = vat_number[0..6]
|
18
|
+
vat_base_int = vat_base.to_i
|
19
|
+
vat_base_sum = generate_vat_base_sum(vat_base)
|
21
20
|
|
22
|
-
old_format_remainder = (vat_base_sum +
|
23
|
-
new_format_remainder = (vat_base_sum + 55 +
|
21
|
+
old_format_remainder = (vat_base_sum + checksum).modulo(97)
|
22
|
+
new_format_remainder = (vat_base_sum + 55 + checksum).modulo(97)
|
24
23
|
|
25
24
|
return false if old_format_remainder == 0 &&
|
26
|
-
OLD_FORMAT_FORBIDDEN_RANGES.any? { |range| range.include?
|
25
|
+
OLD_FORMAT_FORBIDDEN_RANGES.any? { |range| range.include? vat_base_int }
|
27
26
|
|
28
27
|
return false if new_format_remainder == 0 &&
|
29
|
-
NEW_FORMAT_FORBIDDEN_RANGES.any? { |range| range.include?
|
28
|
+
NEW_FORMAT_FORBIDDEN_RANGES.any? { |range| range.include? vat_base_int }
|
30
29
|
|
31
30
|
old_format_remainder == 0 || new_format_remainder == 0
|
32
31
|
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def gov_or_health?(vat_number)
|
36
|
+
vat_number =~ GOV_NUMBER || vat_number =~ HEALTH_NUMBER
|
37
|
+
end
|
38
|
+
|
39
|
+
def all_zero?(vat_number)
|
40
|
+
vat_number =~ /\A0{9}\Z/ || vat_number =~ /\A0{12}\Z/
|
41
|
+
end
|
42
|
+
|
43
|
+
def generate_vat_base_sum(vat_base)
|
44
|
+
vat_base.split('').
|
45
|
+
map(&:to_i).
|
46
|
+
zip([8, 7, 6, 5, 4, 3, 2]).
|
47
|
+
map { |vat_number_digit, multiplier| vat_number_digit * multiplier }.
|
48
|
+
inject(:+)
|
49
|
+
end
|
33
50
|
end
|
34
51
|
end
|
35
52
|
end
|
data/lib/valvat/checksum/ie.rb
CHANGED
data/lib/valvat/checksum/nl.rb
CHANGED
data/lib/valvat/checksum/si.rb
CHANGED
data/lib/valvat/error.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
class Valvat
|
2
|
+
Error = Class.new(RuntimeError)
|
3
|
+
|
4
|
+
class ViesError < Error
|
5
|
+
def initialize(faultstring='UNKNOWN')
|
6
|
+
@faultstring = faultstring
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
"The VIES web service returned the error '#{@faultstring}'."
|
11
|
+
end
|
12
|
+
|
13
|
+
def eql?(other)
|
14
|
+
to_s.eql?(other.to_s)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
ViesMaintenanceError = Class.new(ViesError)
|
18
|
+
|
19
|
+
ServiceUnavailable = Class.new(ViesMaintenanceError)
|
20
|
+
MemberStateUnavailable = Class.new(ViesMaintenanceError)
|
21
|
+
|
22
|
+
Timeout = Class.new(ViesError)
|
23
|
+
InvalidRequester = Class.new(ViesError)
|
24
|
+
BlockedError = Class.new(ViesError)
|
25
|
+
RateLimitError = Class.new(ViesError)
|
26
|
+
|
27
|
+
UnknownViesError = Class.new(ViesError)
|
28
|
+
end
|
data/lib/valvat/local.rb
CHANGED
@@ -22,8 +22,9 @@ class Valvat
|
|
22
22
|
Valvat::Utils.vat_country_to_iso_country(vat_country_code)
|
23
23
|
end
|
24
24
|
|
25
|
+
# TODO: Remove method / not in use
|
25
26
|
def european?
|
26
|
-
Valvat::Utils::
|
27
|
+
Valvat::Utils::EU_MEMBER_STATES.include?(iso_country_code)
|
27
28
|
end
|
28
29
|
|
29
30
|
def to_a
|
data/lib/valvat/locales/en.yml
CHANGED
data/lib/valvat/locales/fr.yml
CHANGED
@@ -5,29 +5,29 @@ fr:
|
|
5
5
|
valvat:
|
6
6
|
country_adjectives:
|
7
7
|
eu: intracommunautaire
|
8
|
-
at:
|
8
|
+
at: autrichien
|
9
9
|
be: belge
|
10
10
|
bg: bulgare
|
11
11
|
cy: chypriote
|
12
12
|
cz: tchèque
|
13
|
-
de:
|
14
|
-
dk:
|
13
|
+
de: allemand
|
14
|
+
dk: danois
|
15
15
|
ee: estonien
|
16
|
-
es:
|
17
|
-
fi:
|
18
|
-
fr:
|
16
|
+
es: espagnol
|
17
|
+
fi: finlandais
|
18
|
+
fr: français
|
19
19
|
gb: britannique
|
20
|
-
gr:
|
21
|
-
ie:
|
22
|
-
it:
|
23
|
-
lt:
|
24
|
-
lu:
|
25
|
-
lv:
|
20
|
+
gr: grec
|
21
|
+
ie: irlandais
|
22
|
+
it: italien
|
23
|
+
lt: lituanien
|
24
|
+
lu: luxembourgeois
|
25
|
+
lv: letton
|
26
26
|
mt: maltais
|
27
|
-
nl:
|
28
|
-
pl:
|
29
|
-
pt:
|
30
|
-
ro:
|
31
|
-
se:
|
27
|
+
nl: hollandais
|
28
|
+
pl: polonais
|
29
|
+
pt: portugais
|
30
|
+
ro: roumain
|
31
|
+
se: suédois
|
32
32
|
si: slovène
|
33
33
|
sk: slovaque
|
data/lib/valvat/locales/pt.yml
CHANGED
@@ -4,31 +4,31 @@ pt:
|
|
4
4
|
invalid_vat: O NIF %{country_adjective} não é válido.
|
5
5
|
valvat:
|
6
6
|
country_adjectives:
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
7
|
+
eu: europeu
|
8
|
+
at: austríaco
|
9
|
+
be: belga
|
10
|
+
bg: búlgaro
|
11
|
+
cy: cipriota
|
12
|
+
cz: checo
|
13
|
+
de: alemão
|
14
|
+
dk: dinamarquês
|
15
|
+
ee: estónio
|
16
|
+
es: espanhol
|
17
|
+
fi: finlandês
|
18
|
+
fr: francês
|
19
|
+
gb: britânico
|
20
|
+
gr: grego
|
21
|
+
hu: húngaro
|
22
|
+
ie: irlandês
|
23
|
+
it: italiano
|
24
|
+
lt: lituano
|
25
|
+
lu: luxemburguês
|
26
|
+
lv: letão
|
27
|
+
mt: maltês
|
28
|
+
nl: holandês
|
29
|
+
pl: polaco
|
30
|
+
pt: português
|
31
|
+
ro: romeno
|
32
|
+
se: sueco
|
33
|
+
si: esloveno
|
34
|
+
sk: eslovaco
|
data/lib/valvat/lookup.rb
CHANGED
@@ -1,36 +1,23 @@
|
|
1
|
-
require 'savon'
|
2
|
-
|
3
1
|
class Valvat
|
4
2
|
class Lookup
|
5
|
-
VIES_WSDL_URL = 'http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl'
|
6
|
-
REMOVE_KEYS = [:valid, :@xmlns]
|
7
|
-
|
8
|
-
attr_reader :vat, :options
|
9
3
|
|
10
4
|
def initialize(vat, options={})
|
11
5
|
@vat = Valvat(vat)
|
12
6
|
@options = options || {}
|
13
|
-
@options[:
|
7
|
+
@options[:requester] ||= @options[:requester_vat]
|
14
8
|
end
|
15
9
|
|
16
10
|
def validate
|
17
|
-
return false
|
11
|
+
return false if !@options[:skip_local_validation] && !@vat.valid?
|
12
|
+
return handle_vies_error(response[:error]) if response[:error]
|
18
13
|
|
19
|
-
valid
|
20
|
-
rescue => error
|
21
|
-
return false if invalid_input?(error)
|
22
|
-
raise error if options[:raise_error]
|
23
|
-
nil
|
14
|
+
response[:valid] && show_details? ? response.to_hash : response[:valid]
|
24
15
|
end
|
25
16
|
|
26
17
|
class << self
|
27
18
|
def validate(vat, options={})
|
28
19
|
new(vat, options).validate
|
29
20
|
end
|
30
|
-
|
31
|
-
def client
|
32
|
-
@client ||= Savon::Client.new(wsdl: VIES_WSDL_URL, log: false)
|
33
|
-
end
|
34
21
|
end
|
35
22
|
|
36
23
|
private
|
@@ -40,37 +27,18 @@ class Valvat
|
|
40
27
|
end
|
41
28
|
|
42
29
|
def response
|
43
|
-
@response ||=
|
44
|
-
end
|
45
|
-
|
46
|
-
def request
|
47
|
-
if requester_vat
|
48
|
-
RequestWithId.new(vat, requester_vat)
|
49
|
-
else
|
50
|
-
Request.new(vat)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def requester_vat
|
55
|
-
options[:requester_vat]
|
56
|
-
end
|
57
|
-
|
58
|
-
def invalid_input?(err)
|
59
|
-
return if !err.respond_to?(:to_hash) || !err.to_hash[:fault]
|
60
|
-
(err.to_hash[:fault][:faultstring] || "").upcase =~ /INVALID_INPUT/
|
30
|
+
@response ||= Request.new(@vat, @options).perform
|
61
31
|
end
|
62
32
|
|
63
33
|
def show_details?
|
64
|
-
|
34
|
+
@options[:requester] || @options[:detail]
|
65
35
|
end
|
66
36
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
73
|
-
hash
|
37
|
+
def handle_vies_error(error)
|
38
|
+
if ViesMaintenanceError === error
|
39
|
+
raise error if @options[:raise_error]
|
40
|
+
else
|
41
|
+
raise error unless @options[:raise_error] == false
|
74
42
|
end
|
75
43
|
end
|
76
44
|
end
|