solidus_easypost 1.0.3 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|