From a37e08c2fd27eb534025289d871d4244261c64d0 Mon Sep 17 00:00:00 2001 From: Ahmed Ejaz Date: Sun, 15 Jun 2025 18:09:02 +0500 Subject: [PATCH] Refactor order management permissions for producers Introduces granular permissions control for producers editing orders: - Adds new :edit_as_producer_only permission for suppliers - Refactors ability checks to clearly separate producer vs admin/distributor access - Updates order views to properly restrict actions based on user role - Prevents admins from being restricted by producer-only edit mode --- app/helpers/spree/admin/orders_helper.rb | 3 +- app/models/spree/ability.rb | 84 ++++++++++++++++--- app/views/spree/admin/orders/_form.html.haml | 2 +- .../spree/admin/orders/_table_row.html.haml | 4 +- app/views/spree/admin/orders/edit.html.haml | 11 ++- 5 files changed, 84 insertions(+), 20 deletions(-) diff --git a/app/helpers/spree/admin/orders_helper.rb b/app/helpers/spree/admin/orders_helper.rb index e80b6d9d13..976f97b948 100644 --- a/app/helpers/spree/admin/orders_helper.rb +++ b/app/helpers/spree/admin/orders_helper.rb @@ -155,8 +155,7 @@ module Spree end def filter_by_supplier?(order) - order.distributor&.enable_producers_to_edit_orders && - spree_current_user.can_manage_line_items_in_orders_only? + can? :edit_as_producer_only, order end def display_value_for_producer(order, value) diff --git a/app/models/spree/ability.rb b/app/models/spree/ability.rb index de867108e5..1a72d66980 100644 --- a/app/models/spree/ability.rb +++ b/app/models/spree/ability.rb @@ -20,6 +20,10 @@ module Spree if user.try(:admin?) can :manage, :all + + # this action was needed for restrictions for distributors and suppliers + # however, admins don't need to be restricted, so, bypassing it for admins + cannot :edit_as_producer_only, Spree::Order else can [:index, :read], Country can :create, Order @@ -257,8 +261,13 @@ module Spree end def add_order_cycle_management_abilities(user) + can [:admin, :index], OrderCycle do |order_cycle| + OrderCycle.visible_by(user).include?(order_cycle) || + order_cycle.orders.any? { |order| can_edit_as_producer(order, user) } + end + can [ - :admin, :index, :read, :edit, :update, :incoming, :outgoing, :checkout_options + :read, :edit, :update, :incoming, :outgoing, :checkout_options ], OrderCycle do |order_cycle| OrderCycle.visible_by(user).include? order_cycle end @@ -274,8 +283,37 @@ module Spree end def add_order_management_abilities(user) - can [:index, :create], Spree::Order - can [:read, :update, :fire, :resend, :invoice, :print], Spree::Order do |order| + can [:manage_order_sections], Spree::Order do |order| + user.admin? || + order.distributor.nil? || + user.enterprises.include?(order.distributor) || + order.order_cycle&.coordinated_by?(user) + end + + can [:edit_as_producer_only], Spree::Order do |order| + cannot?(:manage_order_sections, order) && can_edit_as_producer(order, user) + end + + can [:index], Spree::Order do |order| + user.admin? || + user.enterprises.any?(&:is_distributor) || + can_edit_as_producer(order, user) + end + + can [:create], Spree::Order + + can [:read, :update], Spree::Order do |order| + # We allow editing orders with a nil distributor as this state occurs + # during the order creation process from the admin backend + order.distributor.nil? || + # Enterprise User can access orders that they are a distributor for + user.enterprises.include?(order.distributor) || + # Enterprise User can access orders that are placed inside a OC they coordinate + order.order_cycle&.coordinated_by?(user) || + can_edit_as_producer(order, user) + end + + can [:fire, :resend, :invoice, :print], Spree::Order do |order| # We allow editing orders with a nil distributor as this state occurs # during the order creation process from the admin backend order.distributor.nil? || @@ -284,22 +322,39 @@ module Spree # Enterprise User can access orders that are placed inside a OC they coordinate order.order_cycle&.coordinated_by?(user) end - can [:admin, :bulk_management, :managed, :distribution], Spree::Order do + + can [:admin, :bulk_management], Spree::Order do |order| + user.admin? || + user.enterprises.any?(&:is_distributor) || + can_edit_as_producer(order, user) + end + + can [:managed, :distribution], Spree::Order do user.admin? || user.enterprises.any?(&:is_distributor) end can [:admin, :index, :create, :show, :poll, :generate], :invoice can [:admin, :visible], Enterprise can [:admin, :index, :create, :update, :destroy], :line_item - can [:admin, :index, :create], Spree::LineItem + can [:admin, :index, :create], Spree::LineItem do |item| + user.admin? || + user.enterprises.any?(&:is_distributor) || + can_edit_as_producer(item.order, user) + end can [:destroy, :update], Spree::LineItem do |item| order = item.order user.admin? || user.enterprises.include?(order.distributor) || - order.order_cycle&.coordinated_by?(user) + order.order_cycle&.coordinated_by?(user) || + can_edit_as_producer(order, user) + end + + can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Shipment do |shipment| + user.admin? || + user.enterprises.any?(&:is_distributor) || + can_edit_as_producer(shipment.order, user) end can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Payment - can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Shipment can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Adjustment can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::ReturnAuthorization can [:destroy], Spree::Adjustment do |adjustment| @@ -350,26 +405,31 @@ module Spree can [:admin, :edit, :cancel, :resume], ProxyOrder do |proxy_order| user.enterprises.include?(proxy_order.subscription.shop) end + can [:visible], Enterprise end - def can_edit_order(order, user) + def can_edit_as_producer(order, user) return unless order.distributor&.enable_producers_to_edit_orders order.variants.any? { |variant| user.enterprises.ids.include?(variant.supplier_id) } end def add_manage_line_items_abilities(user) + can [:edit_as_producer_only], Spree::Order do |order| + can_edit_as_producer(order, user) + end + can [:admin, :read, :index, :edit, :update, :bulk_management], Spree::Order do |order| - can_edit_order(order, user) + can_edit_as_producer(order, user) end can [:admin, :index, :create, :destroy, :update], Spree::LineItem do |item| - can_edit_order(item.order, user) + can_edit_as_producer(item.order, user) end can [:index, :create, :add, :read, :edit, :update], Spree::Shipment do |shipment| - can_edit_order(shipment.order, user) + can_edit_as_producer(shipment.order, user) end can [:admin, :index], OrderCycle do |order_cycle| - can_edit_order(order_cycle.order, user) + can_edit_as_producer(order_cycle.order, user) end can [:visible], Enterprise end diff --git a/app/views/spree/admin/orders/_form.html.haml b/app/views/spree/admin/orders/_form.html.haml index 4f1866f330..f0f9a3434b 100644 --- a/app/views/spree/admin/orders/_form.html.haml +++ b/app/views/spree/admin/orders/_form.html.haml @@ -8,7 +8,7 @@ - if @order.shipments.any? = render :partial => "spree/admin/orders/shipment", :collection => @order.shipments, :locals => { :order => @order } - - if spree_current_user.can_manage_orders? + - if can?(:manage_order_sections, @order) - if @order.line_items.exists? = render partial: "spree/admin/orders/note", locals: { order: @order } diff --git a/app/views/spree/admin/orders/_table_row.html.haml b/app/views/spree/admin/orders/_table_row.html.haml index 6705719168..9ea6367a5f 100644 --- a/app/views/spree/admin/orders/_table_row.html.haml +++ b/app/views/spree/admin/orders/_table_row.html.haml @@ -47,10 +47,10 @@ - if local_assigns[:success] %i.success.icon-ok-sign{"data-controller": "ephemeral"} = render AdminTooltipComponent.new(text: t('spree.admin.orders.index.edit'), link_text: "", link: edit_admin_order_path(order), link_class: "icon_link with-tip icon-edit no-text") - - if spree_current_user.can_manage_orders? && order.ready_to_ship? + - if can?(:manage_order_sections, order) && order.ready_to_ship? %form = render ShipOrderComponent.new(order: order) = render partial: 'admin/shared/tooltip_button', locals: {button_class: "icon-road icon_link with-tip no-text", reflex_data_id: order.id.to_s, tooltip_text: t('spree.admin.orders.index.ship'), shipment: true} - - if can?(:update, Spree::Payment) && order.payment_required? && order.pending_payments.reject(&:requires_authorization?).any? + - if can?(:manage_order_sections, order) && can?(:update, Spree::Payment) && order.payment_required? && order.pending_payments.reject(&:requires_authorization?).any? = render partial: 'admin/shared/tooltip_button', locals: {button_class: "icon-capture icon_link no-text", button_reflex: "click->Admin::OrdersReflex#capture", reflex_data_id: order.id.to_s, tooltip_text: t('spree.admin.orders.index.capture')} diff --git a/app/views/spree/admin/orders/edit.html.haml b/app/views/spree/admin/orders/edit.html.haml index 8d9023581d..217eb6027c 100644 --- a/app/views/spree/admin/orders/edit.html.haml +++ b/app/views/spree/admin/orders/edit.html.haml @@ -6,7 +6,7 @@ - content_for :page_actions do - if can?(:fire, @order) %li= event_links(@order) - - if spree_current_user.can_manage_orders? + - if can?(:manage_order_sections, @order) = render partial: 'spree/admin/shared/order_links' - if can?(:admin, Spree::Order) %li @@ -14,7 +14,7 @@ = t(:back_to_orders_list) = render partial: "spree/admin/shared/order_page_title" -- if spree_current_user.can_manage_orders? +- if can?(:manage_order_sections, @order) = render partial: "spree/admin/shared/order_tabs", locals: { current: 'Order Details' } %div @@ -22,7 +22,12 @@ = admin_inject_shops(@shops, module: 'admin.orders') = admin_inject_order_cycles(@order_cycles) - %div{"ng-controller" => "orderCtrl", "ofn-distributor-id" => @order.distributor_id, "ofn-order-cycle-id" => @order.order_cycle_id} + %div{ + "ng-controller" => "orderCtrl", + "ofn-distributor-id" => @order.distributor_id, + "ofn-order-cycle-id" => @order.order_cycle_id, + "ofn-search-variants-as" => (can?(:manage_order_sections, @order) ? 'hub' : 'supplier'), + } = render :partial => 'add_product' if can?(:update, @order)