solidus_tax_cloud 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +35 -0
  3. data/.gem_release.yml +5 -0
  4. data/.gitignore +20 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +7 -0
  7. data/.rubocop_todo.yml +132 -0
  8. data/CHANGELOG.md +52 -0
  9. data/Gemfile +33 -0
  10. data/LICENSE +26 -0
  11. data/README.md +130 -0
  12. data/Rakefile +6 -0
  13. data/TaxCloudImplementationVerificationGuide.pdf +0 -0
  14. data/app/assets/javascripts/spree/backend/solidus_tax_cloud.js +2 -0
  15. data/app/assets/javascripts/spree/frontend/solidus_tax_cloud.js +2 -0
  16. data/app/assets/stylesheets/spree/backend/solidus_tax_cloud.css +4 -0
  17. data/app/assets/stylesheets/spree/backend/solidus_tax_cloud.scss +0 -0
  18. data/app/assets/stylesheets/spree/frontend/solidus_tax_cloud.css +4 -0
  19. data/app/decorators/models/solidus_tax_cloud/spree/app_configuration_decorator.rb +18 -0
  20. data/app/decorators/models/solidus_tax_cloud/spree/line_item_decorator.rb +47 -0
  21. data/app/decorators/models/solidus_tax_cloud/spree/order_decorator.rb +42 -0
  22. data/app/decorators/models/solidus_tax_cloud/spree/product_decorator.rb +25 -0
  23. data/app/decorators/models/solidus_tax_cloud/spree/shipment_decorator.rb +27 -0
  24. data/app/models/spree/calculator/tax_cloud_calculator.rb +111 -0
  25. data/app/models/spree/tax_cloud.rb +66 -0
  26. data/app/overrides/spree/admin/products/_form.rb +9 -0
  27. data/app/overrides/spree/admin/shared/_configuration_menu.rb +9 -0
  28. data/bin/console +17 -0
  29. data/bin/r +15 -0
  30. data/bin/rails +15 -0
  31. data/bin/rake +7 -0
  32. data/bin/sandbox +84 -0
  33. data/bin/sandbox_rails +18 -0
  34. data/bin/setup +8 -0
  35. data/config/initializers/tax_cloud_usps_username.rb +5 -0
  36. data/config/locales/en.yml +13 -0
  37. data/config/routes.rb +7 -0
  38. data/db/migrate/20121220192438_create_spree_tax_cloud_transactions.rb +13 -0
  39. data/db/migrate/20121220193944_create_spree_tax_cloud_cart_items.rb +22 -0
  40. data/db/migrate/20130829215819_fix_scale_of_cart_item_prices.rb +15 -0
  41. data/db/migrate/20140623225628_add_tic_to_products.rb +11 -0
  42. data/lib/assets/javascripts/spree/frontend/solidus_tax_cloud.js.erb +1 -0
  43. data/lib/assets/stylesheets/spree/frontend/solidus_tax_cloud.css.erb +5 -0
  44. data/lib/controllers/backend/spree/admin/tax_cloud_settings_controller.rb +29 -0
  45. data/lib/decorators/backend/controllers/solidus_tax_cloud/spree/admin/orders_controller_decorator.rb +21 -0
  46. data/lib/decorators/frontend/controllers/solidus_tax_cloud/spree/checkout_controller_decorator.rb +24 -0
  47. data/lib/generators/solidus_tax_cloud/install/install_generator.rb +29 -0
  48. data/lib/generators/solidus_tax_cloud/templates/ca-bundle.crt +3895 -0
  49. data/lib/solidus_tax_cloud.rb +10 -0
  50. data/lib/solidus_tax_cloud/engine.rb +23 -0
  51. data/lib/solidus_tax_cloud/error.rb +6 -0
  52. data/lib/solidus_tax_cloud/factories.rb +4 -0
  53. data/lib/solidus_tax_cloud/version.rb +5 -0
  54. data/lib/tasks/.gitkeep +0 -0
  55. data/lib/tasks/tax_cloud.rake +74 -0
  56. data/lib/views/backend/spree/admin/products/_edit_tax_cloud_tic.html.erb +6 -0
  57. data/lib/views/backend/spree/admin/tax_cloud_settings/edit.html.erb +32 -0
  58. data/solidus_tax_cloud.gemspec +39 -0
  59. data/spec/features/checkout_spec.rb +511 -0
  60. data/spec/models/tax_cloud_api_spec.rb +192 -0
  61. data/spec/spec_helper.rb +27 -0
  62. data/spec/support/capybara.rb +20 -0
  63. data/spec/support/tax_cloud.rb +20 -0
  64. data/spec/support/transactions.rb +5 -0
  65. metadata +204 -0
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solidus_core'
4
+ require 'solidus_support'
5
+ require 'deface'
6
+ require 'tax_cloud'
7
+
8
+ require 'solidus_tax_cloud/version'
9
+ require 'solidus_tax_cloud/engine'
10
+ require 'solidus_tax_cloud/error'
@@ -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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusTaxCloud
4
+ class Error < StandardError
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusTaxCloud
4
+ VERSION = '1.0.0'
5
+ end
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