solidus_tax_cloud 1.0.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 +7 -0
- data/.circleci/config.yml +35 -0
- data/.gem_release.yml +5 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +132 -0
- data/CHANGELOG.md +52 -0
- data/Gemfile +33 -0
- data/LICENSE +26 -0
- data/README.md +130 -0
- data/Rakefile +6 -0
- data/TaxCloudImplementationVerificationGuide.pdf +0 -0
- data/app/assets/javascripts/spree/backend/solidus_tax_cloud.js +2 -0
- data/app/assets/javascripts/spree/frontend/solidus_tax_cloud.js +2 -0
- data/app/assets/stylesheets/spree/backend/solidus_tax_cloud.css +4 -0
- data/app/assets/stylesheets/spree/backend/solidus_tax_cloud.scss +0 -0
- data/app/assets/stylesheets/spree/frontend/solidus_tax_cloud.css +4 -0
- data/app/decorators/models/solidus_tax_cloud/spree/app_configuration_decorator.rb +18 -0
- data/app/decorators/models/solidus_tax_cloud/spree/line_item_decorator.rb +47 -0
- data/app/decorators/models/solidus_tax_cloud/spree/order_decorator.rb +42 -0
- data/app/decorators/models/solidus_tax_cloud/spree/product_decorator.rb +25 -0
- data/app/decorators/models/solidus_tax_cloud/spree/shipment_decorator.rb +27 -0
- data/app/models/spree/calculator/tax_cloud_calculator.rb +111 -0
- data/app/models/spree/tax_cloud.rb +66 -0
- data/app/overrides/spree/admin/products/_form.rb +9 -0
- data/app/overrides/spree/admin/shared/_configuration_menu.rb +9 -0
- data/bin/console +17 -0
- data/bin/r +15 -0
- data/bin/rails +15 -0
- data/bin/rake +7 -0
- data/bin/sandbox +84 -0
- data/bin/sandbox_rails +18 -0
- data/bin/setup +8 -0
- data/config/initializers/tax_cloud_usps_username.rb +5 -0
- data/config/locales/en.yml +13 -0
- data/config/routes.rb +7 -0
- data/db/migrate/20121220192438_create_spree_tax_cloud_transactions.rb +13 -0
- data/db/migrate/20121220193944_create_spree_tax_cloud_cart_items.rb +22 -0
- data/db/migrate/20130829215819_fix_scale_of_cart_item_prices.rb +15 -0
- data/db/migrate/20140623225628_add_tic_to_products.rb +11 -0
- data/lib/assets/javascripts/spree/frontend/solidus_tax_cloud.js.erb +1 -0
- data/lib/assets/stylesheets/spree/frontend/solidus_tax_cloud.css.erb +5 -0
- data/lib/controllers/backend/spree/admin/tax_cloud_settings_controller.rb +29 -0
- data/lib/decorators/backend/controllers/solidus_tax_cloud/spree/admin/orders_controller_decorator.rb +21 -0
- data/lib/decorators/frontend/controllers/solidus_tax_cloud/spree/checkout_controller_decorator.rb +24 -0
- data/lib/generators/solidus_tax_cloud/install/install_generator.rb +29 -0
- data/lib/generators/solidus_tax_cloud/templates/ca-bundle.crt +3895 -0
- data/lib/solidus_tax_cloud.rb +10 -0
- data/lib/solidus_tax_cloud/engine.rb +23 -0
- data/lib/solidus_tax_cloud/error.rb +6 -0
- data/lib/solidus_tax_cloud/factories.rb +4 -0
- data/lib/solidus_tax_cloud/version.rb +5 -0
- data/lib/tasks/.gitkeep +0 -0
- data/lib/tasks/tax_cloud.rake +74 -0
- data/lib/views/backend/spree/admin/products/_edit_tax_cloud_tic.html.erb +6 -0
- data/lib/views/backend/spree/admin/tax_cloud_settings/edit.html.erb +32 -0
- data/solidus_tax_cloud.gemspec +39 -0
- data/spec/features/checkout_spec.rb +511 -0
- data/spec/models/tax_cloud_api_spec.rb +192 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/capybara.rb +20 -0
- data/spec/support/tax_cloud.rb +20 -0
- data/spec/support/transactions.rb +5 -0
- metadata +204 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spree/core'
|
4
|
+
require 'solidus_tax_cloud'
|
5
|
+
|
6
|
+
module SolidusTaxCloud
|
7
|
+
class Engine < Rails::Engine
|
8
|
+
include SolidusSupport::EngineExtensions
|
9
|
+
|
10
|
+
isolate_namespace ::Spree
|
11
|
+
|
12
|
+
engine_name 'solidus_tax_cloud'
|
13
|
+
|
14
|
+
# use rspec for tests
|
15
|
+
config.generators do |g|
|
16
|
+
g.test_framework :rspec
|
17
|
+
end
|
18
|
+
|
19
|
+
initializer 'solidus_tax_cloud.permitted_attributes' do |_app|
|
20
|
+
Spree::PermittedAttributes.product_attributes << :tax_cloud_tic
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/tasks/.gitkeep
ADDED
File without changes
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
include FileUtils::Verbose
|
5
|
+
|
6
|
+
namespace :tax_cloud do
|
7
|
+
namespace :db do
|
8
|
+
desc 'Custom migrations'
|
9
|
+
task :migrate do
|
10
|
+
require 'erb'
|
11
|
+
require 'logger'
|
12
|
+
require 'active_record'
|
13
|
+
reference = YAML.safe_load(ERB.new(IO.read("#{Rails.root}/config/database.yml")).result)
|
14
|
+
env = RAILS_ENV = ENV['RAILS_ENV'] || 'development'
|
15
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
16
|
+
ActiveRecord::Base.logger.level = Logger::WARN
|
17
|
+
ActiveRecord::Base.configurations = reference.dup
|
18
|
+
reference.each_key do |name|
|
19
|
+
next unless name.include? env
|
20
|
+
|
21
|
+
puts "Migrating #{name}"
|
22
|
+
ActiveRecord::Base.clear_active_connections!
|
23
|
+
ActiveRecord::Base.configurations[env] = reference[name]
|
24
|
+
ActiveRecord::Base.establish_connection RAILS_ENV
|
25
|
+
ActiveRecord::Migration.verbose = ENV['VERBOSE'] ? ENV['VERBOSE'] == 'true' : true
|
26
|
+
ActiveRecord::Migrator.migrate("#{File.expand_path('../..', File.dirname(__FILE__))}/db/migrate", ENV['VERSION'] ? ENV['VERSION'].to_i : nil)
|
27
|
+
Rake::Task['db:schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'Loads a specified fixture using rake db:load_file[filename.rb]'
|
32
|
+
task :seed do
|
33
|
+
Rake::Task['tax_cloud:db:load_dir'].invoke('default')
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'Loads a specified fixture using rake db:load_file[filename.rb]'
|
37
|
+
task :load_file, [:file] => :environment do |_t, args|
|
38
|
+
file = args.file
|
39
|
+
ext = File.extname file
|
40
|
+
if (ext == '.csv') || (ext == '.yml')
|
41
|
+
puts 'loading fixture ' + file
|
42
|
+
Fixtures.create_fixtures(File.dirname(file), File.basename(file, '.*'))
|
43
|
+
else
|
44
|
+
if File.exist? file
|
45
|
+
puts 'loading ruby ' + file
|
46
|
+
require file
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
desc 'Loads fixtures from the the dir you specify using rake db:load_dir[loadfrom]'
|
52
|
+
task :load_dir, [:dir] => :environment do |_t, args|
|
53
|
+
dir = args.dir
|
54
|
+
fixtures = ActiveSupport::OrderedHash.new
|
55
|
+
ruby_files = ActiveSupport::OrderedHash.new
|
56
|
+
Dir.glob(File.join(File.expand_path('../..', File.dirname(__FILE__)), 'db', dir, '*.{yml,csv,rb}')).each do |fixture_file|
|
57
|
+
ext = File.extname fixture_file
|
58
|
+
if ext == '.rb'
|
59
|
+
ruby_files[File.basename(fixture_file, '.*')] = fixture_file
|
60
|
+
else
|
61
|
+
fixtures[File.basename(fixture_file, '.*')] = fixture_file
|
62
|
+
end
|
63
|
+
end
|
64
|
+
fixtures.sort.each do |_fixture, fixture_file|
|
65
|
+
# an invoke will only execute the task once
|
66
|
+
Rake::Task['db:load_file'].execute(Rake::TaskArguments.new([:file], [fixture_file]))
|
67
|
+
end
|
68
|
+
ruby_files.sort.each do |_fixture, ruby_file|
|
69
|
+
# an invoke will only execute the task once
|
70
|
+
Rake::Task['db:load_file'].execute(Rake::TaskArguments.new([:file], [ruby_file]))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<div data-hook="admin_product_form_tax_cloud_tic">
|
2
|
+
<%= f.field_container :tax_cloud_tic do %>
|
3
|
+
<%= f.label :tax_cloud_tic_id, I18n.t('spree.tax_cloud_tic') %>
|
4
|
+
<%= f.text_field :tax_cloud_tic, size: 5, placeholder: Spree::Config.taxcloud_default_product_tic %>
|
5
|
+
<% end %>
|
6
|
+
</div>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<%= render 'spree/admin/shared/configuration_menu' %>
|
2
|
+
|
3
|
+
<% content_for :page_title do %>
|
4
|
+
<%= I18n.t('spree.tax_cloud_settings') %>
|
5
|
+
<% end %>
|
6
|
+
|
7
|
+
<%= form_tag admin_tax_cloud_settings_path, method: :put do %>
|
8
|
+
<div id="preferences" data-hook>
|
9
|
+
<div class="row">
|
10
|
+
<div class="omega six columns">
|
11
|
+
<fieldset class="currency no-border-bottom">
|
12
|
+
<legend align="center"><%= I18n.t('spree.tax_cloud_taxability_codes')%></legend>
|
13
|
+
<% @preferences_tic.each do |key|
|
14
|
+
type = Spree::Config.preference_type(key) %>
|
15
|
+
<div class="field">
|
16
|
+
<%= label_tag(key, I18n.t("spree.#{key}")) + tag(:br) if type != :boolean %>
|
17
|
+
<%= preference_field_tag(key, Spree::Config[key], type: type) %>
|
18
|
+
<%= label_tag(key, I18n.t("spree.#{key}")) + tag(:br) if type == :boolean %>
|
19
|
+
</div>
|
20
|
+
<% end %>
|
21
|
+
</fieldset>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<div class="form-buttons filter-actions actions" data-hook="buttons">
|
26
|
+
<%= button I18n.t('spree.actions.update'), 'refresh' %>
|
27
|
+
<span class="or"><%= I18n.t('spree.or') %></span>
|
28
|
+
<%= link_to_with_icon 'remove', I18n.t('spree.actions.cancel'), edit_admin_tax_cloud_settings_url, class: 'button' %>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
</div>
|
32
|
+
<% end %>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/solidus_tax_cloud/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'solidus_tax_cloud'
|
7
|
+
spec.version = SolidusTaxCloud::VERSION
|
8
|
+
spec.authors = ['Jerrold Thompson']
|
9
|
+
spec.email = 'jet@whidbey.com'
|
10
|
+
|
11
|
+
spec.summary = 'Solidus extension providing Tax Cloud services'
|
12
|
+
spec.description = 'Solidus extension providing Tax Cloud services'
|
13
|
+
spec.homepage = 'https://github.com/solidusio-contrib/solidus_tax_cloud'
|
14
|
+
spec.license = 'BSD-3-Clause'
|
15
|
+
|
16
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
17
|
+
spec.metadata['source_code_uri'] = 'https://github.com/solidusio-contrib/solidus_tax_cloud'
|
18
|
+
spec.metadata['changelog_uri'] = 'https://github.com/solidusio-contrib/solidus_tax_cloud/blob/master/CHANGELOG.md'
|
19
|
+
|
20
|
+
spec.required_ruby_version = Gem::Requirement.new('~> 2.5')
|
21
|
+
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
+
files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
|
25
|
+
|
26
|
+
spec.files = files.grep_v(%r{^(test|spec|features)/})
|
27
|
+
spec.test_files = files.grep(%r{^(test|spec|features)/})
|
28
|
+
spec.bindir = "exe"
|
29
|
+
spec.executables = files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
spec.add_dependency 'deface'
|
33
|
+
spec.add_dependency 'savon', '~> 2.12.0'
|
34
|
+
spec.add_dependency 'solidus_core', ['>= 2.0.0', '< 3']
|
35
|
+
spec.add_dependency 'solidus_support', '~> 0.5'
|
36
|
+
spec.add_dependency 'tax_cloud', '~> 0.3.0'
|
37
|
+
|
38
|
+
spec.add_development_dependency 'solidus_dev_support'
|
39
|
+
end
|
@@ -0,0 +1,511 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe 'Checkout', js: true do
|
6
|
+
let!(:usa) { create(:country, name: 'United States of America', states_required: true) }
|
7
|
+
let!(:alabama) { create(:state, name: 'Alabama', abbr: 'AL', country: usa) }
|
8
|
+
let!(:georgia) { create(:state, name: 'Georgia', abbr: 'GA', country: usa) }
|
9
|
+
let!(:minnesota) { create(:state, name: 'Minnesota', abbr: 'MN', country: usa) }
|
10
|
+
let!(:oklahoma) { create(:state, name: 'Oklahoma', abbr: 'OK', country: usa) }
|
11
|
+
let!(:washington) { create(:state, name: 'Washington', abbr: 'WA', country: usa) }
|
12
|
+
|
13
|
+
let!(:zone) do
|
14
|
+
zone = create(:zone, name: 'US')
|
15
|
+
zone.members.create(zoneable: usa)
|
16
|
+
zone
|
17
|
+
end
|
18
|
+
|
19
|
+
let!(:uk) { create(:country, name: 'United Kingdom', states_required: false, iso_name: 'UNITED KINGDOM', iso: 'UK', iso3: 'GBR', numcode: 826) }
|
20
|
+
let!(:uk_address) { create(:address, country: uk, state: nil, zipcode: 'SW1A 1AA') }
|
21
|
+
let!(:non_us_zone) do
|
22
|
+
zone = create(:zone, name: 'Rest of the world')
|
23
|
+
zone.members.create(zoneable: uk)
|
24
|
+
zone
|
25
|
+
end
|
26
|
+
|
27
|
+
let!(:store) { create(:store) }
|
28
|
+
let!(:shipping_calculator) { create(:calculator) }
|
29
|
+
# default calculator in the Spree factory is flat rate of $10, which is exactly what we want
|
30
|
+
let!(:shipping_method) { create(:shipping_method, tax_category_id: 1, calculator: shipping_calculator, zones: [zone, non_us_zone]) }
|
31
|
+
let!(:stock_location) { create(:stock_location, country_id: stock_location_address.country.id, state_id: stock_location_address.state.id, address1: stock_location_address.address1, city: stock_location_address.city, zipcode: stock_location_address.zipcode) }
|
32
|
+
let!(:mug) { create(:product, name: 'RoR Mug', price: 10) }
|
33
|
+
let!(:shirt) { create(:product, name: 'Shirt', price: 10, tax_cloud_tic: '20010') }
|
34
|
+
let!(:payment_method) { create(:check_payment_method) }
|
35
|
+
|
36
|
+
let!(:item_promotion) { create(:promotion, :with_line_item_adjustment, code: 'AAAA', adjustment_rate: 5) }
|
37
|
+
let!(:shipping_promotion) do
|
38
|
+
promotion = create(:promotion, code: 'BBBB')
|
39
|
+
action = Spree::Promotion::Actions::FreeShipping.create!
|
40
|
+
promotion.actions << action
|
41
|
+
promotion.save!
|
42
|
+
end
|
43
|
+
|
44
|
+
let!(:tax_rate) { create(:tax_rate, amount: 0, name: 'Sales Tax', zone: zone, calculator: Spree::Calculator::TaxCloudCalculator.create, tax_categories: [Spree::TaxCategory.first], show_rate_in_label: false) }
|
45
|
+
let!(:flat_tax_rate) { create(:tax_rate, amount: 0.1, name: 'Flat Sales Tax', zone: non_us_zone, tax_categories: [Spree::TaxCategory.first], show_rate_in_label: false) }
|
46
|
+
|
47
|
+
before do
|
48
|
+
stock_location.stock_items.update_all(count_on_hand: 1)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'displays tax lookup error if invalid address' do
|
52
|
+
add_to_cart('RoR Mug')
|
53
|
+
click_button 'Checkout'
|
54
|
+
|
55
|
+
fill_in 'order_email', with: 'test@example.com'
|
56
|
+
click_button 'Continue'
|
57
|
+
|
58
|
+
fill_in_address(alabama_address)
|
59
|
+
fill_in 'order_bill_address_attributes_zipcode', with: '12345'
|
60
|
+
|
61
|
+
click_button 'Save and Continue'
|
62
|
+
click_button 'Save and Continue'
|
63
|
+
|
64
|
+
click_button 'Save and Continue'
|
65
|
+
expect(page).to have_content(/Address Verification Failed/i)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'tolerates a missing sku without throwing a Tax Cloud exception' do
|
69
|
+
add_to_cart('RoR Mug')
|
70
|
+
click_button 'Checkout'
|
71
|
+
|
72
|
+
fill_in 'order_email', with: 'test@example.com'
|
73
|
+
click_button 'Continue'
|
74
|
+
|
75
|
+
fill_in_address(alabama_address)
|
76
|
+
Spree::Product.where(name: 'RoR Mug').first.update(sku: '')
|
77
|
+
|
78
|
+
click_button 'Save and Continue'
|
79
|
+
expect(page).not_to have_content(/Address Verification Failed/i)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'calculates and display tax on payment step and allow full checkout' do
|
83
|
+
add_to_cart('RoR Mug')
|
84
|
+
click_button 'Checkout'
|
85
|
+
|
86
|
+
fill_in 'order_email', with: 'test@example.com'
|
87
|
+
click_button 'Continue'
|
88
|
+
|
89
|
+
fill_in_address(alabama_address)
|
90
|
+
click_button 'Save and Continue'
|
91
|
+
click_button 'Save and Continue'
|
92
|
+
|
93
|
+
click_on 'Save and Continue'
|
94
|
+
click_on 'Place Order'
|
95
|
+
expect(current_path).to match(spree.order_path(Spree::Order.last))
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'does not break when removing all items from cart after a tax calculation has been created' do
|
99
|
+
add_to_cart('RoR Mug')
|
100
|
+
click_button 'Checkout'
|
101
|
+
|
102
|
+
fill_in 'order_email', with: 'test@example.com'
|
103
|
+
click_button 'Continue'
|
104
|
+
|
105
|
+
fill_in_address(alabama_address)
|
106
|
+
click_button 'Save and Continue'
|
107
|
+
click_button 'Save and Continue'
|
108
|
+
|
109
|
+
# TODO: Address with TaxCloud support why this appears to have changed as of week of 7/1/18
|
110
|
+
# expect(page).not_to have_content(/Sales\sTax/i)
|
111
|
+
# expect(page).to have_content(/Order Total:\s\$20.00/i) # Alabama orders are configured under this API key to have no tax
|
112
|
+
|
113
|
+
visit spree.cart_path
|
114
|
+
find('a.delete').click
|
115
|
+
expect(page).to have_content(/Shopping Cart/i)
|
116
|
+
expect(page).not_to have_content(/Internal Server Error/i)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'only calculates using tax cloud for orders that use the tax cloud calculator' do
|
120
|
+
add_to_cart('RoR Mug')
|
121
|
+
click_button 'Checkout'
|
122
|
+
|
123
|
+
fill_in 'order_email', with: 'test@example.com'
|
124
|
+
click_button 'Continue'
|
125
|
+
|
126
|
+
fill_in_address(uk_address)
|
127
|
+
click_button 'Save and Continue'
|
128
|
+
|
129
|
+
# There should not be a check on the address because
|
130
|
+
# the rate is not handled by TaxCloud.
|
131
|
+
expect(page).not_to have_content(/Address Verification Failed/i)
|
132
|
+
|
133
|
+
click_button 'Save and Continue'
|
134
|
+
click_button 'Save and Continue'
|
135
|
+
click_button 'Place Order'
|
136
|
+
|
137
|
+
expect(current_path).to match(spree.order_path(Spree::Order.last))
|
138
|
+
expect(page).not_to have_content(/Address Verification Failed/i)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'TaxCloud Test Case 1a: Verify Address with error' do
|
142
|
+
add_to_cart('RoR Mug')
|
143
|
+
click_button 'Checkout'
|
144
|
+
|
145
|
+
fill_in 'order_email', with: 'test@example.com'
|
146
|
+
click_button 'Continue'
|
147
|
+
expect(page).to have_content(/Order Total:\s\$10/i)
|
148
|
+
fill_in_address(test_case_1a_address)
|
149
|
+
click_button 'Save and Continue'
|
150
|
+
# From TaxCloud:
|
151
|
+
# This address will not verify correctly (the VerifyAddress API call will return an error).
|
152
|
+
# That is okay. Occasionally an address cannot be verified. When that happens, pass the
|
153
|
+
# destination address as originally entered to Lookup. The address can still be passed to
|
154
|
+
# Lookup. The only error that should prevent an order from proceeding is when a customer
|
155
|
+
# provided zip code does not exist within the customer provided state (discussed later in Test
|
156
|
+
# Case 7, Handling Errors).
|
157
|
+
#
|
158
|
+
# NOTE: In the API specs (from official TaxCloud Implementation Verification Guide), there is
|
159
|
+
# no shipping item sent to TaxCloud, and there is only a single $0.99 charge for the item.
|
160
|
+
# In this integration test, Solidus will automatically send the shipping information, which
|
161
|
+
# results in a second $0.99 charge, for a total tax of $1.98.
|
162
|
+
expect(page).to have_content(/Sales Tax\s\$1.98/i)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'TaxCloud Test Case 1b: Verify Address without error' do
|
166
|
+
add_to_cart('RoR Mug')
|
167
|
+
click_button 'Checkout'
|
168
|
+
|
169
|
+
fill_in 'order_email', with: 'test@example.com'
|
170
|
+
click_button 'Continue'
|
171
|
+
expect(page).to have_content(/Item Total:\s\$10/i)
|
172
|
+
fill_in_address(test_case_1b_address)
|
173
|
+
click_button 'Save and Continue'
|
174
|
+
# From TaxCloud:
|
175
|
+
# The destination address used as-is will not give the most accurate rate ($1.00 in tax).
|
176
|
+
# The verified address will have a Plus4 Zip Code of 98059-8625 give a correct result
|
177
|
+
# ($0.86 in tax).
|
178
|
+
#
|
179
|
+
# NOTE: In the API specs (from official TaxCloud Implementation Verification Guide), there is
|
180
|
+
# no shipping item sent to TaxCloud, and there is only a single $0.86 charge for the item.
|
181
|
+
# In this integration test, Solidus will automatically send the shipping information, which
|
182
|
+
# results in a second $0.86 charge, for a total tax of $1.72.
|
183
|
+
expect(page).to have_content(/Sales Tax\s\$1.72/i)
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'TaxCloud Test Case 2a: If all items in cart are tax exempt, shipping is not taxed (in some states)' do
|
187
|
+
add_to_cart('Shirt')
|
188
|
+
click_button 'Checkout'
|
189
|
+
|
190
|
+
expect(page).to have_css('.cart-info', text: '$10.00')
|
191
|
+
|
192
|
+
fill_in 'order_email', with: 'test@example.com'
|
193
|
+
click_button 'Continue'
|
194
|
+
|
195
|
+
fill_in_address(test_case_2a_address)
|
196
|
+
click_button 'Save and Continue'
|
197
|
+
|
198
|
+
expect(page).not_to have_content(/Address Verification Failed/i)
|
199
|
+
expect(page).to have_content(/Item Total:\s\$10/i)
|
200
|
+
expect(page).to have_content(/Order Total:\s\$20/i)
|
201
|
+
click_button 'Save and Continue'
|
202
|
+
|
203
|
+
expect(page).to have_content(/Item Total:\s\$10/i)
|
204
|
+
expect(page).to have_content(/Order Total:\s\$20/i)
|
205
|
+
click_on 'Save and Continue'
|
206
|
+
|
207
|
+
click_button 'Place Order'
|
208
|
+
|
209
|
+
expect(current_path).to match(spree.order_path(Spree::Order.last))
|
210
|
+
expect(page).to have_content(/Sales Tax\s\$0.00/i)
|
211
|
+
expect(page).to have_content(/ORDER TOTAL:\s\$20/i)
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'TaxCloud Test Case 2b: With both taxable and tax exempt items, shipping is taxable' do
|
215
|
+
add_to_cart('RoR Mug')
|
216
|
+
add_to_cart('Shirt')
|
217
|
+
click_button 'Checkout'
|
218
|
+
|
219
|
+
fill_in 'order_email', with: 'test@example.com'
|
220
|
+
click_button 'Continue'
|
221
|
+
expect(page).to have_content(/Item Total:\s\$20/i)
|
222
|
+
fill_in_address(test_case_2b_address)
|
223
|
+
click_button 'Save and Continue'
|
224
|
+
|
225
|
+
click_button 'Save and Continue'
|
226
|
+
|
227
|
+
expect(page).to have_content(/Sales Tax\s\$1.58/i)
|
228
|
+
expect(page).to have_content(/Order Total:\s\$31.58/i)
|
229
|
+
|
230
|
+
click_on 'Save and Continue'
|
231
|
+
click_button 'Place Order'
|
232
|
+
|
233
|
+
expect(current_path).to match(spree.order_path(Spree::Order.last))
|
234
|
+
expect(page).to have_content(/Sales Tax\s\$1.58/i)
|
235
|
+
expect(page).to have_content(/ORDER TOTAL:\s\$31.58/i)
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'TaxCloud Test Case 3: Item taxable, shipping not taxable' do
|
239
|
+
add_to_cart('Shirt')
|
240
|
+
click_button 'Checkout'
|
241
|
+
|
242
|
+
fill_in 'order_email', with: 'test@example.com'
|
243
|
+
click_button 'Continue'
|
244
|
+
expect(page).to have_content(/Item Total:\s\$10/i)
|
245
|
+
fill_in_address(test_case_3_address)
|
246
|
+
click_button 'Save and Continue'
|
247
|
+
|
248
|
+
expect(page).not_to have_content(/Address Verification Failed/i)
|
249
|
+
click_button 'Save and Continue'
|
250
|
+
|
251
|
+
expect(page).to have_content(/Sales Tax\s\$0.86/i)
|
252
|
+
expect(page).to have_content(/Order Total:\s\$20.86/i)
|
253
|
+
|
254
|
+
click_on 'Save and Continue'
|
255
|
+
click_button 'Place Order'
|
256
|
+
|
257
|
+
expect(current_path).to match(spree.order_path(Spree::Order.last))
|
258
|
+
expect(page).to have_content(/Sales Tax\s\$0.86/i)
|
259
|
+
expect(page).to have_content(/ORDER TOTAL:\s\$20.86/i)
|
260
|
+
end
|
261
|
+
|
262
|
+
skip 'TaxCloud Test Case 4: Return all items in previous order' do
|
263
|
+
# TODO
|
264
|
+
end
|
265
|
+
|
266
|
+
skip 'TaxCloud Test Case 5: Return single item in previous order' do
|
267
|
+
# TODO
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'TaxCloud Test Case 6: Item and shipping taxable' do
|
271
|
+
add_to_cart('Shirt')
|
272
|
+
click_button 'Checkout'
|
273
|
+
|
274
|
+
fill_in 'order_email', with: 'test@example.com'
|
275
|
+
click_button 'Continue'
|
276
|
+
expect(page).to have_content(/Item Total:\s\$10/i)
|
277
|
+
fill_in_address(test_case_6_address)
|
278
|
+
click_button 'Save and Continue'
|
279
|
+
|
280
|
+
expect(page).not_to have_content(/Address Verification Failed/i)
|
281
|
+
click_button 'Save and Continue'
|
282
|
+
|
283
|
+
expect(page).to have_content(/Sales Tax\s\$1.78/i)
|
284
|
+
expect(page).to have_content(/Order Total:\s\$21.78/i)
|
285
|
+
|
286
|
+
click_on 'Save and Continue'
|
287
|
+
click_button 'Place Order'
|
288
|
+
|
289
|
+
expect(current_path).to match(spree.order_path(Spree::Order.last))
|
290
|
+
expect(page).to have_content(/Sales Tax\s\$1.78/i)
|
291
|
+
expect(page).to have_content(/ORDER TOTAL:\s\$21.78/i)
|
292
|
+
end
|
293
|
+
|
294
|
+
# it 'TaxCloud Test Case 7: Handling errors' do
|
295
|
+
# NOTE: Solidus does not allow for the creation of negative-price products,
|
296
|
+
# rendering TaxCloud Test Case 7 moot.
|
297
|
+
# end
|
298
|
+
|
299
|
+
context 'with discounts' do
|
300
|
+
it 'TaxCloud Test Case 3, with item discount' do
|
301
|
+
add_to_cart('Shirt')
|
302
|
+
|
303
|
+
if Spree.solidus_gem_version >= Gem::Version.new('2.8.0')
|
304
|
+
fill_in 'coupon_code', with: 'AAAA'
|
305
|
+
click_button 'Apply Code'
|
306
|
+
else
|
307
|
+
fill_in 'order_coupon_code', with: 'AAAA'
|
308
|
+
click_button 'Update'
|
309
|
+
end
|
310
|
+
|
311
|
+
expect(page).not_to have_content('The coupon code you entered doesn\'t exist.')
|
312
|
+
expect(page).to have_content('The coupon code was successfully applied to your order.')
|
313
|
+
click_button 'Checkout'
|
314
|
+
|
315
|
+
fill_in 'order_email', with: 'test@example.com'
|
316
|
+
click_button 'Continue'
|
317
|
+
expect(page).to have_content(/Item Total:\s\$10/i)
|
318
|
+
expect(page).to have_content(/Promotion \(Promo\)\s\-\$5.00/i)
|
319
|
+
expect(page).to have_content(/Order Total:\s\$5/i)
|
320
|
+
fill_in_address(test_case_3_address)
|
321
|
+
click_button 'Save and Continue'
|
322
|
+
|
323
|
+
expect(page).not_to have_content(/Address Verification Failed/i)
|
324
|
+
click_button 'Save and Continue'
|
325
|
+
|
326
|
+
expect(page).to have_content(/Sales Tax\s\$0.43/i)
|
327
|
+
expect(page).to have_content(/Order Total:\s\$15.43/i)
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'TaxCloud Test Case 3, with item discount, multiple items' do
|
331
|
+
add_to_cart('Shirt')
|
332
|
+
add_to_cart('Shirt')
|
333
|
+
|
334
|
+
if Spree.solidus_gem_version >= Gem::Version.new('2.8.0')
|
335
|
+
fill_in 'coupon_code', with: 'AAAA'
|
336
|
+
click_button 'Apply Code'
|
337
|
+
else
|
338
|
+
fill_in 'order_coupon_code', with: 'AAAA'
|
339
|
+
click_button 'Update'
|
340
|
+
end
|
341
|
+
|
342
|
+
expect(page).not_to have_content('The coupon code you entered doesn\'t exist.')
|
343
|
+
expect(page).to have_content('The coupon code was successfully applied to your order.')
|
344
|
+
click_button 'Checkout'
|
345
|
+
|
346
|
+
fill_in 'order_email', with: 'test@example.com'
|
347
|
+
click_button 'Continue'
|
348
|
+
expect(page).to have_content(/Item Total:\s\$20/i)
|
349
|
+
expect(page).to have_content(/Promotion \(Promo\)\s\-\$5.00/i)
|
350
|
+
expect(page).to have_content(/Order Total:\s\$15/i)
|
351
|
+
fill_in_address(test_case_3_address)
|
352
|
+
click_button 'Save and Continue'
|
353
|
+
|
354
|
+
expect(page).not_to have_content(/Address Verification Failed/i)
|
355
|
+
click_button 'Save and Continue'
|
356
|
+
|
357
|
+
expect(page).to have_content(/Sales Tax\s\$1.29/i)
|
358
|
+
expect(page).to have_content(/Order Total:\s\$36.29/i)
|
359
|
+
end
|
360
|
+
|
361
|
+
it 'TaxCloud Test Case 6, with shipping promotion' do
|
362
|
+
add_to_cart('Shirt')
|
363
|
+
click_button 'Checkout'
|
364
|
+
fill_in 'order_email', with: 'test@example.com'
|
365
|
+
click_button 'Continue'
|
366
|
+
|
367
|
+
expect(page).to have_content(/Item Total:\s\$10/i)
|
368
|
+
|
369
|
+
fill_in_address(test_case_6_address)
|
370
|
+
click_button 'Save and Continue'
|
371
|
+
|
372
|
+
expect(page).to have_content(/Sales Tax\s\$1.78/i)
|
373
|
+
expect(page).to have_content(/Order Total:\s\$21.78/i)
|
374
|
+
expect(page).not_to have_content(/Address Verification Failed/i)
|
375
|
+
|
376
|
+
click_button 'Save and Continue'
|
377
|
+
|
378
|
+
expect(page).to have_content(/Sales Tax\s\$1.78/i)
|
379
|
+
expect(page).to have_content(/Order Total:\s\$21.78/i)
|
380
|
+
|
381
|
+
fill_in 'Coupon Code', with: 'BBBB'
|
382
|
+
click_button 'Apply Code'
|
383
|
+
|
384
|
+
expect(page).to have_content(/Sales Tax\s\$0.89/i)
|
385
|
+
expect(page).to have_content(/Order Total:\s\$10.89/i)
|
386
|
+
|
387
|
+
click_button 'Save and Continue'
|
388
|
+
click_button 'Place Order'
|
389
|
+
|
390
|
+
expect(current_path).to match(spree.order_path(Spree::Order.last))
|
391
|
+
|
392
|
+
# $10 price + $10 shipping - $10 shipping promo + $0.89 tax
|
393
|
+
expect(page).to have_content(/Sales Tax\s\$0.89/i)
|
394
|
+
expect(page).to have_content(/ORDER TOTAL:\s\$10.89/i)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
def add_to_cart(item_name)
|
399
|
+
visit spree.products_path
|
400
|
+
click_link item_name
|
401
|
+
click_button 'add-to-cart-button'
|
402
|
+
end
|
403
|
+
|
404
|
+
def fill_in_address(address)
|
405
|
+
fieldname = 'order_bill_address_attributes'
|
406
|
+
if Spree::Config.has_preference?(:use_combined_first_and_last_name_in_address) &&
|
407
|
+
Spree::Config.use_combined_first_and_last_name_in_address
|
408
|
+
fill_in "#{fieldname}_name", with: address.name
|
409
|
+
else
|
410
|
+
fill_in "#{fieldname}_firstname", with: address.first_name
|
411
|
+
fill_in "#{fieldname}_lastname", with: address.last_name
|
412
|
+
end
|
413
|
+
|
414
|
+
fill_in "#{fieldname}_address1", with: address.address1
|
415
|
+
fill_in "#{fieldname}_city", with: address.city
|
416
|
+
select address.country.name, from: "#{fieldname}_country_id"
|
417
|
+
|
418
|
+
# Wait for the ajax to complete for the states selector.
|
419
|
+
Timeout.timeout(Capybara.default_max_wait_time) do
|
420
|
+
loop do
|
421
|
+
break if page.evaluate_script('jQuery.active').to_i == 0
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
if !address.state.nil?
|
426
|
+
select address.state.name, from: "#{fieldname}_state_id"
|
427
|
+
else
|
428
|
+
expect(page).not_to have_css("##{fieldname}_state_id.required")
|
429
|
+
end
|
430
|
+
fill_in "#{fieldname}_zipcode", with: address.zipcode
|
431
|
+
fill_in "#{fieldname}_phone", with: address.phone
|
432
|
+
end
|
433
|
+
|
434
|
+
def stock_location_address
|
435
|
+
build(
|
436
|
+
:address,
|
437
|
+
address1: '3121 W Government Way',
|
438
|
+
city: 'Seattle',
|
439
|
+
country: Spree::Country.find_by(name: 'United States of America'),
|
440
|
+
state: Spree::State.find_by(abbr: 'WA'),
|
441
|
+
zipcode: '98199-1402'
|
442
|
+
)
|
443
|
+
end
|
444
|
+
|
445
|
+
def test_case_1a_address
|
446
|
+
build(
|
447
|
+
:address,
|
448
|
+
address1: '1 3rd Street',
|
449
|
+
city: 'Seattle',
|
450
|
+
country: Spree::Country.find_by(name: 'United States of America'),
|
451
|
+
state: Spree::State.find_by(abbr: 'WA'),
|
452
|
+
zipcode: '98001'
|
453
|
+
)
|
454
|
+
end
|
455
|
+
|
456
|
+
def test_case_1b_address
|
457
|
+
build(
|
458
|
+
:address,
|
459
|
+
address1: '16422 SE 128th St',
|
460
|
+
city: 'Renton',
|
461
|
+
country: Spree::Country.find_by(name: 'United States of America'),
|
462
|
+
state: Spree::State.find_by(abbr: 'WA'),
|
463
|
+
zipcode: '98059'
|
464
|
+
)
|
465
|
+
end
|
466
|
+
|
467
|
+
def test_case_2a_address
|
468
|
+
build(
|
469
|
+
:address,
|
470
|
+
address1: '75 Rev Martin Luther King Jr Drive',
|
471
|
+
city: 'St. Paul',
|
472
|
+
country: Spree::Country.find_by(name: 'United States of America'),
|
473
|
+
state: Spree::State.find_by(abbr: 'MN'),
|
474
|
+
zipcode: '55155'
|
475
|
+
)
|
476
|
+
end
|
477
|
+
alias_method :test_case_2b_address, :test_case_2a_address
|
478
|
+
|
479
|
+
def test_case_3_address
|
480
|
+
build(
|
481
|
+
:address,
|
482
|
+
address1: '2300 N Lincoln Blvd',
|
483
|
+
city: 'Oklahoma City',
|
484
|
+
country: Spree::Country.where(name: 'United States of America').first,
|
485
|
+
state: Spree::State.where(abbr: 'OK').first,
|
486
|
+
zipcode: '73105'
|
487
|
+
)
|
488
|
+
end
|
489
|
+
|
490
|
+
def test_case_6_address
|
491
|
+
build(
|
492
|
+
:address,
|
493
|
+
address1: '384 Northyards Blvd NW',
|
494
|
+
city: 'Atlanta',
|
495
|
+
country: Spree::Country.where(name: 'United States of America').first,
|
496
|
+
state: Spree::State.where(abbr: 'GA').first,
|
497
|
+
zipcode: '30313'
|
498
|
+
)
|
499
|
+
end
|
500
|
+
|
501
|
+
def alabama_address
|
502
|
+
build(
|
503
|
+
:address,
|
504
|
+
address1: '143 Swan Street',
|
505
|
+
city: 'Montgomery',
|
506
|
+
country: Spree::Country.where(name: 'United States of America').first,
|
507
|
+
state: Spree::State.where(name: 'Alabama').first,
|
508
|
+
zipcode: '36110'
|
509
|
+
)
|
510
|
+
end
|
511
|
+
end
|