valvat 0.8.1 → 1.0.1
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/.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
|