diff --git a/app/controllers/admin/enterprise_fees_controller.rb b/app/controllers/admin/enterprise_fees_controller.rb index 13e27e05aa..f665bc1c37 100644 --- a/app/controllers/admin/enterprise_fees_controller.rb +++ b/app/controllers/admin/enterprise_fees_controller.rb @@ -79,7 +79,7 @@ module Admin 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? - enterprises = OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle) + enterprises = OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for return EnterpriseFee.for_enterprises(enterprises).order('enterprise_id', 'fee_type', 'name') else collection = EnterpriseFee.managed_by(spree_current_user).order('enterprise_id', 'fee_type', 'name') diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index bb13bb6739..9776f23e69 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -108,7 +108,7 @@ module Admin 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::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle) + return OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for else # TODO was ordered with is_distributor DESC as well, not sure why or how we want to sort this now OpenFoodNetwork::Permissions.new(spree_current_user). diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index aa7b52d19a..acbe28720f 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -4,7 +4,7 @@ module OrderCyclesHelper end def permitted_enterprises_for(order_cycle) - OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle) + OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for end def permitted_producer_enterprises_for(order_cycle) diff --git a/app/serializers/api/admin/exchange_serializer.rb b/app/serializers/api/admin/exchange_serializer.rb index 518d865003..64c2c08cd4 100644 --- a/app/serializers/api/admin/exchange_serializer.rb +++ b/app/serializers/api/admin/exchange_serializer.rb @@ -6,11 +6,11 @@ class Api::Admin::ExchangeSerializer < ActiveModel::Serializer def variants permitted = Spree::Variant.where("1=0") if object.incoming - permitted = OpenFoodNetwork::Permissions.new(options[:current_user]). - visible_variants_for_incoming_exchanges_between(object.sender, object.receiver, order_cycle: object.order_cycle) + permitted = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object.order_cycle). + visible_variants_for_incoming_exchanges_from(object.sender) else - permitted = OpenFoodNetwork::Permissions.new(options[:current_user]). - visible_variants_for_outgoing_exchanges_between(object.sender, object.receiver, order_cycle: object.order_cycle) + permitted = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object.order_cycle). + visible_variants_for_outgoing_exchanges_to(object.receiver) end Hash[ object.variants.merge(permitted).map { |v| [v.id, true] } ] end diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb index d907d3ef6d..795daf85bc 100644 --- a/app/serializers/api/admin/order_cycle_serializer.rb +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -19,7 +19,7 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer end def exchanges - scoped_exchanges = OpenFoodNetwork::Permissions.new(options[:current_user]).order_cycle_exchanges(object).order('id ASC') + scoped_exchanges = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object).order_cycle_exchanges.order('id ASC') ActiveModel::ArraySerializer.new(scoped_exchanges, {each_serializer: Api::Admin::ExchangeSerializer, current_user: options[:current_user] }) end @@ -27,10 +27,10 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer # For each enterprise that the current user is able to see in this order cycle, # work out which variants should be editable within incoming exchanges from that enterprise editable = {} - permissions = OpenFoodNetwork::Permissions.new(options[:current_user]) - enterprises = permissions.order_cycle_enterprises_for(object) + permissions = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object) + enterprises = permissions.order_cycle_enterprises_for enterprises.each do |enterprise| - variants = permissions.editable_variants_for_incoming_exchanges_between(enterprise, object.coordinator, order_cycle: object).pluck(:id) + variants = permissions.editable_variants_for_incoming_exchanges_from(enterprise).pluck(:id) editable[enterprise.id] = variants if variants.any? end editable @@ -40,10 +40,10 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer # For each enterprise that the current user is able to see in this order cycle, # work out which variants should be editable within incoming exchanges from that enterprise editable = {} - permissions = OpenFoodNetwork::Permissions.new(options[:current_user]) - enterprises = permissions.order_cycle_enterprises_for(object) + permissions = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object) + enterprises = permissions.order_cycle_enterprises_for enterprises.each do |enterprise| - variants = permissions.editable_variants_for_outgoing_exchanges_between(object.coordinator, enterprise, order_cycle: object).pluck(:id) + variants = permissions.editable_variants_for_outgoing_exchanges_to(enterprise).pluck(:id) editable[enterprise.id] = variants if variants.any? end editable @@ -53,10 +53,10 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer # For each enterprise that the current user is able to see in this order cycle, # work out which variants should be visible within outgoing exchanges from that enterprise visible = {} - permissions = OpenFoodNetwork::Permissions.new(options[:current_user]) - enterprises = permissions.order_cycle_enterprises_for(object) + permissions = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object) + enterprises = permissions.order_cycle_enterprises_for enterprises.each do |enterprise| - variants = permissions.visible_variants_for_outgoing_exchanges_between(object.coordinator, enterprise, order_cycle: object).pluck(:id) + variants = permissions.visible_variants_for_outgoing_exchanges_to(enterprise).pluck(:id) visible[enterprise.id] = variants if variants.any? end visible diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index f30c740f03..1af364440e 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -8,7 +8,7 @@ - unless order_cycles_simple_index %td.suppliers - - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle)) + - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for) - supplier_list = suppliers.map(&:name).sort.join ', ' - if suppliers.count > 3 %span.with-tip{'data-powertip' => supplier_list} @@ -18,7 +18,7 @@ = supplier_list %td= order_cycle.coordinator.name %td.distributors - - distributors = order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises_for(order_cycle)) + - distributors = order_cycle.distributors.merge(OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).order_cycle_enterprises_for) - distributor_list = distributors.map(&:name).sort.join ', ' - if distributors.count > 3 %span.with-tip{'data-powertip' => distributor_list} diff --git a/lib/open_food_network/order_cycle_form_applicator.rb b/lib/open_food_network/order_cycle_form_applicator.rb index 3447699538..81e070049c 100644 --- a/lib/open_food_network/order_cycle_form_applicator.rb +++ b/lib/open_food_network/order_cycle_form_applicator.rb @@ -102,8 +102,8 @@ module OpenFoodNetwork def permitted_enterprises return @permitted_enterprises unless @permitted_enterprises.nil? - @permitted_enterprises = OpenFoodNetwork::Permissions.new(@spree_current_user). - order_cycle_enterprises_for(@order_cycle) + @permitted_enterprises = OpenFoodNetwork::OrderCyclePermissions. + new(@spree_current_user, @order_cycle).order_cycle_enterprises_for end def manages_coordinator? @@ -112,15 +112,13 @@ module OpenFoodNetwork end def editable_variant_ids_for_incoming_exchange_between(sender, receiver) - OpenFoodNetwork::Permissions.new(@spree_current_user). - editable_variants_for_incoming_exchanges_between(sender, receiver, order_cycle: @order_cycle). - pluck(:id) + OpenFoodNetwork::OrderCyclePermissions.new(@spree_current_user, @order_cycle). + editable_variants_for_incoming_exchanges_from(sender).pluck(:id) end def editable_variant_ids_for_outgoing_exchange_between(sender, receiver) - OpenFoodNetwork::Permissions.new(@spree_current_user). - editable_variants_for_outgoing_exchanges_between(sender, receiver, order_cycle: @order_cycle). - pluck(:id) + OpenFoodNetwork::OrderCyclePermissions.new(@spree_current_user, @order_cycle). + editable_variants_for_outgoing_exchanges_to(receiver).pluck(:id) end def find_incoming_exchange(attrs) diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb new file mode 100644 index 0000000000..80c7bb9f19 --- /dev/null +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -0,0 +1,240 @@ +module OpenFoodNetwork + # Class which is used for determining the permissions around a single order cycle and user + # both of which are set at initialization + class OrderCyclePermissions < Permissions + def initialize(user, order_cycle) + super(user) + @order_cycle = order_cycle + @coordinator = order_cycle.andand.coordinator + end + + # List of any enterprises whose exchanges I should be able to see in order_cycle + # NOTE: the enterprises a given user can see actually in the OC interface depend on the relationships + # of their enterprises to the coordinator of the order cycle, rather than on the order cycle itself + def order_cycle_enterprises_for + return Enterprise.where("1=0") unless @coordinator.present? + if managed_enterprises.include? @coordinator + coordinator_permitted = [@coordinator] + all_active = [] + + if @coordinator.sells == "any" + # If the coordinator sells any, relationships come into play + granting(:add_to_order_cycle, to: [@coordinator]).pluck(:id).each do |enterprise_id| + coordinator_permitted << enterprise_id + end + + # As a safety net, we should load all of the enterprises invloved in existing exchanges in this order cycle + all_active = @order_cycle.suppliers.pluck(:id) | @order_cycle.distributors.pluck(:id) + end + + Enterprise.where(id: coordinator_permitted | all_active) + else + # Any enterprises that I manage directly, which have granted P-OC to the coordinator + managed_permitted = granting(:add_to_order_cycle, to: [@coordinator], scope: managed_enterprises_in(@order_cycle) ).pluck(:id) + + # Any hubs in this OC that have been granted P-OC by producers I manage in this OC + hubs_permitted = granted(:add_to_order_cycle, by: managed_producers_in(@order_cycle), scope: @order_cycle.distributors).pluck(:id) + + # Any hubs in this OC that have granted P-OC to producers I manage in this OC + hubs_permitting = granting(:add_to_order_cycle, to: managed_producers_in(@order_cycle), scope: @order_cycle.distributors).pluck(:id) + + # Any producers in this OC that have been granted P-OC by hubs I manage in this OC + producers_permitted = granted(:add_to_order_cycle, by: managed_hubs_in(@order_cycle), scope: @order_cycle.suppliers).pluck(:id) + + # Any producers in this OC that have granted P-OC to hubs I manage in this OC + producers_permitting = granting(:add_to_order_cycle, to: managed_hubs_in(@order_cycle), scope: @order_cycle.suppliers).pluck(:id) + + managed_active = [] + hubs_active = [] + producers_active = [] + if @order_cycle + # TODO: Remove this when all P-OC are sorted out + # Any enterprises that I manage that are already in the order_cycle + managed_active = managed_enterprises_in(@order_cycle).pluck(:id) + + # TODO: Remove this when all P-OC are sorted out + # Any hubs that currently have outgoing exchanges distributing variants of producers I manage + variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) + active_exchanges = @order_cycle.exchanges.outgoing.with_any_variant(variants) + hubs_active = active_exchanges.map(&:receiver_id) + + # TODO: Remove this when all P-OC are sorted out + # Any producers of variants that hubs I manage are currently distributing in this OC + variants = Spree::Variant.joins(:exchanges).where("exchanges.receiver_id IN (?) AND exchanges.order_cycle_id = (?) AND exchanges.incoming = 'f'", managed_hubs_in(@order_cycle), @order_cycle).pluck(:id).uniq + products = Spree::Product.joins(:variants_including_master).where("spree_variants.id IN (?)", variants).pluck(:id).uniq + producers_active = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).pluck(:id).uniq + end + + ids = managed_permitted | hubs_permitted | hubs_permitting | producers_permitted | producers_permitting | managed_active | hubs_active | producers_active + + Enterprise.where(id: ids.sort ) + end + end + + # Find the exchanges of an order cycle that an admin can manage + def order_cycle_exchanges + ids = order_cycle_exchange_ids_involving_my_enterprises | + order_cycle_exchange_ids_distributing_my_variants | + order_cycle_exchange_ids_with_distributable_variants + + Exchange.where(id: ids, order_cycle_id: @order_cycle) + end + + # Find the variants that a user can POTENTIALLY see within incoming exchanges + def visible_variants_for_incoming_exchanges_from(producer) + return Spree::Variant.where("1=0") unless @order_cycle + if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(@coordinator.id) + # All variants belonging to the producer + Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) + else + # All variants of the producer if it has granted P-OC to any of my managed hubs that are in this order cycle + permitted = EnterpriseRelationship.permitting(managed_hubs_in(@order_cycle)). + permitted_by(producer).with_permission(:add_to_order_cycle).present? + if permitted + Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) + else + Spree::Variant.where("1=0") + end + end + end + + # Find the variants that a user can edit within incoming exchanges + def editable_variants_for_incoming_exchanges_from(producer) + return Spree::Variant.where("1=0") unless @order_cycle + if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(@coordinator.id) + # All variants belonging to the producer + Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) + else + Spree::Variant.where("1=0") + end + end + + # Find the variants that a user is permitted see within outgoing exchanges + # Note that this does not determine whether they actually appear in outgoing exchanges + # as this requires first that the variant is included in an incoming exchange + def visible_variants_for_outgoing_exchanges_to(hub) + return Spree::Variant.where("1=0") unless @order_cycle + if managed_enterprises.pluck(:id).include?(hub.id) || managed_enterprises.pluck(:id).include?(@coordinator.id) + # Any variants produced by the coordinator, for outgoing exchanges with itself + coordinator_variants = [] + if hub == @coordinator + coordinator_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', @coordinator) + end + + # Any variants of any producers that have granted the hub P-OC + producers = granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + + # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break + # TODO: Remove this when all P-OC are sorted out + active_variants = [] + @order_cycle.exchanges.outgoing.where(receiver_id: hub).limit(1).each do |exchange| + active_variants = exchange.variants + end + + Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) + else + # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub + producers = granting(:add_to_order_cycle, to: [hub], scope: managed_producers_in(@order_cycle)) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + + # PLUS any of my incoming producers' variants that are already in an outgoing exchange of this hub, so things don't break + # TODO: Remove this when all P-OC are sorted out + active_variants = Spree::Variant.joins(:exchanges, :product). + where("exchanges.receiver_id = (?) AND spree_products.supplier_id IN (?) AND incoming = 'f'", hub, managed_enterprises.is_primary_producer) + + Spree::Variant.where(id: permitted_variants | active_variants) + end + end + + # Find the variants that a user is permitted edit within outgoing exchanges + def editable_variants_for_outgoing_exchanges_to(hub) + return Spree::Variant.where("1=0") unless @order_cycle + if managed_enterprises.pluck(:id).include?(hub.id) || managed_enterprises.pluck(:id).include?(@coordinator.id) + # Any variants produced by the coordinator, for outgoing exchanges with itself + coordinator_variants = [] + if hub == @coordinator + coordinator_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', @coordinator) + end + + # Any variants of any producers that have granted the hub P-OC + producers = granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + + # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break + # TODO: Remove this when all P-OC are sorted out + active_variants = [] + @order_cycle.exchanges.outgoing.where(receiver_id: hub).limit(1).each do |exchange| + active_variants = exchange.variants + end + + Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) + else + # Any of my managed producers in this order cycle granted P-OC by the hub + granted_producers = granted(:add_to_order_cycle, by: [hub], scope: managed_producers_in(@order_cycle)) + + # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub + granting_producers = granting(:add_to_order_cycle, to: [hub], scope: granted_producers) + permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', granting_producers) + + Spree::Variant.where(id: permitted_variants) + end + end + + + private + + def managed_enterprises_in(order_cycle) + managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors) + end + + def managed_hubs_in(order_cycle) + managed_enterprises_in(order_cycle).is_hub + end + + def managed_producers_in(order_cycle) + managed_enterprises_in(order_cycle).is_primary_producer + end + + def order_cycle_exchange_ids_involving_my_enterprises + # Any exchanges that my managed enterprises are involved in directly + @order_cycle.exchanges.involving(managed_enterprises).pluck :id + end + + def order_cycle_exchange_ids_with_distributable_variants + # Find my managed hubs in this order cycle + hubs = managed_hubs_in(@order_cycle) + # Any incoming exchange where the producer has granted P-OC to one or more of those hubs + producers = granting(:add_to_order_cycle, to: hubs, scope: Enterprise.is_primary_producer).pluck :id + permitted_exchanges = @order_cycle.exchanges.incoming.where(sender_id: producers).pluck :id + + # TODO: remove active_exchanges when we think it is safe to do so + # active_exchanges is for backward compatability, before we restricted variants in each + # outgoing exchange to those where the producer had granted P-OC to the distributor + # For any of my managed hubs in this OC, any incoming exchanges supplying variants in my outgoing exchanges + variants = Spree::Variant.joins(:exchanges).where("exchanges.receiver_id IN (?) AND exchanges.order_cycle_id = (?) AND exchanges.incoming = 'f'", hubs, @order_cycle).pluck(:id).uniq + products = Spree::Product.joins(:variants_including_master).where("spree_variants.id IN (?)", variants).pluck(:id).uniq + producers = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).pluck(:id).uniq + active_exchanges = @order_cycle.exchanges.incoming.where(sender_id: producers).pluck :id + + permitted_exchanges | active_exchanges + end + + def order_cycle_exchange_ids_distributing_my_variants + # Find my producers in this order cycle + producers = managed_producers_in(@order_cycle).pluck :id + # Any outgoing exchange where the distributor has been granted P-OC by one or more of those producers + hubs = granted(:add_to_order_cycle, by: producers, scope: Enterprise.is_hub) + permitted_exchanges = @order_cycle.exchanges.outgoing.where(receiver_id: hubs).pluck :id + + # TODO: remove active_exchanges when we think it is safe to do so + # active_exchanges is for backward compatability, before we restricted variants in each + # outgoing exchange to those where the producer had granted P-OC to the distributor + # For any of my managed producers, any outgoing exchanges with their variants + variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) + active_exchanges = @order_cycle.exchanges.outgoing.with_any_variant(variants).pluck :id + + permitted_exchanges | active_exchanges + end + end +end diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 9a98d7a750..16319ced97 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -15,73 +15,6 @@ module OpenFoodNetwork managed_and_related_enterprises_with :add_to_order_cycle end - # List of any enterprises whose exchanges I should be able to see in order_cycle - # NOTE: the enterprises a given user can see actually in the OC interface depend on the relationships - # of their enterprises to the coordinator of the order cycle, rather than on the order cycle itself - # (until such time as we implement friends of friends) - def order_cycle_enterprises_for(order_cycle) - return Enterprise.where("1=0") unless order_cycle.andand.coordinator.present? - coordinator = order_cycle.coordinator - - if managed_enterprises.include? coordinator - coordinator_permitted = [coordinator] - all_active = [] - - if coordinator.sells == "any" - # If I manage the coordinator (or possibly in the future, if coordinator has made order cycle a friends of friend OC) - # Any hubs that have granted the coordinator P-OC (or any enterprises that have granted mine P-OC if we do friends of friends) - # If the coordinator sells any, relationships come into play - granting(:add_to_order_cycle, to: [coordinator]).pluck(:id).each do |enterprise_id| - coordinator_permitted << enterprise_id - end - - all_active = order_cycle.suppliers.pluck(:id) | order_cycle.distributors.pluck(:id) - end - - Enterprise.where(id: coordinator_permitted | all_active) - else - # Any enterprises that I manage directly, which have granted P-OC to the coordinator - managed_permitted = granting(:add_to_order_cycle, to: [coordinator], scope: managed_enterprises_in(order_cycle) ).pluck(:id) - - # Any hubs in this OC that have been granted P-OC by producers I manage in this OC - hubs_permitted = granted(:add_to_order_cycle, by: managed_producers_in(order_cycle), scope: order_cycle.distributors).pluck(:id) - - # Any hubs in this OC that have granted P-OC to producers I manage in this OC - hubs_permitting = granting(:add_to_order_cycle, to: managed_producers_in(order_cycle), scope: order_cycle.distributors).pluck(:id) - - # Any producers in this OC that have been granted P-OC by hubs I manage in this OC - producers_permitted = granted(:add_to_order_cycle, by: managed_hubs_in(order_cycle), scope: order_cycle.suppliers).pluck(:id) - - # Any producers in this OC that have granted P-OC to hubs I manage in this OC - producers_permitting = granting(:add_to_order_cycle, to: managed_hubs_in(order_cycle), scope: order_cycle.suppliers).pluck(:id) - - managed_active = [] - hubs_active = [] - producers_active = [] - if order_cycle - # TODO: Remove this when all P-OC are sorted out - # Any enterprises that I manage that are already in the order_cycle - managed_active = managed_enterprises_in(order_cycle).pluck(:id) - - # TODO: Remove this when all P-OC are sorted out - # Any hubs that currently have outgoing exchanges distributing variants of producers I manage - variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', managed_enterprises.is_primary_producer) - active_exchanges = order_cycle.exchanges.outgoing.with_any_variant(variants) - hubs_active = active_exchanges.map(&:receiver_id) - - # TODO: Remove this when all P-OC are sorted out - # Any producers of variants that hubs I manage are currently distributing in this OC - variants = Spree::Variant.joins(:exchanges).where("exchanges.receiver_id IN (?) AND exchanges.order_cycle_id = (?) AND exchanges.incoming = 'f'", managed_hubs_in(order_cycle), order_cycle).pluck(:id).uniq - products = Spree::Product.joins(:variants_including_master).where("spree_variants.id IN (?)", variants).pluck(:id).uniq - producers_active = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).pluck(:id).uniq - end - - ids = managed_permitted | hubs_permitted | hubs_permitting | producers_permitted | producers_permitting | managed_active | hubs_active | producers_active - - Enterprise.where(id: ids.sort ) - end - end - # Find enterprises for which an admin is allowed to edit their profile def editable_enterprises managed_and_related_enterprises_with :edit_profile @@ -123,116 +56,6 @@ module OpenFoodNetwork permissions end - # Find the exchanges of an order cycle that an admin can manage - def order_cycle_exchanges(order_cycle) - ids = order_cycle_exchange_ids_involving_my_enterprises(order_cycle) | - order_cycle_exchange_ids_distributing_my_variants(order_cycle) | - order_cycle_exchange_ids_with_distributable_variants(order_cycle) - - Exchange.where(id: ids, order_cycle_id: order_cycle) - end - - # Find the variants that a user can POTENTIALLY see within incoming exchanges - def visible_variants_for_incoming_exchanges_between(producer, coordinator, options={}) - return Spree::Variant.where("1=0") unless options[:order_cycle] - if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(coordinator.id) - # All variants belonging to the producer - Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) - else - # All variants of the producer if it has granted P-OC to any of my managed hubs that are in this order cycle - permitted = EnterpriseRelationship.permitting(managed_hubs_in(options[:order_cycle])). - permitted_by(producer).with_permission(:add_to_order_cycle).present? - if permitted - Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) - else - Spree::Variant.where("1=0") - end - end - end - - # Find the variants that a user can edit within incoming exchanges - def editable_variants_for_incoming_exchanges_between(producer, coordinator, options={}) - return Spree::Variant.where("1=0") unless options[:order_cycle] - if managed_enterprises.pluck(:id).include?(producer.id) || managed_enterprises.pluck(:id).include?(coordinator.id) - # All variants belonging to the producer - Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) - else - Spree::Variant.where("1=0") - end - end - - # Find the variants that a user is permitted see within outgoing exchanges - # Note that this does not determine whether they actually appear in outgoing exchanges - # as this requires first that the variant is included in an incoming exchange - def visible_variants_for_outgoing_exchanges_between(coordinator, hub, options={}) - return Spree::Variant.where("1=0") unless options[:order_cycle] - if managed_enterprises.pluck(:id).include?(hub.id) || managed_enterprises.pluck(:id).include?(coordinator.id) - # Any variants produced by the coordinator, for outgoing exchanges with itself - coordinator_variants = [] - if hub == coordinator - coordinator_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', coordinator) - end - - # Any variants of any producers that have granted the hub P-OC - producers = granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) - permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) - - # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break - # TODO: Remove this when all P-OC are sorted out - active_variants = [] - options[:order_cycle].exchanges.outgoing.where(receiver_id: hub).limit(1).each do |exchange| - active_variants = exchange.variants - end - - Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) - else - # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub - producers = granting(:add_to_order_cycle, to: [hub], scope: managed_producers_in(options[:order_cycle])) - permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) - - # PLUS any of my incoming producers' variants that are already in an outgoing exchange of this hub, so things don't break - # TODO: Remove this when all P-OC are sorted out - active_variants = Spree::Variant.joins(:exchanges, :product). - where("exchanges.receiver_id = (?) AND spree_products.supplier_id IN (?) AND incoming = 'f'", hub, managed_enterprises.is_primary_producer) - - Spree::Variant.where(id: permitted_variants | active_variants) - end - end - - # Find the variants that a user is permitted edit within outgoing exchanges - def editable_variants_for_outgoing_exchanges_between(coordinator, hub, options={}) - return Spree::Variant.where("1=0") unless options[:order_cycle] - if managed_enterprises.pluck(:id).include?(hub.id) || managed_enterprises.pluck(:id).include?(coordinator.id) - # Any variants produced by the coordinator, for outgoing exchanges with itself - coordinator_variants = [] - if hub == coordinator - coordinator_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', coordinator) - end - - # Any variants of any producers that have granted the hub P-OC - producers = granting(:add_to_order_cycle, to: [hub], scope: Enterprise.is_primary_producer) - permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) - - # PLUS any variants that are already in an outgoing exchange of this hub, so things don't break - # TODO: Remove this when all P-OC are sorted out - active_variants = [] - options[:order_cycle].exchanges.outgoing.where(receiver_id: hub).limit(1).each do |exchange| - active_variants = exchange.variants - end - - Spree::Variant.where(id: coordinator_variants | permitted_variants | active_variants) - else - # Any of my managed producers in this order cycle granted P-OC by the hub - granted_producers = granted(:add_to_order_cycle, by: [hub], scope: managed_producers_in(options[:order_cycle])) - - # Any variants produced by MY PRODUCERS that are in this order cycle, where my producer has granted P-OC to the hub - granting_producers = granting(:add_to_order_cycle, to: [hub], scope: granted_producers) - permitted_variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', granting_producers) - - Spree::Variant.where(id: permitted_variants ) - end - end - def managed_products managed_enterprise_products_ids = managed_enterprise_products.pluck :id permitted_enterprise_products_ids = related_enterprise_products.pluck :id @@ -261,18 +84,6 @@ module OpenFoodNetwork Enterprise.managed_by(@user) end - def managed_enterprises_in(order_cycle) - managed_enterprises.where(id: order_cycle.suppliers | order_cycle.distributors) - end - - def managed_hubs_in(order_cycle) - managed_enterprises_in(order_cycle).is_hub - end - - def managed_producers_in(order_cycle) - managed_enterprises_in(order_cycle).is_primary_producer - end - def related_enterprises_with(permission) parent_ids = EnterpriseRelationship. permitting(managed_enterprises). @@ -307,46 +118,5 @@ module OpenFoodNetwork def related_enterprise_products Spree::Product.where('supplier_id IN (?)', related_enterprises_with(:manage_products)) end - - def order_cycle_exchange_ids_involving_my_enterprises(order_cycle) - # Any exchanges that my managed enterprises are involved in directly - order_cycle.exchanges.involving(managed_enterprises).pluck :id - end - - def order_cycle_exchange_ids_with_distributable_variants(order_cycle) - # Find my managed hubs in this order cycle - hubs = managed_hubs_in(order_cycle) - # Any incoming exchange where the producer has granted P-OC to one or more of those hubs - producers = granting(:add_to_order_cycle, to: hubs, scope: Enterprise.is_primary_producer).pluck :id - permitted_exchanges = order_cycle.exchanges.incoming.where(sender_id: producers).pluck :id - - # TODO: remove active_exchanges when we think it is safe to do so - # active_exchanges is for backward compatability, before we restricted variants in each - # outgoing exchange to those where the producer had granted P-OC to the distributor - # For any of my managed hubs in this OC, any incoming exchanges supplying variants in my outgoing exchanges - variants = Spree::Variant.joins(:exchanges).where("exchanges.receiver_id IN (?) AND exchanges.order_cycle_id = (?) AND exchanges.incoming = 'f'", hubs, order_cycle).pluck(:id).uniq - products = Spree::Product.joins(:variants_including_master).where("spree_variants.id IN (?)", variants).pluck(:id).uniq - producers = Enterprise.joins(:supplied_products).where("spree_products.id IN (?)", products).pluck(:id).uniq - active_exchanges = order_cycle.exchanges.incoming.where(sender_id: producers).pluck :id - - permitted_exchanges | active_exchanges - end - - def order_cycle_exchange_ids_distributing_my_variants(order_cycle) - # Find my producers in this order cycle - producers = managed_producers_in(order_cycle).pluck :id - # Any outgoing exchange where the distributor has been granted P-OC by one or more of those producers - hubs = granted(:add_to_order_cycle, by: producers, scope: Enterprise.is_hub) - permitted_exchanges = order_cycle.exchanges.outgoing.where(receiver_id: hubs).pluck :id - - # TODO: remove active_exchanges when we think it is safe to do so - # active_exchanges is for backward compatability, before we restricted variants in each - # outgoing exchange to those where the producer had granted P-OC to the distributor - # For any of my managed producers, any outgoing exchanges with their variants - variants = Spree::Variant.joins(:product).where('spree_products.supplier_id IN (?)', producers) - active_exchanges = order_cycle.exchanges.outgoing.with_any_variant(variants).pluck :id - - permitted_exchanges | active_exchanges - end end end diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 53edeae6b7..1b5849ef9f 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require 'open_food_network/order_cycle_permissions' module Admin describe EnterprisesController do @@ -418,36 +419,36 @@ module Admin Enterprise.stub find_by_id: "existing Enterprise" OrderCycle.stub new: "new OrderCycle" - OpenFoodNetwork::Permissions.stub(:new) { permission_mock } - allow(permission_mock).to receive(:order_cycle_enterprises_for) + allow(OpenFoodNetwork::OrderCyclePermissions).to receive(:new) { permission_mock } + allow(permission_mock).to receive(:order_cycle_enterprises_for) { [] } allow(ActiveModel::ArraySerializer).to receive(:new) { "" } end context "when no order_cycle or coordinator is provided in params" do before { spree_get :for_order_cycle, format: :json } - it "returns an empty scope" do - expect(permission_mock).to have_received(:order_cycle_enterprises_for).with(nil) + it "initializes permissions with nil" do + expect(OpenFoodNetwork::OrderCyclePermissions).to have_received(:new).with(user, nil) end end context "when an order_cycle_id is provided in params" do before { spree_get :for_order_cycle, format: :json, order_cycle_id: 1 } - it "calls order_cycle_enterprises_for() with the existing OrderCycle" do - expect(permission_mock).to have_received(:order_cycle_enterprises_for).with("existing OrderCycle") + it "initializes permissions with the existing OrderCycle" do + expect(OpenFoodNetwork::OrderCyclePermissions).to have_received(:new).with(user, "existing OrderCycle") end end context "when a coordinator is provided in params" do before { spree_get :for_order_cycle, format: :json, coordinator_id: 1 } - it "calls order_cycle_enterprises_for() with a new OrderCycle" do - expect(permission_mock).to have_received(:order_cycle_enterprises_for).with("new OrderCycle") + it "initializes permissions with a new OrderCycle" do + expect(OpenFoodNetwork::OrderCyclePermissions).to have_received(:new).with(user, "new OrderCycle") end end context "when both an order cycle and a coordinator are provided in params" do before { spree_get :for_order_cycle, format: :json, order_cycle_id: 1, coordinator_id: 1 } - it "calls order_cycle_enterprises_for() with the existing OrderCycle" do - expect(permission_mock).to have_received(:order_cycle_enterprises_for).with("existing OrderCycle") + it "initializes permissions with the existing OrderCycle" do + expect(OpenFoodNetwork::OrderCyclePermissions).to have_received(:new).with(user, "existing OrderCycle") end end end diff --git a/spec/lib/open_food_network/order_cycle_permissions_spec.rb b/spec/lib/open_food_network/order_cycle_permissions_spec.rb new file mode 100644 index 0000000000..b3279fc11a --- /dev/null +++ b/spec/lib/open_food_network/order_cycle_permissions_spec.rb @@ -0,0 +1,797 @@ +require 'open_food_network/order_cycle_permissions' + +module OpenFoodNetwork + describe OrderCyclePermissions do + let(:coordinator) { create(:distributor_enterprise) } + let(:hub) { create(:distributor_enterprise) } + let(:producer) { create(:supplier_enterprise) } + let(:user) { double(:user) } + let(:oc) { create(:simple_order_cycle, coordinator: coordinator) } + let(:permissions) { OrderCyclePermissions.new(user, oc) } + + describe "finding enterprises that can be viewed in the order cycle interface" do + context "when permissions are initialized without an order_cycle" do + let(:permissions) { OrderCyclePermissions.new(user, nil) } + + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + end + + it "returns an empty scope" do + expect(permissions.order_cycle_enterprises_for).to be_empty + end + end + + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + end + + it "returns the coordinator itself" do + expect(permissions.order_cycle_enterprises_for).to include coordinator + end + + context "where P-OC has been granted to the coordinator by other enterprises" do + before do + create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) + end + + context "where the coordinator sells any" do + it "returns enterprises which have granted P-OC to the coordinator" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include hub + expect(enterprises).to_not include producer + end + end + + context "where the coordinator sells 'own'" do + before { coordinator.stub(:sells) { 'own' } } + it "returns just the coordinator" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub, producer + end + end + end + + context "where P-OC has not been granted to the coordinator by other enterprises" do + context "where the other enterprise are already in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + context "where the coordinator sells any" do + it "returns enterprises which have granted P-OC to the coordinator" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include hub, producer + end + end + + context "where the coordinator sells 'own'" do + before { coordinator.stub(:sells) { 'own' } } + it "returns just the coordinator" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub, producer + end + end + end + + context "where the other enterprises are not in the order cycle" do + it "returns just the coordinator" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub, producer + end + end + end + end + + context "as a manager of a hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } + end + + context "that has granted P-OC to the coordinator" do + before do + create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) + end + + context "where my hub is in the order cycle" do + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns my hub" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include hub + expect(enterprises).to_not include producer, coordinator + end + + context "and has been granted P-OC by a producer" do + before do + create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) + end + + context "where the producer is in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + it "returns the producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer, hub + end + end + + context "where the producer is not in the order cycle" do + # No incoming exchange + + it "does not return the producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include producer + end + end + end + + context "and has granted P-OC to a producer" do + before do + create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) + end + + context "where the producer is in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + it "returns the producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer, hub + end + end + + context "where the producer is not in the order cycle" do + # No incoming exchange + + it "does not return the producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include producer + end + end + end + end + + context "where my hub is not in the order cycle" do + # No outgoing exchange for my hub + + it "does not return my hub" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub, producer, coordinator + end + end + end + + context "that has not granted P-OC to the coordinator" do + it "does not return my hub" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub, producer, coordinator + end + + context "but is already in the order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns my hub" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include hub + expect(enterprises).to_not include producer, coordinator + end + + context "and distributes variants distributed by an unmanaged and unpermitted producer" do + before { ex.variants << create(:variant, product: create(:product, supplier: producer)) } + + # TODO: update this when we are confident about P-OCs + it "returns that producer as well" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer, hub + expect(enterprises).to_not include coordinator + end + end + end + end + end + + context "as a manager of a producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } + end + + context "which has granted P-OC to the coordinator" do + before do + create(:enterprise_relationship, parent: producer, child: coordinator, permissions_list: [:add_to_order_cycle]) + end + + context "where my producer is in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + it "returns my producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer + expect(enterprises).to_not include hub, coordinator + end + + context "and has been granted P-OC by a hub" do + before do + create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) + end + + context "where the hub is also in the order cycle" do + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns the hub as well" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer, hub + expect(enterprises).to_not include coordinator + end + end + + context "where the hub is not in the order cycle" do + # No outgoing exchange + + it "does not return the hub" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub + end + end + end + + context "and has granted P-OC to a hub" do + before do + create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) + end + + context "where the hub is also in the order cycle" do + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns the hub as well" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer, hub + expect(enterprises).to_not include coordinator + end + end + + context "where the hub is not in the order cycle" do + # No outgoing exchange + + it "does not return the hub" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub + end + end + end + end + + context "where my producer is not in the order cycle" do + # No incoming exchange for producer + + it "does not return my producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include hub, producer, coordinator + end + end + end + + context "which has not granted P-OC to the coordinator" do + it "does not return my producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to_not include producer + end + + context "but is already in the order cycle" do + let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + # TODO: update this when we are confident about P-OCs + it "returns my producer" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer + expect(enterprises).to_not include hub, coordinator + end + + context "and has variants distributed by an outgoing hub" do + let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + before { ex_outgoing.variants << create(:variant, product: create(:product, supplier: producer)) } + + # TODO: update this when we are confident about P-OCs + it "returns that hub as well" do + enterprises = permissions.order_cycle_enterprises_for + expect(enterprises).to include producer, hub + expect(enterprises).to_not include coordinator + end + end + end + end + end + end + + describe "finding exchanges of an order cycle that an admin can manage" do + describe "as the manager of the coordinator" do + let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + let!(:ex_out) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + end + + it "returns all exchanges in the order cycle, regardless of hubE permissions" do + permissions.order_cycle_exchanges.should include ex_in, ex_out + end + end + + + describe "as the manager of a hub" do + let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } + end + + context "where my hub is in the order cycle" do + let!(:ex_out) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns my hub's outgoing exchange" do + permissions.order_cycle_exchanges.should == [ex_out] + end + + context "where my hub has been granted P-OC by an incoming producer" do + before do + create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns the producer's incoming exchange" do + permissions.order_cycle_exchanges.should include ex_in + end + end + + context "where my hub has not been granted P-OC by an incoming producer" do + it "returns the producers's incoming exchange, and my own outhoing exchange" do + permissions.order_cycle_exchanges.should_not include ex_in + end + end + end + + context "where my hub isn't in the order cycle" do + it "does not return the producer's incoming exchanges" do + permissions.order_cycle_exchanges.should == [] + end + end + + # TODO: this is testing legacy behaviour for backwards compatability, remove when behaviour no longer required + describe "legacy compatability" do + context "where my hub's outgoing exchange contains variants of a producer I don't manage and has not given my hub P-OC" do + let!(:product) { create(:product, supplier: producer) } + let!(:variant) { create(:variant, product: product) } + let!(:ex_out) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: true) } + before { ex_out.variants << variant } + + it "returns incoming exchanges supplying the variants in my outgoing exchange" do + permissions.order_cycle_exchanges.should include ex_out + end + end + end + end + + describe "as the manager of a producer" do + let!(:ex_out) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } + end + + context "where my producer supplies to the order cycle" do + let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + it "returns my producer's incoming exchange" do + permissions.order_cycle_exchanges.should == [ex_in] + end + + context "my producer has granted P-OC to an outgoing hub" do + before do + create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns the hub's outgoing exchange" do + permissions.order_cycle_exchanges.should include ex_out + end + end + + context "my producer has not granted P-OC to an outgoing hub" do + it "does not return the hub's outgoing exchange" do + permissions.order_cycle_exchanges.should_not include ex_out + end + end + end + + context "where my producer doesn't supply the order cycle" do + it "does not return the hub's outgoing exchanges" do + permissions.order_cycle_exchanges.should == [] + end + end + + # TODO: this is testing legacy behaviour for backwards compatability, remove when behaviour no longer required + describe "legacy compatability" do + context "where an outgoing exchange contains variants of a producer I manage" do + let!(:product) { create(:product, supplier: producer) } + let!(:variant) { create(:variant, product: product) } + before { ex_out.variants << variant } + + context "where my producer supplies to the order cycle" do + let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } + + it "returns the outgoing exchange" do + permissions.order_cycle_exchanges.should include ex_out + end + end + + context "where my producer doesn't supply to the order cycle" do + it "does not return the outgoing exchange" do + permissions.order_cycle_exchanges.should_not include ex_out + end + end + end + end + end + end + + + describe "finding the variants within a hypothetical exchange between two enterprises which are visible to a user" do + let!(:producer1) { create(:supplier_enterprise) } + let!(:producer2) { create(:supplier_enterprise) } + let!(:v1) { create(:variant, product: create(:simple_product, supplier: producer1)) } + let!(:v2) { create(:variant, product: create(:simple_product, supplier: producer2)) } + + describe "incoming exchanges" do + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + end + + it "returns all variants belonging to the sending producer" do + visible = permissions.visible_variants_for_incoming_exchanges_from(producer1) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as a manager of the producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } + end + + it "returns all variants belonging to the sending producer" do + visible = permissions.visible_variants_for_incoming_exchanges_from(producer1) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as a manager of a hub which has been granted P-OC by the producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + context "where the hub is in the order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + it "returns variants produced by that producer only" do + visible = permissions.visible_variants_for_incoming_exchanges_from(producer1) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "where the hub is not in the order cycle" do + # No outgoing exchange + + it "does not return variants produced by that producer" do + visible = permissions.visible_variants_for_incoming_exchanges_from(producer1) + expect(visible).to_not include v1, v2 + end + end + end + end + + describe "outgoing exchanges" do + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns all variants of any producer which has granted the outgoing hub P-OC" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + + context "where the coordinator produces products" do + let!(:v3) { create(:variant, product: create(:simple_product, supplier: coordinator)) } + + it "returns any variants produced by the coordinator itself for exchanges with 'self'" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(coordinator) + expect(visible).to include v3 + expect(visible).to_not include v1, v2 + end + + it "does not return coordinator's variants for exchanges with other hubs, when permission has not been granted" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2, v3 + end + end + + # TODO: for backwards compatability, remove later + context "when an exchange exists between the coordinator and the hub within this order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + # producer2 produces v2 and has not granted P-OC to hub (or coordinator for that matter) + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1, v2 + end + end + end + + context "as manager of an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns all variants of any producer which has granted the outgoing hub P-OC" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + + # TODO: for backwards compatability, remove later + context "when an exchange exists between the coordinator and the hub within this order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + # producer2 produces v2 and has not granted P-OC to hub + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1, v2 + end + end + end + + context "as the manager of a producer which has granted P-OC to an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + context "where my producer is in the order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: producer1, receiver: coordinator, incoming: true) } + + it "returns all of my produced variants" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "where my producer isn't in the order cycle" do + # No incoming exchange + + it "does not return my variants" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to_not include v1, v2 + end + end + end + + context "as the manager of a producer which has not granted P-OC to an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer2]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns an empty array" do + expect(permissions.visible_variants_for_outgoing_exchanges_to(hub)).to eq [] + end + + # TODO: for backwards compatability, remove later + context "but which has variants already in the exchange" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + # This one won't be in the exchange, and so shouldn't be visible + let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } + + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) + expect(visible).to_not include v1, v3 + expect(visible).to include v2 + end + end + end + end + end + + describe "finding the variants within a hypothetical exchange between two enterprises which are editable by a user" do + let!(:producer1) { create(:supplier_enterprise) } + let!(:producer2) { create(:supplier_enterprise) } + let!(:v1) { create(:variant, product: create(:simple_product, supplier: producer1)) } + let!(:v2) { create(:variant, product: create(:simple_product, supplier: producer2)) } + + describe "incoming exchanges" do + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + end + + it "returns all variants belonging to the sending producer" do + visible = permissions.editable_variants_for_incoming_exchanges_from(producer1) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as a manager of the producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } + end + + it "returns all variants belonging to the sending producer" do + visible = permissions.editable_variants_for_incoming_exchanges_from(producer1) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "as a manager of a hub which has been granted P-OC by the producer" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "does not return variants produced by that producer" do + visible = permissions.editable_variants_for_incoming_exchanges_from(producer1) + expect(visible).to_not include v1, v2 + end + end + end + + describe "outgoing exchanges" do + context "as a manager of the coordinator" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns all variants of any producer which has granted the outgoing hub P-OC" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + + context "where the coordinator produces products" do + let!(:v3) { create(:variant, product: create(:simple_product, supplier: coordinator)) } + + it "returns any variants produced by the coordinator itself for exchanges with 'self'" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(coordinator) + expect(visible).to include v3 + expect(visible).to_not include v1, v2 + end + + it "does not return coordinator's variants for exchanges with other hubs, when permission has not been granted" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2, v3 + end + end + + # TODO: for backwards compatability, remove later + context "when an exchange exists between the coordinator and the hub within this order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + # producer2 produces v2 and has not granted P-OC to hub (or coordinator for that matter) + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1, v2 + end + end + end + + context "as manager of an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns all variants of any producer which has granted the outgoing hub P-OC" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + + # TODO: for backwards compatability, remove later + context "when an exchange exists between the coordinator and the hub within this order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + + # producer2 produces v2 and has not granted P-OC to hub + before { ex.variants << v2 } + + it "returns those variants that are in the exchange" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1, v2 + end + end + end + + context "as the manager of a producer which has granted P-OC to an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + context "where my producer is in the order cycle" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: producer1, receiver: coordinator, incoming: true) } + + context "where the outgoing hub has granted P-OC to my producer" do + before do + create(:enterprise_relationship, parent: hub, child: producer1, permissions_list: [:add_to_order_cycle]) + end + + it "returns all of my produced variants" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to include v1 + expect(visible).to_not include v2 + end + end + + context "where the outgoing hub has not granted P-OC to my producer" do + # No permission granted + + it "does not return my variants" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to_not include v1, v2 + end + end + end + + context "where my producer isn't in the order cycle" do + # No incoming exchange + + it "does not return my variants" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to_not include v1, v2 + end + end + end + + context "as the manager of a producer which has not granted P-OC to an outgoing hub" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer2]) } + create(:enterprise_relationship, parent: producer1, child: hub, permissions_list: [:add_to_order_cycle]) + end + + it "returns an empty array" do + expect(permissions.editable_variants_for_outgoing_exchanges_to(hub)).to eq [] + end + + # TODO: for backwards compatability, remove later + context "but which has variants already in the exchange" do + let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } + # This one won't be in the exchange, and so shouldn't be visible + let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } + + before { ex.variants << v2 } + + it "does not return my variants" do + visible = permissions.editable_variants_for_outgoing_exchanges_to(hub) + expect(visible).to_not include v1, v2, v3 + end + end + end + end + end + end +end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index ddbdd5a359..88611b857c 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -21,302 +21,6 @@ module OpenFoodNetwork end end - describe "finding enterprises that can be viewed in the order cycle interface" do - let(:coordinator) { create(:distributor_enterprise) } - let(:hub) { create(:distributor_enterprise) } - let(:producer) { create(:supplier_enterprise) } - let(:oc) { create(:simple_order_cycle, coordinator: coordinator) } - - context "when no order_cycle or coordinator are provided for reference" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } - end - - it "returns an empty scope" do - expect(permissions.order_cycle_enterprises_for(nil)).to be_empty - end - end - - context "as a manager of the coordinator" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [coordinator]) } - end - - it "returns the coordinator itself" do - expect(permissions.order_cycle_enterprises_for(oc)).to include coordinator - end - - context "where P-OC has been granted to the coordinator by other enterprises" do - before do - create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) - end - - context "where the coordinator sells any" do - it "returns enterprises which have granted P-OC to the coordinator" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include hub - expect(enterprises).to_not include producer - end - end - - context "where the coordinator sells 'own'" do - before { coordinator.stub(:sells) { 'own' } } - it "returns just the coordinator" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub, producer - end - end - end - - context "where P-OC has not been granted to the coordinator by other enterprises" do - context "where the other enterprise are already in the order cycle" do - let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } - let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } - - context "where the coordinator sells any" do - it "returns enterprises which have granted P-OC to the coordinator" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include hub, producer - end - end - - context "where the coordinator sells 'own'" do - before { coordinator.stub(:sells) { 'own' } } - it "returns just the coordinator" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub, producer - end - end - end - - context "where the other enterprises are not in the order cycle" do - it "returns just the coordinator" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub, producer - end - end - end - end - - context "as a manager of a hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [hub]) } - end - - context "that has granted P-OC to the coordinator" do - before do - create(:enterprise_relationship, parent: hub, child: coordinator, permissions_list: [:add_to_order_cycle]) - end - - context "where my hub is in the order cycle" do - let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } - - it "returns my hub" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include hub - expect(enterprises).to_not include producer, coordinator - end - - context "and has been granted P-OC by a producer" do - before do - create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) - end - - context "where the producer is in the order cycle" do - let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } - - it "returns the producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer, hub - end - end - - context "where the producer is not in the order cycle" do - # No incoming exchange - - it "does not return the producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include producer - end - end - end - - context "and has granted P-OC to a producer" do - before do - create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) - end - - context "where the producer is in the order cycle" do - let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } - - it "returns the producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer, hub - end - end - - context "where the producer is not in the order cycle" do - # No incoming exchange - - it "does not return the producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include producer - end - end - end - end - - context "where my hub is not in the order cycle" do - # No outgoing exchange for my hub - - it "does not return my hub" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub, producer, coordinator - end - end - end - - context "that has not granted P-OC to the coordinator" do - it "does not return my hub" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub, producer, coordinator - end - - context "but is already in the order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } - - it "returns my hub" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include hub - expect(enterprises).to_not include producer, coordinator - end - - context "and distributes variants distributed by an unmanaged and unpermitted producer" do - before { ex.variants << create(:variant, product: create(:product, supplier: producer)) } - - # TODO: update this when we are confident about P-OCs - it "returns that producer as well" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer, hub - expect(enterprises).to_not include coordinator - end - end - end - end - end - - context "as a manager of a producer" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } - end - - context "which has granted P-OC to the coordinator" do - before do - create(:enterprise_relationship, parent: producer, child: coordinator, permissions_list: [:add_to_order_cycle]) - end - - context "where my producer is in the order cycle" do - let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } - - it "returns my producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer - expect(enterprises).to_not include hub, coordinator - end - - context "and has been granted P-OC by a hub" do - before do - create(:enterprise_relationship, parent: hub, child: producer, permissions_list: [:add_to_order_cycle]) - end - - context "where the hub is also in the order cycle" do - let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } - - it "returns the hub as well" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer, hub - expect(enterprises).to_not include coordinator - end - end - - context "where the hub is not in the order cycle" do - # No outgoing exchange - - it "does not return the hub" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub - end - end - end - - context "and has granted P-OC to a hub" do - before do - create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:add_to_order_cycle]) - end - - context "where the hub is also in the order cycle" do - let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } - - it "returns the hub as well" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer, hub - expect(enterprises).to_not include coordinator - end - end - - context "where the hub is not in the order cycle" do - # No outgoing exchange - - it "does not return the hub" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub - end - end - end - end - - context "where my producer is not in the order cycle" do - # No incoming exchange for producer - - it "does not return my producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include hub, producer, coordinator - end - end - end - - context "which has not granted P-OC to the coordinator" do - it "does not return my producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to_not include producer - end - - context "but is already in the order cycle" do - let!(:ex_incoming) { create(:exchange, order_cycle: oc, sender: producer, receiver: coordinator, incoming: true) } - - # TODO: update this when we are confident about P-OCs - it "returns my producer" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer - expect(enterprises).to_not include hub, coordinator - end - - context "and has variants distributed by an outgoing hub" do - let!(:ex_outgoing) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: false) } - before { ex_outgoing.variants << create(:variant, product: create(:product, supplier: producer)) } - - # TODO: update this when we are confident about P-OCs - it "returns that hub as well" do - enterprises = permissions.order_cycle_enterprises_for(oc) - expect(enterprises).to include producer, hub - expect(enterprises).to_not include coordinator - end - end - end - end - end - end - describe "finding enterprises whose profiles can be edited" do let(:e) { double(:enterprise) } @@ -404,502 +108,6 @@ module OpenFoodNetwork end end - describe "finding exchanges of an order cycle that an admin can manage" do - let!(:producer) { create(:supplier_enterprise) } - let(:oc) { create(:simple_order_cycle) } - - describe "as the manager of the coordinator" do - let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: e1, incoming: true) } - let!(:ex_out) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } - end - - it "returns all exchanges in the order cycle, regardless of E2E permissions" do - permissions.order_cycle_exchanges(oc).should include ex_in, ex_out - end - end - - - describe "as the manager of a hub" do - let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: e1, incoming: true) } - - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } - end - - context "where my hub is in the order cycle" do - let!(:ex_out) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - it "returns my hub's outgoing exchange" do - permissions.order_cycle_exchanges(oc).should == [ex_out] - end - - context "where my hub has been granted P-OC by an incoming producer" do - before do - create(:enterprise_relationship, parent: producer, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns the producer's incoming exchange" do - permissions.order_cycle_exchanges(oc).should include ex_in - end - end - - context "where my hub has not been granted P-OC by an incoming producer" do - it "returns the producers's incoming exchange, and my own outhoing exchange" do - permissions.order_cycle_exchanges(oc).should_not include ex_in - end - end - end - - context "where my hub isn't in the order cycle" do - it "does not return the producer's incoming exchanges" do - permissions.order_cycle_exchanges(oc).should == [] - end - end - - # TODO: this is testing legacy behaviour for backwards compatability, remove when behaviour no longer required - describe "legacy compatability" do - context "where my hub's outgoing exchange contains variants of a producer I don't manage and has not given my hub P-OC" do - let!(:product) { create(:product, supplier: producer) } - let!(:variant) { create(:variant, product: product) } - let!(:ex_out) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: true) } - before { ex_out.variants << variant } - - it "returns incoming exchanges supplying the variants in my outgoing exchange" do - permissions.order_cycle_exchanges(oc).should include ex_out - end - end - end - end - - describe "as the manager of a producer" do - let!(:ex_out) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer]) } - end - - context "where my producer supplies to the order cycle" do - let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: e1, incoming: true) } - - it "returns my producer's incoming exchange" do - permissions.order_cycle_exchanges(oc).should == [ex_in] - end - - context "my producer has granted P-OC to an outgoing hub" do - before do - create(:enterprise_relationship, parent: producer, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns the hub's outgoing exchange" do - permissions.order_cycle_exchanges(oc).should include ex_out - end - end - - context "my producer has not granted P-OC to an outgoing hub" do - it "does not return the hub's outgoing exchange" do - permissions.order_cycle_exchanges(oc).should_not include ex_out - end - end - end - - context "where my producer doesn't supply the order cycle" do - it "does not return the hub's outgoing exchanges" do - permissions.order_cycle_exchanges(oc).should == [] - end - end - - # TODO: this is testing legacy behaviour for backwards compatability, remove when behaviour no longer required - describe "legacy compatability" do - context "where an outgoing exchange contains variants of a producer I manage" do - let!(:product) { create(:product, supplier: producer) } - let!(:variant) { create(:variant, product: product) } - before { ex_out.variants << variant } - - context "where my producer supplies to the order cycle" do - let!(:ex_in) { create(:exchange, order_cycle: oc, sender: producer, receiver: e1, incoming: true) } - - it "returns the outgoing exchange" do - permissions.order_cycle_exchanges(oc).should include ex_out - end - end - - context "where my producer doesn't supply to the order cycle" do - it "does not return the outgoing exchange" do - permissions.order_cycle_exchanges(oc).should_not include ex_out - end - end - end - end - end - end - - describe "finding the variants within a hypothetical exchange between two enterprises which are visible to a user" do - let!(:producer1) { create(:supplier_enterprise) } - let!(:producer2) { create(:supplier_enterprise) } - let!(:v1) { create(:variant, product: create(:simple_product, supplier: producer1)) } - let!(:v2) { create(:variant, product: create(:simple_product, supplier: producer2)) } - let(:oc) { create(:simple_order_cycle) } - - describe "incoming exchanges" do - context "as a manager of the coordinator" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } - end - - it "returns all variants belonging to the sending producer" do - visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "as a manager of the producer" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } - end - - it "returns all variants belonging to the sending producer" do - visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "as a manager of a hub which has been granted P-OC by the producer" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - context "where the hub is in the order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - it "returns variants produced by that producer only" do - visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "where the hub is not in the order cycle" do - # No outgoing exchange - - it "does not return variants produced by that producer" do - visible = permissions.visible_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to_not include v1, v2 - end - end - end - end - - describe "outgoing exchanges" do - context "as a manager of the coordinator" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns all variants of any producer which has granted the outgoing hub P-OC" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - - context "where the coordinator produces products" do - let!(:v3) { create(:variant, product: create(:simple_product, supplier: e1)) } - - it "returns any variants produced by the coordinator itself for exchanges with 'self'" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e1, order_cycle: oc) - expect(visible).to include v3 - expect(visible).to_not include v1, v2 - end - - it "does not return coordinator's variants for exchanges with other hubs, when permission has not been granted" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2, v3 - end - end - - # TODO: for backwards compatability, remove later - context "when an exchange exists between the coordinator and the hub within this order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - # producer2 produces v2 and has not granted P-OC to e2 (or e1 for that matter) - before { ex.variants << v2 } - - it "returns those variants that are in the exchange" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1, v2 - end - end - end - - context "as manager of an outgoing hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns all variants of any producer which has granted the outgoing hub P-OC" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - - # TODO: for backwards compatability, remove later - context "when an exchange exists between the coordinator and the hub within this order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - # producer2 produces v2 and has not granted P-OC to e2 - before { ex.variants << v2 } - - it "returns those variants that are in the exchange" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1, v2 - end - end - end - - context "as the manager of a producer which has granted P-OC to an outgoing hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - context "where my producer is in the order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: producer1, receiver: e1, incoming: true) } - - it "returns all of my produced variants" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "where my producer isn't in the order cycle" do - # No incoming exchange - - it "does not return my variants" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to_not include v1, v2 - end - end - end - - context "as the manager of a producer which has not granted P-OC to an outgoing hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer2]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns an empty array" do - expect(permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc)).to eq [] - end - - # TODO: for backwards compatability, remove later - context "but which has variants already in the exchange" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - # This one won't be in the exchange, and so shouldn't be visible - let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } - - before { ex.variants << v2 } - - it "returns those variants that are in the exchange" do - visible = permissions.visible_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to_not include v1, v3 - expect(visible).to include v2 - end - end - end - end - end - - describe "finding the variants within a hypothetical exchange between two enterprises which are editable by a user" do - let!(:producer1) { create(:supplier_enterprise) } - let!(:producer2) { create(:supplier_enterprise) } - let!(:v1) { create(:variant, product: create(:simple_product, supplier: producer1)) } - let!(:v2) { create(:variant, product: create(:simple_product, supplier: producer2)) } - let(:oc) { create(:simple_order_cycle) } - - describe "incoming exchanges" do - context "as a manager of the coordinator" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } - end - - it "returns all variants belonging to the sending producer" do - visible = permissions.editable_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "as a manager of the producer" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } - end - - it "returns all variants belonging to the sending producer" do - visible = permissions.editable_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "as a manager of a hub which has been granted P-OC by the producer" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "does not return variants produced by that producer" do - visible = permissions.editable_variants_for_incoming_exchanges_between(producer1, e1, order_cycle: oc) - expect(visible).to_not include v1, v2 - end - end - end - - describe "outgoing exchanges" do - context "as a manager of the coordinator" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e1]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns all variants of any producer which has granted the outgoing hub P-OC" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - - context "where the coordinator produces products" do - let!(:v3) { create(:variant, product: create(:simple_product, supplier: e1)) } - - it "returns any variants produced by the coordinator itself for exchanges with 'self'" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e1, order_cycle: oc) - expect(visible).to include v3 - expect(visible).to_not include v1, v2 - end - - it "does not return coordinator's variants for exchanges with other hubs, when permission has not been granted" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2, v3 - end - end - - # TODO: for backwards compatability, remove later - context "when an exchange exists between the coordinator and the hub within this order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - # producer2 produces v2 and has not granted P-OC to e2 (or e1 for that matter) - before { ex.variants << v2 } - - it "returns those variants that are in the exchange" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1, v2 - end - end - end - - context "as manager of an outgoing hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [e2]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns all variants of any producer which has granted the outgoing hub P-OC" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - - # TODO: for backwards compatability, remove later - context "when an exchange exists between the coordinator and the hub within this order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - - # producer2 produces v2 and has not granted P-OC to e2 - before { ex.variants << v2 } - - it "returns those variants that are in the exchange" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1, v2 - end - end - end - - context "as the manager of a producer which has granted P-OC to an outgoing hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer1]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - context "where my producer is in the order cycle" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: producer1, receiver: e1, incoming: true) } - - context "where the outgoing hub has granted P-OC to my producer" do - before do - create(:enterprise_relationship, parent: e2, child: producer1, permissions_list: [:add_to_order_cycle]) - end - - it "returns all of my produced variants" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to include v1 - expect(visible).to_not include v2 - end - end - - context "where the outgoing hub has not granted P-OC to my producer" do - # No permission granted - - it "does not return my variants" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to_not include v1, v2 - end - end - end - - context "where my producer isn't in the order cycle" do - # No incoming exchange - - it "does not return my variants" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to_not include v1, v2 - end - end - end - - context "as the manager of a producer which has not granted P-OC to an outgoing hub" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where(id: [producer2]) } - create(:enterprise_relationship, parent: producer1, child: e2, permissions_list: [:add_to_order_cycle]) - end - - it "returns an empty array" do - expect(permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc)).to eq [] - end - - # TODO: for backwards compatability, remove later - context "but which has variants already in the exchange" do - let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2, incoming: false) } - # This one won't be in the exchange, and so shouldn't be visible - let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } - - before { ex.variants << v2 } - - it "does not return my variants" do - visible = permissions.editable_variants_for_outgoing_exchanges_between(e1, e2, order_cycle: oc) - expect(visible).to_not include v1, v2, v3 - end - end - end - end - end - describe "finding managed products" do let!(:p1) { create(:simple_product) } let!(:p2) { create(:simple_product) } diff --git a/spec/serializers/admin/exchange_serializer_spec.rb b/spec/serializers/admin/exchange_serializer_spec.rb index 9b32c537b3..b45f6bcb9b 100644 --- a/spec/serializers/admin/exchange_serializer_spec.rb +++ b/spec/serializers/admin/exchange_serializer_spec.rb @@ -1,3 +1,5 @@ +require 'open_food_network/order_cycle_permissions' + describe Api::Admin::ExchangeSerializer do let(:v1) { create(:variant) } let(:v2) { create(:variant) } @@ -7,8 +9,8 @@ describe Api::Admin::ExchangeSerializer do before do - allow(OpenFoodNetwork::Permissions).to receive(:new) { permissions_mock } - allow(permissions_mock).to receive(:visible_variants_for_outgoing_exchanges_between) do + allow(OpenFoodNetwork::OrderCyclePermissions).to receive(:new) { permissions_mock } + allow(permissions_mock).to receive(:visible_variants_for_outgoing_exchanges_to) do # This is the permitted list of variants Spree::Variant.where(id: [v1] ) end @@ -16,8 +18,7 @@ describe Api::Admin::ExchangeSerializer do it "filters variants within the exchange based on permissions" do visible_variants = serializer.variants - expect(permissions_mock).to have_received(:visible_variants_for_outgoing_exchanges_between). - with(exchange.sender, exchange.receiver, order_cycle: exchange.order_cycle) + expect(permissions_mock).to have_received(:visible_variants_for_outgoing_exchanges_to).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