mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
This check was implemented based on 'allowed' shipping methods, but we need to revert that logic. So for now, we can check all 'available' shipping methods. This could potentially result in the same query being run twice, because load_shipping_methods also loads it. I opted to keep things simple and not try to optimise here.
255 lines
7.1 KiB
Ruby
255 lines
7.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'open_food_network/address_finder'
|
|
|
|
class SplitCheckoutController < ::BaseController
|
|
layout 'darkswarm'
|
|
|
|
include OrderStockCheck
|
|
include Spree::BaseHelper
|
|
include CheckoutCallbacks
|
|
include OrderCompletion
|
|
include CablecarResponses
|
|
|
|
helper 'terms_and_conditions'
|
|
helper 'checkout'
|
|
helper 'spree/orders'
|
|
helper EnterprisesHelper
|
|
helper OrderHelper
|
|
|
|
before_action :set_checkout_redirect
|
|
|
|
def edit
|
|
redirect_to_step_based_on_order unless params[:step]
|
|
check_step if params[:step]
|
|
recalculate_tax if params[:step] == "summary"
|
|
|
|
flash_error_when_no_shipping_method_available if available_shipping_methods.none?
|
|
end
|
|
|
|
def update
|
|
if confirm_order || update_order
|
|
return if performed?
|
|
|
|
check_payments_adjustments
|
|
clear_invalid_payments
|
|
advance_order_state
|
|
redirect_to_step
|
|
else
|
|
render_error
|
|
end
|
|
rescue Spree::Core::GatewayError => e
|
|
flash[:error] = I18n.t(:spree_gateway_error_flash_for_checkout, error: e.message)
|
|
@order.update_column(:state, "payment")
|
|
render operations: cable_car.redirect_to(url: checkout_step_path(:payment))
|
|
end
|
|
|
|
private
|
|
|
|
def render_error
|
|
flash.now[:error] ||= I18n.t(
|
|
'split_checkout.errors.saving_failed',
|
|
messages: order_error_messages
|
|
)
|
|
|
|
render status: :unprocessable_entity, operations: cable_car.
|
|
replace("#checkout", partial("split_checkout/checkout")).
|
|
replace("#flashes", partial("shared/flashes", locals: { flashes: flash }))
|
|
end
|
|
|
|
def order_error_messages
|
|
# Remove ship_address.* errors if no shipping method is not selected
|
|
remove_ship_address_errors if no_ship_address_needed?
|
|
|
|
# Reorder errors to make sure the most important ones are shown first
|
|
# and finally, return the error messages to sentence
|
|
reorder_errors.map(&:full_message).to_sentence
|
|
end
|
|
|
|
def no_ship_address_needed?
|
|
@order.errors[:shipping_method].present? || params[:ship_address_same_as_billing] == "1"
|
|
end
|
|
|
|
def remove_ship_address_errors
|
|
@order.errors.delete("ship_address.firstname")
|
|
@order.errors.delete("ship_address.address1")
|
|
@order.errors.delete("ship_address.city")
|
|
@order.errors.delete("ship_address.phone")
|
|
@order.errors.delete("ship_address.lastname")
|
|
@order.errors.delete("ship_address.zipcode")
|
|
end
|
|
|
|
def reorder_errors
|
|
@order.errors.sort_by do |e|
|
|
case e.attribute
|
|
when /email/i then 0
|
|
when /phone/i then 1
|
|
when /bill_address/i then 2 + bill_address_error_order(e)
|
|
else 20
|
|
end
|
|
end
|
|
end
|
|
|
|
def bill_address_error_order(error)
|
|
case error.attribute
|
|
when /firstname/i then 0
|
|
when /lastname/i then 1
|
|
when /address1/i then 2
|
|
when /city/i then 3
|
|
when /zipcode/i then 4
|
|
else 5
|
|
end
|
|
end
|
|
|
|
def flash_error_when_no_shipping_method_available
|
|
flash[:error] = I18n.t('split_checkout.errors.no_shipping_methods_available')
|
|
end
|
|
|
|
def check_payments_adjustments
|
|
@order.payments.each(&:ensure_correct_adjustment)
|
|
end
|
|
|
|
def clear_invalid_payments
|
|
@order.payments.with_state(:invalid).delete_all
|
|
end
|
|
|
|
def confirm_order
|
|
return unless summary_step? && @order.confirmation?
|
|
return unless validate_summary! && @order.errors.empty?
|
|
|
|
@order.customer.touch :terms_and_conditions_accepted_at
|
|
|
|
return true if redirect_to_payment_gateway
|
|
|
|
@order.process_payments!
|
|
@order.confirm!
|
|
order_completion_reset @order
|
|
end
|
|
|
|
def redirect_to_payment_gateway
|
|
return unless selected_payment_method&.external_gateway?
|
|
return unless (redirect_url = selected_payment_method.external_payment_url(order: @order))
|
|
|
|
render operations: cable_car.redirect_to(url: redirect_url)
|
|
true
|
|
end
|
|
|
|
def selected_payment_method
|
|
@selected_payment_method ||= Checkout::PaymentMethodFetcher.new(@order).call
|
|
end
|
|
|
|
def update_order
|
|
return if params[:confirm_order] || @order.errors.any?
|
|
|
|
# If we have "pick up" shipping method (require_ship_address is set to false), use the
|
|
# distributor address as shipping address
|
|
use_shipping_address_from_distributor if shipping_method_ship_address_not_required?
|
|
|
|
@order.select_shipping_method(params[:shipping_method_id])
|
|
@order.update(order_params)
|
|
@order.updater.update_totals_and_states
|
|
|
|
validate_current_step!
|
|
|
|
@order.errors.empty?
|
|
end
|
|
|
|
def use_shipping_address_from_distributor
|
|
@order.ship_address = @order.address_from_distributor
|
|
|
|
# Add the missing data
|
|
bill_address = params[:order][:bill_address_attributes]
|
|
@order.ship_address.firstname = bill_address[:firstname]
|
|
@order.ship_address.lastname = bill_address[:lastname]
|
|
@order.ship_address.phone = bill_address[:phone]
|
|
|
|
# Remove shipping address from parameter so we don't override the address we just set
|
|
params[:order].delete(:ship_address_attributes)
|
|
end
|
|
|
|
def shipping_method_ship_address_not_required?
|
|
selected_shipping_method = available_shipping_methods&.select do |sm|
|
|
sm.id.to_s == params[:shipping_method_id]
|
|
end
|
|
|
|
return false if selected_shipping_method.empty?
|
|
|
|
selected_shipping_method.first.require_ship_address == false
|
|
end
|
|
|
|
def summary_step?
|
|
params[:step] == "summary"
|
|
end
|
|
|
|
def advance_order_state
|
|
return if @order.complete?
|
|
|
|
OrderWorkflow.new(@order).advance_checkout(raw_params.slice(:shipping_method_id))
|
|
end
|
|
|
|
def validate_current_step!
|
|
step = ([params[:step]] & ["details", "payment", "summary"]).first
|
|
send("validate_#{step}!")
|
|
end
|
|
|
|
def validate_details!
|
|
return true if params[:shipping_method_id].present?
|
|
|
|
@order.errors.add :shipping_method, I18n.t('split_checkout.errors.select_a_shipping_method')
|
|
end
|
|
|
|
def validate_payment!
|
|
return true if params.dig(:order, :payments_attributes, 0, :payment_method_id).present?
|
|
|
|
@order.errors.add :payment_method, I18n.t('split_checkout.errors.select_a_payment_method')
|
|
end
|
|
|
|
def validate_summary!
|
|
return true if params[:accept_terms]
|
|
return true unless TermsOfService.required?(@order.distributor)
|
|
|
|
@order.errors.add(:terms_and_conditions, t("split_checkout.errors.terms_not_accepted"))
|
|
end
|
|
|
|
def order_params
|
|
@order_params ||= Checkout::Params.new(@order, params, spree_current_user).call
|
|
end
|
|
|
|
def redirect_to_step_based_on_order
|
|
case @order.state
|
|
when "cart", "address", "delivery"
|
|
redirect_to checkout_step_path(:details)
|
|
when "payment"
|
|
redirect_to checkout_step_path(:payment)
|
|
when "confirmation"
|
|
redirect_to checkout_step_path(:summary)
|
|
else
|
|
redirect_to order_path(@order, order_token: @order.token)
|
|
end
|
|
end
|
|
|
|
def redirect_to_step
|
|
case params[:step]
|
|
when "details"
|
|
return redirect_to checkout_step_path(:payment)
|
|
when "payment"
|
|
return redirect_to checkout_step_path(:summary)
|
|
end
|
|
redirect_to_step_based_on_order
|
|
end
|
|
|
|
def check_step
|
|
case @order.state
|
|
when "cart", "address", "delivery"
|
|
redirect_to checkout_step_path(:details) unless params[:step] == "details"
|
|
when "payment"
|
|
redirect_to checkout_step_path(:payment) if params[:step] == "summary"
|
|
end
|
|
end
|
|
|
|
def recalculate_tax
|
|
@order.create_tax_charge!
|
|
@order.update_order!
|
|
end
|
|
end
|