mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-27 01:43:22 +00:00
Merge pull request #7779 from Matt-Yorkley/stripe-stock-check
Stripe stock check
This commit is contained in:
@@ -37,7 +37,7 @@ class CheckoutController < ::BaseController
|
||||
# This is only required because of spree_paypal_express. If we implement
|
||||
# a version of paypal that uses this controller, and more specifically
|
||||
# the #action_failed method, then we can remove this call
|
||||
OrderCheckoutRestart.new(@order).call
|
||||
reset_order_to_cart
|
||||
rescue Spree::Core::GatewayError => e
|
||||
rescue_from_spree_gateway_error(e)
|
||||
end
|
||||
@@ -82,7 +82,7 @@ class CheckoutController < ::BaseController
|
||||
@order = current_order
|
||||
|
||||
redirect_to(main_app.shop_path) && return if redirect_to_shop?
|
||||
redirect_to_cart_path && return unless valid_order_line_items?
|
||||
handle_invalid_stock && return unless valid_order_line_items?
|
||||
|
||||
before_address
|
||||
setup_for_current_state
|
||||
@@ -100,7 +100,10 @@ class CheckoutController < ::BaseController
|
||||
distributes_order_variants?(@order)
|
||||
end
|
||||
|
||||
def redirect_to_cart_path
|
||||
def handle_invalid_stock
|
||||
cancel_incomplete_payments if valid_payment_intent_provided?
|
||||
reset_order_to_cart
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to main_app.cart_path
|
||||
@@ -112,6 +115,20 @@ class CheckoutController < ::BaseController
|
||||
end
|
||||
end
|
||||
|
||||
def cancel_incomplete_payments
|
||||
# The checkout could not complete due to stock running out. We void any pending (incomplete)
|
||||
# Stripe payments here as the order will need to be changed and resubmitted (or abandoned).
|
||||
@order.payments.incomplete.each do |payment|
|
||||
payment.void!
|
||||
payment.adjustment&.update_columns(eligible: false, state: "finalized")
|
||||
end
|
||||
flash[:notice] = I18n.t("checkout.payment_cancelled_due_to_stock")
|
||||
end
|
||||
|
||||
def reset_order_to_cart
|
||||
OrderCheckoutRestart.new(@order).call
|
||||
end
|
||||
|
||||
def setup_for_current_state
|
||||
method_name = :"before_#{@order.state}"
|
||||
__send__(method_name) if respond_to?(method_name, true)
|
||||
|
||||
@@ -46,9 +46,10 @@ module Spree
|
||||
scope :from_credit_card, -> { where(source_type: 'Spree::CreditCard') }
|
||||
scope :with_state, ->(s) { where(state: s.to_s) }
|
||||
scope :completed, -> { with_state('completed') }
|
||||
scope :incomplete, -> { where(state: %w(checkout pending requires_authorization)) }
|
||||
scope :pending, -> { with_state('pending') }
|
||||
scope :failed, -> { with_state('failed') }
|
||||
scope :valid, -> { where('state NOT IN (?)', %w(failed invalid)) }
|
||||
scope :valid, -> { where.not(state: %w(failed invalid)) }
|
||||
scope :authorization_action_required, -> { where.not(cvv_response_message: nil) }
|
||||
scope :requires_authorization, -> { with_state("requires_authorization") }
|
||||
scope :with_payment_intent, ->(code) { where(response_code: code) }
|
||||
@@ -72,7 +73,7 @@ module Spree
|
||||
transition from: [:processing, :pending, :checkout, :requires_authorization], to: :completed
|
||||
end
|
||||
event :void do
|
||||
transition from: [:pending, :completed, :checkout], to: :void
|
||||
transition from: [:pending, :completed, :requires_authorization, :checkout], to: :void
|
||||
end
|
||||
# when the card brand isnt supported
|
||||
event :invalidate do
|
||||
|
||||
@@ -1315,6 +1315,7 @@ en:
|
||||
message_html: "I agree to the seller's %{terms_and_conditions_link} and the platform %{tos_link}."
|
||||
terms_and_conditions: "Terms and Conditions"
|
||||
failed: "The checkout failed. Please let us know so that we can process your order."
|
||||
payment_cancelled_due_to_stock: "Payment cancelled: the checkout could not be completed due to stock issues."
|
||||
shops:
|
||||
hubs:
|
||||
show_closed_shops: "Show closed shops"
|
||||
|
||||
@@ -49,8 +49,7 @@ describe CheckoutController, type: :controller do
|
||||
let(:shipping_method) { distributor.shipping_methods.first }
|
||||
|
||||
before do
|
||||
order.line_items << create(:line_item,
|
||||
variant: order_cycle.variants_distributed_by(distributor).first)
|
||||
order.contents.add(order_cycle.variants_distributed_by(distributor).first)
|
||||
|
||||
allow(controller).to receive(:current_distributor).and_return(distributor)
|
||||
allow(controller).to receive(:current_order_cycle).and_return(order_cycle)
|
||||
@@ -92,19 +91,54 @@ describe CheckoutController, type: :controller do
|
||||
allow(OrderCycleDistributedVariants).to receive(:new).and_return(order_cycle_distributed_variants)
|
||||
end
|
||||
|
||||
it "redirects when some items are out of stock" do
|
||||
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return false
|
||||
context "running out of stock" do
|
||||
it "redirects when some items are out of stock" do
|
||||
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return false
|
||||
|
||||
get :edit
|
||||
expect(response).to redirect_to cart_path
|
||||
end
|
||||
get :edit
|
||||
expect(response).to redirect_to cart_path
|
||||
end
|
||||
|
||||
it "redirects when some items are not available" do
|
||||
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return true
|
||||
expect(order_cycle_distributed_variants).to receive(:distributes_order_variants?).with(order).and_return(false)
|
||||
it "redirects when some items are not available" do
|
||||
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return true
|
||||
expect(order_cycle_distributed_variants).to receive(:distributes_order_variants?).with(order).and_return(false)
|
||||
|
||||
get :edit
|
||||
expect(response).to redirect_to cart_path
|
||||
get :edit
|
||||
expect(response).to redirect_to cart_path
|
||||
end
|
||||
|
||||
context "after redirecting back from Stripe" do
|
||||
let(:order) { create(:order_with_totals_and_distribution) }
|
||||
let!(:payment) { create(:payment, state: "pending", amount: order.total, order: order) }
|
||||
let!(:transaction_fee) {
|
||||
create(:adjustment, state: "open", amount: 10, order: order, adjustable: payment)
|
||||
}
|
||||
|
||||
before do
|
||||
allow(order).to receive_message_chain(:insufficient_stock_lines, :empty?).and_return(false)
|
||||
allow(order_cycle_distributed_variants).to receive(:distributes_order_variants?).
|
||||
with(order).and_return(true)
|
||||
allow(controller).to receive(:valid_payment_intent_provided?) { true }
|
||||
order.save
|
||||
allow(order).to receive_message_chain(:payments, :completed) { [] }
|
||||
allow(order).to receive_message_chain(:payments, :incomplete) { [payment] }
|
||||
allow(payment).to receive(:adjustment) { transaction_fee }
|
||||
end
|
||||
|
||||
it "cancels the payment and resets the order to cart" do
|
||||
expect(payment).to receive(:void!).and_call_original
|
||||
|
||||
spree_post :edit
|
||||
|
||||
expect(response).to redirect_to cart_path
|
||||
expect(flash[:notice]).to eq I18n.t('checkout.payment_cancelled_due_to_stock')
|
||||
|
||||
expect(order.state).to eq "cart"
|
||||
expect(payment.state).to eq "void"
|
||||
expect(transaction_fee.reload.eligible).to eq false
|
||||
expect(transaction_fee.state).to eq "finalized"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "when items are available and in stock" do
|
||||
|
||||
Reference in New Issue
Block a user