From 722ccfc83b799f9f78b5e69171ca6cb74aa21545 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 31 Jul 2014 15:20:22 +1000 Subject: [PATCH 01/35] ng_text_field handling options parameter --- app/helpers/angular_form_builder.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/helpers/angular_form_builder.rb b/app/helpers/angular_form_builder.rb index 5d913eb291..56fcab79fa 100644 --- a/app/helpers/angular_form_builder.rb +++ b/app/helpers/angular_form_builder.rb @@ -12,8 +12,9 @@ class AngularFormBuilder < ActionView::Helpers::FormBuilder # @object.send(@fields_for_record_name).first.class.to_s.underscore --> enterprise_fee value = "{{ #{@object.send(@fields_for_record_name).first.class.to_s.underscore}.#{method} }}" + options.reverse_merge!({'id' => angular_id(method)}) - @template.text_field_tag angular_name(method), value, :id => angular_id(method) + @template.text_field_tag angular_name(method), value, options end def ng_hidden_field(method, options = {}) From bd3a4acc15a8062a680ac82c19d21aec3d71df3c Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 31 Jul 2014 15:21:13 +1000 Subject: [PATCH 02/35] Giving an example name for an enterprise fee. The example is displayed as input placeholder. See bugherd #439. --- app/views/admin/enterprise_fees/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/enterprise_fees/index.html.haml b/app/views/admin/enterprise_fees/index.html.haml index d3e9a0b88c..48517c5931 100644 --- a/app/views/admin/enterprise_fees/index.html.haml +++ b/app/views/admin/enterprise_fees/index.html.haml @@ -27,7 +27,7 @@ = f.ng_hidden_field :id = f.ng_collection_select :enterprise_id, @enterprises, :id, :name, 'enterprise_fee.enterprise_id', :include_blank => true %td= f.ng_select :fee_type, enterprise_fee_type_options, 'enterprise_fee.fee_type' - %td= f.ng_text_field :name + %td= f.ng_text_field :name, { placeholder: 'e.g. packing fee' } %td= f.ng_collection_select :calculator_type, @calculators, :name, :description, 'enterprise_fee.calculator_type', {'class' => 'calculator_type', 'ng-model' => 'calculatorType', 'spree-ensure-calculator-preferences-match-type' => "1"} %td{'ng-bind-html-unsafe-compiled' => 'enterprise_fee.calculator_settings'} %td.actions{'spree-delete-resource' => "1"} From ca7eb72688cd4a6927b7dadac30c1146a617d886 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 22 Aug 2014 16:04:46 +1000 Subject: [PATCH 03/35] Adding login_nav partial from spree_auth_devise Preparing to change 'Store' link for Bugherd #443. --- app/views/spree/layouts/admin/_login_nav.html.erb | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 app/views/spree/layouts/admin/_login_nav.html.erb diff --git a/app/views/spree/layouts/admin/_login_nav.html.erb b/app/views/spree/layouts/admin/_login_nav.html.erb new file mode 100644 index 0000000000..a43437c4ab --- /dev/null +++ b/app/views/spree/layouts/admin/_login_nav.html.erb @@ -0,0 +1,8 @@ +<% if spree_current_user %> +
    +
  • <%= t(:logged_in_as) %>: <%= spree_current_user.email %>
  • +
  • <%= link_to t(:account), spree.edit_user_path(spree_current_user) %>
  • +
  • <%= link_to t(:logout), spree.logout_path %>
  • +
  • <%= link_to t(:store), spree.products_path, :target => '_blank' %>
  • +
+<% end %> From adf4f92ffbc5192a00e682484847ef8073d07594 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 22 Aug 2014 16:10:23 +1000 Subject: [PATCH 04/35] Store links to root_path instead of product_path --- app/views/spree/layouts/admin/_login_nav.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/spree/layouts/admin/_login_nav.html.erb b/app/views/spree/layouts/admin/_login_nav.html.erb index a43437c4ab..7ab28b3344 100644 --- a/app/views/spree/layouts/admin/_login_nav.html.erb +++ b/app/views/spree/layouts/admin/_login_nav.html.erb @@ -3,6 +3,6 @@
  • <%= t(:logged_in_as) %>: <%= spree_current_user.email %>
  • <%= link_to t(:account), spree.edit_user_path(spree_current_user) %>
  • <%= link_to t(:logout), spree.logout_path %>
  • -
  • <%= link_to t(:store), spree.products_path, :target => '_blank' %>
  • +
  • <%= link_to t(:store), spree.root_path, :target => '_blank' %>
  • <% end %> From 0b61872d964d4ddebb870aa27b722f93a5d028a8 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Aug 2014 16:38:10 +1000 Subject: [PATCH 05/35] Revert 5ede8d1, reinstating a4be0ff..7b89e6a --- .../enterprise_relationships.js.coffee | 6 +- .../admin/order_cycles_controller.rb | 7 +- app/helpers/order_cycles_helper.rb | 14 ++- app/models/enterprise_relationship.rb | 10 +- .../_enterprise_relationship.html.haml | 4 +- .../enterprise_relationships/_form.html.haml | 15 ++- .../order_cycles/_exchange_form.html.haml | 2 +- app/views/admin/order_cycles/_form.html.haml | 4 +- app/views/admin/order_cycles/show.rep | 2 +- lib/open_food_network/permissions.rb | 37 ++++++ .../admin/enterprise_relationships_spec.rb | 21 ++-- spec/features/admin/order_cycles_spec.rb | 113 ++++++++++++------ .../enterprise_relationships_spec.js.coffee | 2 +- .../lib/open_food_network/permissions_spec.rb | 74 ++++++++++++ spec/models/enterprise_relationship_spec.rb | 27 ++++- 15 files changed, 263 insertions(+), 75 deletions(-) create mode 100644 lib/open_food_network/permissions.rb create mode 100644 spec/lib/open_food_network/permissions_spec.rb diff --git a/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee b/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee index 7a799f2f28..cad556efd8 100644 --- a/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee +++ b/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee @@ -2,7 +2,7 @@ angular.module("ofn.admin").factory 'EnterpriseRelationships', ($http, enterpris new class EnterpriseRelationships create_errors: "" all_permissions: [ - 'add_products_to_order_cycle' + 'add_to_order_cycle' 'manage_products' ] @@ -24,5 +24,5 @@ angular.module("ofn.admin").factory 'EnterpriseRelationships', ($http, enterpris permission_presentation: (permission) -> switch permission - when "add_products_to_order_cycle" then "can add products to order cycle from" - when "manage_products" then "can manage the products of" \ No newline at end of file + when "add_to_order_cycle" then "can add to order cycle" + when "manage_products" then "can manage the products of" diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index 566c660112..63daf918de 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -1,7 +1,10 @@ +require 'open_food_network/permissions' require 'open_food_network/order_cycle_form_applicator' module Admin class OrderCyclesController < ResourceController + include OrderCyclesHelper + before_filter :load_order_cycle_set, :only => :index def show @@ -23,7 +26,7 @@ module Admin respond_to do |format| if @order_cycle.save - OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, managed_enterprises).go! + OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, order_cycle_permitted_enterprises).go! flash[:notice] = 'Your order cycle has been created.' format.html { redirect_to admin_order_cycles_path } @@ -40,7 +43,7 @@ module Admin respond_to do |format| if @order_cycle.update_attributes(params[:order_cycle]) - OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, managed_enterprises).go! + OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, order_cycle_permitted_enterprises).go! flash[:notice] = 'Your order cycle has been updated.' format.html { redirect_to admin_order_cycles_path } diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index 92cd968e0b..dfd957a143 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -3,8 +3,20 @@ module OrderCyclesHelper @current_order_cycle ||= current_order(false).andand.order_cycle end + def order_cycle_permitted_enterprises + OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises + end + + def order_cycle_producer_enterprises + order_cycle_permitted_enterprises.is_primary_producer.by_name + end + def coordinating_enterprises - Enterprise.is_distributor.managed_by(spree_current_user).order('name') + order_cycle_hub_enterprises + end + + def order_cycle_hub_enterprises + order_cycle_permitted_enterprises.is_distributor.by_name end def order_cycle_local_remote_class(distributor, order_cycle) diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb index 5c10aafbbf..dba99cc7b3 100644 --- a/app/models/enterprise_relationship.rb +++ b/app/models/enterprise_relationship.rb @@ -9,12 +9,20 @@ class EnterpriseRelationship < ActiveRecord::Base scope :with_enterprises, joins('LEFT JOIN enterprises AS parent_enterprises ON parent_enterprises.id = enterprise_relationships.parent_id'). joins('LEFT JOIN enterprises AS child_enterprises ON child_enterprises.id = enterprise_relationships.child_id') - scope :by_name, with_enterprises.order('parent_enterprises.name, child_enterprises.name') scope :involving_enterprises, ->(enterprises) { where('parent_id IN (?) OR child_id IN (?)', enterprises, enterprises) } + scope :permitting, ->(enterprises) { where('child_id IN (?)', enterprises) } + + scope :with_permission, ->(permission) { + joins(:permissions). + where('enterprise_relationship_permissions.name = ?', permission) + } + + scope :by_name, with_enterprises.order('child_enterprises.name, parent_enterprises.name') + def permissions_list=(perms) perms.andand.each { |name| permissions.build name: name } diff --git a/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml b/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml index 56f0d46c01..6a7dcdc7f4 100644 --- a/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml +++ b/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml @@ -1,8 +1,8 @@ -%td {{ enterprise_relationship.parent_name }} +%td {{ enterprise_relationship.child_name }} %td %ul %li{"ng-repeat" => "permission in enterprise_relationship.permissions"} {{ EnterpriseRelationships.permission_presentation(permission.name) }} -%td {{ enterprise_relationship.child_name }} +%td {{ enterprise_relationship.parent_name }} %td.actions %a.delete-enterprise-relationship.icon-trash.no-text{'ng-click' => 'delete(enterprise_relationship)'} diff --git a/app/views/admin/enterprise_relationships/_form.html.haml b/app/views/admin/enterprise_relationships/_form.html.haml index b9c2f265cd..86086c8781 100644 --- a/app/views/admin/enterprise_relationships/_form.html.haml +++ b/app/views/admin/enterprise_relationships/_form.html.haml @@ -1,14 +1,13 @@ %tr - %td - %select{name: "enterprise_relationship_parent_id", "ng-model" => "parent_id", "ng-options" => "e.id as e.name for e in Enterprises.my_enterprises"} - %td - permits - / %div{"ng-repeat" => "permission in EnterpriseRelationships.all_permissions"} - / %label - / %input{type: "checkbox", "ng-model" => "permissions[permission]"} - / {{ EnterpriseRelationships.permission_presentation(permission) }} %td %select{name: "enterprise_relationship_child_id", "ng-model" => "child_id", "ng-options" => "e.id as e.name for e in Enterprises.all_enterprises"} + %td + %div{"ng-repeat" => "permission in EnterpriseRelationships.all_permissions"} + %label + %input{type: "checkbox", "ng-model" => "permissions[permission]"} + {{ EnterpriseRelationships.permission_presentation(permission) }} + %td + %select{name: "enterprise_relationship_parent_id", "ng-model" => "parent_id", "ng-options" => "e.id as e.name for e in Enterprises.my_enterprises"} %td.actions %input{type: "button", value: "Create", "ng-click" => "create()"} .errors {{ EnterpriseRelationships.create_errors }} diff --git a/app/views/admin/order_cycles/_exchange_form.html.haml b/app/views/admin/order_cycles/_exchange_form.html.haml index 3539892034..8f81b9f03f 100644 --- a/app/views/admin/order_cycles/_exchange_form.html.haml +++ b/app/views/admin/order_cycles/_exchange_form.html.haml @@ -23,4 +23,4 @@ = f.submit 'Add fee', 'ng-click' => 'addExchangeFee($event, exchange)' %td.actions - %a{'ng-click' => 'removeExchange($event, exchange)', :class => "icon-trash no-text"} + %a{'ng-click' => 'removeExchange($event, exchange)', :class => "icon-trash no-text remove-exchange"} diff --git a/app/views/admin/order_cycles/_form.html.haml b/app/views/admin/order_cycles/_form.html.haml index 68dfed9f1a..e859374a3b 100644 --- a/app/views/admin/order_cycles/_form.html.haml +++ b/app/views/admin/order_cycles/_form.html.haml @@ -25,7 +25,7 @@ %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.managed_by(spree_current_user).by_name, :id, :name), {'ng-model' => 'new_supplier_id'} += select_tag :new_supplier_id, options_from_collection_for_select(order_cycle_producer_enterprises, :id, :name), {'ng-model' => 'new_supplier_id'} = f.submit 'Add supplier', 'ng-click' => 'addSupplier($event)' @@ -50,7 +50,7 @@ %tr.products{'ng-show' => 'exchange.showProducts'} = render 'exchange_distributed_products_form' -= select_tag :new_distributor_id, options_from_collection_for_select(Enterprise.is_distributor.managed_by(spree_current_user).by_name, :id, :name), {'ng-model' => 'new_distributor_id'} += select_tag :new_distributor_id, options_from_collection_for_select(order_cycle_hub_enterprises, :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 04fc659813..fb71602eb6 100644 --- a/app/views/admin/order_cycles/show.rep +++ b/app/views/admin/order_cycles/show.rep @@ -9,7 +9,7 @@ r.element :order_cycle, @order_cycle do r.element :id end - r.list_of :exchanges, @order_cycle.exchanges.managed_by(spree_current_user).order('id ASC') do |exchange| + r.list_of :exchanges, OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_exchanges(@order_cycle).order('id ASC') do |exchange| r.element :id r.element :sender_id r.element :receiver_id diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb new file mode 100644 index 0000000000..99c0aa0c15 --- /dev/null +++ b/lib/open_food_network/permissions.rb @@ -0,0 +1,37 @@ +module OpenFoodNetwork + class Permissions + def initialize(user) + @user = user + end + + # Find enterprises that an admin is allowed to add to an order cycle + def order_cycle_enterprises + managed_enterprise_ids = managed_enterprises.pluck :id + permitted_enterprise_ids = related_enterprises_with(:add_to_order_cycle).pluck :id + + Enterprise.where('id IN (?)', managed_enterprise_ids + permitted_enterprise_ids) + end + + # Find the exchanges of an order cycle that an admin can manage + def order_cycle_exchanges(order_cycle) + enterprises = managed_enterprises + related_enterprises_with(:add_to_order_cycle) + order_cycle.exchanges.to_enterprises(enterprises).from_enterprises(enterprises) + end + + + private + + def managed_enterprises + Enterprise.managed_by(@user) + end + + def related_enterprises_with(permission) + parent_ids = EnterpriseRelationship. + permitting(managed_enterprises). + with_permission(permission). + pluck(:parent_id) + + Enterprise.where('id IN (?)', parent_ids) + end + end +end diff --git a/spec/features/admin/enterprise_relationships_spec.rb b/spec/features/admin/enterprise_relationships_spec.rb index 757f8bc2e6..7317f44066 100644 --- a/spec/features/admin/enterprise_relationships_spec.rb +++ b/spec/features/admin/enterprise_relationships_spec.rb @@ -14,9 +14,9 @@ feature %q{ scenario "listing relationships" do # Given some enterprises with relationships e1, e2, e3, e4 = create(:enterprise), create(:enterprise), create(:enterprise), create(:enterprise) - create(:enterprise_relationship, parent: e1, child: e2, permissions_list: [:add_products_to_order_cycle]) + create(:enterprise_relationship, parent: e1, child: e2, permissions_list: [:add_to_order_cycle]) create(:enterprise_relationship, parent: e2, child: e3, permissions_list: [:manage_products]) - create(:enterprise_relationship, parent: e3, child: e4, permissions_list: [:add_products_to_order_cycle, :manage_products]) + create(:enterprise_relationship, parent: e3, child: e4, permissions_list: [:add_to_order_cycle, :manage_products]) # When I go to the relationships page click_link 'Enterprises' @@ -24,10 +24,10 @@ feature %q{ # Then I should see the relationships within('table#enterprise-relationships') do - page.should have_relationship e1, e2, ['can add products to order cycle from'] + page.should have_relationship e1, e2, ['can add to order cycle'] page.should have_relationship e2, e3, ['can manage the products of'] page.should have_relationship e3, e4, - ['can add products to order cycle from', 'can manage the products of'] + ['can add to order cycle', 'can manage the products of'] end end @@ -38,16 +38,17 @@ feature %q{ visit admin_enterprise_relationships_path select 'One', from: 'enterprise_relationship_parent_id' - #check 'can add products to order cycle from' - #check 'can manage the products of' - #uncheck 'can manage the products of' + + check 'can add to order cycle' + check 'can manage the products of' + uncheck 'can manage the products of' select 'Two', from: 'enterprise_relationship_child_id' click_button 'Create' - page.should have_relationship e1, e2 #, ['can add products to order cycle from'] + page.should have_relationship e1, e2, ['can add to order cycle'] er = EnterpriseRelationship.where(parent_id: e1, child_id: e2).first er.should be_present - #er.permissions.map(&:name).should == ['add_products_to_order_cycle'] + er.permissions.map(&:name).should == ['add_to_order_cycle'] end @@ -119,6 +120,6 @@ feature %q{ def have_relationship(parent, child, perms=[]) perms = perms.join(' ') || 'permits' - have_table_row [parent.name, perms, child.name, ''] + have_table_row [child.name, perms, parent.name, ''] end end diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 2423a6dc05..76ee3bdfc2 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -436,26 +436,37 @@ feature %q{ 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') } - let!(:distributor1_fee) { create(:enterprise_fee, enterprise: distributor1, name: 'First Distributor Fee') } - before(:each) do - product = create(:product, supplier: supplier1) - product.distributors << distributor1 + let!(:supplier_managed) { create(:supplier_enterprise, name: 'Managed supplier') } + let!(:supplier_unmanaged) { create(:supplier_enterprise, name: 'Unmanaged supplier') } + let!(:supplier_permitted) { create(:supplier_enterprise, name: 'Permitted supplier') } + let!(:distributor_managed) { create(:distributor_enterprise, name: 'Managed distributor') } + let!(:distributor_unmanaged) { create(:distributor_enterprise, name: 'Unmanaged Distributor') } + let!(:distributor_permitted) { create(:distributor_enterprise, name: 'Permitted distributor') } + let!(:distributor_managed_fee) { create(:enterprise_fee, enterprise: distributor_managed, name: 'Managed distributor fee') } + let!(:supplier_permitted_relationship) do + create(:enterprise_relationship, parent: supplier_permitted, child: supplier_managed, + permissions_list: [:add_to_order_cycle]) + end + let!(:distributor_permitted_relationship) do + create(:enterprise_relationship, parent: distributor_permitted, child: distributor_managed, + permissions_list: [:add_to_order_cycle]) + end + + before do + product = create(:product, supplier: supplier_managed) + product.distributors << distributor_managed product.save! @new_user = create_enterprise_user - @new_user.enterprise_roles.build(enterprise: supplier1).save - @new_user.enterprise_roles.build(enterprise: distributor1).save + @new_user.enterprise_roles.build(enterprise: supplier_managed).save + @new_user.enterprise_roles.build(enterprise: distributor_managed).save login_to_admin_as @new_user end scenario "viewing a list of order cycles I am coordinating" do - oc_user_coordinating = create(:simple_order_cycle, { suppliers: [supplier1, supplier2], coordinator: supplier1, distributors: [distributor1, distributor2], name: 'Order Cycle 1' } ) - oc_for_other_user = create(:simple_order_cycle, { coordinator: supplier2, name: 'Order Cycle 2' } ) + oc_user_coordinating = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_unmanaged], coordinator: supplier_managed, distributors: [distributor_managed, distributor_unmanaged], name: 'Order Cycle 1' } ) + oc_for_other_user = create(:simple_order_cycle, { coordinator: supplier_unmanaged, name: 'Order Cycle 2' } ) click_link "Order Cycles" @@ -464,8 +475,8 @@ feature %q{ page.should_not have_content oc_for_other_user.name # The order cycle should not show enterprises that I don't manage - page.should_not have_selector 'td.suppliers', text: supplier2.name - page.should_not have_selector 'td.distributors', text: distributor2.name + page.should_not have_selector 'td.suppliers', text: supplier_unmanaged.name + page.should_not have_selector 'td.distributors', text: distributor_unmanaged.name end scenario "creating a new order cycle" do @@ -476,57 +487,81 @@ feature %q{ 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' + select 'Managed supplier', from: 'new_supplier_id' + click_button 'Add supplier' + select 'Permitted supplier', from: 'new_supplier_id' click_button 'Add supplier' - select 'First Distributor', from: 'order_cycle_coordinator_id' + select 'Managed distributor', from: 'order_cycle_coordinator_id' click_button 'Add coordinator fee' - select 'First Distributor Fee', from: 'order_cycle_coordinator_fee_0_id' + select 'Managed distributor fee', from: 'order_cycle_coordinator_fee_0_id' - select 'First Distributor', from: 'new_distributor_id' + select 'Managed distributor', from: 'new_distributor_id' + click_button 'Add distributor' + select 'Permitted 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 + # Should only have suppliers / distributors listed which the user is managing or + # has E2E permission to add products to order cycles + page.should_not have_select 'new_supplier_id', with_options: [supplier_unmanaged.name] + page.should_not have_select 'new_distributor_id', with_options: [distributor_unmanaged.name] + + [distributor_unmanaged.name, supplier_managed.name, supplier_unmanaged.name].each do |enterprise_name| + page.should_not have_select 'order_cycle_coordinator_id', with_options: [enterprise_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 + order_cycle.suppliers.sort.should == [supplier_managed, supplier_permitted].sort + order_cycle.coordinator.should == distributor_managed + order_cycle.distributors.sort.should == [distributor_managed, distributor_permitted].sort end - scenario "editing an order cycle" do - oc = create(:simple_order_cycle, { suppliers: [supplier1, supplier2], coordinator: supplier1, distributors: [distributor1, distributor2], name: 'Order Cycle 1' } ) + scenario "editing an order cycle does not affect exchanges we don't manage" do + oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: supplier_managed, distributors: [distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) visit edit_admin_order_cycle_path(oc) - # I should not see exchanges for supplier2 or distributor2 - page.all('tr.supplier').count.should == 1 - page.all('tr.distributor').count.should == 1 + # I should not see exchanges for supplier_unmanaged or distributor_unmanaged + page.all('tr.supplier').count.should == 2 + page.all('tr.distributor').count.should == 2 # When I save, then those exchanges should remain click_button 'Update' page.should have_content "Your order cycle has been updated." oc.reload - oc.suppliers.sort.should == [supplier1, supplier2] - oc.coordinator.should == supplier1 - oc.distributors.sort.should == [distributor1, distributor2] + oc.suppliers.sort.should == [supplier_managed, supplier_permitted, supplier_unmanaged].sort + oc.coordinator.should == supplier_managed + oc.distributors.sort.should == [distributor_managed, distributor_permitted, distributor_unmanaged].sort end + scenario "editing an order cycle" do + oc = create(:simple_order_cycle, { suppliers: [supplier_managed, supplier_permitted, supplier_unmanaged], coordinator: supplier_managed, distributors: [distributor_managed, distributor_permitted, distributor_unmanaged], name: 'Order Cycle 1' } ) + + visit edit_admin_order_cycle_path(oc) + + # When I remove all the exchanges and save + page.find("tr.supplier-#{supplier_managed.id} a.remove-exchange").click + page.find("tr.supplier-#{supplier_permitted.id} a.remove-exchange").click + page.find("tr.distributor-#{distributor_managed.id} a.remove-exchange").click + page.find("tr.distributor-#{distributor_permitted.id} a.remove-exchange").click + click_button 'Update' + + # Then the exchanges should be removed + page.should have_content "Your order cycle has been updated." + + oc.reload + oc.suppliers.should == [supplier_unmanaged] + oc.coordinator.should == supplier_managed + oc.distributors.should == [distributor_unmanaged] + end + + scenario "cloning an order cycle" do - oc = create(:simple_order_cycle) + oc = create(:simple_order_cycle, coordinator: distributor_managed) click_link "Order Cycles" first('a.clone-order-cycle').click diff --git a/spec/javascripts/unit/admin/services/enterprise_relationships_spec.js.coffee b/spec/javascripts/unit/admin/services/enterprise_relationships_spec.js.coffee index 4e90f65335..9701377a73 100644 --- a/spec/javascripts/unit/admin/services/enterprise_relationships_spec.js.coffee +++ b/spec/javascripts/unit/admin/services/enterprise_relationships_spec.js.coffee @@ -12,5 +12,5 @@ describe "enterprise relationships", -> EnterpriseRelationships = _EnterpriseRelationships_ it "presents permission names", -> - expect(EnterpriseRelationships.permission_presentation("add_products_to_order_cycle")).toEqual "can add products to order cycle from" + expect(EnterpriseRelationships.permission_presentation("add_to_order_cycle")).toEqual "can add to order cycle" expect(EnterpriseRelationships.permission_presentation("manage_products")).toEqual "can manage the products of" diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb new file mode 100644 index 0000000000..b0a004cbf1 --- /dev/null +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -0,0 +1,74 @@ +require 'open_food_network/permissions' + +module OpenFoodNetwork + describe Permissions do + let(:user) { double(:user) } + let(:permissions) { Permissions.new(user) } + let(:permission) { 'one' } + let(:e1) { create(:enterprise) } + let(:e2) { create(:enterprise) } + + describe "finding enterprises that can be added to an order cycle" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where('1=0') } + permissions.stub(:related_enterprises_with) { Enterprise.where('1=0') } + end + + it "returns managed enterprises" do + permissions.stub(:managed_enterprises) { Enterprise.where(id: e1) } + permissions.order_cycle_enterprises.should == [e1] + end + + it "returns permitted enterprises" do + permissions.stub(:related_enterprises_with) { Enterprise.where(id: e2) } + permissions.order_cycle_enterprises.should == [e2] + end + end + + describe "finding exchanges of an order cycle that an admin can manage" do + let(:oc) { create(:simple_order_cycle) } + let!(:ex) { create(:exchange, order_cycle: oc, sender: e1, receiver: e2) } + + before do + permissions.stub(:managed_enterprises) { [] } + permissions.stub(:related_enterprises_with) { [] } + end + + it "returns exchanges involving enterprises managed by the user" do + permissions.stub(:managed_enterprises) { [e1, e2] } + permissions.order_cycle_exchanges(oc).should == [ex] + end + + it "returns exchanges involving enterprises with E2E permission" do + permissions.stub(:related_enterprises_with) { [e1, e2] } + permissions.order_cycle_exchanges(oc).should == [ex] + end + + it "does not return exchanges involving only the sender" do + permissions.stub(:managed_enterprises) { [e1] } + permissions.order_cycle_exchanges(oc).should == [] + end + + it "does not return exchanges involving only the receiver" do + permissions.stub(:managed_enterprises) { [e2] } + permissions.order_cycle_exchanges(oc).should == [] + end + end + + ######################################## + + describe "finding related enterprises with a particular permission" do + let!(:er) { create(:enterprise_relationship, parent: e1, child: e2, permissions_list: [permission]) } + + it "returns the enterprises" do + permissions.stub(:managed_enterprises) { e2 } + permissions.send(:related_enterprises_with, permission).should == [e1] + end + + it "returns an empty array when there are none" do + permissions.stub(:managed_enterprises) { e1 } + permissions.send(:related_enterprises_with, permission).should == [] + end + end + end +end diff --git a/spec/models/enterprise_relationship_spec.rb b/spec/models/enterprise_relationship_spec.rb index 6cdf3db657..3a6c48e891 100644 --- a/spec/models/enterprise_relationship_spec.rb +++ b/spec/models/enterprise_relationship_spec.rb @@ -6,10 +6,10 @@ describe EnterpriseRelationship do let(:e2) { create(:enterprise, name: 'B') } let(:e3) { create(:enterprise, name: 'C') } - it "sorts by parent, child enterprise name" do - er1 = create(:enterprise_relationship, parent: e1, child: e3) - er2 = create(:enterprise_relationship, parent: e2, child: e1) - er3 = create(:enterprise_relationship, parent: e1, child: e2) + it "sorts by child, parent enterprise name" do + er1 = create(:enterprise_relationship, parent: e3, child: e1) + er2 = create(:enterprise_relationship, parent: e1, child: e2) + er3 = create(:enterprise_relationship, parent: e2, child: e1) EnterpriseRelationship.by_name.should == [er3, er1, er2] end @@ -43,5 +43,24 @@ describe EnterpriseRelationship do er.permissions.should be_empty end end + + it "finds relationships that grant permissions to some enterprises" do + er1 = create(:enterprise_relationship, parent: e2, child: e1) + er2 = create(:enterprise_relationship, parent: e3, child: e2) + er3 = create(:enterprise_relationship, parent: e1, child: e3) + + EnterpriseRelationship.permitting([e1, e2]).sort.should == [er1, er2] + end + + it "finds relationships that grant a particular permission" do + er1 = create(:enterprise_relationship, parent: e1, child: e2, + permissions_list: ['one', 'two']) + er2 = create(:enterprise_relationship, parent: e2, child: e3, + permissions_list: ['two', 'three']) + er3 = create(:enterprise_relationship, parent: e3, child: e1, + permissions_list: ['three', 'four']) + + EnterpriseRelationship.with_permission('two').sort.should == [er1, er2].sort + end end end From 7f74854a2f72cf668f45c1069ca74d193d8774d3 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 28 Aug 2014 15:06:55 +1000 Subject: [PATCH 06/35] For OC, fetch all enterprises we have access to, including those via E2E relationships --- .../javascripts/admin/order_cycle.js.erb.coffee | 2 +- app/controllers/admin/enterprises_controller.rb | 7 ++++++- app/models/spree/ability_decorator.rb | 1 + .../{index.rabl => for_order_cycle.rabl} | 0 config/routes.rb | 7 +++++-- spec/features/admin/order_cycles_spec.rb | 16 ++++++++++++---- spec/javascripts/unit/order_cycle_spec.js.coffee | 2 +- spec/models/spree/ability_spec.rb | 2 +- 8 files changed, 27 insertions(+), 10 deletions(-) rename app/views/admin/enterprises/{index.rabl => for_order_cycle.rabl} (100%) diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee index aadf66af60..b8068681ca 100644 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycle.js.erb.coffee @@ -330,7 +330,7 @@ angular.module('order_cycle', ['ngResource']) }]) .factory('Enterprise', ['$resource', ($resource) -> - Enterprise = $resource('/admin/enterprises/:enterprise_id.json', {}, {'index': {method: 'GET', isArray: true}}) + Enterprise = $resource('/admin/enterprises/for_order_cycle/:enterprise_id.json', {}, {'index': {method: 'GET', isArray: true}}) { Enterprise: Enterprise diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 53e58d110b..85fe6831fb 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -6,6 +6,11 @@ module Admin create.after :grant_management helper 'spree/products' + include OrderCyclesHelper + + def for_order_cycle + @collection = order_cycle_permitted_enterprises + end def bulk_update @@ -53,7 +58,7 @@ module Admin end def collection_actions - [:index, :bulk_update] + [:index, :for_order_cycle, :bulk_update] end def load_methods_and_fees diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index b29aa6d5b3..68d98bd568 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -76,6 +76,7 @@ class AbilityDecorator can [:admin, :index, :read, :edit, :update, :bulk_update, :clone], OrderCycle do |order_cycle| user.enterprises.include? order_cycle.coordinator end + can [:for_order_cycle], Enterprise can [:index, :create], EnterpriseFee can [:admin, :read, :edit, :bulk_update, :destroy], EnterpriseFee do |enterprise_fee| diff --git a/app/views/admin/enterprises/index.rabl b/app/views/admin/enterprises/for_order_cycle.rabl similarity index 100% rename from app/views/admin/enterprises/index.rabl rename to app/views/admin/enterprises/for_order_cycle.rabl diff --git a/config/routes.rb b/config/routes.rb index 36213b2622..5230e798cc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -33,12 +33,15 @@ Openfoodnetwork::Application.routes.draw do namespace :admin do resources :order_cycles do - post :bulk_update, :on => :collection, :as => :bulk_update + post :bulk_update, on: :collection, as: :bulk_update get :clone, on: :member end resources :enterprises do - post :bulk_update, :on => :collection, :as => :bulk_update + collection do + get :for_order_cycle + post :bulk_update, as: :bulk_update + end resources :producer_properties do post :update_positions, on: :collection diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 76ee3bdfc2..febd966109 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -451,12 +451,10 @@ feature %q{ create(:enterprise_relationship, parent: distributor_permitted, child: distributor_managed, permissions_list: [:add_to_order_cycle]) end + let!(:product_managed) { create(:product, supplier: supplier_managed) } + let!(:product_permitted) { create(:product, supplier: supplier_permitted) } before do - product = create(:product, supplier: supplier_managed) - product.distributors << distributor_managed - product.save! - @new_user = create_enterprise_user @new_user.enterprise_roles.build(enterprise: supplier_managed).save @new_user.enterprise_roles.build(enterprise: distributor_managed).save @@ -492,6 +490,9 @@ feature %q{ select 'Permitted supplier', from: 'new_supplier_id' click_button 'Add supplier' + select_incoming_variant supplier_managed, 0, product_managed.master + select_incoming_variant supplier_permitted, 1, product_permitted.master + select 'Managed distributor', from: 'order_cycle_coordinator_id' click_button 'Add coordinator fee' select 'Managed distributor fee', from: 'order_cycle_coordinator_fee_0_id' @@ -574,4 +575,11 @@ feature %q{ end + + private + + def select_incoming_variant(supplier, exchange_no, variant) + page.find("table.exchanges tr.supplier-#{supplier.id} td.products input").click + check "order_cycle_incoming_exchange_#{exchange_no}_variants_#{variant.id}" + end end diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index 172b2cdb1a..0d5127314a 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -327,7 +327,7 @@ describe 'OrderCycle services', -> inject ($injector, _$httpBackend_)-> Enterprise = $injector.get('Enterprise') $httpBackend = _$httpBackend_ - $httpBackend.whenGET('/admin/enterprises.json').respond [ + $httpBackend.whenGET('/admin/enterprises/for_order_cycle.json').respond [ {id: 1, name: 'One', supplied_products: [1, 2]} {id: 2, name: 'Two', supplied_products: [3, 4]} {id: 3, name: 'Three', supplied_products: [5, 6]} diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index a2d1fb5bdf..9e5ae49780 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -264,7 +264,7 @@ module Spree end it 'should have the ability administrate and create enterpises' do - should have_ability([:admin, :index, :create], for: Enterprise) + should have_ability([:admin, :index, :for_order_cycle, :create], for: Enterprise) end end end From 62e6cacfd0d7db7e043c9b6aa77cf834d37e2c85 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 29 Aug 2014 17:37:56 +1000 Subject: [PATCH 07/35] Rename spec to match view name change --- .../{index.rabl_spec.rb => for_order_cycle.rabl_spec.rb} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename spec/views/admin/enterprises/{index.rabl_spec.rb => for_order_cycle.rabl_spec.rb} (79%) diff --git a/spec/views/admin/enterprises/index.rabl_spec.rb b/spec/views/admin/enterprises/for_order_cycle.rabl_spec.rb similarity index 79% rename from spec/views/admin/enterprises/index.rabl_spec.rb rename to spec/views/admin/enterprises/for_order_cycle.rabl_spec.rb index 91463c2212..1585d08e43 100644 --- a/spec/views/admin/enterprises/index.rabl_spec.rb +++ b/spec/views/admin/enterprises/for_order_cycle.rabl_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -describe "admin/enterprises/index.rabl" do +describe "admin/enterprises/for_order_cycle.rabl" do let(:enterprise) { create(:distributor_enterprise) } let!(:product) { create(:simple_product, supplier: enterprise) } let!(:deleted_product) { create(:simple_product, supplier: enterprise, deleted_at: 1.day.ago) } - let(:render) { Rabl.render([enterprise], 'admin/enterprises/index', view_path: 'app/views', scope: RablHelper::FakeContext.instance) } + let(:render) { Rabl.render([enterprise], 'admin/enterprises/for_order_cycle', view_path: 'app/views', scope: RablHelper::FakeContext.instance) } describe "supplied products" do it "does not render deleted products" do From ac48613556b3572a69616936749ddfac75ce7224 Mon Sep 17 00:00:00 2001 From: digital dreamer Date: Fri, 29 Aug 2014 13:42:27 +0200 Subject: [PATCH 08/35] Fix #242 - foreign key constraint error on load_sample_data --- lib/tasks/dev.rake | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 8479b08ba5..bfa57a07dd 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -32,7 +32,6 @@ namespace :openfoodnetwork do # -- Addresses unless Spree::Address.find_by_zipcode "3160" puts "[#{task_name}] Seeding addresses" - Spree::Address.delete_all FactoryGirl.create(:address, :address1 => "25 Myrtle Street", :zipcode => "3153", :city => "Bayswater") FactoryGirl.create(:address, :address1 => "6 Rollings Road", :zipcode => "3156", :city => "Upper Ferntree Gully") From 66f20a6b8a8acf3d6e4c52ad1f205f478b2d11d5 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 1 Sep 2014 09:17:38 +1000 Subject: [PATCH 09/35] Name test enterprises semantically --- .../admin/bulk_product_update_spec.rb | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index ccddba5508..d1dfc2b129 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -729,20 +729,20 @@ feature %q{ end context "as an enterprise manager" do - let(:s1) { create(:supplier_enterprise, name: 'First Supplier') } - let(:s2) { create(:supplier_enterprise, name: 'Another Supplier') } - let(:s3) { create(:supplier_enterprise, name: 'Yet Another Supplier') } - let(:d1) { create(:distributor_enterprise, name: 'First Distributor') } - let(:d2) { create(:distributor_enterprise, name: 'Another Distributor') } - let!(:product_supplied) { create(:product, supplier: s1, price: 10.0, on_hand: 6) } - let!(:product_not_supplied) { create(:product, supplier: s3) } - let(:product_supplied_inactive) { create(:product, supplier: s1, price: 10.0, on_hand: 6, available_on: 1.week.from_now) } + let(:supplier_managed1) { create(:supplier_enterprise, name: 'Supplier Managed 1') } + let(:supplier_managed2) { create(:supplier_enterprise, name: 'Supplier Managed 2') } + let(:supplier_unmanaged) { create(:supplier_enterprise, name: 'Supplier Unmanaged') } + let(:distributor_managed) { create(:distributor_enterprise, name: 'Distributor Managed') } + let(:distributor_unmanaged) { create(:distributor_enterprise, name: 'Distributor Unmanaged') } + let!(:product_supplied) { create(:product, supplier: supplier_managed1, price: 10.0, on_hand: 6) } + let!(:product_not_supplied) { create(:product, supplier: supplier_unmanaged) } + let(:product_supplied_inactive) { create(:product, supplier: supplier_managed1, price: 10.0, on_hand: 6, available_on: 1.week.from_now) } - before(:each) do + before do @enterprise_user = create_enterprise_user - @enterprise_user.enterprise_roles.build(enterprise: s1).save - @enterprise_user.enterprise_roles.build(enterprise: s2).save - @enterprise_user.enterprise_roles.build(enterprise: d1).save + @enterprise_user.enterprise_roles.build(enterprise: supplier_managed1).save + @enterprise_user.enterprise_roles.build(enterprise: supplier_managed2).save + @enterprise_user.enterprise_roles.build(enterprise: distributor_managed).save login_to_admin_as @enterprise_user end @@ -757,8 +757,8 @@ feature %q{ it "shows only suppliers that I manage" do visit '/admin/products/bulk_edit' - expect(page).to have_select 'producer', with_options: [s1.name, s2.name], selected: s1.name - expect(page).to have_no_select 'producer', with_options: [s3.name] + expect(page).to have_select 'producer', with_options: [supplier_managed1.name, supplier_managed2.name], selected: supplier_managed1.name + expect(page).to have_no_select 'producer', with_options: [supplier_unmanaged.name] end it "shows inactive products that I supply" do @@ -777,13 +777,13 @@ feature %q{ first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click expect(page).to have_field "product_name", with: p.name - expect(page).to have_select "producer", selected: s1.name + expect(page).to have_select "producer", selected: supplier_managed1.name expect(page).to have_field "available_on", with: p.available_on.strftime("%F %T") expect(page).to have_field "price", with: "10.0" expect(page).to have_field "on_hand", with: "6" fill_in "product_name", with: "Big Bag Of Potatoes" - select(s2.name, :from => 'producer') + select(supplier_managed2.name, :from => 'producer') fill_in "available_on", with: (Date.today-3).strftime("%F %T") fill_in "price", with: "20" select "Weight (kg)", from: "variant_unit_with_scale" @@ -795,7 +795,7 @@ feature %q{ p.reload expect(p.name).to eq "Big Bag Of Potatoes" - expect(p.supplier).to eq s2 + expect(p.supplier).to eq supplier_managed2 expect(p.variant_unit).to eq "weight" expect(p.variant_unit_scale).to eq 1000 # Kg expect(p.available_on).to eq 3.days.ago.beginning_of_day From e0645dfbd91e12d16e2833df985332bcf9b11245 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 1 Sep 2014 09:42:50 +1000 Subject: [PATCH 10/35] Fetch managed products via OpenFoodNetwork::Permissions --- .../spree/api/products_controller_decorator.rb | 8 +++++++- lib/open_food_network/permissions.rb | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/controllers/spree/api/products_controller_decorator.rb b/app/controllers/spree/api/products_controller_decorator.rb index 7e81e1f789..65765e03ca 100644 --- a/app/controllers/spree/api/products_controller_decorator.rb +++ b/app/controllers/spree/api/products_controller_decorator.rb @@ -1,3 +1,5 @@ +require 'open_food_network/permissions' + Spree::Api::ProductsController.class_eval do def managed authorize! :admin, Spree::Product @@ -8,7 +10,11 @@ Spree::Api::ProductsController.class_eval do end def bulk_products - @products = product_scope.ransack(params[:q]).result.managed_by(current_api_user).page(params[:page]).per(params[:per_page]) + @products = OpenFoodNetwork::Permissions.new(current_api_user).managed_products. + merge(product_scope). + ransack(params[:q]).result. + page(params[:page]).per(params[:per_page]) + render text: { products: ActiveModel::ArraySerializer.new(@products, each_serializer: Spree::Api::ProductSerializer), pages: @products.num_pages }.to_json end diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 99c0aa0c15..fbd85d933b 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -18,6 +18,10 @@ module OpenFoodNetwork order_cycle.exchanges.to_enterprises(enterprises).from_enterprises(enterprises) end + def managed_products + Spree::Product.managed_by(@user) + end + private From cfb31b46e48fb765976b27c04b99ffcd8ceba997 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 1 Sep 2014 10:13:59 +1000 Subject: [PATCH 11/35] Bulk product edit lists managed products --- lib/open_food_network/permissions.rb | 12 ++++++- .../admin/bulk_product_update_spec.rb | 26 +++++++++++----- .../lib/open_food_network/permissions_spec.rb | 31 +++++++++++++++++++ 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index fbd85d933b..f35a7db602 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -19,7 +19,9 @@ module OpenFoodNetwork end def managed_products - Spree::Product.managed_by(@user) + managed_enterprise_products_ids = managed_enterprise_products.pluck :id + permitted_enterprise_products_ids = related_enterprise_products.pluck :id + Spree::Product.where('id IN (?)', managed_enterprise_products_ids + permitted_enterprise_products_ids) end @@ -37,5 +39,13 @@ module OpenFoodNetwork Enterprise.where('id IN (?)', parent_ids) end + + def managed_enterprise_products + Spree::Product.managed_by(@user) + end + + def related_enterprise_products + Spree::Product.where('supplier_id IN (?)', related_enterprises_with(:manage_products)) + end end end diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index d1dfc2b129..90f83bb26d 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -732,12 +732,21 @@ feature %q{ let(:supplier_managed1) { create(:supplier_enterprise, name: 'Supplier Managed 1') } let(:supplier_managed2) { create(:supplier_enterprise, name: 'Supplier Managed 2') } let(:supplier_unmanaged) { create(:supplier_enterprise, name: 'Supplier Unmanaged') } + let(:supplier_permitted) { create(:supplier_enterprise, name: 'Supplier Permitted') } let(:distributor_managed) { create(:distributor_enterprise, name: 'Distributor Managed') } let(:distributor_unmanaged) { create(:distributor_enterprise, name: 'Distributor Unmanaged') } let!(:product_supplied) { create(:product, supplier: supplier_managed1, price: 10.0, on_hand: 6) } let!(:product_not_supplied) { create(:product, supplier: supplier_unmanaged) } + let!(:product_supplied_permitted) { create(:product, name: 'Product Permitted', supplier: supplier_permitted, price: 10.0, on_hand: 6) } let(:product_supplied_inactive) { create(:product, supplier: supplier_managed1, price: 10.0, on_hand: 6, available_on: 1.week.from_now) } + let!(:supplier_permitted_relationship) do + create(:enterprise_relationship, parent: supplier_permitted, child: supplier_managed1, + permissions_list: [:manage_products]) + end + + use_short_wait + before do @enterprise_user = create_enterprise_user @enterprise_user.enterprise_roles.build(enterprise: supplier_managed1).save @@ -751,6 +760,7 @@ feature %q{ visit '/admin/products/bulk_edit' expect(page).to have_field 'product_name', with: product_supplied.name + expect(page).to have_field 'product_name', with: product_supplied_permitted.name expect(page).to have_no_field 'product_name', with: product_not_supplied.name end @@ -782,13 +792,15 @@ feature %q{ expect(page).to have_field "price", with: "10.0" expect(page).to have_field "on_hand", with: "6" - fill_in "product_name", with: "Big Bag Of Potatoes" - select(supplier_managed2.name, :from => 'producer') - fill_in "available_on", with: (Date.today-3).strftime("%F %T") - fill_in "price", with: "20" - select "Weight (kg)", from: "variant_unit_with_scale" - fill_in "on_hand", with: "18" - fill_in "display_as", with: "Big Bag" + within("tr#p_#{product_supplied.id}") do + fill_in "product_name", with: "Big Bag Of Potatoes" + select(supplier_managed2.name, :from => 'producer') + fill_in "available_on", with: (Date.today-3).strftime("%F %T") + fill_in "price", with: "20" + select "Weight (kg)", from: "variant_unit_with_scale" + fill_in "on_hand", with: "18" + fill_in "display_as", with: "Big Bag" + end click_button 'Save Changes' expect(page.find("#update-status-message")).to have_content "Changes saved." diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index b0a004cbf1..bbab0d7175 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -55,6 +55,26 @@ module OpenFoodNetwork end end + describe "finding managed products" do + let!(:p1) { create(:simple_product) } + let!(:p2) { create(:simple_product) } + + before do + permissions.stub(:managed_enterprise_products) { Spree::Product.where('1=0') } + permissions.stub(:related_enterprise_products) { Spree::Product.where('1=0') } + end + + it "returns products produced by managed enterprises" do + permissions.stub(:managed_enterprise_products) { Spree::Product.where(id: p1) } + permissions.managed_products.should == [p1] + end + + it "returns products produced by permitted enterprises" do + permissions.stub(:related_enterprise_products) { Spree::Product.where(id: p2) } + permissions.managed_products.should == [p2] + end + end + ######################################## describe "finding related enterprises with a particular permission" do @@ -70,5 +90,16 @@ module OpenFoodNetwork permissions.send(:related_enterprises_with, permission).should == [] end end + + describe "finding the supplied products of related enterprises" do + let!(:e) { create(:enterprise) } + let!(:p) { create(:simple_product, supplier: e) } + + it "returns supplied products" do + permissions.should_receive(:related_enterprises_with).with(:manage_products) { [e] } + + permissions.send(:related_enterprise_products).should == [p] + end + end end end From 4d8d74dec7d230660ef9bce6e94da56272de1f4b Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 1 Sep 2014 10:36:59 +1000 Subject: [PATCH 12/35] Find enterprises that we manage products for --- lib/open_food_network/permissions.rb | 17 +++++-- .../lib/open_food_network/permissions_spec.rb | 49 ++++++++++++++----- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index f35a7db602..3d5df1e804 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -6,10 +6,7 @@ module OpenFoodNetwork # Find enterprises that an admin is allowed to add to an order cycle def order_cycle_enterprises - managed_enterprise_ids = managed_enterprises.pluck :id - permitted_enterprise_ids = related_enterprises_with(:add_to_order_cycle).pluck :id - - Enterprise.where('id IN (?)', managed_enterprise_ids + permitted_enterprise_ids) + managed_and_related_enterprises_with :add_to_order_cycle end # Find the exchanges of an order cycle that an admin can manage @@ -24,6 +21,10 @@ module OpenFoodNetwork Spree::Product.where('id IN (?)', managed_enterprise_products_ids + permitted_enterprise_products_ids) end + def managed_product_enterprises + managed_and_related_enterprises_with :manage_products + end + private @@ -40,6 +41,14 @@ module OpenFoodNetwork Enterprise.where('id IN (?)', parent_ids) end + def managed_and_related_enterprises_with(permission) + managed_enterprise_ids = managed_enterprises.pluck :id + permitted_enterprise_ids = related_enterprises_with(permission).pluck :id + + Enterprise.where('id IN (?)', managed_enterprise_ids + permitted_enterprise_ids) + end + + def managed_enterprise_products Spree::Product.managed_by(@user) end diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index bbab0d7175..bcd1f8b742 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -9,19 +9,15 @@ module OpenFoodNetwork let(:e2) { create(:enterprise) } describe "finding enterprises that can be added to an order cycle" do - before do - permissions.stub(:managed_enterprises) { Enterprise.where('1=0') } - permissions.stub(:related_enterprises_with) { Enterprise.where('1=0') } - end + let(:e) { double(:enterprise) } - it "returns managed enterprises" do - permissions.stub(:managed_enterprises) { Enterprise.where(id: e1) } - permissions.order_cycle_enterprises.should == [e1] - end + it "returns managed and related enterprises with add_to_order_cycle permission" do + permissions. + should_receive(:managed_and_related_enterprises_with). + with(:add_to_order_cycle). + and_return([e]) - it "returns permitted enterprises" do - permissions.stub(:related_enterprises_with) { Enterprise.where(id: e2) } - permissions.order_cycle_enterprises.should == [e2] + permissions.order_cycle_enterprises.should == [e] end end @@ -75,6 +71,19 @@ module OpenFoodNetwork end end + describe "finding enterprises that we manage products for" do + let(:e) { double(:enterprise) } + + it "returns managed and related enterprises with manage_products permission" do + permissions. + should_receive(:managed_and_related_enterprises_with). + with(:manage_products). + and_return([e]) + + permissions.managed_product_enterprises.should == [e] + end + end + ######################################## describe "finding related enterprises with a particular permission" do @@ -91,6 +100,24 @@ module OpenFoodNetwork end end + describe "finding enterprises that are managed or with a particular permission" do + before do + permissions.stub(:managed_enterprises) { Enterprise.where('1=0') } + permissions.stub(:related_enterprises_with) { Enterprise.where('1=0') } + end + + it "returns managed enterprises" do + permissions.should_receive(:managed_enterprises) { Enterprise.where(id: e1) } + permissions.send(:managed_and_related_enterprises_with, permission).should == [e1] + end + + it "returns permitted enterprises" do + permissions.should_receive(:related_enterprises_with).with(permission). + and_return(Enterprise.where(id: e2)) + permissions.send(:managed_and_related_enterprises_with, permission).should == [e2] + end + end + describe "finding the supplied products of related enterprises" do let!(:e) { create(:enterprise) } let!(:p) { create(:simple_product, supplier: e) } From c81503d95f3939e20fc335b6458e73cb5e6b98cb Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 1 Sep 2014 10:44:09 +1000 Subject: [PATCH 13/35] Include producers I have permission to in BPE producers choice --- app/controllers/spree/admin/products_controller_decorator.rb | 2 +- spec/features/admin/bulk_product_update_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/spree/admin/products_controller_decorator.rb b/app/controllers/spree/admin/products_controller_decorator.rb index 9f4fdbe46b..7fc9984241 100644 --- a/app/controllers/spree/admin/products_controller_decorator.rb +++ b/app/controllers/spree/admin/products_controller_decorator.rb @@ -85,7 +85,7 @@ Spree::Admin::ProductsController.class_eval do def load_bpe_data current_user.generate_spree_api_key! unless spree_current_user.spree_api_key @spree_api_key = spree_current_user.spree_api_key - @producers = Enterprise.managed_by(spree_current_user).is_primary_producer.order(:name) + @producers = OpenFoodNetwork::Permissions.new(spree_current_user).managed_product_enterprises.is_primary_producer.by_name @taxons = Spree::Taxon.order(:name) end end diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index 90f83bb26d..c4dbba32fd 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -764,10 +764,10 @@ feature %q{ expect(page).to have_no_field 'product_name', with: product_not_supplied.name end - it "shows only suppliers that I manage" do + it "shows only suppliers that I manage or have permission to" do visit '/admin/products/bulk_edit' - expect(page).to have_select 'producer', with_options: [supplier_managed1.name, supplier_managed2.name], selected: supplier_managed1.name + expect(page).to have_select 'producer', with_options: [supplier_managed1.name, supplier_managed2.name, supplier_permitted.name], selected: supplier_managed1.name expect(page).to have_no_select 'producer', with_options: [supplier_unmanaged.name] end From 94683f1eaa153e1287665902f95ee92fbc5aca3d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 1 Sep 2014 11:15:00 +1000 Subject: [PATCH 14/35] Check authorisation for bulk update products --- .../admin/products_controller_decorator.rb | 3 +++ .../spree/admin/products_controller_spec.rb | 21 +++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/controllers/spree/admin/products_controller_decorator.rb b/app/controllers/spree/admin/products_controller_decorator.rb index 7fc9984241..a5b38126e5 100644 --- a/app/controllers/spree/admin/products_controller_decorator.rb +++ b/app/controllers/spree/admin/products_controller_decorator.rb @@ -30,6 +30,9 @@ Spree::Admin::ProductsController.class_eval do "#{string}q[#{filter[:property][:db_column]}_#{filter[:predicate][:predicate]}]=#{filter[:value]};" end + # Ensure we're authorised to update all products + product_set.collection.each { |p| authorize! :update, p } + if product_set.save redirect_to "/api/products/bulk_products?page=1;per_page=500;#{bulk_index_query}" else diff --git a/spec/controllers/spree/admin/products_controller_spec.rb b/spec/controllers/spree/admin/products_controller_spec.rb index f05be2bb69..9746ffa0e2 100644 --- a/spec/controllers/spree/admin/products_controller_spec.rb +++ b/spec/controllers/spree/admin/products_controller_spec.rb @@ -1,11 +1,28 @@ require 'spec_helper' describe Spree::Admin::ProductsController do - context "Creating a new product" do + describe "updating a product we do not have access to" do + let(:s_managed) { create(:enterprise) } + let(:s_unmanaged) { create(:enterprise) } + let(:p) { create(:simple_product, supplier: s_unmanaged, name: 'Peas') } + before do - login_as_admin + login_as_enterprise_user [s_managed] + spree_post :bulk_update, {"products" => [{"id" => p.id, "name" => "Pine nuts"}]} end + it "denies access" do + response.should redirect_to "http://test.host/unauthorized" + end + + it "does not update any product" do + p.reload.name.should_not == "Pine nuts" + end + end + + context "creating a new product" do + before { login_as_admin } + it "redirects to bulk_edit when the user hits 'create'" do s = create(:supplier_enterprise) t = create(:taxon) From e72c3d861b5b8a579163282bd151a88e8b91238c Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 1 Sep 2014 11:49:09 +1000 Subject: [PATCH 15/35] Enterprise manager can edit products from enterprises it has manage_products permission on --- app/models/spree/ability_decorator.rb | 4 ++-- spec/features/admin/bulk_product_update_spec.rb | 14 +++++++------- spec/models/spree/ability_spec.rb | 17 ++++++++++++++--- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 68d98bd568..9f9c96cb15 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -43,12 +43,12 @@ class AbilityDecorator # 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| - user.enterprises.include? product.supplier + OpenFoodNetwork::Permissions.new(user).managed_product_enterprises.include? product.supplier end can [:create], Spree::Variant can [:admin, :index, :read, :edit, :update, :search, :destroy], Spree::Variant do |variant| - user.enterprises.include? variant.product.supplier + OpenFoodNetwork::Permissions.new(user).managed_product_enterprises.include? variant.product.supplier end can [:admin, :index, :read, :create, :edit, :update_positions, :destroy], Spree::ProductProperty diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index c4dbba32fd..9f530f860c 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -780,19 +780,19 @@ feature %q{ end it "allows me to update a product" do - p = product_supplied + p = product_supplied_permitted visit '/admin/products/bulk_edit' first("div#columns_dropdown", :text => "COLUMNS").click first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click - expect(page).to have_field "product_name", with: p.name - expect(page).to have_select "producer", selected: supplier_managed1.name - expect(page).to have_field "available_on", with: p.available_on.strftime("%F %T") - expect(page).to have_field "price", with: "10.0" - expect(page).to have_field "on_hand", with: "6" + within "tr#p_#{p.id}" do + expect(page).to have_field "product_name", with: p.name + expect(page).to have_select "producer", selected: supplier_permitted.name + expect(page).to have_field "available_on", with: p.available_on.strftime("%F %T") + expect(page).to have_field "price", with: "10.0" + expect(page).to have_field "on_hand", with: "6" - within("tr#p_#{product_supplied.id}") do fill_in "product_name", with: "Big Bag Of Potatoes" select(supplier_managed2.name, :from => 'producer') fill_in "available_on", with: (Date.today-3).strftime("%F %T") diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index 9e5ae49780..cf17d7fe3a 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -51,14 +51,17 @@ module Spree # create enterprises let(:s1) { create(:supplier_enterprise) } let(:s2) { create(:supplier_enterprise) } + let(:s_related) { create(:supplier_enterprise) } let(:d1) { create(:distributor_enterprise) } let(:d2) { create(:distributor_enterprise) } let(:p1) { create(:product, supplier: s1, distributors:[d1, d2]) } let(:p2) { create(:product, supplier: s2, distributors:[d1, d2]) } + let(:p_related) { create(:product, supplier: s_related) } let(:er1) { create(:enterprise_relationship, parent: s1, child: d1) } let(:er2) { create(:enterprise_relationship, parent: d1, child: s1) } + let(:er_p) { create(:enterprise_relationship, parent: s_related, child: s1, permissions_list: [:manage_products]) } subject { user } let(:user) { nil } @@ -74,12 +77,20 @@ module Spree let(:order) {create(:order)} - it "should be able to read/write their enterprises' products" do + it "should be able to read/write their enterprises' products and variants" do should have_ability([:admin, :read, :update, :product_distributions, :bulk_edit, :bulk_update, :clone, :destroy], for: p1) + should have_ability([:admin, :index, :read, :edit, :update, :search, :destroy], for: p1.master) end - it "should not be able to read/write other enterprises' products" do + it "should be able to read/write related enterprises' products and variants with manage_products permission" do + er_p + should have_ability([:admin, :read, :update, :product_distributions, :bulk_edit, :bulk_update, :clone, :destroy], for: p_related) + should have_ability([:admin, :index, :read, :edit, :update, :search, :destroy], for: p_related.master) + end + + it "should not be able to read/write other enterprises' products and variants" do should_not have_ability([:admin, :read, :update, :product_distributions, :bulk_edit, :bulk_update, :clone, :destroy], for: p2) + should_not have_ability([:admin, :index, :read, :edit, :update, :search, :destroy], for: p2.master) end it "should not be able to access admin actions on orders" do @@ -247,7 +258,7 @@ module Spree end end - context 'Enterprise manager' do + context 'enterprise manager' do let (:user) do user = create(:user) user.spree_roles = [] From 2cd5afbf9ccbc68fa86f6635110d197c4e242e23 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 1 Sep 2014 16:21:23 +1000 Subject: [PATCH 16/35] Set product.largeImage in JS, use for product modal --- .../darkswarm/services/products.js.coffee | 1 + .../javascripts/templates/product_modal.html.haml | 2 +- .../unit/darkswarm/services/product_spec.js.coffee | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/darkswarm/services/products.js.coffee b/app/assets/javascripts/darkswarm/services/products.js.coffee index 5819c7b661..9da3187f82 100644 --- a/app/assets/javascripts/darkswarm/services/products.js.coffee +++ b/app/assets/javascripts/darkswarm/services/products.js.coffee @@ -48,3 +48,4 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Car product.primaryImage = product.images[0]?.small_url if product.images product.primaryImageOrMissing = product.primaryImage || "/assets/noimage/small.png" + product.largeImage = product.images[0]?.large_url if product.images diff --git a/app/assets/javascripts/templates/product_modal.html.haml b/app/assets/javascripts/templates/product_modal.html.haml index 31f51c6655..82cda0a371 100644 --- a/app/assets/javascripts/templates/product_modal.html.haml +++ b/app/assets/javascripts/templates/product_modal.html.haml @@ -1,6 +1,6 @@ .row .columns.small-12.large-6 - %img.product-img{"ng-src" => "{{product.primaryImage}}", "ng-if" => "product.primaryImage"} + %img.product-img{"ng-src" => "{{product.largeImage}}", "ng-if" => "product.largeImage"} .columns.small-12.large-6.product-header %h2 / %render-svg{path: "{{product.primary_taxon.icon}}"} diff --git a/spec/javascripts/unit/darkswarm/services/product_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/product_spec.js.coffee index d7c5a20b91..27e3f5aff6 100644 --- a/spec/javascripts/unit/darkswarm/services/product_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/product_spec.js.coffee @@ -7,6 +7,7 @@ describe 'Products service', -> CurrentHubMock = {} currentOrder = null product = null + productWithImage = null beforeEach -> product = @@ -16,6 +17,14 @@ describe 'Products service', -> price: 11 master: {} variants: [] + productWithImage = + supplier: + id: 9 + master: {} + variants: [] + images: [ + large_url: 'foo.png' + ] currentOrder = line_items: [] @@ -62,6 +71,11 @@ describe 'Products service', -> expect(Products.products[0].primaryImage).toBeUndefined() expect(Products.products[0].primaryImageOrMissing).toEqual "/assets/noimage/small.png" + it "sets largeImage", -> + $httpBackend.expectGET("/shop/products").respond([productWithImage]) + $httpBackend.flush() + expect(Products.products[0].largeImage).toEqual("foo.png") + describe "determining the price to display for a product", -> it "displays the product price when the product does not have variants", -> $httpBackend.expectGET("/shop/products").respond([product]) From 818a0f859551ef57f7714096978d9e204825a207 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 2 Sep 2014 13:45:11 +1000 Subject: [PATCH 17/35] Convert admin login nav to haml --- app/views/spree/layouts/admin/_login_nav.html.erb | 8 -------- app/views/spree/layouts/admin/_login_nav.html.haml | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 8 deletions(-) delete mode 100644 app/views/spree/layouts/admin/_login_nav.html.erb create mode 100644 app/views/spree/layouts/admin/_login_nav.html.haml diff --git a/app/views/spree/layouts/admin/_login_nav.html.erb b/app/views/spree/layouts/admin/_login_nav.html.erb deleted file mode 100644 index 7ab28b3344..0000000000 --- a/app/views/spree/layouts/admin/_login_nav.html.erb +++ /dev/null @@ -1,8 +0,0 @@ -<% if spree_current_user %> -
      -
    • <%= t(:logged_in_as) %>: <%= spree_current_user.email %>
    • -
    • <%= link_to t(:account), spree.edit_user_path(spree_current_user) %>
    • -
    • <%= link_to t(:logout), spree.logout_path %>
    • -
    • <%= link_to t(:store), spree.root_path, :target => '_blank' %>
    • -
    -<% end %> diff --git a/app/views/spree/layouts/admin/_login_nav.html.haml b/app/views/spree/layouts/admin/_login_nav.html.haml new file mode 100644 index 0000000000..4ecb72d148 --- /dev/null +++ b/app/views/spree/layouts/admin/_login_nav.html.haml @@ -0,0 +1,14 @@ +- if spree_current_user + %ul#login-nav.inline-menu + %li{"data-hook" => "user-logged-in-as"} + = t(:logged_in_as) + \: #{spree_current_user.email} + %li{"data-hook" => "user-account-link"} + %i.icon-user + = link_to t(:account), spree.edit_user_path(spree_current_user) + %li{"data-hook" => "user-logout-link"} + %i.icon-signout + = link_to t(:logout), spree.logout_path + %li{"data-hook" => "store-frontend-link"} + %i.icon-external-link + = link_to t(:store), spree.root_path, :target => '_blank' From 0fd496aa97d42a16c2f46cc2f5034cb87c0096e9 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 2 Sep 2014 15:27:33 +1000 Subject: [PATCH 18/35] Extract bulk product edit interface into partials --- .../spree/admin/products/bulk_edit.html.haml | 145 +----------------- .../products/bulk_edit/_actions.html.haml | 14 ++ .../admin/products/bulk_edit/_data.html.haml | 2 + .../products/bulk_edit/_filters.html.haml | 18 +++ .../products/bulk_edit/_header.html.haml | 12 ++ .../products/bulk_edit/_indicators.html.haml | 9 ++ .../products/bulk_edit/_products.html.haml | 9 ++ .../bulk_edit/_products_head.html.haml | 28 ++++ .../bulk_edit/_products_product.html.haml | 30 ++++ .../bulk_edit/_products_variant.html.haml | 23 +++ 10 files changed, 152 insertions(+), 138 deletions(-) create mode 100644 app/views/spree/admin/products/bulk_edit/_actions.html.haml create mode 100644 app/views/spree/admin/products/bulk_edit/_data.html.haml create mode 100644 app/views/spree/admin/products/bulk_edit/_filters.html.haml create mode 100644 app/views/spree/admin/products/bulk_edit/_header.html.haml create mode 100644 app/views/spree/admin/products/bulk_edit/_indicators.html.haml create mode 100644 app/views/spree/admin/products/bulk_edit/_products.html.haml create mode 100644 app/views/spree/admin/products/bulk_edit/_products_head.html.haml create mode 100644 app/views/spree/admin/products/bulk_edit/_products_product.html.haml create mode 100644 app/views/spree/admin/products/bulk_edit/_products_variant.html.haml diff --git a/app/views/spree/admin/products/bulk_edit.html.haml b/app/views/spree/admin/products/bulk_edit.html.haml index 206a336a5e..fa322931cd 100644 --- a/app/views/spree/admin/products/bulk_edit.html.haml +++ b/app/views/spree/admin/products/bulk_edit.html.haml @@ -1,141 +1,10 @@ -- content_for :page_title do - = "Bulk Edit Products" += render 'spree/admin/products/bulk_edit/header' += render 'spree/admin/products/bulk_edit/data' -- content_for :page_actions do - %div{ :class => "toolbar", 'data-hook' => "toolbar" } - %ul{ :class => "actions header-action-links inline-menu" } - %li#new_product_link - = button_link_to t(:new_product), new_object_url, { :remote => true, :icon => 'icon-plus', :id => 'admin_new_product' } - -= render :partial => 'spree/admin/shared/product_sub_menu' - -%div#new_product(data-hook) - - -=admin_inject_producers -=admin_inject_taxons %div{ 'ng-app' => 'ofn.admin', 'ng-controller' => 'AdminProductEditCtrl', 'ng-init' => "initialise('#{@spree_api_key}');loading=true;" } - %div.sixteen.columns.alpha - %div.quick_search{ :class => "four columns alpha" } - %label{ :for => 'quick_filter' } - %br - %input.search{ :class => "four columns alpha", 'ng-model' => 'query', :name => "quick_filter", :type => 'text', 'placeholder' => 'Quick Search' } - .filter_select{ :class => "four columns" } - %label{ :for => 'producer_filter' }Producer - %br - %select{ :class => "four columns alpha", :id => 'producer_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'producerFilter', 'ng-options' => 'producer.id as producer.name for producer in filterProducers' } - .filter_select{ :class => "four columns" } - %label{ :for => 'category_filter' }Category - %br - %select{ :class => "four columns alpha", :id => 'category_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'categoryFilter', 'ng-options' => 'taxon.id as taxon.name for taxon in filterTaxons'} - %div{ :class => "one column" }   - .filter_clear{ :class => "three columns omega" } - %label{ :for => 'clear_all_filters' } - %br - %input.fullwidth{ :type => 'button', :id => 'clear_all_filters', :value => "Clear Filters", 'ng-click' => "resetSelectFilters()" } - %hr.sixteen.columns.alpha - %div.sixteen.columns.alpha{ 'ng-hide' => 'loading || products.length == 0', style: "margin-bottom: 10px" } - %div.four.columns.alpha - %input.four.columns.alpha{ :type => 'button', :value => 'Save Changes', 'ng-click' => 'submitProducts()'} - %div.nine.columns - %h6{ id: "update-status-message", ng: { style: 'updateStatusMessage.style' } } - {{ updateStatusMessage.text || " " }} - %div.three.columns.omega - %div.ofn_drop_down.three.columns.omega{ 'ng-controller' => "DropDownCtrl", :id => "columns_dropdown", 'ofn-drop-down' => true, :style => 'float:right;' } - %span{ :class => 'icon-reorder' }   Columns - %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } - %div.menu{ 'ng-show' => "expanded" } - %div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true } - %span{ :class => 'one column alpha', :style => 'text-align: center'} {{ column.visible && "✓" || !column.visible && " " }} - %span{ :class => 'two columns omega' } {{column.name }} - %div{ 'ng-show' => '!spree_api_key_ok' } - {{ api_error_msg }} - %div.sixteen.columns.alpha#loading{ 'ng-if' => 'loading' } - %img.spinner{ src: "/assets/loading.gif" } - %h1 LOADING PRODUCTS - %div.sixteen.columns.alpha{ 'ng-show' => '!loading && filteredProducts.length == 0' } - %h1#no_results No products found. - %div.sixteen.columns.alpha{ 'ng-hide' => 'loading || filteredProducts.length == 0' } - %table.index#listing_products.bulk{ "infinite-scroll" => "incrementLimit()", "infinite-scroll-distance" => "1" } - %colgroup - %col.actions - %col.producer{ ng: { show: 'columns.producer.visible' } } - %col.name{ ng: { show: 'columns.name.visible' } } - %col.unit{ ng: { show: 'columns.unit.visible' } } - %col.display_as{ ng: { show: 'columns.unit.visible' } } - %col.price{ ng: { show: 'columns.price.visible'} } - %col.on_hand{ ng: { show: 'columns.on_hand.visible' } } - %col.category{ ng: { show: 'columns.category.visible' } } - %col.available_on{ ng: { show: 'columns.available_on.visible' } } - %col.actions - %col.actions - %col.actions - %thead - %tr - %th.left-actions - %th.producer{ 'ng-show' => 'columns.producer.visible' } Producer - %th.name{ 'ng-show' => 'columns.name.visible' } Name - %th.unit{ 'ng-show' => 'columns.unit.visible' } Unit / Value - %th.display_as{ 'ng-show' => 'columns.unit.visible' } Display As - %th.price{ 'ng-show' => 'columns.price.visible' } Price - %th.on_hand{ 'ng-show' => 'columns.on_hand.visible' } On Hand - %th.category{ 'ng-show' => 'columns.category.visible' } Category - %th.available_on{ 'ng-show' => 'columns.available_on.visible' } Av. On - %th.actions - %th.actions - %th.actions - %tbody{ 'ng-repeat' => 'product in filteredProducts = ( products | filter:query | producer: producerFilter | category: categoryFilter | limitTo:limit )', 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'" } - %tr.product{ :id => "p_{{product.id}}" } - %td.left-actions - %a{ 'ofn-toggle-variants' => 'true', :class => "view-variants icon-chevron-right", 'ng-show' => 'hasVariants(product)' } - %a{ :class => "add-variant icon-plus-sign", 'ng-click' => "addVariant(product)", 'ng-show' => "!hasVariants(product) && hasUnit(product)" } - %td.producer{ 'ng-show' => 'columns.producer.visible' } - %select.select2.fullwidth{ 'ng-model' => 'product.producer', :name => 'producer', 'ofn-track-product' => 'producer', 'ng-options' => 'producer.id as producer.name for producer in producers' } - %td.name{ 'ng-show' => 'columns.name.visible' } - %input{ 'ng-model' => "product.name", :name => 'product_name', 'ofn-track-product' => 'name', :type => 'text' } - %td.unit{ 'ng-show' => 'columns.unit.visible' } - %select.select2{ 'ng-model' => 'product.variant_unit_with_scale', :name => 'variant_unit_with_scale', 'ofn-track-product' => 'variant_unit_with_scale', 'ng-options' => 'unit[1] as unit[0] for unit in variant_unit_options' } - %option{'value' => '', 'ng-hide' => "hasVariants(product) && hasUnit(product)"} - %input{ 'ng-model' => 'product.master.unit_value_with_description', :name => 'master_unit_value_with_description', 'ofn-track-master' => 'unit_value_with_description', :type => 'text', :placeholder => 'value', 'ng-show' => "!hasVariants(product) && hasUnit(product)", 'ofn-maintain-unit-scale' => true } - %input{ 'ng-model' => 'product.variant_unit_name', :name => 'variant_unit_name', 'ofn-track-product' => 'variant_unit_name', :placeholder => 'unit', 'ng-show' => "product.variant_unit_with_scale == 'items'", :type => 'text' } - %td.display_as{ 'ng-show' => 'columns.unit.visible' } - %input{ 'ofn-display-as' => 'product.master', name: 'display_as', 'ofn-track-master' => 'display_as', type: 'text', placeholder: '{{ placeholder_text }}', ng: { hide: 'hasVariants(product)', model: 'product.master.display_as' } } - %td.price{ 'ng-show' => 'columns.price.visible' } - %input{ 'ng-model' => 'product.price', 'ofn-decimal' => :true, :name => 'price', 'ofn-track-product' => 'price', :type => 'text', 'ng-hide' => 'hasVariants(product)' } - %td.on_hand{ 'ng-show' => 'columns.on_hand.visible' } - %span{ 'ng-bind' => 'product.on_hand', :name => 'on_hand', 'ng-show' => '!hasOnDemandVariants(product) && (hasVariants(product) || product.on_demand)' } - %input.field{ 'ng-model' => 'product.on_hand', :name => 'on_hand', 'ofn-track-product' => 'on_hand', 'ng-hide' => 'hasVariants(product) || product.on_demand', :type => 'number' } - %td.category{ 'ng-if' => 'columns.category.visible' } - %input.fullwidth{ :type => 'text', id: "p{{product.id}}_category", 'ng-model' => 'product.category', 'ofn-taxon-autocomplete' => '', 'ofn-track-product' => 'category' } - %td.available_on{ 'ng-show' => 'columns.available_on.visible' } - %input{ 'ng-model' => 'product.available_on', :name => 'available_on', 'ofn-track-product' => 'available_on', 'datetimepicker' => 'product.available_on', type: "text" } - %td.actions - %a{ 'ng-click' => 'editWarn(product)', :class => "edit-product icon-edit no-text" } - %td.actions - %a{ 'ng-click' => 'cloneProduct(product)', :class => "clone-product icon-copy no-text" } - %td.actions - %a{ 'ng-click' => 'deleteProduct(product)', :class => "delete-product icon-trash no-text" } - %tr.variant{ :id => "v_{{variant.id}}", 'ng-repeat' => 'variant in product.variants', 'ng-show' => 'displayProperties[product.id].showVariants', 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'" } - %td.left-actions - %a{ :class => "variant-item icon-caret-right", 'ng-hide' => "$last" } - %a{ :class => "add-variant icon-plus-sign", 'ng-click' => "addVariant(product)", 'ng-show' => "$last" } - %td{ 'ng-show' => 'columns.producer.visible' } - %td{ 'ng-show' => 'columns.name.visible' } - %input{ 'ng-model' => 'variant.display_name', :name => 'variant_display_name', 'ofn-track-variant' => 'display_name', :type => 'text', placeholder: "{{ product.name }}" } - %td.unit_value{ 'ng-show' => 'columns.unit.visible' } - %input{ 'ng-model' => 'variant.unit_value_with_description', :name => 'variant_unit_value_with_description', 'ofn-track-variant' => 'unit_value_with_description', :type => 'text', 'ofn-maintain-unit-scale' => true } - %td.display_as{ 'ng-show' => 'columns.unit.visible' } - %input{ 'ofn-display-as' => 'variant', 'ng-model' => 'variant.display_as', name: 'variant_display_as', 'ofn-track-variant' => 'display_as', type: 'text', placeholder: '{{ placeholder_text }}' } - %td{ 'ng-show' => 'columns.price.visible' } - %input{ 'ng-model' => 'variant.price', 'ofn-decimal' => :true, :name => 'variant_price', 'ofn-track-variant' => 'price', :type => 'text' } - %td{ 'ng-show' => 'columns.on_hand.visible' } - %input.field{ 'ng-model' => 'variant.on_hand', 'ng-change' => 'updateOnHand(product)', :name => 'variant_on_hand', 'ng-hide' => 'variant.on_demand', 'ofn-track-variant' => 'on_hand', :type => 'number' } - %span{ 'ng-bind' => 'variant.on_hand', :name => 'variant_on_hand', 'ng-show' => 'variant.on_demand' } - %td{ 'ng-show' => 'columns.category.visible' } - %td{ 'ng-show' => 'columns.available_on.visible' } - %td.actions - %a{ 'ng-click' => 'editWarn(product,variant)', :class => "edit-variant icon-edit no-text", 'ng-show' => "variantSaved(variant)" } - %td.actions - %td.actions - %a{ 'ng-click' => 'deleteVariant(product,variant)', :class => "delete-variant icon-trash no-text" } + = render 'spree/admin/products/bulk_edit/filters' + %hr.sixteen.columns.alpha + = render 'spree/admin/products/bulk_edit/actions' + = render 'spree/admin/products/bulk_edit/indicators' + = render 'spree/admin/products/bulk_edit/products' diff --git a/app/views/spree/admin/products/bulk_edit/_actions.html.haml b/app/views/spree/admin/products/bulk_edit/_actions.html.haml new file mode 100644 index 0000000000..d86f16bc09 --- /dev/null +++ b/app/views/spree/admin/products/bulk_edit/_actions.html.haml @@ -0,0 +1,14 @@ +%div.sixteen.columns.alpha{ 'ng-hide' => 'loading || products.length == 0', style: "margin-bottom: 10px" } + %div.four.columns.alpha + %input.four.columns.alpha{ :type => 'button', :value => 'Save Changes', 'ng-click' => 'submitProducts()'} + %div.nine.columns + %h6{ id: "update-status-message", ng: { style: 'updateStatusMessage.style' } } + {{ updateStatusMessage.text || " " }} + %div.three.columns.omega + %div.ofn_drop_down.three.columns.omega{ 'ng-controller' => "DropDownCtrl", :id => "columns_dropdown", 'ofn-drop-down' => true, :style => 'float:right;' } + %span{ :class => 'icon-reorder' }   Columns + %span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" } + %div.menu{ 'ng-show' => "expanded" } + %div.menu_item{ :class => "three columns alpha", 'ng-repeat' => "column in columns", 'ofn-toggle-column' => true } + %span{ :class => 'one column alpha', :style => 'text-align: center'} {{ column.visible && "✓" || !column.visible && " " }} + %span{ :class => 'two columns omega' } {{column.name }} diff --git a/app/views/spree/admin/products/bulk_edit/_data.html.haml b/app/views/spree/admin/products/bulk_edit/_data.html.haml new file mode 100644 index 0000000000..25d595bda1 --- /dev/null +++ b/app/views/spree/admin/products/bulk_edit/_data.html.haml @@ -0,0 +1,2 @@ += admin_inject_producers += admin_inject_taxons diff --git a/app/views/spree/admin/products/bulk_edit/_filters.html.haml b/app/views/spree/admin/products/bulk_edit/_filters.html.haml new file mode 100644 index 0000000000..beee7d0a6a --- /dev/null +++ b/app/views/spree/admin/products/bulk_edit/_filters.html.haml @@ -0,0 +1,18 @@ +%div.sixteen.columns.alpha + %div.quick_search{ :class => "four columns alpha" } + %label{ :for => 'quick_filter' } + %br + %input.search{ :class => "four columns alpha", 'ng-model' => 'query', :name => "quick_filter", :type => 'text', 'placeholder' => 'Quick Search' } + .filter_select{ :class => "four columns" } + %label{ :for => 'producer_filter' }Producer + %br + %select{ :class => "four columns alpha", :id => 'producer_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'producerFilter', 'ng-options' => 'producer.id as producer.name for producer in filterProducers' } + .filter_select{ :class => "four columns" } + %label{ :for => 'category_filter' }Category + %br + %select{ :class => "four columns alpha", :id => 'category_filter', 'ofn-select2-min-search' => 5, 'ng-model' => 'categoryFilter', 'ng-options' => 'taxon.id as taxon.name for taxon in filterTaxons'} + %div{ :class => "one column" }   + .filter_clear{ :class => "three columns omega" } + %label{ :for => 'clear_all_filters' } + %br + %input.fullwidth{ :type => 'button', :id => 'clear_all_filters', :value => "Clear Filters", 'ng-click' => "resetSelectFilters()" } diff --git a/app/views/spree/admin/products/bulk_edit/_header.html.haml b/app/views/spree/admin/products/bulk_edit/_header.html.haml new file mode 100644 index 0000000000..09586b3e7c --- /dev/null +++ b/app/views/spree/admin/products/bulk_edit/_header.html.haml @@ -0,0 +1,12 @@ +- content_for :page_title do + = "Bulk Edit Products" + +- content_for :page_actions do + %div{ :class => "toolbar", 'data-hook' => "toolbar" } + %ul{ :class => "actions header-action-links inline-menu" } + %li#new_product_link + = button_link_to t(:new_product), new_object_url, { :remote => true, :icon => 'icon-plus', :id => 'admin_new_product' } + += render :partial => 'spree/admin/shared/product_sub_menu' + +%div#new_product(data-hook) diff --git a/app/views/spree/admin/products/bulk_edit/_indicators.html.haml b/app/views/spree/admin/products/bulk_edit/_indicators.html.haml new file mode 100644 index 0000000000..97c8a45aa2 --- /dev/null +++ b/app/views/spree/admin/products/bulk_edit/_indicators.html.haml @@ -0,0 +1,9 @@ +%div{ 'ng-show' => '!spree_api_key_ok' } + {{ api_error_msg }} + +%div.sixteen.columns.alpha#loading{ 'ng-if' => 'loading' } + %img.spinner{ src: "/assets/loading.gif" } + %h1 LOADING PRODUCTS + +%div.sixteen.columns.alpha{ 'ng-show' => '!loading && filteredProducts.length == 0' } + %h1#no_results No products found. diff --git a/app/views/spree/admin/products/bulk_edit/_products.html.haml b/app/views/spree/admin/products/bulk_edit/_products.html.haml new file mode 100644 index 0000000000..81fb31573b --- /dev/null +++ b/app/views/spree/admin/products/bulk_edit/_products.html.haml @@ -0,0 +1,9 @@ +%div.sixteen.columns.alpha{ 'ng-hide' => 'loading || filteredProducts.length == 0' } + %table.index#listing_products.bulk{ "infinite-scroll" => "incrementLimit()", "infinite-scroll-distance" => "1" } + + = render 'spree/admin/products/bulk_edit/products_head' + + %tbody{ 'ng-repeat' => 'product in filteredProducts = ( products | filter:query | producer: producerFilter | category: categoryFilter | limitTo:limit )', 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'" } + + = render 'spree/admin/products/bulk_edit/products_product' + = render 'spree/admin/products/bulk_edit/products_variant' diff --git a/app/views/spree/admin/products/bulk_edit/_products_head.html.haml b/app/views/spree/admin/products/bulk_edit/_products_head.html.haml new file mode 100644 index 0000000000..d6be2a88e0 --- /dev/null +++ b/app/views/spree/admin/products/bulk_edit/_products_head.html.haml @@ -0,0 +1,28 @@ +%colgroup + %col.actions + %col.producer{ ng: { show: 'columns.producer.visible' } } + %col.name{ ng: { show: 'columns.name.visible' } } + %col.unit{ ng: { show: 'columns.unit.visible' } } + %col.display_as{ ng: { show: 'columns.unit.visible' } } + %col.price{ ng: { show: 'columns.price.visible'} } + %col.on_hand{ ng: { show: 'columns.on_hand.visible' } } + %col.category{ ng: { show: 'columns.category.visible' } } + %col.available_on{ ng: { show: 'columns.available_on.visible' } } + %col.actions + %col.actions + %col.actions + +%thead + %tr + %th.left-actions + %th.producer{ 'ng-show' => 'columns.producer.visible' } Producer + %th.name{ 'ng-show' => 'columns.name.visible' } Name + %th.unit{ 'ng-show' => 'columns.unit.visible' } Unit / Value + %th.display_as{ 'ng-show' => 'columns.unit.visible' } Display As + %th.price{ 'ng-show' => 'columns.price.visible' } Price + %th.on_hand{ 'ng-show' => 'columns.on_hand.visible' } On Hand + %th.category{ 'ng-show' => 'columns.category.visible' } Category + %th.available_on{ 'ng-show' => 'columns.available_on.visible' } Av. On + %th.actions + %th.actions + %th.actions diff --git a/app/views/spree/admin/products/bulk_edit/_products_product.html.haml b/app/views/spree/admin/products/bulk_edit/_products_product.html.haml new file mode 100644 index 0000000000..ee2c1dc136 --- /dev/null +++ b/app/views/spree/admin/products/bulk_edit/_products_product.html.haml @@ -0,0 +1,30 @@ +%tr.product{ :id => "p_{{product.id}}" } + %td.left-actions + %a{ 'ofn-toggle-variants' => 'true', :class => "view-variants icon-chevron-right", 'ng-show' => 'hasVariants(product)' } + %a{ :class => "add-variant icon-plus-sign", 'ng-click' => "addVariant(product)", 'ng-show' => "!hasVariants(product) && hasUnit(product)" } + %td.producer{ 'ng-show' => 'columns.producer.visible' } + %select.select2.fullwidth{ 'ng-model' => 'product.producer', :name => 'producer', 'ofn-track-product' => 'producer', 'ng-options' => 'producer.id as producer.name for producer in producers' } + %td.name{ 'ng-show' => 'columns.name.visible' } + %input{ 'ng-model' => "product.name", :name => 'product_name', 'ofn-track-product' => 'name', :type => 'text' } + %td.unit{ 'ng-show' => 'columns.unit.visible' } + %select.select2{ 'ng-model' => 'product.variant_unit_with_scale', :name => 'variant_unit_with_scale', 'ofn-track-product' => 'variant_unit_with_scale', 'ng-options' => 'unit[1] as unit[0] for unit in variant_unit_options' } + %option{'value' => '', 'ng-hide' => "hasVariants(product) && hasUnit(product)"} + %input{ 'ng-model' => 'product.master.unit_value_with_description', :name => 'master_unit_value_with_description', 'ofn-track-master' => 'unit_value_with_description', :type => 'text', :placeholder => 'value', 'ng-show' => "!hasVariants(product) && hasUnit(product)", 'ofn-maintain-unit-scale' => true } + %input{ 'ng-model' => 'product.variant_unit_name', :name => 'variant_unit_name', 'ofn-track-product' => 'variant_unit_name', :placeholder => 'unit', 'ng-show' => "product.variant_unit_with_scale == 'items'", :type => 'text' } + %td.display_as{ 'ng-show' => 'columns.unit.visible' } + %input{ 'ofn-display-as' => 'product.master', name: 'display_as', 'ofn-track-master' => 'display_as', type: 'text', placeholder: '{{ placeholder_text }}', ng: { hide: 'hasVariants(product)', model: 'product.master.display_as' } } + %td.price{ 'ng-show' => 'columns.price.visible' } + %input{ 'ng-model' => 'product.price', 'ofn-decimal' => :true, :name => 'price', 'ofn-track-product' => 'price', :type => 'text', 'ng-hide' => 'hasVariants(product)' } + %td.on_hand{ 'ng-show' => 'columns.on_hand.visible' } + %span{ 'ng-bind' => 'product.on_hand', :name => 'on_hand', 'ng-show' => '!hasOnDemandVariants(product) && (hasVariants(product) || product.on_demand)' } + %input.field{ 'ng-model' => 'product.on_hand', :name => 'on_hand', 'ofn-track-product' => 'on_hand', 'ng-hide' => 'hasVariants(product) || product.on_demand', :type => 'number' } + %td.category{ 'ng-if' => 'columns.category.visible' } + %input.fullwidth{ :type => 'text', id: "p{{product.id}}_category", 'ng-model' => 'product.category', 'ofn-taxon-autocomplete' => '', 'ofn-track-product' => 'category' } + %td.available_on{ 'ng-show' => 'columns.available_on.visible' } + %input{ 'ng-model' => 'product.available_on', :name => 'available_on', 'ofn-track-product' => 'available_on', 'datetimepicker' => 'product.available_on', type: "text" } + %td.actions + %a{ 'ng-click' => 'editWarn(product)', :class => "edit-product icon-edit no-text" } + %td.actions + %a{ 'ng-click' => 'cloneProduct(product)', :class => "clone-product icon-copy no-text" } + %td.actions + %a{ 'ng-click' => 'deleteProduct(product)', :class => "delete-product icon-trash no-text" } diff --git a/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml b/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml new file mode 100644 index 0000000000..6369da0d99 --- /dev/null +++ b/app/views/spree/admin/products/bulk_edit/_products_variant.html.haml @@ -0,0 +1,23 @@ +%tr.variant{ :id => "v_{{variant.id}}", 'ng-repeat' => 'variant in product.variants', 'ng-show' => 'displayProperties[product.id].showVariants', 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'" } + %td.left-actions + %a{ :class => "variant-item icon-caret-right", 'ng-hide' => "$last" } + %a{ :class => "add-variant icon-plus-sign", 'ng-click' => "addVariant(product)", 'ng-show' => "$last" } + %td{ 'ng-show' => 'columns.producer.visible' } + %td{ 'ng-show' => 'columns.name.visible' } + %input{ 'ng-model' => 'variant.display_name', :name => 'variant_display_name', 'ofn-track-variant' => 'display_name', :type => 'text', placeholder: "{{ product.name }}" } + %td.unit_value{ 'ng-show' => 'columns.unit.visible' } + %input{ 'ng-model' => 'variant.unit_value_with_description', :name => 'variant_unit_value_with_description', 'ofn-track-variant' => 'unit_value_with_description', :type => 'text', 'ofn-maintain-unit-scale' => true } + %td.display_as{ 'ng-show' => 'columns.unit.visible' } + %input{ 'ofn-display-as' => 'variant', 'ng-model' => 'variant.display_as', name: 'variant_display_as', 'ofn-track-variant' => 'display_as', type: 'text', placeholder: '{{ placeholder_text }}' } + %td{ 'ng-show' => 'columns.price.visible' } + %input{ 'ng-model' => 'variant.price', 'ofn-decimal' => :true, :name => 'variant_price', 'ofn-track-variant' => 'price', :type => 'text' } + %td{ 'ng-show' => 'columns.on_hand.visible' } + %input.field{ 'ng-model' => 'variant.on_hand', 'ng-change' => 'updateOnHand(product)', :name => 'variant_on_hand', 'ng-hide' => 'variant.on_demand', 'ofn-track-variant' => 'on_hand', :type => 'number' } + %span{ 'ng-bind' => 'variant.on_hand', :name => 'variant_on_hand', 'ng-show' => 'variant.on_demand' } + %td{ 'ng-show' => 'columns.category.visible' } + %td{ 'ng-show' => 'columns.available_on.visible' } + %td.actions + %a{ 'ng-click' => 'editWarn(product,variant)', :class => "edit-variant icon-edit no-text", 'ng-show' => "variantSaved(variant)" } + %td.actions + %td.actions + %a{ 'ng-click' => 'deleteVariant(product,variant)', :class => "delete-variant icon-trash no-text" } From 20bfcd6e48adeaa6de0cac2c6f968c0fcc1859e4 Mon Sep 17 00:00:00 2001 From: Rob H Date: Fri, 5 Sep 2014 17:11:57 +1000 Subject: [PATCH 19/35] Switching enterprise relationships form around --- .../enterprise_relationships.js.coffee | 4 ++-- .../_enterprise_relationship.html.haml | 18 ++++++++++-------- .../enterprise_relationships/_form.html.haml | 7 +++++-- .../enterprise_relationships/index.html.haml | 3 +-- .../admin/enterprise_relationships_spec.rb | 18 +++++++++--------- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee b/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee index cad556efd8..16c9c38fea 100644 --- a/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee +++ b/app/assets/javascripts/admin/services/enterprise_relationships.js.coffee @@ -24,5 +24,5 @@ angular.module("ofn.admin").factory 'EnterpriseRelationships', ($http, enterpris permission_presentation: (permission) -> switch permission - when "add_to_order_cycle" then "can add to order cycle" - when "manage_products" then "can manage the products of" + when "add_to_order_cycle" then "to add to order cycle" + when "manage_products" then "to manage products" diff --git a/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml b/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml index 6a7dcdc7f4..45e79b1877 100644 --- a/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml +++ b/app/views/admin/enterprise_relationships/_enterprise_relationship.html.haml @@ -1,8 +1,10 @@ -%td {{ enterprise_relationship.child_name }} -%td - %ul - %li{"ng-repeat" => "permission in enterprise_relationship.permissions"} - {{ EnterpriseRelationships.permission_presentation(permission.name) }} -%td {{ enterprise_relationship.parent_name }} -%td.actions - %a.delete-enterprise-relationship.icon-trash.no-text{'ng-click' => 'delete(enterprise_relationship)'} +%tr{"ng-repeat" => "enterprise_relationship in EnterpriseRelationships.enterprise_relationships | filter:query"} + %td {{ enterprise_relationship.parent_name }} + %td permits + %td {{ enterprise_relationship.child_name }} + %td + %ul + %li{"ng-repeat" => "permission in enterprise_relationship.permissions"} + {{ EnterpriseRelationships.permission_presentation(permission.name) }} + %td.actions + %a.delete-enterprise-relationship.icon-trash.no-text{'ng-click' => 'delete(enterprise_relationship)'} diff --git a/app/views/admin/enterprise_relationships/_form.html.haml b/app/views/admin/enterprise_relationships/_form.html.haml index 86086c8781..9d031a57c5 100644 --- a/app/views/admin/enterprise_relationships/_form.html.haml +++ b/app/views/admin/enterprise_relationships/_form.html.haml @@ -1,4 +1,9 @@ %tr + %td + %select{name: "enterprise_relationship_parent_id", "ng-model" => "parent_id", "ng-options" => "e.id as e.name for e in Enterprises.my_enterprises"} + + %td + permits %td %select{name: "enterprise_relationship_child_id", "ng-model" => "child_id", "ng-options" => "e.id as e.name for e in Enterprises.all_enterprises"} %td @@ -6,8 +11,6 @@ %label %input{type: "checkbox", "ng-model" => "permissions[permission]"} {{ EnterpriseRelationships.permission_presentation(permission) }} - %td - %select{name: "enterprise_relationship_parent_id", "ng-model" => "parent_id", "ng-options" => "e.id as e.name for e in Enterprises.my_enterprises"} %td.actions %input{type: "button", value: "Create", "ng-click" => "create()"} .errors {{ EnterpriseRelationships.create_errors }} diff --git a/app/views/admin/enterprise_relationships/index.html.haml b/app/views/admin/enterprise_relationships/index.html.haml index e0e289efcf..0807a37825 100644 --- a/app/views/admin/enterprise_relationships/index.html.haml +++ b/app/views/admin/enterprise_relationships/index.html.haml @@ -11,5 +11,4 @@ %table#enterprise-relationships %tbody = render 'form' - %tr{"ng-repeat" => "enterprise_relationship in EnterpriseRelationships.enterprise_relationships | filter:query"} - = render 'enterprise_relationship' + = render 'enterprise_relationship' diff --git a/spec/features/admin/enterprise_relationships_spec.rb b/spec/features/admin/enterprise_relationships_spec.rb index 7317f44066..007f663db0 100644 --- a/spec/features/admin/enterprise_relationships_spec.rb +++ b/spec/features/admin/enterprise_relationships_spec.rb @@ -24,10 +24,10 @@ feature %q{ # Then I should see the relationships within('table#enterprise-relationships') do - page.should have_relationship e1, e2, ['can add to order cycle'] - page.should have_relationship e2, e3, ['can manage the products of'] + page.should have_relationship e1, e2, ['to add to order cycle'] + page.should have_relationship e2, e3, ['to manage products'] page.should have_relationship e3, e4, - ['can add to order cycle', 'can manage the products of'] + ['to add to order cycle', 'to manage products'] end end @@ -39,13 +39,13 @@ feature %q{ visit admin_enterprise_relationships_path select 'One', from: 'enterprise_relationship_parent_id' - check 'can add to order cycle' - check 'can manage the products of' - uncheck 'can manage the products of' + check 'to add to order cycle' + check 'to manage products' + uncheck 'to manage products' select 'Two', from: 'enterprise_relationship_child_id' click_button 'Create' - page.should have_relationship e1, e2, ['can add to order cycle'] + page.should have_relationship e1, e2, ['to add to order cycle'] er = EnterpriseRelationship.where(parent_id: e1, child_id: e2).first er.should be_present er.permissions.map(&:name).should == ['add_to_order_cycle'] @@ -118,8 +118,8 @@ feature %q{ private def have_relationship(parent, child, perms=[]) - perms = perms.join(' ') || 'permits' + perms = perms.join(' ') - have_table_row [child.name, perms, parent.name, ''] + have_table_row [parent.name, 'permits', child.name, perms, ''] end end From 58dcdbd9c40abadbb0ede928e65b1ce216185619 Mon Sep 17 00:00:00 2001 From: Rob H Date: Fri, 5 Sep 2014 18:30:15 +1000 Subject: [PATCH 20/35] Restricting ability to change enterprise type at the controller level --- .../admin/enterprises_controller.rb | 6 ++++ .../admin/enterprises_controller_spec.rb | 29 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 85fe6831fb..594ccc751c 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -4,6 +4,7 @@ module Admin before_filter :load_countries, :except => :index before_filter :load_methods_and_fees, :only => [:new, :edit, :update, :create] create.after :grant_management + before_filter :override_type, only: :update helper 'spree/products' include OrderCyclesHelper @@ -67,6 +68,11 @@ module Admin @enterprise_fees = EnterpriseFee.managed_by(spree_current_user).for_enterprise(@enterprise).order(:fee_type, :name).all end + def override_type + # TODO this should be done using CanCan, but our current version does not allow this level of fine grained control + params[:enterprise].delete :type unless spree_current_user.admin? + end + # Overriding method on Spree's resource controller def location_after_save if params[:enterprise].key? :producer_properties_attributes diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 20482bef09..e52f09a5a4 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -36,5 +36,34 @@ module Admin admin_user.enterprise_roles.where(enterprise_id: enterprise).should be_empty end end + + describe "updating an enterprise" do + let(:profile_enterprise) { create(:enterprise, type: 'profile') } + + context "as manager" do + it "does not allow 'type' to be changed" do + # TODO should be implemented and tested using cancan abilities, but can't do this using our current version + profile_enterprise.enterprise_roles.build(user: user).save + controller.stub spree_current_user: user + enterprise_params = { id: profile_enterprise.id, enterprise: { type: 'full' } } + + spree_put :update, enterprise_params + profile_enterprise.reload + expect(profile_enterprise.type).to eq 'profile' + end + end + + context "as super admin" do + it "allows 'type' to be changed" do + # TODO should be implemented and tested using cancan abilities, but can't do this using our current version + controller.stub spree_current_user: admin_user + enterprise_params = { id: profile_enterprise.id, enterprise: { type: 'full' } } + + spree_put :update, enterprise_params + profile_enterprise.reload + expect(profile_enterprise.type).to eq 'full' + end + end + end end end From 780df6bfe061f1deaad43c793fe48dc9ca56e8c4 Mon Sep 17 00:00:00 2001 From: Rob H Date: Fri, 5 Sep 2014 18:38:43 +1000 Subject: [PATCH 21/35] Hide 'profile type' form element for non super-admin users --- app/views/admin/enterprises/_form.html.haml | 37 +++++++++++---------- spec/features/admin/enterprises_spec.rb | 2 +- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/app/views/admin/enterprises/_form.html.haml b/app/views/admin/enterprises/_form.html.haml index 07be41dd6e..dbf986c7aa 100644 --- a/app/views/admin/enterprises/_form.html.haml +++ b/app/views/admin/enterprises/_form.html.haml @@ -36,24 +36,25 @@ = f.check_box :is_primary_producer, 'ng-model' => 'Enterprise.is_primary_producer'   = f.label :is_primary_producer, 'Producer' - .row - .alpha.eleven.columns - .three.columns.alpha - = f.label :type, 'Profile type' - .with-tip{'data-powertip' => "Full - enterprise may have products and relationships.
    Single - enterprise may have products but no relationships.
    Profile - enterprise has a profile but no products or relationships.
    "} - %a What's this? - .two.columns - = f.radio_button :type, "full" -   - = f.label :type, "Full", value: "full" - .two.columns - = f.radio_button :type, "single" -   - = f.label :type, "Single", value: "single" - .four.columns.omega - = f.radio_button :type, "profile" -   - = f.label :type, "Profile", value: "profile" + - if spree_current_user.admin? + .row + .alpha.eleven.columns + .three.columns.alpha + = f.label :type, 'Profile type' + .with-tip{'data-powertip' => "Full - enterprise may have products and relationships.
    Single - enterprise may have products but no relationships.
    Profile - enterprise has a profile but no products or relationships.
    "} + %a What's this? + .two.columns + = f.radio_button :type, "full" +   + = f.label :type, "Full", value: "full" + .two.columns + = f.radio_button :type, "single" +   + = f.label :type, "Single", value: "single" + .four.columns.omega + = f.radio_button :type, "profile" +   + = f.label :type, "Profile", value: "profile" .row .three.columns.alpha %label Visible in search? diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index da32f92681..08bae3aec5 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -127,7 +127,7 @@ feature %q{ choose 'Single' fill_in 'enterprise_description', :with => 'Connecting farmers and eaters' fill_in 'enterprise_long_description', :with => 'Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro.' - + # Check Angularjs switching of sidebar elements uncheck 'enterprise_is_primary_producer' uncheck 'enterprise_is_distributor' From ee4a1925fe91c904138670eecbd06ce8e26da537 Mon Sep 17 00:00:00 2001 From: Rob H Date: Sat, 6 Sep 2014 00:34:27 +1000 Subject: [PATCH 22/35] Bulk Order Management works with navigation helper override --- .../spree/admin/navigation_helper_decorator.rb | 1 + spec/features/admin/bulk_order_management_spec.rb | 14 +++++++------- spec/helpers/navigation_helper_spec.rb | 4 ++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/helpers/spree/admin/navigation_helper_decorator.rb b/app/helpers/spree/admin/navigation_helper_decorator.rb index 024f467154..eb210ef482 100644 --- a/app/helpers/spree/admin/navigation_helper_decorator.rb +++ b/app/helpers/spree/admin/navigation_helper_decorator.rb @@ -8,6 +8,7 @@ module Spree klass = klass_for_without_sym_fallback(name) klass ||= name.singularize.to_sym klass = :overview if klass == :dashboard + klass = Spree::Order if klass == :bulk_order_management klass end alias_method_chain :klass_for, :sym_fallback diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index a8fe6cbda7..fe18f1a9bf 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -15,13 +15,6 @@ feature %q{ admin_user = quick_login_as_admin end - it "displays a Bulk Management Tab under the Orders item" do - visit '/admin/orders' - page.should have_link "Bulk Order Management" - click_link "Bulk Order Management" - page.should have_selector "h1.page-title", text: "Bulk Order Management" - end - it "displays a message when number of line items is zero" do visit '/admin/orders/bulk_management' page.should have_text "No orders found." @@ -586,6 +579,13 @@ feature %q{ quick_login_as @enterprise_user end + it "displays a Bulk Management Tab under the Orders item" do + visit '/admin/orders' + page.should have_link "Bulk Order Management" + click_link "Bulk Order Management" + page.should have_selector "h1.page-title", text: "Bulk Order Management" + end + it "shows only line item from orders that I distribute, and not those that I supply" do visit '/admin/orders/bulk_management' diff --git a/spec/helpers/navigation_helper_spec.rb b/spec/helpers/navigation_helper_spec.rb index 41decd6e5a..76fe9573f2 100644 --- a/spec/helpers/navigation_helper_spec.rb +++ b/spec/helpers/navigation_helper_spec.rb @@ -15,6 +15,10 @@ module Spree it "returns :overview for the dashboard" do helper.klass_for('dashboard').should == :overview end + + it "returns Spree::Order for bulk_order_management" do + helper.klass_for('bulk_order_management').should == Spree::Order + end end end end From 9dc2b248c7ef40482eccc2bf7dc609d5d113a295 Mon Sep 17 00:00:00 2001 From: Rob H Date: Sat, 6 Sep 2014 09:37:34 +1000 Subject: [PATCH 23/35] Bulk management permissions make more sense --- app/models/spree/ability_decorator.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 9f9c96cb15..79019f3e8a 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -59,12 +59,12 @@ class AbilityDecorator # Enterprise User can only access orders that they are a distributor for can [:index, :create], Spree::Order - can [:read, :update, :bulk_management, :fire, :resend], Spree::Order do |order| + can [:read, :update, :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?(&:is_distributor?) + can [:admin, :bulk_management], Spree::Order if user.admin? || user.enterprises.any?(&:is_distributor?) can [:admin, :create], Spree::LineItem can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Payment From b8fadb50ae6cbbae7178dd15654f0fc6e5d9a5b6 Mon Sep 17 00:00:00 2001 From: Rob H Date: Sat, 6 Sep 2014 12:00:27 +1000 Subject: [PATCH 24/35] Special Instructions in checkout are actually wired up --- .../checkout/checkout_controller.js.coffee | 4 ++-- .../darkswarm/services/checkout.js.coffee | 6 +++--- app/controllers/checkout_controller.rb | 6 +++--- app/views/checkout/_form.html.haml | 5 ++--- .../archive/features/consumer/checkout_spec.rb | 18 +++++++++--------- .../consumer/shopping/checkout_spec.rb | 18 +++++++++++++----- 6 files changed, 32 insertions(+), 25 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/checkout/checkout_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/checkout/checkout_controller.js.coffee index e77712eae6..9c953c4f4a 100644 --- a/app/assets/javascripts/darkswarm/controllers/checkout/checkout_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/checkout/checkout_controller.js.coffee @@ -6,9 +6,9 @@ Darkswarm.controller "CheckoutCtrl", ($scope, storage, Checkout, CurrentUser, Cu prefix = "order_#{Checkout.order.id}#{CurrentUser?.id}#{CurrentHub.hub.id}" for field in $scope.fieldsToBind - storage.bind $scope, "Checkout.order.#{field}", + storage.bind $scope, "Checkout.order.#{field}", storeName: "#{prefix}_#{field}" - storage.bind $scope, "Checkout.ship_address_same_as_billing", + storage.bind $scope, "Checkout.ship_address_same_as_billing", storeName: "#{prefix}_sameasbilling" defaultValue: true diff --git a/app/assets/javascripts/darkswarm/services/checkout.js.coffee b/app/assets/javascripts/darkswarm/services/checkout.js.coffee index cafd4d6718..a12ed1ae45 100644 --- a/app/assets/javascripts/darkswarm/services/checkout.js.coffee +++ b/app/assets/javascripts/darkswarm/services/checkout.js.coffee @@ -13,7 +13,7 @@ Darkswarm.factory 'Checkout', (CurrentOrder, ShippingMethods, PaymentMethods, $h Loading.clear() @errors = response.errors RailsFlashLoader.loadFlash(response.flash) - + # Rails wants our Spree::Address data to be provided with _attributes preprocess: -> munged_order = {} @@ -25,7 +25,7 @@ Darkswarm.factory 'Checkout', (CurrentOrder, ShippingMethods, PaymentMethods, $h munged_order["ship_address_attributes"] = value when "payment_method_id" munged_order["payments_attributes"] = [{payment_method_id: value}] - when "shipping_method_id", "payment_method_id", "email" + when "shipping_method_id", "payment_method_id", "email", "special_instructions" munged_order[name] = value else # Ignore everything else @@ -58,7 +58,7 @@ Darkswarm.factory 'Checkout', (CurrentOrder, ShippingMethods, PaymentMethods, $h shippingPrice: -> @shippingMethod()?.price || 0.0 - + paymentMethod: -> PaymentMethods.payment_methods_by_id[@order.payment_method_id] diff --git a/app/controllers/checkout_controller.rb b/app/controllers/checkout_controller.rb index 8e2a33bd56..8e249e5a1f 100644 --- a/app/controllers/checkout_controller.rb +++ b/app/controllers/checkout_controller.rb @@ -9,7 +9,7 @@ class CheckoutController < Spree::CheckoutController include OrderCyclesHelper include EnterprisesHelper - + def edit # Because this controller doesn't inherit from our BaseController # We need to duplicate the code here @@ -56,7 +56,7 @@ class CheckoutController < Spree::CheckoutController private - + # Copied and modified from spree. Remove check for order state, since the state machine is # progressed all the way in one go with the one page checkout. def object_params @@ -94,7 +94,7 @@ class CheckoutController < Spree::CheckoutController def skip_state_validation? true end - + def load_order @order = current_order redirect_to main_app.shop_path and return unless @order and @order.checkout_allowed? diff --git a/app/views/checkout/_form.html.haml b/app/views/checkout/_form.html.haml index a64be0ac44..52f6f983cc 100644 --- a/app/views/checkout/_form.html.haml +++ b/app/views/checkout/_form.html.haml @@ -1,8 +1,8 @@ -= f_form_for current_order, url: main_app.update_checkout_path, += f_form_for current_order, html: {name: "checkout", id: "checkout_form", novalidate: true, - name: "checkout"} do |f| + "ng-submit" => "purchase($event)"} do |f| = inject_available_shipping_methods = inject_available_payment_methods @@ -20,7 +20,6 @@ = render partial: "checkout/payment", locals: {f: f} %p %button.button.primary{type: :submit, - "ng-click" => "purchase($event)", "ng-disabled" => "checkout.$invalid"} Place order now / {{ checkout.$valid }} diff --git a/spec/archive/features/consumer/checkout_spec.rb b/spec/archive/features/consumer/checkout_spec.rb index 9641085c39..74a2443b36 100644 --- a/spec/archive/features/consumer/checkout_spec.rb +++ b/spec/archive/features/consumer/checkout_spec.rb @@ -7,7 +7,7 @@ feature %q{ }, skip: true do include AuthenticationWorkflow include WebHelper - + background do set_feature_toggle :order_cycles, true @@ -24,8 +24,8 @@ feature %q{ :state => Spree::State.find_by_name('Victoria'), :country => Spree::Country.find_by_name('Australia')), :pickup_times => 'Tuesday, 4 PM') - - + + @distributor_alternative = create(:distributor_enterprise, :name => 'Alternative Distributor', :address => create(:address, :address1 => '1600 Rathdowne St', @@ -33,7 +33,7 @@ feature %q{ :zipcode => 3054, :state => Spree::State.find_by_name('Victoria'), :country => Spree::Country.find_by_name('Australia')), - :pickup_times => 'Tuesday, 4 PM') + :pickup_times => 'Tuesday, 4 PM') @enterprise_fee_1 = create(:enterprise_fee, :name => 'Enterprise Fee One', :calculator => Spree::Calculator::PerItem.new) @enterprise_fee_1.calculator.set_preference :amount, 1 @@ -347,16 +347,16 @@ feature %q{ login_to_consumer_section click_link 'FruitAndVeg' - visit enterprise_path @distributor1 + visit enterprise_path @distributor1 click_link 'Bananas' click_button 'Add To Cart' - visit enterprise_path @distributor1 + visit enterprise_path @distributor1 click_link 'Zucchini' click_button 'Add To Cart' find('#checkout-link').click - + # And manually visit the old checkout visit "/checkout" @@ -389,7 +389,7 @@ feature %q{ # -- Checkout: Delivery page.should have_content "DELIVERY METHOD" order_charges = page.all("tbody#summary-order-charges tr").map {|row| row.all('td').map(&:text)}.take(2) - order_charges.should == [["Distribution:", "$51.00"]] + order_charges.should == [["Distribution:", "$51.00"]] click_checkout_continue_button @@ -403,7 +403,7 @@ feature %q{ # -- Checkout: Order complete page.should have_content 'Your order has been processed successfully' page.should have_content @payment_method_distributor_oc.description - page.should have_content @distributor_oc.name + page.should have_content @distributor_oc.name page.should have_selector 'tfoot#order-charges tr.total td', text: 'Distribution' page.should have_selector 'tfoot#order-charges tr.total td', text: '51.00' diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index b26e924495..56eee61e4f 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -94,10 +94,6 @@ feature "As a consumer I want to check out my cart", js: true do describe "purchasing" do it "takes us to the order confirmation page when we submit a complete form" do - toggle_shipping - choose sm2.name - toggle_payment - choose pm1.name toggle_details within "#details" do fill_in "First Name", with: "Will" @@ -112,14 +108,25 @@ feature "As a consumer I want to check out my cart", js: true do select "Victoria", from: "State" fill_in "City", with: "Melbourne" fill_in "Postcode", with: "3066" - end + toggle_shipping + within "#shipping" do + choose sm2.name + fill_in 'Any notes or custom delivery instructions?', with: "SpEcIaL NoTeS" + end + toggle_payment + within "#payment" do + choose pm1.name + end + place_order page.should have_content "Your order has been processed successfully" ActionMailer::Base.deliveries.length.should == 2 email = ActionMailer::Base.deliveries.last site_name = Spree::Config[:site_name] email.subject.should include "#{site_name} Order Confirmation" + o = Spree::Order.complete.first + expect(o.special_instructions).to eq "SpEcIaL NoTeS" end context "with basic details filled" do @@ -157,6 +164,7 @@ feature "As a consumer I want to check out my cart", js: true do it "takes us to the order confirmation page when submitted with a valid credit card" do toggle_payment + save_screenshot '/Users/rob/Desktop/ss.png' fill_in 'Card Number', with: "4111111111111111" select 'February', from: 'secrets.card_month' select (Date.today.year+1).to_s, from: 'secrets.card_year' From 6540bb8efc84e62e32e7a2b5290274d3b1fdd5a0 Mon Sep 17 00:00:00 2001 From: Rob H Date: Sun, 7 Sep 2014 19:51:14 +1000 Subject: [PATCH 25/35] Adding select field for enterprise type to index when super admin --- .../admin/enterprises_controller.rb | 14 ++++-- app/views/admin/enterprises/index.html.haml | 8 +-- .../admin/enterprises_controller_spec.rb | 37 +++++++++++++- spec/features/admin/enterprises_spec.rb | 50 ++++++++++++------- 4 files changed, 81 insertions(+), 28 deletions(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 594ccc751c..ceeb26abfc 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -4,7 +4,8 @@ module Admin before_filter :load_countries, :except => :index before_filter :load_methods_and_fees, :only => [:new, :edit, :update, :create] create.after :grant_management - before_filter :override_type, only: :update + before_filter :check_type, only: :update + before_filter :check_bulk_type, only: :bulk_update helper 'spree/products' include OrderCyclesHelper @@ -68,8 +69,15 @@ module Admin @enterprise_fees = EnterpriseFee.managed_by(spree_current_user).for_enterprise(@enterprise).order(:fee_type, :name).all end - def override_type - # TODO this should be done using CanCan, but our current version does not allow this level of fine grained control + def check_bulk_type + unless spree_current_user.admin? + params[:enterprise_set][:collection_attributes].each do |i, enterprise_params| + enterprise_params.delete :type + end + end + end + + def check_type params[:enterprise].delete :type unless spree_current_user.admin? end diff --git a/app/views/admin/enterprises/index.html.haml b/app/views/admin/enterprises/index.html.haml index 7ab6ea4d0e..30dfc7a8f2 100644 --- a/app/views/admin/enterprises/index.html.haml +++ b/app/views/admin/enterprises/index.html.haml @@ -10,17 +10,17 @@ = form_for @enterprise_set, :url => main_app.bulk_update_admin_enterprises_path do |f| %table#listing_enterprises.index %colgroup - %col{style: "width: 20%;"}/ + %col{style: "width: 25%;"}/ %col{style: "width: 10%;"}/ %col{style: "width: 5%;"}/ - %col/ + %col{style: "width: 10%;"}/ %col{style: "width: 20%;"}/ %thead %tr{"data-hook" => "enterprises_header"} %th Name %th Role %th Visible? - %th Description + %th Type %th %tbody = f.fields_for :collection do |enterprise_form| @@ -37,7 +37,7 @@ - else %h1.icon-exclamation-sign.with-tip{"data-powertip" => "This enterprise does not have any roles", style: "text-align: center;color: #DA5354"} %td= enterprise_form.check_box :visible - %td= enterprise.description + %td= enterprise_form.select :type, Enterprise::TYPES, {}, class: 'select2 fullwidth' %td{"data-hook" => "admin_users_index_row_actions"} = render 'actions', enterprise: enterprise - if @enterprises.empty? diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index e52f09a5a4..14faf26287 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -42,7 +42,6 @@ module Admin context "as manager" do it "does not allow 'type' to be changed" do - # TODO should be implemented and tested using cancan abilities, but can't do this using our current version profile_enterprise.enterprise_roles.build(user: user).save controller.stub spree_current_user: user enterprise_params = { id: profile_enterprise.id, enterprise: { type: 'full' } } @@ -55,7 +54,6 @@ module Admin context "as super admin" do it "allows 'type' to be changed" do - # TODO should be implemented and tested using cancan abilities, but can't do this using our current version controller.stub spree_current_user: admin_user enterprise_params = { id: profile_enterprise.id, enterprise: { type: 'full' } } @@ -65,5 +63,40 @@ module Admin end end end + + describe "bulk updating enterprises" do + let(:profile_enterprise1) { create(:enterprise, type: 'profile') } + let(:profile_enterprise2) { create(:enterprise, type: 'profile') } + + context "as manager" do + it "does not allow 'type' to be changed" do + profile_enterprise1.enterprise_roles.build(user: user).save + profile_enterprise2.enterprise_roles.build(user: user).save + controller.stub spree_current_user: user + bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, type: 'full' }, '1' => { id: profile_enterprise2.id, type: 'full' } } } } + + spree_put :bulk_update, bulk_enterprise_params + profile_enterprise1.reload + profile_enterprise2.reload + expect(profile_enterprise1.type).to eq 'profile' + expect(profile_enterprise2.type).to eq 'profile' + end + end + + context "as super admin" do + it "allows 'type' to be changed" do + profile_enterprise1.enterprise_roles.build(user: user).save + profile_enterprise2.enterprise_roles.build(user: user).save + controller.stub spree_current_user: admin_user + bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, type: 'full' }, '1' => { id: profile_enterprise2.id, type: 'full' } } } } + + spree_put :bulk_update, bulk_enterprise_params + profile_enterprise1.reload + profile_enterprise2.reload + expect(profile_enterprise1.type).to eq 'full' + expect(profile_enterprise2.type).to eq 'full' + end + end + end end end diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index 08bae3aec5..00c15160f0 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -15,39 +15,43 @@ feature %q{ click_link 'Enterprises' within("tr.enterprise-#{s.id}") do - page.should have_content s.name - page.should have_content "Edit Profile" - page.should have_content "Delete" - page.should_not have_content "Payment Methods" - page.should_not have_content "Shipping Methods" - page.should have_content "Enterprise Fees" + expect(page).to have_content s.name + expect(page).to have_select "enterprise_set_collection_attributes_1_type" + expect(page).to have_content "Edit Profile" + expect(page).to have_content "Delete" + expect(page).to_not have_content "Payment Methods" + expect(page).to_not have_content "Shipping Methods" + expect(page).to have_content "Enterprise Fees" end within("tr.enterprise-#{d.id}") do - page.should have_content d.name - page.should have_content "Edit Profile" - page.should have_content "Delete" - page.should have_content "Payment Methods" - page.should have_content "Shipping Methods" - page.should have_content "Enterprise Fees" + expect(page).to have_content d.name + expect(page).to have_select "enterprise_set_collection_attributes_0_type" + expect(page).to have_content "Edit Profile" + expect(page).to have_content "Delete" + expect(page).to have_content "Payment Methods" + expect(page).to have_content "Shipping Methods" + expect(page).to have_content "Enterprise Fees" end end scenario "editing enterprises in bulk" do s = create(:supplier_enterprise) - d = create(:distributor_enterprise) + d = create(:distributor_enterprise, type: 'profile') login_to_admin_section click_link 'Enterprises' within("tr.enterprise-#{d.id}") do - page.should have_checked_field "enterprise_set_collection_attributes_0_visible" + expect(page).to have_checked_field "enterprise_set_collection_attributes_0_visible" uncheck "enterprise_set_collection_attributes_0_visible" + select 'full', from: "enterprise_set_collection_attributes_0_type" end click_button "Update" flash_message.should == 'Enterprises updated successfully' distributor = Enterprise.find(d.id) - distributor.visible.should == false + expect(distributor.visible).to eq false + expect(distributor.type).to eq 'full' end scenario "viewing an enterprise" do @@ -259,10 +263,18 @@ feature %q{ click_link "Enterprises" - page.should have_content supplier1.name - page.should have_content distributor1.name - page.should_not have_content supplier2.name - page.should_not have_content distributor2.name + within("tr.enterprise-#{supplier1.id}") do + expect(page).to have_content supplier1.name + expect(page).to have_select "enterprise_set_collection_attributes_1_type" + end + + within("tr.enterprise-#{distributor1.id}") do + expect(page).to have_content distributor1.name + expect(page).to have_select "enterprise_set_collection_attributes_0_type" + end + + expect(page).to_not have_content "supplier2.name" + expect(page).to_not have_content "distributor2.name" end scenario "creating an enterprise" do From 5fb4110328b62ff166b92a7ca7ddced9283ab943 Mon Sep 17 00:00:00 2001 From: Rob H Date: Sun, 7 Sep 2014 21:04:53 +1000 Subject: [PATCH 26/35] Adding distributor and producer checkboxes to enterprise index --- app/views/admin/enterprises/index.html.haml | 13 +++++-------- spec/features/admin/enterprises_spec.rb | 14 +++++++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/views/admin/enterprises/index.html.haml b/app/views/admin/enterprises/index.html.haml index 30dfc7a8f2..c8b3cce3d4 100644 --- a/app/views/admin/enterprises/index.html.haml +++ b/app/views/admin/enterprises/index.html.haml @@ -28,14 +28,11 @@ %tr{class: "enterprise-#{enterprise.id}"} %td= link_to enterprise.name, main_app.edit_admin_enterprise_path(enterprise) %td - - if enterprise.is_primary_producer && enterprise.is_distributor - Producer & Distributor - - elsif enterprise.is_distributor - Distributor - - elsif enterprise.is_primary_producer - Producer - - else - %h1.icon-exclamation-sign.with-tip{"data-powertip" => "This enterprise does not have any roles", style: "text-align: center;color: #DA5354"} + = enterprise_form.check_box :is_primary_producer + Producer + %br/ + = enterprise_form.check_box :is_distributor + Hub %td= enterprise_form.check_box :visible %td= enterprise_form.select :type, Enterprise::TYPES, {}, class: 'select2 fullwidth' %td{"data-hook" => "admin_users_index_row_actions"} diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index 00c15160f0..a824716d95 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -263,16 +263,20 @@ feature %q{ click_link "Enterprises" - within("tr.enterprise-#{supplier1.id}") do - expect(page).to have_content supplier1.name - expect(page).to have_select "enterprise_set_collection_attributes_1_type" - end - within("tr.enterprise-#{distributor1.id}") do expect(page).to have_content distributor1.name + expect(page).to have_checked_field "enterprise_set_collection_attributes_0_is_distributor" + expect(page).to have_unchecked_field "enterprise_set_collection_attributes_0_is_primary_producer" expect(page).to have_select "enterprise_set_collection_attributes_0_type" end + within("tr.enterprise-#{supplier1.id}") do + expect(page).to have_content supplier1.name + expect(page).to have_unchecked_field "enterprise_set_collection_attributes_1_is_distributor" + expect(page).to have_checked_field "enterprise_set_collection_attributes_1_is_primary_producer" + expect(page).to have_select "enterprise_set_collection_attributes_1_type" + end + expect(page).to_not have_content "supplier2.name" expect(page).to_not have_content "distributor2.name" end From a968aa9b9104c1e7957491e162d0a7345edd94ef Mon Sep 17 00:00:00 2001 From: Rob H Date: Mon, 8 Sep 2014 10:09:00 +1000 Subject: [PATCH 27/35] Removing float:left which weirdly breaks specs --- app/assets/stylesheets/darkswarm/hub_node.css.sass | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/hub_node.css.sass b/app/assets/stylesheets/darkswarm/hub_node.css.sass index 0ef4d83672..322da9b375 100644 --- a/app/assets/stylesheets/darkswarm/hub_node.css.sass +++ b/app/assets/stylesheets/darkswarm/hub_node.css.sass @@ -21,7 +21,7 @@ display: inline-block margin-right: 0.25rem float: left - + //Closed & Open column .open_closed i @@ -29,12 +29,11 @@ float: right margin-left: 0.5rem - //Hub Name + //Hub Name span.hub-name-listing - float: left font-weight: 700 - @media all and (max-width: 640px) + @media all and (max-width: 640px) &.closed, &.open .active_table_row:first-child .skinny-head background-color: $clr-brick-light @@ -91,7 +90,7 @@ &.open .active_table_row:nth-child(2) padding-bottom: 0.75rem - + //Current selected row &.current //overwrites active_table From b49a4cbc4fa0ef56a46866c8ca14f7328792131b Mon Sep 17 00:00:00 2001 From: Rob H Date: Mon, 8 Sep 2014 11:11:07 +1000 Subject: [PATCH 28/35] Fix intermittent failing spec --- spec/lib/spree/product_filters_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/lib/spree/product_filters_spec.rb b/spec/lib/spree/product_filters_spec.rb index 0e240a5479..5573ef0407 100644 --- a/spec/lib/spree/product_filters_spec.rb +++ b/spec/lib/spree/product_filters_spec.rb @@ -4,7 +4,9 @@ describe Spree::ProductFilters do context "distributor filter" do it "provides filtering for all distributors" do 3.times { create(:distributor_enterprise) } - Spree::ProductFilters.distributor_filter[:labels].should == Enterprise.is_distributor.sort.map { |d| [d.name, d.name] } + Enterprise.is_distributor.sort.map { |d| [d.name, d.name] }.each do |distributor| + expect(Spree::ProductFilters.distributor_filter[:labels]).to include distributor + end end end end From dfa837cac6acfc97a65eda963697e5b2017c1fd4 Mon Sep 17 00:00:00 2001 From: Rob H Date: Mon, 8 Sep 2014 15:01:34 +1000 Subject: [PATCH 29/35] Moved cached properties which should not be cached --- app/serializers/api/enterprise_serializer.rb | 70 ++++++++++---------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index d7cbc08ff2..6485687d2b 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -24,6 +24,42 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer def active @options[:active_distributors].andand.include? object end + + # TODO: Move this back to uncached section when relavant properties are defined on the Enterprise model + def icon + # TODO: Replace with object.has_shopfront when this property exists + if has_shopfront + if can_aggregate + "/assets/map_005-hub.svg" + else + if object.is_distributor + "/assets/map_003-producer-shop.svg" + else + "/assets/map_001-producer-only.svg" + end + end + else + if can_aggregate + "/assets/map_006-hub-profile.svg" + else + if object.is_distributor + "/assets/map_004-producer-shop-profile.svg" + else + "/assets/map_002-producer-only-profile.svg" + end + end + end + end + + # TODO: Remove this when flags on enterprises are switched over + def has_shopfront + object.type != 'profile' + end + + # TODO: Remove this when flags on enterprises are switched over + def can_aggregate + object.is_distributor && object.suppliers != [object] + end end class Api::CachedEnterpriseSerializer < ActiveModel::Serializer @@ -68,40 +104,6 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer object.promo_image(:large) if object.promo_image.exists? end - def icon - if has_shopfront - if can_aggregate - "/assets/map_005-hub.svg" - else - if object.is_distributor - "/assets/map_003-producer-shop.svg" - else - "/assets/map_001-producer-only.svg" - end - end - else - if can_aggregate - "/assets/map_006-hub-profile.svg" - else - if object.is_distributor - "/assets/map_004-producer-shop-profile.svg" - else - "/assets/map_002-producer-only-profile.svg" - end - end - end - end - - # TODO: Remove this when flags on enterprises are switched over - def has_shopfront - object.type != 'profile' - end - - # TODO: Remove this when flags on enterprises are switched over - def can_aggregate - object.is_distributor && object.suppliers != [object] - end - # TODO when ActiveSerializers supports URL helpers # Then refactor. See readme https://github.com/rails-api/active_model_serializers def path From 78a70292ac3604b2f5abeae1307fb13b8a3b5d08 Mon Sep 17 00:00:00 2001 From: Rob H Date: Mon, 8 Sep 2014 15:39:17 +1000 Subject: [PATCH 30/35] Moved attributes are actually delcared in the right place for Enterprise Serializer --- app/serializers/api/enterprise_serializer.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index 6485687d2b..c76cffd7d1 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -17,6 +17,9 @@ end class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer attributes :orders_close_at, :active + #TODO: Remove these later + attributes :icon, :has_shopfront, :can_aggregate + def orders_close_at OrderCycle.first_closing_for(object).andand.orders_close_at end @@ -69,9 +72,8 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer attributes :name, :id, :description, :latitude, :longitude, :long_description, :website, :instagram, :linkedin, :twitter, :facebook, :is_primary_producer, :is_distributor, :phone, :visible, - :email, :hash, :logo, :promo_image, :icon, :path, + :email, :hash, :logo, :promo_image, :path, :pickup, :delivery - attributes :has_shopfront, :can_aggregate has_many :distributed_taxons, key: :taxons, serializer: Api::IdSerializer has_many :supplied_taxons, serializer: Api::IdSerializer From b49eb8fe03c7023be63fa4f0566c820a0290f5b1 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 10 Sep 2014 11:50:30 +1000 Subject: [PATCH 31/35] Hide angular templates on page load --- app/assets/stylesheets/darkswarm/angular.css.sass | 3 +++ app/views/layouts/darkswarm.html.haml | 2 +- app/views/shared/menu/_large_menu.html.haml | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 app/assets/stylesheets/darkswarm/angular.css.sass diff --git a/app/assets/stylesheets/darkswarm/angular.css.sass b/app/assets/stylesheets/darkswarm/angular.css.sass new file mode 100644 index 0000000000..0430dd1390 --- /dev/null +++ b/app/assets/stylesheets/darkswarm/angular.css.sass @@ -0,0 +1,3 @@ +// https://docs.angularjs.org/api/ng/directive/ngCloak +[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak + display: none !important diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index 07b0db8dfa..304b049ccc 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -35,7 +35,7 @@ = render partial: "shared/menu/menu" %section{ role: "main" } - = yield + .ng-cloak= yield #footer %loading diff --git a/app/views/shared/menu/_large_menu.html.haml b/app/views/shared/menu/_large_menu.html.haml index 22618b50c4..71f297dede 100644 --- a/app/views/shared/menu/_large_menu.html.haml +++ b/app/views/shared/menu/_large_menu.html.haml @@ -32,10 +32,10 @@ - else = render 'shared/signed_in' %li.divider - %li.current_hub{"ng-controller" => "CurrentHubCtrl", "ng-show" => "CurrentHub.hub.id"} + %li.current_hub{"ng-controller" => "CurrentHubCtrl", "ng-show" => "CurrentHub.hub.id", "ng-cloak" => true} %a{href: main_app.shop_path} %em Shopping @ %span.nav-primary.nav-branded {{ CurrentHub.hub.name }} %li.divider - %li.cart + %li.cart{"ng-cloak" => true} = render partial: "shared/menu/cart" From 4168ea054b65f8a455e7b9ccdbec37b861ec5e81 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 10 Sep 2014 12:12:46 +1000 Subject: [PATCH 32/35] For body content, cloak the home page only. JS should be cached after that. --- app/views/home/index.html.haml | 11 ++++++----- app/views/layouts/darkswarm.html.haml | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 63d2216052..36ee67998f 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -4,16 +4,17 @@ %h1= image_tag "ofn_logo_beta.png", title: "Open Food Network (beta)" %h2 An open marketplace that makes it easy to find, buy, sell and move sustainable local food. - %ofn-modal{title: "Learn more"} + %ofn-modal{title: "Learn more", "ng-cloak" => true} = render partial: "modals/learn_more" -= render partial: "home/hubs" +.ng-cloak + = render partial: "home/hubs" -/ = render partial: "home/map" + / = render partial: "home/map" -/ = render partial: "home/producers" + / = render partial: "home/producers" -/ = render partial: "home/groups" + / = render partial: "home/groups" = render partial: "home/beta" diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index 304b049ccc..07b0db8dfa 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -35,7 +35,7 @@ = render partial: "shared/menu/menu" %section{ role: "main" } - .ng-cloak= yield + = yield #footer %loading From c297b7014af967240a83795a460000044826f1fa Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 10 Sep 2014 14:01:36 +1000 Subject: [PATCH 33/35] Charge customers for their shipping fee --- app/controllers/checkout_controller.rb | 8 ++++++++ spec/features/consumer/shopping/checkout_spec.rb | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/app/controllers/checkout_controller.rb b/app/controllers/checkout_controller.rb index 8e249e5a1f..7e8b04afda 100644 --- a/app/controllers/checkout_controller.rb +++ b/app/controllers/checkout_controller.rb @@ -112,6 +112,14 @@ class CheckoutController < Spree::CheckoutController @order.ship_address ||= preferred_ship_address || last_used_ship_address || Spree::Address.default end + def after_payment + # object_params sets the payment amount to the order total, but it does this before + # the shipping method is set. This results in the customer not being charged for their + # order's shipping. To fix this, we refresh the payment amount here. + @order.update_totals + @order.payments.first.update_attribute :amount, @order.total + end + # Overriding Spree's methods def raise_insufficient_quantity flash[:error] = t(:spree_inventory_error_flash_for_insufficient_quantity) diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 56eee61e4f..9e6803a669 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -159,6 +159,20 @@ feature "As a consumer I want to check out my cart", js: true do page.should have_content "Your order has been processed successfully" end + context "when we are charged a shipping fee" do + before { choose sm2.name } + + it "creates a payment for the full amount inclusive of shipping" do + place_order + page.should have_content "Your order has been processed successfully" + + # There are two orders - our order and our new cart + o = Spree::Order.first + o.adjustments.shipping.first.amount.should == 4.56 + o.payments.first.amount.should == 10 + 1.23 + 4.56 # items + fees + shipping + end + end + context "with a credit card payment method" do let!(:pm1) { create(:payment_method, distributors: [distributor], name: "Roger rabbit", type: "Spree::Gateway::Bogus") } From 32a2e793ad9d1a74a844703e95edb4248ad710af Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 10 Sep 2014 14:49:15 +1000 Subject: [PATCH 34/35] When deleting enterprise relationships, delete dependent permissions --- app/models/enterprise_relationship.rb | 2 +- spec/features/admin/enterprise_relationships_spec.rb | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb index dba99cc7b3..ff2c1fe3fa 100644 --- a/app/models/enterprise_relationship.rb +++ b/app/models/enterprise_relationship.rb @@ -1,7 +1,7 @@ class EnterpriseRelationship < ActiveRecord::Base belongs_to :parent, class_name: 'Enterprise', touch: true belongs_to :child, class_name: 'Enterprise', touch: true - has_many :permissions, class_name: 'EnterpriseRelationshipPermission' + has_many :permissions, class_name: 'EnterpriseRelationshipPermission', dependent: :destroy validates_presence_of :parent_id, :child_id validates_uniqueness_of :child_id, scope: :parent_id, message: "^That relationship is already established." diff --git a/spec/features/admin/enterprise_relationships_spec.rb b/spec/features/admin/enterprise_relationships_spec.rb index 007f663db0..bfc624bce5 100644 --- a/spec/features/admin/enterprise_relationships_spec.rb +++ b/spec/features/admin/enterprise_relationships_spec.rb @@ -69,14 +69,13 @@ feature %q{ end.to change(EnterpriseRelationship, :count).by(0) end - scenario "deleting a relationship" do e1 = create(:enterprise, name: 'One') e2 = create(:enterprise, name: 'Two') - er = create(:enterprise_relationship, parent: e1, child: e2) + er = create(:enterprise_relationship, parent: e1, child: e2, permissions_list: [:add_to_order_cycle]) visit admin_enterprise_relationships_path - page.should have_relationship e1, e2 + page.should have_relationship e1, e2, ['to add to order cycle'] first("a.delete-enterprise-relationship").click From 69d1111c731d6cfeed866c846a8ae4a553408ad2 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 10 Sep 2014 17:00:51 +1000 Subject: [PATCH 35/35] Fix expected payment amount --- spec/features/consumer/shopping/checkout_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 9e6803a669..ab53befda5 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -167,7 +167,7 @@ feature "As a consumer I want to check out my cart", js: true do page.should have_content "Your order has been processed successfully" # There are two orders - our order and our new cart - o = Spree::Order.first + o = Spree::Order.complete.first o.adjustments.shipping.first.amount.should == 4.56 o.payments.first.amount.should == 10 + 1.23 + 4.56 # items + fees + shipping end @@ -178,7 +178,6 @@ feature "As a consumer I want to check out my cart", js: true do it "takes us to the order confirmation page when submitted with a valid credit card" do toggle_payment - save_screenshot '/Users/rob/Desktop/ss.png' fill_in 'Card Number', with: "4111111111111111" select 'February', from: 'secrets.card_month' select (Date.today.year+1).to_s, from: 'secrets.card_year' @@ -189,7 +188,7 @@ feature "As a consumer I want to check out my cart", js: true do # Order should have a payment with the correct amount o = Spree::Order.complete.first - o.payments.first.amount.should == 11.23 + o.payments.first.amount.should == 15.79 end it "shows the payment processing failed message when submitted with an invalid credit card" do