valvat 0.0.3 → 0.1.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.
- data/CHANGES.md +12 -2
- data/lib/locale/de.yml +34 -0
- data/lib/locale/en.yml +34 -0
- data/lib/valvat/active_model.rb +29 -5
- data/lib/valvat/syntax.rb +1 -7
- data/lib/valvat/utils.rb +5 -1
- data/lib/valvat/version.rb +1 -1
- data/spec/valvat/active_model_spec.rb +135 -2
- data/spec/valvat/syntax_spec.rb +0 -10
- data/spec/valvat/utils_spec.rb +10 -1
- metadata +5 -3
data/CHANGES.md
CHANGED
@@ -1,10 +1,20 @@
|
|
1
1
|
### dev
|
2
2
|
|
3
|
-
[full changelog](http://github.com/yolk/valvat/compare/v0.0
|
3
|
+
[full changelog](http://github.com/yolk/valvat/compare/v0.1.0...master)
|
4
|
+
|
5
|
+
### 0.1.0 / 2011-01-07
|
6
|
+
|
7
|
+
[full changelog](http://github.com/yolk/valvat/compare/v0.0.3...v0.1.0)
|
8
|
+
|
9
|
+
* ActiveModel validation: added optional lookup support
|
10
|
+
* ActiveModel validation: I18n support with country specific messages
|
11
|
+
* ActiveModel validation: I18n locales in english and german
|
12
|
+
* Fixed bug with wrong iso country code on greek vat number (EL != GR)
|
13
|
+
* Valvat::Util.split only returns countries from europe
|
4
14
|
|
5
15
|
### 0.0.3 / 2011-01-06
|
6
16
|
|
7
|
-
[full changelog](http://github.com/yolk/valvat/compare/v0.0.
|
17
|
+
[full changelog](http://github.com/yolk/valvat/compare/v0.0.2...v0.0.3)
|
8
18
|
|
9
19
|
* Basic support for ActiveModel validation
|
10
20
|
|
data/lib/locale/de.yml
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
de:
|
2
|
+
errors:
|
3
|
+
messages:
|
4
|
+
invalid_vat: ist kein gültige %{country_adjective} UStId-Nr.
|
5
|
+
valvat:
|
6
|
+
country_adjectives:
|
7
|
+
eu: europäische
|
8
|
+
at: österreichische
|
9
|
+
be: belgische
|
10
|
+
bg: bulgarische
|
11
|
+
cy: zypriotische
|
12
|
+
cz: tschechische
|
13
|
+
de: deutsche
|
14
|
+
dk: dänische
|
15
|
+
ee: estnisch
|
16
|
+
es: spanisch
|
17
|
+
fi: finnische
|
18
|
+
fr: französische
|
19
|
+
gb: britische
|
20
|
+
gr: griechische
|
21
|
+
hu: ungarische
|
22
|
+
ie: irische
|
23
|
+
it: italienische
|
24
|
+
lt: litauisch
|
25
|
+
lu: luxemburgisch
|
26
|
+
lv: lettische
|
27
|
+
mt: malteser
|
28
|
+
nl: niederländische
|
29
|
+
pl: polnische
|
30
|
+
pt: portugiesische
|
31
|
+
ro: rumänische
|
32
|
+
se: schwedische
|
33
|
+
si: slowenische
|
34
|
+
sk: slowakische
|
data/lib/locale/en.yml
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
en:
|
2
|
+
errors:
|
3
|
+
messages:
|
4
|
+
invalid_vat: is not a valid %{country_adjective} vat number
|
5
|
+
valvat:
|
6
|
+
country_adjectives:
|
7
|
+
eu: european
|
8
|
+
at: austrian
|
9
|
+
be: belgian
|
10
|
+
bg: bulgarian
|
11
|
+
cy: cypriot
|
12
|
+
cz: czech
|
13
|
+
de: german
|
14
|
+
dk: danish
|
15
|
+
ee: estonian
|
16
|
+
es: spanish
|
17
|
+
fi: finnish
|
18
|
+
fr: french
|
19
|
+
gb: british
|
20
|
+
gr: greek
|
21
|
+
hu: hungarian
|
22
|
+
ie: irish
|
23
|
+
it: italian
|
24
|
+
lt: lithuanian
|
25
|
+
lu: luxembourg
|
26
|
+
lv: latvian
|
27
|
+
mt: maltese
|
28
|
+
nl: dutch
|
29
|
+
pl: polish
|
30
|
+
pt: portuguese
|
31
|
+
ro: rumanian
|
32
|
+
se: swedish
|
33
|
+
si: slovenian
|
34
|
+
sk: slovakian
|
data/lib/valvat/active_model.rb
CHANGED
@@ -1,7 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
require 'active_model'
|
2
|
+
require 'valvat/syntax'
|
3
|
+
require 'valvat/lookup'
|
4
|
+
|
5
|
+
module ActiveModel
|
6
|
+
module Validations
|
7
|
+
class ValvatValidator < ::ActiveModel::EachValidator
|
8
|
+
|
9
|
+
def validate_each(record, attribute, value)
|
10
|
+
is_valid = Valvat::Syntax.validate(value)
|
11
|
+
|
12
|
+
if is_valid && options[:lookup]
|
13
|
+
is_valid = Valvat::Lookup.validate(value)
|
14
|
+
is_valid.nil? && is_valid = (options[:lookup] != :fail_if_down)
|
15
|
+
end
|
16
|
+
|
17
|
+
unless is_valid
|
18
|
+
record.errors.add(attribute, :invalid_vat,
|
19
|
+
:message => options[:message],
|
20
|
+
:country_adjective => I18n.t(
|
21
|
+
:"valvat.country_adjectives.#{(Valvat::Utils.split(value)[0] || "eu").downcase}",
|
22
|
+
:default => [:"valvat.country_adjectives.eu", "european"]
|
23
|
+
)
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
5
27
|
end
|
6
28
|
end
|
7
|
-
end
|
29
|
+
end
|
30
|
+
|
31
|
+
I18n.load_path << File.dirname(__FILE__) + '/../locale/en.yml'
|
data/lib/valvat/syntax.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
require 'valvat/utils'
|
2
2
|
|
3
3
|
module Valvat
|
4
|
-
class InvalidSyntax < StandardError;end
|
5
|
-
|
6
4
|
module Syntax
|
7
5
|
|
8
6
|
VAT_PATTERNS = {
|
@@ -14,7 +12,7 @@ module Valvat
|
|
14
12
|
'DE' => /\ADE[0-9]{9}\Z/, # Germany
|
15
13
|
'DK' => /\ADK[0-9]{8}\Z/, # Denmark
|
16
14
|
'EE' => /\AEE[0-9]{9}\Z/, # Estonia
|
17
|
-
'
|
15
|
+
'GR' => /\AEL[0-9]{9}\Z/, # Greece
|
18
16
|
'ES' => /\AES([A-Z][0-9]{8}|[0-9]{8}[A-Z]|[A-Z][0-9]{7}[A-Z])\Z/, # Spain
|
19
17
|
'FI' => /\AFI[0-9]{8}\Z/, # Finland
|
20
18
|
'FR' => /\AFR[A-Z0-9]{2}[0-9]{9}\Z/, # France
|
@@ -39,10 +37,6 @@ module Valvat
|
|
39
37
|
pattern = VAT_PATTERNS[Valvat::Utils.split(vat)[0]]
|
40
38
|
!!(pattern && pattern =~ vat)
|
41
39
|
end
|
42
|
-
|
43
|
-
def self.validate!(vat)
|
44
|
-
validate(vat) || raise(Valvat::InvalidSyntax.new("#{vat.inspect} is an invalid vat number!"))
|
45
|
-
end
|
46
40
|
end
|
47
41
|
|
48
42
|
end
|
data/lib/valvat/utils.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
module Valvat
|
2
2
|
module Utils
|
3
3
|
|
4
|
+
EU_COUNTRIES = %w(AT BE BG CY CZ DE DK EE ES FI FR GB GR HU IE IT LT LU LV MT NL PL PT RO SE SI SK)
|
4
5
|
COUNTRY_PATTERN = /\A([A-Z]{2})(.+)\Z/
|
5
6
|
|
6
7
|
def self.split(vat)
|
7
8
|
COUNTRY_PATTERN =~ vat
|
8
|
-
[$1, $2]
|
9
|
+
result = [$1, $2]
|
10
|
+
result[0] = "GR" if result[0] == "EL"
|
11
|
+
return [nil, nil] unless EU_COUNTRIES.include?(result[0])
|
12
|
+
result
|
9
13
|
end
|
10
14
|
|
11
15
|
def self.normalize(vat)
|
data/lib/valvat/version.rb
CHANGED
@@ -4,6 +4,22 @@ class Invoice < ModelBase
|
|
4
4
|
validates :vat_number, :valvat => true
|
5
5
|
end
|
6
6
|
|
7
|
+
class InvoiceWithLookup < ModelBase
|
8
|
+
validates :vat_number, :valvat => {:lookup => true}
|
9
|
+
end
|
10
|
+
|
11
|
+
class InvoiceWithLookupAndFailIfDown < ModelBase
|
12
|
+
validates :vat_number, :valvat => {:lookup => :fail_if_down}
|
13
|
+
end
|
14
|
+
|
15
|
+
class InvoiceAllowBlank < ModelBase
|
16
|
+
validates :vat_number, :valvat => {:allow_blank => true}
|
17
|
+
end
|
18
|
+
|
19
|
+
class InvoiceAllowBlankOnAll < ModelBase
|
20
|
+
validates :vat_number, :valvat => true, :allow_blank => true
|
21
|
+
end
|
22
|
+
|
7
23
|
describe Invoice do
|
8
24
|
context "with valid vat number" do
|
9
25
|
before do
|
@@ -11,7 +27,7 @@ describe Invoice do
|
|
11
27
|
end
|
12
28
|
|
13
29
|
it "should be valid" do
|
14
|
-
Invoice.new(:vat_number => "
|
30
|
+
Invoice.new(:vat_number => "DE123").should be_valid
|
15
31
|
end
|
16
32
|
end
|
17
33
|
|
@@ -20,8 +36,125 @@ describe Invoice do
|
|
20
36
|
Valvat::Syntax.stub(:validate => false)
|
21
37
|
end
|
22
38
|
|
39
|
+
it "should not be valid" do
|
40
|
+
Invoice.new(:vat_number => "DE123").should_not be_valid
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should add default (country specific) error message" do
|
44
|
+
i = Invoice.new(:vat_number => "DE123")
|
45
|
+
i.valid?
|
46
|
+
i.errors[:vat_number].should eql(["is not a valid german vat number"])
|
47
|
+
end
|
48
|
+
|
49
|
+
context "with i18n translation in place" do
|
50
|
+
before do
|
51
|
+
I18n.backend.store_translations(:en, :activemodel => {
|
52
|
+
:errors => {:models => {:invoice => {:invalid_vat => "is ugly."}}}
|
53
|
+
})
|
54
|
+
end
|
55
|
+
|
56
|
+
after { I18n.reload! }
|
57
|
+
|
58
|
+
it "should use translation" do
|
59
|
+
i = Invoice.new(:vat_number => "DE123")
|
60
|
+
i.valid?
|
61
|
+
i.errors[:vat_number].should eql(["is ugly."])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "with i18n translation with country adjective placeholder in place" do
|
66
|
+
before do
|
67
|
+
I18n.backend.store_translations(:en, :activemodel => {
|
68
|
+
:errors => {:models => {:invoice => {:invalid_vat => "is not a %{country_adjective} vat"}}}
|
69
|
+
})
|
70
|
+
end
|
71
|
+
|
72
|
+
after { I18n.reload! }
|
73
|
+
|
74
|
+
it "should replace country adjective placeholder" do
|
75
|
+
i = Invoice.new(:vat_number => "IE123")
|
76
|
+
i.valid?
|
77
|
+
i.errors[:vat_number].should eql(["is not a irish vat"])
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should fall back to 'european' if country is missing" do
|
81
|
+
i = Invoice.new(:vat_number => "XX123")
|
82
|
+
i.valid?
|
83
|
+
i.errors[:vat_number].should eql(["is not a european vat"])
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "with blank vat number" do
|
89
|
+
it "should not be valid" do
|
90
|
+
Invoice.new(:vat_number => "").should_not be_valid
|
91
|
+
Invoice.new(:vat_number => nil).should_not be_valid
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe InvoiceWithLookup do
|
97
|
+
context "with valid but not existing vat number" do
|
98
|
+
before do
|
99
|
+
Valvat::Syntax.stub(:validate => true)
|
100
|
+
Valvat::Lookup.stub(:validate => false)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should not be valid" do
|
104
|
+
InvoiceWithLookup.new(:vat_number => "DE123").should_not be_valid
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "with valid and existing vat number" do
|
109
|
+
before do
|
110
|
+
Valvat::Syntax.stub(:validate => true)
|
111
|
+
Valvat::Lookup.stub(:validate => true)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should be valid" do
|
115
|
+
InvoiceWithLookup.new(:vat_number => "DE123").should be_valid
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context "with valid vat number and VIES country service down" do
|
120
|
+
before do
|
121
|
+
Valvat::Syntax.stub(:validate => true)
|
122
|
+
Valvat::Lookup.stub(:validate => nil)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should be valid" do
|
126
|
+
InvoiceWithLookup.new(:vat_number => "DE123").should be_valid
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe InvoiceWithLookupAndFailIfDown do
|
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 not be valid" do
|
139
|
+
InvoiceWithLookupAndFailIfDown.new(:vat_number => "DE123").should_not be_valid
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe InvoiceAllowBlank do
|
145
|
+
context "with blank vat number" do
|
146
|
+
it "should be valid" do
|
147
|
+
InvoiceAllowBlank.new(:vat_number => "").should be_valid
|
148
|
+
InvoiceAllowBlank.new(:vat_number => nil).should be_valid
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe InvoiceAllowBlankOnAll do
|
154
|
+
context "with blank vat number" do
|
23
155
|
it "should be valid" do
|
24
|
-
|
156
|
+
InvoiceAllowBlankOnAll.new(:vat_number => "").should be_valid
|
157
|
+
InvoiceAllowBlankOnAll.new(:vat_number => nil).should be_valid
|
25
158
|
end
|
26
159
|
end
|
27
160
|
end
|
data/spec/valvat/syntax_spec.rb
CHANGED
@@ -267,14 +267,4 @@ describe Valvat::Syntax do
|
|
267
267
|
Valvat::Syntax.validate("BEFR").should eql(false)
|
268
268
|
end
|
269
269
|
end
|
270
|
-
|
271
|
-
context "#validate!" do
|
272
|
-
it "returns true on valid vat number" do
|
273
|
-
Valvat::Syntax.validate!("DE345889003").should eql(true)
|
274
|
-
end
|
275
|
-
|
276
|
-
it "raises Valvat::InvalidSyntax on invalid vat number" do
|
277
|
-
lambda { Valvat::Syntax.validate!("DE34588900") }.should raise_error(Valvat::InvalidSyntax)
|
278
|
-
end
|
279
|
-
end
|
280
270
|
end
|
data/spec/valvat/utils_spec.rb
CHANGED
@@ -4,7 +4,12 @@ describe Valvat::Utils do
|
|
4
4
|
context "#split" do
|
5
5
|
it "returns country and rest on vat number as array" do
|
6
6
|
Valvat::Utils.split("DE345889003").should eql(["DE", "345889003"])
|
7
|
-
Valvat::Utils.split("
|
7
|
+
Valvat::Utils.split("ESX4588900X").should eql(["ES", "X4588900X"])
|
8
|
+
end
|
9
|
+
|
10
|
+
it "returns two nils on non-european iso codes as array" do
|
11
|
+
Valvat::Utils.split("US345889003").should eql([nil, nil])
|
12
|
+
Valvat::Utils.split("RUX4588900X").should eql([nil, nil])
|
8
13
|
end
|
9
14
|
|
10
15
|
it "returns two nils on non-sense input as array" do
|
@@ -14,6 +19,10 @@ describe Valvat::Utils do
|
|
14
19
|
Valvat::Utils.split("1234").should eql([nil, nil])
|
15
20
|
Valvat::Utils.split(" ").should eql([nil, nil])
|
16
21
|
end
|
22
|
+
|
23
|
+
it "returns GR on greek vat" do
|
24
|
+
Valvat::Utils.split("EL999999999").should eql(["GR", "999999999"])
|
25
|
+
end
|
17
26
|
end
|
18
27
|
|
19
28
|
context "#normalize" do
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
|
9
|
-
version: 0.0.3
|
9
|
+
version: 0.1.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Sebastian Munz
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-01-
|
17
|
+
date: 2011-01-07 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -109,6 +109,8 @@ files:
|
|
109
109
|
- MIT-LICENSE
|
110
110
|
- README.md
|
111
111
|
- Rakefile
|
112
|
+
- lib/locale/de.yml
|
113
|
+
- lib/locale/en.yml
|
112
114
|
- lib/valvat.rb
|
113
115
|
- lib/valvat/active_model.rb
|
114
116
|
- lib/valvat/lookup.rb
|