From f06d909c23e23104485e923370c709095963bde9 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 15 Jan 2016 18:09:07 +1100 Subject: [PATCH] WIP: Adding infrastructure to show/hide variants + overrides using inventory items --- app/assets/javascripts/admin/all.js | 1 + .../inventory_items/inventory_items.js.coffee | 1 + .../inventory_item_resource.js.coffee | 5 + .../services/inventory_items.js.coffee | 25 ++ .../variant_overrides_controller.js.coffee | 7 +- .../inventory_products_filter.js.coffee | 12 + .../inventory_variants_filter.js.coffee | 12 + .../new_inventory_products_filter.js.coffee | 8 + .../new_inventory_variants_filter.js.coffee | 7 + .../variant_overrides.js.coffee | 2 +- .../admin/components/save_bar.sass | 4 + .../admin/inventory_items_controller.rb | 28 ++ .../admin/variant_overrides_controller.rb | 2 + app/helpers/admin/injection_helper.rb | 4 + app/models/inventory_item.rb | 3 + app/models/spree/ability_decorator.rb | 14 + .../api/admin/inventory_item_serializer.rb | 3 + .../admin/variant_overrides/_data.html.haml | 1 + .../variant_overrides/_new_variants.html.haml | 25 ++ .../variant_overrides/_products.html.haml | 11 +- .../_products_product.html.haml | 1 + .../_products_variants.html.haml | 4 +- .../admin/variant_overrides/index.html.haml | 6 +- .../admin/orders/bulk_management.html.haml | 2 +- config/routes.rb | 2 + .../admin/inventory_items_controller_spec.rb | 129 ++++++ .../variant_overrides_controller_spec.rb | 17 + spec/factories.rb | 6 + .../admin/bulk_order_management_spec.rb | 4 + spec/features/admin/variant_overrides_spec.rb | 405 ++++++++++-------- ...ariant_overrides_controller_spec.js.coffee | 2 + .../services/inventory_items_spec.js.coffee | 73 ++++ 32 files changed, 643 insertions(+), 183 deletions(-) create mode 100644 app/assets/javascripts/admin/inventory_items/inventory_items.js.coffee create mode 100644 app/assets/javascripts/admin/inventory_items/services/inventory_item_resource.js.coffee create mode 100644 app/assets/javascripts/admin/inventory_items/services/inventory_items.js.coffee create mode 100644 app/assets/javascripts/admin/variant_overrides/filters/inventory_products_filter.js.coffee create mode 100644 app/assets/javascripts/admin/variant_overrides/filters/inventory_variants_filter.js.coffee create mode 100644 app/assets/javascripts/admin/variant_overrides/filters/new_inventory_products_filter.js.coffee create mode 100644 app/assets/javascripts/admin/variant_overrides/filters/new_inventory_variants_filter.js.coffee create mode 100644 app/controllers/admin/inventory_items_controller.rb create mode 100644 app/serializers/api/admin/inventory_item_serializer.rb create mode 100644 app/views/admin/variant_overrides/_new_variants.html.haml create mode 100644 spec/controllers/admin/inventory_items_controller_spec.rb create mode 100644 spec/javascripts/unit/admin/inventory_items/services/inventory_items_spec.js.coffee diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js index 3485f45e23..344c5e8a5d 100644 --- a/app/assets/javascripts/admin/all.js +++ b/app/assets/javascripts/admin/all.js @@ -30,6 +30,7 @@ //= require ./enterprise_fees/enterprise_fees //= require ./enterprise_groups/enterprise_groups //= require ./index_utils/index_utils +//= require ./inventory_items/inventory_items //= require ./line_items/line_items //= require ./orders/orders //= require ./order_cycles/order_cycles diff --git a/app/assets/javascripts/admin/inventory_items/inventory_items.js.coffee b/app/assets/javascripts/admin/inventory_items/inventory_items.js.coffee new file mode 100644 index 0000000000..5ee31bad4b --- /dev/null +++ b/app/assets/javascripts/admin/inventory_items/inventory_items.js.coffee @@ -0,0 +1 @@ +angular.module("admin.inventoryItems", ['ngResource']) diff --git a/app/assets/javascripts/admin/inventory_items/services/inventory_item_resource.js.coffee b/app/assets/javascripts/admin/inventory_items/services/inventory_item_resource.js.coffee new file mode 100644 index 0000000000..fb2699f854 --- /dev/null +++ b/app/assets/javascripts/admin/inventory_items/services/inventory_item_resource.js.coffee @@ -0,0 +1,5 @@ +angular.module("admin.inventoryItems").factory 'InventoryItemResource', ($resource) -> + $resource('/admin/inventory_items/:id/:action.json', {}, { + 'update': + method: 'PUT' + }) diff --git a/app/assets/javascripts/admin/inventory_items/services/inventory_items.js.coffee b/app/assets/javascripts/admin/inventory_items/services/inventory_items.js.coffee new file mode 100644 index 0000000000..ac7971ad24 --- /dev/null +++ b/app/assets/javascripts/admin/inventory_items/services/inventory_items.js.coffee @@ -0,0 +1,25 @@ +angular.module("admin.inventoryItems").factory "InventoryItems", (inventoryItems, InventoryItemResource) -> + new class InventoryItems + inventoryItems: {} + errors: {} + + constructor: -> + for ii in inventoryItems + @inventoryItems[ii.enterprise_id] ||= {} + @inventoryItems[ii.enterprise_id][ii.variant_id] = new InventoryItemResource(ii) + + setVisibility: (hub_id, variant_id, visible) -> + if @inventoryItems[hub_id] && @inventoryItems[hub_id][variant_id] + inventory_item = angular.extend(angular.copy(@inventoryItems[hub_id][variant_id]), {visible: visible}) + InventoryItemResource.update {id: inventory_item.id}, inventory_item, (data) => + @inventoryItems[hub_id][variant_id] = data + , (response) => + @errors[hub_id] ||= {} + @errors[hub_id][variant_id] = response.data.errors + else + InventoryItemResource.save {enterprise_id: hub_id, variant_id: variant_id, visible: visible}, (data) => + @inventoryItems[hub_id] ||= {} + @inventoryItems[hub_id][variant_id] = data + , (response) => + @errors[hub_id] ||= {} + @errors[hub_id][variant_id] = response.data.errors diff --git a/app/assets/javascripts/admin/variant_overrides/controllers/variant_overrides_controller.js.coffee b/app/assets/javascripts/admin/variant_overrides/controllers/variant_overrides_controller.js.coffee index b65df80d66..c7743efa4f 100644 --- a/app/assets/javascripts/admin/variant_overrides/controllers/variant_overrides_controller.js.coffee +++ b/app/assets/javascripts/admin/variant_overrides/controllers/variant_overrides_controller.js.coffee @@ -1,11 +1,15 @@ -angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl", ($scope, $http, $timeout, Indexer, Columns, SpreeApiAuth, PagedFetcher, StatusMessage, hubs, producers, hubPermissions, VariantOverrides, DirtyVariantOverrides) -> +angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl", ($scope, $http, $timeout, Indexer, Columns, SpreeApiAuth, PagedFetcher, StatusMessage, hubs, producers, hubPermissions, InventoryItems, VariantOverrides, DirtyVariantOverrides) -> $scope.hubs = Indexer.index hubs $scope.hub = null $scope.products = [] $scope.producers = producers $scope.producersByID = Indexer.index producers $scope.hubPermissions = hubPermissions + $scope.showHidden = false + $scope.productLimit = 10 $scope.variantOverrides = VariantOverrides.variantOverrides + $scope.inventoryItems = InventoryItems.inventoryItems + $scope.setVisibility = InventoryItems.setVisibility $scope.StatusMessage = StatusMessage $scope.columns = Columns.setColumns @@ -17,6 +21,7 @@ angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl", on_demand: { name: "On Demand", visible: false } reset: { name: "Reset Stock Level", visible: false } inheritance: { name: "Inheritance", visible: false } + visibility: { name: "Show/Hide", visible: false } $scope.resetSelectFilters = -> $scope.producerFilter = 0 diff --git a/app/assets/javascripts/admin/variant_overrides/filters/inventory_products_filter.js.coffee b/app/assets/javascripts/admin/variant_overrides/filters/inventory_products_filter.js.coffee new file mode 100644 index 0000000000..4f5a3c5191 --- /dev/null +++ b/app/assets/javascripts/admin/variant_overrides/filters/inventory_products_filter.js.coffee @@ -0,0 +1,12 @@ +angular.module("admin.variantOverrides").filter "inventoryProducts", ($filter, InventoryItems) -> + return (products, hub_id, showHidden) -> + return [] if !hub_id + return $filter('filter')(products, (product) -> + for variant in product.variants + if InventoryItems.inventoryItems.hasOwnProperty(hub_id) && InventoryItems.inventoryItems[hub_id].hasOwnProperty(variant.id) + if showHidden + return true + else + return true if InventoryItems.inventoryItems[hub_id][variant.id].visible + false + , true) diff --git a/app/assets/javascripts/admin/variant_overrides/filters/inventory_variants_filter.js.coffee b/app/assets/javascripts/admin/variant_overrides/filters/inventory_variants_filter.js.coffee new file mode 100644 index 0000000000..51238fa189 --- /dev/null +++ b/app/assets/javascripts/admin/variant_overrides/filters/inventory_variants_filter.js.coffee @@ -0,0 +1,12 @@ +angular.module("admin.variantOverrides").filter "inventoryVariants", ($filter, InventoryItems) -> + return (variants, hub_id, showHidden) -> + return [] if !hub_id + return $filter('filter')(variants, (variant) -> + if InventoryItems.inventoryItems.hasOwnProperty(hub_id) && InventoryItems.inventoryItems[hub_id].hasOwnProperty(variant.id) + if showHidden + return true + else + return InventoryItems.inventoryItems[hub_id][variant.id].visible + else + false + , true) diff --git a/app/assets/javascripts/admin/variant_overrides/filters/new_inventory_products_filter.js.coffee b/app/assets/javascripts/admin/variant_overrides/filters/new_inventory_products_filter.js.coffee new file mode 100644 index 0000000000..ec1148c22e --- /dev/null +++ b/app/assets/javascripts/admin/variant_overrides/filters/new_inventory_products_filter.js.coffee @@ -0,0 +1,8 @@ +angular.module("admin.variantOverrides").filter "newInventoryProducts", ($filter, InventoryItems) -> + return (products, hub_id) -> + return [] if !hub_id + return products unless InventoryItems.inventoryItems.hasOwnProperty(hub_id) + return $filter('filter')(products, (product) -> + for variant in product.variants + return true if !InventoryItems.inventoryItems[hub_id].hasOwnProperty(variant.id) + , true) diff --git a/app/assets/javascripts/admin/variant_overrides/filters/new_inventory_variants_filter.js.coffee b/app/assets/javascripts/admin/variant_overrides/filters/new_inventory_variants_filter.js.coffee new file mode 100644 index 0000000000..d06724a704 --- /dev/null +++ b/app/assets/javascripts/admin/variant_overrides/filters/new_inventory_variants_filter.js.coffee @@ -0,0 +1,7 @@ +angular.module("admin.variantOverrides").filter "newInventoryVariants", ($filter, InventoryItems) -> + return (variants, hub_id) -> + return [] if !hub_id + return variants unless InventoryItems.inventoryItems.hasOwnProperty(hub_id) + return $filter('filter')(variants, (variant) -> + !InventoryItems.inventoryItems[hub_id].hasOwnProperty(variant.id) + , true) diff --git a/app/assets/javascripts/admin/variant_overrides/variant_overrides.js.coffee b/app/assets/javascripts/admin/variant_overrides/variant_overrides.js.coffee index ae46cd14c7..c302c0463e 100644 --- a/app/assets/javascripts/admin/variant_overrides/variant_overrides.js.coffee +++ b/app/assets/javascripts/admin/variant_overrides/variant_overrides.js.coffee @@ -1 +1 @@ -angular.module("admin.variantOverrides", ["pasvaz.bindonce", "admin.indexUtils", "admin.utils", "admin.dropdown"]) +angular.module("admin.variantOverrides", ["pasvaz.bindonce", "admin.indexUtils", "admin.utils", "admin.dropdown", "admin.inventoryItems"]) diff --git a/app/assets/stylesheets/admin/components/save_bar.sass b/app/assets/stylesheets/admin/components/save_bar.sass index c6b1236490..3fdeb47339 100644 --- a/app/assets/stylesheets/admin/components/save_bar.sass +++ b/app/assets/stylesheets/admin/components/save_bar.sass @@ -7,3 +7,7 @@ color: #5498da h5 color: #5498da + +// Making space for save-bar +.margin-bottom-50 + margin-bottom: 50px diff --git a/app/controllers/admin/inventory_items_controller.rb b/app/controllers/admin/inventory_items_controller.rb new file mode 100644 index 0000000000..cb649fe6f2 --- /dev/null +++ b/app/controllers/admin/inventory_items_controller.rb @@ -0,0 +1,28 @@ +module Admin + class InventoryItemsController < ResourceController + + respond_to :json + + respond_override update: { json: { + success: lambda { sleep 3; render_as_json @inventory_item }, + failure: lambda { render json: { errors: @inventory_item.errors.full_messages }, status: :unprocessable_entity } + } } + + respond_override create: { json: { + success: lambda { sleep 3; render_as_json @inventory_item }, + failure: lambda { render json: { errors: @inventory_item.errors.full_messages }, status: :unprocessable_entity } + } } + + private + + # Overriding Spree method to load data from params here so that + # we can authorise #create using an object with required attributes + def build_resource + if parent_data.present? + parent.send(controller_name).build + else + model_class.new(params[object_name]) # This line changed + end + end + end +end diff --git a/app/controllers/admin/variant_overrides_controller.rb b/app/controllers/admin/variant_overrides_controller.rb index c886f80652..446ca457eb 100644 --- a/app/controllers/admin/variant_overrides_controller.rb +++ b/app/controllers/admin/variant_overrides_controller.rb @@ -54,6 +54,8 @@ module Admin @hub_permissions = OpenFoodNetwork::Permissions.new(spree_current_user). variant_override_enterprises_per_hub + + @inventory_items = InventoryItem.where(enterprise_id: @hubs) end def load_collection diff --git a/app/helpers/admin/injection_helper.rb b/app/helpers/admin/injection_helper.rb index ba65e37860..511eced707 100644 --- a/app/helpers/admin/injection_helper.rb +++ b/app/helpers/admin/injection_helper.rb @@ -39,6 +39,10 @@ module Admin admin_inject_json_ams_array opts[:module], "producers", @producers, Api::Admin::IdNameSerializer end + def admin_inject_inventory_items(opts={module: 'ofn.admin'}) + admin_inject_json_ams_array opts[:module], "inventoryItems", @inventory_items, Api::Admin::InventoryItemSerializer + end + def admin_inject_enterprise_permissions permissions = {can_manage_shipping_methods: can?(:manage_shipping_methods, @enterprise), diff --git a/app/models/inventory_item.rb b/app/models/inventory_item.rb index b9aed9899a..05198b6dec 100644 --- a/app/models/inventory_item.rb +++ b/app/models/inventory_item.rb @@ -5,6 +5,9 @@ class InventoryItem < ActiveRecord::Base belongs_to :variant, class_name: "Spree::Variant" validates :variant_id, uniqueness: { scope: :enterprise_id } + validates :enterprise_id, presence: true + validates :variant_id, presence: true + validates :visible, inclusion: { in: [true, false], message: "must be true or false" } scope :visible, where(visible: true) scope :hidden, where(visible: false) diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 9bc303116b..8c93fe4b71 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -125,6 +125,20 @@ class AbilityDecorator hub_auth && producer_auth end + can [:admin, :create, :update], InventoryItem do |ii| + next false unless ii.enterprise.present? && ii.variant.andand.product.andand.supplier.present? + + hub_auth = OpenFoodNetwork::Permissions.new(user). + variant_override_hubs. + include? ii.enterprise + + producer_auth = OpenFoodNetwork::Permissions.new(user). + variant_override_producers. + include? ii.variant.product.supplier + + hub_auth && producer_auth + end + can [:admin, :index, :read, :create, :edit, :update_positions, :destroy], Spree::ProductProperty can [:admin, :index, :read, :create, :edit, :update, :destroy], Spree::Image diff --git a/app/serializers/api/admin/inventory_item_serializer.rb b/app/serializers/api/admin/inventory_item_serializer.rb new file mode 100644 index 0000000000..15f8d35058 --- /dev/null +++ b/app/serializers/api/admin/inventory_item_serializer.rb @@ -0,0 +1,3 @@ +class Api::Admin::InventoryItemSerializer < ActiveModel::Serializer + attributes :id, :enterprise_id, :variant_id, :visible +end diff --git a/app/views/admin/variant_overrides/_data.html.haml b/app/views/admin/variant_overrides/_data.html.haml index 64a7619ea7..9d371415fe 100644 --- a/app/views/admin/variant_overrides/_data.html.haml +++ b/app/views/admin/variant_overrides/_data.html.haml @@ -3,3 +3,4 @@ = admin_inject_hub_permissions = admin_inject_producers module: 'admin.variantOverrides' = admin_inject_variant_overrides += admin_inject_inventory_items module: 'admin.variantOverrides' diff --git a/app/views/admin/variant_overrides/_new_variants.html.haml b/app/views/admin/variant_overrides/_new_variants.html.haml new file mode 100644 index 0000000000..ed87bc3e86 --- /dev/null +++ b/app/views/admin/variant_overrides/_new_variants.html.haml @@ -0,0 +1,25 @@ +%hr.divider.sixteen.columns.alpha.omega{ ng: { show: 'newProducts.length > 0' } } +%table#new-variants{ ng: { show: 'newProducts.length > 0' } } + %col.producer{ width: "20%" } + %col.product{ width: "20%" } + %col.variant{ width: "40%" } + %col.add{ width: "10%" } + %col.ignore{ width: "10%" } + %thead + %tr + %th.producer Producer + %th.product Product + %th.variant Variant + %th.add Add + %th.ignore Ignore + %tbody{ bindonce: true, ng: { repeat: 'product in newProducts = (products | hubPermissions:hubPermissions:hub.id | newInventoryProducts:hub.id)' } } + %tr{ id: "nv_{{variant.id}}", ng: { repeat: 'variant in product.variants | newInventoryVariants:hub.id'} } + %td.producer{ bo: { bind: 'producersByID[product.producer_id].name'} } + %td.product{ bo: { bind: 'product.name'} } + %td.variant + %span{ bo: { bind: 'variant.display_name || ""'} } + .variant-override-unit{ bo: { bind: 'variant.unit_to_display'} } + %td.add + %input.fullwidth{ :type => 'button', value: "Add", ng: { click: "setVisibility(hub.id,variant.id,true)" } } + %td.ignore + %input.fullwidth{ :type => 'button', value: "Ignore", ng: { click: "setVisibility(hub.id,variant.id,false)" } } diff --git a/app/views/admin/variant_overrides/_products.html.haml b/app/views/admin/variant_overrides/_products.html.haml index ed3354de9a..e34f6eccaa 100644 --- a/app/views/admin/variant_overrides/_products.html.haml +++ b/app/views/admin/variant_overrides/_products.html.haml @@ -1,4 +1,4 @@ -%table.index.bulk{ ng: {show: 'hub'}} +%table.index.bulk#variant-overrides{ ng: {show: 'hub'}} %col.producer{ width: "20%", ng: { show: 'columns.producer.visible' } } %col.product{ width: "20%", ng: { show: 'columns.product.visible' } } %col.sku{ width: "20%", ng: { show: 'columns.sku.visible' } } @@ -8,6 +8,7 @@ %col.reset{ width: "1%", ng: { show: 'columns.reset.visible' } } %col.reset{ width: "15%", ng: { show: 'columns.reset.visible' } } %col.inheritance{ width: "5%", ng: { show: 'columns.inheritance.visible' } } + %col.visibility{ width: "10%", ng: { show: 'columns.visibility.visible' } } %thead %tr{ ng: { controller: "ColumnsCtrl" } } %th.producer{ ng: { show: 'columns.producer.visible' } } Producer @@ -18,6 +19,12 @@ %th.on_demand{ ng: { show: 'columns.on_demand.visible' } } On Demand? %th.reset{ colspan: 2, ng: { show: 'columns.reset.visible' } } Enable Stock Level Reset? %th.inheritance{ ng: { show: 'columns.inheritance.visible' } } Inherit? - %tbody{bindonce: true, ng: {repeat: 'product in products | hubPermissions:hubPermissions:hub.id | attrFilter:{producer_id:producerFilter} | filter:query' } } + %th.visibility{ ng: { show: 'columns.visibility.visible' } } Show/Hide? + %tbody{bindonce: true, ng: {repeat: 'product in filteredProducts = (products | hubPermissions:hubPermissions:hub.id | inventoryProducts:hub.id:showHidden | attrFilter:{producer_id:producerFilter} | filter:query) | limitTo:productLimit' } } = render 'admin/variant_overrides/products_product' = render 'admin/variant_overrides/products_variants' + +.sixteen.columns.alpha.omega.text-center{ ng: {show: 'hub && productLimit < filteredProducts.length'}} + %input{ type: 'button', value: 'Show More', ng: { click: 'productLimit = productLimit + 10' } } + or + %input{ type: 'button', value: "Show All ({{ filteredProducts.length - productLimit }})", ng: { click: 'productLimit = filteredProducts.length' } } diff --git a/app/views/admin/variant_overrides/_products_product.html.haml b/app/views/admin/variant_overrides/_products_product.html.haml index b7cb11041b..70b48e3909 100644 --- a/app/views/admin/variant_overrides/_products_product.html.haml +++ b/app/views/admin/variant_overrides/_products_product.html.haml @@ -7,3 +7,4 @@ %td.on_demand{ ng: { show: 'columns.on_demand.visible' } } %td.reset{ colspan: 2, ng: { show: 'columns.reset.visible' } } %td.inheritance{ ng: { show: 'columns.inheritance.visible' } } + %td.visibility{ ng: { show: 'columns.visibility.visible' } } diff --git a/app/views/admin/variant_overrides/_products_variants.html.haml b/app/views/admin/variant_overrides/_products_variants.html.haml index 87ec1709e4..1af6dab3d8 100644 --- a/app/views/admin/variant_overrides/_products_variants.html.haml +++ b/app/views/admin/variant_overrides/_products_variants.html.haml @@ -1,4 +1,4 @@ -%tr.variant{ id: "v_{{variant.id}}", ng: {repeat: 'variant in product.variants'}} +%tr.variant{ id: "v_{{variant.id}}", ng: {repeat: 'variant in product.variants | inventoryVariants:hub.id:showHidden'}} %td.producer{ ng: { show: 'columns.producer.visible' } } %td.product{ ng: { show: 'columns.product.visible' } } %span{ bo: { bind: 'variant.display_name || ""'} } @@ -17,3 +17,5 @@ %input{name: 'variant-overrides-{{ variant.id }}-default_stock', type: 'text', ng: {model: 'variantOverrides[hub.id][variant.id].default_stock'}, placeholder: '{{ variant.default_stock ? variant.default_stock : "Default stock"}}', 'ofn-track-variant-override' => 'default_stock'} %td.inheritance{ ng: { show: 'columns.inheritance.visible' } } %input.field{ :type => 'checkbox', name: 'variant-overrides-{{ variant.id }}-inherit', ng: { model: 'inherit' }, 'track-inheritance' => true } + %td.visibility{ ng: { show: 'columns.visibility.visible' } } + %input.fullwidth{ :type => 'button', ng: { value: "inventoryItems[hub.id][variant.id].visible ? 'Hide' : 'Show'", click: "setVisibility(hub.id,variant.id,!inventoryItems[hub.id][variant.id].visible)", class: "{ hidden: !inventoryItems[hub.id][variant.id].visible}" } } diff --git a/app/views/admin/variant_overrides/index.html.haml b/app/views/admin/variant_overrides/index.html.haml index 9027445e62..ba5ce24479 100644 --- a/app/views/admin/variant_overrides/index.html.haml +++ b/app/views/admin/variant_overrides/index.html.haml @@ -1,12 +1,14 @@ = render 'admin/variant_overrides/header' = render 'admin/variant_overrides/data' -%div{ ng: { app: 'admin.variantOverrides', controller: 'AdminVariantOverridesCtrl', init: 'initialise()' } } +.margin-bottom-50{ ng: { app: 'admin.variantOverrides', controller: 'AdminVariantOverridesCtrl', init: 'initialise()' } } = render 'admin/variant_overrides/filters' + = render 'admin/variant_overrides/new_variants' %hr.divider.sixteen.columns.alpha.omega{ ng: { show: 'hub' } } .controls.sixteen.columns.alpha.omega{ ng: { show: 'hub' } } %input.four.columns.alpha{ type: 'button', value: 'Reset Stock to Defaults', 'ng-click' => 'resetStock()' } - %div.nine.columns.alpha   + %input.four.columns{ type: 'button', ng: { value: "showHidden ? 'Hide Hidden' : 'Show Hidden'", click: 'showHidden = !showHidden' } } + %div.five.columns   = render 'admin/shared/columns_dropdown' %form{ name: 'variant_overrides_form' } diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 46e5caf191..003c1ab5f8 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -92,7 +92,7 @@ %div{ :class => "sixteen columns alpha", 'ng-show' => '!RequestMonitor.loading && filteredLineItems.length == 0'} %h1#no_results No orders found. - %div{ 'ng-hide' => 'RequestMonitor.loading || filteredLineItems.length == 0' } + .margin-bottom-50{ 'ng-hide' => 'RequestMonitor.loading || filteredLineItems.length == 0' } %form{ name: 'bulk_order_form' } %table.index#listing_orders.bulk{ :class => "sixteen columns alpha" } %thead diff --git a/config/routes.rb b/config/routes.rb index 62c4153d14..64dbc7c7f8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -109,6 +109,8 @@ Openfoodnetwork::Application.routes.draw do post :bulk_reset, on: :collection end + resources :inventory_items, only: [:create, :update] + resources :customers, only: [:index, :update] resource :content diff --git a/spec/controllers/admin/inventory_items_controller_spec.rb b/spec/controllers/admin/inventory_items_controller_spec.rb new file mode 100644 index 0000000000..4828f5c662 --- /dev/null +++ b/spec/controllers/admin/inventory_items_controller_spec.rb @@ -0,0 +1,129 @@ +require 'spec_helper' + +describe Admin::InventoryItemsController, type: :controller do + # include AuthenticationWorkflow + + describe "create" do + context "json" do + let(:format) { :json } + + let(:enterprise) { create(:distributor_enterprise) } + let(:variant) { create(:variant) } + let(:inventory_item) { create(:inventory_item, enterprise: enterprise, variant: variant, visible: true) } + let(:params) { { format: format, inventory_item: { enterprise_id: enterprise.id, variant_id: variant.id, visible: false } } } + + context "where I don't manage the inventory item enterprise" do + before do + user = create(:user) + user.owned_enterprises << create(:enterprise) + allow(controller).to receive(:spree_current_user) { user } + end + + it "redirects to unauthorized" do + spree_post :create, params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "where I manage the variant override hub" do + before do + allow(controller).to receive(:spree_current_user) { enterprise.owner } + end + + context "but the producer has not granted VO permission" do + it "redirects to unauthorized" do + spree_post :create, params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "and the producer has granted VO permission" do + before do + create(:enterprise_relationship, parent: variant.product.supplier, child: enterprise, permissions_list: [:create_variant_overrides]) + end + + context "with acceptable data" do + it "allows me to create the inventory item" do + expect{ spree_post :create, params }.to change{InventoryItem.count}.by(1) + inventory_item = InventoryItem.last + expect(inventory_item.enterprise).to eq enterprise + expect(inventory_item.variant).to eq variant + expect(inventory_item.visible).to be false + end + end + + context "with unacceptable data" do + render_views + let!(:bad_params) { { format: format, inventory_item: { enterprise_id: enterprise.id, variant_id: variant.id, visible: nil } } } + + it "returns an error message" do + expect{ spree_post :create, bad_params }.to change{InventoryItem.count}.by(0) + expect(response.body).to eq Hash[:errors, ["Visible must be true or false"]].to_json + end + end + end + end + end + end + + describe "update" do + context "json" do + let(:format) { :json } + + let(:enterprise) { create(:distributor_enterprise) } + let(:variant) { create(:variant) } + let(:inventory_item) { create(:inventory_item, enterprise: enterprise, variant: variant, visible: true) } + let(:params) { { format: format, id: inventory_item.id, inventory_item: { visible: false } } } + + context "where I don't manage the inventory item enterprise" do + before do + user = create(:user) + user.owned_enterprises << create(:enterprise) + allow(controller).to receive(:spree_current_user) { user } + end + + it "redirects to unauthorized" do + spree_put :update, params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "where I manage the variant override hub" do + before do + allow(controller).to receive(:spree_current_user) { enterprise.owner } + end + + context "but the producer has not granted VO permission" do + it "redirects to unauthorized" do + spree_put :update, params + expect(response).to redirect_to spree.unauthorized_path + end + end + + context "and the producer has granted VO permission" do + before do + create(:enterprise_relationship, parent: variant.product.supplier, child: enterprise, permissions_list: [:create_variant_overrides]) + end + + context "with acceptable data" do + it "allows me to update the inventory item" do + spree_put :update, params + inventory_item.reload + expect(inventory_item.visible).to eq false + end + end + + context "with unacceptable data" do + render_views + let!(:bad_params) { { format: format, id: inventory_item.id, inventory_item: { visible: nil } } } + + it "returns an error message" do + expect{ spree_put :update, bad_params }.to change{InventoryItem.count}.by(0) + expect(response.body).to eq Hash[:errors, ["Visible must be true or false"]].to_json + end + end + end + end + end + end +end diff --git a/spec/controllers/admin/variant_overrides_controller_spec.rb b/spec/controllers/admin/variant_overrides_controller_spec.rb index d796f2d52f..3bd2632979 100644 --- a/spec/controllers/admin/variant_overrides_controller_spec.rb +++ b/spec/controllers/admin/variant_overrides_controller_spec.rb @@ -9,6 +9,7 @@ describe Admin::VariantOverridesController, type: :controller do let(:hub) { create(:distributor_enterprise) } let(:variant) { create(:variant) } + let!(:inventory_item) { create(:inventory_item, enterprise: hub, variant: variant, visible: true) } let!(:variant_override) { create(:variant_override, hub: hub, variant: variant) } let(:variant_override_params) { [ { id: variant_override.id, price: 123.45, count_on_hand: 321, sku: "MySKU", on_demand: false } ] } @@ -42,6 +43,14 @@ describe Admin::VariantOverridesController, type: :controller do create(:enterprise_relationship, parent: variant.product.supplier, child: hub, permissions_list: [:create_variant_overrides]) end + it "loads data" do + spree_put :bulk_update, format: format, variant_overrides: variant_override_params + expect(assigns[:hubs]).to eq [hub] + expect(assigns[:producers]).to eq [variant.product.supplier] + expect(assigns[:hub_permissions]).to eq Hash[hub.id,[variant.product.supplier.id]] + expect(assigns[:inventory_items]).to eq [inventory_item] + end + it "allows me to update the variant override" do spree_put :bulk_update, format: format, variant_overrides: variant_override_params variant_override.reload @@ -106,6 +115,14 @@ describe Admin::VariantOverridesController, type: :controller do context "where the producer has granted create_variant_overrides permission to the hub" do let!(:er1) { create(:enterprise_relationship, parent: producer, child: hub, permissions_list: [:create_variant_overrides]) } + it "loads data" do + spree_put :bulk_reset, params + expect(assigns[:hubs]).to eq [hub] + expect(assigns[:producers]).to eq [producer] + expect(assigns[:hub_permissions]).to eq Hash[hub.id,[producer.id]] + expect(assigns[:inventory_items]).to eq [] + end + it "updates stock to default values where reset is enabled" do expect(variant_override1.reload.count_on_hand).to eq 5 # reset enabled expect(variant_override2.reload.count_on_hand).to eq 2 # reset disabled diff --git a/spec/factories.rb b/spec/factories.rb index dd8d02e7bf..21c6cecd15 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -98,6 +98,12 @@ FactoryGirl.define do resettable false end + factory :inventory_item, :class => InventoryItem do + enterprise + variant + visible true + end + factory :enterprise, :class => Enterprise do owner { FactoryGirl.create :user } sequence(:name) { |n| "Enterprise #{n}" } diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index d28a20d6f9..767274b274 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -159,6 +159,7 @@ feature %q{ first("div#columns-dropdown", :text => "COLUMNS").click first("div#columns-dropdown div.menu div.menu_item", text: "Weight/Volume").click first("div#columns-dropdown div.menu div.menu_item", text: "Price").click + first("div#columns-dropdown", :text => "COLUMNS").click within "tr#li_#{li1.id}" do expect(page).to have_field "price", with: "$50.00" fill_in "final_weight_volume", :with => 2000 @@ -177,6 +178,7 @@ feature %q{ visit '/admin/orders/bulk_management' first("div#columns-dropdown", :text => "COLUMNS").click first("div#columns-dropdown div.menu div.menu_item", text: "Price").click + first("div#columns-dropdown", :text => "COLUMNS").click within "tr#li_#{li1.id}" do expect(page).to have_field "price", with: "$#{format("%.2f",li1.price * 5)}" fill_in "quantity", :with => 6 @@ -190,6 +192,7 @@ feature %q{ visit '/admin/orders/bulk_management' first("div#columns-dropdown", :text => "COLUMNS").click first("div#columns-dropdown div.menu div.menu_item", text: "Weight/Volume").click + first("div#columns-dropdown", :text => "COLUMNS").click within "tr#li_#{li1.id}" do expect(page).to have_field "final_weight_volume", with: "#{li1.final_weight_volume.round}" fill_in "quantity", :with => 6 @@ -211,6 +214,7 @@ feature %q{ first("div#columns-dropdown", :text => "COLUMNS").click first("div#columns-dropdown div.menu div.menu_item", text: "Producer").click + first("div#columns-dropdown", :text => "COLUMNS").click expect(page).to_not have_selector "th", :text => "PRODUCER" expect(page).to have_selector "th", :text => "NAME" diff --git a/spec/features/admin/variant_overrides_spec.rb b/spec/features/admin/variant_overrides_spec.rb index 378afa3626..fafcb348db 100644 --- a/spec/features/admin/variant_overrides_spec.rb +++ b/spec/features/admin/variant_overrides_spec.rb @@ -28,13 +28,15 @@ feature %q{ end end - context "when a hub is selected" do + context "when inventory_items exist for variants" do let!(:product) { create(:simple_product, supplier: producer, variant_unit: 'weight', variant_unit_scale: 1) } let!(:variant) { create(:variant, product: product, unit_value: 1, price: 1.23, on_hand: 12) } + let!(:inventory_item) { create(:inventory_item, enterprise: hub, variant: variant ) } let!(:producer_related) { create(:supplier_enterprise) } let!(:product_related) { create(:simple_product, supplier: producer_related) } let!(:variant_related) { create(:variant, product: product_related, unit_value: 2, price: 2.34, on_hand: 23) } + let!(:inventory_item_related) { create(:inventory_item, enterprise: hub, variant: variant_related ) } let!(:er2) { create(:enterprise_relationship, parent: producer_related, child: hub, permissions_list: [:create_variant_overrides]) } @@ -47,85 +49,85 @@ feature %q{ variant.option_values.first.destroy end - context "with no overrides" do + context "when a hub is selected" do before do visit '/admin/variant_overrides' select2_select hub.name, from: 'hub_id' end - it "displays the list of products with variants" do - page.should have_table_row ['PRODUCER', 'PRODUCT', 'PRICE', 'ON HAND'] - page.should have_table_row [producer.name, product.name, '', ''] - page.should have_input "variant-overrides-#{variant.id}-price", placeholder: '1.23' - page.should have_input "variant-overrides-#{variant.id}-count_on_hand", placeholder: '12' + context "with no overrides" do + it "displays the list of products with variants" do + page.should have_table_row ['PRODUCER', 'PRODUCT', 'PRICE', 'ON HAND'] + page.should have_table_row [producer.name, product.name, '', ''] + page.should have_input "variant-overrides-#{variant.id}-price", placeholder: '1.23' + page.should have_input "variant-overrides-#{variant.id}-count_on_hand", placeholder: '12' - page.should have_table_row [producer_related.name, product_related.name, '', ''] - page.should have_input "variant-overrides-#{variant_related.id}-price", placeholder: '2.34' - page.should have_input "variant-overrides-#{variant_related.id}-count_on_hand", placeholder: '23' + page.should have_table_row [producer_related.name, product_related.name, '', ''] + page.should have_input "variant-overrides-#{variant_related.id}-price", placeholder: '2.34' + page.should have_input "variant-overrides-#{variant_related.id}-count_on_hand", placeholder: '23' - # filters the products to those the hub can override - page.should_not have_content producer_unrelated.name - page.should_not have_content product_unrelated.name + # filters the products to those the hub can override + page.should_not have_content producer_unrelated.name + page.should_not have_content product_unrelated.name - # Filters based on the producer select filter - expect(page).to have_selector "#v_#{variant.id}" - expect(page).to have_selector "#v_#{variant_related.id}" - select2_select producer.name, from: 'producer_filter' - expect(page).to have_selector "#v_#{variant.id}" - expect(page).to_not have_selector "#v_#{variant_related.id}" - select2_select 'All', from: 'producer_filter' + # Filters based on the producer select filter + expect(page).to have_selector "#v_#{variant.id}" + expect(page).to have_selector "#v_#{variant_related.id}" + select2_select producer.name, from: 'producer_filter' + expect(page).to have_selector "#v_#{variant.id}" + expect(page).to_not have_selector "#v_#{variant_related.id}" + select2_select 'All', from: 'producer_filter' - # Filters based on the quick search box - expect(page).to have_selector "#v_#{variant.id}" - expect(page).to have_selector "#v_#{variant_related.id}" - fill_in 'query', with: product.name - expect(page).to have_selector "#v_#{variant.id}" - expect(page).to_not have_selector "#v_#{variant_related.id}" - fill_in 'query', with: '' + # Filters based on the quick search box + expect(page).to have_selector "#v_#{variant.id}" + expect(page).to have_selector "#v_#{variant_related.id}" + fill_in 'query', with: product.name + expect(page).to have_selector "#v_#{variant.id}" + expect(page).to_not have_selector "#v_#{variant_related.id}" + fill_in 'query', with: '' - # Clears the filters - expect(page).to have_selector "#v_#{variant.id}" - expect(page).to have_selector "#v_#{variant_related.id}" - select2_select producer.name, from: 'producer_filter' - fill_in 'query', with: product_related.name - expect(page).to_not have_selector "#v_#{variant.id}" - expect(page).to_not have_selector "#v_#{variant_related.id}" - click_button 'Clear All' - expect(page).to have_selector "#v_#{variant.id}" - expect(page).to have_selector "#v_#{variant_related.id}" - end + # Clears the filters + expect(page).to have_selector "tr#v_#{variant.id}" + expect(page).to have_selector "tr#v_#{variant_related.id}" + select2_select producer.name, from: 'producer_filter' + fill_in 'query', with: product_related.name + expect(page).to_not have_selector "tr#v_#{variant.id}" + expect(page).to_not have_selector "tr#v_#{variant_related.id}" + click_button 'Clear All' + expect(page).to have_selector "tr#v_#{variant.id}" + expect(page).to have_selector "tr#v_#{variant_related.id}" - it "creates new overrides" do - first("div#columns-dropdown", :text => "COLUMNS").click - first("div#columns-dropdown div.menu div.menu_item", text: "SKU").click - first("div#columns-dropdown div.menu div.menu_item", text: "On Demand").click - first("div#columns-dropdown", :text => "COLUMNS").click + # Show/Hide products + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "Show/Hide").click + first("div#columns-dropdown", :text => "COLUMNS").click + expect(page).to have_selector "tr#v_#{variant.id}" + expect(page).to have_selector "tr#v_#{variant_related.id}" + within "tr#v_#{variant.id}" do click_button 'Hide' end + expect(page).to_not have_selector "tr#v_#{variant.id}" + expect(page).to have_selector "tr#v_#{variant_related.id}" + click_button 'Show Hidden' + expect(page).to have_selector "tr#v_#{variant.id}" + expect(page).to have_selector "tr#v_#{variant_related.id}" + within "tr#v_#{variant.id}" do click_button 'Show' end + within "tr#v_#{variant_related.id}" do click_button 'Hide' end + expect(page).to have_selector "tr#v_#{variant.id}" + expect(page).to have_selector "tr#v_#{variant_related.id}" + click_button 'Hide Hidden' + expect(page).to have_selector "tr#v_#{variant.id}" + expect(page).to_not have_selector "tr#v_#{variant_related.id}" + end - fill_in "variant-overrides-#{variant.id}-sku", with: 'NEWSKU' - fill_in "variant-overrides-#{variant.id}-price", with: '777.77' - fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '123' - check "variant-overrides-#{variant.id}-on_demand" - page.should have_content "Changes to one override remain unsaved." + it "creates new overrides" do + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "SKU").click + first("div#columns-dropdown div.menu div.menu_item", text: "On Demand").click + first("div#columns-dropdown", :text => "COLUMNS").click - expect do - click_button 'Save Changes' - page.should have_content "Changes saved." - end.to change(VariantOverride, :count).by(1) - - vo = VariantOverride.last - vo.variant_id.should == variant.id - vo.hub_id.should == hub.id - vo.sku.should == "NEWSKU" - vo.price.should == 777.77 - vo.count_on_hand.should == 123 - vo.on_demand.should == true - end - - describe "creating and then updating the new override" do - it "updates the same override instead of creating a duplicate" do - # When I create a new override + fill_in "variant-overrides-#{variant.id}-sku", with: 'NEWSKU' fill_in "variant-overrides-#{variant.id}-price", with: '777.77' fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '123' + check "variant-overrides-#{variant.id}-on_demand" page.should have_content "Changes to one override remain unsaved." expect do @@ -133,137 +135,190 @@ feature %q{ page.should have_content "Changes saved." end.to change(VariantOverride, :count).by(1) - # And I update its settings without reloading the page - fill_in "variant-overrides-#{variant.id}-price", with: '111.11' - fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '111' + vo = VariantOverride.last + vo.variant_id.should == variant.id + vo.hub_id.should == hub.id + vo.sku.should == "NEWSKU" + vo.price.should == 777.77 + vo.count_on_hand.should == 123 + vo.on_demand.should == true + end + + describe "creating and then updating the new override" do + it "updates the same override instead of creating a duplicate" do + # When I create a new override + fill_in "variant-overrides-#{variant.id}-price", with: '777.77' + fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '123' + page.should have_content "Changes to one override remain unsaved." + + expect do + click_button 'Save Changes' + page.should have_content "Changes saved." + end.to change(VariantOverride, :count).by(1) + + # And I update its settings without reloading the page + fill_in "variant-overrides-#{variant.id}-price", with: '111.11' + fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '111' + page.should have_content "Changes to one override remain unsaved." + + # Then I shouldn't see a new override + expect do + click_button 'Save Changes' + page.should have_content "Changes saved." + end.to change(VariantOverride, :count).by(0) + + # And the override should be updated + vo = VariantOverride.last + vo.variant_id.should == variant.id + vo.hub_id.should == hub.id + vo.price.should == 111.11 + vo.count_on_hand.should == 111 + end + end + + it "displays an error when unauthorised to access the page" do + fill_in "variant-overrides-#{variant.id}-price", with: '777.77' + fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '123' + page.should have_content "Changes to one override remain unsaved." + + user.enterprises.clear + + expect do + click_button 'Save Changes' + page.should have_content "I couldn't get authorisation to save those changes, so they remain unsaved." + end.to change(VariantOverride, :count).by(0) + end + + it "displays an error when unauthorised to update a particular override" do + fill_in "variant-overrides-#{variant_related.id}-price", with: '777.77' + fill_in "variant-overrides-#{variant_related.id}-count_on_hand", with: '123' + page.should have_content "Changes to one override remain unsaved." + + er2.destroy + + expect do + click_button 'Save Changes' + page.should have_content "I couldn't get authorisation to save those changes, so they remain unsaved." + end.to change(VariantOverride, :count).by(0) + end + end + + context "with overrides" do + let!(:vo) { create(:variant_override, variant: variant, hub: hub, price: 77.77, count_on_hand: 11111, default_stock: 1000, resettable: true) } + let!(:vo_no_auth) { create(:variant_override, variant: variant, hub: hub3, price: 1, count_on_hand: 2) } + let!(:product2) { create(:simple_product, supplier: producer, variant_unit: 'weight', variant_unit_scale: 1) } + let!(:variant2) { create(:variant, product: product2, unit_value: 8, price: 1.00, on_hand: 12) } + let!(:inventory_item2) { create(:inventory_item, enterprise: hub, variant: variant2) } + let!(:vo_no_reset) { create(:variant_override, variant: variant2, hub: hub, price: 3.99, count_on_hand: 40, default_stock: 100, resettable: false) } + let!(:variant3) { create(:variant, product: product, unit_value: 2, price: 5.00, on_hand: 6) } + let!(:vo3) { create(:variant_override, variant: variant3, hub: hub, price: 6, count_on_hand: 7, sku: "SOMESKU", default_stock: 100, resettable: false) } + let!(:inventory_item3) { create(:inventory_item, enterprise: hub, variant: variant3) } + + before do + visit '/admin/variant_overrides' + select2_select hub.name, from: 'hub_id' + end + + it "product values are affected by overrides" do + page.should have_input "variant-overrides-#{variant.id}-price", with: '77.77', placeholder: '1.23' + page.should have_input "variant-overrides-#{variant.id}-count_on_hand", with: '11111', placeholder: '12' + end + + it "updates existing overrides" do + fill_in "variant-overrides-#{variant.id}-price", with: '22.22' + fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '8888' page.should have_content "Changes to one override remain unsaved." - # Then I shouldn't see a new override expect do click_button 'Save Changes' page.should have_content "Changes saved." end.to change(VariantOverride, :count).by(0) - # And the override should be updated - vo = VariantOverride.last + vo.reload vo.variant_id.should == variant.id vo.hub_id.should == hub.id - vo.price.should == 111.11 - vo.count_on_hand.should == 111 + vo.price.should == 22.22 + vo.count_on_hand.should == 8888 + end + + # Any new fields added to the VO model need to be added to this test + it "deletes overrides when values are cleared" do + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "On Demand").click + first("div#columns-dropdown div.menu div.menu_item", text: "Reset Stock Level").click + first("div#columns-dropdown", :text => "COLUMNS").click + + # Clearing values by 'inheriting' + first("div#columns-dropdown", :text => "COLUMNS").click + first("div#columns-dropdown div.menu div.menu_item", text: "Inheritance").click + first("div#columns-dropdown", :text => "COLUMNS").click + check "variant-overrides-#{variant3.id}-inherit" + + # Clearing values manually + fill_in "variant-overrides-#{variant.id}-price", with: '' + fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '' + fill_in "variant-overrides-#{variant.id}-default_stock", with: '' + page.uncheck "variant-overrides-#{variant.id}-resettable" + page.should have_content "Changes to 2 overrides remain unsaved." + + expect do + click_button 'Save Changes' + page.should have_content "Changes saved." + end.to change(VariantOverride, :count).by(-2) + + VariantOverride.where(id: vo.id).should be_empty + VariantOverride.where(id: vo3.id).should be_empty + end + + it "resets stock to defaults" do + click_button 'Reset Stock to Defaults' + page.should have_content 'Stocks reset to defaults.' + vo.reload + page.should have_input "variant-overrides-#{variant.id}-count_on_hand", with: '1000', placeholder: '12' + vo.count_on_hand.should == 1000 + end + + it "doesn't reset stock levels if the behaviour is disabled" do + click_button 'Reset Stock to Defaults' + vo_no_reset.reload + page.should have_input "variant-overrides-#{variant2.id}-count_on_hand", with: '40', placeholder: '12' + vo_no_reset.count_on_hand.should == 40 + end + + it "prompts to save changes before reset if any are pending" do + fill_in "variant-overrides-#{variant.id}-price", with: '200' + click_button 'Reset Stock to Defaults' + page.should have_content "Save changes first" end end - - it "displays an error when unauthorised to access the page" do - fill_in "variant-overrides-#{variant.id}-price", with: '777.77' - fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '123' - page.should have_content "Changes to one override remain unsaved." - - user.enterprises.clear - - expect do - click_button 'Save Changes' - page.should have_content "I couldn't get authorisation to save those changes, so they remain unsaved." - end.to change(VariantOverride, :count).by(0) - end - - it "displays an error when unauthorised to update a particular override" do - fill_in "variant-overrides-#{variant_related.id}-price", with: '777.77' - fill_in "variant-overrides-#{variant_related.id}-count_on_hand", with: '123' - page.should have_content "Changes to one override remain unsaved." - - er2.destroy - - expect do - click_button 'Save Changes' - page.should have_content "I couldn't get authorisation to save those changes, so they remain unsaved." - end.to change(VariantOverride, :count).by(0) - end end + end - context "with overrides" do - let!(:vo) { create(:variant_override, variant: variant, hub: hub, price: 77.77, count_on_hand: 11111, default_stock: 1000, resettable: true) } - let!(:vo_no_auth) { create(:variant_override, variant: variant, hub: hub3, price: 1, count_on_hand: 2) } - let!(:product2) { create(:simple_product, supplier: producer, variant_unit: 'weight', variant_unit_scale: 1) } - let!(:variant2) { create(:variant, product: product2, unit_value: 8, price: 1.00, on_hand: 12) } - let!(:vo_no_reset) { create(:variant_override, variant: variant2, hub: hub, price: 3.99, count_on_hand: 40, default_stock: 100, resettable: false) } - let!(:variant3) { create(:variant, product: product, unit_value: 2, price: 5.00, on_hand: 6) } - let!(:vo3) { create(:variant_override, variant: variant3, hub: hub, price: 6, count_on_hand: 7, sku: "SOMESKU", default_stock: 100, resettable: false) } + describe "when inventory_items do not exist for variants" do + let!(:product) { create(:simple_product, supplier: producer, variant_unit: 'weight', variant_unit_scale: 1) } + let!(:variant1) { create(:variant, product: product, unit_value: 1, price: 1.23, on_hand: 12) } + let!(:variant2) { create(:variant, product: product, unit_value: 2, price: 4.56, on_hand: 3) } + context "when a hub is selected" do before do visit '/admin/variant_overrides' select2_select hub.name, from: 'hub_id' end - it "product values are affected by overrides" do - page.should have_input "variant-overrides-#{variant.id}-price", with: '77.77', placeholder: '1.23' - page.should have_input "variant-overrides-#{variant.id}-count_on_hand", with: '11111', placeholder: '12' - end + it "shows new variants, and allows them to be added or ignored" do + expect(page).to_not have_selector "table#variant-overrides tr#v_#{variant1.id}" + expect(page).to_not have_selector "table#variant-overrides tr#v_#{variant2.id}" - it "updates existing overrides" do - fill_in "variant-overrides-#{variant.id}-price", with: '22.22' - fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '8888' - page.should have_content "Changes to one override remain unsaved." + expect(page).to have_table_row ['PRODUCER', 'PRODUCT', 'VARIANT', 'ADD', 'IGNORE'] + expect(page).to have_selector "table#new-variants tr#nv_#{variant1.id}" + expect(page).to have_selector "table#new-variants tr#nv_#{variant2.id}" + within "table#new-variants tr#nv_#{variant1.id}" do click_button 'Add' end + within "table#new-variants tr#nv_#{variant2.id}" do click_button 'Ignore' end + expect(page).to_not have_selector "table#new-variants tr#nv_#{variant1.id}" + expect(page).to_not have_selector "table#new-variants tr#nv_#{variant2.id}" - expect do - click_button 'Save Changes' - page.should have_content "Changes saved." - end.to change(VariantOverride, :count).by(0) - - vo.reload - vo.variant_id.should == variant.id - vo.hub_id.should == hub.id - vo.price.should == 22.22 - vo.count_on_hand.should == 8888 - end - - # Any new fields added to the VO model need to be added to this test - it "deletes overrides when values are cleared" do - first("div#columns-dropdown", :text => "COLUMNS").click - first("div#columns-dropdown div.menu div.menu_item", text: "On Demand").click - first("div#columns-dropdown div.menu div.menu_item", text: "Reset Stock Level").click - first("div#columns-dropdown", :text => "COLUMNS").click - - # Clearing values manually - fill_in "variant-overrides-#{variant.id}-price", with: '' - fill_in "variant-overrides-#{variant.id}-count_on_hand", with: '' - fill_in "variant-overrides-#{variant.id}-default_stock", with: '' - page.uncheck "variant-overrides-#{variant.id}-resettable" - page.should have_content "Changes to one override remain unsaved." - - # Clearing values by 'inheriting' - first("div#columns-dropdown", :text => "COLUMNS").click - first("div#columns-dropdown div.menu div.menu_item", text: "Inheritance").click - first("div#columns-dropdown", :text => "COLUMNS").click - page.check "variant-overrides-#{variant3.id}-inherit" - - expect do - click_button 'Save Changes' - page.should have_content "Changes saved." - end.to change(VariantOverride, :count).by(-2) - - VariantOverride.where(id: vo.id).should be_empty - VariantOverride.where(id: vo3.id).should be_empty - end - - it "resets stock to defaults" do - click_button 'Reset Stock to Defaults' - page.should have_content 'Stocks reset to defaults.' - vo.reload - page.should have_input "variant-overrides-#{variant.id}-count_on_hand", with: '1000', placeholder: '12' - vo.count_on_hand.should == 1000 - end - - it "doesn't reset stock levels if the behaviour is disabled" do - click_button 'Reset Stock to Defaults' - vo_no_reset.reload - page.should have_input "variant-overrides-#{variant2.id}-count_on_hand", with: '40', placeholder: '12' - vo_no_reset.count_on_hand.should == 40 - end - - it "prompts to save changes before reset if any are pending" do - fill_in "variant-overrides-#{variant.id}-price", with: '200' - click_button 'Reset Stock to Defaults' - page.should have_content "Save changes first" + expect(page).to have_selector "table#variant-overrides tr#v_#{variant1.id}" + expect(page).to_not have_selector "table#variant-overrides tr#v_#{variant2.id}" end end end diff --git a/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee b/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee index 699e1bc4ab..75e6102863 100644 --- a/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee @@ -9,6 +9,7 @@ describe "VariantOverridesCtrl", -> variantOverrides = {} DirtyVariantOverrides = null dirtyVariantOverrides = {} + inventoryItems = {} StatusMessage = null statusMessage = {} @@ -18,6 +19,7 @@ describe "VariantOverridesCtrl", -> $provide.value 'SpreeApiKey', 'API_KEY' $provide.value 'variantOverrides', variantOverrides $provide.value 'dirtyVariantOverrides', dirtyVariantOverrides + $provide.value 'inventoryItems', inventoryItems null inject ($controller, _VariantOverrides_, _DirtyVariantOverrides_, _StatusMessage_) -> diff --git a/spec/javascripts/unit/admin/inventory_items/services/inventory_items_spec.js.coffee b/spec/javascripts/unit/admin/inventory_items/services/inventory_items_spec.js.coffee new file mode 100644 index 0000000000..49ea827900 --- /dev/null +++ b/spec/javascripts/unit/admin/inventory_items/services/inventory_items_spec.js.coffee @@ -0,0 +1,73 @@ +describe "InventoryItems service", -> + InventoryItems = InventoryItemResource = inventoryItems = $httpBackend = null + inventoryItems = {} + + beforeEach -> + module 'admin.inventoryItems' + module ($provide) -> + $provide.value 'inventoryItems', inventoryItems + null + + this.addMatchers + toDeepEqual: (expected) -> + return angular.equals(this.actual, expected) + + inject ($q, _$httpBackend_, _InventoryItems_, _InventoryItemResource_) -> + InventoryItems = _InventoryItems_ + InventoryItemResource = _InventoryItemResource_ + $httpBackend = _$httpBackend_ + + + describe "#setVisiblity", -> + describe "on an inventory item that already exists", -> + existing = null + + beforeEach -> + existing = new InventoryItemResource({ id: 1, enterprise_id: 2, variant_id: 3, visible: true }) + InventoryItems.inventoryItems[2] = {} + InventoryItems.inventoryItems[2][3] = existing + + describe "success", -> + beforeEach -> + $httpBackend.expectPUT('/admin/inventory_items/1.json', { id: 1, enterprise_id: 2, variant_id: 3, visible: false } ) + .respond 200, { id: 1, enterprise_id: 2, variant_id: 3, visible: false } + InventoryItems.setVisibility(2,3,false) + + it "saves the new visible value AFTER the request responds successfully", -> + expect(InventoryItems.inventoryItems[2][3].visible).toBe true + $httpBackend.flush() + expect(InventoryItems.inventoryItems[2][3].visible).toBe false + + describe "failure", -> + beforeEach -> + $httpBackend.expectPUT('/admin/inventory_items/1.json',{ id: 1, enterprise_id: 2, variant_id: 3, visible: null }) + .respond 422, { errors: ["Visible must be true or false"] } + InventoryItems.setVisibility(2,3,null) + + it "store the errors in the errors object", -> + expect(InventoryItems.errors).toEqual {} + $httpBackend.flush() + expect(InventoryItems.errors[2][3]).toEqual ["Visible must be true or false"] + + describe "on an inventory item that does not exist", -> + describe "success", -> + beforeEach -> + $httpBackend.expectPOST('/admin/inventory_items.json', { enterprise_id: 5, variant_id: 6, visible: false } ) + .respond 200, { id: 1, enterprise_id: 2, variant_id: 3, visible: false } + InventoryItems.setVisibility(5,6,false) + + it "saves the new visible value AFTER the request responds successfully", -> + expect(InventoryItems.inventoryItems).toEqual {} + $httpBackend.flush() + expect(InventoryItems.inventoryItems[5][6].visible).toBe false + + describe "failure", -> + beforeEach -> + $httpBackend.expectPOST('/admin/inventory_items.json',{ enterprise_id: 5, variant_id: 6, visible: null }) + .respond 422, { errors: ["Visible must be true or false"] } + InventoryItems.setVisibility(5,6,null) + + it "store the errors in the errors object", -> + expect(InventoryItems.errors).toEqual {} + $httpBackend.flush() + expect(InventoryItems.errors[5][6]).toEqual ["Visible must be true or false"]