# frozen_string_literal: true module Admin class OrderCyclesController < Admin::ResourceController class DateTimeChangeError < StandardError; end include ::OrderCyclesHelper include PaperTrailLogging prepend_before_action :set_order_cycle_id, only: [:incoming, :outgoing, :checkout_options] before_action :load_data_for_index, only: :index before_action :require_coordinator, only: :new before_action :remove_protected_attrs, only: [:update] before_action :require_order_cycle_set_params, only: [:bulk_update] around_action :protect_invalid_destroy, only: :destroy def index respond_to do |format| format.html format.json do render_as_json @collection, ams_prefix: params[:ams_prefix], current_user: spree_current_user, subscriptions_count: OrderManagement::Subscriptions::Count.new(@collection) end end end def show respond_to do |format| format.html do redirect_to edit_admin_order_cycle_path(@order_cycle) end format.json do render_as_json @order_cycle, current_user: spree_current_user end end end def new respond_to do |format| format.html format.json do render_as_json @order_cycle, current_user: spree_current_user end end end def create @order_cycle_form = OrderCycles::FormService.new(@order_cycle, order_cycle_params, spree_current_user) if @order_cycle_form.save flash[:success] = t('.success') render json: { success: true, edit_path: main_app.admin_order_cycle_incoming_path(@order_cycle) } else render json: { errors: @order_cycle.errors.full_messages }, status: :unprocessable_entity end end def set_order_cycle_id params[:id] = params[:order_cycle_id] end def update @order_cycle_form = set_order_cycle_form if @order_cycle_form.save update_nil_subscription_line_items_price_estimate(@order_cycle) respond_to do |format| flash[:success] = t('.success') if params[:reloading] == '1' format.html { redirect_to_after_update_path } format.json { render json: { success: true } } end elsif request.format.html? render :checkout_options elsif request.format.json? render json: { errors: @order_cycle.errors.full_messages }, status: :unprocessable_entity end rescue DateTimeChangeError render json: { trigger_action: params[:trigger_action] }, status: :unprocessable_entity end def bulk_update if order_cycle_set&.save bulk_update_nil_subscription_line_items_price_estimate render_as_json @order_cycles, ams_prefix: 'index', current_user: spree_current_user, subscriptions_count: OrderManagement::Subscriptions::Count.new(@collection) else order_cycle = order_cycle_set.collection.find{ |oc| oc.errors.present? } render json: { errors: order_cycle.errors.full_messages }, status: :unprocessable_entity end rescue DateTimeChangeError render json: { trigger_action: params[:trigger_action] }, status: :unprocessable_entity end def bulk_update_nil_subscription_line_items_price_estimate @collection.upcoming.each do |order_cycle| update_nil_subscription_line_items_price_estimate(order_cycle) end end def update_nil_subscription_line_items_price_estimate(order_cycle) order_cycle.schedules.each do |schedule| Subscription.where(schedule_id: schedule.id).find_each do |subscription| shop = Enterprise.managed_by(spree_current_user).find_by(id: subscription.shop_id) fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(shop, order_cycle) subscription.subscription_line_items.nil_price_estimate.each do |line_item| variant = OrderManagement::Subscriptions:: VariantsList.eligible_variants(shop).find_by(id: line_item.variant_id) # If the variant is not available in the shop, the price estimate will be nil next if variant.nil? price = variant.price + fee_calculator.indexed_fees_for(variant) line_item.update_column(:price_estimate, price) end end end end def clone @order_cycle = OrderCycle.find params[:id] @order_cycle.clone! redirect_to main_app.admin_order_cycles_path, flash: { success: t('.success', name: @order_cycle.name) } end # Send notifications to all producers who are part of the order cycle def notify_producers OrderCycleNotificationJob.perform_later params[:id].to_i redirect_to main_app.admin_order_cycles_path, flash: { success: t('.success') } end protected def collection return Enterprise.where("1=0") unless json_request? return order_cycles_from_set if params[:order_cycle_set].present? ocs = order_cycles ocs.undated | ocs.soonest_closing | ocs.soonest_opening | ocs.closed end def collection_actions [:index, :bulk_update] end private def order_cycles if params[:as] == "distributor" order_cycles_as_distributor elsif params[:as] == "producer" order_cycles_as_producer else order_cycles_as_both end end def order_cycles_as_distributor OrderCycle. preload(:schedules). ransack(raw_params[:q]). result. involving_managed_distributors_of(spree_current_user). order('updated_at DESC') end def order_cycles_as_producer OrderCycle. preload(:schedules). ransack(raw_params[:q]). result. involving_managed_producers_of(spree_current_user). order('updated_at DESC') end def order_cycles_as_both OrderCycle. preload(:schedules). ransack(raw_params[:q]). result. visible_by(spree_current_user) end def load_data_for_index return unless json_request? # Split ransack params into all those that currently exist and new ones # to limit returned ocs to recent or undated orders_close_at_gt = raw_params[:q]&.delete(:orders_close_at_gt) || 31.days.ago raw_params[:q] = { g: [raw_params.delete(:q) || {}, { m: 'or', orders_close_at_gt:, orders_close_at_null: true }] } @collection = collection end def redirect_to_after_update_path if params[:context] == "checkout_options" && params[:save] redirect_to main_app.admin_order_cycle_checkout_options_path(@order_cycle) elsif params[:context] == "checkout_options" && params[:save_and_back_to_list] redirect_to main_app.admin_order_cycles_path else redirect_back(fallback_location: root_path) end end def require_coordinator @order_cycle.coordinator = permitted_coordinating_enterprises_for(@order_cycle).find_by(id: params[:coordinator_id]) return if params[:coordinator_id] && @order_cycle.coordinator available_coordinators = permitted_coordinating_enterprises_for(@order_cycle) case available_coordinators.count when 0 flash[:error] = I18n.t(:order_cycles_no_permission_to_coordinate_error) redirect_to main_app.admin_order_cycles_path when 1 @order_cycle.coordinator = available_coordinators.first else if params[:coordinator_id] flash[:error] = I18n.t(:order_cycles_no_permission_to_create_error) end render :set_coordinator end end def protect_invalid_destroy # Can't delete if OC is linked to any orders or schedules if @order_cycle.schedules.any? redirect_to main_app.admin_order_cycles_url flash[:error] = I18n.t('admin.order_cycles.destroy_errors.schedule_present') else begin yield rescue ActiveRecord::InvalidForeignKey, ActiveRecord::DeleteRestrictionError redirect_to main_app.admin_order_cycles_url flash[:error] = I18n.t('admin.order_cycles.destroy_errors.orders_present') end end end def remove_protected_attrs return if order_cycle_params.blank? order_cycle_params.delete :coordinator_id return if Enterprise.managed_by(spree_current_user).include?(@order_cycle.coordinator) order_cycle_params.delete_if do |k, _v| [:name, :orders_open_at, :orders_close_at].include? k.to_sym end end def authorized_order_cycles managed_ids = managed_enterprises.map(&:id) (order_cycle_bulk_params[:collection_attributes] || []).keep_if do |_index, hash| order_cycle = OrderCycle.find(hash[:id]) managed_ids.include?(order_cycle&.coordinator_id) end end def order_cycles_from_set return if authorized_order_cycles.blank? OrderCycle.where(id: authorized_order_cycles.map{ |_k, v| v[:id] }) end def order_cycle_set @order_cycle_set ||= Sets::OrderCycleSet.new( @order_cycles, { **order_cycle_bulk_params, confirm_datetime_change: params[:confirm], error_class: DateTimeChangeError } ) end def require_order_cycle_set_params return if params[:order_cycle_set].present? render json: { errors: t('admin.order_cycles.bulk_update.no_data') }, status: :unprocessable_entity end def ams_prefix_whitelist [:basic, :index] end def order_cycle_params @order_cycle_params ||= PermittedAttributes::OrderCycle.new(params).call. to_h.with_indifferent_access end def order_cycle_bulk_params params.require(:order_cycle_set).permit( collection_attributes: [:id] + PermittedAttributes::OrderCycle.basic_attributes ).to_h.with_indifferent_access end def set_order_cycle_form OrderCycles::FormService.new( @order_cycle, order_cycle_params.merge( { confirm_datetime_change: params[:order_cycle][:confirm], error_class: DateTimeChangeError } ), spree_current_user ) end end end