Files
openfoodnetwork/app/controllers/admin/enterprises_controller.rb
Gaetan Craig-Riou b1d95cac7f Display filter by variant tag rule
We only support one of filter by inventory variants or filter by variants
at any given time, based on enabled feature. If both features inventory
and variant tag are enabled, variant tag takes precedence.
2025-11-03 14:25:05 +11:00

463 lines
15 KiB
Ruby

# frozen_string_literal: true
require 'open_food_network/referer_parser'
require 'open_food_network/permissions'
require 'open_food_network/order_cycle_permissions'
module Admin
class EnterprisesController < Admin::ResourceController
include GeocodeEnterpriseAddress
include CablecarResponses
include Pagy::Backend
# These need to run before #load_resource so that @object is initialised with sanitised values
prepend_before_action :override_owner, only: :create
prepend_before_action :override_sells, only: :create
before_action :load_countries, except: [:index, :register]
before_action :require_enterprise, only: [:edit, :update]
before_action :load_methods_and_fees, only: [:edit, :update]
before_action :load_groups, only: [:new, :edit, :update, :create]
before_action :load_taxons, only: [:new, :edit, :update, :create]
before_action :check_can_change_sells, only: :update
before_action :check_can_change_bulk_sells, only: :bulk_update
before_action :check_can_change_owner, only: :update
before_action :check_can_change_bulk_owner, only: :bulk_update
before_action :check_can_change_managers, only: :update
before_action :strip_new_properties, only: [:create, :update]
before_action :load_properties, only: [:edit, :update]
before_action :setup_property, only: [:edit]
after_action :geocode_address_if_use_geocoder, only: [:create, :update]
include OrderCyclesHelper
def index
load_enterprise_set_on_index
respond_to do |format|
format.html
format.json {
render_as_json @collection, ams_prefix: params[:ams_prefix],
spree_current_user:
}
end
end
def edit
@object = Enterprise.where(permalink: params[:id]).
includes(users: [:ship_address, :bill_address]).first
@object.build_custom_tab if @object.custom_tab.nil?
load_tag_rule_types
load_tag_rules
return unless params[:stimulus]
@enterprise.is_primary_producer = params[:is_primary_producer]
@enterprise.sells = params[:enterprise_sells]
render cable_ready: cable_car.morph("#side_menu", partial("admin/shared/side_menu"))
.morph("#permalink", partial("admin/enterprises/form/permalink"))
end
def welcome
render layout: "spree/layouts/bare_admin"
end
def update
tag_rules_attributes = params[object_name].delete :tag_rules_attributes
update_tag_rules(tag_rules_attributes) if tag_rules_attributes.present?
update_enterprise_notifications
update_vouchers
delete_custom_tab if params[:custom_tab] == 'false'
if @object.update(enterprise_params)
flash[:success] = flash_success_message
respond_with(@object) do |format|
format.html { redirect_to location_after_save }
format.js { render layout: false }
format.json {
render_as_json @object, ams_prefix: 'index', spree_current_user:
}
format.turbo_stream
end
else
load_tag_rule_types
respond_with(@object) do |format|
format.json {
render json: { errors: @object.errors.messages }, status: :unprocessable_entity
}
end
end
end
def register
register_params = params.permit(:sells)
if register_params[:sells] == 'unspecified'
flash[:error] = I18n.t(:enterprise_register_package_error)
return render :welcome, layout: "spree/layouts/bare_admin"
end
attributes = { sells: register_params[:sells], visible: "only_through_links" }
if @enterprise.update(attributes)
flash[:success] = I18n.t(:enterprise_register_success_notice, enterprise: @enterprise.name)
redirect_to spree.admin_dashboard_path
else
flash[:error] = I18n.t(:enterprise_register_error, enterprise: @enterprise.name)
render :welcome, layout: "spree/layouts/bare_admin"
end
end
def bulk_update
load_enterprise_set_with_params(bulk_params)
if @enterprise_set.save
flash[:success] = I18n.t(:enterprise_bulk_update_success_notice)
redirect_to main_app.admin_enterprises_path
else
touched_enterprises = @enterprise_set.collection.select(&:changed?)
@enterprise_set.collection.to_a.select! { |e| touched_enterprises.include? e }
flash[:error] = I18n.t(:enterprise_bulk_update_error)
render :index
end
end
def for_order_cycle
respond_to do |format|
format.json do
render(
json: @collection,
each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer,
order_cycle: @order_cycle,
spree_current_user:
)
end
end
end
def visible
respond_to do |format|
format.json do
render_as_json @collection, ams_prefix: params[:ams_prefix] || 'basic',
spree_current_user:
end
end
end
def new_tag_rule_group
load_tag_rule_types
@index = params[:index]
@customer_rule_index = params[:customer_rule_index].to_i
@group = { tags: [], rules: [] }
respond_to do |format|
format.turbo_stream
end
end
protected
def delete_custom_tab
@object.custom_tab.destroy if @object.custom_tab.present?
enterprise_params.delete(:custom_tab_attributes)
end
def build_resource
enterprise = super
enterprise.address ||= Spree::Address.new
enterprise.address.country ||= DefaultCountry.country
enterprise
end
# Overriding method on Spree's resource controller,
# so that resources are found using permalink
def find_resource
Enterprise.find_by(permalink: params[:id])
end
private
def flash_success_message
if attachment_removal?
I18n.t("admin.controllers.enterprises.#{attachment_removal_parameter}_success")
else
flash_message_for(@object, :successfully_updated)
end
end
def attachment_removal?
attachment_removal_parameter.present?
end
def attachment_removal_parameter
if enterprise_params.keys.one? && enterprise_params.keys.first.to_s.start_with?("remove_")
enterprise_params.keys.first
end
end
helper_method :attachment_removal_parameter
def load_enterprise_set_on_index
return unless spree_current_user.admin?
load_enterprise_set_with_params
end
def load_enterprise_set_with_params(params = {})
@pagy, @paginated_collection = pagy(@collection)
@enterprise_set = Sets::EnterpriseSet.new(@paginated_collection, params)
end
def load_countries
@countries = Spree::Country.order(:name)
end
def collection
case action
when :for_order_cycle
@order_cycle = OrderCycle.find_by(id: params[:order_cycle_id]) if params[:order_cycle_id]
coordinator = Enterprise.find_by(id: params[:coordinator_id]) if params[:coordinator_id]
@order_cycle ||= OrderCycle.new(coordinator:) if coordinator.present?
enterprises = OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, @order_cycle)
.visible_enterprises
if enterprises.present?
enterprises.includes(supplied_products: [:variants, :image])
end
when :index
if spree_current_user.admin?
OpenFoodNetwork::Permissions.new(spree_current_user).
editable_enterprises.
order('is_primary_producer ASC, name')
elsif json_request?
OpenFoodNetwork::Permissions.new(spree_current_user)
.editable_enterprises.ransack(params[:q]).result
else
Enterprise.where("1=0")
end
when :visible
OpenFoodNetwork::Permissions.new(spree_current_user).visible_enterprises
.includes(:shipping_methods, :payment_methods).ransack(params[:q]).result
else
OpenFoodNetwork::Permissions.new(spree_current_user).
editable_enterprises.
order('is_primary_producer ASC, name')
end
end
def collection_actions
[:index, :for_order_cycle, :visible, :bulk_update]
end
def require_enterprise
raise ActiveRecord::RecordNotFound if @enterprise.blank?
end
def load_methods_and_fees
enterprise_payment_methods = @enterprise.payment_methods.to_a
enterprise_shipping_methods = @enterprise.shipping_methods.to_a
# rubocop:disable Style/TernaryParentheses
@payment_methods = Spree::PaymentMethod.managed_by(spree_current_user).to_a.sort_by! do |pm|
[(enterprise_payment_methods.include? pm) ? 0 : 1, pm.name]
end
@shipping_methods = Spree::ShippingMethod.managed_by(spree_current_user).to_a.sort_by! do |sm|
[(enterprise_shipping_methods.include? sm) ? 0 : 1, sm.name]
end
# rubocop:enable Style/TernaryParentheses
@enterprise_fees = EnterpriseFee
.managed_by(spree_current_user)
.for_enterprise(@enterprise)
.order(:fee_type, :name)
.all
end
def load_groups
@groups = EnterpriseGroup.managed_by(spree_current_user) | @enterprise.groups
end
def load_taxons
@taxons = Spree::Taxon.order(:name)
end
def update_tag_rules(tag_rules_attributes)
# Due to the combination of trying to use nested attributes and type inheritance
# we cannot apply all attributes to tag rules in one hit because mass assignment
# methods that are specific to each class do not become available until after the
# record is persisted. This problem is compounded by the use of calculators.
@object.transaction do
tag_rules_attributes.select{ |_i, attrs| attrs[:type].present? }.each_value do |attrs|
rule = @object.tag_rules.find_by(id: attrs.delete(:id)) ||
attrs[:type].constantize.new(enterprise: @object)
rule.update(attrs.permit(PermittedAttributes::TagRules.attributes))
end
end
end
def update_enterprise_notifications
user_id = params[:receives_notifications].to_i
return unless user_id.positive? && @enterprise.user_ids.include?(user_id)
@enterprise.update_contact(user_id)
end
def update_vouchers
params_voucher_ids = params[:enterprise][:voucher_ids].to_a.map(&:to_i)
voucher_ids = @enterprise.vouchers.map(&:id)
deleted_voucher_ids = @enterprise.vouchers.only_deleted.map(&:id)
vouchers_to_destroy = voucher_ids - params_voucher_ids
Voucher.where(id: vouchers_to_destroy).destroy_all if vouchers_to_destroy.present?
vouchers_to_restore = deleted_voucher_ids.intersection(params_voucher_ids)
Voucher.restore(vouchers_to_restore) if vouchers_to_restore.present?
end
def create_calculator_for(rule, attrs)
return unless attrs[:calculator_type].present? && attrs[:calculator_attributes].present?
rule.update(calculator_type: attrs[:calculator_type])
attrs[:calculator_attributes].merge!( id: rule.calculator.id )
end
def check_can_change_bulk_sells
return if spree_current_user.admin?
params[:sets_enterprise_set][:collection_attributes].each_value do |enterprise_params|
unless spree_current_user == Enterprise.find_by(id: enterprise_params[:id]).owner
enterprise_params.delete :sells
end
end
end
def check_can_change_sells
return if spree_current_user.admin? || spree_current_user == @enterprise.owner
enterprise_params.delete :sells
end
def override_owner
enterprise_params[:owner_id] = spree_current_user.id unless spree_current_user.admin?
end
def override_sells
return if spree_current_user.admin?
has_hub = spree_current_user.owned_enterprises.is_hub.any?
new_enterprise_is_producer = Enterprise.new(enterprise_params).is_primary_producer
enterprise_params[:sells] = has_hub && !new_enterprise_is_producer ? 'any' : 'none'
end
def check_can_change_owner
return if ( spree_current_user == @enterprise.owner ) || spree_current_user.admin?
enterprise_params.delete :owner_id
end
def check_can_change_bulk_owner
return if spree_current_user.admin?
bulk_params[:collection_attributes].each_value do |enterprise_params|
enterprise_params.delete :owner_id
end
end
def check_can_change_managers
return if ( spree_current_user == @enterprise.owner ) || spree_current_user.admin?
enterprise_params.delete :user_ids
end
def strip_new_properties
unless spree_current_user.admin? || params.dig(:enterprise,
:producer_properties_attributes).nil?
names = Spree::Property.pluck(:name)
enterprise_params[:producer_properties_attributes].each do |key, property|
unless names.include? property[:property_name]
enterprise_params[:producer_properties_attributes].delete key
end
end
end
end
def load_properties
@properties = Spree::Property.pluck(:name)
end
def load_tag_rule_types
@tag_rule_types = [
[t(".form.tag_rules.show_hide_shipping"), "FilterShippingMethods"],
[t(".form.tag_rules.show_hide_payment"), "FilterPaymentMethods"],
[t(".form.tag_rules.show_hide_order_cycles"), "FilterOrderCycles"]
]
if helpers.feature?(:variant_tag, @object)
@tag_rule_types.prepend([t(".form.tag_rules.show_hide_variants_new"), "FilterVariants"])
elsif helpers.feature?(:inventory, @object)
@tag_rule_types.prepend([t(".form.tag_rules.show_hide_variants"), "FilterProducts"])
end
end
def load_tag_rules
if helpers.feature?(:variant_tag, @object)
@default_rules = @enterprise.tag_rules.exclude_inventory.select(&:is_default)
@rules = @enterprise.tag_rules.exclude_inventory.prioritised.reject(&:is_default)
elsif helpers.feature?(:inventory, @object)
@default_rules = @enterprise.tag_rules.exclude_variant.select(&:is_default)
@rules = @enterprise.tag_rules.exclude_variant.prioritised.reject(&:is_default)
else
@default_rules =
@enterprise.tag_rules.exclude_inventory.exclude_variant.select(&:is_default)
@rules =
@enterprise.tag_rules.exclude_inventory.exclude_variant.prioritised.reject(&:is_default)
end
end
def setup_property
@enterprise.producer_properties.build
end
# Overriding method on Spree's resource controller
def location_after_save
referer_path = OpenFoodNetwork::RefererParser.path(request.referer)
# rubocop:disable Style/RegexpLiteral
refered_from_producer_properties = referer_path =~ /\/producer_properties$/
# rubocop:enable Style/RegexpLiteral
if refered_from_producer_properties
main_app.admin_enterprise_producer_properties_path(@enterprise)
else
main_app.edit_admin_enterprise_path(@enterprise)
end
end
def ams_prefix_whitelist
[:index, :basic]
end
def enterprise_params
@enterprise_params ||= PermittedAttributes::Enterprise.new(params).call.
to_h.with_indifferent_access
end
def bulk_params
@bulk_params ||= params.require(:sets_enterprise_set).permit(
collection_attributes: PermittedAttributes::Enterprise.attributes
).to_h.with_indifferent_access
end
# Used in Admin::ResourceController#create
def permitted_resource_params
enterprise_params
end
end
end