solidus_easypost 2.0.0 → 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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +6 -0
  3. data/.github/stale.yml +4 -4
  4. data/.github_changelog_generator +2 -0
  5. data/.gitignore +1 -0
  6. data/.rubocop.yml +3 -0
  7. data/.rubocop_todo.yml +12 -47
  8. data/CHANGELOG.md +113 -0
  9. data/LICENSE +1 -1
  10. data/README.md +111 -21
  11. data/app/controllers/spree/admin/postage_labels_controller.rb +20 -0
  12. data/app/decorators/models/solidus_easypost/spree/carton_decorator.rb +38 -0
  13. data/app/decorators/models/solidus_easypost/spree/shipment_decorator.rb +18 -23
  14. data/app/models/solidus_easypost/parcel_dimension.rb +34 -0
  15. data/app/models/solidus_easypost/return_authorization.rb +2 -13
  16. data/app/overrides/spree/admin/orders/_shipment/add_postage_label.html.erb.deface +10 -0
  17. data/bin/rails +0 -1
  18. data/bin/rails-engine +0 -2
  19. data/bin/rails-sandbox +1 -2
  20. data/bin/sandbox +2 -0
  21. data/config/initializers/webhooks.rb +7 -0
  22. data/config/locales/en.yml +7 -4
  23. data/config/routes.rb +5 -1
  24. data/db/migrate/20201025110912_add_tracker_id_to_cartons.rb +6 -0
  25. data/lib/generators/solidus_easypost/install/install_generator.rb +7 -1
  26. data/lib/generators/solidus_easypost/install/templates/initializer.rb +26 -0
  27. data/lib/solidus_easypost/address_builder.rb +45 -0
  28. data/lib/solidus_easypost/calculator/base_dimension_calculator.rb +28 -0
  29. data/lib/solidus_easypost/calculator/weight_dimension_calculator.rb +22 -0
  30. data/lib/solidus_easypost/configuration.rb +25 -2
  31. data/lib/solidus_easypost/errors/unknown_partial_resource_error.rb +11 -0
  32. data/lib/solidus_easypost/estimator.rb +37 -0
  33. data/lib/solidus_easypost/parcel_builder.rb +27 -0
  34. data/lib/solidus_easypost/shipment_builder.rb +32 -0
  35. data/lib/solidus_easypost/shipping_method_selector.rb +17 -0
  36. data/lib/solidus_easypost/shipping_rate_calculator.rb +9 -0
  37. data/lib/solidus_easypost/testing_support/factories/address_factory.rb +10 -0
  38. data/lib/solidus_easypost/testing_support/factories/product_factory.rb +7 -0
  39. data/lib/solidus_easypost/testing_support/factories/shipment_factory.rb +30 -0
  40. data/lib/solidus_easypost/testing_support/factories/shipping_method_factory.rb +8 -0
  41. data/lib/solidus_easypost/testing_support/factories/stock_location_factory.rb +10 -0
  42. data/lib/solidus_easypost/testing_support/factories/variant_factory.rb +7 -0
  43. data/lib/solidus_easypost/testing_support/factories.rb +3 -0
  44. data/lib/solidus_easypost/tracker_webhook_handler.rb +14 -0
  45. data/lib/solidus_easypost/version.rb +1 -1
  46. data/lib/solidus_easypost.rb +11 -0
  47. data/solidus_easypost.gemspec +8 -5
  48. data/spec/cassettes/{Spree_Address/_easypost_address/is_an_EasyPost_Address_object.yml → address_builder/from_address.yml} +19 -23
  49. data/spec/cassettes/{Spree_StockLocation/_easypost_address/is_an_EasyPost_Address_object.yml → address_builder/from_stock_location.yml} +19 -23
  50. 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 → estimator.yml} +0 -0
  51. data/spec/cassettes/integration/checkout.yml +377 -0
  52. data/spec/cassettes/integration/order_shipping/with_purchase_labels.yml +373 -0
  53. data/spec/cassettes/integration/order_shipping/without_purchase_labels.yml +251 -0
  54. data/spec/cassettes/{Spree_Stock_Package/_easypost_parcel/is_an_EasyPost_Parcel_object.yml → parcel_builder/from_package.yml} +17 -22
  55. data/spec/cassettes/{Spree_Stock_Package/_easypost_parcel/has_the_correct_attributes.yml → parcel_builder/from_return_authorization.yml} +17 -22
  56. data/spec/cassettes/postage_labels/show.yml +72 -0
  57. data/spec/cassettes/return_authorization.yml +310 -0
  58. data/spec/cassettes/{Spree_Stock_Package/_easypost_shipment/is_an_EasyPost_Shipment_object.yml → shipment_builder/from_package.yml} +74 -92
  59. data/spec/cassettes/{Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_exist/create_no_new_shipping_methods.yml → shipment_builder/from_return_authorization.yml} +77 -95
  60. data/spec/cassettes/shipment_builder/from_shipment.yml +250 -0
  61. data/spec/controllers/spree/admin/postage_labels_controller_spec.rb +18 -0
  62. data/spec/features/admin/postage_labels_spec.rb +18 -0
  63. data/spec/integration/checkout_spec.rb +45 -0
  64. data/spec/integration/order_shipping_spec.rb +33 -0
  65. data/spec/models/solidus_easypost/parcel_dimension_spec.rb +80 -0
  66. data/spec/models/solidus_easypost/return_authorization_spec.rb +7 -40
  67. data/spec/models/spree/carton_spec.rb +57 -0
  68. data/spec/models/spree/shipment_spec.rb +48 -59
  69. data/spec/models/spree/shipping_rate_spec.rb +14 -7
  70. data/spec/solidus_easypost/address_builder_spec.rb +17 -0
  71. data/spec/solidus_easypost/calculator/weight_dimension_calculator_spec.rb +39 -0
  72. data/spec/solidus_easypost/estimator_spec.rb +52 -0
  73. data/spec/solidus_easypost/parcel_builder_spec.rb +113 -0
  74. data/spec/solidus_easypost/shipment_builder_spec.rb +28 -0
  75. data/spec/solidus_easypost/shipping_method_selector_spec.rb +33 -0
  76. data/spec/solidus_easypost/shipping_rate_calculator_spec.rb +12 -0
  77. data/spec/solidus_easypost/tracker_webhook_handler_spec.rb +58 -0
  78. data/spec/spec_helper.rb +11 -7
  79. data/spec/support/easypost.rb +3 -1
  80. data/spec/support/helpers/api_stubs.rb +40 -0
  81. data/spec/support/helpers/configuration.rb +28 -0
  82. data/spec/support/helpers/shipping_methods.rb +26 -0
  83. data/spec/support/solidus.rb +1 -0
  84. data/spec/support/vcr.rb +1 -0
  85. metadata +118 -85
  86. data/app/decorators/models/solidus_easypost/spree/address_decorator.rb +0 -30
  87. data/app/decorators/models/solidus_easypost/spree/stock/estimator_decorator.rb +0 -15
  88. data/app/decorators/models/solidus_easypost/spree/stock/package_decorator.rb +0 -27
  89. data/app/decorators/models/solidus_easypost/spree/stock_location_decorator.rb +0 -26
  90. data/app/models/solidus_easypost/estimator.rb +0 -51
  91. data/lib/solidus_easypost/factories.rb +0 -24
  92. data/spec/cassettes/SolidusEasypost_ReturnAuthorization/_easypost_shipment/is_an_EasyPost_Shipment_object.yml +0 -271
  93. data/spec/cassettes/SolidusEasypost_ReturnAuthorization/_return_label/has_the_correct_fields.yml +0 -336
  94. data/spec/cassettes/SolidusEasypost_ReturnAuthorization/_return_label/is_an_EasyPost_PackageLabel_object.yml +0 -336
  95. data/spec/cassettes/Spree_Address/_easypost_address/has_the_correct_attributes.yml +0 -69
  96. data/spec/cassettes/Spree_Order/_refresh_shipment_rates/can_get_rates_from_easy_post.yml +0 -271
  97. data/spec/cassettes/Spree_Order/_refresh_shipment_rates/create_shipping_methods_for_the_rates.yml +0 -269
  98. data/spec/cassettes/Spree_Shipment/_buys_a_shipping_rate_after_transitioning_to_ship.yml +0 -403
  99. data/spec/cassettes/Spree_Shipment/_easypost_shipment/when_it_is_a_new_shipment/behaves_like_an_easypost_shipment/is_an_EasyPost_Shipment_object.yml +0 -271
  100. data/spec/cassettes/Spree_Shipment/_easypost_shipment/when_it_is_a_new_shipment/calls_the_api.yml +0 -200
  101. data/spec/cassettes/Spree_Shipment/_easypost_shipment/when_it_is_an_existing_shipment/behaves_like_an_easypost_shipment/is_an_EasyPost_Shipment_object.yml +0 -334
  102. data/spec/cassettes/Spree_Shipment/_easypost_shipment/when_it_is_an_existing_shipment/loads_the_existing_shipment.yml +0 -271
  103. data/spec/cassettes/Spree_StockLocation/_easypost_address/has_the_correct_attributes.yml +0 -69
  104. data/spec/cassettes/Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_dont_exist/creates_new_shipping_methods.yml +0 -269
  105. data/spec/cassettes/Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_dont_exist/is_empty.yml +0 -269
  106. 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 +0 -269
  107. 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 +0 -269
  108. data/spec/cassettes/Spree_Stock_Package/_easypost_shipment/calls_the_api.yml +0 -200
  109. data/spec/factories/spree_modification.rb +0 -25
  110. data/spec/models/spree/address_spec.rb +0 -31
  111. data/spec/models/spree/order_spec.rb +0 -31
  112. data/spec/models/spree/stock/estimator_spec.rb +0 -80
  113. data/spec/models/spree/stock/package_spec.rb +0 -35
  114. data/spec/models/spree/stock_location_spec.rb +0 -31
  115. data/spec/support/shipping_method_helpers.rb +0 -43
@@ -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/rails CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
2
 
4
3
  if %w[g generate].include? ARGV.first
5
4
  exec "#{__dir__}/rails-engine", *ARGV
data/bin/rails-engine CHANGED
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
2
  # This command will automatically be run when you run "rails" with Rails gems
5
3
  # installed from the root of your application.
6
4
 
data/bin/rails-sandbox CHANGED
@@ -1,12 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
2
 
4
3
  app_root = 'sandbox'
5
4
 
6
5
  unless File.exist? "#{app_root}/bin/rails"
7
6
  warn 'Creating the sandbox app...'
8
7
  Dir.chdir "#{__dir__}/.." do
9
- system "#{__dir__}/sandbox" or begin # rubocop:disable Style/AndOr
8
+ system "#{__dir__}/sandbox" or begin
10
9
  warn 'Automatic creation of the sandbox app failed'
11
10
  exit 1
12
11
  end
data/bin/sandbox CHANGED
@@ -72,9 +72,11 @@ unbundled bundle exec rails generate spree:install \
72
72
  --user_class=Spree::User \
73
73
  --enforce_available_locales=true \
74
74
  --with-authentication=false \
75
+ --payment-method=none \
75
76
  $@
76
77
 
77
78
  unbundled bundle exec rails generate solidus:auth:install
79
+ unbundled bundle exec rails generate ${extension_name}:install
78
80
 
79
81
  echo
80
82
  echo "🚀 Sandbox app successfully created for $extension_name!"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(SolidusWebhooks)
4
+ SolidusWebhooks.config.register_webhook_handler :easypost_trackers, ->(payload) do
5
+ SolidusEasypost.configuration.webhook_handler_class.call(payload)
6
+ end
7
+ end
@@ -1,5 +1,8 @@
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
1
  en:
5
- hello: Hello world
2
+ spree:
3
+ postage_label: 'Postage Label'
4
+ open_postage_label: 'Open'
5
+ admin:
6
+ postage_labels:
7
+ show:
8
+ no_postage_label: 'No postage label available for shipment %{shipment_number}.'
data/config/routes.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Spree::Core::Engine.routes.draw do
4
- # Add your extension routes here
4
+ namespace :admin do
5
+ resources :shipments, only: [] do
6
+ resource :postage_label, only: :show
7
+ end
8
+ end
5
9
  end
@@ -0,0 +1,6 @@
1
+ class AddTrackerIdToCartons < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :spree_cartons, :easy_post_tracker_id, :string
4
+ add_index :spree_cartons, :easy_post_tracker_id
5
+ end
6
+ end
@@ -3,14 +3,20 @@
3
3
  module SolidusEasypost
4
4
  module Generators
5
5
  class InstallGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('templates', __dir__)
7
+
6
8
  class_option :auto_run_migrations, type: :boolean, default: false
7
9
 
10
+ def copy_initializer
11
+ template 'initializer.rb', 'config/initializers/solidus_easypost.rb'
12
+ end
13
+
8
14
  def add_migrations
9
15
  run 'bin/rails railties:install:migrations FROM=solidus_easypost'
10
16
  end
11
17
 
12
18
  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
19
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
14
20
  if run_migrations
15
21
  run 'bin/rails db:migrate'
16
22
  else
@@ -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
@@ -2,10 +2,33 @@
2
2
 
3
3
  module SolidusEasypost
4
4
  class Configuration
5
- attr_reader :purchase_labels
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
6
8
 
7
9
  def initialize
8
- @purchase_labels = true
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
9
32
  end
10
33
  end
11
34
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusEasypost
4
+ module Errors
5
+ class UnknownPartialResourceError < StandardError
6
+ def message
7
+ 'The passed resource must be a Spree::ReturnAuthorization or a Spree::Stock::Package!'
8
+ end
9
+ end
10
+ end
11
+ 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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusEasypost
4
+ class ShippingRateCalculator
5
+ def compute(rate)
6
+ rate.rate
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.modify do
4
+ factory :address do
5
+ address1 { '215 N 7th Ave' }
6
+ city { 'Manville' }
7
+ association(:state, name: 'New Jersey', abbr: 'NJ')
8
+ zipcode { '08835' }
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.modify do
4
+ factory :base_product do
5
+ weight { 10.0 }
6
+ end
7
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.modify do
4
+ factory :shipment do
5
+ transient do
6
+ inventory_units { 1 }
7
+ end
8
+
9
+ after(:create) do |shipment, e|
10
+ create_list(:inventory_unit, e.inventory_units, shipment: shipment)
11
+ end
12
+
13
+ trait :with_easypost do
14
+ transient do
15
+ easypost_shipment_id { 'sh_test' }
16
+ easypost_rate_id { 'rt_test' }
17
+ end
18
+
19
+ after(:create) do |shipment, evaluator|
20
+ create(
21
+ :shipping_rate,
22
+ shipment: shipment,
23
+ selected: true,
24
+ easy_post_shipment_id: evaluator.easypost_shipment_id,
25
+ easy_post_rate_id: evaluator.easypost_rate_id,
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.modify do
4
+ factory :shipping_method do
5
+ admin_name { 'Stuff' }
6
+ available_to_users { true }
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.modify do
4
+ factory :stock_location do
5
+ address1 { '131 S 8th Ave' }
6
+ city { 'Manville' }
7
+ association(:state, name: 'New Jersey', abbr: 'NJ')
8
+ zipcode { '08835' }
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.modify do
4
+ factory :variant do
5
+ weight { 10.0 }
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir["#{__dir__}/factories/**/*.rb"].sort.each { |f| require f }
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusEasypost
4
+ class TrackerWebhookHandler
5
+ def self.call(payload)
6
+ return unless payload['description'] == 'tracker.updated'
7
+
8
+ carton = ::Spree::Carton.find_by(easy_post_tracker_id: payload['result']['id'])
9
+ return unless carton
10
+
11
+ ::Spree::Event.fire 'solidus_easypost.tracker.updated', carton: carton, payload: payload
12
+ end
13
+ end
14
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidusEasypost
4
- VERSION = '2.0.0'
4
+ VERSION = '3.0.0'
5
5
  end
@@ -2,11 +2,22 @@
2
2
 
3
3
  require 'solidus_core'
4
4
  require 'solidus_support'
5
+ require 'deface'
5
6
  require 'easypost'
6
7
 
7
8
  require 'solidus_easypost/version'
8
9
  require 'solidus_easypost/engine'
9
10
  require 'solidus_easypost/configuration'
11
+ require 'solidus_easypost/address_builder'
12
+ require 'solidus_easypost/parcel_builder'
13
+ require 'solidus_easypost/shipment_builder'
14
+ require 'solidus_easypost/estimator'
15
+ require 'solidus_easypost/shipping_rate_calculator'
16
+ require 'solidus_easypost/shipping_method_selector'
17
+ require 'solidus_easypost/calculator/base_dimension_calculator'
18
+ require 'solidus_easypost/calculator/weight_dimension_calculator'
19
+ require 'solidus_easypost/tracker_webhook_handler'
20
+ require 'solidus_easypost/errors/unknown_partial_resource_error'
10
21
 
11
22
  module SolidusEasypost
12
23
  class << self
@@ -8,15 +8,14 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ['Brendan Deere']
9
9
  spec.email = 'brendan@stembolt.com'
10
10
 
11
- spec.summary = 'Easy post integration for Solidus'
12
- spec.description = 'Easy post integration for Solidus'
11
+ spec.summary = 'EasyPost integration for Solidus stores.'
13
12
  spec.homepage = 'https://github.com/solidusio-contrib/solidus_easypost'
14
13
  spec.license = 'BSD-3-Clause'
15
14
 
16
15
  spec.metadata['homepage_uri'] = spec.homepage
17
16
  spec.metadata['source_code_uri'] = 'https://github.com/solidusio-contrib/solidus_easypost'
18
17
 
19
- spec.required_ruby_version = Gem::Requirement.new('~> 2.5')
18
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5')
20
19
 
21
20
  # Specify which files should be added to the gem when it is released.
22
21
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -28,11 +27,15 @@ Gem::Specification.new do |spec|
28
27
  spec.executables = files.grep(%r{^exe/}) { |f| File.basename(f) }
29
28
  spec.require_paths = ["lib"]
30
29
 
30
+ spec.add_dependency 'deface'
31
31
  spec.add_dependency 'easypost'
32
- spec.add_dependency 'solidus_core', ['>= 2.0.0', '< 3']
32
+ spec.add_dependency 'solidus_core', ['>= 2.0.0', '< 4']
33
33
  spec.add_dependency 'solidus_support', '~> 0.5'
34
34
 
35
- spec.add_development_dependency 'solidus_dev_support'
35
+ spec.add_development_dependency 'solidus_dev_support', '~> 2.1'
36
36
  spec.add_development_dependency 'vcr'
37
37
  spec.add_development_dependency 'webmock'
38
+ spec.metadata = {
39
+ 'rubygems_mfa_required' => 'true'
40
+ }
38
41
  end