solidus_easypost 1.0.5 → 2.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 (77) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +35 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github/stale.yml +17 -0
  5. data/.gitignore +20 -0
  6. data/.rspec +2 -0
  7. data/.rubocop.yml +4 -0
  8. data/.rubocop_todo.yml +71 -0
  9. data/Gemfile +33 -0
  10. data/LICENSE +26 -0
  11. data/README.md +104 -0
  12. data/Rakefile +6 -0
  13. data/app/decorators/models/solidus_easypost/spree/address_decorator.rb +30 -0
  14. data/app/decorators/models/solidus_easypost/spree/shipment_decorator.rb +52 -0
  15. data/app/decorators/models/solidus_easypost/spree/shipping_rate_decorator.rb +13 -0
  16. data/app/decorators/models/solidus_easypost/spree/stock/estimator_decorator.rb +15 -0
  17. data/app/decorators/models/solidus_easypost/spree/stock/package_decorator.rb +27 -0
  18. data/app/decorators/models/solidus_easypost/spree/stock_location_decorator.rb +26 -0
  19. data/app/models/solidus_easypost/estimator.rb +51 -0
  20. data/app/models/solidus_easypost/return_authorization.rb +38 -0
  21. data/bin/console +17 -0
  22. data/bin/rails +8 -0
  23. data/bin/rails-engine +15 -0
  24. data/bin/rails-sandbox +17 -0
  25. data/bin/rake +7 -0
  26. data/bin/sandbox +84 -0
  27. data/bin/setup +8 -0
  28. data/config/locales/en.yml +5 -0
  29. data/config/routes.rb +5 -0
  30. data/db/migrate/20140515024440_add_easy_post_fields_to_shipping_rate.rb +9 -0
  31. data/lib/generators/solidus_easypost/install/install_generator.rb +22 -0
  32. data/lib/solidus_easypost.rb +21 -0
  33. data/lib/solidus_easypost/configuration.rb +11 -0
  34. data/lib/solidus_easypost/engine.rb +19 -0
  35. data/lib/solidus_easypost/factories.rb +24 -0
  36. data/lib/solidus_easypost/version.rb +5 -0
  37. data/solidus_easypost.gemspec +38 -0
  38. data/spec/cassettes/SolidusEasypost_ReturnAuthorization/_easypost_shipment/is_an_EasyPost_Shipment_object.yml +271 -0
  39. data/spec/cassettes/SolidusEasypost_ReturnAuthorization/_return_label/has_the_correct_fields.yml +336 -0
  40. data/spec/cassettes/SolidusEasypost_ReturnAuthorization/_return_label/is_an_EasyPost_PackageLabel_object.yml +336 -0
  41. data/spec/cassettes/Spree_Address/_easypost_address/has_the_correct_attributes.yml +69 -0
  42. data/spec/cassettes/Spree_Address/_easypost_address/is_an_EasyPost_Address_object.yml +69 -0
  43. data/spec/cassettes/Spree_Order/_refresh_shipment_rates/can_get_rates_from_easy_post.yml +271 -0
  44. data/spec/cassettes/Spree_Order/_refresh_shipment_rates/create_shipping_methods_for_the_rates.yml +269 -0
  45. data/spec/cassettes/Spree_Shipment/_buys_a_shipping_rate_after_transitioning_to_ship.yml +403 -0
  46. data/spec/cassettes/Spree_Shipment/_easypost_shipment/when_it_is_a_new_shipment/behaves_like_an_easypost_shipment/is_an_EasyPost_Shipment_object.yml +271 -0
  47. data/spec/cassettes/Spree_Shipment/_easypost_shipment/when_it_is_a_new_shipment/calls_the_api.yml +200 -0
  48. data/spec/cassettes/Spree_Shipment/_easypost_shipment/when_it_is_an_existing_shipment/behaves_like_an_easypost_shipment/is_an_EasyPost_Shipment_object.yml +334 -0
  49. data/spec/cassettes/Spree_Shipment/_easypost_shipment/when_it_is_an_existing_shipment/loads_the_existing_shipment.yml +271 -0
  50. data/spec/cassettes/Spree_StockLocation/_easypost_address/has_the_correct_attributes.yml +69 -0
  51. data/spec/cassettes/Spree_StockLocation/_easypost_address/is_an_EasyPost_Address_object.yml +69 -0
  52. data/spec/cassettes/Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_dont_exist/creates_new_shipping_methods.yml +269 -0
  53. data/spec/cassettes/Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_dont_exist/is_empty.yml +269 -0
  54. data/spec/cassettes/Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_exist/create_no_new_shipping_methods.yml +269 -0
  55. data/spec/cassettes/Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_exist/when_shipping_methods_are_front_end_visible/has_the_correct_costs.yml +269 -0
  56. data/spec/cassettes/Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_exist/when_shipping_methods_are_front_end_visible/has_the_correct_names.yml +269 -0
  57. data/spec/cassettes/Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_exist/when_shipping_methods_are_not_front_end_visible/is_empty.yml +269 -0
  58. data/spec/cassettes/Spree_Stock_Package/_easypost_parcel/has_the_correct_attributes.yml +68 -0
  59. data/spec/cassettes/Spree_Stock_Package/_easypost_parcel/is_an_EasyPost_Parcel_object.yml +68 -0
  60. data/spec/cassettes/Spree_Stock_Package/_easypost_shipment/calls_the_api.yml +200 -0
  61. data/spec/cassettes/Spree_Stock_Package/_easypost_shipment/is_an_EasyPost_Shipment_object.yml +269 -0
  62. data/spec/factories/spree_modification.rb +25 -0
  63. data/spec/models/solidus_easypost/return_authorization_spec.rb +50 -0
  64. data/spec/models/spree/address_spec.rb +31 -0
  65. data/spec/models/spree/order_spec.rb +31 -0
  66. data/spec/models/spree/shipment_spec.rb +80 -0
  67. data/spec/models/spree/shipping_rate_spec.rb +16 -0
  68. data/spec/models/spree/stock/estimator_spec.rb +80 -0
  69. data/spec/models/spree/stock/package_spec.rb +35 -0
  70. data/spec/models/spree/stock_location_spec.rb +31 -0
  71. data/spec/solidus_easypost_spec.rb +19 -0
  72. data/spec/spec_helper.rb +27 -0
  73. data/spec/support/easypost.rb +3 -0
  74. data/spec/support/factory_bot.rb +3 -0
  75. data/spec/support/shipping_method_helpers.rb +43 -0
  76. data/spec/support/vcr.rb +11 -0
  77. metadata +145 -170
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusEasypost
4
+ module Spree
5
+ module Stock
6
+ module EstimatorDecorator
7
+ def shipping_rates(package, frontend_only = true)
8
+ SolidusEasypost::Estimator.new.shipping_rates(package, frontend_only)
9
+ end
10
+
11
+ ::Spree::Stock::Estimator.prepend self
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusEasypost
4
+ module Spree
5
+ module Stock
6
+ module PackageDecorator
7
+ def easypost_parcel
8
+ total_weight = contents.sum do |item|
9
+ item.quantity * item.variant.weight
10
+ end
11
+
12
+ ::EasyPost::Parcel.create weight: total_weight
13
+ end
14
+
15
+ def easypost_shipment
16
+ ::EasyPost::Shipment.create(
17
+ to_address: order.ship_address.easypost_address,
18
+ from_address: stock_location.easypost_address,
19
+ parcel: easypost_parcel
20
+ )
21
+ end
22
+
23
+ ::Spree::Stock::Package.prepend self
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusEasypost
4
+ module Spree
5
+ module StockLocationDecorator
6
+ def easypost_address
7
+ attributes = {
8
+ street1: address1,
9
+ street2: address2,
10
+ city: city,
11
+ zip: zipcode,
12
+ phone: phone,
13
+ name: name,
14
+ company: name
15
+ }
16
+
17
+ attributes[:state] = state ? state.abbr : state_name
18
+ attributes[:country] = country&.iso
19
+
20
+ ::EasyPost::Address.create attributes
21
+ end
22
+
23
+ ::Spree::StockLocation.prepend self
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusEasypost
4
+ class Estimator
5
+ def shipping_rates(package, _frontend_only = true)
6
+ shipment = package.easypost_shipment
7
+ rates = shipment.rates.sort_by { |r| r.rate.to_i }
8
+
9
+ shipping_rates = []
10
+
11
+ if rates.any?
12
+ rates.each do |rate|
13
+ spree_rate = ::Spree::ShippingRate.new(
14
+ name: "#{rate.carrier} #{rate.service}",
15
+ cost: rate.rate,
16
+ easy_post_shipment_id: rate.shipment_id,
17
+ easy_post_rate_id: rate.id,
18
+ shipping_method: find_or_create_shipping_method(rate)
19
+ )
20
+
21
+ shipping_rates << spree_rate if spree_rate.shipping_method.available_to_users?
22
+ end
23
+
24
+ # Sets cheapest rate to be selected by default
25
+ if shipping_rates.any?
26
+ shipping_rates.min_by(&:cost).selected = true
27
+ end
28
+
29
+ shipping_rates
30
+ else
31
+ []
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ # Cartons require shipping methods to be present, This will lookup a
38
+ # Shipping method based on the admin(internal)_name. This is not user facing
39
+ # and should not be changed in the admin.
40
+ def find_or_create_shipping_method(rate)
41
+ method_name = "#{rate.carrier} #{rate.service}"
42
+ ::Spree::ShippingMethod.find_or_create_by(admin_name: method_name) do |r|
43
+ r.name = method_name
44
+ r.available_to_users = false
45
+ r.code = rate.service
46
+ r.calculator = ::Spree::Calculator::Shipping::FlatRate.create
47
+ r.shipping_categories = [::Spree::ShippingCategory.first]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
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 ||= ::EasyPost::Shipment.create(
19
+ from_address: stock_location.easypost_address,
20
+ to_address: order.ship_address.easypost_address,
21
+ parcel: build_parcel,
22
+ is_return: true
23
+ )
24
+ end
25
+
26
+ def return_label(rate)
27
+ easypost_shipment.buy(rate) unless easypost_shipment.postage_label
28
+ easypost_shipment.postage_label
29
+ end
30
+
31
+ private
32
+
33
+ def build_parcel
34
+ total_weight = inventory_units.joins(:variant).sum(:weight)
35
+ ::EasyPost::Parcel.create weight: total_weight
36
+ end
37
+ end
38
+ end
@@ -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__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ if %w[g generate].include? ARGV.first
5
+ exec "#{__dir__}/rails-engine", *ARGV
6
+ else
7
+ exec "#{__dir__}/rails-sandbox", *ARGV
8
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # This command will automatically be run when you run "rails" with Rails gems
5
+ # installed from the root of your application.
6
+
7
+ ENGINE_ROOT = File.expand_path('..', __dir__)
8
+ ENGINE_PATH = File.expand_path('../lib/solidus_easypost/engine', __dir__)
9
+
10
+ # Set up gems listed in the Gemfile.
11
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
12
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
13
+
14
+ require 'rails/all'
15
+ require 'rails/engine/commands'
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ app_root = 'sandbox'
5
+
6
+ unless File.exist? "#{app_root}/bin/rails"
7
+ warn 'Creating the sandbox app...'
8
+ Dir.chdir "#{__dir__}/.." do
9
+ system "#{__dir__}/sandbox" or begin # rubocop:disable Style/AndOr
10
+ warn 'Automatic creation of the sandbox app failed'
11
+ exit 1
12
+ end
13
+ end
14
+ end
15
+
16
+ Dir.chdir app_root
17
+ exec 'bin/rails', *ARGV
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "rubygems"
5
+ require "bundler/setup"
6
+
7
+ load Gem.bin_path("rake", "rake")
@@ -0,0 +1,84 @@
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
+ $@
76
+
77
+ unbundled bundle exec rails generate solidus:auth:install
78
+
79
+ echo
80
+ echo "🚀 Sandbox app successfully created for $extension_name!"
81
+ echo "🚀 Using $RAILSDB and Solidus $BRANCH"
82
+ echo "🚀 Use 'export DB=[postgres|mysql|sqlite]' to control the DB adapter"
83
+ echo "🚀 Use 'export SOLIDUS_BRANCH=<BRANCH-NAME>' to control the Solidus version"
84
+ echo "🚀 This app is intended for test purposes."
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ gem install bundler --conservative
7
+ bundle update
8
+ bin/rake clobber
@@ -0,0 +1,5 @@
1
+ # Sample localization file for English. Add more files in this directory for other locales.
2
+ # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3
+
4
+ en:
5
+ hello: Hello world
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Spree::Core::Engine.routes.draw do
4
+ # Add your extension routes here
5
+ end
@@ -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,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusEasypost
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ class_option :auto_run_migrations, type: :boolean, default: false
7
+
8
+ def add_migrations
9
+ run 'bin/rails railties:install:migrations FROM=solidus_easypost'
10
+ end
11
+
12
+ def run_migrations
13
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]')) # rubocop:disable Metrics/LineLength
14
+ if run_migrations
15
+ run 'bin/rails db:migrate'
16
+ else
17
+ puts 'Skipping bin/rails db:migrate, don\'t forget to run it!' # rubocop:disable Rails/Output
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solidus_core'
4
+ require 'solidus_support'
5
+ require 'easypost'
6
+
7
+ require 'solidus_easypost/version'
8
+ require 'solidus_easypost/engine'
9
+ require 'solidus_easypost/configuration'
10
+
11
+ module SolidusEasypost
12
+ class << self
13
+ def configuration
14
+ @configuration ||= Configuration.new
15
+ end
16
+
17
+ def configure
18
+ yield configuration
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusEasypost
4
+ class Configuration
5
+ attr_reader :purchase_labels
6
+
7
+ def initialize
8
+ @purchase_labels = true
9
+ end
10
+ end
11
+ 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,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ end
5
+
6
+ FactoryBot.modify do
7
+ factory :address do
8
+ lastname { 'Doe' }
9
+ end
10
+
11
+ factory :variant do
12
+ weight { 10.0 }
13
+ end
14
+
15
+ factory :shipment do
16
+ transient do
17
+ inventory_units { 1 }
18
+ end
19
+
20
+ after(:create) do |shipment, e|
21
+ create_list(:inventory_unit, e.inventory_units, shipment: shipment)
22
+ end
23
+ end
24
+ end