mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-25 20:46:48 +00:00
Fixes #6435 i.e. If the customer paid for their order by Stripe/Paypal then the Enterprise needs to know that the order was cancelled in order to arrange a refund. Refunds are not automatically processed when an order is cancelled. This will send a very basic email to the shop, it only includes a link to view the cancelled order in the admin area initially. I created a CustomerOrderCancellation object here because orders can be cancelled in two ways (1) by the customer, so an email should be sent to the shop. (2) by the shop, so an email doesn't need to be sent. However the code for cancelling order happens in Order#cancel via the state machine. Rather than passing some sort of parameter into #cancel to indicate whether it is a customer or shop cancelled order it might be clearer to have a CustomerOrderCancellation object, there could be other differences between customer or shop cancelled orders in future maybe.
220 lines
6.7 KiB
Ruby
220 lines
6.7 KiB
Ruby
module Spree
|
|
class OrdersController < ::BaseController
|
|
include OrderCyclesHelper
|
|
include Rails.application.routes.url_helpers
|
|
|
|
layout 'darkswarm'
|
|
|
|
ssl_required :show
|
|
|
|
before_action :check_authorization
|
|
rescue_from ActiveRecord::RecordNotFound, with: :render_404
|
|
helper 'spree/products', 'spree/orders'
|
|
|
|
respond_to :html
|
|
respond_to :json
|
|
|
|
before_action :update_distribution, only: :update
|
|
before_action :filter_order_params, only: :update
|
|
before_action :enable_embedded_shopfront
|
|
|
|
prepend_before_action :require_order_authentication, only: :show
|
|
prepend_before_action :require_order_cycle, only: :edit
|
|
prepend_before_action :require_distributor_chosen, only: :edit
|
|
before_action :check_hub_ready_for_checkout, only: :edit
|
|
before_action :check_at_least_one_line_item, only: :update
|
|
|
|
def show
|
|
@order = Spree::Order.find_by!(number: params[:id])
|
|
end
|
|
|
|
def empty
|
|
if @order = current_order
|
|
@order.empty!
|
|
end
|
|
|
|
redirect_to main_app.cart_path
|
|
end
|
|
|
|
def check_authorization
|
|
session[:access_token] ||= params[:token]
|
|
order = Spree::Order.find_by(number: params[:id]) || current_order
|
|
|
|
if order
|
|
authorize! :edit, order, session[:access_token]
|
|
else
|
|
authorize! :create, Spree::Order
|
|
end
|
|
end
|
|
|
|
# Patching to redirect to shop if order is empty
|
|
def edit
|
|
@order = current_order(true)
|
|
@insufficient_stock_lines = @order.insufficient_stock_lines
|
|
@unavailable_order_variants = OrderCycleDistributedVariants.
|
|
new(current_order_cycle, current_distributor).unavailable_order_variants(@order)
|
|
|
|
if @order.line_items.empty?
|
|
redirect_to main_app.shop_path
|
|
else
|
|
associate_user
|
|
|
|
if @order.insufficient_stock_lines.present? || @unavailable_order_variants.present?
|
|
flash[:error] = t("spree.orders.error_flash_for_unavailable_items")
|
|
end
|
|
end
|
|
end
|
|
|
|
def update
|
|
@insufficient_stock_lines = []
|
|
@order = order_to_update
|
|
unless @order
|
|
flash[:error] = t(:order_not_found)
|
|
redirect_to(main_app.root_path) && return
|
|
end
|
|
|
|
if @order.update(order_params)
|
|
discard_empty_line_items
|
|
with_open_adjustments { update_totals_and_taxes }
|
|
|
|
@order.update_distribution_charge!
|
|
|
|
respond_with(@order) do |format|
|
|
format.html do
|
|
if params.key?(:checkout)
|
|
@order.next_transition.run_callbacks if @order.cart?
|
|
redirect_to checkout_state_path(@order.checkout_steps.first)
|
|
elsif @order.complete?
|
|
redirect_to order_path(@order)
|
|
else
|
|
redirect_to main_app.cart_path
|
|
end
|
|
end
|
|
end
|
|
else
|
|
# Show order with original values, not newly entered ones
|
|
@insufficient_stock_lines = @order.insufficient_stock_lines
|
|
@order.line_items(true)
|
|
respond_with(@order)
|
|
end
|
|
end
|
|
|
|
def update_distribution
|
|
@order = current_order(true)
|
|
|
|
if params[:commit] == 'Choose Hub'
|
|
distributor = Enterprise.is_distributor.find params[:order][:distributor_id]
|
|
@order.set_distributor! distributor
|
|
|
|
flash[:notice] = I18n.t(:order_choosing_hub_notice)
|
|
redirect_to request.referer
|
|
|
|
elsif params[:commit] == 'Choose Order Cycle'
|
|
@order.empty! # empty cart
|
|
order_cycle = OrderCycle.active.find params[:order][:order_cycle_id]
|
|
@order.set_order_cycle! order_cycle
|
|
|
|
flash[:notice] = I18n.t(:order_choosing_hub_notice)
|
|
redirect_to request.referer
|
|
end
|
|
end
|
|
|
|
def filter_order_params
|
|
if params[:order] && params[:order][:line_items_attributes]
|
|
params[:order][:line_items_attributes] =
|
|
remove_missing_line_items(params[:order][:line_items_attributes])
|
|
end
|
|
end
|
|
|
|
def remove_missing_line_items(attrs)
|
|
attrs.select do |_i, line_item|
|
|
Spree::LineItem.find_by(id: line_item[:id])
|
|
end
|
|
end
|
|
|
|
def cancel
|
|
@order = Spree::Order.find_by!(number: params[:id])
|
|
authorize! :cancel, @order
|
|
|
|
if CustomerOrderCancellation.new(@order).call
|
|
flash[:success] = I18n.t(:orders_your_order_has_been_cancelled)
|
|
else
|
|
flash[:error] = I18n.t(:orders_could_not_cancel)
|
|
end
|
|
redirect_to request.referer || order_path(@order)
|
|
end
|
|
|
|
private
|
|
|
|
# Updates the various denormalized total attributes of the order and
|
|
# recalculates the shipment taxes
|
|
def update_totals_and_taxes
|
|
@order.updater.update_totals
|
|
@order.shipment&.ensure_correct_adjustment
|
|
end
|
|
|
|
# Sets the adjustments to open to perform the block's action and restores
|
|
# their state to whatever the they had. Note that it does not change any new
|
|
# adjustments that might get created in the yielded block.
|
|
def with_open_adjustments
|
|
previous_states = @order.adjustments.each_with_object({}) do |adjustment, hash|
|
|
hash[adjustment.id] = adjustment.state
|
|
end
|
|
@order.adjustments.each { |adjustment| adjustment.fire_events(:open) }
|
|
|
|
yield
|
|
|
|
@order.adjustments.each do |adjustment|
|
|
previous_state = previous_states[adjustment.id]
|
|
adjustment.update_attribute(:state, previous_state) if previous_state
|
|
end
|
|
end
|
|
|
|
def discard_empty_line_items
|
|
@order.line_items = @order.line_items.select { |li| li.quantity > 0 }
|
|
end
|
|
|
|
def require_order_authentication
|
|
return if session[:access_token] || params[:token] || spree_current_user
|
|
|
|
flash[:error] = I18n.t("spree.orders.edit.login_to_view_order")
|
|
redirect_to main_app.root_path(anchor: "login?after_login=#{request.env['PATH_INFO']}")
|
|
end
|
|
|
|
def order_to_update
|
|
return @order_to_update if defined? @order_to_update
|
|
return @order_to_update = current_order unless params[:id]
|
|
|
|
@order_to_update = changeable_order_from_number
|
|
end
|
|
|
|
# If a specific order is requested, return it if it is COMPLETE and
|
|
# changes are allowed and the user has access. Return nil if not.
|
|
def changeable_order_from_number
|
|
order = Spree::Order.complete.find_by(number: params[:id])
|
|
return nil unless order.andand.changes_allowed? && can?(:update, order)
|
|
|
|
order
|
|
end
|
|
|
|
def check_at_least_one_line_item
|
|
return unless order_to_update.andand.complete?
|
|
|
|
items = params[:order][:line_items_attributes]
|
|
.andand.select{ |_k, attrs| attrs["quantity"].to_i > 0 }
|
|
|
|
if items.empty?
|
|
flash[:error] = I18n.t(:orders_cannot_remove_the_final_item)
|
|
redirect_to order_path(order_to_update)
|
|
end
|
|
end
|
|
|
|
def order_params
|
|
params.require(:order).permit(
|
|
:distributor_id, :order_cycle_id,
|
|
line_items_attributes: [:id, :quantity]
|
|
)
|
|
end
|
|
end
|
|
end
|