vat_calculator 1.2.2

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.
@@ -0,0 +1,66 @@
1
+ # Description
2
+
3
+ Use this plugin to calculate the VAT rate depending on the country of the buyer.
4
+
5
+ # Configuration
6
+
7
+ First set the country of the seller in your config/application.rb file
8
+
9
+ config.vat_calculator_base_country = 'FR' # or 'BE', etc....
10
+
11
+ Note: by default, the value of the vat_calculator_base_country option is set to 'FR'
12
+
13
+ # Basic usage
14
+
15
+ Let's review the different cases for a seller in France:
16
+
17
+ 1. the buyer is outside Europe
18
+
19
+ VatCalculator.get('US') # returns 0.0
20
+
21
+ 2. the buyer is in France
22
+
23
+ VatCalculator.get('FR') # returns 19.6
24
+
25
+ 3. the buyer is in Europe without a vat number
26
+
27
+ VatCalculator.get('BE') # returns 19.6
28
+
29
+ 4. the buyer is in Europe with an almost correct vat number
30
+
31
+ VatCalculator.get('BE', { vat_number: 'a_valid_vat_number' }) # returns 0.0
32
+
33
+ 5. the buyer is in Europe with an almost correct vat number
34
+
35
+ VatCalculator.get('BE', { vat_number: 'bla bla', validation: :simple }) # returns 19.6
36
+
37
+ 6. the buyer is in Europe with an almost correct vat number
38
+
39
+ VatCalculator.get('BE', { vat_number: 'BE00000000000', validation: :full }) # returns 0.0
40
+
41
+ 7. the buyer is in Martinique, Guadeloupe or la Réunion
42
+
43
+ VatCalculator.get('MQ') # returns 8.5
44
+
45
+ 8. the buyer is in French Guyana
46
+
47
+ VatCalculator.get('GF') # returns 0.0
48
+
49
+ # Installation
50
+
51
+ In your project's Gemfile :
52
+
53
+ gem 'vat_calculator', git: 'git://github.com/did/vat_calculator.git'
54
+
55
+ # Tests
56
+
57
+ If you want to run the specs :
58
+
59
+ bundle exec rake spec
60
+
61
+ # Credits
62
+
63
+ This plugin in released under MIT license by Didier Lafforgue (see MIT-LICENSE
64
+ file).
65
+
66
+ (c) http://www.nocoffee.fr
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'rdoc/task'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ require 'rspec/core/rake_task'
7
+
8
+ desc 'Run specs'
9
+ task :spec do
10
+ RSpec::Core::RakeTask.new(:spec) do |t|
11
+ t.rspec_opts = %w{--colour --format progress}
12
+ t.pattern = 'spec/*_spec.rb'
13
+ end
14
+ end
15
+
16
+ task :default => [ :spec ]
@@ -0,0 +1,131 @@
1
+ require 'ostruct'
2
+ require 'vat_validator'
3
+ require 'active_support'
4
+ require 'active_support/core_ext/object/blank'
5
+ require 'active_support/core_ext/module/attribute_accessors'
6
+
7
+ module VatCalculator
8
+
9
+ DEFAULT_BASE_COUNTRY = 'FR'
10
+
11
+ mattr_accessor :base_country
12
+
13
+ mattr_accessor :current_rate_rule
14
+
15
+ # EUROPEAN_COUNTRIES = %w(DE AT BE BG CY DK ES EE FI FR EL HU IE IT LV LT LU MT NL PL GB RO SK SI SE CZ) # @deprecated
16
+
17
+ VAT_RATES = {
18
+ 'FR' => {
19
+ :rate => 19.6
20
+ # FIXME (Did): no exceptions an accouting person told me (to be double checked)
21
+ # :exceptions => {
22
+ # 'GF' => 0.0, # French Guyana
23
+ # 'GP' => 8.5, # Guadeloupe
24
+ # 'MQ' => 8.5, # Martinique
25
+ # 'RE' => 8.5 # Réunion
26
+ # }
27
+ },
28
+
29
+ 'GP' => 8.5,
30
+ 'MQ' => 8.5,
31
+ 'RE' => 8.5,
32
+
33
+ 'BE' => 21
34
+ }
35
+
36
+ def self.formalize_rate_rule(data)
37
+ unless data.is_a?(Hash)
38
+ data = { :rate => data.to_f }
39
+ end
40
+
41
+ OpenStruct.new({
42
+ :exceptions => {},
43
+ :rate => 0.0,
44
+ :no_rate => 0.0
45
+ }.merge(data))
46
+ end
47
+
48
+ class NoCountryException < Exception; end
49
+
50
+ class NoRuleFoundException < Exception; end
51
+
52
+ # Method arguments:
53
+ # country_code the country of the buyer [required]
54
+ # options see below
55
+
56
+ # Possible options are:
57
+ # :base_country_code the country of the seller, if not given, then take the one by default [optional]
58
+ # :vat_number in case the buyer left his vat number. Validation should have been done before [optional]
59
+ # :validation :none, :simple (just check the format of the vat number), :full (format + existence of the vat number). By default, :none
60
+ #
61
+ def self.get(country_code, options = {})
62
+ raise NoCountryException if country_code.nil?
63
+
64
+ country_code.upcase!
65
+
66
+ base_country_code = self.base_country
67
+
68
+ if options[:base_country_code] && options[:base_country_code] != self.base_country # different country code for the seller ?
69
+ if rule_data = VAT_RATES[options[:base_country_code]]
70
+ base_country_code = options[:base_country_code]
71
+ rule = self.formalize_rate_rule(rule_data)
72
+ end
73
+ else
74
+ rule = self.current_rate_rule
75
+ end
76
+
77
+ raise NoRuleFoundException if rule.nil?
78
+
79
+ # same country for both the buyer and the seller, no need to go further
80
+ return rule.rate if base_country_code == country_code
81
+
82
+ if self.is_an_european_country?(country_code)
83
+ # case when the country of the buyer is in Europe, check if he has a vat_number
84
+ if options[:vat_number].present? && self.validate_vat_number?(options[:vat_number], options[:validation])
85
+ rule.no_rate
86
+ else
87
+ rule.rate
88
+ end
89
+ elsif rate = rule.exceptions[country_code] # exceptions (France for instance has many exceptions for the DOM-TOM territories)
90
+ rate
91
+ else
92
+ rule.no_rate
93
+ end
94
+ end
95
+
96
+ def self.is_an_european_country?(country_code)
97
+ VatValidator::VAT_PATTERNS.keys.include?(country_code.upcase)
98
+ end
99
+
100
+ def self.validate_vat_number?(number, level = :none)
101
+ case level
102
+ when :simple then self.vat_format_valid?(number)
103
+ when :full then self.vat_format_valid?(number) && self.vat_number_existence?(number)
104
+ else
105
+ true
106
+ end
107
+ end
108
+
109
+ def self.vat_format_valid?(number)
110
+ number =~ VatValidator::VAT_PATTERNS.values.detect { |p| number.to_s =~ p }
111
+ end
112
+
113
+ def self.vat_number_existence?(number)
114
+ VatValidator::ViesChecker.check(number)
115
+ end
116
+
117
+ end
118
+
119
+ if defined?(Rails)
120
+ if Rails::VERSION::MAJOR >= 3
121
+ require 'vat_calculator/railtie'
122
+ else
123
+ puts "[Warning] VatCalculator does not work with Rails < 3"
124
+ end
125
+ else
126
+ VatCalculator.base_country ||= VatCalculator::DEFAULT_BASE_COUNTRY
127
+
128
+ if rule_data = VatCalculator::VAT_RATES[VatCalculator.base_country]
129
+ VatCalculator.current_rate_rule = VatCalculator.formalize_rate_rule(rule_data)
130
+ end
131
+ end
@@ -0,0 +1,16 @@
1
+ module VatCalculator
2
+
3
+ class Railtie < Rails::Railtie
4
+
5
+ config.vat_calculator_base_country = DEFAULT_BASE_COUNTRY
6
+
7
+ initializer 'vat_calculator.initialize' do |app|
8
+ VatCalculator.base_country = app.config.vat_calculator_base_country.upcase
9
+
10
+ if rule = VatCalculator::VAT_RATES[VatCalculator.base_country]
11
+ VatCalculator.current_rate_rule = VatCalculator.formalize_rate_rule(rule)
12
+ end
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,16 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
3
+
4
+ require 'rubygems'
5
+ require 'bundler'
6
+
7
+ Bundler.setup
8
+ Bundler.require(:test)
9
+
10
+ require 'mocha'
11
+ require 'rspec'
12
+ require 'vat_calculator'
13
+
14
+ RSpec.configure do |config|
15
+ config.mock_with :mocha
16
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Processing tax rate' do
4
+
5
+ it 'should raise an exception of no country is provided' do
6
+ lambda {
7
+ VatCalculator.get(nil)
8
+ }.should raise_exception(VatCalculator::NoCountryException)
9
+ end
10
+
11
+ it 'should raise an exception of the country of the seller is not recorded' do
12
+ lambda {
13
+ VatCalculator.get('FR', { base_country_code: 'DE' })
14
+ }.should raise_exception(VatCalculator::NoRuleFoundException)
15
+ end
16
+
17
+ context 'Seller in France' do
18
+
19
+ it 'should apply tax since the customer is in France' do
20
+ VatCalculator.get('FR').should == 19.6
21
+ end
22
+
23
+ it 'should apply tax if the buyer is in Europe BUT didn\'t give his vta number in CEE' do
24
+ VatCalculator.get('IT').should == 19.6
25
+ end
26
+
27
+ it 'should not apply tax if the buyer is in Europe AND gave his vta number in CEE' do
28
+ VatCalculator.get('IT', { vat_number: 'valid_number' }).should == 0.0
29
+ end
30
+
31
+ # Note (Did): see the vat_calculator.rb file for more explanations
32
+ # %w(GP MQ RE).each do |code|
33
+ # it "should apply a different tax rate if the buyer is in the country with the '#{code}' code" do
34
+ # VatCalculator.get(code).should == 8.5
35
+ # end
36
+ # end
37
+
38
+ it 'should not apply tax if the buyer lives in French Guyana' do
39
+ VatCalculator.get('GF').should == 0.0
40
+ end
41
+
42
+ it 'should not apply tax if the buyer lives is in the United States' do
43
+ VatCalculator.get('US').should == 0.0
44
+ end
45
+
46
+ end
47
+
48
+ context 'Validating the VAT number' do
49
+
50
+ it 'refuses a wrong VAT number' do
51
+ VatCalculator.get('IT', { vat_number: 'invalid_number', validation: :simple }).should == 19.6
52
+ end
53
+
54
+ it 'accepts a wrong VAT number which looks like almost valid' do
55
+ VatCalculator.get('IT', { vat_number: 'IT12345678923', validation: :simple }).should == 0.0
56
+ end
57
+
58
+ it 'refuses a VAT number by calling an external service' do
59
+ VatCalculator.get('IT', { vat_number: 'IT12345678923', validation: :full }).should == 19.6
60
+ end
61
+
62
+ it 'accepts a VAT number by calling an external service' do
63
+ VatCalculator.get('SE', { vat_number: 'SE556866920301', validation: :full }).should == 0.0
64
+ end
65
+
66
+ end
67
+
68
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vat_calculator
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 1.2.2
6
+ platform: ruby
7
+ authors:
8
+ - Didier Lafforgue
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-05-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ type: :runtime
16
+ version_requirements: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.2.0
22
+ name: savon
23
+ prerelease: false
24
+ requirement: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.2.0
30
+ - !ruby/object:Gem::Dependency
31
+ type: :runtime
32
+ version_requirements: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ name: activesupport
39
+ prerelease: false
40
+ requirement: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ type: :runtime
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ name: vat_validator
55
+ prerelease: false
56
+ requirement: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Helper to calculate the VAT rate
63
+ email: didier@nocoffee.fr
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - lib/vat_calculator/railtie.rb
69
+ - lib/vat_calculator.rb
70
+ - spec/spec_helper.rb
71
+ - spec/vat_calculator_spec.rb
72
+ - README.md
73
+ - Rakefile
74
+ homepage: http://github.com/did/vat_calculator
75
+ licenses: []
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ segments:
86
+ - 0
87
+ hash: 2311850019608209240
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: 1.3.6
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 1.8.23
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: Helper to calculate the VAT rate
101
+ test_files: []