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.
@@ -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