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.
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