vat-gst-calculator 0.5.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b41f3eb40c2fc01049599af67185633c146994e9e8aa68ef2518ecee6d4cdc3f
4
- data.tar.gz: 7603c43981f2bcb359a77be92d714873dbfbb0b0e8d035ab401b94e6e5b995e6
3
+ metadata.gz: 4c0b1547695777a69f063993628affe4eaf28498180236ca0bd2e5902d5bd7d7
4
+ data.tar.gz: 5f7fd7051a155b3f5c0187ec2ebde52df83991a35491e0d8abf780d93ff94aed
5
5
  SHA512:
6
- metadata.gz: b61a549c2b33e5549bce687e018516f4f8b2218634c25ac81c799d732239cc3461f73a8b502b24749c3657abe206b09f4695284c2167836f300882c40aa1bb5b
7
- data.tar.gz: 7ef3a105c047ed5c26806df2bc2a5f7da234c7a36b079c19a13c40d19481ce7f2be781e67554e3c2050e60e993e419fe933d040325fa0951014a16e92573c6fc
6
+ metadata.gz: 444a34ccad4d894adca304083e25cc4baae48ae6a6abb7d1aabcd0c074a87d790120f0c6abd6ff2bfa242174c9939a00957c53a399151d68fe1acc0a551d7fbb
7
+ data.tar.gz: a7b0bcf732ca7a34da47c703e6b9f85fdae33461ad2602c73058f5770284b0e50204a3938b6674fd30a46317ab5481971a2d5d282665fccc70e713f1c9c8a28c
data/.gitignore CHANGED
@@ -1,3 +1,6 @@
1
1
  .byebug_history
2
2
  .rspec_status
3
3
  Gemfile.lock
4
+ .idea/*
5
+ *.swp
6
+ tags
data/README.md CHANGED
@@ -26,7 +26,9 @@ Easyship::SalesTax::Calculator.calculate(
26
26
  destination_country_alpha2: destination_country_alpha2,
27
27
  origin_state: origin_state,
28
28
  destination_state: destination_state,
29
- fees: Easyship::SalesTax::Fee.new(10, 20, 30, 40) # (shipment_charge_total, insurance_fee, pickup_fee, residential_surcharge)
29
+ fees: Easyship::SalesTax::Fee.new(10, 20, 30, 40), # (shipment_charge_total, insurance_fee, pickup_fee, residential_surcharge)
30
+ effective_timestamp: "2023-01-01", # String and Time instances are supported. Default to current time (utc)
31
+ effective_country_alpha2s: ["HK", "US"] # when empty or not specified, all avalilable country data is used
30
32
  )
31
33
  ```
32
34
 
@@ -1,7 +1,19 @@
1
+ require "time"
2
+
1
3
  module Easyship
2
4
  module SalesTax
3
5
  module Formula
4
6
  class Error < StandardError; end
7
+
8
+ NO_TAX = {
9
+ shipment_charge_total: 0,
10
+ insurance_fee: 0,
11
+ pickup_fee: 0,
12
+ residential_surcharge: 0
13
+ }.freeze
14
+
15
+ DEFAULT_EFFECTIVE_TIMESTAMP = Time.at(0).utc.freeze
16
+
5
17
  class << self
6
18
  def build_fee_struct(shipment_charge_total, insurance_fee, pickup_fee, residential_surcharge)
7
19
  {
@@ -12,23 +24,42 @@ module Easyship
12
24
  }
13
25
  end
14
26
 
15
- def no_taxes
16
- build_fee_struct(0, 0, 0, 0)
27
+ def effective_data(country_alpha2, effective_timestamp = nil, effective_country_alpha2s = [])
28
+ DOMESTIC
29
+ .slice(*(effective_country_alpha2s.empty? ? DEFAULT_EFFECTIVE_COUNTRY_ALPHA2S : effective_country_alpha2s))
30
+ .fetch(country_alpha2, [])
31
+ .find { |rates| rates[:effective_timestamp] <= effective_time(effective_timestamp) }
32
+ end
33
+
34
+ private
35
+
36
+ def effective_time(effective_timestamp)
37
+ case effective_timestamp
38
+ when String
39
+ Time.parse(effective_timestamp).utc
40
+ when Time
41
+ effective_timestamp
42
+ else
43
+ Time.now.utc
44
+ end
17
45
  end
18
46
  end
19
47
 
20
48
  DOMESTIC = {
21
- 'AU' => {
49
+ 'AU' => [{
50
+ effective_timestamp: DEFAULT_EFFECTIVE_TIMESTAMP,
22
51
  sales_tax_website_name: 'GST',
23
- sales_tax: { '_ALL': build_fee_struct(0.1, 0, 0.1, 0) },
24
- provincial_sales_taxes: { '_ALL': no_taxes }
25
- },
26
- 'GB' => {
52
+ sales_tax: {'_ALL': build_fee_struct(0.1, 0, 0.1, 0)},
53
+ provincial_sales_taxes: {'_ALL': NO_TAX}
54
+ }],
55
+ 'GB' => [{
56
+ effective_timestamp: DEFAULT_EFFECTIVE_TIMESTAMP,
27
57
  sales_tax_website_name: 'VAT',
28
- sales_tax: { '_ALL': build_fee_struct(0.2, 0, 0.2, 0.2) }, # TBC
29
- provincial_sales_taxes: { '_ALL': no_taxes }
30
- },
31
- 'CA' => {
58
+ sales_tax: {'_ALL': build_fee_struct(0.2, 0, 0.2, 0.2)}, # TBC
59
+ provincial_sales_taxes: {'_ALL': NO_TAX}
60
+ }],
61
+ 'CA' => [{
62
+ effective_timestamp: DEFAULT_EFFECTIVE_TIMESTAMP,
32
63
  sales_tax_website_name: 'Taxes',
33
64
  sales_tax: {
34
65
  'AB' => build_fee_struct(0.05, 0, 0.05, 0.05), # Alberta
@@ -46,41 +77,52 @@ module Easyship
46
77
  'YT' => build_fee_struct(0.05, 0, 0.05, 0.05), # Yukon
47
78
  },
48
79
  provincial_sales_taxes: {
49
- 'AB' => no_taxes, # Alberta
50
- 'BC' => no_taxes, # British Columbia
51
- 'MB' => no_taxes, # Manitoba
52
- 'NB' => no_taxes, # New Brunswick
53
- 'NL' => no_taxes, # Newfoundland and Labrador
54
- 'NT' => no_taxes, # Northwest Territories
55
- 'NS' => no_taxes, # Nova Scotia
56
- 'NU' => no_taxes, # Nunavut
57
- 'ON' => no_taxes, # Ontario
58
- 'PE' => no_taxes, # Prince Edward Island
80
+ 'AB' => NO_TAX, # Alberta
81
+ 'BC' => NO_TAX, # British Columbia
82
+ 'MB' => NO_TAX, # Manitoba
83
+ 'NB' => NO_TAX, # New Brunswick
84
+ 'NL' => NO_TAX, # Newfoundland and Labrador
85
+ 'NT' => NO_TAX, # Northwest Territories
86
+ 'NS' => NO_TAX, # Nova Scotia
87
+ 'NU' => NO_TAX, # Nunavut
88
+ 'ON' => NO_TAX, # Ontario
89
+ 'PE' => NO_TAX, # Prince Edward Island
59
90
  'QC' => build_fee_struct(0.09975, 0, 0.09975, 0.09975), # Quebec
60
- 'SK' => no_taxes, # Saskatchewan
61
- 'YT' => no_taxes, # Yukon
91
+ 'SK' => NO_TAX, # Saskatchewan
92
+ 'YT' => NO_TAX, # Yukon
62
93
  }
63
- },
64
- 'SG' => {
65
- sales_tax_website_name: 'GST',
66
- sales_tax: { '_ALL': build_fee_struct(0.07, 0.07, 0.07, 0.07) },
67
- provincial_sales_taxes: { '_ALL': no_taxes }
68
- },
69
- 'HK' => {
94
+ }],
95
+ 'SG' => [
96
+ {
97
+ effective_timestamp: DEFAULT_EFFECTIVE_TIMESTAMP,
98
+ sales_tax_website_name: 'GST',
99
+ sales_tax: {'_ALL': build_fee_struct(0.07, 0.07, 0.07, 0.07)},
100
+ provincial_sales_taxes: {'_ALL': NO_TAX}
101
+ },
102
+ {
103
+ effective_timestamp: Time.utc(2023, 1, 1, 8),
104
+ sales_tax_website_name: 'GST',
105
+ sales_tax: {'_ALL': build_fee_struct(0.08, 0.08, 0.08, 0.08)},
106
+ provincial_sales_taxes: {'_ALL': NO_TAX}
107
+ }
108
+ ],
109
+ 'HK' => [{
110
+ effective_timestamp: DEFAULT_EFFECTIVE_TIMESTAMP,
70
111
  sales_tax_website_name: nil,
71
- sales_tax: { '_ALL': no_taxes },
72
- provincial_sales_taxes: { '_ALL': no_taxes }
73
- },
112
+ sales_tax: {'_ALL': NO_TAX},
113
+ provincial_sales_taxes: {'_ALL': NO_TAX}
114
+ }],
74
115
  # 'NL' => {
75
116
  # sales_tax_website_name: 'VAT',
76
117
  # sales_tax: { '_ALL': build_fee_struct(0.21, 0, 0.21, 0.21) },
77
118
  # provincial_sales_taxes: { '_ALL': no_taxes }
78
119
  # },
79
- 'US' => {
120
+ 'US' => [{
121
+ effective_timestamp: DEFAULT_EFFECTIVE_TIMESTAMP,
80
122
  sales_tax_website_name: nil,
81
- sales_tax: { '_ALL': no_taxes },
82
- provincial_sales_taxes: { '_ALL': no_taxes }
83
- },
123
+ sales_tax: {'_ALL': NO_TAX},
124
+ provincial_sales_taxes: {'_ALL': NO_TAX}
125
+ }],
84
126
  # 'DE' => {
85
127
  # sales_tax_website_name: 'VAT',
86
128
  # sales_tax: { '_ALL': build_fee_struct(0.19, 0, 0, 0) },
@@ -126,16 +168,18 @@ module Easyship
126
168
  # sales_tax: { '_ALL': build_fee_struct(0.2, 0, 0, 0) },
127
169
  # provincial_sales_taxes: { '_ALL': no_taxes }
128
170
  # },
129
- 'AE' => {
171
+ 'AE' => [{
172
+ effective_timestamp: DEFAULT_EFFECTIVE_TIMESTAMP,
130
173
  sales_tax_website_name: 'VAT',
131
- sales_tax: { '_ALL': build_fee_struct(0.05, 0, 0, 0) },
132
- provincial_sales_taxes: { '_ALL': no_taxes }
133
- },
134
- # 'IE' => {
135
- # sales_tax_website_name: 'VAT',
136
- # sales_tax: { '_ALL': build_fee_struct(0.23, 0, 0, 0) },
137
- # provincial_sales_taxes: { '_ALL': no_taxes }
138
- # },
174
+ sales_tax: {'_ALL': build_fee_struct(0.05, 0, 0, 0)},
175
+ provincial_sales_taxes: {'_ALL': NO_TAX}
176
+ }],
177
+ 'IE' => [{
178
+ effective_timestamp: DEFAULT_EFFECTIVE_TIMESTAMP,
179
+ sales_tax_website_name: 'VAT',
180
+ sales_tax: { '_ALL': build_fee_struct(0.23, 0, 0.23, 0.23) },
181
+ provincial_sales_taxes: { '_ALL': NO_TAX }
182
+ }],
139
183
  # 'DK' => {
140
184
  # sales_tax_website_name: 'VAT',
141
185
  # sales_tax: { '_ALL': build_fee_struct(0.25, 0, 0, 0) },
@@ -176,11 +220,12 @@ module Easyship
176
220
  # sales_tax: { '_ALL': build_fee_struct(0.1, 0, 0, 0) },
177
221
  # provincial_sales_taxes: { '_ALL': no_taxes }
178
222
  # },
179
- 'NZ' => {
223
+ 'NZ' => [{
224
+ effective_timestamp: DEFAULT_EFFECTIVE_TIMESTAMP,
180
225
  sales_tax_website_name: 'VAT',
181
- sales_tax: { '_ALL': build_fee_struct(0.15, 0.15, 0.15, 0.15) },
182
- provincial_sales_taxes: { '_ALL': no_taxes }
183
- },
226
+ sales_tax: {'_ALL': build_fee_struct(0.15, 0.15, 0.15, 0.15)},
227
+ provincial_sales_taxes: {'_ALL': NO_TAX}
228
+ }],
184
229
  # 'IN' => {
185
230
  # sales_tax_website_name: 'VAT',
186
231
  # sales_tax: { '_ALL': build_fee_struct(0.15, 0, 0, 0) },
@@ -196,7 +241,9 @@ module Easyship
196
241
  # sales_tax: { '_ALL': build_fee_struct(0.16, 0, 0, 0) },
197
242
  # provincial_sales_taxes: { '_ALL': no_taxes }
198
243
  # }
199
- }.freeze
244
+ }.transform_values { |value| value.sort_by { |rates| -rates[:effective_timestamp].to_i } }.freeze
245
+
246
+ DEFAULT_EFFECTIVE_COUNTRY_ALPHA2S = DOMESTIC.keys.freeze
200
247
  end
201
248
  end
202
249
  end
@@ -1,7 +1,7 @@
1
1
  module Easyship
2
2
  module SalesTax
3
3
  module Calculator
4
- VERSION = "0.5.0"
4
+ VERSION = "0.7.0"
5
5
  end
6
6
  end
7
7
  end
@@ -6,33 +6,45 @@ module Easyship
6
6
  module Calculator
7
7
  class Error < StandardError; end
8
8
 
9
- def self.calculate(origin_country_alpha2: nil, destination_country_alpha2: nil, origin_state: nil, destination_state: nil, fees: nil)
10
- return fallback_value if origin_country_alpha2.nil? || origin_country_alpha2.empty? || destination_country_alpha2.nil? || destination_country_alpha2.empty?
11
- return fallback_value unless fees.is_a?(Fee)
9
+ FALLBACK_VALUE = {sales_tax: 0, provincial_sales_tax: 0}.freeze
10
+
11
+ def self.calculate(origin_country_alpha2: nil, destination_country_alpha2: nil, origin_state: nil, destination_state: nil, fees: nil, effective_timestamp: nil, effective_country_alpha2s: [])
12
+ return FALLBACK_VALUE if origin_country_alpha2.nil? || origin_country_alpha2.empty? || destination_country_alpha2.nil? || destination_country_alpha2.empty?
13
+ return FALLBACK_VALUE unless fees.is_a?(Fee)
12
14
 
13
15
  if domestic?(origin_country_alpha2, destination_country_alpha2) || within_eu?(origin_country_alpha2, destination_country_alpha2)
14
- domestic_value(origin_country_alpha2: origin_country_alpha2, origin_state: origin_state, destination_state: destination_state, fees: fees)
16
+ domestic_value(
17
+ origin_country_alpha2: origin_country_alpha2,
18
+ origin_state: origin_state,
19
+ destination_state: destination_state,
20
+ fees: fees,
21
+ effective_timestamp: effective_timestamp,
22
+ effective_country_alpha2s: effective_country_alpha2s
23
+ )
15
24
  else
16
- fallback_value
25
+ FALLBACK_VALUE
17
26
  end
18
27
  rescue
19
- fallback_value
28
+ FALLBACK_VALUE
29
+ end
30
+
31
+ def self.sales_tax_website_name(country_alpha2, effective_timestamp = nil)
32
+ rates = Formula.effective_data(country_alpha2, effective_timestamp)
33
+ (rates || {}).dig(:sales_tax_website_name)
20
34
  end
21
35
 
22
- def self.domestic_value(origin_country_alpha2: nil, origin_state: nil, destination_state: nil, fees: nil)
23
- domestic_rates = Formula::DOMESTIC[origin_country_alpha2.to_s]
24
- return fallback_value if domestic_rates.nil? || domestic_rates.empty?
36
+ private
37
+
38
+ def self.domestic_value(origin_country_alpha2: nil, origin_state: nil, destination_state: nil, fees: nil, effective_timestamp: nil, effective_country_alpha2s: [])
39
+ rates = Formula.effective_data(origin_country_alpha2, effective_timestamp, effective_country_alpha2s)
40
+ return FALLBACK_VALUE if rates.nil?
25
41
 
26
42
  {
27
- sales_tax: tax_calculate(domestic_rates[:sales_tax], nil, destination_state, fees, false),
28
- provincial_sales_tax: tax_calculate(domestic_rates[:provincial_sales_taxes], origin_state, destination_state, fees, true)
43
+ sales_tax: tax_calculate(rates[:sales_tax], nil, destination_state, fees, false),
44
+ provincial_sales_tax: tax_calculate(rates[:provincial_sales_taxes], origin_state, destination_state, fees, true)
29
45
  }
30
46
  end
31
47
 
32
- def self.fallback_value
33
- { sales_tax: 0, provincial_sales_tax: 0 }
34
- end
35
-
36
48
  def self.tax_calculate(formula, origin_state, destination_state, fees, is_provincial_sales_taxes)
37
49
  if (tax_destination_formula = formula[destination_state.to_s.upcase])
38
50
  # ONLY CA has provincial_sales_taxes(QST/PST) and it only apply when ship within same state
@@ -46,11 +58,6 @@ module Easyship
46
58
  end
47
59
  end
48
60
 
49
- def self.sales_tax_website_name(country_alpha2)
50
- return if Formula::DOMESTIC[country_alpha2.to_s].nil? || Formula::DOMESTIC[country_alpha2.to_s].empty?
51
- Formula::DOMESTIC[country_alpha2][:sales_tax_website_name]
52
- end
53
-
54
61
  def self.in_eu?(country_alpha2)
55
62
  %w(NL LU EE LT HU BE PT SK HR CZ IT FI PL MT DE SI RO BG AT SE CY DK FR IE ES GR LV).include?(country_alpha2)
56
63
  end
@@ -23,8 +23,6 @@ Gem::Specification.new do |spec|
23
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  spec.require_paths = ["lib"]
25
25
 
26
- spec.add_development_dependency "bundler", "~> 1.17"
27
- spec.add_development_dependency "rake", "~> 10.0"
28
26
  spec.add_development_dependency "rspec", "~> 3.0"
29
27
  spec.add_development_dependency "byebug"
30
28
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vat-gst-calculator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aloha Chen
@@ -9,36 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2022-11-11 00:00:00.000000000 Z
12
+ date: 2023-01-31 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: bundler
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - "~>"
19
- - !ruby/object:Gem::Version
20
- version: '1.17'
21
- type: :development
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - "~>"
26
- - !ruby/object:Gem::Version
27
- version: '1.17'
28
- - !ruby/object:Gem::Dependency
29
- name: rake
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - "~>"
33
- - !ruby/object:Gem::Version
34
- version: '10.0'
35
- type: :development
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - "~>"
40
- - !ruby/object:Gem::Version
41
- version: '10.0'
42
14
  - !ruby/object:Gem::Dependency
43
15
  name: rspec
44
16
  requirement: !ruby/object:Gem::Requirement