super_good-solidus_taxjar 0.18.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +58 -0
- data/CHANGELOG.md +118 -5
- data/Gemfile +28 -10
- data/README.md +300 -45
- data/Rakefile +3 -0
- data/app/controllers/spree/admin/taxjar_settings_controller.rb +93 -0
- data/app/controllers/spree/admin/taxjar_transactions_controller.rb +37 -0
- data/app/controllers/spree/admin/transaction_sync_batches_controller.rb +18 -0
- data/app/jobs/super_good/solidus_taxjar/backfill_transaction_sync_batch_job.rb +23 -0
- data/app/jobs/super_good/solidus_taxjar/replace_transaction_job.rb +26 -0
- data/app/jobs/super_good/solidus_taxjar/report_transaction_job.rb +22 -0
- data/app/models/super_good/solidus_taxjar/configuration.rb +27 -0
- data/app/models/super_good/solidus_taxjar/order_transaction.rb +16 -0
- data/app/models/super_good/solidus_taxjar/refund_transaction.rb +13 -0
- data/app/models/super_good/solidus_taxjar/transaction_sync_batch.rb +11 -0
- data/app/models/super_good/solidus_taxjar/transaction_sync_log.rb +8 -0
- data/app/overrides/spree/admin/orders_controller_override.rb +12 -0
- data/app/overrides/spree/admin/shared/_order_submenu/add_taxjar_sync_history_tab.html.erb.deface +6 -0
- data/app/overrides/spree/admin/shared/_order_summary/add_taxjar_reported_at.html.erb.deface +30 -0
- data/app/overrides/spree/admin/shared/_taxes_tabs/add_configuration_menu_items.html.erb.deface +5 -0
- data/app/overrides/super_good/solidus_taxjar/spree/order_override.rb +21 -0
- data/app/views/spree/admin/orders/taxjar_transactions.html.erb +4 -0
- data/app/views/spree/admin/shared/_transaction_sync_log_table.html.erb +35 -0
- data/app/views/spree/admin/taxjar_settings/_nexus_regions.html.erb +23 -0
- data/app/views/spree/admin/taxjar_settings/_tax_categories.html.erb +41 -0
- data/app/views/spree/admin/taxjar_settings/edit.html.erb +17 -0
- data/app/views/spree/admin/taxjar_settings/edit_no_api_key.html.erb +21 -0
- data/app/views/spree/admin/transaction_sync_batches/index.html.erb +50 -0
- data/app/views/spree/admin/transaction_sync_batches/show.html.erb +7 -0
- data/bin/console +2 -0
- data/bin/rails-engine +1 -1
- data/bin/sandbox +43 -36
- data/bin/setup +3 -3
- data/config/routes.rb +19 -0
- data/db/migrate/20210908205201_create_taxjar_order_transactions.rb +16 -0
- data/db/migrate/20211008175113_create_taxjar_refund_transaction.rb +15 -0
- data/db/migrate/20211008183858_add_transaction_date_to_order_transaction.rb +5 -0
- data/db/migrate/20211119143354_create_configuration.rb +8 -0
- data/db/migrate/20220405213958_create_transaction_sync_batches.rb +7 -0
- data/db/migrate/20220405215225_create_transaction_sync_logs.rb +14 -0
- data/db/migrate/20220908181655_add_dates_to_transaction_sync_batch.rb +6 -0
- data/db/migrate/20220912182210_allow_null_transaction_sync_batches_on_logs.rb +5 -0
- data/db/migrate/20230320211309_add_refund_transaction_to_sync_logs.rb +5 -0
- data/lib/generators/super_good/solidus_taxjar/install/install_generator.rb +68 -0
- data/lib/super_good/engine.rb +2 -0
- data/lib/super_good/solidus_taxjar/addresses.rb +3 -3
- data/lib/super_good/solidus_taxjar/api.rb +45 -9
- data/lib/super_good/solidus_taxjar/api_params.rb +93 -26
- data/lib/super_good/solidus_taxjar/backfill_transactions.rb +11 -0
- data/lib/super_good/solidus_taxjar/cached_api.rb +23 -0
- data/lib/super_good/solidus_taxjar/calculator_helper.rb +34 -5
- data/lib/super_good/solidus_taxjar/discount_calculator.rb +1 -1
- data/lib/super_good/solidus_taxjar/overrides/request_override.rb +15 -0
- data/lib/super_good/solidus_taxjar/reportable.rb +91 -0
- data/lib/super_good/solidus_taxjar/reporting.rb +44 -0
- data/lib/super_good/solidus_taxjar/spree/legacy_reporting_subscriber.rb +41 -0
- data/lib/super_good/solidus_taxjar/spree/reporting_subscriber.rb +40 -0
- data/lib/super_good/solidus_taxjar/tax_calculator.rb +10 -4
- data/lib/super_good/solidus_taxjar/testing_support/factories/address_factory.rb +11 -0
- data/lib/super_good/solidus_taxjar/testing_support/factories/configuration_factory.rb +11 -0
- data/lib/super_good/solidus_taxjar/testing_support/factories/order_transaction_factory.rb +22 -0
- data/lib/super_good/solidus_taxjar/testing_support/factories/refund_transaction_factory.rb +7 -0
- data/lib/super_good/solidus_taxjar/testing_support/factories/transaction_sync_batch_factory.rb +9 -0
- data/lib/super_good/solidus_taxjar/testing_support/factories/transaction_sync_log_factory.rb +18 -0
- data/lib/super_good/solidus_taxjar/transaction_id_generator.rb +45 -0
- data/lib/super_good/solidus_taxjar/version.rb +1 -1
- data/lib/super_good/solidus_taxjar.rb +30 -3
- data/spec/features/spree/admin/backfill_transactions_spec.rb +138 -0
- data/spec/features/spree/admin/refund_spec.rb +167 -0
- data/spec/features/spree/admin/reporting_to_taxjar_spec.rb +156 -0
- data/spec/features/spree/admin/taxjar_settings_spec.rb +90 -0
- data/spec/features/spree/checkout_spec.rb +58 -0
- data/spec/fixtures/cassettes/Admin_TaxJar_Settings/GET_sync_nexus_regions/Taxjar_API_token_is_not_set/doesn_t_make_a_request_for_the_nexus_regions.yml +57 -0
- data/spec/fixtures/cassettes/Admin_TaxJar_Settings/GET_sync_tax_categories/Taxjar_API_token_is_not_set/doesn_t_make_a_request_for_the_tax_categories.yml +57 -0
- data/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/Taxjar_API_token_is_set/shows_the_settings_page.yml +2437 -0
- data/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/Taxjar_API_token_isn_t_set/doesn_t_show_any_other_TaxJar_features.yml +57 -0
- data/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/Taxjar_API_token_isn_t_set/shows_a_descriptive_error_message.yml +57 -0
- data/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/Taxjar_reporting_is_enabled/shows_that_reporting_is_enabled.yml +2382 -0
- data/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/order_is_shipped/the_user_backfills_their_transactions.yml +2511 -0
- data/spec/fixtures/cassettes/Admin_TaxJar_Settings/Taxjar_settings_tab/the_user_navigates_to_the_TaxJar_Settings.yml +2382 -0
- data/spec/fixtures/cassettes/Admin_Transaction_Sync_Batches/user_has_a_shipped_order/starts_a_transaction_backfill.yml +370 -0
- data/spec/fixtures/cassettes/Reporting_orders_to_TaxJar/shipping_a_complete_and_paid_order.yml +310 -0
- data/spec/fixtures/cassettes/Reporting_orders_to_TaxJar/updating_an_order_which_was_not_reported_due_to_failure/it_reports_the_order_instead_of_trying_to_replace_it.yml +794 -0
- data/spec/fixtures/cassettes/Reporting_orders_to_TaxJar/with_an_order_with_invalid_zipcode/retry_of_a_previously_failed_transaction_sync.yml +418 -0
- data/spec/fixtures/cassettes/Spree_Admin_TransactionSyncBatchesController/_create/creates_a_batch.yml +250 -0
- data/spec/fixtures/cassettes/Spree_Admin_TransactionSyncBatchesController/_create/creates_a_log_in_the_batch_with_an_order.yml +250 -0
- data/spec/fixtures/cassettes/Spree_Admin_TransactionSyncBatchesController/_create/user_supplies_a_start_date/creates_a_batch.yml +250 -0
- data/spec/fixtures/cassettes/Spree_Admin_TransactionSyncBatchesController/_create/user_supplies_a_start_date/user_supplies_start_and_end_date/creates_a_batch.yml +250 -0
- data/spec/fixtures/cassettes/SuperGood_SolidusTaxjar_CalculatorHelper/_taxable_address_/when_taxable_address_check_returns_true/with_US_address/when_the_address_is_not_within_a_nexus_region/1_3_2_2_2_1.yml +58 -0
- data/spec/fixtures/cassettes/SuperGood_SolidusTaxjar_CalculatorHelper/_taxable_address_/when_taxable_address_check_returns_true/with_US_address/when_the_address_is_within_a_nexus_region/1_3_2_2_1_1.yml +58 -0
- data/spec/fixtures/cassettes/SuperGood_SolidusTaxjar_Reporting/_refund_and_create_transaction/when_Taxjar_cannot_create_a_refund_transaction/doesn_t_create_a_new_transaction.yml +393 -0
- data/spec/fixtures/cassettes/SuperGood_SolidusTaxjar_Reporting/_refund_and_create_transaction/when_Taxjar_cannot_create_a_refund_transaction/raises_an_error.yml +393 -0
- data/spec/fixtures/cassettes/Taxjar_API_Request/logging_is_disabled/doesn_t_call_the_logger.yml +158 -0
- data/spec/fixtures/cassettes/Taxjar_API_Request/logging_is_enabled/calls_the_logger.yml +158 -0
- data/spec/fixtures/cassettes/features/spree/admin/checkout.yml +238 -0
- data/spec/fixtures/cassettes/features/spree/admin/refund.yml +1162 -0
- data/spec/jobs/super_good/solidus_taxjar/backfill_transaction_sync_batch_job_spec.rb +117 -0
- data/spec/jobs/super_good/solidus_taxjar/replace_transaction_job_spec.rb +95 -0
- data/spec/jobs/super_good/solidus_taxjar/report_transaction_job_spec.rb +76 -0
- data/spec/models/super_good/solidus_taxjar/configuration_spec.rb +79 -0
- data/spec/models/super_good/solidus_taxjar/order_transaction_spec.rb +36 -0
- data/spec/models/super_good/solidus_taxjar/transaction_sync_batch_spec.rb +48 -0
- data/spec/requests/spree/admin/order_request_spec.rb +121 -0
- data/spec/requests/spree/admin/taxjar_settings_request_spec.rb +198 -0
- data/spec/requests/spree/admin/taxjar_transactions_request_spec.rb +62 -0
- data/spec/requests/spree/admin/transaction_sync_batches_request_spec.rb +82 -0
- data/spec/spec_helper.rb +47 -4
- data/spec/subscribers/super_good/solidus_taxjar/spree/reporting_subscriber_spec.rb +278 -0
- data/spec/super_good/solidus_taxjar/addresses_spec.rb +0 -14
- data/spec/super_good/solidus_taxjar/api_params_spec.rb +284 -80
- data/spec/super_good/solidus_taxjar/api_spec.rb +168 -28
- data/spec/super_good/solidus_taxjar/backfill_transactions_spec.rb +24 -0
- data/spec/super_good/solidus_taxjar/cached_api_spec.rb +58 -0
- data/spec/super_good/solidus_taxjar/calculator_helper_spec.rb +131 -0
- data/spec/super_good/solidus_taxjar/discount_calculator_spec.rb +19 -2
- data/spec/super_good/solidus_taxjar/reportable_spec.rb +194 -0
- data/spec/super_good/solidus_taxjar/reporting_spec.rb +243 -0
- data/spec/super_good/solidus_taxjar/tax_calculator_spec.rb +19 -19
- data/spec/super_good/solidus_taxjar/tax_rate_calculator_spec.rb +8 -3
- data/spec/super_good/solidus_taxjar/transaction_id_generator_spec.rb +77 -0
- data/spec/super_good/solidus_taxjar_spec.rb +99 -0
- data/spec/support/checkoutable_store_shared_context.rb +19 -0
- data/spec/support/solidus_events_helper.rb +26 -0
- data/spec/taxjar/api/request_spec.rb +52 -0
- data/super_good-solidus_taxjar.gemspec +4 -3
- metadata +176 -14
data/bin/sandbox
CHANGED
@@ -1,58 +1,65 @@
|
|
1
1
|
#!/usr/bin/env bash
|
2
|
-
|
3
2
|
set -e
|
3
|
+
test -z "${DEBUG+empty_string}" || set -x
|
4
4
|
|
5
|
-
|
6
|
-
postgres|postgresql)
|
7
|
-
RAILSDB="postgresql"
|
8
|
-
;;
|
9
|
-
mysql)
|
10
|
-
RAILSDB="mysql"
|
11
|
-
;;
|
12
|
-
sqlite|'')
|
13
|
-
RAILSDB="sqlite3"
|
14
|
-
;;
|
15
|
-
*)
|
16
|
-
echo "Invalid DB specified: $DB"
|
17
|
-
exit 1
|
18
|
-
;;
|
19
|
-
esac
|
5
|
+
test "$DB" = "sqlite" && export DB="sqlite3"
|
20
6
|
|
21
|
-
if [
|
7
|
+
if [ -z "$PAYMENT_METHOD" ]
|
22
8
|
then
|
23
|
-
|
24
|
-
else
|
25
|
-
BRANCH="master"
|
9
|
+
PAYMENT_METHOD="none"
|
26
10
|
fi
|
27
11
|
|
12
|
+
if [ -z "$SOLIDUS_BRANCH" ]
|
13
|
+
then
|
14
|
+
echo "~~> Use 'export SOLIDUS_BRANCH=[main|v4.0|...]' to control the Solidus branch"
|
15
|
+
SOLIDUS_BRANCH="main"
|
16
|
+
fi
|
17
|
+
echo "~~> Using branch $SOLIDUS_BRANCH of solidus"
|
18
|
+
|
19
|
+
case $SOLIDUS_BRANCH in
|
20
|
+
v3* | v4.0* | v4.1*)
|
21
|
+
echo "~~> Using a SOLIDUS_BRANCH older than v4.2. Skipping the --admin-preview flag for compatibility."
|
22
|
+
ADMIN_PREVIEW_FLAG=" "
|
23
|
+
;;
|
24
|
+
*)
|
25
|
+
ADMIN_PREVIEW_FLAG="--admin-preview=false"
|
26
|
+
;;
|
27
|
+
esac
|
28
|
+
|
28
29
|
extension_name="super_good-solidus_taxjar"
|
29
30
|
|
30
31
|
# Stay away from the bundler env of the containing extension.
|
31
32
|
function unbundled {
|
32
|
-
ruby -rbundler -e'
|
33
|
+
ruby -rbundler -e'
|
34
|
+
Bundler.with_unbundled_env {system *ARGV}' -- \
|
35
|
+
env BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES=true $@
|
33
36
|
}
|
34
37
|
|
38
|
+
echo "~~~> Removing the old sandbox"
|
35
39
|
rm -rf ./sandbox
|
36
|
-
|
37
|
-
|
40
|
+
|
41
|
+
echo "~~~> Creating a pristine Rails app"
|
42
|
+
rails new sandbox \
|
43
|
+
--database="${DB:-sqlite3}" \
|
38
44
|
--skip-git \
|
39
45
|
--skip-keeps \
|
40
46
|
--skip-rc \
|
41
|
-
--skip-
|
42
|
-
--skip-test
|
43
|
-
--skip-javascript
|
47
|
+
--skip-bootsnap \
|
48
|
+
--skip-test
|
44
49
|
|
45
50
|
if [ ! -d "sandbox" ]; then
|
46
51
|
echo 'sandbox rails application failed'
|
47
52
|
exit 1
|
48
53
|
fi
|
49
54
|
|
55
|
+
echo "~~~> Adding solidus (with i18n) to the Gemfile"
|
50
56
|
cd ./sandbox
|
51
57
|
cat <<RUBY >> Gemfile
|
52
|
-
gem 'solidus', github: 'solidusio/solidus', branch: '$
|
53
|
-
gem 'solidus_auth_devise', '>= 2.1.0'
|
58
|
+
gem 'solidus', github: 'solidusio/solidus', branch: '$SOLIDUS_BRANCH'
|
54
59
|
gem 'rails-i18n'
|
55
60
|
gem 'solidus_i18n'
|
61
|
+
gem 'solidus_auth_devise'
|
62
|
+
gem "solidus_frontend"
|
56
63
|
|
57
64
|
gem '$extension_name', path: '..'
|
58
65
|
|
@@ -67,18 +74,18 @@ unbundled bundle install --gemfile Gemfile
|
|
67
74
|
|
68
75
|
unbundled bundle exec rake db:drop db:create
|
69
76
|
|
70
|
-
unbundled bundle exec rails generate
|
77
|
+
unbundled bundle exec rails generate solidus:install \
|
71
78
|
--auto-accept \
|
72
|
-
--
|
73
|
-
--
|
74
|
-
|
79
|
+
--payment-method=none \
|
80
|
+
--frontend=none \
|
81
|
+
${ADMIN_PREVIEW_FLAG} \
|
75
82
|
$@
|
76
83
|
|
77
|
-
unbundled bundle exec rails generate
|
84
|
+
SKIP_SOLIDUS_BOLT=true unbundled bundle exec rails generate solidus_frontend:install --auto-accept
|
85
|
+
unbundled bundle exec rails generate solidus:auth:install --auto-run-migrations
|
86
|
+
unbundled bundle exec rails generate super_good:solidus_taxjar:install --auto-run-migrations
|
78
87
|
|
79
88
|
echo
|
80
89
|
echo "🚀 Sandbox app successfully created for $extension_name!"
|
81
90
|
echo "🚀 Using $RAILSDB and Solidus $BRANCH"
|
82
|
-
echo "
|
83
|
-
echo "🚀 Use 'export SOLIDUS_BRANCH=<BRANCH-NAME>' to control the Solidus version"
|
84
|
-
echo "🚀 This app is intended for test purposes."
|
91
|
+
echo "🧪 This app is intended for test purposes."
|
data/bin/setup
CHANGED
data/config/routes.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Spree::Core::Engine.routes.draw do
|
4
|
+
namespace :admin do
|
5
|
+
resource :taxjar_settings, only: [:edit, :update]
|
6
|
+
resources :transaction_sync_batches, only: [:index, :show, :create]
|
7
|
+
get 'taxjar_settings/sync_nexus_regions', to: 'taxjar_settings#sync_nexus_regions'
|
8
|
+
get 'taxjar_settings/sync_tax_categories', to: 'taxjar_settings#sync_tax_categories'
|
9
|
+
post 'taxjar_settings/backfill_transactions', to: 'taxjar_settings#backfill_transactions'
|
10
|
+
|
11
|
+
resources :orders do
|
12
|
+
member do
|
13
|
+
get :taxjar_transactions
|
14
|
+
end
|
15
|
+
|
16
|
+
post 'taxjar_transaction/retry', to: "taxjar_transactions#retry"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateTaxjarOrderTransactions < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :solidus_taxjar_order_transactions do |t|
|
4
|
+
t.references :order, null: false
|
5
|
+
t.string :transaction_id, null: false
|
6
|
+
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
|
10
|
+
add_foreign_key :solidus_taxjar_order_transactions,
|
11
|
+
:spree_orders,
|
12
|
+
column: :order_id
|
13
|
+
|
14
|
+
add_index :solidus_taxjar_order_transactions, :transaction_id, unique: true
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateTaxjarRefundTransaction < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :solidus_taxjar_refund_transactions do |t|
|
4
|
+
t.references :order_transaction, index: { unique: true, name: :refund_transactions_orders_idx }
|
5
|
+
t.string :transaction_id, null: false, index: { unique: true }
|
6
|
+
t.datetime :transaction_date, null: false
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
add_foreign_key :solidus_taxjar_refund_transactions,
|
12
|
+
:solidus_taxjar_order_transactions,
|
13
|
+
column: :order_transaction_id
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateTransactionSyncLogs < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :solidus_taxjar_transaction_sync_logs do |t|
|
4
|
+
t.references :transaction_sync_batch, foreign_key: {to_table: :solidus_taxjar_transaction_sync_batches}, index: {name: "index_transaction_sync_logs_on_transaction_sync_batch_id"}, null: false
|
5
|
+
t.references :order, foreign_key: {to_table: :spree_orders}, null: false
|
6
|
+
|
7
|
+
t.references :order_transaction, foreign_key: {to_table: :solidus_taxjar_order_transactions}, index: {name: "index_transaction_sync_logs_on_order_transaction_id"}
|
8
|
+
t.integer :status, null: false, default: 0
|
9
|
+
t.string :error_message
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
class AddRefundTransactionToSyncLogs < ActiveRecord::Migration[6.1]
|
2
|
+
def change
|
3
|
+
add_reference :solidus_taxjar_transaction_sync_logs, :refund_transaction, foreign_key: {to_table: :solidus_taxjar_refund_transactions}, index: {name: "index_transaction_sync_logs_on_refund_transaction_id"}
|
4
|
+
end
|
5
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module SuperGood
|
2
|
+
module SolidusTaxjar
|
3
|
+
module Generators
|
4
|
+
class InstallGenerator < Rails::Generators::Base
|
5
|
+
class_option :auto_run_migrations, type: :boolean, default: false
|
6
|
+
|
7
|
+
def create_initializer_file
|
8
|
+
solidus_initializer_path = "config/initializers/solidus.rb"
|
9
|
+
|
10
|
+
create_file(solidus_initializer_path) unless File.exist?(solidus_initializer_path)
|
11
|
+
append_to_file(solidus_initializer_path, <<~INIT)
|
12
|
+
Spree.config do |config|
|
13
|
+
config.tax_calculator_class = SuperGood::SolidusTaxjar::TaxCalculator
|
14
|
+
end
|
15
|
+
INIT
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_omes_initializer_file
|
19
|
+
return unless Gem::Requirement.new('>= 3.2.0.alpha')
|
20
|
+
.satisfied_by?(::Spree.solidus_gem_version)
|
21
|
+
|
22
|
+
omnes_initializer_path = "config/initializers/omnes.rb"
|
23
|
+
|
24
|
+
create_file(omnes_initializer_path) unless File.exist?(omnes_initializer_path)
|
25
|
+
append_to_file(omnes_initializer_path, <<~INIT)
|
26
|
+
Rails.application.config.to_prepare do
|
27
|
+
if SolidusSupport::LegacyEventCompat.using_legacy?
|
28
|
+
require 'super_good/solidus_taxjar/spree/legacy_reporting_subscriber.rb'
|
29
|
+
SuperGood::SolidusTaxjar::Spree::LegacyReportingSubscriber.omnes_subscriber.subscribe_to(Spree::Bus)
|
30
|
+
else
|
31
|
+
require 'super_good/solidus_taxjar/spree/reporting_subscriber.rb'
|
32
|
+
SuperGood::SolidusTaxjar::Spree::ReportingSubscriber.new.subscribe_to(Spree::Bus)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
INIT
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_legacy_events_initializer_file
|
39
|
+
return unless Gem::Requirement.new('< 3.2')
|
40
|
+
.satisfied_by?(::Spree.solidus_gem_version)
|
41
|
+
|
42
|
+
initializer_path = "config/initializers/solidus_taxjar_legacy_events.rb"
|
43
|
+
|
44
|
+
create_file(initializer_path) unless File.exist?(initializer_path)
|
45
|
+
append_to_file(initializer_path, <<~INIT)
|
46
|
+
Rails.application.config.to_prepare do
|
47
|
+
require 'super_good/solidus_taxjar/spree/legacy_reporting_subscriber.rb'
|
48
|
+
SuperGood::SolidusTaxjar::Spree::LegacyReportingSubscriber.activate
|
49
|
+
end
|
50
|
+
INIT
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_migrations
|
54
|
+
run 'bin/rails railties:install:migrations FROM=super_good_solidus_taxjar'
|
55
|
+
end
|
56
|
+
|
57
|
+
def run_migrations
|
58
|
+
run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
|
59
|
+
if run_migrations
|
60
|
+
run 'bin/rails db:migrate'
|
61
|
+
else
|
62
|
+
puts 'Skipping bin/rails db:migrate, don\'t forget to run it!'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/super_good/engine.rb
CHANGED
@@ -20,7 +20,7 @@ module SuperGood
|
|
20
20
|
|
21
21
|
return if taxjar_address.nil?
|
22
22
|
|
23
|
-
Spree::Address.immutable_merge(spree_address, {
|
23
|
+
::Spree::Address.immutable_merge(spree_address, {
|
24
24
|
country: us, # TaxJar only supports the US currently.
|
25
25
|
state: state(taxjar_address.state),
|
26
26
|
zipcode: taxjar_address.zip,
|
@@ -31,7 +31,7 @@ module SuperGood
|
|
31
31
|
|
32
32
|
def possibilities(spree_address)
|
33
33
|
taxjar_addresses(spree_address).map { |taxjar_address|
|
34
|
-
Spree::Address.immutable_merge(spree_address, {
|
34
|
+
::Spree::Address.immutable_merge(spree_address, {
|
35
35
|
country: us, # TaxJar only supports the US currently.
|
36
36
|
state: state(taxjar_address.state),
|
37
37
|
zipcode: taxjar_address.zip,
|
@@ -52,7 +52,7 @@ module SuperGood
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def us
|
55
|
-
Spree::Country.find_by iso: "US"
|
55
|
+
::Spree::Country.find_by iso: "US"
|
56
56
|
end
|
57
57
|
|
58
58
|
def state(abbr)
|
@@ -3,7 +3,7 @@ module SuperGood
|
|
3
3
|
class Api
|
4
4
|
def self.default_taxjar_client
|
5
5
|
client = ::Taxjar::Client.new(
|
6
|
-
api_key: ENV
|
6
|
+
api_key: ENV["TAXJAR_API_KEY"],
|
7
7
|
api_url: ENV.fetch("TAXJAR_API_URL") { "https://api.taxjar.com" } # Sandbox URL: https://api.sandbox.taxjar.com
|
8
8
|
)
|
9
9
|
client.set_api_config('headers', {
|
@@ -17,14 +17,12 @@ module SuperGood
|
|
17
17
|
@taxjar_client = taxjar_client
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
taxjar_client.
|
22
|
-
|
20
|
+
def tax_categories
|
21
|
+
taxjar_client.categories
|
22
|
+
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
)
|
27
|
-
end
|
24
|
+
def tax_for(order)
|
25
|
+
taxjar_client.tax_for_order(ApiParams.order_params(order))
|
28
26
|
end
|
29
27
|
|
30
28
|
def tax_rate_for(address)
|
@@ -36,7 +34,17 @@ module SuperGood
|
|
36
34
|
end
|
37
35
|
|
38
36
|
def create_transaction_for(order)
|
39
|
-
|
37
|
+
latest_transaction_id =
|
38
|
+
OrderTransaction.latest_for(order)&.transaction_id
|
39
|
+
|
40
|
+
transaction_id = TransactionIdGenerator.next_transaction_id(
|
41
|
+
order: order,
|
42
|
+
current_transaction_id: latest_transaction_id
|
43
|
+
)
|
44
|
+
|
45
|
+
taxjar_client.create_order(
|
46
|
+
ApiParams.transaction_params(order, transaction_id)
|
47
|
+
)
|
40
48
|
end
|
41
49
|
|
42
50
|
def update_transaction_for(order)
|
@@ -47,6 +55,30 @@ module SuperGood
|
|
47
55
|
taxjar_client.delete_order order.number
|
48
56
|
end
|
49
57
|
|
58
|
+
def show_latest_transaction_for(order)
|
59
|
+
latest_transaction_id =
|
60
|
+
OrderTransaction.latest_for(order)&.transaction_id
|
61
|
+
|
62
|
+
return unless latest_transaction_id
|
63
|
+
|
64
|
+
taxjar_client.show_order(latest_transaction_id)
|
65
|
+
rescue Taxjar::Error::NotFound
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_refund_transaction_for(order)
|
70
|
+
unless OrderTransaction.latest_for(order)
|
71
|
+
raise NotImplementedError,
|
72
|
+
"No latest TaxJar order transaction for #{order.number}. " \
|
73
|
+
"Backfilling TaxJar transaction orders from Solidus is not yet " \
|
74
|
+
"implemented."
|
75
|
+
end
|
76
|
+
|
77
|
+
taxjar_order = show_latest_transaction_for(order)
|
78
|
+
|
79
|
+
taxjar_client.create_refund ApiParams.refund_transaction_params(order, taxjar_order)
|
80
|
+
end
|
81
|
+
|
50
82
|
def create_refund_for(reimbursement)
|
51
83
|
taxjar_client.create_refund ApiParams.refund_params(reimbursement)
|
52
84
|
end
|
@@ -55,6 +87,10 @@ module SuperGood
|
|
55
87
|
taxjar_client.validate_address ApiParams.validate_address_params(spree_address)
|
56
88
|
end
|
57
89
|
|
90
|
+
def nexus_regions
|
91
|
+
taxjar_client.nexus_regions
|
92
|
+
end
|
93
|
+
|
58
94
|
private
|
59
95
|
|
60
96
|
attr_reader :taxjar_client
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module SuperGood
|
2
2
|
module SolidusTaxjar
|
3
3
|
module ApiParams
|
4
|
+
UNTAXABLE_INVENTORY_UNIT_STATES = ["returned", "canceled"]
|
5
|
+
|
4
6
|
class << self
|
5
7
|
def order_params(order)
|
6
8
|
{}
|
@@ -9,13 +11,6 @@ module SuperGood
|
|
9
11
|
.merge(line_items_params(order.line_items))
|
10
12
|
.merge(shipping: shipping(order))
|
11
13
|
.merge(SuperGood::SolidusTaxjar.custom_order_params.call(order))
|
12
|
-
.tap do |params|
|
13
|
-
next unless SuperGood::SolidusTaxjar.logging_enabled
|
14
|
-
|
15
|
-
Rails.logger.info(
|
16
|
-
"TaxJar params for #{order.number}: #{params.inspect}"
|
17
|
-
)
|
18
|
-
end
|
19
14
|
end
|
20
15
|
|
21
16
|
def address_params(address)
|
@@ -37,20 +32,44 @@ module SuperGood
|
|
37
32
|
}.merge(order_address_params(address))
|
38
33
|
end
|
39
34
|
|
40
|
-
def transaction_params(order)
|
35
|
+
def transaction_params(order, transaction_id = order.number)
|
41
36
|
{}
|
42
37
|
.merge(customer_params(order))
|
43
38
|
.merge(order_address_params(order.tax_address))
|
44
39
|
.merge(transaction_line_items_params(order.line_items))
|
45
40
|
.merge(
|
46
|
-
transaction_id:
|
41
|
+
transaction_id: transaction_id,
|
47
42
|
transaction_date: order.completed_at.to_formatted_s(:iso8601),
|
48
|
-
|
43
|
+
# We use `payment_total` to reflect the total liablity
|
44
|
+
# transferred.
|
45
|
+
amount: [order.payments.completed.sum(&:amount) - refund_total_without_tax(order) - order.additional_tax_total, 0].max,
|
49
46
|
shipping: shipping(order),
|
50
47
|
sales_tax: sales_tax(order)
|
51
48
|
)
|
52
49
|
end
|
53
50
|
|
51
|
+
def refund_transaction_params(spree_order, taxjar_order)
|
52
|
+
{}
|
53
|
+
.merge(order_address_params(spree_order.tax_address))
|
54
|
+
.merge(
|
55
|
+
{
|
56
|
+
transaction_id: TransactionIdGenerator.refund_transaction_id(taxjar_order.transaction_id),
|
57
|
+
transaction_reference_id: taxjar_order.transaction_id,
|
58
|
+
transaction_date: spree_order.completed_at.to_formatted_s(:iso8601),
|
59
|
+
amount: -1 * taxjar_order.amount,
|
60
|
+
sales_tax: -1 * taxjar_order.sales_tax,
|
61
|
+
shipping: -1 * taxjar_order.shipping,
|
62
|
+
line_items: taxjar_order.line_items.map { |line_item|
|
63
|
+
line_item.to_h.merge({
|
64
|
+
unit_price: line_item.unit_price * -1,
|
65
|
+
discount: line_item.discount * -1,
|
66
|
+
sales_tax: line_item.sales_tax * -1
|
67
|
+
})
|
68
|
+
}
|
69
|
+
}
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
54
73
|
def refund_params(reimbursement)
|
55
74
|
additional_taxes = reimbursement.return_items.sum(&:additional_tax_total)
|
56
75
|
|
@@ -72,7 +91,7 @@ module SuperGood
|
|
72
91
|
state: spree_address.state&.abbr || spree_address.state_name,
|
73
92
|
zip: spree_address.zipcode,
|
74
93
|
city: spree_address.city,
|
75
|
-
street: spree_address.address1
|
94
|
+
street: [spree_address.address1, spree_address.address2].compact.join(' ')
|
76
95
|
}
|
77
96
|
end
|
78
97
|
|
@@ -94,9 +113,18 @@ module SuperGood
|
|
94
113
|
}
|
95
114
|
end
|
96
115
|
|
116
|
+
# @private
|
117
|
+
# This method builds line item parameters as expected by the TaxJar
|
118
|
+
# Tax API.
|
119
|
+
#
|
120
|
+
# @param line_items [Spree::LineItem::ActiveRecord_Relation] All of the
|
121
|
+
# order's line items.
|
122
|
+
# @return [Hash] A TaxJar API-friendly line item collection.
|
97
123
|
def line_items_params(line_items)
|
98
124
|
{
|
99
|
-
line_items:
|
125
|
+
line_items: line_items.filter_map { |line_item|
|
126
|
+
next unless line_item.quantity.positive?
|
127
|
+
|
100
128
|
{
|
101
129
|
id: line_item.id,
|
102
130
|
quantity: line_item.quantity,
|
@@ -104,34 +132,39 @@ module SuperGood
|
|
104
132
|
discount: discount(line_item),
|
105
133
|
product_tax_code: line_item.tax_category&.tax_code
|
106
134
|
}
|
107
|
-
|
135
|
+
}
|
108
136
|
}
|
109
137
|
end
|
110
138
|
|
139
|
+
# @private
|
140
|
+
# This method builds line item parameters as expected by the TaxJar
|
141
|
+
# Transactions API. Note that this logic different from
|
142
|
+
# `.line_item_params` as it excludes inventory units we consider to be
|
143
|
+
# untaxable (i.e. returned or cancelled inventory units).
|
144
|
+
#
|
145
|
+
# @param line_items [Spree::LineItem::ActiveRecord_Relation] All of the
|
146
|
+
# order's line items.
|
147
|
+
# @return [Hash] A TaxJar API-friendly line item collection.
|
111
148
|
def transaction_line_items_params(line_items)
|
112
149
|
{
|
113
|
-
line_items:
|
150
|
+
line_items: line_items.filter_map { |line_item|
|
151
|
+
quantity = taxable_quantity line_item
|
152
|
+
next unless quantity.positive?
|
153
|
+
|
114
154
|
{
|
115
155
|
id: line_item.id,
|
116
|
-
quantity:
|
156
|
+
quantity: quantity,
|
117
157
|
product_identifier: line_item.sku,
|
158
|
+
description: line_item.variant.descriptive_name,
|
118
159
|
product_tax_code: line_item.tax_category&.tax_code,
|
119
160
|
unit_price: line_item.price,
|
120
161
|
discount: discount(line_item),
|
121
162
|
sales_tax: line_item_sales_tax(line_item)
|
122
163
|
}
|
123
|
-
|
164
|
+
}
|
124
165
|
}
|
125
166
|
end
|
126
167
|
|
127
|
-
def valid_line_items(line_items)
|
128
|
-
# The API appears to error when sent line items with no quantity...
|
129
|
-
# but why would you do that anyway.
|
130
|
-
line_items.reject do |line_item|
|
131
|
-
line_item.quantity.zero?
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
168
|
def discount(line_item)
|
136
169
|
::SuperGood::SolidusTaxjar.discount_calculator.new(line_item).discount
|
137
170
|
end
|
@@ -143,13 +176,47 @@ module SuperGood
|
|
143
176
|
def sales_tax(order)
|
144
177
|
return 0 if order.total.zero?
|
145
178
|
|
146
|
-
order.additional_tax_total
|
179
|
+
order.additional_tax_total - order_reimbursement_tax_total(order)
|
147
180
|
end
|
148
181
|
|
149
182
|
def line_item_sales_tax(line_item)
|
150
183
|
return 0 if line_item.order.total.zero?
|
151
184
|
|
152
|
-
line_item.additional_tax_total
|
185
|
+
line_item.additional_tax_total - line_item_reimbursement_tax_total(line_item)
|
186
|
+
end
|
187
|
+
|
188
|
+
def taxable_quantity(line_item)
|
189
|
+
line_item.inventory_units
|
190
|
+
.where.not(state: UNTAXABLE_INVENTORY_UNIT_STATES)
|
191
|
+
.count
|
192
|
+
end
|
193
|
+
|
194
|
+
def line_item_reimbursement_tax_total(line_item)
|
195
|
+
line_item
|
196
|
+
.inventory_units
|
197
|
+
.flat_map(&:return_items)
|
198
|
+
.filter { |return_item| return_item.reimbursement.present? }
|
199
|
+
.sum(&:additional_tax_total)
|
200
|
+
end
|
201
|
+
|
202
|
+
def order_reimbursement_tax_total(order)
|
203
|
+
order.reimbursements.sum { |reimbursement| reimbursement_tax_total(reimbursement) }
|
204
|
+
end
|
205
|
+
|
206
|
+
def reimbursement_tax_total(reimbursement)
|
207
|
+
reimbursement.return_items.sum(&:additional_tax_total)
|
208
|
+
end
|
209
|
+
|
210
|
+
def refund_total_without_tax(order)
|
211
|
+
order.refunds.sum do |refund|
|
212
|
+
if refund.reimbursement.present?
|
213
|
+
refund.reimbursement.total - reimbursement_tax_total(refund.reimbursement)
|
214
|
+
else
|
215
|
+
# This use case represents making a line item level adjustment, and then refunding
|
216
|
+
# that amount.
|
217
|
+
refund.amount
|
218
|
+
end
|
219
|
+
end
|
153
220
|
end
|
154
221
|
end
|
155
222
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module SuperGood
|
2
|
+
module SolidusTaxjar
|
3
|
+
class BackfillTransactions
|
4
|
+
def call(start_date:, end_date:)
|
5
|
+
transaction_sync_batch = SuperGood::SolidusTaxjar::TransactionSyncBatch.create!(start_date: start_date, end_date: end_date)
|
6
|
+
SuperGood::SolidusTaxjar::BackfillTransactionSyncBatchJob.perform_later(transaction_sync_batch)
|
7
|
+
transaction_sync_batch
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SuperGood
|
2
|
+
module SolidusTaxjar
|
3
|
+
class CachedApi
|
4
|
+
# @param api [SuperGood::SolidusTaxjar::Api] instance of API wrapper
|
5
|
+
def initialize(api: SuperGood::SolidusTaxjar.api)
|
6
|
+
@api = api
|
7
|
+
end
|
8
|
+
|
9
|
+
# @param refresh [Boolean] forces a refresh of the cached regions
|
10
|
+
def nexus_regions(refresh: false)
|
11
|
+
Rails.cache.fetch(
|
12
|
+
:nexus_regions,
|
13
|
+
expires_in: SuperGood::SolidusTaxjar.cache_duration,
|
14
|
+
force: refresh
|
15
|
+
) { api.nexus_regions }
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :api
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|