mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-01 02:03:22 +00:00
OC Coordinators can opt to restrict products in an order cycle to those in their inventory only
This commit is contained in:
19
app/assets/stylesheets/admin/advanced_settings.css.scss
Normal file
19
app/assets/stylesheets/admin/advanced_settings.css.scss
Normal file
@@ -0,0 +1,19 @@
|
||||
#advanced_settings {
|
||||
background-color: #eff5fc;
|
||||
border: 1px solid #cee1f4;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.row{
|
||||
margin: 0px -4px;
|
||||
|
||||
padding: 20px 0px;
|
||||
|
||||
.column.alpha, .columns.alpha {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.column.omega, .columns.omega {
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ module Admin
|
||||
def for_order_cycle
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: @collection, each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer, spree_current_user: spree_current_user
|
||||
render json: @collection, each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer, order_cycle: @order_cycle, spree_current_user: spree_current_user
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -138,10 +138,10 @@ module Admin
|
||||
def collection
|
||||
case action
|
||||
when :for_order_cycle
|
||||
order_cycle = OrderCycle.find_by_id(params[:order_cycle_id]) if params[:order_cycle_id]
|
||||
@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: coordinator) if order_cycle.nil? && coordinator.present?
|
||||
return OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).visible_enterprises
|
||||
@order_cycle = OrderCycle.new(coordinator: coordinator) if @order_cycle.nil? && coordinator.present?
|
||||
return OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, @order_cycle).visible_enterprises
|
||||
when :index
|
||||
if spree_current_user.admin?
|
||||
OpenFoodNetwork::Permissions.new(spree_current_user).
|
||||
|
||||
@@ -60,8 +60,12 @@ module Admin
|
||||
|
||||
respond_to do |format|
|
||||
if @order_cycle.update_attributes(params[:order_cycle])
|
||||
OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, spree_current_user).go!
|
||||
unless params[:order_cycle][:incoming_exchanges].nil? && params[:order_cycle][:outgoing_exchanges].nil?
|
||||
# Only update apply exchange information if it is actually submmitted
|
||||
OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, spree_current_user).go!
|
||||
end
|
||||
flash[:notice] = 'Your order cycle has been updated.' if params[:reloading] == '1'
|
||||
format.html { redirect_to main_app.edit_admin_order_cycle_path(@order_cycle) }
|
||||
format.json { render :json => {:success => true} }
|
||||
else
|
||||
format.json { render :json => {:success => false} }
|
||||
|
||||
@@ -11,6 +11,8 @@ class OrderCycle < ActiveRecord::Base
|
||||
|
||||
validates_presence_of :name, :coordinator_id
|
||||
|
||||
preference :product_selection_from_coordinator_inventory_only, :boolean, default: false
|
||||
|
||||
scope :active, lambda { where('order_cycles.orders_open_at <= ? AND order_cycles.orders_close_at >= ?', Time.zone.now, Time.zone.now) }
|
||||
scope :active_or_complete, lambda { where('order_cycles.orders_open_at <= ?', Time.zone.now) }
|
||||
scope :inactive, lambda { where('order_cycles.orders_open_at > ? OR order_cycles.orders_close_at < ?', Time.zone.now, Time.zone.now) }
|
||||
@@ -113,6 +115,7 @@ class OrderCycle < ActiveRecord::Base
|
||||
oc.name = "COPY OF #{oc.name}"
|
||||
oc.orders_open_at = oc.orders_close_at = nil
|
||||
oc.coordinator_fee_ids = self.coordinator_fee_ids
|
||||
oc.preferred_product_selection_from_coordinator_inventory_only = self.preferred_product_selection_from_coordinator_inventory_only
|
||||
oc.save!
|
||||
self.exchanges.each { |e| e.clone!(oc) }
|
||||
oc.reload
|
||||
|
||||
@@ -52,6 +52,13 @@ Spree::Product.class_eval do
|
||||
|
||||
scope :with_order_cycles_inner, joins(:variants_including_master => {:exchanges => :order_cycle})
|
||||
|
||||
scope :visible_for, lambda { |enterprise|
|
||||
joins('LEFT OUTER JOIN spree_variants AS o_spree_variants ON (o_spree_variants.product_id = spree_products.id)').
|
||||
joins('LEFT OUTER JOIN inventory_items AS o_inventory_items ON (o_spree_variants.id = o_inventory_items.variant_id)').
|
||||
where('o_inventory_items.enterprise_id = (?) AND visible = (?)', enterprise, true).
|
||||
select('DISTINCT spree_products.*')
|
||||
}
|
||||
|
||||
|
||||
# -- Scopes
|
||||
scope :in_supplier, lambda { |supplier| where(:supplier_id => supplier) }
|
||||
|
||||
@@ -4,24 +4,35 @@ class Api::Admin::ExchangeSerializer < ActiveModel::Serializer
|
||||
has_many :enterprise_fees, serializer: Api::Admin::BasicEnterpriseFeeSerializer
|
||||
|
||||
def variants
|
||||
permitted = Spree::Variant.where("1=0")
|
||||
if object.incoming
|
||||
permitted = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object.order_cycle).
|
||||
visible_variants_for_incoming_exchanges_from(object.sender)
|
||||
variants = object.incoming? ? visible_incoming_variants : visible_outgoing_variants
|
||||
Hash[ object.variants.merge(variants).map { |v| [v.id, true] } ]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def visible_incoming_variants
|
||||
if object.order_cycle.prefers_product_selection_from_coordinator_inventory_only?
|
||||
permitted_incoming_variants.visible_for(object.order_cycle.coordinator)
|
||||
else
|
||||
# This is hopefully a temporary measure, pending the arrival of multiple named inventories
|
||||
# for shops. We need this here to allow hubs to restrict visible variants to only those in
|
||||
# their inventory if they so choose
|
||||
permitted = if object.receiver.prefers_product_selection_from_inventory_only?
|
||||
OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object.order_cycle)
|
||||
.visible_variants_for_outgoing_exchanges_to(object.receiver)
|
||||
.visible_for(object.receiver)
|
||||
else
|
||||
OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object.order_cycle)
|
||||
.visible_variants_for_outgoing_exchanges_to(object.receiver)
|
||||
.not_hidden_for(object.receiver)
|
||||
end
|
||||
permitted_incoming_variants
|
||||
end
|
||||
Hash[ object.variants.merge(permitted).map { |v| [v.id, true] } ]
|
||||
end
|
||||
|
||||
def visible_outgoing_variants
|
||||
if object.receiver.prefers_product_selection_from_inventory_only?
|
||||
permitted_outgoing_variants.visible_for(object.receiver)
|
||||
else
|
||||
permitted_outgoing_variants.not_hidden_for(object.receiver)
|
||||
end
|
||||
end
|
||||
|
||||
def permitted_incoming_variants
|
||||
OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object.order_cycle).
|
||||
visible_variants_for_incoming_exchanges_from(object.sender)
|
||||
end
|
||||
|
||||
def permitted_outgoing_variants
|
||||
OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object.order_cycle)
|
||||
.visible_variants_for_outgoing_exchanges_to(object.receiver)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,11 @@ class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer
|
||||
attributes :is_primary_producer, :is_distributor, :sells
|
||||
|
||||
def issues_summary_supplier
|
||||
OpenFoodNetwork::EnterpriseIssueValidator.new(object).issues_summary confirmation_only: true
|
||||
issues = OpenFoodNetwork::EnterpriseIssueValidator.new(object).issues_summary confirmation_only: true
|
||||
if issues.nil? && products.empty?
|
||||
issues = "no products in inventory"
|
||||
end
|
||||
issues
|
||||
end
|
||||
|
||||
def issues_summary_distributor
|
||||
@@ -18,8 +22,22 @@ class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer
|
||||
end
|
||||
|
||||
def supplied_products
|
||||
objects = object.supplied_products.not_deleted
|
||||
serializer = Api::Admin::ForOrderCycle::SuppliedProductSerializer
|
||||
ActiveModel::ArraySerializer.new(objects, each_serializer: serializer)
|
||||
ActiveModel::ArraySerializer.new(products, each_serializer: serializer, order_cycle: order_cycle)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def products
|
||||
return @products unless @products.nil?
|
||||
@products = if order_cycle.prefers_product_selection_from_coordinator_inventory_only?
|
||||
object.supplied_products.not_deleted.visible_for(order_cycle.coordinator)
|
||||
else
|
||||
object.supplied_products.not_deleted
|
||||
end
|
||||
end
|
||||
|
||||
def order_cycle
|
||||
options[:order_cycle]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,8 +14,17 @@ class Api::Admin::ForOrderCycle::SuppliedProductSerializer < ActiveModel::Serial
|
||||
end
|
||||
|
||||
def variants
|
||||
object.variants.map do |variant|
|
||||
{ id: variant.id, label: variant.full_name }
|
||||
variants = if order_cycle.prefers_product_selection_from_coordinator_inventory_only?
|
||||
object.variants.visible_for(order_cycle.coordinator)
|
||||
else
|
||||
object.variants
|
||||
end
|
||||
variants.map { |variant| { id: variant.id, label: variant.full_name } }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def order_cycle
|
||||
options[:order_cycle]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
.row
|
||||
.alpha.eleven.columns
|
||||
.three.columns.alpha
|
||||
= f.label "enterprise_preferred_product_selection_from_inventory_only", t(:select_products_for_order_cycle_from)
|
||||
= f.label "enterprise_preferred_product_selection_from_inventory_only", t('admin.enterprise.select_outgoing_oc_products_from')
|
||||
.three.columns
|
||||
= radio_button :enterprise, :preferred_product_selection_from_inventory_only, "1", { 'ng-model' => 'Enterprise.preferred_product_selection_from_inventory_only' }
|
||||
= label :enterprise, :preferred_product_selection_from_inventory_only, "Inventory Only"
|
||||
|
||||
22
app/views/admin/order_cycles/_advanced_settings.html.haml
Normal file
22
app/views/admin/order_cycles/_advanced_settings.html.haml
Normal file
@@ -0,0 +1,22 @@
|
||||
.row
|
||||
.alpha.omega.sixteen.columns
|
||||
%h3 Advanced Settings
|
||||
|
||||
= form_for [main_app, :admin, @order_cycle] do |f|
|
||||
.row
|
||||
.six.columns.alpha
|
||||
= f.label "enterprise_preferred_product_selection_from_coordinator_inventory_only", t('admin.order_cycle.choose_products_from')
|
||||
.with-tip{'data-powertip' => "You can opt to restrict all available products (both incoming and outgoing), to only those in #{@order_cycle.coordinator.name}'s inventory."}
|
||||
%a What's this?
|
||||
.four.columns
|
||||
= f.radio_button :preferred_product_selection_from_coordinator_inventory_only, true
|
||||
= f.label :preferred_product_selection_from_coordinator_inventory_only, "Coordinator's Inventory Only"
|
||||
.six.columns.omega
|
||||
= f.radio_button :preferred_product_selection_from_coordinator_inventory_only, false
|
||||
= f.label :preferred_product_selection_from_coordinator_inventory_only, "All Available Products"
|
||||
|
||||
.row
|
||||
.sixteen.columns.alpha.omega.text-center
|
||||
%input{ type: 'submit', value: 'Save and Reload Page' }
|
||||
or
|
||||
%a{ href: "#", onClick: "toggleSettings()" } Close
|
||||
@@ -9,8 +9,9 @@
|
||||
.exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle | visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges | orderBy:"name"' }
|
||||
.exchange-product-details
|
||||
%label
|
||||
= check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}',
|
||||
'ng-disabled' => 'product.variants.length > 0 || !order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0'
|
||||
-# MASTER_VARIANTS: No longer required
|
||||
-# = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}',
|
||||
-# 'ng-disabled' => 'product.variants.length > 0 || !order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0'
|
||||
%img{'ng-src' => '{{ product.image_url }}'}
|
||||
.name {{ product.name }}
|
||||
.supplier {{ product.supplier_name }}
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
- if can? :notify_producers, @order_cycle
|
||||
= content_for :page_actions do
|
||||
- content_for :page_actions do
|
||||
:javascript
|
||||
function toggleSettings(){
|
||||
if( $('#advanced_settings').is(":visible") ){
|
||||
$('button#toggle_settings i').switchClass("icon-chevron-up","icon-chevron-down")
|
||||
}
|
||||
else {
|
||||
$('button#toggle_settings i').switchClass("icon-chevron-down","icon-chevron-up")
|
||||
}
|
||||
$("#advanced_settings").slideToggle()
|
||||
}
|
||||
|
||||
- if can? :notify_producers, @order_cycle
|
||||
%li
|
||||
= button_to "Notify producers", main_app.notify_producers_admin_order_cycle_path, :id => 'admin_notify_producers', :confirm => 'Are you sure?'
|
||||
%li
|
||||
%button#toggle_settings{ onClick: 'toggleSettings()' }
|
||||
Advanced Settings
|
||||
%i.icon-chevron-down
|
||||
|
||||
|
||||
#advanced_settings{ hidden: true }
|
||||
= render partial: "/admin/order_cycles/advanced_settings"
|
||||
|
||||
%h1 Edit Order Cycle
|
||||
|
||||
|
||||
|
||||
@@ -94,6 +94,13 @@ en:
|
||||
hidden_powertip: These products have been hidden from your inventory and will not be available to add to your shop. You can click 'Add' to add a product to you inventory.
|
||||
new_powertip: These products are available to be added to your inventory. Click 'Add' to add a product to your inventory, or 'Hide' to hide it from view. You can always change your mind later!
|
||||
|
||||
|
||||
order_cycle:
|
||||
choose_products_from: "Choose Products From:"
|
||||
|
||||
enterprise:
|
||||
select_outgoing_oc_products_from: Select outgoing OC products from
|
||||
|
||||
# Printable Invoice Columns
|
||||
invoice_column_item: "Item"
|
||||
invoice_column_qty: "Qty"
|
||||
|
||||
@@ -103,10 +103,34 @@ module Admin
|
||||
end
|
||||
|
||||
it "does not set flash message otherwise" do
|
||||
spree_put :update, id: order_cycle.id, reloading: '0', order_cycle: {}
|
||||
flash[:notice].should be_nil
|
||||
end
|
||||
|
||||
context "when updating without explicitly submitting exchanges" do
|
||||
let(:form_applicator_mock) { double(:form_applicator) }
|
||||
let(:incoming_exchange) { create(:exchange, order_cycle: order_cycle, incoming: true) }
|
||||
let(:outgoing_exchange) { create(:exchange, order_cycle: order_cycle, incoming: false) }
|
||||
|
||||
|
||||
before do
|
||||
allow(OpenFoodNetwork::OrderCycleFormApplicator).to receive(:new) { form_applicator_mock }
|
||||
allow(form_applicator_mock).to receive(:go!) { nil }
|
||||
end
|
||||
|
||||
it "does not run the OrderCycleFormApplicator" do
|
||||
expect(order_cycle.exchanges.incoming).to eq [incoming_exchange]
|
||||
expect(order_cycle.exchanges.outgoing).to eq [outgoing_exchange]
|
||||
expect(order_cycle.prefers_product_selection_from_coordinator_inventory_only?).to be false
|
||||
spree_put :update, id: order_cycle.id, order_cycle: { name: 'Some new name', preferred_product_selection_from_coordinator_inventory_only: true }
|
||||
expect(form_applicator_mock).to_not have_received(:go!)
|
||||
order_cycle.reload
|
||||
expect(order_cycle.exchanges.incoming).to eq [incoming_exchange]
|
||||
expect(order_cycle.exchanges.outgoing).to eq [outgoing_exchange]
|
||||
expect(order_cycle.name).to eq 'Some new name'
|
||||
expect(order_cycle.prefers_product_selection_from_coordinator_inventory_only?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context "as a producer supplying to an order cycle" do
|
||||
let(:producer) { create(:supplier_enterprise) }
|
||||
let(:coordinator) { order_cycle.coordinator }
|
||||
|
||||
@@ -380,7 +380,7 @@ describe OrderCycle do
|
||||
|
||||
it "clones itself" do
|
||||
coordinator = create(:enterprise);
|
||||
oc = create(:simple_order_cycle, coordinator_fees: [create(:enterprise_fee, enterprise: coordinator)])
|
||||
oc = create(:simple_order_cycle, coordinator_fees: [create(:enterprise_fee, enterprise: coordinator)], preferred_product_selection_from_coordinator_inventory_only: true)
|
||||
ex1 = create(:exchange, order_cycle: oc)
|
||||
ex2 = create(:exchange, order_cycle: oc)
|
||||
oc.clone!
|
||||
@@ -390,10 +390,12 @@ describe OrderCycle do
|
||||
occ.orders_open_at.should be_nil
|
||||
occ.orders_close_at.should be_nil
|
||||
occ.coordinator.should_not be_nil
|
||||
occ.preferred_product_selection_from_coordinator_inventory_only.should be_true
|
||||
occ.coordinator.should == oc.coordinator
|
||||
|
||||
occ.coordinator_fee_ids.should_not be_empty
|
||||
occ.coordinator_fee_ids.should == oc.coordinator_fee_ids
|
||||
occ.preferred_product_selection_from_coordinator_inventory_only.should == oc.preferred_product_selection_from_coordinator_inventory_only
|
||||
|
||||
# to_h gives us a unique hash for each exchange
|
||||
# check that the clone has no additional exchanges
|
||||
|
||||
@@ -338,6 +338,22 @@ module Spree
|
||||
product.should include @p2
|
||||
end
|
||||
end
|
||||
|
||||
describe "visible_for" do
|
||||
let(:enterprise) { create(:distributor_enterprise) }
|
||||
let!(:new_variant) { create(:variant) }
|
||||
let!(:hidden_variant) { create(:variant) }
|
||||
let!(:visible_variant) { create(:variant) }
|
||||
let!(:hidden_inventory_item) { create(:inventory_item, enterprise: enterprise, variant: hidden_variant, visible: false ) }
|
||||
let!(:visible_inventory_item) { create(:inventory_item, enterprise: enterprise, variant: visible_variant, visible: true ) }
|
||||
|
||||
let!(:products) { Spree::Product.visible_for(enterprise) }
|
||||
|
||||
it "lists any products with variants that are listed as visible=true" do
|
||||
expect(products).to include visible_variant.product
|
||||
expect(products).to_not include new_variant.product, hidden_variant.product
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "finders" do
|
||||
|
||||
@@ -133,7 +133,7 @@ module Spree
|
||||
context "finding variants that are visible in an enterprise's inventory" do
|
||||
let!(:variants) { Spree::Variant.visible_for(enterprise) }
|
||||
|
||||
it "lists any variants that are not listed as visible=false" do
|
||||
it "lists any variants that are listed as visible=true" do
|
||||
expect(variants).to include visible_variant
|
||||
expect(variants).to_not include new_variant, hidden_variant
|
||||
end
|
||||
|
||||
@@ -3,65 +3,93 @@ require 'open_food_network/order_cycle_permissions'
|
||||
describe Api::Admin::ExchangeSerializer do
|
||||
let(:v1) { create(:variant) }
|
||||
let(:v2) { create(:variant) }
|
||||
let(:v3) { create(:variant) }
|
||||
let(:permissions_mock) { double(:permissions) }
|
||||
let(:permitted_variants) { Spree::Variant.where(id: [v1, v2]) }
|
||||
let(:serializer) { Api::Admin::ExchangeSerializer.new exchange }
|
||||
|
||||
|
||||
context "serializing incoming exchanges" do
|
||||
let(:exchange) { create(:exchange, incoming: true, variants: [v1, v2]) }
|
||||
let(:permitted_variants) { double(:permitted_variants) }
|
||||
let(:exchange) { create(:exchange, incoming: true, variants: [v1, v2, v3]) }
|
||||
let!(:inventory_item) { create(:inventory_item, enterprise: exchange.order_cycle.coordinator, variant: v1, visible: true) }
|
||||
|
||||
before do
|
||||
allow(OpenFoodNetwork::OrderCyclePermissions).to receive(:new) { permissions_mock }
|
||||
allow(permissions_mock).to receive(:visible_variants_for_incoming_exchanges_from) { Spree::Variant.where(id: [v1]) }
|
||||
allow(permissions_mock).to receive(:visible_variants_for_incoming_exchanges_from) { permitted_variants }
|
||||
allow(permitted_variants).to receive(:visible_for).and_call_original
|
||||
end
|
||||
|
||||
it "filters variants within the exchange based on permissions" do
|
||||
visible_variants = serializer.variants
|
||||
expect(permissions_mock).to have_received(:visible_variants_for_incoming_exchanges_from).with(exchange.sender)
|
||||
expect(exchange.variants).to include v1, v2
|
||||
expect(visible_variants.keys).to include v1.id
|
||||
expect(visible_variants.keys).to_not include v2.id
|
||||
context "when order cycle shows only variants in the coordinator's inventory" do
|
||||
before do
|
||||
allow(exchange.order_cycle).to receive(:prefers_product_selection_from_coordinator_inventory_only?) { true }
|
||||
end
|
||||
|
||||
it "filters variants within the exchange based on permissions, and visibility in inventory" do
|
||||
visible_variants = serializer.variants
|
||||
expect(permissions_mock).to have_received(:visible_variants_for_incoming_exchanges_from).with(exchange.sender)
|
||||
expect(permitted_variants).to have_received(:visible_for).with(exchange.order_cycle.coordinator)
|
||||
expect(exchange.variants).to include v1, v2, v3
|
||||
expect(visible_variants.keys).to include v1.id
|
||||
expect(visible_variants.keys).to_not include v2.id, v3.id
|
||||
end
|
||||
end
|
||||
|
||||
context "when order cycle shows all available products" do
|
||||
before do
|
||||
allow(exchange.order_cycle).to receive(:prefers_product_selection_from_coordinator_inventory_only?) { false }
|
||||
end
|
||||
|
||||
it "filters variants within the exchange based on permissions only" do
|
||||
visible_variants = serializer.variants
|
||||
expect(permissions_mock).to have_received(:visible_variants_for_incoming_exchanges_from).with(exchange.sender)
|
||||
expect(permitted_variants).to_not have_received(:visible_for)
|
||||
expect(exchange.variants).to include v1, v2, v3
|
||||
expect(visible_variants.keys).to include v1.id, v2.id
|
||||
expect(visible_variants.keys).to_not include v3.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "serializing outgoing exchanges" do
|
||||
let(:exchange) { create(:exchange, incoming: false, variants: [v1, v2]) }
|
||||
let(:permitted_variants) { double(:permitted_variants) }
|
||||
let(:exchange) { create(:exchange, incoming: false, variants: [v1, v2, v3]) }
|
||||
let!(:inventory_item) { create(:inventory_item, enterprise: exchange.receiver, variant: v1, visible: true) }
|
||||
|
||||
before do
|
||||
allow(OpenFoodNetwork::OrderCyclePermissions).to receive(:new) { permissions_mock }
|
||||
allow(permissions_mock).to receive(:visible_variants_for_outgoing_exchanges_to) { permitted_variants }
|
||||
allow(permitted_variants).to receive(:visible_for).and_call_original
|
||||
allow(permitted_variants).to receive(:not_hidden_for).and_call_original
|
||||
end
|
||||
|
||||
context "when the receiver prefers to see all variants (not just those in their inventory)" do
|
||||
before do
|
||||
allow(exchange.receiver).to receive(:prefers_product_selection_from_inventory_only?) { false }
|
||||
allow(permitted_variants).to receive(:not_hidden_for) { Spree::Variant.where(id: [v1]) }
|
||||
end
|
||||
|
||||
it "filters variants within the exchange based on permissions" do
|
||||
it "filters variants within the exchange based on permissions only" do
|
||||
visible_variants = serializer.variants
|
||||
expect(permissions_mock).to have_received(:visible_variants_for_outgoing_exchanges_to).with(exchange.receiver)
|
||||
expect(permitted_variants).to have_received(:not_hidden_for).with(exchange.receiver)
|
||||
expect(exchange.variants).to include v1, v2
|
||||
expect(visible_variants.keys).to include v1.id
|
||||
expect(visible_variants.keys).to_not include v2.id
|
||||
expect(permitted_variants).to_not have_received(:visible_for)
|
||||
expect(exchange.variants).to include v1, v2, v3
|
||||
expect(visible_variants.keys).to include v1.id, v2.id
|
||||
expect(visible_variants.keys).to_not include v3.id
|
||||
end
|
||||
end
|
||||
|
||||
context "when the receiver prefers to restrict visible variants to only those in their inventory" do
|
||||
before do
|
||||
allow(exchange.receiver).to receive(:prefers_product_selection_from_inventory_only?) { true }
|
||||
allow(permitted_variants).to receive(:visible_for) { Spree::Variant.where(id: [v1]) }
|
||||
end
|
||||
|
||||
it "filters variants within the exchange based on permissions, and inventory visibility" do
|
||||
visible_variants = serializer.variants
|
||||
expect(permissions_mock).to have_received(:visible_variants_for_outgoing_exchanges_to).with(exchange.receiver)
|
||||
expect(permitted_variants).to have_received(:visible_for).with(exchange.receiver)
|
||||
expect(exchange.variants).to include v1, v2
|
||||
expect(permitted_variants).to_not have_received(:not_hidden_for)
|
||||
expect(exchange.variants).to include v1, v2, v3
|
||||
expect(visible_variants.keys).to include v1.id
|
||||
expect(visible_variants.keys).to_not include v2.id
|
||||
expect(visible_variants.keys).to_not include v2.id, v3.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,13 +1,46 @@
|
||||
describe Api::Admin::ForOrderCycle::EnterpriseSerializer do
|
||||
let(:enterprise) { create(:distributor_enterprise) }
|
||||
let!(:product) { create(:simple_product, supplier: enterprise) }
|
||||
let!(:deleted_product) { create(:simple_product, supplier: enterprise, deleted_at: 24.hours.ago ) }
|
||||
let(:serialized_enterprise) { Api::Admin::ForOrderCycle::EnterpriseSerializer.new(enterprise, spree_current_user: enterprise.owner ).to_json }
|
||||
let(:coordinator) { create(:distributor_enterprise) }
|
||||
let(:order_cycle) { double(:order_cycle, coordinator: coordinator) }
|
||||
let(:enterprise) { create(:distributor_enterprise) }
|
||||
let!(:non_inventory_product) { create(:simple_product, supplier: enterprise) }
|
||||
let!(:non_inventory_variant) { non_inventory_product.variants.first }
|
||||
let!(:inventory_product) { create(:simple_product, supplier: enterprise) }
|
||||
let!(:inventory_variant) { inventory_product.variants.first }
|
||||
let!(:deleted_product) { create(:simple_product, supplier: enterprise, deleted_at: 24.hours.ago ) }
|
||||
let!(:deleted_variant) { deleted_product.variants.first }
|
||||
let(:serialized_enterprise) { Api::Admin::ForOrderCycle::EnterpriseSerializer.new(enterprise, order_cycle: order_cycle, spree_current_user: enterprise.owner ).to_json }
|
||||
let!(:inventory_item1) { create(:inventory_item, enterprise: coordinator, variant: inventory_variant, visible: true)}
|
||||
let!(:inventory_item2) { create(:inventory_item, enterprise: coordinator, variant: deleted_variant, visible: true)}
|
||||
|
||||
describe "supplied products" do
|
||||
it "does not render deleted products" do
|
||||
expect(serialized_enterprise).to have_json_size(1).at_path 'supplied_products'
|
||||
expect(serialized_enterprise).to be_json_eql(product.master.id).at_path 'supplied_products/0/master_id'
|
||||
context "when order cycle shows only variants in the coordinator's inventory" do
|
||||
before do
|
||||
allow(order_cycle).to receive(:prefers_product_selection_from_coordinator_inventory_only?) { true }
|
||||
end
|
||||
|
||||
describe "supplied products" do
|
||||
it "renders only non-deleted variants that are in the coordinators inventory" do
|
||||
expect(serialized_enterprise).to have_json_size(1).at_path 'supplied_products'
|
||||
expect(serialized_enterprise).to have_json_size(1).at_path 'supplied_products/0/variants'
|
||||
expect(serialized_enterprise).to be_json_eql(inventory_variant.id).at_path 'supplied_products/0/variants/0/id'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
context "when order cycle shows all available products" do
|
||||
before do
|
||||
allow(order_cycle).to receive(:prefers_product_selection_from_coordinator_inventory_only?) { false }
|
||||
end
|
||||
|
||||
describe "supplied products" do
|
||||
it "renders variants that are not in the coordinators inventory but not variants of deleted products" do
|
||||
expect(serialized_enterprise).to have_json_size(2).at_path 'supplied_products'
|
||||
expect(serialized_enterprise).to have_json_size(1).at_path 'supplied_products/0/variants'
|
||||
expect(serialized_enterprise).to have_json_size(1).at_path 'supplied_products/1/variants'
|
||||
variant_ids = parse_json(serialized_enterprise)['supplied_products'].map{ |p| p['variants'].first['id'] }
|
||||
expect(variant_ids).to include non_inventory_variant.id, inventory_variant.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
describe Api::Admin::ForOrderCycle::EnterpriseSerializer do
|
||||
let(:coordinator) { create(:distributor_enterprise) }
|
||||
let(:order_cycle) { double(:order_cycle, coordinator: coordinator) }
|
||||
let!(:product) { create(:simple_product) }
|
||||
let!(:non_inventory_variant) { product.variants.first }
|
||||
let!(:inventory_variant) { create(:variant, product: product.reload) }
|
||||
let(:serialized_product) { Api::Admin::ForOrderCycle::SuppliedProductSerializer.new(product, order_cycle: order_cycle ).to_json }
|
||||
let!(:inventory_item) { create(:inventory_item, enterprise: coordinator, variant: inventory_variant, visible: true)}
|
||||
|
||||
context "when order cycle shows only variants in the coordinator's inventory" do
|
||||
before do
|
||||
allow(order_cycle).to receive(:prefers_product_selection_from_coordinator_inventory_only?) { true }
|
||||
end
|
||||
|
||||
describe "variants" do
|
||||
it "renders only variants that are in the coordinators inventory" do
|
||||
expect(serialized_product).to have_json_size(1).at_path 'variants'
|
||||
expect(serialized_product).to be_json_eql(inventory_variant.id).at_path 'variants/0/id'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
context "when order cycle shows all available products" do
|
||||
before do
|
||||
allow(order_cycle).to receive(:prefers_product_selection_from_coordinator_inventory_only?) { false }
|
||||
end
|
||||
|
||||
describe "supplied products" do
|
||||
it "renders variants regardless of whether they are in the coordinators inventory" do
|
||||
expect(serialized_product).to have_json_size(2).at_path 'variants'
|
||||
variant_ids = parse_json(serialized_product)['variants'].map{ |v| v['id'] }
|
||||
expect(variant_ids).to include non_inventory_variant.id, inventory_variant.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user