solidus_easypost 1.0.3 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.circleci/config.yml +41 -0
- data/.gem_release.yml +5 -0
- data/.github/stale.yml +17 -0
- data/.github_changelog_generator +2 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +36 -0
- data/CHANGELOG.md +113 -0
- data/Gemfile +33 -0
- data/LICENSE +26 -0
- data/README.md +194 -0
- data/Rakefile +6 -0
- data/app/controllers/spree/admin/postage_labels_controller.rb +20 -0
- data/app/decorators/models/solidus_easypost/spree/carton_decorator.rb +38 -0
- data/app/decorators/models/solidus_easypost/spree/shipment_decorator.rb +47 -0
- data/app/decorators/models/solidus_easypost/spree/shipping_rate_decorator.rb +13 -0
- data/app/models/solidus_easypost/parcel_dimension.rb +34 -0
- data/app/models/solidus_easypost/return_authorization.rb +27 -0
- data/app/overrides/spree/admin/orders/_shipment/add_postage_label.html.erb.deface +10 -0
- data/bin/console +17 -0
- data/bin/rails +7 -0
- data/bin/rails-engine +13 -0
- data/bin/rails-sandbox +16 -0
- data/bin/rake +7 -0
- data/bin/sandbox +86 -0
- data/bin/setup +8 -0
- data/config/initializers/webhooks.rb +7 -0
- data/config/locales/en.yml +8 -0
- data/config/routes.rb +9 -0
- data/db/migrate/20140515024440_add_easy_post_fields_to_shipping_rate.rb +9 -0
- data/db/migrate/20201025110912_add_tracker_id_to_cartons.rb +6 -0
- data/lib/generators/solidus_easypost/install/install_generator.rb +28 -0
- data/lib/generators/solidus_easypost/install/templates/initializer.rb +26 -0
- data/lib/solidus_easypost/address_builder.rb +45 -0
- data/lib/solidus_easypost/calculator/base_dimension_calculator.rb +28 -0
- data/lib/solidus_easypost/calculator/weight_dimension_calculator.rb +22 -0
- data/lib/solidus_easypost/configuration.rb +34 -0
- data/lib/solidus_easypost/engine.rb +19 -0
- data/lib/solidus_easypost/errors/unknown_partial_resource_error.rb +11 -0
- data/lib/solidus_easypost/estimator.rb +37 -0
- data/lib/solidus_easypost/parcel_builder.rb +27 -0
- data/lib/solidus_easypost/shipment_builder.rb +32 -0
- data/lib/solidus_easypost/shipping_method_selector.rb +17 -0
- data/lib/solidus_easypost/shipping_rate_calculator.rb +9 -0
- data/lib/solidus_easypost/testing_support/factories/address_factory.rb +10 -0
- data/lib/solidus_easypost/testing_support/factories/product_factory.rb +7 -0
- data/lib/solidus_easypost/testing_support/factories/shipment_factory.rb +30 -0
- data/lib/solidus_easypost/testing_support/factories/shipping_method_factory.rb +8 -0
- data/lib/solidus_easypost/testing_support/factories/stock_location_factory.rb +10 -0
- data/lib/solidus_easypost/testing_support/factories/variant_factory.rb +7 -0
- data/lib/solidus_easypost/testing_support/factories.rb +3 -0
- data/lib/solidus_easypost/tracker_webhook_handler.rb +14 -0
- data/lib/solidus_easypost/version.rb +5 -0
- data/lib/solidus_easypost.rb +32 -0
- data/solidus_easypost.gemspec +41 -0
- data/spec/cassettes/address_builder/from_address.yml +65 -0
- data/spec/cassettes/address_builder/from_stock_location.yml +65 -0
- data/spec/cassettes/estimator.yml +269 -0
- data/spec/cassettes/integration/checkout.yml +377 -0
- data/spec/cassettes/integration/order_shipping/with_purchase_labels.yml +373 -0
- data/spec/cassettes/integration/order_shipping/without_purchase_labels.yml +251 -0
- data/spec/cassettes/parcel_builder/from_package.yml +63 -0
- data/spec/cassettes/parcel_builder/from_return_authorization.yml +63 -0
- data/spec/cassettes/postage_labels/show.yml +72 -0
- data/spec/cassettes/return_authorization.yml +310 -0
- data/spec/cassettes/shipment_builder/from_package.yml +251 -0
- data/spec/cassettes/shipment_builder/from_return_authorization.yml +251 -0
- data/spec/cassettes/shipment_builder/from_shipment.yml +250 -0
- data/spec/controllers/spree/admin/postage_labels_controller_spec.rb +18 -0
- data/spec/features/admin/postage_labels_spec.rb +18 -0
- data/spec/integration/checkout_spec.rb +45 -0
- data/spec/integration/order_shipping_spec.rb +33 -0
- data/spec/models/solidus_easypost/parcel_dimension_spec.rb +80 -0
- data/spec/models/solidus_easypost/return_authorization_spec.rb +17 -0
- data/spec/models/spree/carton_spec.rb +57 -0
- data/spec/models/spree/shipment_spec.rb +69 -0
- data/spec/models/spree/shipping_rate_spec.rb +23 -0
- data/spec/solidus_easypost/address_builder_spec.rb +17 -0
- data/spec/solidus_easypost/calculator/weight_dimension_calculator_spec.rb +39 -0
- data/spec/solidus_easypost/estimator_spec.rb +52 -0
- data/spec/solidus_easypost/parcel_builder_spec.rb +113 -0
- data/spec/solidus_easypost/shipment_builder_spec.rb +28 -0
- data/spec/solidus_easypost/shipping_method_selector_spec.rb +33 -0
- data/spec/solidus_easypost/shipping_rate_calculator_spec.rb +12 -0
- data/spec/solidus_easypost/tracker_webhook_handler_spec.rb +58 -0
- data/spec/solidus_easypost_spec.rb +19 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/easypost.rb +5 -0
- data/spec/support/factory_bot.rb +3 -0
- data/spec/support/helpers/api_stubs.rb +40 -0
- data/spec/support/helpers/configuration.rb +28 -0
- data/spec/support/helpers/shipping_methods.rb +26 -0
- data/spec/support/solidus.rb +1 -0
- data/spec/support/vcr.rb +12 -0
- metadata +170 -142
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusEasypost
|
|
4
|
+
class ParcelDimension
|
|
5
|
+
def initialize(**params)
|
|
6
|
+
raise ArgumentError, 'The weight param is mandatory!' unless valid_value?(value: params[:weight])
|
|
7
|
+
|
|
8
|
+
@weight = params[:weight]
|
|
9
|
+
@width = params[:width]
|
|
10
|
+
@height = params[:height]
|
|
11
|
+
@depth = params[:depth]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_h
|
|
15
|
+
hash = {
|
|
16
|
+
weight: weight
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
hash[:width] = width if valid_value?(value: width)
|
|
20
|
+
hash[:height] = height if valid_value?(value: height)
|
|
21
|
+
hash[:length] = depth if valid_value?(value: depth)
|
|
22
|
+
|
|
23
|
+
hash
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def valid_value?(value:)
|
|
29
|
+
value.present? && !value.zero?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
attr_reader :width, :height, :depth, :weight
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
|
|
5
|
+
module SolidusEasypost
|
|
6
|
+
class ReturnAuthorization
|
|
7
|
+
extend Forwardable
|
|
8
|
+
|
|
9
|
+
attr_reader :return_authorization
|
|
10
|
+
|
|
11
|
+
def_delegators :@return_authorization, :stock_location, :order, :inventory_units
|
|
12
|
+
|
|
13
|
+
def initialize(return_authorization)
|
|
14
|
+
@return_authorization = return_authorization
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def easypost_shipment
|
|
18
|
+
@easypost_shipment ||= ShipmentBuilder.from_return_authorization(self)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def return_label(rate)
|
|
22
|
+
easypost_shipment.buy(rate) unless easypost_shipment.postage_label
|
|
23
|
+
|
|
24
|
+
easypost_shipment.postage_label
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<!-- insert_bottom '[data-hook="stock-contents"] tbody' -->
|
|
2
|
+
|
|
3
|
+
<% if shipment.selected_easy_post_rate_id && shipment.shipped? %>
|
|
4
|
+
<tr class="postage-label">
|
|
5
|
+
<td colspan="5">
|
|
6
|
+
<strong><%= t('spree.postage_label') %>:</strong>
|
|
7
|
+
<%= link_to_with_icon('external-link', t('spree.open_postage_label'), admin_shipment_postage_label_path(shipment)) %>
|
|
8
|
+
</td>
|
|
9
|
+
</tr>
|
|
10
|
+
<% end %>
|
data/bin/console
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
require "bundler/setup"
|
|
6
|
+
require "solidus_easypost"
|
|
7
|
+
|
|
8
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
9
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
10
|
+
$LOAD_PATH.unshift(*Dir["#{__dir__}/../app/*"])
|
|
11
|
+
|
|
12
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
13
|
+
# require "pry"
|
|
14
|
+
# Pry.start
|
|
15
|
+
|
|
16
|
+
require "irb"
|
|
17
|
+
IRB.start(__FILE__)
|
data/bin/rails
ADDED
data/bin/rails-engine
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# This command will automatically be run when you run "rails" with Rails gems
|
|
3
|
+
# installed from the root of your application.
|
|
4
|
+
|
|
5
|
+
ENGINE_ROOT = File.expand_path('..', __dir__)
|
|
6
|
+
ENGINE_PATH = File.expand_path('../lib/solidus_easypost/engine', __dir__)
|
|
7
|
+
|
|
8
|
+
# Set up gems listed in the Gemfile.
|
|
9
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
|
10
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
|
11
|
+
|
|
12
|
+
require 'rails/all'
|
|
13
|
+
require 'rails/engine/commands'
|
data/bin/rails-sandbox
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
app_root = 'sandbox'
|
|
4
|
+
|
|
5
|
+
unless File.exist? "#{app_root}/bin/rails"
|
|
6
|
+
warn 'Creating the sandbox app...'
|
|
7
|
+
Dir.chdir "#{__dir__}/.." do
|
|
8
|
+
system "#{__dir__}/sandbox" or begin
|
|
9
|
+
warn 'Automatic creation of the sandbox app failed'
|
|
10
|
+
exit 1
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Dir.chdir app_root
|
|
16
|
+
exec 'bin/rails', *ARGV
|
data/bin/rake
ADDED
data/bin/sandbox
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
case "$DB" in
|
|
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
|
|
20
|
+
|
|
21
|
+
if [ ! -z $SOLIDUS_BRANCH ]
|
|
22
|
+
then
|
|
23
|
+
BRANCH=$SOLIDUS_BRANCH
|
|
24
|
+
else
|
|
25
|
+
BRANCH="master"
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
extension_name="solidus_easypost"
|
|
29
|
+
|
|
30
|
+
# Stay away from the bundler env of the containing extension.
|
|
31
|
+
function unbundled {
|
|
32
|
+
ruby -rbundler -e'b = proc {system *ARGV}; Bundler.respond_to?(:with_unbundled_env) ? Bundler.with_unbundled_env(&b) : Bundler.with_clean_env(&b)' -- $@
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
rm -rf ./sandbox
|
|
36
|
+
unbundled bundle exec rails new sandbox --database="$RAILSDB" \
|
|
37
|
+
--skip-bundle \
|
|
38
|
+
--skip-git \
|
|
39
|
+
--skip-keeps \
|
|
40
|
+
--skip-rc \
|
|
41
|
+
--skip-spring \
|
|
42
|
+
--skip-test \
|
|
43
|
+
--skip-javascript
|
|
44
|
+
|
|
45
|
+
if [ ! -d "sandbox" ]; then
|
|
46
|
+
echo 'sandbox rails application failed'
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
cd ./sandbox
|
|
51
|
+
cat <<RUBY >> Gemfile
|
|
52
|
+
gem 'solidus', github: 'solidusio/solidus', branch: '$BRANCH'
|
|
53
|
+
gem 'solidus_auth_devise', '>= 2.1.0'
|
|
54
|
+
gem 'rails-i18n'
|
|
55
|
+
gem 'solidus_i18n'
|
|
56
|
+
|
|
57
|
+
gem '$extension_name', path: '..'
|
|
58
|
+
|
|
59
|
+
group :test, :development do
|
|
60
|
+
platforms :mri do
|
|
61
|
+
gem 'pry-byebug'
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
RUBY
|
|
65
|
+
|
|
66
|
+
unbundled bundle install --gemfile Gemfile
|
|
67
|
+
|
|
68
|
+
unbundled bundle exec rake db:drop db:create
|
|
69
|
+
|
|
70
|
+
unbundled bundle exec rails generate spree:install \
|
|
71
|
+
--auto-accept \
|
|
72
|
+
--user_class=Spree::User \
|
|
73
|
+
--enforce_available_locales=true \
|
|
74
|
+
--with-authentication=false \
|
|
75
|
+
--payment-method=none \
|
|
76
|
+
$@
|
|
77
|
+
|
|
78
|
+
unbundled bundle exec rails generate solidus:auth:install
|
|
79
|
+
unbundled bundle exec rails generate ${extension_name}:install
|
|
80
|
+
|
|
81
|
+
echo
|
|
82
|
+
echo "🚀 Sandbox app successfully created for $extension_name!"
|
|
83
|
+
echo "🚀 Using $RAILSDB and Solidus $BRANCH"
|
|
84
|
+
echo "🚀 Use 'export DB=[postgres|mysql|sqlite]' to control the DB adapter"
|
|
85
|
+
echo "🚀 Use 'export SOLIDUS_BRANCH=<BRANCH-NAME>' to control the Solidus version"
|
|
86
|
+
echo "🚀 This app is intended for test purposes."
|
data/bin/setup
ADDED
data/config/routes.rb
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddEasyPostFieldsToShippingRate < SolidusSupport::Migration[4.2]
|
|
4
|
+
def change
|
|
5
|
+
add_column :spree_shipping_rates, :name, :string
|
|
6
|
+
add_column :spree_shipping_rates, :easy_post_shipment_id, :string
|
|
7
|
+
add_column :spree_shipping_rates, :easy_post_rate_id, :string
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusEasypost
|
|
4
|
+
module Generators
|
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
|
7
|
+
|
|
8
|
+
class_option :auto_run_migrations, type: :boolean, default: false
|
|
9
|
+
|
|
10
|
+
def copy_initializer
|
|
11
|
+
template 'initializer.rb', 'config/initializers/solidus_easypost.rb'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def add_migrations
|
|
15
|
+
run 'bin/rails railties:install:migrations FROM=solidus_easypost'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run_migrations
|
|
19
|
+
run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
|
|
20
|
+
if run_migrations
|
|
21
|
+
run 'bin/rails db:migrate'
|
|
22
|
+
else
|
|
23
|
+
puts 'Skipping bin/rails db:migrate, don\'t forget to run it!' # rubocop:disable Rails/Output
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
EasyPost.api_key = 'YOUR_API_KEY_HERE'
|
|
4
|
+
|
|
5
|
+
SolidusEasypost.configure do |config|
|
|
6
|
+
# Purchase labels from EasyPost when shipping shipments in Solidus?
|
|
7
|
+
# config.purchase_labels = true
|
|
8
|
+
|
|
9
|
+
# Create a tracker in EasyPost and receive webhooks for all cartons?
|
|
10
|
+
# config.track_all_cartons = false
|
|
11
|
+
|
|
12
|
+
# A class that responds to `#compute`, accepting an `EasyPost::Rate`
|
|
13
|
+
# instance and returning the cost for that rate.
|
|
14
|
+
# config.shipping_rate_calculator_class = 'SolidusEasypost::ShippingRateCalculator'
|
|
15
|
+
|
|
16
|
+
# A class that responds to `#shipping_method_for`, accepting an
|
|
17
|
+
# `EasyPost::Rate` instance and returning the shipping method for
|
|
18
|
+
# that rate.
|
|
19
|
+
# config.shipping_method_selector_class = 'SolidusEasypost::ShippingMethodSelector'
|
|
20
|
+
|
|
21
|
+
# A class that responds to '#compute', accepting a `SolidusEasypost::ReturnAuthorization`
|
|
22
|
+
# instance or a `Spree::Stock::Package` instance and returing the `SolidusEasypost::ParcelDimension` object.
|
|
23
|
+
# The `SolidusEasypost::Calculator::BaseDimensionCalculator` class can be extended to have a common
|
|
24
|
+
# functionality.
|
|
25
|
+
# config.parcel_dimension_calculator_class = 'SolidusEasypost::Calculator::WeightDimensionCalculator'
|
|
26
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusEasypost
|
|
4
|
+
class AddressBuilder
|
|
5
|
+
class << self
|
|
6
|
+
def from_address(address)
|
|
7
|
+
attributes = {
|
|
8
|
+
street1: address.address1,
|
|
9
|
+
street2: address.address2,
|
|
10
|
+
city: address.city,
|
|
11
|
+
zip: address.zipcode,
|
|
12
|
+
phone: address.phone
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
attributes[:company] = address.company if address.respond_to?(:company)
|
|
16
|
+
attributes[:name] = if address.respond_to?(:name)
|
|
17
|
+
address.name
|
|
18
|
+
elsif respond_to?(:full_name)
|
|
19
|
+
address.full_name
|
|
20
|
+
end
|
|
21
|
+
attributes[:state] = address.state ? address.state.abbr : address.state_name
|
|
22
|
+
attributes[:country] = address.country&.iso
|
|
23
|
+
|
|
24
|
+
::EasyPost::Address.create attributes
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def from_stock_location(stock_location)
|
|
28
|
+
attributes = {
|
|
29
|
+
street1: stock_location.address1,
|
|
30
|
+
street2: stock_location.address2,
|
|
31
|
+
city: stock_location.city,
|
|
32
|
+
zip: stock_location.zipcode,
|
|
33
|
+
phone: stock_location.phone,
|
|
34
|
+
name: stock_location.name,
|
|
35
|
+
company: stock_location.name
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
attributes[:state] = stock_location.state ? stock_location.state.abbr : stock_location.state_name
|
|
39
|
+
attributes[:country] = stock_location.country&.iso
|
|
40
|
+
|
|
41
|
+
::EasyPost::Address.create attributes
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusEasypost
|
|
4
|
+
module Calculator
|
|
5
|
+
class BaseDimensionCalculator
|
|
6
|
+
def compute(resource)
|
|
7
|
+
case resource
|
|
8
|
+
when ::SolidusEasypost::ReturnAuthorization
|
|
9
|
+
compute_for_return_authorization(resource)
|
|
10
|
+
when ::Spree::Stock::Package
|
|
11
|
+
compute_for_package(resource)
|
|
12
|
+
else
|
|
13
|
+
raise SolidusEasypost::Errors::UnknownPartialResourceError
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
protected
|
|
18
|
+
|
|
19
|
+
def compute_for_return_authorization(return_authorization)
|
|
20
|
+
raise NotImplementedError
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def compute_for_package(package)
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusEasypost
|
|
4
|
+
module Calculator
|
|
5
|
+
class WeightDimensionCalculator < BaseDimensionCalculator
|
|
6
|
+
protected
|
|
7
|
+
|
|
8
|
+
def compute_for_return_authorization(return_authorization)
|
|
9
|
+
total_weight = return_authorization.inventory_units.joins(:variant).sum(:weight)
|
|
10
|
+
SolidusEasypost::ParcelDimension.new(weight: total_weight)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def compute_for_package(package)
|
|
14
|
+
total_weight = package.contents.sum do |item|
|
|
15
|
+
item.quantity * item.variant.weight
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
SolidusEasypost::ParcelDimension.new(weight: total_weight)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusEasypost
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :purchase_labels, :track_all_cartons
|
|
6
|
+
attr_writer :shipping_rate_calculator_class, :shipping_method_selector_class, :parcel_dimension_calculator_class,
|
|
7
|
+
:webhook_handler_class
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
self.purchase_labels = true
|
|
11
|
+
self.track_all_cartons = false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def shipping_rate_calculator_class
|
|
15
|
+
@shipping_rate_calculator_class ||= 'SolidusEasypost::ShippingRateCalculator'
|
|
16
|
+
@shipping_rate_calculator_class.constantize
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def shipping_method_selector_class
|
|
20
|
+
@shipping_method_selector_class ||= 'SolidusEasypost::ShippingMethodSelector'
|
|
21
|
+
@shipping_method_selector_class.constantize
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def parcel_dimension_calculator_class
|
|
25
|
+
@parcel_dimension_calculator_class ||= 'SolidusEasypost::Calculator::WeightDimensionCalculator'
|
|
26
|
+
@parcel_dimension_calculator_class.constantize
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def webhook_handler_class
|
|
30
|
+
@webhook_handler_class ||= 'SolidusEasypost::TrackerWebhookHandler'
|
|
31
|
+
@webhook_handler_class.constantize
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spree/core'
|
|
4
|
+
require 'solidus_easypost'
|
|
5
|
+
|
|
6
|
+
module SolidusEasypost
|
|
7
|
+
class Engine < Rails::Engine
|
|
8
|
+
include SolidusSupport::EngineExtensions
|
|
9
|
+
|
|
10
|
+
isolate_namespace ::Spree
|
|
11
|
+
|
|
12
|
+
engine_name 'solidus_easypost'
|
|
13
|
+
|
|
14
|
+
# use rspec for tests
|
|
15
|
+
config.generators do |g|
|
|
16
|
+
g.test_framework :rspec
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusEasypost
|
|
4
|
+
class Estimator
|
|
5
|
+
def shipping_rates(package, _frontend_only = true)
|
|
6
|
+
easypost_rates = ShipmentBuilder.from_package(package).rates.sort_by(&:rate)
|
|
7
|
+
|
|
8
|
+
shipping_rates = easypost_rates.map { |rate| build_shipping_rate(rate) }.compact
|
|
9
|
+
shipping_rates.min_by(&:cost)&.selected = true
|
|
10
|
+
|
|
11
|
+
shipping_rates
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def build_shipping_rate(rate)
|
|
17
|
+
shipping_method = shipping_method_selector.shipping_method_for(rate)
|
|
18
|
+
return unless shipping_method.available_to_users?
|
|
19
|
+
|
|
20
|
+
::Spree::ShippingRate.new(
|
|
21
|
+
name: "#{rate.carrier} #{rate.service}",
|
|
22
|
+
cost: shipping_rate_calculator.compute(rate),
|
|
23
|
+
easy_post_shipment_id: rate.shipment_id,
|
|
24
|
+
easy_post_rate_id: rate.id,
|
|
25
|
+
shipping_method: shipping_method,
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def shipping_rate_calculator
|
|
30
|
+
@shipping_rate_calculator ||= SolidusEasypost.configuration.shipping_rate_calculator_class.new
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def shipping_method_selector
|
|
34
|
+
@shipping_method_selector ||= SolidusEasypost.configuration.shipping_method_selector_class.new
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusEasypost
|
|
4
|
+
class ParcelBuilder
|
|
5
|
+
class << self
|
|
6
|
+
def from_package(package)
|
|
7
|
+
parcel_dimension = SolidusEasypost
|
|
8
|
+
.configuration
|
|
9
|
+
.parcel_dimension_calculator_class
|
|
10
|
+
.new
|
|
11
|
+
.compute(package)
|
|
12
|
+
|
|
13
|
+
::EasyPost::Parcel.create(parcel_dimension.to_h)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def from_return_authorization(return_authorization)
|
|
17
|
+
parcel_dimension = SolidusEasypost
|
|
18
|
+
.configuration
|
|
19
|
+
.parcel_dimension_calculator_class
|
|
20
|
+
.new
|
|
21
|
+
.compute(return_authorization)
|
|
22
|
+
|
|
23
|
+
::EasyPost::Parcel.create(parcel_dimension.to_h)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusEasypost
|
|
4
|
+
class ShipmentBuilder
|
|
5
|
+
class << self
|
|
6
|
+
def from_package(package)
|
|
7
|
+
::EasyPost::Shipment.create(
|
|
8
|
+
to_address: AddressBuilder.from_address(package.order.ship_address),
|
|
9
|
+
from_address: AddressBuilder.from_stock_location(package.stock_location),
|
|
10
|
+
parcel: ParcelBuilder.from_package(package),
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def from_shipment(shipment)
|
|
15
|
+
::EasyPost::Shipment.create(
|
|
16
|
+
to_address: AddressBuilder.from_address(shipment.order.ship_address),
|
|
17
|
+
from_address: AddressBuilder.from_stock_location(shipment.stock_location),
|
|
18
|
+
parcel: ParcelBuilder.from_package(shipment.to_package),
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def from_return_authorization(return_authorization)
|
|
23
|
+
::EasyPost::Shipment.create(
|
|
24
|
+
from_address: AddressBuilder.from_stock_location(return_authorization.stock_location),
|
|
25
|
+
to_address: AddressBuilder.from_address(return_authorization.order.ship_address),
|
|
26
|
+
parcel: ParcelBuilder.from_return_authorization(return_authorization),
|
|
27
|
+
is_return: true
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusEasypost
|
|
4
|
+
class ShippingMethodSelector
|
|
5
|
+
def shipping_method_for(rate)
|
|
6
|
+
::Spree::ShippingMethod.find_or_create_by(
|
|
7
|
+
carrier: rate.carrier,
|
|
8
|
+
service_level: rate.service,
|
|
9
|
+
) do |shipping_method|
|
|
10
|
+
shipping_method.name = "#{rate.carrier} #{rate.service}"
|
|
11
|
+
shipping_method.calculator = ::Spree::Calculator::Shipping::FlatRate.create
|
|
12
|
+
shipping_method.shipping_categories = [::Spree::ShippingCategory.first]
|
|
13
|
+
shipping_method.available_to_users = false
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|