solidus_easypost 1.0.5 → 2.0.0

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