mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-04-05 07:19:14 +00:00
When a guest places an order and tries to cancel it from the order confirmation page, the cancellation silently failed and redirected to the home page. The guest was left unsure whether the order was cancelled, and the hub received no cancellation notification. Root cause: two missing pieces for guest (token-based) authorization: 1. The `:cancel` ability in Ability#add_shopping_abilities only checked `order.user == user`, ignoring the guest token. The `:read` and `:update` abilities already support `order.token && token == order.token` as a fallback — `:cancel` now does the same. 2. The `cancel` action called `authorize! :cancel, @order` without passing `session[:access_token]`, so even with the corrected ability the token was never evaluated. Fixes #13817 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
204 lines
6.3 KiB
Ruby
204 lines
6.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Spree
|
|
class OrdersController < ::BaseController
|
|
include OrderCyclesHelper
|
|
include Rails.application.routes.url_helpers
|
|
include CablecarResponses
|
|
include WhiteLabel
|
|
|
|
layout 'darkswarm'
|
|
|
|
rescue_from ActiveRecord::RecordNotFound, with: :render404
|
|
helper 'spree/orders'
|
|
|
|
respond_to :html, :json
|
|
|
|
before_action :check_authorization
|
|
before_action :set_order_from_params, only: :show
|
|
before_action :set_current_order, only: [:edit, :update]
|
|
before_action :filter_order_params, only: :update
|
|
|
|
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
|
|
|
|
before_action only: [:show, :edit] do
|
|
hide_ofn_navigation(@order.distributor)
|
|
end
|
|
|
|
def show
|
|
credit_payment_method = @order.distributor.payment_methods.customer_credit
|
|
credit_payment = @order.payments.find_by(payment_method: credit_payment_method)
|
|
@paid_with_credit = credit_payment&.amount
|
|
|
|
@payment_total = @order.payment_total - @paid_with_credit.to_f
|
|
end
|
|
|
|
def empty
|
|
if @order = current_order
|
|
@order.empty!
|
|
end
|
|
|
|
redirect_to main_app.cart_path
|
|
end
|
|
|
|
# Patching to redirect to shop if order is empty
|
|
def edit
|
|
@insufficient_stock_lines = @order.insufficient_stock_lines
|
|
@unavailable_order_variants = OrderCycles::DistributedVariantsService.
|
|
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.now[: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_updated)
|
|
redirect_to(main_app.root_path) && return
|
|
end
|
|
|
|
# This action is called either from the cart page when the order is not yet complete, or from
|
|
# the edit order page (frontoffice) if the hub allows users to update completed orders.
|
|
if @order.contents.update_cart(order_params)
|
|
@order.recreate_all_fees! # Enterprise fees on line items and on the order itself
|
|
|
|
# Re apply the voucher
|
|
OrderManagement::Order::Updater.new(@order).update_voucher
|
|
|
|
if @order.complete?
|
|
@order.update_payment_fees!
|
|
@order.create_tax_charge!
|
|
end
|
|
|
|
AmendBackorderJob.perform_later(@order) if @order.completed?
|
|
|
|
respond_with(@order) do |format|
|
|
format.html do
|
|
if params.key?(:checkout)
|
|
@order.next_transition.run_callbacks if @order.cart?
|
|
redirect_to main_app.checkout_step_path("address")
|
|
elsif @order.complete?
|
|
redirect_to main_app.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.reload
|
|
respond_with(@order)
|
|
end
|
|
end
|
|
|
|
def cancel
|
|
@order = Spree::Order.find_by!(number: params[:id])
|
|
authorize! :cancel, @order, session[:access_token]
|
|
|
|
if Orders::CustomerCancellationService.new(@order).call
|
|
flash[:success] = I18n.t(:orders_your_order_has_been_cancelled)
|
|
else
|
|
flash[:error] = I18n.t(:orders_could_not_cancel)
|
|
end
|
|
render status: :found,
|
|
cable_ready: cable_car.redirect_to(url: request.referer || main_app.order_path(@order))
|
|
end
|
|
|
|
private
|
|
|
|
def set_order_from_params
|
|
@order = Spree::Order.find_by!(number: params[:id])
|
|
end
|
|
|
|
def set_current_order
|
|
@order = current_order(true)
|
|
end
|
|
|
|
def check_authorization
|
|
session[:access_token] ||= params[:order_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
|
|
|
|
def filter_order_params
|
|
return unless params[:order] && params[:order][:line_items_attributes]
|
|
|
|
params[:order][:line_items_attributes] =
|
|
remove_missing_line_items(params[:order][:line_items_attributes])
|
|
end
|
|
|
|
def remove_missing_line_items(attrs)
|
|
attrs.select do |_i, line_item|
|
|
Spree::LineItem.find_by(id: line_item[:id])
|
|
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[:order_token] || spree_current_user
|
|
|
|
store_location_for :spree_user, request.original_fullpath
|
|
|
|
flash[:error] = I18n.t("spree.orders.edit.login_to_view_order")
|
|
redirect_to main_app.root_path(anchor: "/login", after_login: request.original_fullpath)
|
|
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&.changes_allowed? && can?(:update, order)
|
|
|
|
order
|
|
end
|
|
|
|
def check_at_least_one_line_item
|
|
return unless order_to_update&.complete?
|
|
|
|
items = params[:order][:line_items_attributes]
|
|
&.select{ |_k, attrs| attrs["quantity"].to_i > 0 }
|
|
|
|
return unless items.empty?
|
|
|
|
flash[:error] = I18n.t(:orders_cannot_remove_the_final_item)
|
|
redirect_to main_app.order_path(order_to_update)
|
|
end
|
|
|
|
def order_params
|
|
params.require(:order).permit(
|
|
:distributor_id, :order_cycle_id,
|
|
line_items_attributes: [:id, :quantity]
|
|
)
|
|
end
|
|
end
|
|
end
|