super_good-solidus_taxjar 0.17.1 → 0.18.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +19 -13
  3. data/.travis.yml +11 -5
  4. data/CHANGELOG.md +30 -0
  5. data/Gemfile +21 -5
  6. data/LICENSE +26 -0
  7. data/PULL_REQUEST_TEMPLATE.md +19 -0
  8. data/README.md +20 -11
  9. data/Rakefile +4 -17
  10. data/bin/rails +7 -0
  11. data/bin/rails-engine +13 -0
  12. data/bin/rails-sandbox +16 -0
  13. data/bin/rake +7 -0
  14. data/bin/sandbox +84 -0
  15. data/lib/super_good-solidus_taxjar.rb +4 -0
  16. data/lib/super_good/engine.rb +8 -0
  17. data/lib/super_good/solidus_taxjar.rb +11 -6
  18. data/lib/super_good/solidus_taxjar/addresses.rb +63 -0
  19. data/lib/super_good/solidus_taxjar/api.rb +20 -11
  20. data/lib/super_good/solidus_taxjar/api_params.rb +19 -9
  21. data/lib/super_good/solidus_taxjar/calculator_helper.rb +4 -10
  22. data/lib/super_good/solidus_taxjar/discount_calculator.rb +1 -1
  23. data/lib/super_good/solidus_taxjar/tax_calculator.rb +12 -12
  24. data/lib/super_good/solidus_taxjar/tax_rate_calculator.rb +6 -7
  25. data/lib/super_good/solidus_taxjar/version.rb +2 -2
  26. data/spec/spec_helper.rb +20 -0
  27. data/spec/super_good/solidus_taxjar/addresses_spec.rb +288 -0
  28. data/spec/super_good/solidus_taxjar/api_params_spec.rb +402 -0
  29. data/spec/super_good/solidus_taxjar/api_spec.rb +205 -0
  30. data/spec/super_good/solidus_taxjar/discount_calculator_spec.rb +13 -0
  31. data/spec/super_good/solidus_taxjar/tax_calculator_spec.rb +332 -0
  32. data/spec/super_good/solidus_taxjar/tax_rate_calculator_spec.rb +116 -0
  33. data/spec/super_good/solidus_taxjar_spec.rb +77 -0
  34. data/super_good-solidus_taxjar.gemspec +17 -14
  35. metadata +35 -10
  36. data/LICENSE.txt +0 -21
@@ -1,6 +1,6 @@
1
- require 'solidus_core'
2
- require 'solidus_support'
3
- require 'taxjar'
1
+ require "solidus_core"
2
+ require "solidus_support"
3
+ require "taxjar"
4
4
 
5
5
  require "super_good/solidus_taxjar/version"
6
6
  require "super_good/solidus_taxjar/api_params"
@@ -9,9 +9,10 @@ require "super_good/solidus_taxjar/calculator_helper"
9
9
  require "super_good/solidus_taxjar/tax_calculator"
10
10
  require "super_good/solidus_taxjar/tax_rate_calculator"
11
11
  require "super_good/solidus_taxjar/discount_calculator"
12
+ require "super_good/solidus_taxjar/addresses"
12
13
 
13
14
  module SuperGood
14
- module SolidusTaxJar
15
+ module SolidusTaxjar
15
16
  class << self
16
17
  attr_accessor :cache_duration
17
18
  attr_accessor :cache_key
@@ -25,15 +26,19 @@ module SuperGood
25
26
  attr_accessor :taxable_address_check
26
27
  attr_accessor :taxable_order_check
27
28
  attr_accessor :test_mode
29
+
30
+ def api
31
+ ::SuperGood::SolidusTaxjar::Api.new
32
+ end
28
33
  end
29
34
 
30
35
  self.cache_duration = 3.hours
31
36
  self.cache_key = ->(record) {
32
37
  record_type = record.class.name.demodulize.underscore
33
- APIParams.send("#{record_type}_params", record).to_json
38
+ ApiParams.send("#{record_type}_params", record).to_json
34
39
  }
35
40
  self.custom_order_params = ->(order) { {} }
36
- self.discount_calculator = ::SuperGood::SolidusTaxJar::DiscountCalculator
41
+ self.discount_calculator = ::SuperGood::SolidusTaxjar::DiscountCalculator
37
42
  self.exception_handler = ->(e) {
38
43
  Rails.logger.error "An error occurred while fetching TaxJar tax rates - #{e}: #{e.message}"
39
44
  }
@@ -0,0 +1,63 @@
1
+ module SuperGood
2
+ module SolidusTaxjar
3
+ class Addresses
4
+ class << self
5
+ def normalize(spree_address)
6
+ new.normalize(spree_address)
7
+ end
8
+
9
+ def possibilities(spree_address)
10
+ new.possibilities(spree_address)
11
+ end
12
+ end
13
+
14
+ def initialize(api: ::SuperGood::SolidusTaxjar.api)
15
+ @api = api
16
+ end
17
+
18
+ def normalize(spree_address)
19
+ taxjar_address = taxjar_addresses(spree_address).first
20
+
21
+ return if taxjar_address.nil?
22
+
23
+ Spree::Address.immutable_merge(spree_address, {
24
+ country: us, # TaxJar only supports the US currently.
25
+ state: state(taxjar_address.state),
26
+ zipcode: taxjar_address.zip,
27
+ city: taxjar_address.city,
28
+ address1: taxjar_address.street
29
+ })
30
+ end
31
+
32
+ def possibilities(spree_address)
33
+ taxjar_addresses(spree_address).map { |taxjar_address|
34
+ Spree::Address.immutable_merge(spree_address, {
35
+ country: us, # TaxJar only supports the US currently.
36
+ state: state(taxjar_address.state),
37
+ zipcode: taxjar_address.zip,
38
+ city: taxjar_address.city,
39
+ address1: taxjar_address.street
40
+ })
41
+ }
42
+ end
43
+
44
+ private
45
+
46
+ attr_reader :api
47
+
48
+ def taxjar_addresses(spree_address)
49
+ api.validate_spree_address(spree_address)
50
+ rescue Taxjar::Error::NotFound
51
+ []
52
+ end
53
+
54
+ def us
55
+ Spree::Country.find_by iso: "US"
56
+ end
57
+
58
+ def state(abbr)
59
+ us.states.find_by_abbr abbr
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,11 +1,16 @@
1
1
  module SuperGood
2
- module SolidusTaxJar
3
- class API
2
+ module SolidusTaxjar
3
+ class Api
4
4
  def self.default_taxjar_client
5
- ::Taxjar::Client.new(
5
+ client = ::Taxjar::Client.new(
6
6
  api_key: ENV.fetch("TAXJAR_API_KEY"),
7
- api_url: ENV.fetch("TAXJAR_API_URL") { 'https://api.taxjar.com' } # Sandbox URL: https://api.sandbox.taxjar.com
7
+ api_url: ENV.fetch("TAXJAR_API_URL") { "https://api.taxjar.com" } # Sandbox URL: https://api.sandbox.taxjar.com
8
8
  )
9
+ client.set_api_config('headers', {
10
+ 'x-api-version' => '2020-08-07',
11
+ 'plugin' => 'supergoodsolidustaxjar'
12
+ })
13
+ client
9
14
  end
10
15
 
11
16
  def initialize(taxjar_client: self.class.default_taxjar_client)
@@ -13,8 +18,8 @@ module SuperGood
13
18
  end
14
19
 
15
20
  def tax_for(order)
16
- taxjar_client.tax_for_order(APIParams.order_params(order)).tap do |taxes|
17
- next unless SuperGood::SolidusTaxJar.logging_enabled
21
+ taxjar_client.tax_for_order(ApiParams.order_params(order)).tap do |taxes|
22
+ next unless SuperGood::SolidusTaxjar.logging_enabled
18
23
 
19
24
  Rails.logger.info(
20
25
  "TaxJar response for #{order.number}: #{taxes.to_h.inspect}"
@@ -23,19 +28,19 @@ module SuperGood
23
28
  end
24
29
 
25
30
  def tax_rate_for(address)
26
- taxjar_client.tax_for_order(APIParams.tax_rate_address_params(address)).rate
31
+ taxjar_client.tax_for_order(ApiParams.tax_rate_address_params(address)).rate
27
32
  end
28
33
 
29
34
  def tax_rates_for(address)
30
- taxjar_client.rates_for_location(*APIParams.address_params(address))
35
+ taxjar_client.rates_for_location(*ApiParams.address_params(address))
31
36
  end
32
37
 
33
38
  def create_transaction_for(order)
34
- taxjar_client.create_order APIParams.transaction_params(order)
39
+ taxjar_client.create_order ApiParams.transaction_params(order)
35
40
  end
36
41
 
37
42
  def update_transaction_for(order)
38
- taxjar_client.update_order APIParams.transaction_params(order)
43
+ taxjar_client.update_order ApiParams.transaction_params(order)
39
44
  end
40
45
 
41
46
  def delete_transaction_for(order)
@@ -43,7 +48,11 @@ module SuperGood
43
48
  end
44
49
 
45
50
  def create_refund_for(reimbursement)
46
- taxjar_client.create_refund APIParams.refund_params(reimbursement)
51
+ taxjar_client.create_refund ApiParams.refund_params(reimbursement)
52
+ end
53
+
54
+ def validate_spree_address(spree_address)
55
+ taxjar_client.validate_address ApiParams.validate_address_params(spree_address)
47
56
  end
48
57
 
49
58
  private
@@ -1,6 +1,6 @@
1
1
  module SuperGood
2
- module SolidusTaxJar
3
- module APIParams
2
+ module SolidusTaxjar
3
+ module ApiParams
4
4
  class << self
5
5
  def order_params(order)
6
6
  {}
@@ -8,9 +8,9 @@ module SuperGood
8
8
  .merge(order_address_params(order.tax_address))
9
9
  .merge(line_items_params(order.line_items))
10
10
  .merge(shipping: shipping(order))
11
- .merge(SuperGood::SolidusTaxJar.custom_order_params.(order))
11
+ .merge(SuperGood::SolidusTaxjar.custom_order_params.call(order))
12
12
  .tap do |params|
13
- next unless SuperGood::SolidusTaxJar.logging_enabled
13
+ next unless SuperGood::SolidusTaxjar.logging_enabled
14
14
 
15
15
  Rails.logger.info(
16
16
  "TaxJar params for #{order.number}: #{params.inspect}"
@@ -33,7 +33,7 @@ module SuperGood
33
33
  def tax_rate_address_params(address)
34
34
  {
35
35
  amount: 100,
36
- shipping: 0,
36
+ shipping: 0
37
37
  }.merge(order_address_params(address))
38
38
  end
39
39
 
@@ -66,12 +66,22 @@ module SuperGood
66
66
  )
67
67
  end
68
68
 
69
+ def validate_address_params(spree_address)
70
+ {
71
+ country: spree_address.country&.iso,
72
+ state: spree_address.state&.abbr || spree_address.state_name,
73
+ zip: spree_address.zipcode,
74
+ city: spree_address.city,
75
+ street: spree_address.address1
76
+ }
77
+ end
78
+
69
79
  private
70
80
 
71
81
  def customer_params(order)
72
82
  return {} unless order.user_id
73
83
 
74
- { customer_id: order.user_id.to_s }
84
+ {customer_id: order.user_id.to_s}
75
85
  end
76
86
 
77
87
  def order_address_params(address)
@@ -80,7 +90,7 @@ module SuperGood
80
90
  to_zip: address.zipcode,
81
91
  to_city: address.city,
82
92
  to_state: address&.state&.abbr || address.state_name,
83
- to_street: address.address1,
93
+ to_street: address.address1
84
94
  }
85
95
  end
86
96
 
@@ -123,11 +133,11 @@ module SuperGood
123
133
  end
124
134
 
125
135
  def discount(line_item)
126
- ::SuperGood::SolidusTaxJar.discount_calculator.new(line_item).discount
136
+ ::SuperGood::SolidusTaxjar.discount_calculator.new(line_item).discount
127
137
  end
128
138
 
129
139
  def shipping(order)
130
- SuperGood::SolidusTaxJar.shipping_calculator.(order)
140
+ SuperGood::SolidusTaxjar.shipping_calculator.call(order)
131
141
  end
132
142
 
133
143
  def sales_tax(order)
@@ -1,14 +1,8 @@
1
1
  module SuperGood
2
- module SolidusTaxJar
2
+ module SolidusTaxjar
3
3
  module CalculatorHelper
4
4
  extend ActiveSupport::Concern
5
5
 
6
- class_methods do
7
- def default_api
8
- ::SuperGood::SolidusTaxJar::API.new
9
- end
10
- end
11
-
12
6
  def incomplete_address?(address)
13
7
  return true if address.is_a?(Spree::Tax::TaxLocation)
14
8
 
@@ -22,14 +16,14 @@ module SuperGood
22
16
  end
23
17
 
24
18
  def taxable_address?(address)
25
- SuperGood::SolidusTaxJar.taxable_address_check.(address)
19
+ SuperGood::SolidusTaxjar.taxable_address_check.call(address)
26
20
  end
27
21
 
28
22
  def cache
29
23
  if !Rails.env.test?
30
24
  Rails.cache.fetch(
31
25
  cache_key,
32
- expires_in: SuperGood::SolidusTaxJar.cache_duration
26
+ expires_in: SuperGood::SolidusTaxjar.cache_duration
33
27
  ) { yield }
34
28
  else
35
29
  yield
@@ -37,7 +31,7 @@ module SuperGood
37
31
  end
38
32
 
39
33
  def exception_handler
40
- SuperGood::SolidusTaxJar.exception_handler
34
+ SuperGood::SolidusTaxjar.exception_handler
41
35
  end
42
36
  end
43
37
  end
@@ -1,5 +1,5 @@
1
1
  module SuperGood
2
- module SolidusTaxJar
2
+ module SolidusTaxjar
3
3
  class DiscountCalculator
4
4
  def initialize(line_item)
5
5
  @line_item = line_item
@@ -1,15 +1,15 @@
1
1
  module SuperGood
2
- module SolidusTaxJar
2
+ module SolidusTaxjar
3
3
  class TaxCalculator
4
4
  include CalculatorHelper
5
5
 
6
- def initialize(order, api: self.class.default_api)
6
+ def initialize(order, api: SuperGood::SolidusTaxjar.api)
7
7
  @order = order
8
8
  @api = api
9
9
  end
10
10
 
11
11
  def calculate
12
- return no_tax if SuperGood::SolidusTaxJar.test_mode
12
+ return no_tax if SuperGood::SolidusTaxjar.test_mode
13
13
  return no_tax if incomplete_address?(order.tax_address) || order.line_items.none?
14
14
  return no_tax unless taxable_order? order
15
15
  return no_tax unless taxable_address? order.tax_address
@@ -23,8 +23,8 @@ module SuperGood
23
23
  shipment_taxes: shipment_taxes
24
24
  )
25
25
  end
26
- rescue StandardError => e
27
- exception_handler.(e)
26
+ rescue => e
27
+ exception_handler.call(e)
28
28
  no_tax
29
29
  end
30
30
 
@@ -34,7 +34,7 @@ module SuperGood
34
34
 
35
35
  def line_item_taxes
36
36
  @line_item_taxes ||=
37
- taxjar_breakdown.line_items.map do |taxjar_line_item|
37
+ taxjar_breakdown.line_items.map { |taxjar_line_item|
38
38
  spree_line_item_id = taxjar_line_item.id.to_i
39
39
 
40
40
  # Searching in memory because this association is loaded and most
@@ -48,13 +48,13 @@ module SuperGood
48
48
  amount: taxjar_line_item.tax_collectable,
49
49
  included_in_price: false
50
50
  )
51
- end
51
+ }
52
52
  end
53
53
 
54
54
  def shipment_taxes
55
55
  @shipment_taxes ||=
56
56
  if taxjar_breakdown.shipping? &&
57
- (total_shipping_tax = taxjar_breakdown.shipping.tax_collectable) != 0
57
+ (total_shipping_tax = taxjar_breakdown.shipping.tax_collectable) != 0
58
58
 
59
59
  # Distribute shipping tax across shipments:
60
60
  # TaxJar does not provide a breakdown of shipping taxes, so we have
@@ -114,22 +114,22 @@ module SuperGood
114
114
  end
115
115
 
116
116
  def cache_key
117
- SuperGood::SolidusTaxJar.cache_key.(order)
117
+ SuperGood::SolidusTaxjar.cache_key.call(order)
118
118
  end
119
119
 
120
120
  def taxable_order?(order)
121
- SuperGood::SolidusTaxJar.taxable_order_check.(order)
121
+ SuperGood::SolidusTaxjar.taxable_order_check.call(order)
122
122
  end
123
123
 
124
124
  def shipping_tax_label(shipment, shipping_tax)
125
- SuperGood::SolidusTaxJar.shipping_tax_label_maker.(
125
+ SuperGood::SolidusTaxjar.shipping_tax_label_maker.call(
126
126
  shipment,
127
127
  shipping_tax
128
128
  )
129
129
  end
130
130
 
131
131
  def line_item_tax_label(taxjar_line_item, spree_line_item)
132
- SuperGood::SolidusTaxJar.line_item_tax_label_maker.(taxjar_line_item, spree_line_item)
132
+ SuperGood::SolidusTaxjar.line_item_tax_label_maker.call(taxjar_line_item, spree_line_item)
133
133
  end
134
134
  end
135
135
  end
@@ -1,22 +1,21 @@
1
1
  module SuperGood
2
- module SolidusTaxJar
2
+ module SolidusTaxjar
3
3
  class TaxRateCalculator
4
4
  include CalculatorHelper
5
- def initialize(address, api: self.class.default_api)
5
+ def initialize(address, api: SuperGood::SolidusTaxjar.api)
6
6
  @address = address
7
7
  @api = api
8
8
  end
9
9
 
10
10
  def calculate
11
- return no_rate if SuperGood::SolidusTaxJar.test_mode
11
+ return no_rate if SuperGood::SolidusTaxjar.test_mode
12
12
  return no_rate if incomplete_address?(address)
13
13
  return no_rate unless taxable_address?(address)
14
14
  cache do
15
15
  api.tax_rate_for(address).to_d
16
16
  end
17
-
18
- rescue StandardError => e
19
- exception_handler.(e)
17
+ rescue => e
18
+ exception_handler.call(e)
20
19
  no_rate
21
20
  end
22
21
 
@@ -29,7 +28,7 @@ module SuperGood
29
28
  end
30
29
 
31
30
  def cache_key
32
- SuperGood::SolidusTaxJar.cache_key.(address)
31
+ SuperGood::SolidusTaxjar.cache_key.call(address)
33
32
  end
34
33
  end
35
34
  end
@@ -1,5 +1,5 @@
1
1
  module SuperGood
2
- module SolidusTaxJar
3
- VERSION = "0.17.1"
2
+ module SolidusTaxjar
3
+ VERSION = "0.18.1"
4
4
  end
5
5
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Configure Rails Environment
4
+ ENV["RAILS_ENV"] = "test"
5
+
6
+ require File.expand_path("dummy/config/environment.rb", __dir__).tap { |file|
7
+ # Create the dummy app if it's still missing.
8
+ system "bin/rake extension:test_app" unless File.exist? file
9
+ }
10
+
11
+ require "solidus_dev_support/rspec/rails_helper"
12
+
13
+ # Requires supporting ruby files with custom matchers and macros, etc,
14
+ # in spec/support/ and its subdirectories.
15
+ Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each { |f| require f }
16
+
17
+ RSpec.configure do |config|
18
+ config.infer_spec_type_from_file_location!
19
+ config.use_transactional_fixtures = false
20
+ end