valvat 0.5.0 → 0.6.0
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
- data/.travis.yml +8 -0
- data/CHANGES.md +11 -1
- data/README.md +13 -7
- data/gemfiles/activemodel-3-2 +8 -0
- data/gemfiles/activemodel-4 +8 -0
- data/gemfiles/standalone +7 -0
- data/lib/active_model/validations/valvat_validator.rb +54 -0
- data/lib/valvat.rb +1 -1
- data/lib/valvat/checksum.rb +24 -3
- data/lib/valvat/checksum/at.rb +1 -3
- data/lib/valvat/checksum/bg.rb +10 -10
- data/lib/valvat/checksum/es.rb +12 -9
- data/lib/valvat/checksum/gr.rb +2 -2
- data/lib/valvat/checksum/ie.rb +2 -2
- data/lib/valvat/checksum/it.rb +1 -4
- data/lib/valvat/checksum/nl.rb +2 -2
- data/lib/valvat/checksum/pt.rb +1 -3
- data/lib/valvat/checksum/se.rb +1 -3
- data/lib/valvat/checksum/si.rb +1 -3
- data/lib/valvat/locales/de.yml +1 -1
- data/lib/valvat/locales/pt.yml +29 -29
- data/lib/valvat/lookup.rb +59 -32
- data/lib/valvat/lookup/request.rb +1 -1
- data/lib/valvat/lookup/request_with_id.rb +1 -1
- data/lib/valvat/syntax.rb +1 -1
- data/lib/valvat/version.rb +1 -1
- data/spec/active_model/validations/valvat_validator_spec.rb +242 -0
- data/spec/spec_helper.rb +16 -10
- data/spec/valvat/lookup_spec.rb +38 -27
- data/spec/valvat/syntax_spec.rb +3 -0
- data/valvat.gemspec +1 -1
- metadata +11 -7
- data/lib/valvat/active_model.rb +0 -45
- data/spec/valvat/active_model_spec.rb +0 -240
data/lib/valvat/locales/pt.yml
CHANGED
@@ -1,34 +1,34 @@
|
|
1
1
|
pt:
|
2
2
|
errors:
|
3
3
|
messages:
|
4
|
-
invalid_vat:
|
4
|
+
invalid_vat: não é um NIF %{country_adjective} 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: luxemburguese
|
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
@@ -3,52 +3,79 @@ require 'net/http'
|
|
3
3
|
require 'yaml'
|
4
4
|
|
5
5
|
class Valvat
|
6
|
-
|
6
|
+
class Lookup
|
7
|
+
VIES_WSDL_URL = 'http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl'
|
8
|
+
REMOVE_KEYS = [:valid, :@xmlns]
|
9
|
+
|
10
|
+
attr_reader :vat, :options
|
7
11
|
|
8
|
-
def
|
9
|
-
vat = Valvat(vat)
|
12
|
+
def initialize(vat, options={})
|
13
|
+
@vat = Valvat(vat)
|
14
|
+
@options = options || {}
|
15
|
+
@options[:requester_vat] = Valvat(requester_vat) if requester_vat
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate
|
10
19
|
return false unless vat.european?
|
11
20
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
valid? && show_details? ? response_details : valid?
|
22
|
+
rescue => error
|
23
|
+
return false if invalid_input?(error)
|
24
|
+
raise error if options[:raise_error]
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
def validate(vat, options={})
|
30
|
+
new(vat, options).validate
|
31
|
+
end
|
32
|
+
|
33
|
+
def client
|
34
|
+
@client ||= begin
|
35
|
+
# Require Savon only if really needed!
|
36
|
+
require 'savon' unless defined?(Savon)
|
37
|
+
|
38
|
+
Savon::Client.new(
|
39
|
+
wsdl: VIES_WSDL_URL,
|
40
|
+
# Quiet down Savon and HTTPI
|
41
|
+
log: false
|
42
|
+
)
|
23
43
|
end
|
24
|
-
raise err if options[:raise_error]
|
25
|
-
nil
|
26
44
|
end
|
27
45
|
end
|
28
46
|
|
29
|
-
|
30
|
-
@client ||= begin
|
31
|
-
# Require Savon only if really needed!
|
32
|
-
require 'savon' unless defined?(Savon)
|
47
|
+
private
|
33
48
|
|
34
|
-
|
35
|
-
|
49
|
+
def valid?
|
50
|
+
response[:valid]
|
51
|
+
end
|
52
|
+
|
53
|
+
def response
|
54
|
+
@response ||= request.perform(self.class.client)
|
55
|
+
end
|
36
56
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
log: false
|
43
|
-
)
|
57
|
+
def request
|
58
|
+
if requester_vat
|
59
|
+
RequestWithId.new(vat, requester_vat)
|
60
|
+
else
|
61
|
+
Request.new(vat)
|
44
62
|
end
|
45
63
|
end
|
46
64
|
|
47
|
-
|
65
|
+
def requester_vat
|
66
|
+
options[:requester_vat]
|
67
|
+
end
|
48
68
|
|
49
|
-
|
69
|
+
def invalid_input?(err)
|
70
|
+
return if !err.respond_to?(:to_hash) || !err.to_hash[:fault]
|
71
|
+
(err.to_hash[:fault][:faultstring] || "").upcase =~ /INVALID_INPUT/
|
72
|
+
end
|
73
|
+
|
74
|
+
def show_details?
|
75
|
+
requester_vat || options[:detail]
|
76
|
+
end
|
50
77
|
|
51
|
-
def
|
78
|
+
def response_details
|
52
79
|
response.inject({}) do |hash, kv|
|
53
80
|
key, value = kv
|
54
81
|
unless REMOVE_KEYS.include?(key)
|
data/lib/valvat/syntax.rb
CHANGED
@@ -19,7 +19,7 @@ class Valvat
|
|
19
19
|
'GB' => /\AGB([0-9]{9}|[0-9]{12}|(HA|GD)[0-9]{3})\Z/, # United Kingdom
|
20
20
|
'HR' => /\AHR[0-9]{11}\Z/, # Croatia
|
21
21
|
'HU' => /\AHU[0-9]{8}\Z/, # Hungary
|
22
|
-
'IE' => /\AIE([0-9][A-Z][0-9]{5}|[0-9]{7})[A-Z]\Z/,
|
22
|
+
'IE' => /\AIE([0-9][A-Z][0-9]{5}|[0-9]{7}[A-Z]?)[A-Z]\Z/, # Ireland
|
23
23
|
'IT' => /\AIT[0-9]{11}\Z/, # Italy
|
24
24
|
'LT' => /\ALT([0-9]{9}|[0-9]{12})\Z/, # Lithuania
|
25
25
|
'LU' => /\ALU[0-9]{8}\Z/, # Luxembourg
|
data/lib/valvat/version.rb
CHANGED
@@ -0,0 +1,242 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
if defined?(ActiveModel)
|
4
|
+
class Invoice < ModelBase
|
5
|
+
validates :vat_number, :valvat => true
|
6
|
+
end
|
7
|
+
|
8
|
+
class InvoiceWithLookup < ModelBase
|
9
|
+
validates :vat_number, :valvat => {:lookup => true}
|
10
|
+
end
|
11
|
+
|
12
|
+
class InvoiceWithLookupAndFailIfDown < ModelBase
|
13
|
+
validates :vat_number, :valvat => {:lookup => :fail_if_down}
|
14
|
+
end
|
15
|
+
|
16
|
+
class InvoiceAllowBlank < ModelBase
|
17
|
+
validates :vat_number, :valvat => {:allow_blank => true}
|
18
|
+
end
|
19
|
+
|
20
|
+
class InvoiceAllowBlankOnAll < ModelBase
|
21
|
+
validates :vat_number, :valvat => true, :allow_blank => true
|
22
|
+
end
|
23
|
+
|
24
|
+
class InvoiceCheckCountry < ModelBase
|
25
|
+
validates :vat_number, :valvat => {:match_country => :country}
|
26
|
+
|
27
|
+
def country
|
28
|
+
@attributes[:country]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class InvoiceCheckCountryWithLookup < ModelBase
|
33
|
+
validates :vat_number, :valvat => {:match_country => :country, :lookup => true}
|
34
|
+
|
35
|
+
def country
|
36
|
+
@attributes[:country]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class InvoiceWithChecksum < ModelBase
|
41
|
+
validates :vat_number, :valvat => {:checksum => true}
|
42
|
+
end
|
43
|
+
|
44
|
+
describe Invoice do
|
45
|
+
context "with valid vat number" do
|
46
|
+
it "should be valid" do
|
47
|
+
Invoice.new(:vat_number => "DE259597697").should be_valid
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "with invalid vat number" do
|
52
|
+
let(:invoice) { Invoice.new(:vat_number => "DE259597697123") }
|
53
|
+
|
54
|
+
it "should not be valid" do
|
55
|
+
invoice.should_not be_valid
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should add default (country specific) error message" do
|
59
|
+
invoice.valid?
|
60
|
+
invoice.errors[:vat_number].should eql(["is not a valid German vat number"])
|
61
|
+
end
|
62
|
+
|
63
|
+
context "with i18n translation in place" do
|
64
|
+
before do
|
65
|
+
I18n.backend.store_translations(:en, :activemodel => {
|
66
|
+
:errors => {:models => {:invoice => {:invalid_vat => "is ugly."}}}
|
67
|
+
})
|
68
|
+
end
|
69
|
+
|
70
|
+
after { I18n.reload! }
|
71
|
+
|
72
|
+
it "should use translation" do
|
73
|
+
invoice.valid?
|
74
|
+
invoice.errors[:vat_number].should eql(["is ugly."])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "with i18n translation with country adjective placeholder in place" do
|
79
|
+
before do
|
80
|
+
I18n.backend.store_translations(:en, :activemodel => {
|
81
|
+
:errors => {:models => {:invoice => {:invalid_vat => "is not a %{country_adjective} vat"}}}
|
82
|
+
})
|
83
|
+
end
|
84
|
+
|
85
|
+
after { I18n.reload! }
|
86
|
+
|
87
|
+
it "should replace country adjective placeholder" do
|
88
|
+
invoice = Invoice.new(:vat_number => "IE123")
|
89
|
+
invoice.valid?
|
90
|
+
invoice.errors[:vat_number].should eql(["is not a Irish vat"])
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should fall back to 'European' if country is missing" do
|
94
|
+
invoice = Invoice.new(:vat_number => "XX123")
|
95
|
+
invoice.valid?
|
96
|
+
invoice.errors[:vat_number].should eql(["is not a European vat"])
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "with blank vat number" do
|
102
|
+
it "should not be valid" do
|
103
|
+
Invoice.new(:vat_number => "").should_not be_valid
|
104
|
+
Invoice.new(:vat_number => nil).should_not be_valid
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe InvoiceWithLookup do
|
110
|
+
context "with valid but not existing vat number" do
|
111
|
+
before do
|
112
|
+
Valvat::Syntax.stub(:validate => true)
|
113
|
+
Valvat::Lookup.stub(:validate => false)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should not be valid" do
|
117
|
+
InvoiceWithLookup.new(:vat_number => "DE123").should_not be_valid
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context "with valid and existing vat number" do
|
122
|
+
before do
|
123
|
+
Valvat::Syntax.stub(:validate => true)
|
124
|
+
Valvat::Lookup.stub(:validate => true)
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should be valid" do
|
128
|
+
InvoiceWithLookup.new(:vat_number => "DE123").should be_valid
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context "with valid vat number and VIES country service down" do
|
133
|
+
before do
|
134
|
+
Valvat::Syntax.stub(:validate => true)
|
135
|
+
Valvat::Lookup.stub(:validate => nil)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should be valid" do
|
139
|
+
InvoiceWithLookup.new(:vat_number => "DE123").should be_valid
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe InvoiceWithLookupAndFailIfDown do
|
145
|
+
context "with valid vat number and VIES country service down" do
|
146
|
+
before do
|
147
|
+
Valvat::Syntax.stub(:validate => true)
|
148
|
+
Valvat::Lookup.stub(:validate => nil)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should not be valid" do
|
152
|
+
InvoiceWithLookupAndFailIfDown.new(:vat_number => "DE123").should_not be_valid
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe InvoiceAllowBlank do
|
158
|
+
context "with blank vat number" do
|
159
|
+
it "should be valid" do
|
160
|
+
InvoiceAllowBlank.new(:vat_number => "").should be_valid
|
161
|
+
InvoiceAllowBlank.new(:vat_number => nil).should be_valid
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe InvoiceAllowBlankOnAll do
|
167
|
+
context "with blank vat number" do
|
168
|
+
it "should be valid" do
|
169
|
+
InvoiceAllowBlankOnAll.new(:vat_number => "").should be_valid
|
170
|
+
InvoiceAllowBlankOnAll.new(:vat_number => nil).should be_valid
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe InvoiceCheckCountry do
|
176
|
+
it "should be not valid on blank country" do
|
177
|
+
InvoiceCheckCountry.new(:country => nil, :vat_number => "DE259597697").should_not be_valid
|
178
|
+
InvoiceCheckCountry.new(:country => "", :vat_number => "DE259597697").should_not be_valid
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should be not valid on wired country" do
|
182
|
+
InvoiceCheckCountry.new(:country => "XAXXX", :vat_number => "DE259597697").should_not be_valid
|
183
|
+
InvoiceCheckCountry.new(:country => "ZO", :vat_number => "DE259597697").should_not be_valid
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should be not valid on mismatching (eu) country" do
|
187
|
+
InvoiceCheckCountry.new(:country => "FR", :vat_number => "DE259597697").should_not be_valid
|
188
|
+
InvoiceCheckCountry.new(:country => "AT", :vat_number => "DE259597697").should_not be_valid
|
189
|
+
InvoiceCheckCountry.new(:country => "DE", :vat_number => "ATU65931334").should_not be_valid
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should be valid on matching country" do
|
193
|
+
InvoiceCheckCountry.new(:country => "DE", :vat_number => "DE259597697").should be_valid
|
194
|
+
InvoiceCheckCountry.new(:country => "AT", :vat_number => "ATU65931334").should be_valid
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should give back error message with country from :country_match" do
|
198
|
+
invoice = InvoiceCheckCountry.new(:country => "FR", :vat_number => "DE259597697")
|
199
|
+
invoice.valid?
|
200
|
+
invoice.errors[:vat_number].should eql(["is not a valid French vat number"])
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should give back error message with country from :country_match even on invalid vat number" do
|
204
|
+
invoice = InvoiceCheckCountry.new(:country => "FR", :vat_number => "DE259597697123")
|
205
|
+
invoice.valid?
|
206
|
+
invoice.errors[:vat_number].should eql(["is not a valid French vat number"])
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe InvoiceCheckCountryWithLookup do
|
211
|
+
before do
|
212
|
+
Valvat::Syntax.stub(:validate => true)
|
213
|
+
Valvat::Lookup.stub(:validate => true)
|
214
|
+
end
|
215
|
+
|
216
|
+
it "avoids lookup or syntax check on failed because of mismatching country" do
|
217
|
+
Valvat::Syntax.should_not_receive(:validate)
|
218
|
+
Valvat::Lookup.should_not_receive(:validate)
|
219
|
+
InvoiceCheckCountryWithLookup.new(:country => "FR", :vat_number => "DE259597697").valid?
|
220
|
+
end
|
221
|
+
|
222
|
+
it "check syntax and looup on matching country" do
|
223
|
+
Valvat::Syntax.should_receive(:validate).and_return(true)
|
224
|
+
Valvat::Lookup.should_receive(:validate).and_return(true)
|
225
|
+
InvoiceCheckCountryWithLookup.new(:country => "DE", :vat_number => "DE259597697").valid?
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe InvoiceWithChecksum do
|
230
|
+
context "with valid vat number" do
|
231
|
+
it "should be valid" do
|
232
|
+
InvoiceWithChecksum.new(:vat_number => "DE259597697").should be_valid
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context "with invalid vat number" do
|
237
|
+
it "should not be valid" do
|
238
|
+
InvoiceWithChecksum.new(:vat_number => "DE259597687").should_not be_valid
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|