solidus_avatax 1.0.0 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c3f13da28a6ec0b6a5dedfc5db7f298ae881ba1c
4
- data.tar.gz: bbf4d9d57e5b13968824d4c9d8520c135fdb9e3e
3
+ metadata.gz: 4525b6e8313606fbd57767df4d08f36d55fa200b
4
+ data.tar.gz: 51e8687c4913ed18239ec2d4b49c69fb52a18d82
5
5
  SHA512:
6
- metadata.gz: a79dfd4cfd4739b2c4de0bb977bbddecc2dd95da488b22f9c45c1dbf9616826d28063db60cc43b51526384cece47d486f5a0ed9d71bb04fd484fdfa21cf1fce2
7
- data.tar.gz: 6da751a3e486acca13773199cfef7eb2d37774b1a1a4887838be4f115a55a27ad10a6e4b1b555d875a3f8b3ecdd88ff7e3956ea7c1a8e76c184aae536f7d1727
6
+ metadata.gz: b344c75e4567fb07469a05caa6956a9c4df9ba79f60226a3c87ef7582636abced2c66d47e2214421691886e63f52e662ada8b84c5dcac2bb1bd650128c040ef3
7
+ data.tar.gz: c50aa1946c3e50fcb4d252edfdbc54384445129c0554cc91f393aee18c6bb77ec4ab43353aef221f5b1bb57e56e663644a3bba334718d6367c05f3952c45fe97
@@ -0,0 +1,14 @@
1
+ sudo: false
2
+ cache: bundler
3
+ language: ruby
4
+ env:
5
+ matrix:
6
+ - SOLIDUS_BRANCH=v1.1 DB=mysql
7
+ - SOLIDUS_BRANCH=v1.2 DB=mysql
8
+ - SOLIDUS_BRANCH=v1.1 DB=postgres
9
+ - SOLIDUS_BRANCH=v1.2 DB=postgres
10
+ script:
11
+ - bundle exec rake test_app
12
+ - bundle exec rspec
13
+ rvm:
14
+ - 2.1.8
data/Gemfile CHANGED
@@ -1,8 +1,12 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gem "solidus", github: "solidusio/solidus", branch: "master"
3
+ branch = ENV.fetch('SOLIDUS_BRANCH', 'v1.2')
4
+ gem "solidus", github: "solidusio/solidus", branch: branch
4
5
  gem "solidus_auth_devise", "~> 1.0"
5
6
 
7
+ gem 'pg'
8
+ gem 'mysql2'
9
+
6
10
  group :development, :test do
7
11
  gem "pry-rails"
8
12
  gem 'pry-byebug'
data/README.md CHANGED
@@ -22,6 +22,22 @@ rails g solidus_avatax:install
22
22
  Configuration
23
23
  -------------
24
24
 
25
+ #### Disabling Avatax and Avatax API Timeouts
26
+
27
+ In case of service problems or outages on Avatax's end, you can disable Avatax
28
+ or change the API timeout. These values are stored in the database so that they
29
+ can be applied instantly and without restarting your application. To change the
30
+ values create a new SpreeAvatax::Config. The default values are:
31
+
32
+ ```ruby
33
+ SpreeAvatax::Config.create!(enabled: true, timeout: SpreeAvatax::Config::DEFAULT_TIMEOUT)
34
+ ```
35
+
36
+ This is an append-only table and solidus_avatax will read these config values
37
+ from the last record (by id).
38
+
39
+ #### Short Ships
40
+
25
41
  If you want to notify Avatax about short ships you should configure the
26
42
  following:
27
43
 
@@ -0,0 +1,38 @@
1
+ module SpreeAvatax
2
+ class Config < Spree::Base
3
+ DEFAULT_TIMEOUT = 20
4
+
5
+ class << self
6
+ attr_accessor :username
7
+ attr_accessor :password
8
+ attr_accessor :company_code
9
+ attr_accessor :service_url
10
+ # These error handlers should be objects that respond to "call" and accept an order and an
11
+ # exception as arguments. This allows you to ignore certain errors or handle them in
12
+ # specific ways.
13
+ attr_accessor :sales_invoice_generate_error_handler
14
+ attr_accessor :sales_invoice_commit_error_handler
15
+ attr_accessor :sales_invoice_cancel_error_handler
16
+
17
+ # These configurations are stored in the database so that they can be
18
+ # updated immediately and synchronously across all servers in the event of
19
+ # an outage, without a redeploy or restart.
20
+ def timeout
21
+ (config = active) ? config.timeout : DEFAULT_TIMEOUT
22
+ end
23
+
24
+ def enabled
25
+ (config = active) ? config.enabled : true
26
+ end
27
+
28
+ private
29
+
30
+ def active
31
+ order(:id).last
32
+ end
33
+ end
34
+
35
+ validates :enabled, inclusion: {in: [true, false]}
36
+ validates :timeout, presence: true
37
+ end
38
+ end
@@ -31,6 +31,11 @@ class SpreeAvatax::ReturnInvoice < ActiveRecord::Base
31
31
  # After the reimbursement completes the ".finalize" method will get called and we'll commit the
32
32
  # return invoice.
33
33
  def generate(reimbursement)
34
+ if !SpreeAvatax::Config.enabled
35
+ logger.info("Avatax disabled. Skipping ReturnInvoice.generate for reimbursement #{reimbursement.number}")
36
+ return
37
+ end
38
+
34
39
  success_result = get_tax(reimbursement)
35
40
 
36
41
  if reimbursement.return_invoice
@@ -74,6 +79,11 @@ class SpreeAvatax::ReturnInvoice < ActiveRecord::Base
74
79
  # On failure it will raise.
75
80
  # On success it markes the invoice as committed.
76
81
  def finalize(reimbursement)
82
+ if !SpreeAvatax::Config.enabled
83
+ logger.info("Avatax disabled. Skipping ReturnInvoice.finalize for reimbursement #{reimbursement.number}")
84
+ return
85
+ end
86
+
77
87
  post_tax(reimbursement.return_invoice)
78
88
 
79
89
  reimbursement.return_invoice.update!(committed: true)
@@ -87,7 +97,7 @@ class SpreeAvatax::ReturnInvoice < ActiveRecord::Base
87
97
  avatax_logger.info "AVATAX_REQUEST context=get_tax reimbursement_id=#{reimbursement.id}"
88
98
  avatax_logger.debug params.to_json
89
99
 
90
- result = tax_svc.gettax(params)
100
+ result = SpreeAvatax::Shared.get_tax(params)
91
101
  require_success!(result, reimbursement, 'get_tax')
92
102
 
93
103
  result
@@ -99,7 +109,7 @@ class SpreeAvatax::ReturnInvoice < ActiveRecord::Base
99
109
  avatax_logger.info "AVATAX_REQUEST context=post_tax reimbursement_id=#{return_invoice.reimbursement.id} return_invoice_id=#{return_invoice.id}"
100
110
  avatax_logger.debug params.to_json
101
111
 
102
- result = tax_svc.posttax(params)
112
+ result = SpreeAvatax::Shared.post_tax(params)
103
113
  require_success!(result, return_invoice.reimbursement, 'post_tax')
104
114
 
105
115
  result
@@ -184,14 +194,5 @@ class SpreeAvatax::ReturnInvoice < ActiveRecord::Base
184
194
  totaltax: return_invoice.additional_tax_total,
185
195
  }
186
196
  end
187
-
188
- def tax_svc
189
- @tax_svc ||= AvaTax::TaxService.new({
190
- username: SpreeAvatax::Config.username,
191
- password: SpreeAvatax::Config.password,
192
- service_url: SpreeAvatax::Config.service_url,
193
- clientname: 'Spree::Avatax',
194
- })
195
- end
196
197
  end
197
198
  end
@@ -23,6 +23,11 @@ class SpreeAvatax::SalesInvoice < ActiveRecord::Base
23
23
  def generate(order)
24
24
  bench_start = Time.now
25
25
 
26
+ if !SpreeAvatax::Config.enabled
27
+ logger.info("Avatax disabled. Skipping SalesInvoice.generate for order #{order.number}")
28
+ return
29
+ end
30
+
26
31
  return if order.completed? || !SpreeAvatax::Shared.taxable_order?(order)
27
32
 
28
33
  taxable_records = order.line_items + order.shipments
@@ -42,6 +47,8 @@ class SpreeAvatax::SalesInvoice < ActiveRecord::Base
42
47
  end
43
48
  end
44
49
 
50
+ SpreeAvatax::SalesShared.update_taxes(order, tax_line_data)
51
+
45
52
  sales_invoice = order.create_avatax_sales_invoice!({
46
53
  transaction_id: result[:transaction_id],
47
54
  doc_id: result[:doc_id],
@@ -51,8 +58,6 @@ class SpreeAvatax::SalesInvoice < ActiveRecord::Base
51
58
  additional_tax_total: result[:total_tax],
52
59
  })
53
60
 
54
- SpreeAvatax::SalesShared.update_taxes(order, tax_line_data)
55
-
56
61
  sales_invoice
57
62
  rescue Exception => e
58
63
  if SpreeAvatax::Config.sales_invoice_generate_error_handler
@@ -66,6 +71,11 @@ class SpreeAvatax::SalesInvoice < ActiveRecord::Base
66
71
  end
67
72
 
68
73
  def commit(order)
74
+ if !SpreeAvatax::Config.enabled
75
+ logger.info("Avatax disabled. Skipping SalesInvoice.commit for order #{order.number}")
76
+ return
77
+ end
78
+
69
79
  return if !SpreeAvatax::Shared.taxable_order?(order)
70
80
 
71
81
  raise CommitInvoiceNotFound.new("No invoice for order #{order.number}") if order.avatax_sales_invoice.nil?
@@ -84,6 +94,11 @@ class SpreeAvatax::SalesInvoice < ActiveRecord::Base
84
94
  end
85
95
 
86
96
  def cancel(order)
97
+ if !SpreeAvatax::Config.enabled
98
+ logger.info("Avatax disabled. Skipping SalesInvoice.cancel for order #{order.number}")
99
+ return
100
+ end
101
+
87
102
  return if order.avatax_sales_invoice.nil?
88
103
 
89
104
  result = cancel_tax(order.avatax_sales_invoice)
@@ -108,7 +123,7 @@ class SpreeAvatax::SalesInvoice < ActiveRecord::Base
108
123
  logger.info "[avatax] posttax sales_invoice=#{sales_invoice.id} order=#{sales_invoice.order_id}"
109
124
  logger.debug { "[avatax] params: #{params.to_json}" }
110
125
 
111
- response = SpreeAvatax::Shared.tax_svc.posttax(params)
126
+ response = SpreeAvatax::Shared.post_tax(params)
112
127
  SpreeAvatax::Shared.require_success!(response)
113
128
 
114
129
  response
@@ -120,8 +135,7 @@ class SpreeAvatax::SalesInvoice < ActiveRecord::Base
120
135
  logger.info "[avatax] canceltax sales_invoice=#{sales_invoice.id}"
121
136
  logger.debug { "[avatax] params: #{params.to_json}" }
122
137
 
123
- response = SpreeAvatax::Shared.tax_svc.canceltax(params)
124
-
138
+ response = SpreeAvatax::Shared.cancel_tax(params)
125
139
  SpreeAvatax::Shared.require_success!(response)
126
140
 
127
141
  response
@@ -18,7 +18,8 @@ module SpreeAvatax::SalesShared
18
18
  logger.info "[avatax] gettax order=#{order.id} doc_type=#{doc_type}"
19
19
  logger.debug { "[avatax] params: #{params.to_json}" }
20
20
 
21
- response = SpreeAvatax::Shared.tax_svc.gettax(params)
21
+ response = SpreeAvatax::Shared.get_tax(params)
22
+
22
23
  SpreeAvatax::Shared.require_success!(response)
23
24
 
24
25
  response
@@ -30,7 +31,7 @@ module SpreeAvatax::SalesShared
30
31
  tax_line_data.each do |data|
31
32
  record, tax_line = data[:record], data[:tax_line]
32
33
 
33
- record.update_column(:pre_tax_amount, record.discounted_amount)
34
+ record.update_column(:pre_tax_amount, record.discounted_amount.round(2))
34
35
 
35
36
  tax = BigDecimal.new(tax_line[:tax]).abs
36
37
 
@@ -72,7 +73,7 @@ module SpreeAvatax::SalesShared
72
73
  if data[avatax_id]
73
74
  data[avatax_id][:tax_line] = tax_line
74
75
  else
75
- raise InvalidApiResponse.new("Couldn't find #{avatax_id.inspect}")
76
+ raise InvalidApiResponse.new("Couldn't find #{avatax_id.inspect} from avatax response in known ids #{data.keys.inspect}")
76
77
  end
77
78
  end
78
79
 
@@ -98,19 +99,32 @@ module SpreeAvatax::SalesShared
98
99
  def reset_tax_attributes(order)
99
100
  return if order.completed?
100
101
 
102
+ # Delete the avatax_sales_invoice to avoid accidentally committing it
103
+ # later.
104
+ if invoice = order.avatax_sales_invoice
105
+ if invoice.committed_at
106
+ raise SpreeAvatax::SalesInvoice::AlreadyCommittedError.new(
107
+ "Tried to clear tax attributes for already-committed order #{order.number}"
108
+ )
109
+ else
110
+ invoice.destroy!
111
+ end
112
+ end
113
+
101
114
  destroyed_adjustments = order.all_adjustments.tax.destroy_all
102
115
  return if destroyed_adjustments.empty?
103
116
 
104
- order.line_items.each do |line_item|
105
- line_item.update_attributes!({
117
+ taxable_records = order.line_items + order.shipments
118
+ taxable_records.each do |taxable_record|
119
+ taxable_record.update_attributes!({
106
120
  additional_tax_total: 0,
107
121
  adjustment_total: 0,
108
- pre_tax_amount: 0,
122
+ pre_tax_amount: taxable_record.discounted_amount.round(2),
109
123
  included_tax_total: 0,
110
124
  })
111
125
 
112
- Spree::ItemAdjustments.new(line_item).update
113
- line_item.save!
126
+ Spree::ItemAdjustments.new(taxable_record).update
127
+ taxable_record.save!
114
128
  end
115
129
 
116
130
  order.update_attributes!({
@@ -28,6 +28,18 @@ module SpreeAvatax::Shared
28
28
  order.line_items.present? && order.ship_address.present?
29
29
  end
30
30
 
31
+ def get_tax(params)
32
+ call_tax_svc_with_timeout(:gettax, params)
33
+ end
34
+
35
+ def post_tax(params)
36
+ call_tax_svc_with_timeout(:posttax, params)
37
+ end
38
+
39
+ def cancel_tax(params)
40
+ call_tax_svc_with_timeout(:canceltax, params)
41
+ end
42
+
31
43
  def tax_svc
32
44
  @tax_svc ||= AvaTax::TaxService.new({
33
45
  username: SpreeAvatax::Config.username,
@@ -37,6 +49,13 @@ module SpreeAvatax::Shared
37
49
  })
38
50
  end
39
51
 
52
+ # We looked at the code in the AvaTax gem and using timeout here seems safe.
53
+ def call_tax_svc_with_timeout(method, *args)
54
+ Timeout.timeout(SpreeAvatax::Config.timeout, SpreeAvatax::AvataxTimeout) do
55
+ tax_svc.public_send(method, *args)
56
+ end
57
+ end
58
+
40
59
  def require_success!(response)
41
60
  if response[:result_code] == 'Success'
42
61
  logger.info "[avatax] response - result=success doc_id=#{response[:doc_id]} doc_code=#{response[:doc_code]} transaction_id=#{response[:transaction_id]}"
@@ -22,6 +22,11 @@ class SpreeAvatax::ShortShipReturnInvoice < ActiveRecord::Base
22
22
  #
23
23
  # On failure it will raise.
24
24
  def generate(unit_cancels:)
25
+ if !SpreeAvatax::Config.enabled
26
+ logger.info("Avatax disabled. Skipping ShortShipReturnInvoice.generate for unit_cancels #{unit_cancels.map(&:id)}")
27
+ return
28
+ end
29
+
25
30
  inventory_units = unit_cancels.map(&:inventory_unit)
26
31
 
27
32
  order_ids = inventory_units.map(&:order_id).uniq
@@ -48,7 +53,7 @@ class SpreeAvatax::ShortShipReturnInvoice < ActiveRecord::Base
48
53
  logger.info("[avatax] gettax unit_cancel_ids=#{unit_cancels.map(&:id)} doc_type=#{DOC_TYPE}")
49
54
  logger.debug("[avatax] params: " + params.to_json)
50
55
 
51
- response = SpreeAvatax::Shared.tax_svc.gettax(params)
56
+ response = SpreeAvatax::Shared.get_tax(params)
52
57
  SpreeAvatax::Shared.require_success!(response)
53
58
 
54
59
  response
@@ -0,0 +1,10 @@
1
+ class AddSpreeAvataxConfigs < ActiveRecord::Migration
2
+ def change
3
+ create_table 'spree_avatax_configs' do |t|
4
+ t.boolean 'enabled', null: false
5
+ t.float 'timeout', null: false
6
+
7
+ t.datetime 'created_at', null: false
8
+ end
9
+ end
10
+ end
@@ -1,4 +1,7 @@
1
1
  require 'spree_core'
2
- require "spree_avatax/config"
3
2
  require 'spree_avatax/engine'
4
3
  require 'avatax_taxservice'
4
+
5
+ module SpreeAvatax
6
+ class AvataxTimeout < Timeout::Error; end
7
+ end
@@ -22,4 +22,9 @@ FactoryGirl.define do
22
22
  pre_tax_total { reimbursement.return_items.sum(:pre_tax_amount) }
23
23
  additional_tax_total { reimbursement.return_items.sum(:additional_tax_total) }
24
24
  end
25
+
26
+ factory :avatax_config, class: SpreeAvatax::Config do
27
+ enabled true
28
+ timeout SpreeAvatax::Config::DEFAULT_TIMEOUT
29
+ end
25
30
  end
@@ -29,13 +29,6 @@ namespace :spree_avatax do
29
29
 
30
30
  # PREP TO ITERATE OVER ORDERS
31
31
 
32
- tax_svc = AvaTax::TaxService.new({
33
- username: SpreeAvatax::Config.username,
34
- password: SpreeAvatax::Config.password,
35
- service_url: SpreeAvatax::Config.service_url,
36
- clientname: 'Spree::Avatax',
37
- })
38
-
39
32
  handle_result_errors = ->(result, order, request_method) do
40
33
  puts
41
34
  puts "** Error on order id=#{order.id} number=#{order.number} **"
@@ -63,7 +56,7 @@ namespace :spree_avatax do
63
56
  detaillevel: 'Tax',
64
57
  }
65
58
 
66
- history_result = tax_svc.gettaxhistory(history_request)
59
+ history_result = SpreeAvatax::Shared.tax_svc.gettaxhistory(history_request)
67
60
 
68
61
  if history_result[:result_code] != 'Success'
69
62
  error_count += 1
@@ -91,7 +84,7 @@ namespace :spree_avatax do
91
84
  totaltax: history_result[:get_tax_result][:total_tax],
92
85
  }
93
86
 
94
- post_result = tax_svc.posttax(post_request)
87
+ post_result = SpreeAvatax::Shared.tax_svc.posttax(post_request)
95
88
 
96
89
  if post_result[:result_code] != 'Success'
97
90
  error_count += 1
@@ -2,7 +2,7 @@
2
2
  Gem::Specification.new do |s|
3
3
  s.platform = Gem::Platform::RUBY
4
4
  s.name = "solidus_avatax"
5
- s.version = "1.0.0"
5
+ s.version = "1.1.0"
6
6
  s.summary = "Avatax extension for Solidus"
7
7
  s.description = "Solidus extension to retrieve tax rates via Avalara's SOAP API."
8
8
  s.required_ruby_version = ">= 2.1"
@@ -17,13 +17,12 @@ Gem::Specification.new do |s|
17
17
  s.require_path = "lib"
18
18
  s.requirements << "none"
19
19
 
20
- s.add_dependency "solidus_core", "~> 1.1.0.pre"
20
+ s.add_dependency "solidus_core", ">= 1.1.0.pre", "< 1.3"
21
21
  s.add_dependency "hashie", "~> 2.l.5"
22
22
  s.add_dependency "multi_json"
23
23
  s.add_dependency "Avatax_TaxService", "~> 2.0.0"
24
24
 
25
25
  s.add_development_dependency "rspec-rails","~> 3.2"
26
- s.add_development_dependency "simplecov"
27
26
  s.add_development_dependency "sqlite3"
28
27
  s.add_development_dependency "sass-rails"
29
28
  s.add_development_dependency "coffee-rails"
@@ -54,12 +54,24 @@ RSpec.describe "Taxes with Store Credits" do
54
54
  fill_in "Phone", with: "(555) 555-5555"
55
55
  end
56
56
  click_on "Save and Continue"
57
-
58
57
  end
59
58
 
60
59
  it "adjusts the credits to cover taxes" do
61
60
  # Use a cassette so that we don't hit the Avatax API all of the time.
62
61
  VCR.use_cassette("taxes_with_store_credits") do
62
+ expect(SpreeAvatax::SalesShared).to(
63
+ receive(:avatax_id).
64
+ with(an_instance_of(Spree::LineItem)).
65
+ at_least(:once).
66
+ and_return('Spree::LineItem-1')
67
+ )
68
+ expect(SpreeAvatax::SalesShared).to(
69
+ receive(:avatax_id).
70
+ with(an_instance_of(Spree::Shipment)).
71
+ at_least(:once).
72
+ and_return('Spree::Shipment-1')
73
+ )
74
+
63
75
  click_on "Save and Continue"
64
76
  end
65
77
 
@@ -5,6 +5,7 @@ describe "Tax Calculation" do
5
5
  let(:address) { create(:address, address1: "35 Crosby St", city: "New York", zipcode: 10013) }
6
6
  let(:line_item_1) { order.line_items.first }
7
7
  let(:line_item_2) { order.line_items.last }
8
+ let(:shipment) { order.shipments.first }
8
9
 
9
10
  before do
10
11
  # Set up Avatax (just in case we don't have a cassette)
@@ -14,6 +15,25 @@ describe "Tax Calculation" do
14
15
  SpreeAvatax::Config.company_code = ENV["AVATAX_COMPANY_CODE"]
15
16
 
16
17
  order.line_items.first.product.tax_category.tax_rates << Spree::TaxRate.first
18
+
19
+ expect(SpreeAvatax::SalesShared).to(
20
+ receive(:avatax_id).
21
+ with(line_item_1).
22
+ at_least(:once).
23
+ and_return('Spree::LineItem-1')
24
+ )
25
+ expect(SpreeAvatax::SalesShared).to(
26
+ receive(:avatax_id).
27
+ with(line_item_2).
28
+ at_least(:once).
29
+ and_return('Spree::LineItem-2')
30
+ )
31
+ expect(SpreeAvatax::SalesShared).to(
32
+ receive(:avatax_id).
33
+ with(shipment).
34
+ at_least(:once).
35
+ and_return('Spree::Shipment-1')
36
+ )
17
37
  end
18
38
 
19
39
  context "without discounts" do
@@ -56,8 +56,8 @@ describe SpreeAvatax::ReturnInvoice do
56
56
  BigDecimal.new(gettax_response_return_item_tax_line[:tax]).abs
57
57
  end
58
58
 
59
- before do
60
- expect(SpreeAvatax::ReturnInvoice.send(:tax_svc))
59
+ let!(:tax_svc_expectation) do
60
+ expect(SpreeAvatax::Shared.tax_svc)
61
61
  .to receive(:gettax)
62
62
  .with(expected_gettax_params)
63
63
  .and_return(gettax_response)
@@ -150,6 +150,17 @@ describe SpreeAvatax::ReturnInvoice do
150
150
  subject
151
151
  end
152
152
  end
153
+
154
+ context 'when avatax is disabled' do
155
+ let!(:config) { create(:avatax_config, enabled: false) }
156
+ let!(:tax_svc_expectation) { expect(SpreeAvatax::Shared).to_not receive(:tax_svc) }
157
+
158
+ it 'does nothing' do
159
+ expect {
160
+ subject
161
+ }.to_not change { SpreeAvatax::ReturnInvoice.count }
162
+ end
163
+ end
153
164
  end
154
165
 
155
166
  describe '.finalize' do
@@ -170,8 +181,8 @@ describe SpreeAvatax::ReturnInvoice do
170
181
  }
171
182
  end
172
183
 
173
- before do
174
- expect(SpreeAvatax::ReturnInvoice.send(:tax_svc))
184
+ let!(:tax_svc_expectation) do
185
+ expect(SpreeAvatax::Shared.tax_svc)
175
186
  .to receive(:posttax)
176
187
  .with(expected_posttax_params)
177
188
  .and_return(
@@ -188,6 +199,16 @@ describe SpreeAvatax::ReturnInvoice do
188
199
  subject
189
200
  }.to change { return_invoice.reload.committed? }.from(false).to(true)
190
201
  end
202
+
203
+ context 'when avatax is disabled' do
204
+ let!(:config) { create(:avatax_config, enabled: false) }
205
+ let!(:tax_svc_expectation) { expect(SpreeAvatax::Shared).to_not receive(:tax_svc) }
206
+
207
+ it 'does nothing' do
208
+ subject
209
+ expect(return_invoice.reload.committed?).to be_falsey
210
+ end
211
+ end
191
212
  end
192
213
 
193
214
  end
@@ -93,7 +93,7 @@ describe SpreeAvatax::SalesInvoice do
93
93
  BigDecimal.new(gettax_response_shipment_tax_line[:tax]).abs
94
94
  end
95
95
 
96
- let!(:gettax_stub) do
96
+ let!(:tax_svc_expectation) do
97
97
  expect(SpreeAvatax::Shared.tax_svc)
98
98
  .to receive(:gettax)
99
99
  .with(expected_gettax_params)
@@ -180,7 +180,7 @@ describe SpreeAvatax::SalesInvoice do
180
180
 
181
181
  context 'when an error occurs' do
182
182
  let(:error) { StandardError.new('just testing') }
183
- let!(:gettax_stub) { }
183
+ let!(:tax_svc_expectation) { }
184
184
  let(:order) do
185
185
  create(:order_with_line_items,
186
186
  line_items_count: 2,
@@ -200,7 +200,7 @@ describe SpreeAvatax::SalesInvoice do
200
200
  .to receive(:get_tax)
201
201
  .and_raise(error)
202
202
 
203
- order.line_items.update_all(pre_tax_amount: nil)
203
+ order.line_items.update_all(pre_tax_amount: 0)
204
204
  order.reload
205
205
  end
206
206
 
@@ -288,7 +288,7 @@ describe SpreeAvatax::SalesInvoice do
288
288
  context 'when the order is not taxable' do
289
289
  let(:order) { create(:order_with_line_items, ship_address: nil, line_items_count: 1) }
290
290
 
291
- let!(:gettax_stub) { }
291
+ let!(:tax_svc_expectation) { }
292
292
 
293
293
  it 'does not create a sales invoice' do
294
294
  expect {
@@ -306,7 +306,7 @@ describe SpreeAvatax::SalesInvoice do
306
306
  context 'when the order is already completed' do
307
307
  let(:order) { create(:completed_order_with_totals) }
308
308
 
309
- let!(:gettax_stub) { }
309
+ let!(:tax_svc_expectation) { }
310
310
 
311
311
  it 'does not create a sales invoice' do
312
312
  expect {
@@ -320,6 +320,33 @@ describe SpreeAvatax::SalesInvoice do
320
320
  subject
321
321
  end
322
322
  end
323
+
324
+ context 'when avatax is disabled' do
325
+ let!(:config) { create(:avatax_config, enabled: false) }
326
+ let!(:tax_svc_expectation) { expect(SpreeAvatax::Shared).to_not receive(:tax_svc) }
327
+
328
+ it 'does nothing' do
329
+ expect {
330
+ subject
331
+ }.to_not change { SpreeAvatax::SalesInvoice.count }
332
+ end
333
+ end
334
+
335
+ context 'when an error occurs during tax updating' do
336
+ it 'does not create a SalesInvoice record' do
337
+ error = StandardError.new
338
+
339
+ expect(SpreeAvatax::SalesShared)
340
+ .to receive(:update_taxes)
341
+ .and_raise(error)
342
+
343
+ expect {
344
+ expect { subject }.to raise_error(error)
345
+ }.to_not change {
346
+ SpreeAvatax::SalesInvoice.count
347
+ }
348
+ end
349
+ end
323
350
  end
324
351
 
325
352
  describe '.commit' do
@@ -417,6 +444,16 @@ describe SpreeAvatax::SalesInvoice do
417
444
  end
418
445
  end
419
446
  end
447
+
448
+ context 'when avatax is disabled' do
449
+ let!(:config) { create(:avatax_config, enabled: false) }
450
+ let!(:tax_svc_expectation) { expect(SpreeAvatax::Shared).to_not receive(:tax_svc) }
451
+
452
+ it 'does nothing' do
453
+ subject
454
+ expect(sales_invoice.reload.committed_at?).to be_falsey
455
+ end
456
+ end
420
457
  end
421
458
 
422
459
  describe '.cancel' do
@@ -439,7 +476,7 @@ describe SpreeAvatax::SalesInvoice do
439
476
 
440
477
  let(:canceltax_response) { sales_invoice_canceltax_response }
441
478
 
442
- let!(:canceltax_stub) do
479
+ let!(:tax_svc_expectation) do
443
480
  expect(SpreeAvatax::Shared.tax_svc)
444
481
  .to receive(:canceltax)
445
482
  .with(expected_canceltax_params)
@@ -455,7 +492,7 @@ describe SpreeAvatax::SalesInvoice do
455
492
 
456
493
  context 'when an error occurs' do
457
494
  let(:error) { StandardError.new('just testing') }
458
- let!(:canceltax_stub) { }
495
+ let!(:tax_svc_expectation) { }
459
496
 
460
497
  before do
461
498
  expect(SpreeAvatax::SalesInvoice)
@@ -486,6 +523,16 @@ describe SpreeAvatax::SalesInvoice do
486
523
  end
487
524
  end
488
525
  end
526
+
527
+ context 'when avatax is disabled' do
528
+ let!(:config) { create(:avatax_config, enabled: false) }
529
+ let!(:tax_svc_expectation) { expect(SpreeAvatax::Shared).to_not receive(:tax_svc) }
530
+
531
+ it 'does nothing' do
532
+ subject
533
+ expect(sales_invoice.canceled_at?).to be_falsey
534
+ end
535
+ end
489
536
  end
490
537
  end
491
538
  end
@@ -7,10 +7,19 @@ describe SpreeAvatax::SalesShared do
7
7
  SpreeAvatax::SalesShared.reset_tax_attributes(order)
8
8
  end
9
9
 
10
- let(:order) { create(:order_with_line_items, additional_tax_total: 1, adjustment_total: 1, included_tax_total: 1, line_items_count: 1) }
10
+ let(:order) do
11
+ create(:order_with_line_items,
12
+ line_items_count: 1, # quantity set to 2 below
13
+ line_items_price: 3,
14
+ shipment_cost: 5,
15
+ )
16
+ end
11
17
  let(:line_item) { order.line_items.first }
18
+ let(:shipment) { order.shipments.first }
12
19
 
13
20
  before do
21
+ line_item.update_attributes!(quantity: 2)
22
+
14
23
  line_item.adjustments.eligible.tax.additional.create!({
15
24
  adjustable: line_item,
16
25
  amount: 1.23,
@@ -43,5 +52,34 @@ describe SpreeAvatax::SalesShared do
43
52
  subject
44
53
  expect(line_item.adjustments.tax.count).to eq 0
45
54
  end
55
+
56
+ it 'sets pre_tax_amount to discounted_amount' do
57
+ subject
58
+ expect(line_item.reload.pre_tax_amount).to eq(2 * 3)
59
+ expect(shipment.reload.pre_tax_amount).to eq(5)
60
+ end
61
+
62
+ context 'when a SalesInvoice record is present' do
63
+ let!(:sales_invoice) { create(:avatax_sales_invoice, order: order) }
64
+
65
+ it 'deletes the SalesInvoice record if present' do
66
+ subject
67
+ expect(order.reload.avatax_sales_invoice).to eq(nil)
68
+ end
69
+
70
+ context 'when the SalesInvoice is committed' do
71
+ before do
72
+ sales_invoice.update!(committed_at: Time.now)
73
+ end
74
+
75
+ it 'raises without clearing anything' do
76
+ expect {
77
+ subject
78
+ }.to raise_error(SpreeAvatax::SalesInvoice::AlreadyCommittedError)
79
+
80
+ expect(line_item.adjustments.tax.count).to eq(1)
81
+ end
82
+ end
83
+ end
46
84
  end
47
85
  end
@@ -86,4 +86,58 @@ describe SpreeAvatax::Shared do
86
86
  end
87
87
  end
88
88
  end
89
+
90
+ describe '.get_tax' do
91
+ it 'calls gettax' do
92
+ params = {}
93
+ expect(SpreeAvatax::Shared.tax_svc).to receive(:gettax).with(params)
94
+ SpreeAvatax::Shared.get_tax(params)
95
+ end
96
+
97
+ context 'with a timeout' do
98
+ let!(:config) { create(:avatax_config, timeout: 0.001) }
99
+ it 'times out' do
100
+ expect(SpreeAvatax::Shared.tax_svc).to(receive(:gettax) { sleep 0.1 })
101
+ expect {
102
+ SpreeAvatax::Shared.get_tax({})
103
+ }.to raise_error(SpreeAvatax::AvataxTimeout)
104
+ end
105
+ end
106
+ end
107
+
108
+ describe '.post_tax' do
109
+ it 'calls posttax' do
110
+ params = {}
111
+ expect(SpreeAvatax::Shared.tax_svc).to receive(:posttax).with(params)
112
+ SpreeAvatax::Shared.post_tax(params)
113
+ end
114
+
115
+ context 'with a timeout' do
116
+ let!(:config) { create(:avatax_config, timeout: 0.001) }
117
+ it 'times out' do
118
+ expect(SpreeAvatax::Shared.tax_svc).to(receive(:posttax) { sleep 0.1 })
119
+ expect {
120
+ SpreeAvatax::Shared.post_tax({})
121
+ }.to raise_error(SpreeAvatax::AvataxTimeout)
122
+ end
123
+ end
124
+ end
125
+
126
+ describe '.cancel_tax' do
127
+ it 'calls canceltax' do
128
+ params = {}
129
+ expect(SpreeAvatax::Shared.tax_svc).to receive(:canceltax).with(params)
130
+ SpreeAvatax::Shared.cancel_tax(params)
131
+ end
132
+
133
+ context 'with a timeout' do
134
+ let!(:config) { create(:avatax_config, timeout: 0.001) }
135
+ it 'times out' do
136
+ expect(SpreeAvatax::Shared.tax_svc).to(receive(:canceltax) { sleep 0.1 })
137
+ expect {
138
+ SpreeAvatax::Shared.cancel_tax({})
139
+ }.to raise_error(SpreeAvatax::AvataxTimeout)
140
+ end
141
+ end
142
+ end
89
143
  end
@@ -131,6 +131,17 @@ describe SpreeAvatax::ShortShipReturnInvoice do
131
131
 
132
132
  SpreeAvatax::ShortShipReturnInvoice.generate(unit_cancels: unit_cancels)
133
133
  end
134
+
135
+ context 'when avatax is disabled' do
136
+ let!(:config) { create(:avatax_config, enabled: false) }
137
+
138
+ it 'does nothing' do
139
+ expect(SpreeAvatax::Shared).to_not receive(:tax_svc)
140
+ expect {
141
+ subject
142
+ }.to_not change { SpreeAvatax::ShortShipReturnInvoice.count }
143
+ end
144
+ end
134
145
  end
135
146
 
136
147
  context 'with a successful response' do
@@ -1,15 +1,3 @@
1
- # Run Coverage report
2
- require "simplecov"
3
- SimpleCov.start do
4
- add_filter "spec/dummy"
5
- add_group "Controllers", "app/controllers"
6
- add_group "Helpers", "app/helpers"
7
- add_group "Mailers", "app/mailers"
8
- add_group "Models", "app/models"
9
- add_group "Views", "app/views"
10
- add_group "Libraries", "lib"
11
- end
12
-
13
1
  # Configure Rails Environment
14
2
  ENV["RAILS_ENV"] = "test"
15
3
 
metadata CHANGED
@@ -1,29 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solidus_avatax
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Solidus Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-27 00:00:00.000000000 Z
11
+ date: 2016-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: solidus_core
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 1.1.0.pre
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '1.3'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
29
  version: 1.1.0.pre
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.3'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: hashie
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -80,20 +86,6 @@ dependencies:
80
86
  - - "~>"
81
87
  - !ruby/object:Gem::Version
82
88
  version: '3.2'
83
- - !ruby/object:Gem::Dependency
84
- name: simplecov
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
89
  - !ruby/object:Gem::Dependency
98
90
  name: sqlite3
99
91
  requirement: !ruby/object:Gem::Requirement
@@ -200,6 +192,7 @@ extra_rdoc_files: []
200
192
  files:
201
193
  - ".gitignore"
202
194
  - ".rspec"
195
+ - ".travis.yml"
203
196
  - Gemfile
204
197
  - LICENSE
205
198
  - README.md
@@ -213,6 +206,7 @@ files:
213
206
  - app/models/spree/reimbursement_decorator.rb
214
207
  - app/models/spree/tax_rate_decorator.rb
215
208
  - app/models/spree_avatax/calculator.rb
209
+ - app/models/spree_avatax/config.rb
216
210
  - app/models/spree_avatax/return_invoice.rb
217
211
  - app/models/spree_avatax/sales_invoice.rb
218
212
  - app/models/spree_avatax/sales_shared.rb
@@ -234,10 +228,10 @@ files:
234
228
  - db/migrate/20140911215422_add_canceled_at_and_cancel_transaction_id_to_sales_invoices.rb
235
229
  - db/migrate/20150427154942_create_spree_avatax_short_ship_return_invoices.rb
236
230
  - db/migrate/20150518172627_fix_avatax_short_ship_index.rb
231
+ - db/migrate/20151130195450_add_spree_avatax_configs.rb
237
232
  - lib/generators/solidus_avatax/install/install_generator.rb
238
233
  - lib/generators/solidus_avatax/install/templates/config/initializers/avatax.rb
239
234
  - lib/solidus_avatax.rb
240
- - lib/spree_avatax/config.rb
241
235
  - lib/spree_avatax/engine.rb
242
236
  - lib/spree_avatax/factories.rb
243
237
  - lib/tasks/commit_backfill.rake
@@ -285,29 +279,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
285
279
  requirements:
286
280
  - none
287
281
  rubyforge_project:
288
- rubygems_version: 2.2.5
282
+ rubygems_version: 2.5.1
289
283
  signing_key:
290
284
  specification_version: 4
291
285
  summary: Avatax extension for Solidus
292
- test_files:
293
- - spec/features/store_credits_spec.rb
294
- - spec/features/tax_calculation_spec.rb
295
- - spec/fixtures/vcr_cassettes/sales_invoice_gettax_with_discounts.yml
296
- - spec/fixtures/vcr_cassettes/sales_invoice_gettax_without_discounts.yml
297
- - spec/fixtures/vcr_cassettes/taxes_with_store_credits.yml
298
- - spec/models/spree/adjustment_spec.rb
299
- - spec/models/spree/order_contents_spec.rb
300
- - spec/models/spree/order_spec.rb
301
- - spec/models/spree/shipping_rate_spec.rb
302
- - spec/models/spree/tax_rate_spec.rb
303
- - spec/models/spree_avatax/calculator.rb
304
- - spec/models/spree_avatax/return_invoice_spec.rb
305
- - spec/models/spree_avatax/sales_invoice_spec.rb
306
- - spec/models/spree_avatax/sales_shared_spec.rb
307
- - spec/models/spree_avatax/shared_spec.rb
308
- - spec/models/spree_avatax/short_ship_return_invoice_spec.rb
309
- - spec/spec_helper.rb
310
- - spec/support/return_invoice_soap_responses.rb
311
- - spec/support/sales_invoice_soap_responses.rb
312
- - spec/support/short_ship_return_invoice_soap_responses.rb
313
- - spec/support/zone_support.rb
286
+ test_files: []
@@ -1,16 +0,0 @@
1
- module SpreeAvatax
2
- class Config
3
- class << self
4
- attr_accessor :username
5
- attr_accessor :password
6
- attr_accessor :company_code
7
- attr_accessor :service_url
8
- # These error handlers should be objects that respond to "call" and accept an order and an
9
- # exception as arguments. This allows you to ignore certain errors or handle them in
10
- # specific ways.
11
- attr_accessor :sales_invoice_generate_error_handler
12
- attr_accessor :sales_invoice_commit_error_handler
13
- attr_accessor :sales_invoice_cancel_error_handler
14
- end
15
- end
16
- end