mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Merge remote-tracking branch 'origin/ent_types_frontend' into ent_types_frontend
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
when "add_to_order_cycle" then "can add to order cycle"
|
||||
when "manage_products" then "can manage the products of"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}}"}
|
||||
|
||||
@@ -10,23 +10,33 @@
|
||||
overflow-x: hidden
|
||||
overflow-y: visible
|
||||
|
||||
//Hub icon styline
|
||||
//Generic text link style
|
||||
a:hover, a:active, a:focus
|
||||
color: $clr-brick-bright
|
||||
|
||||
//Hub and Producer icons
|
||||
// i.ofn-i_040-hub
|
||||
i.ofn-i_063-hub
|
||||
i.ofn-i_063-hub, i.ofn-i_064-hub-reversed, i.ofn-i_059-producer, i.ofn-i_060-producer-reversed
|
||||
font-size: 2rem
|
||||
display: inline-block
|
||||
margin-right: 0.25rem
|
||||
float: left
|
||||
@media all and (max-width: 768px)
|
||||
font-size: 1rem
|
||||
// font-size: 1rem
|
||||
|
||||
//Generic text link style
|
||||
a:hover, a:active, a:focus
|
||||
color: $clr-brick-bright
|
||||
//Closed & Open column
|
||||
.open_closed
|
||||
i
|
||||
font-size: 2rem
|
||||
float: right
|
||||
margin-left: 0.5rem
|
||||
|
||||
.hub span.hub-name-listing
|
||||
margin-top: 0.5rem
|
||||
span.margin-top
|
||||
margin-top: 0.5rem
|
||||
display: inline-block
|
||||
|
||||
//Hub Name
|
||||
span.hub-name-listing
|
||||
float: left
|
||||
font-weight: 700
|
||||
|
||||
@@ -42,38 +52,35 @@
|
||||
&.closed, &.open
|
||||
.active_table_row:first-child .skinny-head
|
||||
background-color: white
|
||||
|
||||
&.current
|
||||
&.closed, &.open
|
||||
.active_table_row:first-child .skinny-head
|
||||
background-color: $clr-brick-bright
|
||||
&.current
|
||||
&.inactive
|
||||
&.closed, &.open
|
||||
.active_table_row:first-child .skinny-head
|
||||
background-color: #555
|
||||
|
||||
//Inactive row
|
||||
&.inactive, &.inactive strong
|
||||
color: $disabled-dark
|
||||
|
||||
&, & *
|
||||
color: $disabled-dark
|
||||
|
||||
a i.ofn-i_040-hub
|
||||
color: $disabled-dark
|
||||
|
||||
&.current
|
||||
&.current
|
||||
&.inactive
|
||||
&.closed, &.open
|
||||
|
||||
a, a strong, a span, a i
|
||||
color: $disabled-dark
|
||||
&:hover, &:focus, &:active
|
||||
color: $disabled-dark
|
||||
|
||||
a i.ofn-i_040-hub
|
||||
color: white
|
||||
.active_table_row:first-child
|
||||
background-color: $disabled-dark
|
||||
a:hover, a:focus, a:active
|
||||
color: $disabled-dark
|
||||
i.ofn-i_040-hub
|
||||
background-color: $disabled-dark
|
||||
|
||||
&, & *
|
||||
color: white
|
||||
&.closed
|
||||
&:hover, &:active, &:focus
|
||||
border: none
|
||||
color: $disabled-dark
|
||||
|
||||
&.open
|
||||
.active_table_row:first-child
|
||||
color: $disabled-dark
|
||||
@@ -86,7 +93,7 @@
|
||||
.active_table_row:nth-child(2)
|
||||
background-color: rgba(255, 255, 255, 0)
|
||||
|
||||
//Open row
|
||||
//Padding second row
|
||||
&.open
|
||||
.active_table_row:nth-child(2)
|
||||
padding-bottom: 0.75rem
|
||||
@@ -94,18 +101,19 @@
|
||||
//Current selected row
|
||||
&.current
|
||||
//overwrites active_table
|
||||
&.closed
|
||||
&, & *
|
||||
color: white
|
||||
|
||||
&.closed, &.open
|
||||
div.active_table_row
|
||||
.active_table_row:first-child
|
||||
background-color: $clr-brick
|
||||
a:hover, a:focus, a:active
|
||||
strong, span
|
||||
color: $clr-brick-light
|
||||
|
||||
|
||||
|
||||
|
||||
opacity: 1
|
||||
&:hover, &:focus, &:active
|
||||
opacity: 0.9
|
||||
&, & *
|
||||
color: white
|
||||
&.open, &.closed
|
||||
.active_table_row
|
||||
border-color: $clr-brick
|
||||
&.inactive
|
||||
&.open, &.closed
|
||||
.active_table_row
|
||||
border-color: $disabled-dark
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
@@ -85,7 +88,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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 = {})
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
@@ -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|
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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)'}
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,21 +3,21 @@
|
||||
%a.hub{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"}
|
||||
%i{ ng: { class: "{'ofn-i_063-hub': hub.can_aggregate, 'ofn-i_059-producer': !hub.can_aggregate}" } }
|
||||
/ %i.ofn-i_063-hub
|
||||
%span.hub-name-listing {{ hub.name | truncate:40}}
|
||||
%span.margin-top.hub-name-listing {{ hub.name | truncate:40}}
|
||||
.columns.small-4.medium-2.large-2
|
||||
{{ hub.address.city }}
|
||||
%span.margin-top {{ hub.address.city }}
|
||||
.columns.small-2.medium-1.large-1
|
||||
{{ hub.address.state_name | uppercase }}
|
||||
%span.margin-top {{ hub.address.state_name | uppercase }}
|
||||
|
||||
.columns.small-6.medium-3.large-4.text-right{"bo-if" => "hub.active"}
|
||||
%a.hub{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"}
|
||||
%a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"}
|
||||
%i.ofn-i_033-open-sign
|
||||
%span {{ hub.orders_close_at | sensible_timeframe }}
|
||||
%span.margin-top {{ hub.orders_close_at | sensible_timeframe }}
|
||||
|
||||
.columns.small-6.medium-3.large-4.text-right{"bo-if" => "!hub.active"}
|
||||
%a.hub{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"}
|
||||
%a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"}
|
||||
%i.ofn-i_032-closed-sign
|
||||
%span Orders closed
|
||||
%span.margin-top Orders closed
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
14
app/views/spree/admin/products/bulk_edit/_actions.html.haml
Normal file
14
app/views/spree/admin/products/bulk_edit/_actions.html.haml
Normal file
@@ -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 }}
|
||||
2
app/views/spree/admin/products/bulk_edit/_data.html.haml
Normal file
2
app/views/spree/admin/products/bulk_edit/_data.html.haml
Normal file
@@ -0,0 +1,2 @@
|
||||
= admin_inject_producers
|
||||
= admin_inject_taxons
|
||||
18
app/views/spree/admin/products/bulk_edit/_filters.html.haml
Normal file
18
app/views/spree/admin/products/bulk_edit/_filters.html.haml
Normal file
@@ -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()" }
|
||||
12
app/views/spree/admin/products/bulk_edit/_header.html.haml
Normal file
12
app/views/spree/admin/products/bulk_edit/_header.html.haml
Normal file
@@ -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)
|
||||
@@ -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.
|
||||
@@ -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'
|
||||
@@ -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
|
||||
@@ -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" }
|
||||
@@ -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" }
|
||||
14
app/views/spree/layouts/admin/_login_nav.html.haml
Normal file
14
app/views/spree/layouts/admin/_login_nav.html.haml
Normal file
@@ -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'
|
||||
@@ -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
|
||||
|
||||
60
lib/open_food_network/permissions.rb
Normal file
60
lib/open_food_network/permissions.rb
Normal file
@@ -0,0 +1,60 @@
|
||||
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_and_related_enterprises_with :add_to_order_cycle
|
||||
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
|
||||
|
||||
def managed_products
|
||||
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
|
||||
|
||||
def managed_product_enterprises
|
||||
managed_and_related_enterprises_with :manage_products
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def related_enterprise_products
|
||||
Spree::Product.where('supplier_id IN (?)', related_enterprises_with(:manage_products))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -729,20 +729,29 @@ 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(: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) }
|
||||
|
||||
before(:each) do
|
||||
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: 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
|
||||
@@ -751,14 +760,15 @@ 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
|
||||
|
||||
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: [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, supplier_permitted.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
|
||||
@@ -770,32 +780,34 @@ 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: s1.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"
|
||||
|
||||
fill_in "product_name", with: "Big Bag Of Potatoes"
|
||||
select(s2.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"
|
||||
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."
|
||||
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -436,26 +436,35 @@ 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
|
||||
product.save!
|
||||
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
|
||||
let!(:product_managed) { create(:product, supplier: supplier_managed) }
|
||||
let!(:product_permitted) { create(:product, supplier: supplier_permitted) }
|
||||
|
||||
before do
|
||||
@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 +473,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 +485,84 @@ 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'
|
||||
click_button 'Add coordinator fee'
|
||||
select 'First Distributor Fee', from: 'order_cycle_coordinator_fee_0_id'
|
||||
select_incoming_variant supplier_managed, 0, product_managed.master
|
||||
select_incoming_variant supplier_permitted, 1, product_permitted.master
|
||||
|
||||
select 'First Distributor', from: 'new_distributor_id'
|
||||
select 'Managed distributor', from: 'order_cycle_coordinator_id'
|
||||
click_button 'Add coordinator fee'
|
||||
select 'Managed distributor fee', from: 'order_cycle_coordinator_fee_0_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
|
||||
@@ -539,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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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]}
|
||||
|
||||
132
spec/lib/open_food_network/permissions_spec.rb
Normal file
132
spec/lib/open_food_network/permissions_spec.rb
Normal file
@@ -0,0 +1,132 @@
|
||||
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
|
||||
let(:e) { double(:enterprise) }
|
||||
|
||||
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])
|
||||
|
||||
permissions.order_cycle_enterprises.should == [e]
|
||||
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 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 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
|
||||
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
|
||||
|
||||
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) }
|
||||
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -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 = []
|
||||
@@ -264,7 +275,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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user