diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee index 4b416c99d8..a1ec15b6ef 100644 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycle.js.erb.coffee @@ -21,6 +21,9 @@ angular.module('order_cycle', ['ngResource']) $scope.incomingExchangesVariants = -> OrderCycle.incomingExchangesVariants() + $scope.exchangeDirection = (exchange) -> + OrderCycle.exchangeDirection(exchange) + $scope.participatingEnterprises = -> $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() @@ -82,6 +85,9 @@ angular.module('order_cycle', ['ngResource']) $scope.incomingExchangesVariants = -> OrderCycle.incomingExchangesVariants() + $scope.exchangeDirection = (exchange) -> + OrderCycle.exchangeDirection(exchange) + $scope.participatingEnterprises = -> $scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() @@ -141,6 +147,9 @@ angular.module('order_cycle', ['ngResource']) numActiveVariants++ for id, active of exchange.variants when active numActiveVariants + exchangeDirection: (exchange) -> + if this.order_cycle.incoming_exchanges.indexOf(exchange) == -1 then 'outgoing' else 'incoming' + toggleProducts: (exchange) -> exchange.showProducts = !exchange.showProducts @@ -234,6 +243,7 @@ angular.module('order_cycle', ['ngResource']) data = angular.extend({}, this.order_cycle) data = this.removeInactiveExchanges(data) data = this.translateCoordinatorFees(data) + data = this.translateExchangeFees(data) data removeInactiveExchanges: (order_cycle) -> @@ -247,6 +257,15 @@ angular.module('order_cycle', ['ngResource']) order_cycle.coordinator_fee_ids = (fee.id for fee in order_cycle.coordinator_fees) delete order_cycle.coordinator_fees order_cycle + + translateExchangeFees: (order_cycle) -> + for exchange in order_cycle.incoming_exchanges + exchange.enterprise_fee_ids = (fee.id for fee in exchange.enterprise_fees) + delete exchange.enterprise_fees + for exchange in order_cycle.outgoing_exchanges + exchange.enterprise_fee_ids = (fee.id for fee in exchange.enterprise_fees) + delete exchange.enterprise_fees + order_cycle }]) .factory('Enterprise', ['$resource', ($resource) -> diff --git a/app/assets/stylesheets/admin/openfoodweb.css.scss b/app/assets/stylesheets/admin/openfoodweb.css.scss index 747f935a1f..c700489208 100644 --- a/app/assets/stylesheets/admin/openfoodweb.css.scss +++ b/app/assets/stylesheets/admin/openfoodweb.css.scss @@ -32,6 +32,12 @@ form.order_cycle { float: left; margin-right: 3em; } + ol { + list-style-type: none; + li { + margin-bottom: 0.5em; + } + } table.exchanges { tr td.active { width: 20px; @@ -68,6 +74,7 @@ form.order_cycle { .coordinator-fees { margin-top: 1em; } + .actions { margin-top: 3em; } diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index 651785e999..924510dbfc 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -61,6 +61,10 @@ module Admin end end + protected + def collection + OrderCycle.managed_by(spree_current_user) + end private def load_order_cycle_set diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index cc85b9e69b..ab202cc50c 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -4,7 +4,7 @@ module OrderCyclesHelper end def coordinating_enterprises - Enterprise.is_distributor.order('name') + Enterprise.is_distributor.managed_by(spree_current_user).order('name') end def order_cycle_local_remote_class(distributor, order_cycle) diff --git a/app/models/exchange.rb b/app/models/exchange.rb index 362edaf102..fe7f04933c 100644 --- a/app/models/exchange.rb +++ b/app/models/exchange.rb @@ -14,4 +14,7 @@ class Exchange < ActiveRecord::Base validates_uniqueness_of :sender_id, :scope => [:order_cycle_id, :receiver_id] accepts_nested_attributes_for :variants + + scope :incoming, joins(:order_cycle).where('exchanges.receiver_id = order_cycles.coordinator_id') + scope :outgoing, joins(:order_cycle).where('exchanges.sender_id = order_cycles.coordinator_id') end diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index a6bc72ffe1..51af905060 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -20,6 +20,14 @@ class OrderCycle < ActiveRecord::Base where('spree_variants.id IN (?)', product.variants_including_master.map(&:id)). select('DISTINCT order_cycles.*') } + scope :managed_by, lambda { |user| + if user.has_spree_role?('admin') + scoped + else + where('coordinator_id IN (?)', user.enterprises.map {|enterprise| enterprise.id }) + end + } + def suppliers self.exchanges.where(:receiver_id => self.coordinator).map(&:sender).uniq end diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 67837feaea..f29b8046c7 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -23,7 +23,7 @@ class AbilityDecorator user.enterprises.include? order.distributor end - can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Payment + can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Payment can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Shipment can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Adjustment can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::ReturnAuthorization @@ -33,6 +33,13 @@ class AbilityDecorator can [:admin, :read, :update, :fire, :resend ], Spree::PaymentMethod do |payment_method| user.enterprises.include? payment_method.distributor end + + can [:admin, :index, :read, :edit, :update], OrderCycle do |order_cycle| + user.enterprises.include? order_cycle.coordinator + end + + can [:create], OrderCycle + end end end diff --git a/app/views/admin/order_cycles/_coordinator_fees.html.haml b/app/views/admin/order_cycles/_coordinator_fees.html.haml index 338fadb435..86e0d0a7af 100644 --- a/app/views/admin/order_cycles/_coordinator_fees.html.haml +++ b/app/views/admin/order_cycles/_coordinator_fees.html.haml @@ -1,5 +1,6 @@ -%table.coordinator-fees - %tr{'ng-repeat' => 'enterprise_fee in order_cycle.coordinator_fees'} - %td= select_tag 'order_cycle_coordinator_fee_{{ $index }}_id', nil, {'ng-model' => 'enterprise_fee.id', 'ng-options' => 'enterprise_fee.id as enterprise_fee.name for enterprise_fee in enterpriseFeesForEnterprise(order_cycle.coordinator_id)'} - %td= link_to 'Remove', '#', {'id' => 'order_cycle_coordinator_fee_{{ $index }}_remove', 'ng-click' => 'removeCoordinatorFee($event, $index)'} +%ol.coordinator-fees + %li{'ng-repeat' => 'enterprise_fee in order_cycle.coordinator_fees'} + = select_tag 'order_cycle_coordinator_fee_{{ $index }}_id', nil, {'ng-model' => 'enterprise_fee.id', 'ng-options' => 'enterprise_fee.id as enterprise_fee.name for enterprise_fee in enterpriseFeesForEnterprise(order_cycle.coordinator_id)'} + = link_to 'Remove', '#', {'id' => 'order_cycle_coordinator_fee_{{ $index }}_remove', 'ng-click' => 'removeCoordinatorFee($event, $index)'} + = f.submit 'Add coordinator fee', 'ng-click' => 'addCoordinatorFee($event)' diff --git a/app/views/admin/order_cycles/_exchange_form.html.haml b/app/views/admin/order_cycles/_exchange_form.html.haml index 47c24bb92a..ab72dcf5c0 100644 --- a/app/views/admin/order_cycles/_exchange_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_form.html.haml @@ -13,3 +13,13 @@ = text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', '', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', 'placeholder' => 'Pickup time / date', 'ng-model' => 'exchange.pickup_time' %br/ = text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', '', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', 'placeholder' => 'Delivery instructions', 'ng-model' => 'exchange.pickup_instructions' +%td.fees + %ol + %li{'ng-repeat' => 'enterprise_fee in exchange.enterprise_fees'} + = select_tag 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_id', nil, {'id' => 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_id', 'ng-model' => 'enterprise_fee.enterprise_id', 'ng-options' => 'enterprise.id as enterprise.name for enterprise in participatingEnterprises()'} + + = select_tag 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_fee_id', nil, {'id' => 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_enterprise_fee_id', 'ng-model' => 'enterprise_fee.id', 'ng-options' => 'enterprise_fee.id as enterprise_fee.name for enterprise_fee in enterpriseFeesForEnterprise(enterprise_fee.enterprise_id)'} + + = link_to 'Remove', '#', {'id' => 'order_cycle_{{ exchangeDirection(exchange) }}_exchange_{{ $parent.$index }}_enterprise_fees_{{ $index }}_remove', 'ng-click' => 'removeExchangeFee($event, exchange, $index)'} + + = f.submit 'Add fee', 'ng-click' => 'addExchangeFee($event, exchange)' diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index 64190c525b..0f1cac71fc 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -18,13 +18,14 @@ %th %th Supplier %th Products + %th Fees %tbody{'ng-repeat' => 'exchange in order_cycle.incoming_exchanges'} - %tr.supplier + %tr{'class' => "supplier supplier-{{ exchange.enterprise_id }}"} = render 'exchange_form', :f => f, :type => 'supplier' %tr.products{'ng-show' => 'exchange.showProducts'} = render 'exchange_supplied_products_form' -= select_tag :new_supplier_id, options_from_collection_for_select(Enterprise.is_primary_producer, :id, :name), {'ng-model' => 'new_supplier_id'} += select_tag :new_supplier_id, options_from_collection_for_select(Enterprise.is_primary_producer.managed_by(spree_current_user), :id, :name), {'ng-model' => 'new_supplier_id'} = f.submit 'Add supplier', 'ng-click' => 'addSupplier($event)' @@ -42,13 +43,14 @@ %th Distributor %th Products %th Collection details + %th Fees %tbody{'ng-repeat' => 'exchange in order_cycle.outgoing_exchanges'} - %tr.distributor + %tr{'class' => "distributor distributor-{{ exchange.enterprise_id }}"} = render 'exchange_form', :f => f, :type => 'distributor' %tr.products{'ng-show' => 'exchange.showProducts'} = render 'exchange_distributed_products_form' -= select_tag :new_distributor_id, options_from_collection_for_select(Enterprise.is_distributor, :id, :name), {'ng-model' => 'new_distributor_id'} += select_tag :new_distributor_id, options_from_collection_for_select(Enterprise.is_distributor.managed_by(spree_current_user), :id, :name), {'ng-model' => 'new_distributor_id'} = f.submit 'Add distributor', 'ng-click' => 'addDistributor($event)' .actions diff --git a/app/views/admin/order_cycles/show.rep b/app/views/admin/order_cycles/show.rep index 9876bbd419..fbf297822f 100644 --- a/app/views/admin/order_cycles/show.rep +++ b/app/views/admin/order_cycles/show.rep @@ -18,6 +18,7 @@ r.element :order_cycle, @order_cycle do r.list_of :enterprise_fees do |fee| r.element :id + r.element :enterprise_id end r.element :pickup_time diff --git a/lib/open_food_web/order_cycle_form_applicator.rb b/lib/open_food_web/order_cycle_form_applicator.rb index 8dd2867f36..6ea5b5f174 100644 --- a/lib/open_food_web/order_cycle_form_applicator.rb +++ b/lib/open_food_web/order_cycle_form_applicator.rb @@ -1,4 +1,9 @@ module OpenFoodWeb + + # There are two translator classes on the boundary between Angular and Rails: On the Angular side, + # there is the OrderCycle#dataForSubmit method, and on the Rails side is this class. I think data + # translation is more a responsibility of Angular, so I'd be inclined to refactor this class to move + # as much as possible (if not all) of its logic into Angular. class OrderCycleFormApplicator def initialize(order_cycle) @order_cycle = order_cycle @@ -10,22 +15,30 @@ module OpenFoodWeb @order_cycle.incoming_exchanges ||= [] @order_cycle.incoming_exchanges.each do |exchange| variant_ids = exchange_variant_ids(exchange) + enterprise_fee_ids = exchange[:enterprise_fee_ids] if exchange_exists?(exchange[:enterprise_id], @order_cycle.coordinator_id) - update_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, {variant_ids: variant_ids}) + update_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, + {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids}) else - add_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, {variant_ids: variant_ids}) + add_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, + {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids}) end end @order_cycle.outgoing_exchanges ||= [] @order_cycle.outgoing_exchanges.each do |exchange| variant_ids = exchange_variant_ids(exchange) + enterprise_fee_ids = exchange[:enterprise_fee_ids] if exchange_exists?(@order_cycle.coordinator_id, exchange[:enterprise_id]) - update_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], {variant_ids: variant_ids, pickup_time: exchange[:pickup_time], pickup_instructions: exchange[:pickup_instructions]}) + update_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], + {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids, + pickup_time: exchange[:pickup_time], pickup_instructions: exchange[:pickup_instructions]}) else - add_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], {variant_ids: variant_ids, pickup_time: exchange[:pickup_time], pickup_instructions: exchange[:pickup_instructions]}) + add_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], + {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids, + pickup_time: exchange[:pickup_time], pickup_instructions: exchange[:pickup_instructions]}) end end diff --git a/script-angular/e2e-test.bat b/script-angular/e2e-test.bat deleted file mode 100644 index c89a708a14..0000000000 --- a/script-angular/e2e-test.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off - -REM Windows script for running e2e tests -REM You have to run server and capture some browser first -REM -REM Requirements: -REM - NodeJS (http://nodejs.org/) -REM - Testacular (npm install -g testacular) - -set BASE_DIR=%~dp0 -testacular start "%BASE_DIR%\..\config\testacular-e2e.conf.js" %* diff --git a/script-angular/e2e-test.sh b/script-angular/e2e-test.sh deleted file mode 100755 index e887c34f41..0000000000 --- a/script-angular/e2e-test.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -BASE_DIR=`dirname $0` - -echo "" -echo "Starting Testacular Server (http://vojtajina.github.com/testacular)" -echo "-------------------------------------------------------------------" - -testacular start $BASE_DIR/../config/testacular-e2e.conf.js $* diff --git a/script-angular/update-repo.sh b/script-angular/update-repo.sh deleted file mode 100755 index 5a85bfd116..0000000000 --- a/script-angular/update-repo.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -### -# This scripts updates the local repo with the latest changes from github. -# -# The master branch will be REPLACED with what's in github and all local changes -# will be LOST. -### - -git checkout master -git fetch -f origin -git fetch --tags origin -git reset --hard origin/master diff --git a/spec/factories.rb b/spec/factories.rb index 1cdf062e9b..82a8efe333 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -9,10 +9,18 @@ FactoryGirl.define do # Suppliers ex1 = create(:exchange, :order_cycle => oc, :receiver => oc.coordinator) ex2 = create(:exchange, :order_cycle => oc, :receiver => oc.coordinator) + ExchangeFee.create!(exchange: ex1, + enterprise_fee: create(:enterprise_fee, enterprise: ex1.sender)) + ExchangeFee.create!(exchange: ex2, + enterprise_fee: create(:enterprise_fee, enterprise: ex2.sender)) # Distributors - create(:exchange, :order_cycle => oc, :sender => oc.coordinator, :pickup_time => 'time 0', :pickup_instructions => 'instructions 0') - create(:exchange, :order_cycle => oc, :sender => oc.coordinator, :pickup_time => 'time 1', :pickup_instructions => 'instructions 1') + ex3 = create(:exchange, :order_cycle => oc, :sender => oc.coordinator, :pickup_time => 'time 0', :pickup_instructions => 'instructions 0') + ex4 = create(:exchange, :order_cycle => oc, :sender => oc.coordinator, :pickup_time => 'time 1', :pickup_instructions => 'instructions 1') + ExchangeFee.create!(exchange: ex3, + enterprise_fee: create(:enterprise_fee, enterprise: ex3.receiver)) + ExchangeFee.create!(exchange: ex4, + enterprise_fee: create(:enterprise_fee, enterprise: ex4.receiver)) # Products with images [ex1, ex2].each do |exchange| diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index d9496086fd..c1bada0060 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -6,7 +6,7 @@ feature %q{ }, js: true do include AuthenticationWorkflow include WebHelper - + before :all do @orig_default_wait_time = Capybara.default_wait_time Capybara.default_wait_time = 5 @@ -50,7 +50,9 @@ feature %q{ distributor = create(:distributor_enterprise, name: 'My distributor') # And some enterprise fees + supplier_fee = create(:enterprise_fee, enterprise: supplier, name: 'Supplier fee') coordinator_fee = create(:enterprise_fee, enterprise: coordinator, name: 'Coord fee') + distributor_fee = create(:enterprise_fee, enterprise: distributor, name: 'Distributor fee') # When I go to the new order cycle page login_to_admin_section @@ -74,6 +76,11 @@ feature %q{ check 'order_cycle_incoming_exchange_0_variants_2' check 'order_cycle_incoming_exchange_0_variants_3' + # And I add a supplier fee + within("tr.supplier-#{supplier.id}") { click_button 'Add fee' } + select 'My supplier', from: 'order_cycle_incoming_exchange_0_enterprise_fees_0_enterprise_id' + select 'Supplier fee', from: 'order_cycle_incoming_exchange_0_enterprise_fees_0_enterprise_fee_id' + # And I add a distributor with the same products select 'My distributor', from: 'new_distributor_id' click_button 'Add distributor' @@ -85,6 +92,11 @@ feature %q{ check 'order_cycle_outgoing_exchange_0_variants_2' check 'order_cycle_outgoing_exchange_0_variants_3' + # And I add a distributor fee + within("tr.distributor-#{distributor.id}") { click_button 'Add fee' } + select 'My distributor', from: 'order_cycle_outgoing_exchange_0_enterprise_fees_0_enterprise_id' + select 'Distributor fee', from: 'order_cycle_outgoing_exchange_0_enterprise_fees_0_enterprise_fee_id' + # And I click Create click_button 'Create' @@ -100,8 +112,10 @@ feature %q{ page.should have_selector 'td.suppliers', text: 'My supplier' page.should have_selector 'td.distributors', text: 'My distributor' - # And it should have a coordinator fee - OrderCycle.last.coordinator_fees.should == [coordinator_fee] + # And it should have some fees + OrderCycle.last.exchanges.first.enterprise_fees.should == [supplier_fee] + OrderCycle.last.coordinator_fees.should == [coordinator_fee] + OrderCycle.last.exchanges.last.enterprise_fees.should == [distributor_fee] # And it should have some variants selected OrderCycle.last.exchanges.first.variants.count.should == 2 @@ -132,10 +146,11 @@ feature %q{ page.find('#order_cycle_coordinator_id').value.to_i.should == oc.coordinator_id page.should have_selector "select[name='order_cycle_coordinator_fee_0_id']" - # And I should see the suppliers with products + # And I should see the suppliers page.should have_selector 'td.supplier_name', :text => oc.suppliers.first.name page.should have_selector 'td.supplier_name', :text => oc.suppliers.last.name + # And the suppliers should have products page.all('table.exchanges tbody tr.supplier').each do |row| row.find('td.products input').click @@ -145,7 +160,18 @@ feature %q{ row.find('td.products input').click end - # And I should see the distributors with products + # And the suppliers should have fees + page.find('#order_cycle_incoming_exchange_0_enterprise_fees_0_enterprise_id option[selected=selected]'). + text.should == oc.suppliers.first.name + page.find('#order_cycle_incoming_exchange_0_enterprise_fees_0_enterprise_fee_id option[selected=selected]'). + text.should == oc.suppliers.first.enterprise_fees.first.name + + page.find('#order_cycle_incoming_exchange_1_enterprise_fees_0_enterprise_id option[selected=selected]'). + text.should == oc.suppliers.last.name + page.find('#order_cycle_incoming_exchange_1_enterprise_fees_0_enterprise_fee_id option[selected=selected]'). + text.should == oc.suppliers.last.enterprise_fees.first.name + + # And I should see the distributors page.should have_selector 'td.distributor_name', :text => oc.distributors.first.name page.should have_selector 'td.distributor_name', :text => oc.distributors.last.name @@ -154,6 +180,7 @@ feature %q{ page.find('#order_cycle_outgoing_exchange_1_pickup_time').value.should == 'time 1' page.find('#order_cycle_outgoing_exchange_1_pickup_instructions').value.should == 'instructions 1' + # And the distributors should have products page.all('table.exchanges tbody tr.distributor').each do |row| row.find('td.products input').click @@ -162,6 +189,17 @@ feature %q{ row.find('td.products input').click end + + # And the distributors should have fees + page.find('#order_cycle_outgoing_exchange_0_enterprise_fees_0_enterprise_id option[selected=selected]'). + text.should == oc.distributors.first.name + page.find('#order_cycle_outgoing_exchange_0_enterprise_fees_0_enterprise_fee_id option[selected=selected]'). + text.should == oc.distributors.first.enterprise_fees.first.name + + page.find('#order_cycle_outgoing_exchange_1_enterprise_fees_0_enterprise_id option[selected=selected]'). + text.should == oc.distributors.last.name + page.find('#order_cycle_outgoing_exchange_1_enterprise_fees_0_enterprise_fee_id option[selected=selected]'). + text.should == oc.distributors.last.enterprise_fees.first.name end @@ -178,8 +216,12 @@ feature %q{ v2 = create(:variant, product: product) # And some enterprise fees + supplier_fee1 = create(:enterprise_fee, enterprise: supplier, name: 'Supplier fee 1') + supplier_fee2 = create(:enterprise_fee, enterprise: supplier, name: 'Supplier fee 2') coordinator_fee1 = create(:enterprise_fee, enterprise: coordinator, name: 'Coord fee 1') coordinator_fee2 = create(:enterprise_fee, enterprise: coordinator, name: 'Coord fee 2') + distributor_fee1 = create(:enterprise_fee, enterprise: distributor, name: 'Distributor fee 1') + distributor_fee2 = create(:enterprise_fee, enterprise: distributor, name: 'Distributor fee 2') # When I go to its edit page login_to_admin_section @@ -210,6 +252,16 @@ feature %q{ check "order_cycle_incoming_exchange_2_variants_#{v1.id}" check "order_cycle_incoming_exchange_2_variants_#{v2.id}" + # And I configure some supplier fees + within("tr.supplier-#{supplier.id}") { click_button 'Add fee' } + select 'My supplier', from: 'order_cycle_incoming_exchange_2_enterprise_fees_0_enterprise_id' + select 'Supplier fee 1', from: 'order_cycle_incoming_exchange_2_enterprise_fees_0_enterprise_fee_id' + within("tr.supplier-#{supplier.id}") { click_button 'Add fee' } + within("tr.supplier-#{supplier.id}") { click_button 'Add fee' } + click_link 'order_cycle_incoming_exchange_2_enterprise_fees_0_remove' + select 'My supplier', from: 'order_cycle_incoming_exchange_2_enterprise_fees_0_enterprise_id' + select 'Supplier fee 2', from: 'order_cycle_incoming_exchange_2_enterprise_fees_0_enterprise_fee_id' + # And I add a distributor and some products select 'My distributor', from: 'new_distributor_id' click_button 'Add distributor' @@ -224,6 +276,16 @@ feature %q{ uncheck "order_cycle_outgoing_exchange_2_variants_#{v1.id}" check "order_cycle_outgoing_exchange_2_variants_#{v2.id}" + # And I configure some distributor fees + within("tr.distributor-#{distributor.id}") { click_button 'Add fee' } + select 'My distributor', from: 'order_cycle_outgoing_exchange_2_enterprise_fees_0_enterprise_id' + select 'Distributor fee 1', from: 'order_cycle_outgoing_exchange_2_enterprise_fees_0_enterprise_fee_id' + within("tr.distributor-#{distributor.id}") { click_button 'Add fee' } + within("tr.distributor-#{distributor.id}") { click_button 'Add fee' } + click_link 'order_cycle_outgoing_exchange_2_enterprise_fees_0_remove' + select 'My distributor', from: 'order_cycle_outgoing_exchange_2_enterprise_fees_0_enterprise_id' + select 'Distributor fee 2', from: 'order_cycle_outgoing_exchange_2_enterprise_fees_0_enterprise_fee_id' + # And I click Update click_button 'Update' @@ -242,6 +304,12 @@ feature %q{ # And my coordinator fees should have been configured OrderCycle.last.coordinator_fee_ids.sort.should == [coordinator_fee1.id, coordinator_fee2.id].sort + # And my supplier fees should have been configured + OrderCycle.last.exchanges.incoming.last.enterprise_fee_ids.should == [supplier_fee2.id] + + # And my distributor fees should have been configured + OrderCycle.last.exchanges.outgoing.last.enterprise_fee_ids.should == [distributor_fee2.id] + # And it should have some variants selected OrderCycle.last.variants.map { |v| v.id }.sort.should == [1, v1.id, v2.id].sort @@ -274,4 +342,71 @@ feature %q{ OrderCycle.all.map { |oc| oc.orders_close_at.sec }.should == [1, 3, 5] end + context 'as an Enterprise user' do + + let(:supplier1) { create(:supplier_enterprise, name: 'First Supplier') } + let(:supplier2) { create(:supplier_enterprise, name: 'Another Supplier') } + let(:distributor1) { create(:distributor_enterprise, name: 'First Distributor') } + let(:distributor2) { create(:distributor_enterprise, name: 'Another Distributor') } + + before(:each) do + product = create(:product, supplier: supplier1) + product.distributors << distributor1 + product.save! + + @new_user = create_enterprise_user + @new_user.enterprise_roles.build(enterprise: supplier1).save + @new_user.enterprise_roles.build(enterprise: distributor1).save + + login_to_admin_as @new_user + end + + scenario "can view products I am coordinating" do + oc_user_coordinating = create(:simple_order_cycle, { coordinator: supplier1, name: 'Order Cycle 1' } ) + oc_for_other_user = create(:simple_order_cycle, { coordinator: supplier2, name: 'Order Cycle 2' } ) + + click_link "Order Cycles" + + page.should have_content oc_user_coordinating.name + page.should_not have_content oc_for_other_user.name + end + + scenario "can create a new order cycle" do + click_link "Order Cycles" + click_link 'New Order Cycle' + + fill_in 'order_cycle_name', with: 'My order cycle' + fill_in 'order_cycle_orders_open_at', with: '2012-11-06 06:00:00' + fill_in 'order_cycle_orders_close_at', with: '2012-11-13 17:00:00' + + select 'First Supplier', from: 'new_supplier_id' + click_button 'Add supplier' + + select 'First Distributor', from: 'order_cycle_coordinator_id' + + select 'First Distributor', from: 'new_distributor_id' + click_button 'Add distributor' + + # Should only have suppliers / distributors listed which the user can manage + within "#new_supplier_id" do + page.should_not have_content supplier2.name + end + within "#new_distributor_id" do + page.should_not have_content distributor2.name + end + within "#order_cycle_coordinator_id" do + page.should_not have_content distributor2.name + page.should_not have_content supplier1.name + page.should_not have_content supplier2.name + end + + click_button 'Create' + + flash_message.should == "Your order cycle has been created." + order_cycle = OrderCycle.find_by_name('My order cycle') + order_cycle.coordinator.should == distributor1 + end + + end + end diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index 7e2f040a97..e466396dde 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -17,6 +17,7 @@ describe 'OrderCycle controllers', -> exchangeSelectedVariants: jasmine.createSpy('exchangeSelectedVariants').andReturn('variants selected') productSuppliedToOrderCycle: jasmine.createSpy('productSuppliedToOrderCycle').andReturn('product supplied') variantSuppliedToOrderCycle: jasmine.createSpy('variantSuppliedToOrderCycle').andReturn('variant supplied') + exchangeDirection: jasmine.createSpy('exchangeDirection').andReturn('exchange direction') toggleProducts: jasmine.createSpy('toggleProducts') addSupplier: jasmine.createSpy('addSupplier') addDistributor: jasmine.createSpy('addDistributor') @@ -66,6 +67,10 @@ describe 'OrderCycle controllers', -> expect(scope.variantSuppliedToOrderCycle('variant')).toEqual('variant supplied') expect(OrderCycle.variantSuppliedToOrderCycle).toHaveBeenCalledWith('variant') + it 'Delegates exchangeDirection to OrderCycle', -> + expect(scope.exchangeDirection('exchange')).toEqual('exchange direction') + expect(OrderCycle.exchangeDirection).toHaveBeenCalledWith('exchange') + it 'Finds enterprises participating in the order cycle', -> scope.enterprises = 1: {id: 1, name: 'Eaterprises'} @@ -141,6 +146,7 @@ describe 'OrderCycle controllers', -> exchangeSelectedVariants: jasmine.createSpy('exchangeSelectedVariants').andReturn('variants selected') productSuppliedToOrderCycle: jasmine.createSpy('productSuppliedToOrderCycle').andReturn('product supplied') variantSuppliedToOrderCycle: jasmine.createSpy('variantSuppliedToOrderCycle').andReturn('variant supplied') + exchangeDirection: jasmine.createSpy('exchangeDirection').andReturn('exchange direction') toggleProducts: jasmine.createSpy('toggleProducts') addSupplier: jasmine.createSpy('addSupplier') addDistributor: jasmine.createSpy('addDistributor') @@ -189,6 +195,10 @@ describe 'OrderCycle controllers', -> expect(scope.variantSuppliedToOrderCycle('variant')).toEqual('variant supplied') expect(OrderCycle.variantSuppliedToOrderCycle).toHaveBeenCalledWith('variant') + it 'Delegates exchangeDirection to OrderCycle', -> + expect(scope.exchangeDirection('exchange')).toEqual('exchange direction') + expect(OrderCycle.exchangeDirection).toHaveBeenCalledWith('exchange') + it 'Finds enterprises participating in the order cycle', -> scope.enterprises = 1: {id: 1, name: 'Eaterprises'} @@ -350,6 +360,19 @@ describe 'OrderCycle services', -> result = OrderCycle.exchangeSelectedVariants({variants: {1: true, 2: false, 3: true}}) expect(result).toEqual(2) + describe 'fetching the direction for an exchange', -> + it 'returns "incoming" for incoming exchanges', -> + exchange = {id: 1} + OrderCycle.order_cycle.incoming_exchanges = [exchange] + OrderCycle.order_cycle.outgoing_exchanges = [] + expect(OrderCycle.exchangeDirection(exchange)).toEqual 'incoming' + + it 'returns "outgoing" for outgoing exchanges', -> + exchange = {id: 1} + OrderCycle.order_cycle.incoming_exchanges = [] + OrderCycle.order_cycle.outgoing_exchanges = [exchange] + expect(OrderCycle.exchangeDirection(exchange)).toEqual 'outgoing' + describe 'toggling products', -> exchange = null @@ -568,9 +591,11 @@ describe 'OrderCycle services', -> OrderCycle.order_cycle = {foo: 'bar'} spyOn(OrderCycle, 'removeInactiveExchanges') spyOn(OrderCycle, 'translateCoordinatorFees') + spyOn(OrderCycle, 'translateExchangeFees') OrderCycle.dataForSubmit() expect(OrderCycle.removeInactiveExchanges).toHaveBeenCalled() expect(OrderCycle.translateCoordinatorFees).toHaveBeenCalled() + expect(OrderCycle.translateExchangeFees).toHaveBeenCalled() it 'removes inactive exchanges', -> data = @@ -605,5 +630,26 @@ describe 'OrderCycle services', -> data = OrderCycle.translateCoordinatorFees(data) expect(data.coordinator_fees).toBeUndefined() - expect(data.coordinator_fee_ids).toEqual([1, 2]) - \ No newline at end of file + expect(data.coordinator_fee_ids).toEqual [1, 2] + + it 'converts exchange fees into a list of ids', -> + data = + incoming_exchanges: [ + enterprise_fees: [ + {id: 1} + {id: 2} + ] + ] + outgoing_exchanges: [ + enterprise_fees: [ + {id: 3} + {id: 4} + ] + ] + + data = OrderCycle.translateExchangeFees(data) + + expect(data.incoming_exchanges[0].enterprise_fees).toBeUndefined() + expect(data.outgoing_exchanges[0].enterprise_fees).toBeUndefined() + expect(data.incoming_exchanges[0].enterprise_fee_ids).toEqual [1, 2] + expect(data.outgoing_exchanges[0].enterprise_fee_ids).toEqual [3, 4] \ No newline at end of file diff --git a/spec/lib/open_food_web/order_cycle_form_applicator_spec.rb b/spec/lib/open_food_web/order_cycle_form_applicator_spec.rb index 18b64986bf..d5bf56b34c 100644 --- a/spec/lib/open_food_web/order_cycle_form_applicator_spec.rb +++ b/spec/lib/open_food_web/order_cycle_form_applicator_spec.rb @@ -7,7 +7,7 @@ module OpenFoodWeb coordinator_id = 123 supplier_id = 456 - incoming_exchange = {:enterprise_id => supplier_id, :variants => {'1' => true, '2' => false, '3' => true}} + incoming_exchange = {:enterprise_id => supplier_id, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2]} oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [incoming_exchange], :outgoing_exchanges => []) @@ -15,7 +15,7 @@ module OpenFoodWeb applicator.should_receive(:exchange_variant_ids).with(incoming_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id).and_return(false) - applicator.should_receive(:add_exchange).with(supplier_id, coordinator_id, {:variant_ids => [1, 3]}) + applicator.should_receive(:add_exchange).with(supplier_id, coordinator_id, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2]}) applicator.should_receive(:destroy_untouched_exchanges) applicator.go! @@ -25,7 +25,7 @@ module OpenFoodWeb coordinator_id = 123 distributor_id = 456 - outgoing_exchange = {:enterprise_id => distributor_id, :variants => {'1' => true, '2' => false, '3' => true}, :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'} + outgoing_exchange = {:enterprise_id => distributor_id, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'} oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [], :outgoing_exchanges => [outgoing_exchange]) @@ -33,7 +33,7 @@ module OpenFoodWeb applicator.should_receive(:exchange_variant_ids).with(outgoing_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(coordinator_id, distributor_id).and_return(false) - applicator.should_receive(:add_exchange).with(coordinator_id, distributor_id, {:variant_ids => [1, 3], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'}) + applicator.should_receive(:add_exchange).with(coordinator_id, distributor_id, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'}) applicator.should_receive(:destroy_untouched_exchanges) applicator.go! @@ -43,7 +43,7 @@ module OpenFoodWeb coordinator_id = 123 supplier_id = 456 - incoming_exchange = {:enterprise_id => supplier_id, :variants => {'1' => true, '2' => false, '3' => true}} + incoming_exchange = {:enterprise_id => supplier_id, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2]} oc = double(:order_cycle, :coordinator_id => coordinator_id, @@ -55,7 +55,7 @@ module OpenFoodWeb applicator.should_receive(:exchange_variant_ids).with(incoming_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id).and_return(true) - applicator.should_receive(:update_exchange).with(supplier_id, coordinator_id, {:variant_ids => [1, 3]}) + applicator.should_receive(:update_exchange).with(supplier_id, coordinator_id, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2]}) applicator.should_receive(:destroy_untouched_exchanges) applicator.go! @@ -65,7 +65,7 @@ module OpenFoodWeb coordinator_id = 123 distributor_id = 456 - outgoing_exchange = {:enterprise_id => distributor_id, :variants => {'1' => true, '2' => false, '3' => true}, :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'} + outgoing_exchange = {:enterprise_id => distributor_id, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'} oc = double(:order_cycle, :coordinator_id => coordinator_id, @@ -77,7 +77,7 @@ module OpenFoodWeb applicator.should_receive(:exchange_variant_ids).with(outgoing_exchange).and_return([1, 3]) applicator.should_receive(:exchange_exists?).with(coordinator_id, distributor_id).and_return(true) - applicator.should_receive(:update_exchange).with(coordinator_id, distributor_id, {:variant_ids => [1, 3], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'}) + applicator.should_receive(:update_exchange).with(coordinator_id, distributor_id, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'}) applicator.should_receive(:destroy_untouched_exchanges) applicator.go! @@ -133,14 +133,17 @@ module OpenFoodWeb receiver = FactoryGirl.create(:enterprise) variant1 = FactoryGirl.create(:variant) variant2 = FactoryGirl.create(:variant) + enterprise_fee1 = FactoryGirl.create(:enterprise_fee) + enterprise_fee2 = FactoryGirl.create(:enterprise_fee) applicator.send(:touched_exchanges=, []) - applicator.send(:add_exchange, sender.id, receiver.id, {:variant_ids => [variant1.id, variant2.id]}) + applicator.send(:add_exchange, sender.id, receiver.id, {:variant_ids => [variant1.id, variant2.id], :enterprise_fee_ids => [enterprise_fee1.id, enterprise_fee2.id]}) exchange = Exchange.last exchange.sender.should == sender exchange.receiver.should == receiver exchange.variants.sort.should == [variant1, variant2].sort + exchange.enterprise_fees.sort.should == [enterprise_fee1, enterprise_fee2].sort applicator.send(:touched_exchanges).should == [exchange] end @@ -153,14 +156,18 @@ module OpenFoodWeb variant1 = FactoryGirl.create(:variant) variant2 = FactoryGirl.create(:variant) variant3 = FactoryGirl.create(:variant) + enterprise_fee1 = FactoryGirl.create(:enterprise_fee) + enterprise_fee2 = FactoryGirl.create(:enterprise_fee) + enterprise_fee3 = FactoryGirl.create(:enterprise_fee) - exchange = FactoryGirl.create(:exchange, order_cycle: oc, sender: sender, receiver: receiver, variant_ids: [variant1.id, variant2.id]) + exchange = FactoryGirl.create(:exchange, order_cycle: oc, sender: sender, receiver: receiver, variant_ids: [variant1.id, variant2.id], enterprise_fee_ids: [enterprise_fee1.id, enterprise_fee2.id]) applicator.send(:touched_exchanges=, []) - applicator.send(:update_exchange, sender.id, receiver.id, {:variant_ids => [variant1.id, variant3.id]}) + applicator.send(:update_exchange, sender.id, receiver.id, {:variant_ids => [variant1.id, variant3.id], :enterprise_fee_ids => [enterprise_fee2.id, enterprise_fee3.id]}) exchange.reload - exchange.variant_ids.sort.should == [variant1.id, variant3.id].sort + exchange.variants.sort.should == [variant1, variant3].sort + exchange.enterprise_fees.sort.should == [enterprise_fee2, enterprise_fee3] applicator.send(:touched_exchanges).should == [exchange] end end diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index b757eb744e..9007ff8a2a 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -13,22 +13,10 @@ module Spree let(:s2) { create(:supplier_enterprise) } let(:d1) { create(:distributor_enterprise) } let(:d2) { create(:distributor_enterprise) } - # create product for each enterprise + let(:p1) { create(:product, supplier: s1, distributors:[d1, d2]) } let(:p2) { create(:product, supplier: s2, distributors:[d1, d2]) } - # create order for each enterprise - let(:o1) do - o = create(:order, distributor: d1, bill_address: create(:address)) - create(:line_item, order: o, product: p1) - o - end - let(:o2) do - o = create(:order, distributor: d2, bill_address: create(:address)) - create(:line_item, order: o, product: p1) - o - end - subject { user } let(:user){ nil } @@ -55,7 +43,7 @@ module Spree should have_ability(:create, for: Spree::Product) end - it "should be able to read/write their enterprises' product variants" do + it "should be able to read/write their enterprises' product variants" do should have_ability([:admin, :index, :read, :create, :edit], for: Spree::Variant) end @@ -66,7 +54,7 @@ module Spree it "should be able to read/write their enterprises' product images" do should have_ability([:admin, :index, :read, :create, :edit], for: Spree::Image) end - + it "should be able to read Taxons (in order to create classifications)" do should have_ability([:admin, :index, :read, :search], for: Spree::Taxon) end @@ -74,6 +62,7 @@ module Spree it "should be able to read/write Classifications on a product" do should have_ability([:admin, :index, :read, :create, :edit], for: Spree::Classification) end + end context "when is a distributor enterprise user" do @@ -84,13 +73,24 @@ module Spree d1.enterprise_roles.build(user: user).save user end + # create order for each enterprise + let(:o1) do + o = create(:order, distributor: d1, bill_address: create(:address)) + create(:line_item, order: o, product: p1) + o + end + let(:o2) do + o = create(:order, distributor: d2, bill_address: create(:address)) + create(:line_item, order: o, product: p1) + o + end it "should be able to read/write their enterprises' orders" do - should have_ability([:admin, :index, :read, :edit], for: o1) + should have_ability([:admin, :index, :read, :edit], for: o1) end it "should not be able to read/write other enterprises' orders" do - should_not have_ability([:admin, :index, :read, :edit], for: o2) + should_not have_ability([:admin, :index, :read, :edit], for: o2) end it "should be able to create a new order" do @@ -114,6 +114,29 @@ module Spree end end + context 'Order Cycle co-ordinator' do + + let (:user) do + user = create(:user) + user.spree_roles = [] + s1.enterprise_roles.build(user: user).save + user + end + let(:oc1) { create(:simple_order_cycle, {coordinator: s1}) } + let(:oc2) { create(:simple_order_cycle) } + + it "should be able to read/write OrderCycles they are the co-ordinator of" do + should have_ability([:admin, :index, :read, :edit], for: oc1) + end + + it "should not be able to read/write OrderCycles they are not the co-ordinator of" do + should_not have_ability([:admin, :index, :read, :create, :edit], for: oc2) + end + + it "should be able to create OrderCycles" do + should have_ability([:create], for: OrderCycle) + end + end end end end \ No newline at end of file diff --git a/spec/models/exchange_spec.rb b/spec/models/exchange_spec.rb index 0b87d69c50..523ee23e5e 100644 --- a/spec/models/exchange_spec.rb +++ b/spec/models/exchange_spec.rb @@ -43,4 +43,22 @@ describe Exchange do e.exchange_fees.create(:enterprise_fee => f) e.enterprise_fees.count.should == 1 end + + describe "scopes" do + let(:supplier) { create(:supplier_enterprise) } + let(:coordinator) { create(:distributor_enterprise) } + let(:distributor) { create(:distributor_enterprise) } + let(:oc) { create(:simple_order_cycle, coordinator: coordinator) } + + let!(:incoming_exchange) { oc.exchanges.create! sender: supplier, receiver: coordinator } + let!(:outgoing_exchange) { oc.exchanges.create! sender: coordinator, receiver: distributor } + + it "finds incoming exchanges" do + Exchange.incoming.should == [incoming_exchange] + end + + it "finds outgoing exchanges" do + Exchange.outgoing.should == [outgoing_exchange] + end + end end