eurovat 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Phusion
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
20
+
@@ -0,0 +1,145 @@
1
+ require 'thread'
2
+ begin
3
+ require 'soap/wsdlDriver'
4
+ rescue LoadError
5
+ require 'savon'
6
+ end
7
+
8
+ class Eurovat
9
+ SERVICE_URL = 'http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl'.freeze
10
+ VAT_FORMAT = /\A([A-Z]{2})([0-9A-Za-z\+\*\.]{2,12})\Z/.freeze
11
+
12
+ # Names must be consistent with the country_select plugin. If you know
13
+ # alternative country spellings please add them here.
14
+ EU_MEMBER_STATES = [
15
+ 'Austria',
16
+ 'Belgium',
17
+ 'Bulgaria',
18
+ 'Cyprus',
19
+ 'Czech Republic',
20
+ 'Denmark',
21
+ 'Estonia',
22
+ 'Finland',
23
+ 'France',
24
+ 'Germany',
25
+ 'Greece',
26
+ 'Hungary',
27
+ 'Ireland',
28
+ 'Italy',
29
+ 'Latvia',
30
+ 'Lithuania',
31
+ 'Luxembourg',
32
+ 'Malta',
33
+ 'Netherlands',
34
+ 'Poland',
35
+ 'Portugal',
36
+ 'Romania',
37
+ 'Slovakia',
38
+ 'Slovenia',
39
+ 'Spain',
40
+ 'Sweden',
41
+ 'United Kingdom'
42
+ ].freeze
43
+
44
+ class InvalidFormatError < StandardError
45
+ end
46
+
47
+ @@country = 'Netherlands'
48
+ @@instance = nil
49
+
50
+ def self.country
51
+ @@country
52
+ end
53
+
54
+ def self.country=(val)
55
+ @@country = country
56
+ end
57
+
58
+ def self.must_charge_vat?(customer_country, vat_number)
59
+ # http://www.belastingdienst.nl/reken/diensten_in_en_uit_het_buitenland/
60
+ if customer_country == @@country
61
+ true
62
+ else
63
+ if present?(vat_number)
64
+ false
65
+ else
66
+ EU_MEMBER_STATES.include?(customer_country)
67
+ end
68
+ end
69
+ end
70
+
71
+ def self.check_vat_number(vat_number)
72
+ @@instance ||= new
73
+ @@instance.check_vat_number(vat_number)
74
+ end
75
+
76
+ def initialize
77
+ @country = @@country
78
+ @mutex = Mutex.new
79
+ if defined?(SOAP)
80
+ @driver = SOAP::WSDLDriverFactory.new(SERVICE_URL).create_rpc_driver
81
+ else
82
+ @driver = Savon::Client.new(SERVICE_URL)
83
+ end
84
+ end
85
+
86
+ # Any exception other than InvalidFormatError indicates that the service is down.
87
+ def check_vat_number(vat_number)
88
+ vat_number = sanitize_vat_number(vat_number)
89
+ if vat_number =~ VAT_FORMAT
90
+ country_code = $1
91
+ number = $2
92
+ if @driver.respond_to?(:checkVat)
93
+ @mutex.synchronize do
94
+ begin
95
+ result = @driver.checkVat(
96
+ :countryCode => country_code,
97
+ :vatNumber => number)
98
+ result.valid == "true"
99
+ rescue SOAP::FaultError => e
100
+ if e.message == "INVALID_INPUT"
101
+ raise InvalidFormatError, "#{vat_number.inspect} is not formatted like a valid VAT number"
102
+ else
103
+ raise e
104
+ end
105
+ end
106
+ end
107
+ else
108
+ @mutex.synchronize do
109
+ begin
110
+ result = @driver.request(:checkVat) do
111
+ soap.body = {
112
+ :countryCode => country_code,
113
+ :vatNumber => number
114
+ }
115
+ end
116
+ result[:check_vat_response][:valid]
117
+ rescue Savon::SOAP::Fault => e
118
+ if e.message =~ /INVALID_INPUT/
119
+ raise InvalidFormatError, "#{vat_number.inspect} is not formatted like a valid VAT number"
120
+ else
121
+ raise e
122
+ end
123
+ end
124
+ end
125
+ end
126
+ else
127
+ raise InvalidFormatError, "#{vat_number.inspect} is not formatted like a valid VAT number"
128
+ end
129
+ end
130
+
131
+ private
132
+ def self.present?(val)
133
+ if val.nil?
134
+ false
135
+ elsif val.is_a?(String)
136
+ !val.empty?
137
+ else
138
+ !!val
139
+ end
140
+ end
141
+
142
+ def sanitize_vat_number(vat_number)
143
+ vat_number.gsub(/[\s\t\.]/, '').upcase
144
+ end
145
+ end
@@ -0,0 +1,3 @@
1
+ class Eurovat
2
+ VERSION_STRING = '1.0.0'
3
+ end
@@ -0,0 +1,43 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
2
+ require 'rubygems'
3
+ require 'eurovat'
4
+
5
+ describe Eurovat do
6
+ # NL819225642B01 is Phusion's VAT number. If we ever go out of business replace
7
+ # it with any other valid VAT number to have the unit tests pass.
8
+
9
+ it "works" do
10
+ @eurovat = Eurovat.new
11
+ @eurovat.check_vat_number('NL819225642B01').should be_true
12
+ @eurovat.check_vat_number('NL010101010B99').should be_false
13
+ end
14
+
15
+ it "strips away spaces and dots" do
16
+ @eurovat = Eurovat.new
17
+ @eurovat.check_vat_number('nl8192.25 642.b01').should be_true
18
+ end
19
+
20
+ it "raises InvalidFormatError if the VAT number is not formatted like one" do
21
+ @eurovat = Eurovat.new
22
+ lambda { @eurovat.check_vat_number('foobar') }.should raise_error(Eurovat::InvalidFormatError)
23
+ lambda { @eurovat.check_vat_number('foobar!') }.should raise_error(Eurovat::InvalidFormatError)
24
+ end
25
+
26
+ it "always charges VAT if the customer is from one's own country" do
27
+ Eurovat.must_charge_vat?('Netherlands', nil).should be_true
28
+ Eurovat.must_charge_vat?('Netherlands', 'NL819225642B01').should be_true
29
+ end
30
+
31
+ it "doesn't charge VAT if the customer is outside one's own country and supplied a VAT number" do
32
+ Eurovat.must_charge_vat?('Germany', 'NL819225642B01').should be_false
33
+ Eurovat.must_charge_vat?('United States', 'NL819225642B01').should be_false
34
+ end
35
+
36
+ it "charges VAT if the customer is outside one's own country, but inside the EU, and didn't supply a VAT number" do
37
+ Eurovat.must_charge_vat?('Germany', nil).should be_true
38
+ end
39
+
40
+ it "doesn't charge VAT if the customer is outside one's own country, outside the EU, and didn't supply a VAT number" do
41
+ Eurovat.must_charge_vat?('United States', nil).should be_false
42
+ end
43
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eurovat
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Hongli Lai
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-06-21 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: A utility library for dealing with European Union VAT numbers
22
+ email: info@phusion.nl
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - LICENSE.txt
31
+ - lib/eurovat/version.rb
32
+ - lib/eurovat.rb
33
+ - test/eurovat_spec.rb
34
+ homepage: https://github.com/phusion/eurovat
35
+ licenses: []
36
+
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ hash: 3
48
+ segments:
49
+ - 0
50
+ version: "0"
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.8.15
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: European Union VAT number utilities
67
+ test_files: []
68
+
69
+ has_rdoc: