Skip to content

Commit

Permalink
Merge pull request #3023 from nebulab/issue-3020-availability-validat…
Browse files Browse the repository at this point in the history
…ions

Rescue from `Spree::Order::InsufficientStock` on `frontend` checkout flow
  • Loading branch information
kennyadsl authored Jan 18, 2019
2 parents 24ad270 + fb9a631 commit b825223
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 0 deletions.
1 change: 1 addition & 0 deletions core/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1472,6 +1472,7 @@ en:
inventory_adjustment: Inventory Adjustment
inventory_canceled: Inventory canceled
inventory_error_flash_for_insufficient_quantity: "%{names} became unavailable."
inventory_error_flash_for_insufficient_shipment_quantity: "Quantity selected of %{unavailable_items} is not available. Still, items may be available from another stock location, please try again."
inventory_not_available: Inventory not available for %{item}.
inventory_state: Inventory State
inventory_states:
Expand Down
18 changes: 18 additions & 0 deletions frontend/app/controllers/spree/checkout_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class CheckoutController < Spree::StoreController
helper 'spree/orders'

rescue_from Spree::Core::GatewayError, with: :rescue_from_spree_gateway_error
rescue_from Spree::Order::InsufficientStock, with: :insufficient_stock_error

# Updates the order and advances to the next state (when possible.)
def update
Expand Down Expand Up @@ -233,5 +234,22 @@ def rescue_from_spree_gateway_error(exception)
def check_authorization
authorize!(:edit, current_order, cookies.signed[:guest_token])
end

def insufficient_stock_error
packages = @order.shipments.map(&:to_package)
if packages.empty?
flash[:error] = I18n.t('spree.insufficient_stock_for_order')
redirect_to cart_path
else
availability_validator = Spree::Stock::AvailabilityValidator.new
unavailable_items = @order.line_items.reject { |line_item| availability_validator.validate(line_item) }
if unavailable_items.any?
item_names = unavailable_items.map(&:name).to_sentence
flash[:error] = t('spree.inventory_error_flash_for_insufficient_shipment_quantity', unavailable_items: item_names)
@order.restart_checkout_flow
redirect_to spree.checkout_state_path(@order.state)
end
end
end
end
end
39 changes: 39 additions & 0 deletions frontend/spec/controllers/spree/checkout_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,45 @@ def post_address
expect(flash[:error]).to eq(I18n.t('spree.payment_processing_failed'))
end
end

context "when InsufficientStock error is raised" do
before do
allow(controller).to receive_messages current_order: order
allow(controller).to receive_messages check_authorization: true
allow(controller).to receive_messages ensure_sufficient_stock_lines: true
end

context "when the order has no shipments" do
let(:order) { Spree::TestingSupport::OrderWalkthrough.up_to(:address) }

before do
allow(order).to receive_messages shipments: []
# Order#next is the tipical failure point here:
allow(order).to receive(:next).and_raise(Spree::Order::InsufficientStock)
end

it "redirects the customer to the cart page with an error message" do
put :update, params: { state: order.state, order: {} }
expect(flash[:error]).to eq(I18n.t('spree.insufficient_stock_for_order'))
expect(response).to redirect_to(spree.cart_path)
end
end

context "when the order has shipments" do
let(:order) { Spree::TestingSupport::OrderWalkthrough.up_to(:payment) }

context "when items become somehow not available anymore" do
before { Spree::StockItem.update_all backorderable: false }

it "redirects the customer to the address checkout page with an error message" do
put :update, params: { state: order.state, order: {} }
error = I18n.t('spree.inventory_error_flash_for_insufficient_shipment_quantity', unavailable_items: order.products.first.name)
expect(flash[:error]).to eq(error)
expect(response).to redirect_to(spree.checkout_state_path(state: :address))
end
end
end
end
end

context "When last inventory item has been purchased" do
Expand Down
71 changes: 71 additions & 0 deletions frontend/spec/features/checkout_confirm_insufficient_stock_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# frozen_string_literal: true

require 'spec_helper'

describe "Checkout confirm page submission", type: :feature do
include_context 'checkout setup'

context "when the product from the order is not backorderable but has enough stock quantity" do
let(:user) { create(:user) }

let(:order) { Spree::TestingSupport::OrderWalkthrough.up_to(:payment) }
let(:order_product) { order.products.first }
let(:order_stock_item) { order_product.stock_items.first }

before do
order_stock_item.update! backorderable: false
order_stock_item.set_count_on_hand(1)
allow_any_instance_of(Spree::CheckoutController).to receive_messages(current_order: order)
allow_any_instance_of(Spree::CheckoutController).to receive_messages(try_spree_current_user: user)
allow_any_instance_of(Spree::OrdersController).to receive_messages(try_spree_current_user: user)
end

context 'when there are not other backorderable stock locations' do
context 'when the customer is on the confirm page and the availabilty drops to zero' do
before do
visit spree.checkout_state_path(:confirm)
order_stock_item.set_count_on_hand(0)
end

it 'redirects to cart page and shows an unavailable product message' do
click_button "Place Order"
expect(page).to have_content "#{order_product.name} became unavailable"
expect(page).to have_current_path spree.cart_path
end
end
end

context 'when there is another backorderable stock location' do
before do
create :stock_location, backorderable_default: true, default: false
end

context 'when the customer is on the confirm page and the availabilty drops to zero' do
before do
visit spree.checkout_state_path(:confirm)
order_stock_item.set_count_on_hand(0)
end

it "redirects to the address checkout page and shows an availability changed message" do
click_button "Place Order"
error_message = "Quantity selected of #{order_product.name} is not available. Still, items may be available from another stock location, please try again."
expect(page).to have_content error_message
expect(page).to have_current_path spree.checkout_state_path(:address)
end

it "can still complete the order using the backorderable stock location by restarting the checkout" do
click_button "Place Order"
expect(page).to have_current_path spree.checkout_state_path(:address)
click_button "Save and Continue"
expect(page).to have_current_path spree.checkout_state_path(:delivery)
click_button "Save and Continue"
expect(page).to have_current_path spree.checkout_state_path(:payment)
click_button "Save and Continue"
expect(page).to have_current_path spree.checkout_state_path(:confirm)
click_button "Place Order"
expect(page).to have_content 'Your order has been processed successfully'
end
end
end
end
end

0 comments on commit b825223

Please sign in to comment.