Merge pull request #13113 from chahmedejaz/task/13031-allow-producers-to-edit-orders

Allow producer to edit their products on hubs' orders
This commit is contained in:
Filipe
2025-04-21 11:28:36 +01:00
committed by GitHub
35 changed files with 959 additions and 315 deletions

View File

@@ -67,7 +67,8 @@ module Api
def serialized_orders(orders)
ActiveModel::ArraySerializer.new(
orders,
each_serializer: Api::Admin::OrderSerializer
each_serializer: Api::Admin::OrderSerializer,
current_user: current_api_user
)
end

View File

@@ -64,7 +64,10 @@ module Spree
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

View File

@@ -142,6 +142,32 @@ module Spree
end
number_field_tag :quantity, manifest_item.quantity, html_options
end
def prepare_shipment_manifest(shipment)
manifest = shipment.manifest
if filter_by_supplier?(shipment.order)
supplier_ids = spree_current_user.enterprises.ids
manifest.select! { |mi| supplier_ids.include?(mi.variant.supplier_id) }
end
manifest
end
def filter_by_supplier?(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 filter_by_supplier?(order)
if order.distributor&.show_customer_names_to_suppliers
value
else
t("admin.reports.hidden_field")
end
end
end
end
end

View File

@@ -381,6 +381,10 @@ class Enterprise < ApplicationRecord
sells == 'any'
end
def is_producer_only
is_primary_producer && sells == 'none'
end
# 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.

View File

@@ -47,7 +47,11 @@ module Spree
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
@@ -81,7 +85,13 @@ module Spree
# 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)
user.can_manage_line_items_in_orders?
end
def can_manage_relationships?(user)
@@ -343,6 +353,28 @@ module Spree
end
end
def can_edit_order(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 [:admin, :read, :index, :edit, :update, :bulk_management], Spree::Order do |order|
can_edit_order(order, user)
end
can [:admin, :index, :create, :destroy, :update], Spree::LineItem do |item|
can_edit_order(item.order, user)
end
can [:index, :create, :add, :read, :edit, :update], Spree::Shipment do |shipment|
can_edit_order(shipment.order, user)
end
can [:admin, :index], OrderCycle do |order_cycle|
can_edit_order(order_cycle.order, user)
end
can [:visible], Enterprise
end
def add_relationship_management_abilities(user)
can [:admin, :index, :create], EnterpriseRelationship
can [:destroy], EnterpriseRelationship do |enterprise_relationship|

View File

@@ -108,6 +108,13 @@ module Spree
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

View File

@@ -140,6 +140,15 @@ module Spree
end
}
scope :editable_by_producers, ->(enterprises) {
joins(
:distributor, line_items: :supplier
).where(
supplier: { id: enterprises },
distributor: { enable_producers_to_edit_orders: true }
)
}
scope :distributed_by_user, lambda { |user|
if user.admin?
where(nil)

View File

@@ -147,6 +147,25 @@ module Spree
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?
return @can_manage_line_items_in_orders if defined? @can_manage_line_items_in_orders
@can_manage_line_items_in_orders =
enterprises.any?(&:is_producer_only) &&
Spree::Order.editable_by_producers(enterprises).exists?
end
def can_manage_line_items_in_orders_only?
!can_manage_orders? && can_manage_line_items_in_orders?
end
protected
def password_required?

View File

@@ -15,8 +15,14 @@ module Api
has_one :distributor, serializer: Api::Admin::IdSerializer
has_one :order_cycle, serializer: Api::Admin::IdSerializer
def full_name_for_sorting
value = [last_name, first_name].compact_blank.join(", ")
display_value_for_producer(object, value)
end
def full_name
object.billing_address.nil? ? "" : ( object.billing_address.full_name || "" )
value = object.billing_address.nil? ? "" : ( object.billing_address.full_name || "" )
display_value_for_producer(object, value)
end
def first_name
@@ -65,11 +71,12 @@ module Api
end
def email
object.email || ""
display_value_for_producer(object, object.email || "")
end
def phone
object.billing_address.nil? ? "a" : ( object.billing_address.phone || "" )
value = object.billing_address.nil? ? "a" : ( object.billing_address.phone || "" )
display_value_for_producer(object, value)
end
def created_at
@@ -93,6 +100,19 @@ module Api
def spree_routes_helper
Spree::Core::Engine.routes.url_helpers
end
def display_value_for_producer(order, value)
filter_by_supplier =
order.distributor&.enable_producers_to_edit_orders &&
options[:current_user]&.can_manage_line_items_in_orders_only?
return value unless filter_by_supplier
if order.distributor&.show_customer_names_to_suppliers
value
else
I18n.t("admin.reports.hidden_field")
end
end
end
end
end

View File

@@ -23,9 +23,16 @@ module Permissions
# 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?
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
@@ -36,7 +43,13 @@ module Permissions
# 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
end
private
@@ -79,6 +92,13 @@ module Permissions
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(

View File

@@ -39,6 +39,7 @@ module PermittedAttributes
: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

View File

@@ -123,3 +123,15 @@
.five.columns.omega
= f.radio_button :show_customer_contacts_to_suppliers, false
= f.label :show_customer_contacts_to_suppliers, t('.customer_contacts_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

View File

@@ -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}';

View File

@@ -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
@@ -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')

View File

@@ -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?

View File

@@ -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
@@ -46,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 order.ready_to_ship?
- if spree_current_user.can_manage_orders? && 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 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')}

View File

@@ -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 }

View File

@@ -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) && spree_current_user.can_manage_orders?
- 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'

View File

@@ -1359,12 +1359,16 @@ 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"
customer_contacts_in_reports: "Customer contact details in reports"
customer_contacts_tip: "Enable your suppliers to see your customer email and phone numbers in reports"
customer_contacts_false: "Disabled"
customer_contacts_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.

View File

@@ -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

View File

@@ -230,6 +230,7 @@ ActiveRecord::Schema[7.0].define(version: 2025_03_04_234657) do
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.boolean "show_customer_contacts_to_suppliers", 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"

View File

@@ -9,8 +9,9 @@ require 'open_food_network/scope_variant_to_hub'
module OpenFoodNetwork
class ScopeVariantsForSearch
def initialize(params)
def initialize(params, spree_current_user)
@params = params
@spree_current_user = spree_current_user
end
def search
@@ -20,13 +21,14 @@ module OpenFoodNetwork
scope_to_schedule if params[:schedule_id]
scope_to_order_cycle if params[:order_cycle_id]
scope_to_distributor if params[:distributor_id]
scope_to_supplier if spree_current_user.can_manage_line_items_in_orders_only?
@variants
end
private
attr_reader :params
attr_reader :params, :spree_current_user
def search_params
{ product_name_cont: params[:q], sku_cont: params[:q], product_sku_cont: params[:q] }
@@ -96,5 +98,9 @@ module OpenFoodNetwork
# Filtering could be a problem on scoped variants.
variants.each { |v| scoper.scope(v) }
end
def scope_to_supplier
@variants = @variants.where(supplier_id: spree_current_user.enterprises.ids)
end
end
end

View File

@@ -120,11 +120,26 @@ RSpec.describe Admin::BulkLineItemsController, type: :controller do
context "producer enterprise" do
before do
allow(controller).to receive_messages spree_current_user: supplier.owner
get :index, as: :json
end
it "does not display line items for which my enterprise is a supplier" do
expect(response).to redirect_to unauthorized_path
context "with no distributor allows to edit orders" do
before { get :index, as: :json }
it "does not display line items for which my enterprise is a supplier" do
expect(response).to redirect_to unauthorized_path
end
end
context "with distributor allows to edit orders" do
before do
distributor1.update_columns(enable_producers_to_edit_orders: true)
get :index, as: :json
end
it "retrieves a list of line_items from the supplier" do
keys = json_response['line_items'].first.keys.map(&:to_sym)
expect(line_item_attributes.all?{ |attr| keys.include? attr }).to eq(true)
end
end
end

View File

@@ -84,11 +84,25 @@ module Api
context 'producer enterprise' do
before do
allow(controller).to receive(:spree_current_user) { supplier.owner }
get :index
end
it "does not display line items for which my enterprise is a supplier" do
assert_unauthorized!
context "with no distributor allows to edit orders" do
before { get :index }
it "does not display line items for which my enterprise is a supplier" do
assert_unauthorized!
end
end
context "with distributor allows to edit orders" do
before do
distributor.update_columns(enable_producers_to_edit_orders: true)
get :index
end
it "retrieves a list of orders which have my supplied products" do
returns_orders(json_response)
end
end
end

View File

@@ -22,7 +22,8 @@ RSpec.describe Spree::Admin::MailMethodsController do
owned_groups: nil)
allow(user).to receive_messages(enterprises: [create(:enterprise)],
admin?: true,
locale: nil)
locale: nil,
can_manage_orders?: true)
allow(controller).to receive_messages(spree_current_user: user)
expect {

View File

@@ -5,151 +5,199 @@ require 'spec_helper'
module Spree
module Admin
RSpec.describe VariantsController, type: :controller do
before { controller_login_as_admin }
context "log in as admin user" do
before { controller_login_as_admin }
describe "#index" do
describe "deleted variants" do
let(:product) { create(:product, name: 'Product A') }
let(:deleted_variant) do
deleted_variant = product.variants.create(
unit_value: "2", variant_unit: "weight", variant_unit_scale: 1, price: 1,
primary_taxon: create(:taxon), supplier: create(:supplier_enterprise)
)
deleted_variant.delete
deleted_variant
end
describe "#index" do
describe "deleted variants" do
let(:product) { create(:product, name: 'Product A') }
let(:deleted_variant) do
deleted_variant = product.variants.create(
unit_value: "2", variant_unit: "weight", variant_unit_scale: 1, price: 1,
primary_taxon: create(:taxon), supplier: create(:supplier_enterprise)
)
deleted_variant.delete
deleted_variant
end
it "lists only non-deleted variants with params[:deleted] == off" do
spree_get :index, product_id: product.id, deleted: "off"
expect(assigns(:variants)).to eq(product.variants)
end
it "lists only non-deleted variants with params[:deleted] == off" do
spree_get :index, product_id: product.id, deleted: "off"
expect(assigns(:variants)).to eq(product.variants)
end
it "lists only deleted variants with params[:deleted] == on" do
spree_get :index, product_id: product.id, deleted: "on"
expect(assigns(:variants)).to eq([deleted_variant])
it "lists only deleted variants with params[:deleted] == on" do
spree_get :index, product_id: product.id, deleted: "on"
expect(assigns(:variants)).to eq([deleted_variant])
end
end
end
end
describe "#update" do
let!(:variant) { create(:variant, display_name: "Tomatoes", sku: 123, supplier: producer) }
let(:producer) { create(:enterprise) }
it "updates the variant" do
expect {
spree_put(
:update,
id: variant.id,
product_id: variant.product.id,
variant: { display_name: "Better tomatoes", sku: 456 }
describe "#update" do
let!(:variant) {
create(
:variant,
display_name: "Tomatoes",
sku: 123,
supplier: producer
)
variant.reload
}.to change { variant.display_name }.to("Better tomatoes")
.and change { variant.sku }.to(456.to_s)
end
}
let(:producer) { create(:enterprise) }
context "when updating supplier" do
let(:new_producer) { create(:enterprise) }
it "updates the supplier" do
it "updates the variant" do
expect {
spree_put(
:update,
id: variant.id,
product_id: variant.product.id,
variant: { display_name: "Better tomatoes", sku: 456 }
)
variant.reload
}.to change { variant.display_name }.to("Better tomatoes")
.and change { variant.sku }.to(456.to_s)
end
context "when updating supplier" do
let(:new_producer) { create(:enterprise) }
it "updates the supplier" do
expect {
spree_put(
:update,
id: variant.id,
product_id: variant.product.id,
variant: { supplier_id: new_producer.id }
)
variant.reload
}.to change { variant.supplier_id }.to(new_producer.id)
end
it "removes associated product from existing Order Cycles" do
distributor = create(:distributor_enterprise)
order_cycle = create(
:simple_order_cycle,
variants: [variant],
coordinator: distributor,
distributors: [distributor]
)
spree_put(
:update,
id: variant.id,
product_id: variant.product.id,
variant: { supplier_id: new_producer.id }
)
variant.reload
}.to change { variant.supplier_id }.to(new_producer.id)
expect(order_cycle.reload.distributed_variants).not_to include variant
end
end
end
describe "#search" do
let(:supplier) { create(:supplier_enterprise) }
let!(:p1) { create(:simple_product, name: 'Product 1', supplier_id: supplier.id) }
let!(:p2) { create(:simple_product, name: 'Product 2', supplier_id: supplier.id) }
let!(:v1) { p1.variants.first }
let!(:v2) { p2.variants.first }
let!(:vo) { create(:variant_override, variant: v1, hub: d, count_on_hand: 44) }
let!(:d) { create(:distributor_enterprise) }
let!(:oc) { create(:simple_order_cycle, distributors: [d], variants: [v1]) }
it "filters by distributor" do
spree_get :search, q: 'Prod', distributor_id: d.id.to_s
expect(assigns(:variants)).to eq([v1])
end
it "removes associated product from existing Order Cycles" do
distributor = create(:distributor_enterprise)
order_cycle = create(
:simple_order_cycle,
variants: [variant],
coordinator: distributor,
distributors: [distributor]
)
it "applies variant overrides" do
spree_get :search, q: 'Prod', distributor_id: d.id.to_s
expect(assigns(:variants)).to eq([v1])
expect(assigns(:variants).first.on_hand).to eq(44)
end
spree_put(
:update,
id: variant.id,
product_id: variant.product.id,
variant: { supplier_id: new_producer.id }
)
it "filters by order cycle" do
spree_get :search, q: 'Prod', order_cycle_id: oc.id.to_s
expect(assigns(:variants)).to eq([v1])
end
expect(order_cycle.reload.distributed_variants).not_to include variant
it "does not filter when no distributor or order cycle is specified" do
spree_get :search, q: 'Prod'
expect(assigns(:variants)).to match_array [v1, v2]
end
end
describe '#destroy' do
let(:variant) { create(:variant) }
context 'when requesting with html' do
before do
allow(Spree::Variant).to receive(:find).with(variant.id.to_s) { variant }
allow(variant).to receive(:destroy).and_call_original
end
it 'deletes the variant' do
spree_delete :destroy, id: variant.id, product_id: variant.product.id,
format: 'html'
expect(variant).to have_received(:destroy)
end
it 'shows a success flash message' do
spree_delete :destroy, id: variant.id, product_id: variant.product.id,
format: 'html'
expect(flash[:success]).to be
end
it 'redirects to admin_product_variants_url' do
spree_delete :destroy, id: variant.id, product_id: variant.product.id,
format: 'html'
expect(response).to redirect_to spree.admin_product_variants_url(variant.product.id)
end
it 'destroys all its exchanges' do
exchange = create(:exchange)
variant.exchanges << exchange
spree_delete :destroy, id: variant.id, product_id: variant.product.id,
format: 'html'
expect(variant.exchanges.reload).to be_empty
end
end
end
end
describe "#search" do
let(:supplier) { create(:supplier_enterprise) }
let!(:p1) { create(:simple_product, name: 'Product 1', supplier_id: supplier.id) }
let!(:p2) { create(:simple_product, name: 'Product 2', supplier_id: supplier.id) }
context "log in as supplier and distributor enable_producers_to_edit_orders" do
let(:supplier1) { create(:supplier_enterprise) }
let(:supplier2) { create(:supplier_enterprise) }
let!(:p1) { create(:simple_product, name: 'Product 1', supplier_id: supplier1.id) }
let!(:p2) { create(:simple_product, name: 'Product 2', supplier_id: supplier2.id) }
let!(:v1) { p1.variants.first }
let!(:v2) { p2.variants.first }
let!(:vo) { create(:variant_override, variant: v1, hub: d, count_on_hand: 44) }
let!(:d) { create(:distributor_enterprise) }
let!(:oc) { create(:simple_order_cycle, distributors: [d], variants: [v1]) }
let!(:d) { create(:distributor_enterprise, enable_producers_to_edit_orders: true) }
let!(:oc) { create(:simple_order_cycle, distributors: [d], variants: [v1, v2]) }
it "filters by distributor" do
spree_get :search, q: 'Prod', distributor_id: d.id.to_s
expect(assigns(:variants)).to eq([v1])
before do
order = create(:order_with_line_items, distributor: d, line_items_count: 1)
order.line_items.take.variant.update_attribute(:supplier_id, supplier1.id)
controller_login_as_enterprise_user([supplier1])
end
it "applies variant overrides" do
spree_get :search, q: 'Prod', distributor_id: d.id.to_s
expect(assigns(:variants)).to eq([v1])
expect(assigns(:variants).first.on_hand).to eq(44)
describe "#search" do
it "filters by distributor and supplier1 products" do
spree_get :search, q: 'Prod', distributor_id: d.id.to_s
expect(assigns(:variants)).to eq([v1])
end
end
it "filters by order cycle" do
spree_get :search, q: 'Prod', order_cycle_id: oc.id.to_s
expect(assigns(:variants)).to eq([v1])
end
it "does not filter when no distributor or order cycle is specified" do
spree_get :search, q: 'Prod'
expect(assigns(:variants)).to match_array [v1, v2]
end
end
describe '#destroy' do
let(:variant) { create(:variant) }
context 'when requesting with html' do
before do
allow(Spree::Variant).to receive(:find).with(variant.id.to_s) { variant }
allow(variant).to receive(:destroy).and_call_original
end
it 'deletes the variant' do
spree_delete :destroy, id: variant.id, product_id: variant.product.id,
format: 'html'
expect(variant).to have_received(:destroy)
end
it 'shows a success flash message' do
spree_delete :destroy, id: variant.id, product_id: variant.product.id,
format: 'html'
expect(flash[:success]).to be
end
it 'redirects to admin_product_variants_url' do
spree_delete :destroy, id: variant.id, product_id: variant.product.id,
format: 'html'
expect(response).to redirect_to spree.admin_product_variants_url(variant.product.id)
end
it 'destroys all its exchanges' do
exchange = create(:exchange)
variant.exchanges << exchange
spree_delete :destroy, id: variant.id, product_id: variant.product.id,
format: 'html'
expect(variant.exchanges.reload).to be_empty
describe "#update" do
it "updates the variant" do
expect {
spree_put(
:update,
id: v1.id,
product_id: v1.product.id,
variant: { display_name: "Better tomatoes", sku: 456 }
)
v1.reload
}.to change { v1.display_name }.to("Better tomatoes")
.and change { v1.sku }.to(456.to_s)
end
end
end

View File

@@ -19,8 +19,9 @@ RSpec.describe OpenFoodNetwork::ScopeVariantsForSearch do
let!(:oc3) { create(:simple_order_cycle, distributors: [d2], variants: [v4]) }
let!(:s1) { create(:schedule, order_cycles: [oc1]) }
let!(:s2) { create(:schedule, order_cycles: [oc2]) }
let!(:spree_current_user) { create(:user) }
let(:scoper) { OpenFoodNetwork::ScopeVariantsForSearch.new(params) }
let(:scoper) { OpenFoodNetwork::ScopeVariantsForSearch.new(params, spree_current_user) }
describe "search" do
let(:result) { scoper.search }
@@ -68,8 +69,11 @@ RSpec.describe OpenFoodNetwork::ScopeVariantsForSearch do
expect{ result }.to query_database [
"Enterprise Load",
"VariantOverride Load",
"SQL"
"SQL",
"Enterprise Pluck",
"Enterprise Load"
]
expect(result).to include v4
expect(result).not_to include v1, v2, v3
end
@@ -179,6 +183,25 @@ RSpec.describe OpenFoodNetwork::ScopeVariantsForSearch do
to eq(["Product 1", "Product a", "Product b", "Product c"])
end
end
context "when search is done by the producer allowing to edit orders" do
let(:params) { { q: "product" } }
let(:producer) { create(:supplier_enterprise) }
let!(:spree_current_user) {
instance_double('Spree::User', enterprises: Enterprise.where(id: producer.id),
can_manage_line_items_in_orders_only?: true)
}
it "returns products distributed by distributors allowing producers to edit orders" do
v1.supplier_id = producer.id
v2.supplier_id = producer.id
v1.save!
v2.save!
expect(result).to include v1, v2
expect(result).not_to include v3, v4
end
end
end
private

View File

@@ -151,6 +151,7 @@ RSpec.describe Reporting::Reports::OrdersAndDistributors::Base do
subject # build context first
expect { subject.table_rows }.to query_database [
"Enterprise Pluck",
"SQL",
"Spree::LineItem Load",
"Spree::PaymentMethod Load",

View File

@@ -1012,6 +1012,29 @@ RSpec.describe Enterprise do
expect(expected).to include(sender)
end
end
describe "#is_producer_only" do
context "when enterprise is_primary_producer and sells none" do
it "returns true" do
enterprise = build(:supplier_enterprise)
expect(enterprise.is_producer_only).to be true
end
end
context "when enterprise is_primary_producer and sells any" do
it "returns false" do
enterprise = build(:enterprise, is_primary_producer: true, sells: "any")
expect(enterprise.is_producer_only).to be false
end
end
context "when enterprise is_primary_producer and sells own" do
it "returns false" do
enterprise = build(:enterprise, is_primary_producer: true, sells: "own")
expect(enterprise.is_producer_only).to be false
end
end
end
end
def enterprise_name_error(owner_email)

View File

@@ -240,6 +240,24 @@ RSpec.describe Spree::Ability do
it { expect(subject.can_manage_enterprises?(user)).to be true }
it { expect(subject.can_manage_orders?(user)).to be false }
it { expect(subject.can_manage_order_cycles?(user)).to be false }
context "with no distributor allows me to edit orders" do
it { expect(subject.can_manage_orders?(user)).to be false }
it { expect(subject.can_manage_line_items_in_orders?(user)).to be false }
end
context "with any distributor allows me to edit orders containing my product" do
before do
order = create(
:order_with_line_items,
line_items_count: 1,
distributor: create(:distributor_enterprise, enable_producers_to_edit_orders: true)
)
order.line_items.first.variant.update!(supplier_id: enterprise_none_producer.id)
end
it { expect(subject.can_manage_line_items_in_orders?(user)).to be true }
end
end
context "as a profile" do
@@ -260,6 +278,7 @@ RSpec.describe Spree::Ability do
it { expect(subject.can_manage_products?(user)).to be false }
it { expect(subject.can_manage_enterprises?(user)).to be false }
it { expect(subject.can_manage_orders?(user)).to be false }
it { expect(subject.can_manage_line_items_in_orders?(user)).to be false }
it { expect(subject.can_manage_order_cycles?(user)).to be false }
it "can create enterprises straight off the bat" do

View File

@@ -298,4 +298,40 @@ RSpec.describe Spree::User do
end
end
end
describe "#can_manage_line_items_in_orders_only?" do
let(:producer) { create(:supplier_enterprise) }
let(:order) { create(:order, distributor:) }
subject { user.can_manage_line_items_in_orders_only? }
context "when user has producer" do
let(:user) { create(:user, enterprises: [producer]) }
context "order containing their product" do
before do
order.line_items << create(:line_item,
product: create(:product, supplier_id: producer.id))
end
context "order distributor allow producer to edit orders" do
let(:distributor) do
create(:distributor_enterprise, enable_producers_to_edit_orders: true)
end
it { is_expected.to be_truthy }
end
context "order distributor doesn't allow producer to edit orders" do
let(:distributor) { create(:distributor_enterprise) }
it { is_expected.to be_falsey }
end
end
end
context "no order containing their product" do
let(:user) { create(:user, enterprises: [create(:distributor_enterprise)]) }
it { is_expected.to be_falsey }
end
end
end

View File

@@ -4,7 +4,6 @@ require 'spec_helper'
module Permissions
RSpec.describe Order do
let(:user) { double(:user) }
let(:permissions) { Permissions::Order.new(user) }
let!(:basic_permissions) { OpenFoodNetwork::Permissions.new(user) }
let(:distributor) { create(:distributor_enterprise) }
@@ -28,68 +27,24 @@ module Permissions
before { allow(OpenFoodNetwork::Permissions).to receive(:new) { basic_permissions } }
describe "finding orders that are visible in reports" do
let(:random_enterprise) { create(:distributor_enterprise) }
let(:order) { create(:order, order_cycle:, distributor: ) }
let!(:line_item) { create(:line_item, order:) }
let!(:producer) { create(:supplier_enterprise) }
context "with user cannot only manage line_items in orders" do
let(:user) { instance_double('Spree::User', can_manage_line_items_in_orders_only?: false) }
before do
allow(basic_permissions).to receive(:coordinated_order_cycles) { Enterprise.where("1=0") }
end
describe "finding orders that are visible in reports" do
let(:random_enterprise) { create(:distributor_enterprise) }
let(:order) { create(:order, order_cycle:, distributor: ) }
let!(:line_item) { create(:line_item, order:) }
let!(:producer) { create(:supplier_enterprise) }
context "as the hub through which the order was placed" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: distributor)
}
allow(basic_permissions).to receive(:coordinated_order_cycles) { Enterprise.where("1=0") }
end
it "should let me see the order" do
expect(permissions.visible_orders).to include order
end
end
context "as the coordinator of the order cycle through which the order was placed" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: coordinator)
}
allow(basic_permissions).to receive(:coordinated_order_cycles) {
OrderCycle.where(id: order_cycle)
}
end
it "should let me see the order" do
expect(permissions.visible_orders).to include order
end
context "with search params" do
let(:search_params) { { completed_at_gt: Time.zone.now.yesterday.strftime('%Y-%m-%d') } }
let(:permissions) { Permissions::Order.new(user, search_params) }
it "only returns completed, non-cancelled orders within search filter range" do
expect(permissions.visible_orders).to include order_completed
expect(permissions.visible_orders).not_to include order_cancelled
expect(permissions.visible_orders).not_to include order_cart
expect(permissions.visible_orders).not_to include order_from_last_year
end
end
end
context "as a producer which has granted P-OC to the distributor of an order" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: producer)
}
create(:enterprise_relationship, parent: producer, child: distributor,
permissions_list: [:add_to_order_cycle])
end
context "which contains my products" do
context "as the hub through which the order was placed" do
before do
line_item.variant.supplier = producer
line_item.variant.save
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: distributor)
}
end
it "should let me see the order" do
@@ -97,118 +52,208 @@ module Permissions
end
end
context "which does not contain my products" do
context "as the coordinator of the order cycle through which the order was placed" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: coordinator)
}
allow(basic_permissions).to receive(:coordinated_order_cycles) {
OrderCycle.where(id: order_cycle)
}
end
it "should let me see the order" do
expect(permissions.visible_orders).to include order
end
context "with search params" do
let(:search_params) {
{ completed_at_gt: Time.zone.now.yesterday.strftime('%Y-%m-%d') }
}
let(:permissions) { Permissions::Order.new(user, search_params) }
it "only returns completed, non-cancelled orders within search filter range" do
expect(permissions.visible_orders).to include order_completed
expect(permissions.visible_orders).not_to include order_cancelled
expect(permissions.visible_orders).not_to include order_cart
expect(permissions.visible_orders).not_to include order_from_last_year
end
end
end
context "as a producer which has granted P-OC to the distributor of an order" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: producer)
}
create(:enterprise_relationship, parent: producer, child: distributor,
permissions_list: [:add_to_order_cycle])
end
context "which contains my products" do
before do
line_item.variant.supplier = producer
line_item.variant.save
end
it "should let me see the order" do
expect(permissions.visible_orders).to include order
end
end
context "which does not contain my products" do
it "should not let me see the order" do
expect(permissions.visible_orders).not_to include order
end
end
end
context "as an enterprise that is a distributor in the order cycle, " \
"but not the distributor of the order" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: random_enterprise)
}
end
it "should not let me see the order" do
expect(permissions.visible_orders).not_to include order
end
end
end
context "as an enterprise that is a distributor in the order cycle, " \
"but not the distributor of the order" do
describe "finding line items that are visible in reports" do
let(:random_enterprise) { create(:distributor_enterprise) }
let(:order) { create(:order, order_cycle:, distributor: ) }
let!(:line_item1) { create(:line_item, order:) }
let!(:line_item2) { create(:line_item, order:) }
let!(:producer) { create(:supplier_enterprise) }
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: random_enterprise)
}
allow(basic_permissions).to receive(:coordinated_order_cycles) { Enterprise.where("1=0") }
end
it "should not let me see the order" do
expect(permissions.visible_orders).not_to include order
context "as the hub through which the parent order was placed" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: distributor)
}
end
it "should let me see the line_items" do
expect(permissions.visible_line_items).to include line_item1, line_item2
end
end
context "as the coordinator of the order cycle through which the parent order was placed" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: coordinator)
}
allow(basic_permissions).to receive(:coordinated_order_cycles) {
OrderCycle.where(id: order_cycle)
}
end
it "should let me see the line_items" do
expect(permissions.visible_line_items).to include line_item1, line_item2
end
end
context "as the manager producer which has granted P-OC to the distributor " \
"of the parent order" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: producer)
}
create(:enterprise_relationship, parent: producer, child: distributor,
permissions_list: [:add_to_order_cycle])
line_item1.variant.supplier = producer
line_item1.variant.save
end
it "should let me see the line_items pertaining to variants I produce" do
ps = permissions.visible_line_items
expect(ps).to include line_item1
expect(ps).not_to include line_item2
end
end
context "as an enterprise that is a distributor in the order cycle, " \
"but not the distributor of the parent order" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: random_enterprise)
}
end
it "should not let me see the line_items" do
expect(permissions.visible_line_items).not_to include line_item1, line_item2
end
end
context "with search params" do
let!(:line_item3) { create(:line_item, order: order_completed) }
let!(:line_item4) { create(:line_item, order: order_cancelled) }
let!(:line_item5) { create(:line_item, order: order_cart) }
let!(:line_item6) { create(:line_item, order: order_from_last_year) }
let(:search_params) { { completed_at_gt: Time.zone.now.yesterday.strftime('%Y-%m-%d') } }
let(:permissions) { Permissions::Order.new(user, search_params) }
before do
allow(user).to receive(:admin?) { "admin" }
end
it "only returns line items from completed, " \
"non-cancelled orders within search filter range" do
expect(permissions.visible_line_items).to include order_completed.line_items.first
expect(permissions.visible_line_items).not_to include order_cancelled.line_items.first
expect(permissions.visible_line_items).not_to include order_cart.line_items.first
expect(permissions.visible_line_items)
.not_to include order_from_last_year.line_items.first
end
end
end
end
describe "finding line items that are visible in reports" do
let(:random_enterprise) { create(:distributor_enterprise) }
let(:order) { create(:order, order_cycle:, distributor: ) }
let!(:line_item1) { create(:line_item, order:) }
let!(:line_item2) { create(:line_item, order:) }
let!(:producer) { create(:supplier_enterprise) }
before do
allow(basic_permissions).to receive(:coordinated_order_cycles) { Enterprise.where("1=0") }
context "with user can only manage line_items in orders" do
let(:producer) { create(:supplier_enterprise) }
let(:user) do
create(:user, enterprises: [producer])
end
let!(:order_by_distributor_allow_edits) do
order = create(
:order_with_line_items,
distributor: create(
:distributor_enterprise,
enable_producers_to_edit_orders: true
),
line_items_count: 1
)
order.line_items.first.variant.update_attribute(:supplier_id, producer.id)
context "as the hub through which the parent order was placed" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: distributor)
}
end
it "should let me see the line_items" do
expect(permissions.visible_line_items).to include line_item1, line_item2
order
end
let!(:order_by_distributor_disallow_edits) do
create(
:order_with_line_items,
distributor: create(:distributor_enterprise),
line_items_count: 1
)
end
describe "#editable_orders" do
it "returns orders where the distributor allows producers to edit" do
expect(permissions.editable_orders.count).to eq 1
expect(permissions.editable_orders).to include order_by_distributor_allow_edits
end
end
context "as the coordinator of the order cycle through which the parent order was placed" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: coordinator)
}
allow(basic_permissions).to receive(:coordinated_order_cycles) {
OrderCycle.where(id: order_cycle)
}
end
it "should let me see the line_items" do
expect(permissions.visible_line_items).to include line_item1, line_item2
end
end
context "as the manager producer which has granted P-OC to the distributor " \
"of the parent order" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: producer)
}
create(:enterprise_relationship, parent: producer, child: distributor,
permissions_list: [:add_to_order_cycle])
line_item1.variant.supplier = producer
line_item1.variant.save
end
it "should let me see the line_items pertaining to variants I produce" do
ps = permissions.visible_line_items
expect(ps).to include line_item1
expect(ps).not_to include line_item2
end
end
context "as an enterprise that is a distributor in the order cycle, " \
"but not the distributor of the parent order" do
before do
allow(basic_permissions).to receive(:managed_enterprises) {
Enterprise.where(id: random_enterprise)
}
end
it "should not let me see the line_items" do
expect(permissions.visible_line_items).not_to include line_item1, line_item2
end
end
context "with search params" do
let!(:line_item3) { create(:line_item, order: order_completed) }
let!(:line_item4) { create(:line_item, order: order_cancelled) }
let!(:line_item5) { create(:line_item, order: order_cart) }
let!(:line_item6) { create(:line_item, order: order_from_last_year) }
let(:search_params) { { completed_at_gt: Time.zone.now.yesterday.strftime('%Y-%m-%d') } }
let(:permissions) { Permissions::Order.new(user, search_params) }
before do
allow(user).to receive(:admin?) { "admin" }
end
it "only returns line items from completed, " \
"non-cancelled orders within search filter range" do
expect(permissions.visible_line_items).to include order_completed.line_items.first
expect(permissions.visible_line_items).not_to include order_cancelled.line_items.first
expect(permissions.visible_line_items).not_to include order_cart.line_items.first
expect(permissions.visible_line_items)
.not_to include order_from_last_year.line_items.first
describe "#editable_line_items" do
it "returns line items from orders where the distributor allows producers to edit" do
expect(permissions.editable_line_items.count).to eq 1
expect(permissions.editable_line_items.first.order).to eq order_by_distributor_allow_edits
end
end
end

View File

@@ -0,0 +1,165 @@
# frozen_string_literal: true
require 'system_helper'
RSpec.describe 'As a producer who have the ability to update orders' do
include AdminHelper
include AuthenticationHelper
include WebHelper
let!(:supplier1) { create(:supplier_enterprise, name: 'My supplier1') }
let!(:supplier2) { create(:supplier_enterprise, name: 'My supplier2') }
let!(:supplier1_v1) { create(:variant, supplier_id: supplier1.id) }
let!(:supplier1_v2) { create(:variant, supplier_id: supplier1.id) }
let!(:supplier2_v1) { create(:variant, supplier_id: supplier2.id) }
let(:order_cycle) do
create(:simple_order_cycle, distributors: [distributor], variants: [supplier1_v1, supplier1_v2])
end
let!(:order_containing_supplier1_products) do
o = create(
:completed_order_with_totals,
distributor:, order_cycle:,
user: supplier1_ent_user, line_items_count: 1
)
o.line_items.first.update_columns(variant_id: supplier1_v1.id)
o
end
let!(:order_containing_supplier2_v1_products) do
o = create(
:completed_order_with_totals,
distributor:, order_cycle:,
user: supplier2_ent_user, line_items_count: 1
)
o.line_items.first.update_columns(variant_id: supplier2_v1.id)
o
end
let(:supplier1_ent_user) { create(:user, enterprises: [supplier1]) }
let(:supplier2_ent_user) { create(:user, enterprises: [supplier2]) }
context "As supplier1 enterprise user" do
before { login_as(supplier1_ent_user) }
let(:order) { order_containing_supplier1_products }
let(:user) { supplier1_ent_user }
describe 'orders index page' do
before { visit spree.admin_orders_path }
context "when no distributor allow the producer to edit orders" do
let(:distributor) { create(:distributor_enterprise) }
it "should not allow producer to view orders page" do
expect(page).to have_content 'Unauthorized'
end
end
context "when distributor allows the producer to edit orders" do
let(:distributor) { create(:distributor_enterprise, enable_producers_to_edit_orders: true) }
it "should not allow to add new orders" do
expect(page).not_to have_link('New Order')
end
context "when distributor doesn't allow to view customer details" do
it "should allow producer to view orders page with HIDDEN customer details" do
within('#listing_orders tbody') do
expect(page).to have_selector('tr', count: 1) # Only one order
# One for Email, one for Name
expect(page).to have_selector('td', text: '< Hidden >', count: 2)
end
end
end
context "when distributor allows to view customer details" do
let(:distributor) do
create(
:distributor_enterprise,
enable_producers_to_edit_orders: true,
show_customer_names_to_suppliers: true
)
end
it "should allow producer to view orders page with customer details" do
within('#listing_orders tbody') do
name = order.bill_address&.full_name_for_sorting
email = order.email
expect(page).to have_selector('tr', count: 1) # Only one order
expect(page).to have_selector('td', text: name, count: 1)
expect(page).to have_selector('td', text: email, count: 1)
within 'td.actions' do
# to have edit button
expect(page).to have_selector("a.icon-edit")
# not to have ship button
expect(page).not_to have_selector('button.icon-road')
end
end
end
end
end
end
describe 'orders edit page' do
before { visit spree.edit_admin_order_path(order) }
context "when no distributor allow the producer to edit orders" do
let(:distributor) { create(:distributor_enterprise) }
it "should not allow producer to view orders page" do
expect(page).to have_content 'Unauthorized'
end
end
context "when distributor allows to edit orders" do
let(:distributor) { create(:distributor_enterprise, enable_producers_to_edit_orders: true) }
let(:product) { supplier1_v2.product }
it "should allow me to manage my products in the order" do
expect(page).to have_content 'Add Product'
# Add my product
add_product(product)
expect_product_change(product, :add)
# Edit my product
edit_product(product)
expect_product_change(product, :update, 2)
# Delete my product
delete_product(product)
expect_product_change(product, :remove)
end
end
def expect_product_change(product, action, expected_qty = 0)
# JS for this page sometimes take more than 2 seconds (default timeout for cappybara)
timeout = 5
within('table.index tbody tr', wait: timeout) do
case action
when :add
expect(page).to have_text(product.name, wait: timeout)
when :update
expect(page).to have_text(expected_qty.to_s, wait: timeout)
when :remove
expect(page).not_to have_text(product.name, wait: timeout)
else
raise 'Invalid action'
end
end
end
def add_product(product)
select2_select product.name, from: 'add_variant_id', search: true
find('button.add_variant').click
end
def edit_product(product)
find('a.edit-item.icon_link.icon-edit.no-text.with-tip').click
fill_in 'quantity', with: 2
find("a[data-variant-id='#{product.variants.last.id}'][data-action='save']").click
end
def delete_product(product)
find("a[data-variant-id='#{product.variants.last.id}'][data-action='remove']").click
click_button 'OK'
end
end
end
end

View File

@@ -0,0 +1,77 @@
# frozen_string_literal: true
require 'system_helper'
RSpec.describe 'As a producer who have the ability to update orders' do
include AdminHelper
include AuthenticationHelper
include WebHelper
let!(:supplier1) { create(:supplier_enterprise, name: 'My supplier1') }
let!(:supplier2) { create(:supplier_enterprise, name: 'My supplier2') }
let!(:supplier1_v1) { create(:variant, supplier_id: supplier1.id) }
let!(:supplier1_v2) { create(:variant, supplier_id: supplier1.id) }
let!(:supplier2_v1) { create(:variant, supplier_id: supplier2.id) }
let(:order_cycle) do
create(:simple_order_cycle, distributors: [distributor], variants: [supplier1_v1, supplier1_v2])
end
let!(:order_containing_supplier1_products) do
o = create(
:completed_order_with_totals,
distributor:, order_cycle:,
user: supplier1_ent_user, line_items_count: 1
)
o.line_items.first.update_columns(variant_id: supplier1_v1.id)
o
end
let(:supplier1_ent_user) { create(:user, enterprises: [supplier1]) }
context "As supplier1 enterprise user" do
before { login_as(supplier1_ent_user) }
let(:order) { order_containing_supplier1_products }
let(:user) { supplier1_ent_user }
describe 'bulk orders index page' do
before { visit spree.admin_bulk_order_management_path }
context "when no distributor allow the producer to edit orders" do
let(:distributor) { create(:distributor_enterprise) }
it "should not allow producer to view orders page" do
expect(page).to have_content 'Unauthorized'
end
end
context "when distributor allows the producer to edit orders" do
let(:distributor) { create(:distributor_enterprise, enable_producers_to_edit_orders: true) }
context "when distributor doesn't allow to view customer details" do
it "should allow producer to view bulk orders page with HIDDEN customer details" do
within('tbody') do
expect(page).to have_selector('tr', count: 1)
expect(page).to have_selector('td', text: '< Hidden >', count: 1)
end
end
end
context "when distributor allows to view customer details" do
let(:distributor) do
create(
:distributor_enterprise,
enable_producers_to_edit_orders: true,
show_customer_names_to_suppliers: true
)
end
it "should allow producer to view bulk orders page with customer details" do
within('tbody') do
expect(page).to have_selector('tr', count: 1)
expect(page).to have_selector('td', text: order.bill_address.full_name_for_sorting,
count: 1)
end
end
end
end
end
end
end

View File

@@ -21,7 +21,7 @@ RSpec.describe "spree/admin/orders/edit.html.haml" do
end
end
allow(view).to receive_messages spree_current_user: create(:user)
allow(view).to receive_messages spree_current_user: create(:admin_user)
end
context "when order is complete" do