solidus_tax_cloud 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|