Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow producer to edit their products on hubs' orders #13113

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
5 changes: 4 additions & 1 deletion app/controllers/spree/admin/variants_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ def update
end

def search
scoper = OpenFoodNetwork::ScopeVariantsForSearch.new(variant_search_params)
scoper = OpenFoodNetwork::ScopeVariantsForSearch.new(
variant_search_params,
spree_current_user
)
@variants = scoper.search
render json: @variants, each_serializer: ::Api::Admin::VariantSerializer
end
Expand Down
22 changes: 22 additions & 0 deletions app/helpers/spree/admin/orders_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,28 @@ def quantity_field_tag(manifest_item)
end
number_field_tag :quantity, manifest_item.quantity, html_options
end

def prepare_shipment_manifest(shipment)
manifest = shipment.manifest

if distributor_allows_order_editing?(shipment.order)
supplier_ids = spree_current_user.enterprises.ids
manifest.select! { |mi| supplier_ids.include?(mi.variant.supplier_id) }
end

manifest
end

def distributor_allows_order_editing?(order)
order.distributor&.enable_producers_to_edit_orders &&
spree_current_user.can_manage_line_items_in_orders_only?
end

def display_value_for_producer(order, value)
return value unless distributor_allows_order_editing?(order)

order.distributor&.show_customer_names_to_suppliers ? value : t("admin.reports.hidden")
end
end
end
end
4 changes: 4 additions & 0 deletions app/models/enterprise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,10 @@ def is_hub
sells == 'any'
end

def is_producer
is_primary_producer && sells == 'none'
end

Comment on lines +378 to +381
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

specs ?

# Simplify enterprise categories for frontend logic and icons, and maybe other things.
def category
# Make this crazy logic human readable so we can argue about it sanely.
Expand Down
34 changes: 32 additions & 2 deletions app/models/spree/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ def initialize(user)
add_group_management_abilities user if can_manage_groups? user
add_product_management_abilities user if can_manage_products? user
add_order_cycle_management_abilities user if can_manage_order_cycles? user
add_order_management_abilities user if can_manage_orders? user
if can_manage_orders? user
add_order_management_abilities user
elsif can_manage_line_items_in_orders? user
add_manage_line_items_abilities user
end
add_relationship_management_abilities user if can_manage_relationships? user
end

Expand Down Expand Up @@ -81,7 +85,13 @@ def can_manage_order_cycles?(user)

# Users can manage orders if they have a sells own/any enterprise.
def can_manage_orders?(user)
( user.enterprises.map(&:sells) & %w(own any) ).any?
user.can_manage_orders?
end

# Users can manage line items in orders if they have producer enterprise and
# any of order distributors allow them to edit their orders.
def can_manage_line_items_in_orders?(user)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the same user method names here for the sake of writing ability specs to better show the assigned abilities

user.can_manage_line_items_in_orders?
Comment on lines +93 to +94
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

specs ?

end

def can_manage_relationships?(user)
Expand Down Expand Up @@ -343,6 +353,26 @@ def add_order_management_abilities(user)
end
end

def add_manage_line_items_abilities(user)
can_edit_order_lambda = lambda do |order|
return unless order.distributor&.enable_producers_to_edit_orders

order.variants.any? { |variant| user.enterprises.ids.include?(variant.supplier_id) }
end
Comment on lines +357 to +361
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that should be it's own method instead of having disguised as lambda here.


can [:admin, :read, :index, :edit, :update, :bulk_management], Spree::Order do |order|
can_edit_order_lambda.call(order)
end
can [:admin, :index, :create, :destroy, :update], Spree::LineItem do |item|
can_edit_order_lambda.call(item.order)
end
can [:index, :create, :add, :read, :edit, :update], Spree::Shipment do |shipment|
can_edit_order_lambda.call(shipment.order)
end

can [:visible], Enterprise
end

def add_relationship_management_abilities(user)
can [:admin, :index, :create], EnterpriseRelationship
can [:destroy], EnterpriseRelationship do |enterprise_relationship|
Expand Down
7 changes: 7 additions & 0 deletions app/models/spree/line_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ class LineItem < ApplicationRecord
where(spree_adjustments: { id: nil })
}

scope :editable_by_producers, ->(enterprises_ids) {
joins(:variant, order: :distributor).where(
distributor: { enable_producers_to_edit_orders: true },
spree_variants: { supplier_id: enterprises_ids }
)
}

def copy_price
return unless variant

Expand Down
8 changes: 8 additions & 0 deletions app/models/spree/order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@ def states
scope :invoiceable, -> { where(state: [:complete, :resumed]) }
scope :by_state, lambda { |state| where(state:) }
scope :not_state, lambda { |state| where.not(state:) }
scope :editable_by_producers, ->(enterprises) {
joins(
:distributor, line_items: :supplier
).where(
supplier: { id: enterprises.ids },
distributor: { enable_producers_to_edit_orders: true }
)
}

def initialize(*_args)
@checkout_processing = nil
Expand Down
19 changes: 19 additions & 0 deletions app/models/spree/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,25 @@ def affiliate_enterprises
Enterprise.joins(:connected_apps).merge(ConnectedApps::AffiliateSalesData.ready)
end

# Users can manage orders if they have a sells own/any enterprise. or is admin
def can_manage_orders?
@can_manage_orders ||= (enterprises.pluck(:sells).intersect?(%w(own any)) or admin?)
end

# Users can manage line items in orders if they have producer enterprise and
# any of order distributors allow them to edit their orders.
def can_manage_line_items_in_orders?
@can_manage_line_items_in_orders ||= begin
has_any_producer = enterprises.any?(&:is_producer)
has_producer_editable_orders = Spree::Order.editable_by_producers(enterprises).exists?
has_any_producer && has_producer_editable_orders
end
end
Comment on lines +150 to +163
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

specs ?


def can_manage_line_items_in_orders_only?
!can_manage_orders? && can_manage_line_items_in_orders?
end
Comment on lines +165 to +167
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spec ?


protected

def password_required?
Expand Down
28 changes: 24 additions & 4 deletions app/services/permissions/order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,16 @@ def visible_orders

# Any orders that the user can edit
def editable_orders
orders = Spree::Order.
where(managed_orders_where_values.
or(coordinated_orders_where_values))
orders = if @user.can_manage_line_items_in_orders_only?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't that be can_manage_line_items_in_orders?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be, however, the _only method would make sure it won't be able to manage the orders and can only manage line items in the orders.

Spree::Order.joins(:distributor).where(
id: produced_orders.select(:id),
distributor: { enable_producers_to_edit_orders: true }
)
else
Spree::Order.where(
managed_orders_where_values.or(coordinated_orders_where_values)
)
end

filtered_orders(orders)
end
Expand All @@ -36,7 +43,13 @@ def visible_line_items

# Any line items that I can edit
def editable_line_items
Spree::LineItem.where(order_id: editable_orders.select(:id))
if @user.can_manage_line_items_in_orders_only?
Spree::LineItem.editable_by_producers(
@permissions.managed_enterprises.select("enterprises.id")
)
else
Spree::LineItem.where(order_id: editable_orders.select(:id))
end
Comment on lines +46 to +52
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

specs ?

end

private
Expand Down Expand Up @@ -79,6 +92,13 @@ def coordinated_orders_where_values
reduce(:and)
end

def produced_orders
Spree::Order.with_line_items_variants_and_products_outer.
where(
spree_variants: { supplier_id: @permissions.managed_enterprises.select("enterprises.id") }
)
end

def produced_orders_where_values
Spree::Order.with_line_items_variants_and_products_outer.
where(
Expand Down
1 change: 1 addition & 0 deletions app/services/permitted_attributes/enterprise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def self.basic_permitted_attributes
:preferred_product_low_stock_display,
:hide_ofn_navigation, :white_label_logo, :white_label_logo_link,
:hide_groups_tab, :external_billing_id,
:enable_producers_to_edit_orders
]
end
end
Expand Down
12 changes: 12 additions & 0 deletions app/views/admin/enterprises/form/_shop_preferences.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,15 @@
.five.columns.omega
= radio_button :enterprise, :show_customer_names_to_suppliers, false
= label :enterprise_show_customer_names_to_suppliers, t('.customer_names_false'), value: :false

.row
.three.columns.alpha
%label= t('.producers_to_edit_orders')
%div{'ofn-with-tip' => t('.producers_to_edit_orders_tip')}
%a= t 'admin.whats_this'
.three.columns
= radio_button :enterprise, :enable_producers_to_edit_orders, true
= label :enterprise_enable_producers_to_edit_orders, t('.producers_edit_orders_true'), value: :true
.five.columns.omega
= radio_button :enterprise, :enable_producers_to_edit_orders, false
= label :enterprise_enable_producers_to_edit_orders, t('.producers_edit_orders_false'), value: :false
27 changes: 14 additions & 13 deletions app/views/spree/admin/orders/_form.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,24 @@
- if @order.shipments.any?
= render :partial => "spree/admin/orders/shipment", :collection => @order.shipments, :locals => { :order => @order }

- if @order.line_items.exists?
= render partial: "spree/admin/orders/note", locals: { order: @order }
- if spree_current_user.can_manage_orders?
- if @order.line_items.exists?
= render partial: "spree/admin/orders/note", locals: { order: @order }

= render :partial => "spree/admin/orders/_form/adjustments", :locals => { :adjustments => @order.line_item_adjustments, :title => t(".line_item_adjustments")}
= render :partial => "spree/admin/orders/_form/adjustments", :locals => { :adjustments => order_adjustments_for_display(@order), :title => t(".order_adjustments")}
= render :partial => "spree/admin/orders/_form/adjustments", :locals => { :adjustments => @order.line_item_adjustments, :title => t(".line_item_adjustments")}
= render :partial => "spree/admin/orders/_form/adjustments", :locals => { :adjustments => order_adjustments_for_display(@order), :title => t(".order_adjustments")}

- if @order.line_items.exists?
%fieldset#order-total.no-border-bottom.order-details-total
%legend{ align: 'center' }= t(".order_total")
%span.order-total= @order.display_total
- if @order.line_items.exists?
%fieldset#order-total.no-border-bottom.order-details-total
%legend{ align: 'center' }= t(".order_total")
%span.order-total= @order.display_total

= form_for @order, url: spree.admin_order_url(@order), method: :put do |f|
= render partial: 'spree/admin/orders/_form/distribution_fields'
= form_for @order, url: spree.admin_order_url(@order), method: :put do |f|
= render partial: 'spree/admin/orders/_form/distribution_fields'

.filter-actions.actions{"ng-show" => "distributionChosen()"}
= button t(:update_and_recalculate_fees), 'icon-refresh'
= link_to_with_icon 'button icon-arrow-left', t(:back), spree.admin_orders_url
.filter-actions.actions{"ng-show" => "distributionChosen()"}
= button t(:update_and_recalculate_fees), 'icon-refresh'
= link_to_with_icon 'button icon-arrow-left', t(:back), spree.admin_orders_url

= javascript_tag do
var order_number = '#{@order.number}';
Expand Down
4 changes: 2 additions & 2 deletions app/views/spree/admin/orders/_shipment.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@

- if shipment.fee_adjustment.present? && shipment.can_modify?
%td.actions
- if can? :update, shipment
- if can? :update, shipment.shipping_method
= link_to '', '', :class => 'edit-method icon_link icon-edit no-text with-tip', :data => { :action => 'edit' }, :title => Spree.t('edit')

%tr.edit-tracking.hidden.total
Expand All @@ -86,7 +86,7 @@
= Spree.t(:no_tracking_present)

%td.actions
- if can?(:update, shipment) && shipment.can_modify?
- if spree_current_user.can_manage_orders? && can?(:update, shipment) && shipment.can_modify?
= link_to '', '', :class => 'edit-tracking icon_link icon-edit no-text with-tip', :data => { :action => 'edit' }, :title => Spree.t('edit')
- if shipment.tracking.present?
= link_to '', '', :class => 'delete-tracking icon_link icon-trash no-text with-tip', :data => { 'shipment-number' => shipment.number, :action => 'remove' }, :title => Spree.t('delete')
2 changes: 1 addition & 1 deletion app/views/spree/admin/orders/_shipment_manifest.html.haml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- shipment.manifest.each do |item|
- prepare_shipment_manifest(shipment).each do |item|
- line_item = order.find_line_item_by_variant(item.variant)

- if line_item.present?
Expand Down
9 changes: 5 additions & 4 deletions app/views/spree/admin/orders/_table_row.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@
%span.state{ class: order.shipment_state.to_s}
= t('js.admin.orders.shipment_states.' + order.shipment_state.to_s)
%td
%a{ href: "mailto:#{order.email}", target: "_blank" }
= order.email
- email_value = display_value_for_producer(order, order.email)
%a{ href: "mailto:#{email_value}", target: "_blank" }
= email_value
%td
= order&.bill_address&.full_name_for_sorting
= display_value_for_producer(order, order.bill_address&.full_name_for_sorting)
%td.align-left
%span
= order.display_total
Expand All @@ -51,5 +52,5 @@
= 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 order.payment_required? && order.pending_payments.reject(&:requires_authorization?).any?
- if 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')}
6 changes: 4 additions & 2 deletions app/views/spree/admin/orders/edit.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
- content_for :page_actions do
- if can?(:fire, @order)
%li= event_links(@order)
= render partial: 'spree/admin/shared/order_links'
- if spree_current_user.can_manage_orders?
= render partial: 'spree/admin/shared/order_links'
- if can?(:admin, Spree::Order)
%li
%a.button.icon-arrow-left{icon: 'icon-arrow-left', href: admin_orders_path }
= t(:back_to_orders_list)

= render partial: "spree/admin/shared/order_page_title"
= render partial: "spree/admin/shared/order_tabs", locals: { current: 'Order Details' }
- if spree_current_user.can_manage_orders?
= render partial: "spree/admin/shared/order_tabs", locals: { current: 'Order Details' }

%div
= render partial: "spree/shared/error_messages", locals: { target: @order }
Expand Down
7 changes: 4 additions & 3 deletions app/views/spree/admin/orders/index.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

- content_for :minimal_js, true

- content_for :page_actions do
%li
= button_link_to t('.new_order'), spree.new_admin_order_url, icon: 'icon-plus', id: 'admin_new_order'
- if can?(:create, Spree::Order)
- content_for :page_actions do
%li
= button_link_to t('.new_order'), spree.new_admin_order_url, icon: 'icon-plus', id: 'admin_new_order'

= render partial: 'spree/admin/shared/order_sub_menu'

Expand Down
4 changes: 4 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1339,8 +1339,12 @@ en:
enable_subscriptions_true: "Enabled"
customer_names_in_reports: "Customer Names in Reports"
customer_names_tip: "Enable your suppliers to see your customers names in reports"
producers_to_edit_orders: "Ability for producers to edit orders"
producers_to_edit_orders_tip: "Enable your suppliers to see orders containing their products, and edit quantity and weight for their own products only."
customer_names_false: "Disabled"
customer_names_true: "Enabled"
producers_edit_orders_false: "Disabled"
producers_edit_orders_true: "Enabled"
shopfront_message: "Shopfront Message"
shopfront_message_placeholder: >
An optional message to welcome customers and explain how to shop with you. If text is entered here it will be displayed in a home tab when customers first arrive at your shopfront.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class AddEnableProducersToEditOrdersToEnterprises < ActiveRecord::Migration[7.0]
def change
add_column :enterprises, :enable_producers_to_edit_orders, :boolean, default: false, null: false
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.0].define(version: 2025_01_28_031518) do
ActiveRecord::Schema[7.0].define(version: 2025_02_02_121858) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
enable_extension "plpgsql"
Expand Down Expand Up @@ -230,6 +230,7 @@
t.text "white_label_logo_link"
t.boolean "hide_groups_tab", default: false
t.string "external_billing_id", limit: 128
t.boolean "enable_producers_to_edit_orders", default: false, null: false
t.index ["address_id"], name: "index_enterprises_on_address_id"
t.index ["is_primary_producer", "sells"], name: "index_enterprises_on_is_primary_producer_and_sells"
t.index ["name"], name: "index_enterprises_on_name", unique: true
Expand Down
Loading
Loading