Merging with master

This commit is contained in:
Will Marshall
2014-04-23 14:43:39 +10:00
18 changed files with 260 additions and 65 deletions

View File

@@ -73,28 +73,27 @@ orderManagementModule.factory "pendingChanges",[
pendingChanges: {}
add: (id, attrName, changeObj) ->
this.pendingChanges["#{id}"] = {} unless this.pendingChanges.hasOwnProperty("#{id}")
this.pendingChanges["#{id}"]["#{attrName}"] = changeObj
@pendingChanges["#{id}"] = {} unless @pendingChanges.hasOwnProperty("#{id}")
@pendingChanges["#{id}"]["#{attrName}"] = changeObj
removeAll: ->
this.pendingChanges = {}
@pendingChanges = {}
remove: (id, attrName) ->
if this.pendingChanges.hasOwnProperty("#{id}")
delete this.pendingChanges["#{id}"]["#{attrName}"]
delete this.pendingChanges["#{id}"] if this.changeCount( this.pendingChanges["#{id}"] ) < 1
if @pendingChanges.hasOwnProperty("#{id}")
delete @pendingChanges["#{id}"]["#{attrName}"]
delete @pendingChanges["#{id}"] if @changeCount( @pendingChanges["#{id}"] ) < 1
submitAll: ->
all = []
for id,lineItem of this.pendingChanges
for id,lineItem of @pendingChanges
for attrName,changeObj of lineItem
all.push this.submit(id, attrName, changeObj)
all.push @submit(id, attrName, changeObj)
all
submit: (id, attrName, change) ->
factory = this
dataSubmitter(change).then (data) ->
factory.remove id, attrName
dataSubmitter(change).then (data) =>
@remove id, attrName
change.element.dbValue = data["#{attrName}"]
changeCount: (lineItem) ->
@@ -144,13 +143,13 @@ orderManagementModule.controller "AdminOrderMgmtCtrl", [
$scope.spree_api_key_ok = data.hasOwnProperty("success") and data["success"] == "Use of API Authorised"
if $scope.spree_api_key_ok
$http.defaults.headers.common["X-Spree-Token"] = spree_api_key
dataFetcher("/api/enterprises/managed?template=bulk_index&q[is_primary_producer_eq]=true").then (data) ->
dataFetcher("/api/enterprises/accessible?template=bulk_index&q[is_primary_producer_eq]=true").then (data) ->
$scope.suppliers = data
$scope.suppliers.unshift blankOption()
dataFetcher("/api/enterprises/managed?template=bulk_index&q[is_distributor_eq]=true").then (data) ->
dataFetcher("/api/enterprises/accessible?template=bulk_index&q[is_distributor_eq]=true").then (data) ->
$scope.distributors = data
$scope.distributors.unshift blankOption()
ocFetcher = dataFetcher("/api/order_cycles/managed").then (data) ->
ocFetcher = dataFetcher("/api/order_cycles/accessible").then (data) ->
$scope.orderCycles = data
$scope.orderCycles.unshift blankOption()
$scope.fetchOrders()
@@ -163,7 +162,7 @@ orderManagementModule.controller "AdminOrderMgmtCtrl", [
$scope.fetchOrders = ->
$scope.loading = true
dataFetcher("/api/orders/managed?template=bulk_index&q[completed_at_not_null]=true&q[completed_at_gt]=#{$scope.startDate}&q[completed_at_lt]=#{$scope.endDate}").then (data) ->
dataFetcher("/api/orders/managed?template=bulk_index;page=1;per_page=500;q[completed_at_not_null]=true;q[completed_at_gt]=#{$scope.startDate};q[completed_at_lt]=#{$scope.endDate}").then (data) ->
$scope.resetOrders data
$scope.loading = false

View File

@@ -4,7 +4,12 @@ module Api
def managed
@enterprises = Enterprise.ransack(params[:q]).result.managed_by(current_api_user)
respond_with(@enterprises)
render params[:template] || :bulk_index
end
def accessible
@enterprises = Enterprise.ransack(params[:q]).result.accessible_by(current_api_user)
render params[:template] || :bulk_index
end
end
end

View File

@@ -3,7 +3,12 @@ module Api
respond_to :json
def managed
@order_cycles = OrderCycle.ransack(params[:q]).result.managed_by(current_api_user)
render :bulk_index
render params[:template] || :bulk_index
end
def accessible
@order_cycles = OrderCycle.ransack(params[:q]).result.accessible_by(current_api_user)
render params[:template] || :bulk_index
end
end
end

View File

@@ -0,0 +1,13 @@
module Spree
module Admin
class OverviewController < Spree::Admin::BaseController
def index
if current_spree_user.admin? || current_spree_user.enterprises.any?{ |e| e.is_distributor? }
redirect_to admin_orders_path
elsif current_spree_user.enterprises.any?{ |e| e.is_primary_producer? }
redirect_to bulk_edit_admin_products_path
end
end
end
end
end

View File

@@ -7,7 +7,7 @@ Spree::Api::OrdersController.class_eval do
before_filter :authorize_read!, :except => [:managed]
def managed
@orders = Spree::Order.ransack(params[:q]).result.managed_by(current_api_user).page(params[:page]).per(params[:per_page])
@orders = Spree::Order.ransack(params[:q]).result.distributed_by_user(current_api_user).page(params[:page]).per(params[:per_page])
respond_with(@orders, default_template: :index)
end
end

View File

@@ -47,24 +47,26 @@ class Enterprise < ActiveRecord::Base
scope :with_distributed_products_outer,
joins('LEFT OUTER JOIN product_distributions ON product_distributions.distributor_id = enterprises.id').
joins('LEFT OUTER JOIN spree_products ON spree_products.id = product_distributions.product_id')
scope :with_order_cycles_outer,
scope :with_order_cycles_as_distributor_outer,
joins("LEFT OUTER JOIN exchanges ON (exchanges.receiver_id = enterprises.id AND exchanges.incoming = 'f')").
joins('LEFT OUTER JOIN order_cycles ON (order_cycles.id = exchanges.order_cycle_id)')
scope :with_order_cycles_outer,
joins("LEFT OUTER JOIN exchanges ON (exchanges.receiver_id = enterprises.id OR exchanges.sender_id = enterprises.id)").
joins('LEFT OUTER JOIN order_cycles ON (order_cycles.id = exchanges.order_cycle_id)')
scope :with_order_cycles_and_exchange_variants_outer,
with_order_cycles_outer.
with_order_cycles_as_distributor_outer.
joins('LEFT OUTER JOIN exchange_variants ON (exchange_variants.exchange_id = exchanges.id)').
joins('LEFT OUTER JOIN spree_variants ON (spree_variants.id = exchange_variants.variant_id)')
scope :active_distributors, lambda {
with_distributed_products_outer.with_order_cycles_outer.
with_distributed_products_outer.with_order_cycles_as_distributor_outer.
where('(product_distributions.product_id IS NOT NULL AND spree_products.deleted_at IS NULL AND spree_products.available_on <= ? AND spree_products.count_on_hand > 0) OR (order_cycles.id IS NOT NULL AND order_cycles.orders_open_at <= ? AND order_cycles.orders_close_at >= ?)', Time.now, Time.now, Time.now).
select('DISTINCT enterprises.*')
}
scope :distributors_with_active_order_cycles, lambda {
with_order_cycles_outer.
with_order_cycles_as_distributor_outer.
merge(OrderCycle.active).
select('DISTINCT enterprises.*')
}
@@ -87,6 +89,17 @@ class Enterprise < ActiveRecord::Base
end
}
# Return enterprises that participate in order cycles that user coordinates, sends to or receives from
scope :accessible_by, lambda { |user|
if user.has_spree_role?('admin')
scoped
else
with_order_cycles_outer.
where('order_cycles.id IN (?)', OrderCycle.accessible_by(user)).
select('DISTINCT enterprises.*')
end
}
# Force a distinct count to work around relation count issue https://github.com/rails/rails/issues/5554
def self.distinct_count

View File

@@ -8,6 +8,8 @@ class AbilityDecorator
# when searching for variants to add to the order
can [:create, :search, :bulk_update], nil
can [:admin, :index], :overview
# Enterprise User can only access products that they are a supplier for
can [:create], Spree::Product
can [:admin, :read, :update, :product_distributions, :bulk_edit, :bulk_update, :clone, :destroy], Spree::Product do |product|
@@ -23,11 +25,12 @@ class AbilityDecorator
# Enterprise User can only access orders that they are a distributor for
can [:index, :create], Spree::Order
can [:admin, :read, :update, :bulk_management, :fire, :resend], Spree::Order do |order|
can [:read, :update, :bulk_management, :fire, :resend], Spree::Order do |order|
# We allow editing orders with a nil distributor as this state occurs
# during the order creation process from the admin backend
order.distributor.nil? || user.enterprises.include?(order.distributor)
end
can [:admin], Spree::Order if user.admin? || user.enterprises.any?{ |e| e.is_distributor? }
can [:admin, :create], Spree::LineItem
can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Payment

View File

@@ -8,7 +8,7 @@ node( :completed_at ) { |order| order.completed_at.blank? ? "" : order.completed
node( :distributor ) { |order| partial 'api/enterprises/bulk_show', :object => order.distributor }
node( :order_cycle ) { |order| partial 'api/order_cycles/bulk_show', :object => order.order_cycle }
node( :line_items ) do |order|
order.line_items.order('id ASC').map do |line_item|
order.line_items.managed_by(@current_api_user).order('id ASC').map do |line_item|
partial 'spree/api/line_items/bulk_show', :object => line_item
end
end

View File

@@ -51,9 +51,11 @@ Openfoodnetwork::Application.routes.draw do
namespace :api do
resources :enterprises do
get :managed, on: :collection
get :accessible, on: :collection
end
resources :order_cycles do
get :managed, on: :collection
get :accessible, on: :collection
end
end
@@ -96,6 +98,7 @@ Spree::Core::Engine.routes.prepend do
match '/admin/orders/bulk_management' => 'admin/orders#bulk_management', :as => "admin_bulk_order_management"
match '/admin/reports/products_and_inventory' => 'admin/reports#products_and_inventory', :as => "products_and_inventory_admin_reports", :via => [:get, :post]
match '/admin/reports/customers' => 'admin/reports#customers', :as => "customers_admin_reports", :via => [:get, :post]
match '/admin', :to => 'admin/overview#index', :as => :admin
namespace :api, :defaults => { :format => 'json' } do

View File

@@ -6,21 +6,87 @@ module Api
include Spree::Api::TestingSupport::Helpers
render_views
let!(:oc1) { FactoryGirl.create(:order_cycle) }
let!(:oc2) { FactoryGirl.create(:order_cycle) }
let(:attributes) { [:id, :name, :suppliers, :distributors] }
before do
stub_authentication!
Spree.user_class.stub :find_by_spree_api_key => current_api_user
end
context "as a normal user" do
let!(:oc1) { FactoryGirl.create(:order_cycle) }
let!(:oc2) { FactoryGirl.create(:order_cycle) }
let(:attributes) { [:id, :name, :suppliers, :distributors] }
before do
stub_authentication!
Spree.user_class.stub :find_by_spree_api_key => current_api_user
end
it "retrieves a list of variants with appropriate attributes" do
get :managed, { :format => :json }
keys = json_response.first.keys.map{ |key| key.to_sym }
attributes.all?{ |attr| keys.include? attr }.should == true
end
end
context "using the accessible action to list order cycles" do
let(:oc_supplier) { create(:supplier_enterprise) }
let(:oc_distributor) { create(:distributor_enterprise) }
let(:other_supplier) { create(:supplier_enterprise) }
let(:oc_supplier_user) do
user = create(:user)
user.spree_roles = []
user.enterprise_roles.create(enterprise: oc_supplier)
user.save!
user
end
let(:oc_distributor_user) do
user = create(:user)
user.spree_roles = []
user.enterprise_roles.create(enterprise: oc_distributor)
user.save!
user
end
let(:other_supplier_user) do
user = create(:user)
user.spree_roles = []
user.enterprise_roles.create(enterprise: other_supplier)
user.save!
user
end
let!(:order_cycle) { create(:order_cycle, suppliers: [oc_supplier], distributors: [oc_distributor]) }
context "as the user of a supplier to an order cycle" do
before :each do
stub_authentication!
Spree.user_class.stub :find_by_spree_api_key => oc_supplier_user
spree_get :accessible, { :template => 'bulk_index', :format => :json }
end
it "gives me access" do
json_response.length.should == 1
json_response[0]['id'].should == order_cycle.id
end
end
context "as the user of some other supplier" do
before :each do
stub_authentication!
Spree.user_class.stub :find_by_spree_api_key => other_supplier_user
spree_get :accessible, { :template => 'bulk_index', :format => :json }
end
it "does not give me access" do
json_response.length.should == 0
end
end
context "as the user of a hub for the order cycle" do
before :each do
stub_authentication!
Spree.user_class.stub :find_by_spree_api_key => oc_distributor_user
spree_get :accessible, { :template => 'bulk_index', :format => :json }
end
it "gives me access" do
json_response.length.should == 1
json_response[0]['id'].should == order_cycle.id
end
end
end
end
end

View File

@@ -5,24 +5,23 @@ module Spree
describe Spree::Api::OrdersController do
include Spree::Api::TestingSupport::Helpers
render_views
let!(:dist1) { FactoryGirl.create(:distributor_enterprise) }
let!(:order1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) }
let!(:order2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) }
let!(:order3) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) }
let!(:line_item1) { FactoryGirl.create(:line_item, order: order1) }
let!(:line_item2) { FactoryGirl.create(:line_item, order: order2) }
let!(:line_item3) { FactoryGirl.create(:line_item, order: order2) }
let!(:line_item4) { FactoryGirl.create(:line_item, order: order3) }
let(:order_attributes) { [:id, :full_name, :email, :phone, :completed_at, :line_items, :distributor, :order_cycle, :number] }
let(:line_item_attributes) { [:id, :quantity, :max_quantity, :supplier, :units_product, :units_variant] }
before do
stub_authentication!
Spree.user_class.stub :find_by_spree_api_key => current_api_user
end
context "as a normal user" do
let!(:dist1) { FactoryGirl.create(:distributor_enterprise) }
let!(:order1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) }
let!(:order2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) }
let!(:order3) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: dist1, billing_address: FactoryGirl.create(:address) ) }
let!(:line_item1) { FactoryGirl.create(:line_item, order: order1) }
let!(:line_item2) { FactoryGirl.create(:line_item, order: order2) }
let!(:line_item3) { FactoryGirl.create(:line_item, order: order2) }
let!(:line_item4) { FactoryGirl.create(:line_item, order: order3) }
let(:order_attributes) { [:id, :full_name, :email, :phone, :completed_at, :line_items, :distributor, :order_cycle, :number] }
let(:line_item_attributes) { [:id, :quantity, :max_quantity, :supplier, :units_product, :units_variant] }
before do
stub_authentication!
Spree.user_class.stub :find_by_spree_api_key => current_api_user
end
before :each do
spree_get :managed, { :template => 'bulk_index', :format => :json }
end
@@ -68,5 +67,61 @@ module Spree
json_response.map{ |order| order['number'] }.all?{ |number| number.match("^R\\d{5,10}$") }.should == true
end
end
context "As an enterprise user" do
let(:supplier) { create(:supplier_enterprise) }
let(:distributor1) { create(:distributor_enterprise) }
let(:distributor2) { create(:distributor_enterprise) }
let!(:order1) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: distributor1, billing_address: FactoryGirl.create(:address) ) }
let!(:line_item1) { FactoryGirl.create(:line_item, order: order1, product: FactoryGirl.create(:product, supplier: supplier)) }
let!(:line_item2) { FactoryGirl.create(:line_item, order: order1, product: FactoryGirl.create(:product, supplier: supplier)) }
let!(:order2) { FactoryGirl.create(:order, state: 'complete', completed_at: Time.now, distributor: distributor2, billing_address: FactoryGirl.create(:address) ) }
let!(:line_item3) { FactoryGirl.create(:line_item, order: order2, product: FactoryGirl.create(:product, supplier: supplier)) }
let(:supplier_user) do
user = create(:user)
user.spree_roles = []
user.enterprise_roles.create(enterprise: supplier)
user.save!
user
end
let(:distributor1_user) do
user = create(:user)
user.spree_roles = []
user.enterprise_roles.create(enterprise: distributor1)
user.save!
user
end
let(:distributor2_user) do
user = create(:user)
user.spree_roles = []
user.enterprise_roles.create(enterprise: distributor2)
user.save!
user
end
context "producer enterprise" do
before :each do
stub_authentication!
Spree.user_class.stub :find_by_spree_api_key => supplier_user
spree_get :managed, { :template => 'bulk_index', :format => :json }
end
it "does not display line item for which my enteprise is a supplier" do
json_response.map{ |order| order['line_items'] }.flatten.length.should == 0
end
end
context "hub enterprise" do
before :each do
stub_authentication!
Spree.user_class.stub :find_by_spree_api_key => distributor1_user
spree_get :managed, { :template => 'bulk_index', :format => :json }
end
it "only displays line items from orders for which my enterprise is a distributor" do
json_response.map{ |order| order['line_items'] }.flatten.map{ |line_item| line_item["id"] }.should == [line_item1.id, line_item2.id]
end
end
end
end
end

View File

@@ -507,7 +507,7 @@ feature %q{
before :each do
visit '/admin/orders/bulk_management'
within "tr#li_#{li3.id}" do
click_link li3.variant.options_text
find("a", text: li3.product.name + ": " + li3.variant.options_text).click
end
end
@@ -542,7 +542,7 @@ feature %q{
context "clicking 'Clear' in group buy box" do
before :each do
click_link 'Clear'
find("a", text: "Clear").click
end
it "shows all products and clears group buy box" do

View File

@@ -237,7 +237,7 @@ feature %q{
visit '/admin/products/bulk_edit'
click_link 'New Product'
find("a", text: "New Product").click
page.should have_content 'NEW PRODUCT'
@@ -783,7 +783,7 @@ feature %q{
select '25', :from => 'perPage'
page.all("input[name='product_name']").select{ |e| e.visible? }.all?{ |e| e.value == "page1product" }.should == true
click_link "2"
find("a", text: "2").click
page.all("input[name='product_name']").select{ |e| e.visible? }.all?{ |e| e.value == "page2product" }.should == true
end
@@ -795,7 +795,7 @@ feature %q{
visit '/admin/products/bulk_edit'
select '25', :from => 'perPage'
click_link "3"
find("a", text: "3").click
select '50', :from => 'perPage'
page.first("div.pagenav span.page.current").should have_text "2"
page.all("input[name='product_name']", :visible => true).length.should == 1
@@ -869,7 +869,7 @@ feature %q{
describe "clicking the 'Remove Filter' link" do
before(:each) do
click_link "Remove Filter"
find("a", text: "Remove Filter").click
end
it "removes the filter from the list of applied filters" do

View File

@@ -16,7 +16,7 @@ feature %q{
page.should have_content "ComfortableMexicanSofa"
click_link 'Spree Admin'
current_path.should == spree.admin_path
current_path.should == spree.admin_orders_path
end
scenario "anonymous user can't access CMS admin" do

View File

@@ -71,7 +71,7 @@ feature %q{
end
scenario "manage products that I supply" do
visit 'admin/products'
visit '/admin/products'
within '#listing_products' do
page.should have_content 'Green eggs'
@@ -90,12 +90,12 @@ feature %q{
end
scenario "should not be able to see system configuration" do
visit 'admin/general_settings/edit'
visit '/admin/general_settings/edit'
page.should have_content 'Authorization Failure'
end
scenario "should not be able to see user management" do
visit 'admin/users'
visit '/admin/users'
page.should have_content 'Authorization Failure'
end
end

View File

@@ -18,9 +18,9 @@ describe "AdminOrderMgmtCtrl", ->
returnedDistributors = ["list of distributors"]
returnedOrderCycles = [ "oc1", "oc2", "oc3" ]
httpBackend.expectGET("/api/users/authorise_api?token=api_key").respond success: "Use of API Authorised"
httpBackend.expectGET("/api/enterprises/managed?template=bulk_index&q[is_primary_producer_eq]=true").respond returnedSuppliers
httpBackend.expectGET("/api/enterprises/managed?template=bulk_index&q[is_distributor_eq]=true").respond returnedDistributors
httpBackend.expectGET("/api/order_cycles/managed").respond returnedOrderCycles
httpBackend.expectGET("/api/enterprises/accessible?template=bulk_index&q[is_primary_producer_eq]=true").respond returnedSuppliers
httpBackend.expectGET("/api/enterprises/accessible?template=bulk_index&q[is_distributor_eq]=true").respond returnedDistributors
httpBackend.expectGET("/api/order_cycles/accessible").respond returnedOrderCycles
spyOn(scope, "initialiseVariables").andCallThrough()
spyOn(scope, "fetchOrders").andReturn "nothing"
#spyOn(returnedSuppliers, "unshift")
@@ -40,7 +40,7 @@ describe "AdminOrderMgmtCtrl", ->
describe "fetching orders", ->
beforeEach ->
scope.initialiseVariables()
httpBackend.expectGET("/api/orders/managed?template=bulk_index&q[completed_at_not_null]=true&q[completed_at_gt]=SomeDate&q[completed_at_lt]=SomeDate").respond "list of orders"
httpBackend.expectGET("/api/orders/managed?template=bulk_index;page=1;per_page=500;q[completed_at_not_null]=true;q[completed_at_gt]=SomeDate;q[completed_at_lt]=SomeDate").respond "list of orders"
it "makes a call to dataFetcher, with current start and end date parameters", ->
scope.fetchOrders()

View File

@@ -254,6 +254,35 @@ describe Enterprise do
enterprises.should include e2
end
end
describe "accessible_by" do
it "shows only enterprises that are invloved in order cycles which are common to those managed by the given user" do
user = create(:user)
user.spree_roles = []
e1 = create(:enterprise)
e2 = create(:enterprise)
e3 = create(:enterprise)
e4 = create(:enterprise)
e1.enterprise_roles.build(user: user).save
oc = create(:simple_order_cycle, coordinator: e2, suppliers: [e1], distributors: [e3])
enterprises = Enterprise.accessible_by user
enterprises.length.should == 3
enterprises.should include e1, e2, e3
enterprises.should_not include e4
end
it "shows all enterprises for admin user" do
user = create(:admin_user)
e1 = create(:enterprise)
e2 = create(:enterprise)
enterprises = Enterprise.managed_by user
enterprises.length.should == 2
enterprises.should include e1
enterprises.should include e2
end
end
end
describe "has_supplied_products_on_hand?" do

View File

@@ -39,6 +39,10 @@ module Spree
should_not have_ability([:admin, :read, :update, :product_distributions, :bulk_edit, :bulk_update, :clone, :destroy], for: p2)
end
it "should not be able to access admin actions on orders" do
should_not have_ability([:admin], for: Spree::Order)
end
it "should be able to create a new product" do
should have_ability(:create, for: Spree::Product)
end