diff --git a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee index 4e8d462a35..98b685766e 100644 --- a/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/order_cycle.js.coffee @@ -163,6 +163,7 @@ angular.module('admin.order_cycles').factory('OrderCycle', ($resource, $window) stripNonSubmittableAttributes: (order_cycle) -> delete order_cycle.id delete order_cycle.editable_variants_for_incoming_exchanges + delete order_cycle.editable_variants_for_outgoing_exchanges delete order_cycle.visible_variants_for_outgoing_exchanges order_cycle diff --git a/app/serializers/api/admin/order_cycle_serializer.rb b/app/serializers/api/admin/order_cycle_serializer.rb index 6ad7b6b175..53976ee09a 100644 --- a/app/serializers/api/admin/order_cycle_serializer.rb +++ b/app/serializers/api/admin/order_cycle_serializer.rb @@ -1,6 +1,6 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer attributes :id, :name, :orders_open_at, :orders_close_at, :coordinator_id, :exchanges - attributes :editable_variants_for_incoming_exchanges + attributes :editable_variants_for_incoming_exchanges, :editable_variants_for_outgoing_exchanges attributes :visible_variants_for_outgoing_exchanges has_many :coordinator_fees, serializer: Api::IdSerializer @@ -31,6 +31,19 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer editable end + def editable_variants_for_outgoing_exchanges + # 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(order_cycle: object) + enterprises.each do |enterprise| + variants = permissions.editable_variants_for_outgoing_exchanges_between(object.coordinator, enterprise, order_cycle: object).pluck(:id) + editable[enterprise.id] = variants if variants.any? + end + editable + end + def visible_variants_for_outgoing_exchanges # 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 diff --git a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml index efdf3bdf35..5197242d8a 100644 --- a/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_distributed_products_form.html.haml @@ -9,12 +9,14 @@ .exchange-product-details .supplier {{ product.supplier_name }} %label - = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-disabled' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}' + = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', + 'ng-disabled' => 'product.variants.length > 0 || !order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0' %img{'ng-src' => '{{ product.image_url }}'} {{ product.name }} -# if we ever need to filter variants within a product using visibility permissions, we can use this filter: visibleVariants:exchange:order_cycle.visible_variants_for_outgoing_exchanges .exchange-product-variant{'ng-repeat' => 'variant in product.variants | filter:variantSuppliedToOrderCycle'} %label - = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}' + = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', 1, 1, 'ng-model' => 'exchange.variants[variant.id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}', + 'ng-disabled' => '!order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' {{ variant.label }} diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index d5d63799dc..2d6a854d5c 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -637,18 +637,32 @@ feature %q{ end context "that is a manager of a participating producer" do - before do - @new_user = create_enterprise_user - @new_user.enterprise_roles.build(enterprise: supplier_managed).save + let(:new_user) { create_enterprise_user } - login_to_admin_as @new_user + before do + new_user.enterprise_roles.build(enterprise: supplier_managed).save + login_to_admin_as new_user end scenario "editing an order cycle" do oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: distributor_managed, distributors: [distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) v1 = create(:variant, product: create(:product, supplier: supplier_managed) ) - ex = oc.exchanges.where(sender_id: distributor_managed, receiver_id: distributor_unmanaged, incoming: false).first - ex.update_attributes(variant_ids: [v1.id]) + v2 = create(:variant, product: create(:product, supplier: supplier_managed) ) + + # Incoming exchange + ex_in = oc.exchanges.where(sender_id: supplier_managed, receiver_id: distributor_managed, incoming: true).first + ex_in.update_attributes(variant_ids: [v1.id, v2.id]) + + # Outgoing exchange + ex_out = oc.exchanges.where(sender_id: distributor_managed, receiver_id: distributor_managed, incoming: false).first + ex_out.update_attributes(variant_ids: [v1.id, v2.id]) + + # Stub editable_variants_for_outgoing_exchanges method so we can test permissions + serializer = Api::Admin::OrderCycleSerializer.new(oc, current_user: new_user) + allow(Api::Admin::OrderCycleSerializer).to receive(:new) { serializer } + allow(serializer).to receive(:editable_variants_for_outgoing_exchanges) do + { "#{distributor_managed.id}" => [v1.id] } + end visit edit_admin_order_cycle_path(oc) @@ -656,7 +670,18 @@ feature %q{ # distributor_managed and distributor_permitted (who I have given permission to) AND # and distributor_unmanaged (who distributes my products) page.all('tr.supplier').count.should == 1 - page.all('tr.distributor').count.should == 3 + page.all('tr.distributor').count.should == 2 + + # Open the products list for managed_supplier's incoming exchange + within "tr.distributor-#{distributor_managed.id}" do + page.find("td.products input").click + end + + # I should be able to see and toggle v1 + expect(page).to have_field "order_cycle_outgoing_exchange_0_variants_#{v1.id}", disabled: false + + # I should be able to see but not toggle v2, because I don't have permission + expect(page).to have_field "order_cycle_outgoing_exchange_0_variants_#{v2.id}", disabled: true # When I save, any exchanges that I can't manage remain click_button 'Update' @@ -687,7 +712,7 @@ feature %q{ ex = oc.exchanges.where(sender_id: distributor_managed, receiver_id: my_distributor, incoming: false).first ex.update_attributes(variant_ids: [v1.id, v2.id]) - # # Stub editable_variants_for_incoming_exchanges method so we can test permissions + # Stub editable_variants_for_incoming_exchanges method so we can test permissions serializer = Api::Admin::OrderCycleSerializer.new(oc, current_user: new_user) allow(Api::Admin::OrderCycleSerializer).to receive(:new) { serializer } allow(serializer).to receive(:editable_variants_for_incoming_exchanges) do