valvat 1.1.4 → 1.2.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/.github/workflows/ruby.yml +3 -3
- data/.rubocop.yml +11 -2
- data/CHANGES.md +19 -2
- data/MIT-LICENSE +1 -1
- data/README.md +151 -80
- data/gemfiles/activemodel-6 +3 -0
- data/gemfiles/activemodel-7 +3 -0
- data/gemfiles/standalone +3 -0
- data/lib/active_model/validations/valvat_validator.rb +1 -1
- data/lib/valvat/checksum/es.rb +49 -16
- data/lib/valvat/checksum/fr.rb +1 -1
- data/lib/valvat/checksum/hr.rb +1 -1
- data/lib/valvat/checksum/hu.rb +1 -1
- data/lib/valvat/error.rb +16 -16
- data/lib/valvat/local.rb +2 -49
- data/lib/valvat/lookup/base.rb +73 -0
- data/lib/valvat/lookup/hmrc.rb +86 -0
- data/lib/valvat/lookup/vies.rb +98 -0
- data/lib/valvat/lookup.rb +16 -5
- data/lib/valvat/utils.rb +0 -1
- data/lib/valvat/version.rb +1 -1
- data/lib/valvat.rb +50 -4
- data/spec/spec_helper.rb +2 -1
- data/spec/valvat/checksum/es_spec.rb +48 -1
- data/spec/valvat/checksum/gb_spec.rb +1 -0
- data/spec/valvat/lookup/hmrc_spec.rb +32 -0
- data/spec/valvat/lookup/vies_spec.rb +23 -0
- data/spec/valvat/lookup_spec.rb +272 -80
- data/spec/valvat/utils_spec.rb +1 -1
- data/spec/valvat_spec.rb +12 -12
- data/valvat.gemspec +3 -2
- data.tar.gz.sig +0 -0
- metadata +24 -25
- metadata.gz.sig +0 -0
- data/lib/valvat/lookup/fault.rb +0 -38
- data/lib/valvat/lookup/request.rb +0 -57
- data/lib/valvat/lookup/response.rb +0 -37
- data/spec/valvat/lookup/fault_spec.rb +0 -34
- data/spec/valvat/lookup/request_spec.rb +0 -32
- data/spec/valvat/lookup/response_spec.rb +0 -29
data/lib/valvat/lookup/fault.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Valvat
|
4
|
-
class Lookup
|
5
|
-
class Fault < Response
|
6
|
-
def to_hash
|
7
|
-
@to_hash ||= case @raw
|
8
|
-
when Savon::HTTPError
|
9
|
-
{ error: HTTPError.new(nil, @raw) }
|
10
|
-
when Savon::UnknownOperationError
|
11
|
-
{ error: OperationUnknown.new(nil, @raw) }
|
12
|
-
else
|
13
|
-
fault = @raw.to_hash[:fault][:faultstring]
|
14
|
-
|
15
|
-
if fault == 'INVALID_INPUT'
|
16
|
-
{ valid: false }
|
17
|
-
else
|
18
|
-
error = (FAULTS[fault] || UnknownViesError).new(fault)
|
19
|
-
{ error: error }
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
FAULTS = {
|
25
|
-
'SERVICE_UNAVAILABLE' => ServiceUnavailable,
|
26
|
-
'MS_UNAVAILABLE' => MemberStateUnavailable,
|
27
|
-
'INVALID_REQUESTER_INFO' => InvalidRequester,
|
28
|
-
'TIMEOUT' => Timeout,
|
29
|
-
'VAT_BLOCKED' => BlockedError,
|
30
|
-
'IP_BLOCKED' => BlockedError,
|
31
|
-
'GLOBAL_MAX_CONCURRENT_REQ' => RateLimitError,
|
32
|
-
'GLOBAL_MAX_CONCURRENT_REQ_TIME' => RateLimitError,
|
33
|
-
'MS_MAX_CONCURRENT_REQ' => RateLimitError,
|
34
|
-
'MS_MAX_CONCURRENT_REQ_TIME' => RateLimitError
|
35
|
-
}.freeze
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'savon'
|
4
|
-
|
5
|
-
class Valvat
|
6
|
-
class Lookup
|
7
|
-
class Request
|
8
|
-
VIES_WSDL_URL = 'https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl'
|
9
|
-
|
10
|
-
def initialize(vat, options)
|
11
|
-
@vat = Valvat(vat)
|
12
|
-
@options = options || {}
|
13
|
-
@requester = @options[:requester] && Valvat(@options[:requester])
|
14
|
-
end
|
15
|
-
|
16
|
-
def perform
|
17
|
-
Response.new(
|
18
|
-
client.call(action, message: message, message_tag: message_tag, soap_action: nil)
|
19
|
-
)
|
20
|
-
rescue Savon::SOAPFault, Savon::HTTPError, Savon::UnknownOperationError => e
|
21
|
-
Fault.new(e)
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def client
|
27
|
-
Savon::Client.new({
|
28
|
-
wsdl: VIES_WSDL_URL, log: false, follow_redirects: true
|
29
|
-
}.merge(@options[:savon] || {}))
|
30
|
-
end
|
31
|
-
|
32
|
-
def message
|
33
|
-
add_requester({
|
34
|
-
country_code: @vat.vat_country_code,
|
35
|
-
vat_number: @vat.to_s_wo_country
|
36
|
-
})
|
37
|
-
end
|
38
|
-
|
39
|
-
def message_tag
|
40
|
-
@requester ? :checkVatApprox : :checkVat
|
41
|
-
end
|
42
|
-
|
43
|
-
def action
|
44
|
-
@requester ? :check_vat_approx : :check_vat
|
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
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Valvat
|
4
|
-
class Lookup
|
5
|
-
class Response
|
6
|
-
def initialize(raw)
|
7
|
-
@raw = raw
|
8
|
-
end
|
9
|
-
|
10
|
-
def [](key)
|
11
|
-
to_hash[key]
|
12
|
-
end
|
13
|
-
|
14
|
-
def to_hash
|
15
|
-
@to_hash ||= self.class.cleanup(@raw.to_hash)
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.cleanup(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"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
TRADER_PREFIX = /\Atrader_/.freeze
|
27
|
-
|
28
|
-
def self.cleanup_key(key)
|
29
|
-
key.to_s.sub(TRADER_PREFIX, '').to_sym
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.cleanup_value(value)
|
33
|
-
value == '---' ? nil : value
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Valvat::Lookup::Fault do
|
6
|
-
it "returns {valid: false} on fault 'INVALID_INPUT'" do
|
7
|
-
expect(described_class.new({
|
8
|
-
fault: { faultstring: 'INVALID_INPUT' }
|
9
|
-
}).to_hash).to eql({ valid: false })
|
10
|
-
end
|
11
|
-
|
12
|
-
{
|
13
|
-
'SERVICE_UNAVAILABLE' => Valvat::ServiceUnavailable,
|
14
|
-
'MS_UNAVAILABLE' => Valvat::MemberStateUnavailable,
|
15
|
-
'INVALID_REQUESTER_INFO' => Valvat::InvalidRequester,
|
16
|
-
'TIMEOUT' => Valvat::Timeout,
|
17
|
-
'VAT_BLOCKED' => Valvat::BlockedError,
|
18
|
-
'IP_BLOCKED' => Valvat::BlockedError,
|
19
|
-
'GLOBAL_MAX_CONCURRENT_REQ' => Valvat::RateLimitError,
|
20
|
-
'GLOBAL_MAX_CONCURRENT_REQ_TIME' => Valvat::RateLimitError,
|
21
|
-
'MS_MAX_CONCURRENT_REQ' => Valvat::RateLimitError,
|
22
|
-
'MS_MAX_CONCURRENT_REQ_TIME' => Valvat::RateLimitError,
|
23
|
-
'ANYTHING ELSE' => Valvat::UnknownViesError,
|
24
|
-
'REALLY ANYTHING' => Valvat::UnknownViesError
|
25
|
-
}.each do |fault, error|
|
26
|
-
it "returns error on fault '#{fault}'" do
|
27
|
-
expect(described_class.new({
|
28
|
-
fault: { faultstring: fault }
|
29
|
-
}).to_hash).to eql({
|
30
|
-
error: error.new(fault)
|
31
|
-
})
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Valvat::Lookup::Request do
|
6
|
-
it 'returns Response on success' do
|
7
|
-
response = described_class.new('IE6388047V', {}).perform
|
8
|
-
expect(response).to be_a(Valvat::Lookup::Response)
|
9
|
-
|
10
|
-
# Skip if VIES is down
|
11
|
-
expect(response.to_hash[:name]).to eql('GOOGLE IRELAND LIMITED') unless response.is_a?(Valvat::Lookup::Fault)
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'returns Fault on failure' do
|
15
|
-
response = described_class.new('XC123123', {}).perform
|
16
|
-
expect(response).to be_a(Valvat::Lookup::Fault)
|
17
|
-
expect(response.to_hash).to eql({ valid: false })
|
18
|
-
end
|
19
|
-
|
20
|
-
context 'when Savon::UnknownOperationError is (wrongly) thrown' do
|
21
|
-
before do
|
22
|
-
dbl = double(Savon::Client)
|
23
|
-
allow(Savon::Client).to receive(:new).and_return(dbl)
|
24
|
-
allow(dbl).to receive(:call).and_raise(Savon::UnknownOperationError.new('from stub'))
|
25
|
-
end
|
26
|
-
|
27
|
-
it "does handle it like vies down" do
|
28
|
-
response = described_class.new('IE6388047V', {}).perform
|
29
|
-
expect(response.to_hash[:error]).to be_a(Valvat::OperationUnknown)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Valvat::Lookup::Response do
|
6
|
-
it 'removes @xmlns from :check_vat_response hash' do
|
7
|
-
expect(described_class.new({
|
8
|
-
check_vat_response: { :a => 1, :b => 2, :@xmlns => true }
|
9
|
-
}).to_hash).to eql({ a: 1, b: 2 })
|
10
|
-
end
|
11
|
-
|
12
|
-
it "removes 'trader_'-Prefixes :check_vat_response hash" do
|
13
|
-
expect(described_class.new({
|
14
|
-
check_vat_response: { a: 1, trader_b: 2 }
|
15
|
-
}).to_hash).to eql({ a: 1, b: 2 })
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'accepts hash keyed as :check_vat_approx_response' do
|
19
|
-
expect(described_class.new({
|
20
|
-
check_vat_approx_response: { a: 1, b: 2 }
|
21
|
-
}).to_hash).to eql({ a: 1, b: 2 })
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'allows direct access to hash via []' do
|
25
|
-
expect(described_class.new({
|
26
|
-
check_vat_response: { a: 123, b: 2 }
|
27
|
-
})[:a]).to be(123)
|
28
|
-
end
|
29
|
-
end
|