Files
openfoodnetwork/app/controllers/spree/admin/orders_controller.rb
2024-10-30 23:05:55 +01:00

351 lines
11 KiB
Ruby

# frozen_string_literal: true
require 'open_food_network/spree_api_key_loader'
module Spree
module Admin
class OrdersController < Spree::Admin::BaseController
include OpenFoodNetwork::SpreeApiKeyLoader
helper CheckoutHelper
before_action :load_order,
only: [:edit, :update, :fire, :resend, :invoice, :print, :capture, :ship]
before_action :load_distribution_choices, only: [:new, :create, :edit, :update]
before_action :require_distributor_abn, only: :invoice
def index
orders = SearchOrders.new(search_params, spree_current_user).orders
@pagy, @orders = pagy(orders, items: params[:per_page] || 15)
@stored_query = search_params.to_query
end
def new
@order = Spree::Order.new
end
def edit
@order.shipments.map(&:refresh_rates)
end
def create
@order = Spree::Order.new(order_params.merge(created_by: spree_current_user))
if @order.save(context: :require_distribution)
redirect_to spree.admin_order_customer_path(@order)
else
render :new
end
end
def update
on_update
order_updated = order_params.present? && @order.update(order_params)
unless order_updated && line_items_present?
flash[:error] = @order.errors.full_messages.join(', ') if @order.errors.present?
return redirect_to spree.edit_admin_order_path(@order)
end
::Orders::WorkflowService.new(@order).advance_to_payment
if @order.complete?
redirect_to spree.edit_admin_order_path(@order)
else
# Jump to next step if order is not complete
redirect_to spree.admin_order_payments_path(@order)
end
end
def bulk_management
load_spree_api_key
end
def fire
event = params[:e]
@order.send_cancellation_email = params[:send_cancellation_email] != "false"
@order.restock_items = params.fetch(:restock_items, "true") == "true"
if @order.public_send(event.to_s)
flash[:success] = Spree.t(:order_updated)
else
flash[:error] = Spree.t(:cannot_perform_operation)
end
rescue Spree::Core::GatewayError => e
flash[:error] = e.message.to_s
ensure
redirect_back fallback_location: spree.admin_dashboard_path
end
def resend
Spree::OrderMailer.confirm_email_for_customer(@order.id, true).deliver_later
flash[:success] = t('admin.orders.order_email_resent')
redirect_back(fallback_location: spree.admin_dashboard_path)
end
def invoice
Spree::OrderMailer.invoice_email(@order.id,
current_user_id: spree_current_user.id ).deliver_later
flash[:success] = t('admin.orders.invoice_email_sent')
redirect_to spree.edit_admin_order_path(@order)
end
def print
if OpenFoodNetwork::FeatureToggle.enabled?(:invoices, spree_current_user)
@order = if params[:invoice_id].present?
@order.invoices.find(params[:invoice_id]).presenter
else
::Orders::GenerateInvoiceService.new(@order).generate_or_update_latest_invoice
@order.invoices.first.presenter
end
end
render_with_wicked_pdf InvoiceRenderer.new.args(@order, spree_current_user)
end
def capture
payment_capture = ::Orders::CaptureService.new(@order)
unless (@saved = payment_capture.call)
message = payment_capture.gateway_error || I18n.t(:payment_processing_failed)
end
respond_to do |format|
format.html do
flash[:error] = message unless @saved
redirect_to admin_orders_path
end
format.turbo_stream do
flash.now[:error] = message unless @saved
render 'spree/admin/orders/capture'
end
end
end
def ship
@order.send_shipment_email = false unless params[:send_shipment_email]
if @order.ship
if params[:current_page] != 'index'
return redirect_back fallback_location: admin_orders_path
end
@shipped = true
end
respond_to do |format|
format.html do
flash[:error] = I18n.t("api.orders.failed_to_update") unless @shipped
redirect_back fallback_location: admin_orders_path
end
format.turbo_stream do
flash.now[:error] = I18n.t("api.orders.failed_to_update") unless @shipped
render 'spree/admin/orders/ship'
end
end
end
def bulk_invoice
visible_orders = bulk_load_orders
return if notify_if_abn_related_issue(visible_orders)
@file_id = "#{Time.zone.now.to_i}-#{SecureRandom.hex(2)}"
# Preserve order of bulk_ids.
# The ids are supplied in the sequence of the orders screen and may be
# sorted, for example by last name of the customer.
visible_order_ids = params[:bulk_ids].map(&:to_i) & visible_orders.pluck(:id)
BulkInvoiceJob.perform_later(
visible_order_ids,
"tmp/invoices/#{@file_id}.pdf",
channel: SessionChannel.for_request(request),
current_user_id: spree_current_user.id
)
respond_to do |format|
format.html { redirect_to admin_orders_path }
format.turbo_stream { render 'spree/admin/orders/bulk_invoice' }
end
end
def cancel_orders
@cancelled_orders = ::Orders::BulkCancelService.new(params, spree_current_user).call
respond_to do |format|
format.html { redirect_to admin_orders_path }
format.turbo_stream { render 'spree/admin/orders/cancel_orders' }
end
end
def resend_confirmation_emails
editable_orders.where(id: params[:bulk_ids]).find_each do |order|
next unless can? :resend, order
Spree::OrderMailer.confirm_email_for_customer(order.id, true).deliver_later
end
message = t("admin.resend_confirmation_emails_feedback", count: params[:bulk_ids].count)
respond_to do |format|
format.html do
flash[:success] = message
redirect_to admin_orders_path
end
format.turbo_stream do
flash.now[:success] = message
render 'spree/admin/orders/resend_confirmation_emails'
end
end
end
def send_invoices
count = 0
editable_orders.invoiceable.where(id: params[:bulk_ids]).find_each do |o|
next unless o.distributor.can_invoice?
Spree::OrderMailer.invoice_email(o.id,
current_user_id: current_spree_user.id).deliver_later
count += 1
end
respond_to do |format|
format.html do
flash[:success] = t("admin.send_invoice_feedback", count:)
redirect_to admin_orders_path
end
format.turbo_stream do
flash.now[:success] = t("admin.send_invoice_feedback", count:)
render 'spree/admin/orders/send_invoices'
end
end
end
private
def line_items_present?
return true if @order.line_items.any?
@order.errors.add(:line_items, Spree.t('errors.messages.blank'))
false
end
def search_params
default_filters.deep_merge(
params.permit(:page, :per_page, :shipping_method_id, q: {})
).to_h.with_indifferent_access
end
def default_filters
{ q: { completed_at_not_null: 1, s: "completed_at desc" } }
end
def on_update
@order.recreate_all_fees!
return if @order.cart?
@order.create_tax_charge!
@order.update_order!
end
def order_params
return params[:order] if params[:order].blank?
params.require(:order).permit(:distributor_id, :order_cycle_id)
end
def load_order
if params[:id]
@order = Order.includes(:adjustments, :shipments, line_items: :adjustments).
find_by!(number: params[:id])
end
authorize! action, @order
end
def set_param_for_controller
redirect_back fallback_location: admin_orders_path
params[:id] = @order.number
end
def bulk_load_orders
editable_orders.invoiceable.where(id: params[:bulk_ids])
end
def notify_if_abn_related_issue(orders)
return false unless abn_required?
distributors = distributors_without_abn(orders)
return false if distributors.empty?
render_business_number_required_error(distributors)
true
end
def abn_required?
Spree::Config.enterprise_number_required_on_invoices?
end
def distributors_without_abn(orders)
abn = if OpenFoodNetwork::FeatureToggle.enabled?(:invoices)
[nil, ""]
else
[nil]
end
Enterprise.where(
id: orders.select(:distributor_id),
abn:,
)
end
def render_business_number_required_error(distributors)
distributor_names = distributors.pluck(:name)
respond_to do |format|
format.html do
flash[:error] = I18n.t(:must_have_valid_business_number,
enterprise_name: distributor_names.join(", "))
redirect_to admin_orders_path
end
format.turbo_stream do
flash[:error] = I18n.t(:must_have_valid_business_number,
enterprise_name: distributor_names.join(", "))
render turbo_stream:
turbo_stream.append(
"flashes",
partial: 'admin/shared/flashes', locals: { flashes: flash }
)
end
end
end
def editable_orders
Permissions::Order.new(current_spree_user).editable_orders
end
def model_class
Spree::Order
end
def require_distributor_abn
return if @order.distributor.can_invoice?
flash[:error] = t(:must_have_valid_business_number,
enterprise_name: @order.distributor.name)
respond_with(@order) { |format|
format.html { redirect_to spree.edit_admin_order_path(@order) }
}
end
def load_distribution_choices
@shops = Enterprise.is_distributor.managed_by(spree_current_user).by_name
ocs = OrderCycle.includes(:suppliers, :distributors).managed_by(spree_current_user)
@order_cycles = ocs.soonest_closing +
ocs.soonest_opening +
ocs.closed +
ocs.undated
end
end
end
end