valvat 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,34 +1,34 @@
1
1
  pt:
2
2
  errors:
3
3
  messages:
4
- invalid_vat: IVA inválido para %{country_adjective}
4
+ invalid_vat: não é um NIF %{country_adjective} válido.
5
5
  valvat:
6
6
  country_adjectives:
7
- eu: Europa
8
- at: Áustria
9
- be: Bélgica
10
- bg: Bulgária
11
- cy: Chipre
12
- cz: República Checa
13
- de: Alemanha
14
- dk: Dinamarca
15
- ee: Estónia
16
- es: Espanha
17
- fi: Finlândia
18
- fr: França
19
- gb: Reino Unido
20
- gr: Grécia
21
- hu: Hungria
22
- ie: Irlanda
23
- it: Itália
24
- lt: Lituânia
25
- lu: Luxemburgo
26
- lv: Letónia
27
- mt: Malta
28
- nl: Holanda
29
- pl: Polónia
30
- pt: Portugal
31
- ro: Roménia
32
- se: Suécia
33
- si: Eslovénia
34
- sk: Eslováquia
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
@@ -3,52 +3,79 @@ require 'net/http'
3
3
  require 'yaml'
4
4
 
5
5
  class Valvat
6
- module Lookup
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 self.validate(vat, options={})
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
- request = options[:requester_vat] ?
13
- Valvat::Lookup::RequestWithId.new(vat, Valvat(options[:requester_vat])) :
14
- Valvat::Lookup::Request.new(vat)
15
-
16
- begin
17
- response = request.perform(self.client)
18
- response[:valid] && (options[:detail] || options[:requester_vat]) ?
19
- filter_detail(response) : response[:valid]
20
- rescue => err
21
- if err.respond_to?(:to_hash) && err.to_hash[:fault] && (err.to_hash[:fault][:faultstring] || "").upcase =~ /INVALID_INPUT/
22
- return false
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
- def self.client
30
- @client ||= begin
31
- # Require Savon only if really needed!
32
- require 'savon' unless defined?(Savon)
47
+ private
33
48
 
34
- # Quiet down HTTPI
35
- HTTPI.log = false
49
+ def valid?
50
+ response[:valid]
51
+ end
52
+
53
+ def response
54
+ @response ||= request.perform(self.class.client)
55
+ end
36
56
 
37
- Savon::Client.new(
38
- wsdl: 'http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl',
39
- namespace: 'urn:ec.europa.eu:taxud:vies:services:checkVat:types',
40
- namespaces: {'xmlns:impl'=>'urn:ec.europa.eu:taxud:vies:services:checkVat:types'},
41
- # Quiet down Savon
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
- private
65
+ def requester_vat
66
+ options[:requester_vat]
67
+ end
48
68
 
49
- REMOVE_KEYS = [:valid, :@xmlns]
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 self.filter_detail(response)
78
+ def response_details
52
79
  response.inject({}) do |hash, kv|
53
80
  key, value = kv
54
81
  unless REMOVE_KEYS.include?(key)
@@ -1,5 +1,5 @@
1
1
  class Valvat
2
- module Lookup
2
+ class Lookup
3
3
  class Request
4
4
  def initialize(vat)
5
5
  @vat = vat
@@ -1,5 +1,5 @@
1
1
  class Valvat
2
- module Lookup
2
+ class Lookup
3
3
  class RequestWithId < Request
4
4
  def initialize(vat, requester_vat)
5
5
  @vat = vat
@@ -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/, # Ireland
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
@@ -1,3 +1,3 @@
1
1
  class Valvat
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -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