From 2a5f598fb02cf322c5e0599eeacf13d10cbe2fb9 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 24 Jun 2016 10:37:19 +1000 Subject: [PATCH] Angularising Order Cycles Index --- .../directives/datetime_picker.js.coffee | 2 +- .../line_items_controller.js.coffee | 4 +- .../order_cycles_controller.js.coffee | 34 +++++++ .../order_cycles/order_cycles.js.erb.coffee | 37 ++++--- .../resources/order_cycle_resource.js.coffee | 5 + .../resources/services/order_cycles.js.coffee | 43 +++++++- .../admin/enterprises_controller.rb | 8 +- .../admin/order_cycles_controller.rb | 20 ++-- app/helpers/order_cycles_helper.rb | 4 +- app/models/spree/ability_decorator.rb | 2 +- .../api/admin/index_order_cycle_serializer.rb | 65 ++++++++++++ .../admin/order_cycles/_header.html.haml | 33 +++++++ .../order_cycles/_loading_flash.html.haml | 4 + app/views/admin/order_cycles/_row.html.haml | 65 ++++++------ .../admin/order_cycles/_show_more.html.haml | 4 + app/views/admin/order_cycles/index.html.haml | 53 ++-------- config/locales/en.yml | 6 ++ config/routes.rb | 2 +- .../admin/enterprises_controller_spec.rb | 16 +-- .../admin/order_cycles_controller_spec.rb | 43 +++++--- spec/features/admin/order_cycles_spec.rb | 98 +++++++++++-------- .../line_items_controller_spec.js.coffee | 4 +- .../order_cycles_controller_spec.js.coffee | 60 ++++++++++++ .../services/order_cycles_spec.js.coffee | 7 +- 24 files changed, 437 insertions(+), 182 deletions(-) create mode 100644 app/assets/javascripts/admin/order_cycles/controllers/order_cycles_controller.js.coffee create mode 100644 app/serializers/api/admin/index_order_cycle_serializer.rb create mode 100644 app/views/admin/order_cycles/_header.html.haml create mode 100644 app/views/admin/order_cycles/_loading_flash.html.haml create mode 100644 app/views/admin/order_cycles/_show_more.html.haml create mode 100644 spec/javascripts/unit/admin/order_cycles/controllers/order_cycles_controller_spec.js.coffee diff --git a/app/assets/javascripts/admin/directives/datetime_picker.js.coffee b/app/assets/javascripts/admin/directives/datetime_picker.js.coffee index b0d91e538a..0d9728701f 100644 --- a/app/assets/javascripts/admin/directives/datetime_picker.js.coffee +++ b/app/assets/javascripts/admin/directives/datetime_picker.js.coffee @@ -8,4 +8,4 @@ angular.module("ofn.admin").directive "datetimepicker", -> onSelect: (dateText, inst) -> scope.$apply (scope) -> # Fires ngModel.$parsers - ngModel.$setViewValue dateText \ No newline at end of file + ngModel.$setViewValue dateText diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index 53b429dcde..267879e60a 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -29,9 +29,9 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, RequestMonitor.load $scope.lineItems = LineItems.index("q[order][state_not_eq]": "canceled", "q[order][completed_at_not_null]": "true", "q[order][completed_at_gt]": "#{parseDate($scope.startDate)}", "q[order][completed_at_lt]": "#{parseDate($scope.endDate)}") unless $scope.initialized - RequestMonitor.load $scope.distributors = Enterprises.index(action: "for_line_items", ams_prefix: "basic", "q[sells_in][]": ["own", "any"]) + RequestMonitor.load $scope.distributors = Enterprises.index(action: "visible", ams_prefix: "basic", "q[sells_in][]": ["own", "any"]) RequestMonitor.load $scope.orderCycles = OrderCycles.index(ams_prefix: "basic", as: "distributor", "q[orders_close_at_gt]": "#{daysFromToday(-90)}") - RequestMonitor.load $scope.suppliers = Enterprises.index(action: "for_line_items", ams_prefix: "basic", "q[is_primary_producer_eq]": "true") + RequestMonitor.load $scope.suppliers = Enterprises.index(action: "visible", ams_prefix: "basic", "q[is_primary_producer_eq]": "true") RequestMonitor.load $q.all([$scope.orders.$promise, $scope.distributors.$promise, $scope.orderCycles.$promise]).then -> Dereferencer.dereferenceAttr $scope.orders, "distributor", Enterprises.byID diff --git a/app/assets/javascripts/admin/order_cycles/controllers/order_cycles_controller.js.coffee b/app/assets/javascripts/admin/order_cycles/controllers/order_cycles_controller.js.coffee new file mode 100644 index 0000000000..89b619f17d --- /dev/null +++ b/app/assets/javascripts/admin/order_cycles/controllers/order_cycles_controller.js.coffee @@ -0,0 +1,34 @@ +angular.module("admin.orderCycles").controller "OrderCyclesCtrl", ($scope, $q, StatusMessage, RequestMonitor, OrderCycles, Enterprises) -> + $scope.RequestMonitor = RequestMonitor + $scope.saveAll = -> OrderCycles.saveChanges($scope.order_cycles_form) + $scope.ordersCloseAtLimit = -31 # days + + compileDataFor = (orderCycles) -> + for orderCycle in orderCycles + OrderCycles.linkToEnterprises(orderCycle) + orderCycle.producerNames = orderCycle.producers.map((producer) -> producer.name).join(", ") + orderCycle.shopNames = orderCycle.shops.map((shop) -> shop.name).join(", ") + + # NOTE: this is using the Enterprises service from the admin.enterprises module + RequestMonitor.load ($scope.enterprises = Enterprises.index(includeBlank: true, action: "visible", ams_prefix: "basic")).$promise + RequestMonitor.load ($scope.orderCycles = OrderCycles.index(ams_prefix: "index", "q[orders_close_at_gt]": "#{daysFromToday($scope.ordersCloseAtLimit)}")).$promise + RequestMonitor.load $q.all([$scope.enterprises.$promise, $scope.orderCycles.$promise]).then -> compileDataFor($scope.orderCycles) + + $scope.$watch 'order_cycles_form.$dirty', (newVal, oldVal) -> + StatusMessage.display 'notice', "You have unsaved changes" if newVal + + $scope.showMore = (days) -> + $scope.ordersCloseAtLimit -= days + existingIDs = Object.keys(OrderCycles.orderCyclesByID) + RequestMonitor.load (orderCycles = OrderCycles.index(ams_prefix: "index", "q[orders_close_at_gt]": "#{daysFromToday($scope.ordersCloseAtLimit)}", "q[id_not_in][]": existingIDs)).$promise + orderCycles.$promise.then -> + compileDataFor(orderCycles) + $scope.orderCycles.push(orderCycle) for orderCycle in orderCycles + +daysFromToday = (days) -> + now = new Date + now.setHours(0) + now.setMinutes(0) + now.setSeconds(0) + now.setDate( now.getDate() + days ) + now diff --git a/app/assets/javascripts/admin/order_cycles/order_cycles.js.erb.coffee b/app/assets/javascripts/admin/order_cycles/order_cycles.js.erb.coffee index 99dcbfe4b7..d43bcc2ad6 100644 --- a/app/assets/javascripts/admin/order_cycles/order_cycles.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycles/order_cycles.js.erb.coffee @@ -1,23 +1,28 @@ -angular.module('admin.orderCycles', ['admin.utils', 'admin.indexUtils', 'ngTagsInput']) +angular.module('admin.orderCycles', ['admin.utils', 'admin.indexUtils', 'admin.enterprises', 'ngTagsInput']) .config ($httpProvider) -> $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content') - .directive 'datetimepicker', ($parse) -> - (scope, element, attrs) -> - # using $parse instead of scope[attrs.datetimepicker] for cases - # where attrs.datetimepicker is 'foo.bar.lol' - $(element).datetimepicker - dateFormat: 'yy-mm-dd' - timeFormat: 'HH:mm:ss' - showOn: "button" - buttonImage: "<%= asset_path 'datepicker/cal.gif' %>" - buttonImageOnly: true - stepMinute: 15 - onSelect: (dateText, inst) -> - scope.$apply -> - parsed = $parse(attrs.datetimepicker) - parsed.assign(scope, dateText) + .directive 'datetimepicker', ($timeout, $parse) -> + require: "ngModel" + link: (scope, element, attrs, ngModel) -> + $timeout -> + # using $parse instead of scope[attrs.datetimepicker] for cases + # where attrs.datetimepicker is 'foo.bar.lol' + $(element).datetimepicker + dateFormat: 'yy-mm-dd' + timeFormat: 'HH:mm:ss' + showOn: "button" + buttonImage: "<%= asset_path 'datepicker/cal.gif' %>" + buttonImageOnly: true + stepMinute: 15 + onSelect: (dateText, inst) -> + scope.$apply(-> + element.val(dateText) + parsed = $parse(attrs.datetimepicker) + parsed.assign(scope, dateText) + ) + .directive 'ofnOnChange', -> (scope, element, attrs) -> diff --git a/app/assets/javascripts/admin/resources/resources/order_cycle_resource.js.coffee b/app/assets/javascripts/admin/resources/resources/order_cycle_resource.js.coffee index cd5be31acd..770016030a 100644 --- a/app/assets/javascripts/admin/resources/resources/order_cycle_resource.js.coffee +++ b/app/assets/javascripts/admin/resources/resources/order_cycle_resource.js.coffee @@ -5,4 +5,9 @@ angular.module("admin.resources").factory 'OrderCycleResource', ($resource) -> isArray: true 'update': method: 'PUT' + 'bulkUpdate': + method: 'POST' + isArray: true + params: + action: 'bulk_update' }) diff --git a/app/assets/javascripts/admin/resources/services/order_cycles.js.coffee b/app/assets/javascripts/admin/resources/services/order_cycles.js.coffee index 728b173e35..3216969408 100644 --- a/app/assets/javascripts/admin/resources/services/order_cycles.js.coffee +++ b/app/assets/javascripts/admin/resources/services/order_cycles.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.resources").factory 'OrderCycles', ($q, $injector, OrderCycleResource) -> +angular.module("admin.resources").factory 'OrderCycles', ($q, $injector, OrderCycleResource, StatusMessage, Enterprises, blankOption) -> new class OrderCycles all: [] byID: {} @@ -30,14 +30,53 @@ angular.module("admin.resources").factory 'OrderCycles', ($q, $injector, OrderCy deferred.reject(response) deferred.promise + saveChanges: (form) -> + changed = {} + for id, orderCycle of @orderCyclesByID when not @saved(orderCycle) + changed[Object.keys(changed).length] = @changesFor(orderCycle) + if Object.keys(changed).length > 0 + StatusMessage.display('progress', "Saving...") + OrderCycleResource.bulkUpdate { order_cycle_set: { collection_attributes: changed } }, (data) => + for orderCycle in data + angular.extend(@orderCyclesByID[orderCycle.id], orderCycle) + angular.extend(@pristineByID[orderCycle.id], orderCycle) + @linkToEnterprises(orderCycle) + form.$setPristine() if form? + StatusMessage.display('success', "Order cycles have been updated.") + , (response) => + StatusMessage.display('failure', "Oh no! I was unable to save your changes.") + saved: (order_cycle) -> @diff(order_cycle).length == 0 diff: (order_cycle) -> changed = [] for attr, value of order_cycle when not angular.equals(value, @pristineByID[order_cycle.id][attr]) - changed.push attr unless attr is "$$hashKey" + changed.push attr if attr in @attrsToSave() changed + changesFor: (orderCycle) -> + changes = { id: orderCycle.id } + for attr, value of orderCycle when not angular.equals(value, @pristineByID[orderCycle.id][attr]) + changes[attr] = orderCycle[attr] if attr in @attrsToSave() + changes + + attrsToSave: -> + ['orders_open_at','orders_close_at'] + resetAttribute: (order_cycle, attribute) -> order_cycle[attribute] = @pristineByID[order_cycle.id][attribute] + + linkAllToEnterprises: -> + for id, orderCycle of @orderCyclesByID + @linkToEnterprises(orderCycle) + + linkToEnterprises: (orderCycle) -> + coordinator = Enterprises.enterprisesByID[orderCycle.coordinator.id] + orderCycle.coordinator = coordinator if coordinator? + for producer, i in orderCycle.producers + producer = Enterprises.enterprisesByID[producer.id] + orderCycle.producers[i] = producer if producer? + for shop, i in orderCycle.shops + shop = Enterprises.enterprisesByID[shop.id] + orderCycle.shops[i] = shop if shop? diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index cff0227aa8..11dd547042 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -106,10 +106,10 @@ module Admin end end - def for_line_items + def visible respond_to do |format| format.json do - render_as_json @collection, ams_prefix: 'basic', spree_current_user: spree_current_user + render_as_json @collection, ams_prefix: params[:ams_prefix] || 'basic', spree_current_user: spree_current_user end end end @@ -157,7 +157,7 @@ module Admin else Enterprise.where("1=0") end - when :for_line_items + when :visible OpenFoodNetwork::Permissions.new(spree_current_user).visible_enterprises.ransack(params[:q]).result else # TODO was ordered with is_distributor DESC as well, not sure why or how we want to sort this now @@ -168,7 +168,7 @@ module Admin end def collection_actions - [:index, :for_order_cycle, :for_line_items, :bulk_update] + [:index, :for_order_cycle, :visible, :bulk_update] end def load_methods_and_fees diff --git a/app/controllers/admin/order_cycles_controller.rb b/app/controllers/admin/order_cycles_controller.rb index da50617189..963b9195d0 100644 --- a/app/controllers/admin/order_cycles_controller.rb +++ b/app/controllers/admin/order_cycles_controller.rb @@ -76,9 +76,14 @@ module Admin def bulk_update @order_cycle_set = params[:order_cycle_set] && OrderCycleSet.new(params[:order_cycle_set]) if @order_cycle_set.andand.save - redirect_to main_app.admin_order_cycles_path, notice: I18n.t(:order_cycles_bulk_update_notice) + respond_to do |format| + order_cycles = OrderCycle.where(id: params[:order_cycle_set][:collection_attributes].map{ |k,v| v[:id] }) + format.json { render_as_json order_cycles, ams_prefix: 'index', current_user: spree_current_user } + end else - render :index + respond_to do |format| + format.json { render :json => {:success => false} } + end end end @@ -98,6 +103,7 @@ module Admin protected def collection + return Enterprise.where("1=0") unless json_request? ocs = if params[:as] == "distributor" OrderCycle.ransack(params[:q]).result. involving_managed_distributors_of(spree_current_user).order('updated_at DESC') @@ -120,14 +126,14 @@ module Admin private def load_data_for_index - @show_more = !!params[:show_more] - unless @show_more || params[:q].andand[:orders_close_at_gt].present? + if json_request? # Split ransack params into all those that currently exist and new ones to limit returned ocs to recent or undated + orders_close_at_gt = params[:q].andand.delete(:orders_close_at_gt) || 31.days.ago params[:q] = { - g: [ params.delete(:q) || {}, { m: 'or', orders_close_at_gt: 31.days.ago, orders_close_at_null: true } ] + g: [ params.delete(:q) || {}, { m: 'or', orders_close_at_gt: orders_close_at_gt, orders_close_at_null: true } ] } + @order_cycle_set = OrderCycleSet.new :collection => (@collection = collection) end - @order_cycle_set = OrderCycleSet.new :collection => (@collection = collection) end def require_coordinator @@ -177,7 +183,7 @@ module Admin end def ams_prefix_whitelist - [:basic] + [:basic, :index] end end end diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index 3f32830fbb..8afca2b949 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -58,8 +58,8 @@ module OrderCyclesHelper OrderCycle.active.with_distributor(@distributor).present? end - def order_cycles_simple_index - @order_cycles_simple_index ||= !OpenFoodNetwork::Permissions.new(spree_current_user).can_manage_complex_order_cycles? + def simple_index + @simple_index ||= !OpenFoodNetwork::Permissions.new(spree_current_user).can_manage_complex_order_cycles? end def order_cycles_simple_form diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index c08bd1f654..cbcf086682 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -201,7 +201,7 @@ class AbilityDecorator order.distributor.nil? || user.enterprises.include?(order.distributor) || order.order_cycle.andand.coordinated_by?(user) end can [:admin, :bulk_management, :managed], Spree::Order if user.admin? || user.enterprises.any?(&:is_distributor) - can [:admin , :for_line_items], Enterprise + can [:admin, :visible], Enterprise can [:admin, :index, :create, :update, :destroy], :line_item can [:admin, :index, :create], Spree::LineItem can [:destroy, :update], Spree::LineItem do |item| diff --git a/app/serializers/api/admin/index_order_cycle_serializer.rb b/app/serializers/api/admin/index_order_cycle_serializer.rb new file mode 100644 index 0000000000..876c3d131b --- /dev/null +++ b/app/serializers/api/admin/index_order_cycle_serializer.rb @@ -0,0 +1,65 @@ +require 'open_food_network/order_cycle_permissions' + +class Api::Admin::IndexOrderCycleSerializer < ActiveModel::Serializer + include OrderCyclesHelper + + attributes :id, :name, :orders_open_at, :orders_close_at, :status, :variant_count, :deletable + attributes :coordinator, :producers, :shops, :viewing_as_coordinator + attributes :edit_path, :clone_path, :delete_path + + def deletable + can_delete?(object) + end + + def variant_count + object.variants.count + end + + def status + order_cycle_status_class object + end + + def orders_open_at + object.orders_open_at.to_s + end + + def orders_close_at + object.orders_close_at.to_s + end + + def viewing_as_coordinator + Enterprise.managed_by(options[:current_user]).include? object.coordinator + end + + def coordinator + Api::Admin::IdNameSerializer.new(object.coordinator).serializable_hash + end + + def producers + producers = object.suppliers.merge(visible_enterprises) + ActiveModel::ArraySerializer.new(producers, {each_serializer: Api::Admin::IdNameSerializer}) + end + + def shops + shops = object.distributors.merge(visible_enterprises) + ActiveModel::ArraySerializer.new(shops, {each_serializer: Api::Admin::IdNameSerializer}) + end + + def edit_path + edit_admin_order_cycle_path(object) + end + + def clone_path + clone_admin_order_cycle_path(object) + end + + def delete_path + admin_order_cycle_path(object) + end + + private + + def visible_enterprises + @visible_enterprises ||= OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object).visible_enterprises + end +end diff --git a/app/views/admin/order_cycles/_header.html.haml b/app/views/admin/order_cycles/_header.html.haml new file mode 100644 index 0000000000..972fa1f49e --- /dev/null +++ b/app/views/admin/order_cycles/_header.html.haml @@ -0,0 +1,33 @@ +%colgroup + %col + %col{'style' => 'width: 20%;'} + %col{'style' => 'width: 20%;'} + - unless simple_index + %col + %col + %col + %col + %col + %col + %col + +%thead + %tr + %th + =t :name + %th + =t :open + %th + =t :close + - unless simple_index + %th + =t :label_producers + %th + =t :coordinator + %th + =t :label_shops + %th + =t :products + %th.actions + %th.actions + %th.actions diff --git a/app/views/admin/order_cycles/_loading_flash.html.haml b/app/views/admin/order_cycles/_loading_flash.html.haml new file mode 100644 index 0000000000..c579a294af --- /dev/null +++ b/app/views/admin/order_cycles/_loading_flash.html.haml @@ -0,0 +1,4 @@ +%div.sixteen.columns.alpha.omega#loading{ ng: { cloak: true, if: 'RequestMonitor.loading' } } + %img.spinner{ src: "/assets/spinning-circles.svg" } + %h1{ ng: { hide: 'orderCycles.length > 0' } } LOADING ORDER CYCLES + %h1{ ng: { show: 'orderCycles.length > 0' } } LOADING... diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index cbfde7f546..610faa4407 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -1,39 +1,36 @@ -- order_cycle = order_cycle_form.object -- klass = "order-cycle-#{order_cycle.id} #{order_cycle_status_class order_cycle}" +%tr{ class: "order-cycle-{{orderCycle.id}} {{orderCycle.status}}", ng: { repeat: 'orderCycle in orderCycles track by orderCycle.id' } } + %td + %a{ ng: { href: '{{orderCycle.edit_path}}' } } + {{ orderCycle.name }} + %td + %input.datetimepicker{ id: 'oc{{::orderCycle.id}}_orders_open_at', name: 'oc{{::orderCycle.id}}[orders_open_at]', type: 'text', ng: { if: 'orderCycle.viewing_as_coordinator', model: 'orderCycle.orders_open_at' }, datetimepicker: 'orderCycle.orders_open_at' } + %input{ id: 'oc{{::orderCycle.id}}_orders_open_at', name: 'oc{{::orderCycle.id}}[orders_open_at]', type: 'text', ng: { if: '!orderCycle.viewing_as_coordinator', model: 'orderCycle.orders_open_at'}, disabled: true } + %td + %input.datetimepicker{ id: 'oc{{::orderCycle.id}}_orders_close_at', name: 'oc{{::orderCycle.id}}[orders_close_at]', type: 'text', ng: { if: 'orderCycle.viewing_as_coordinator', model: 'orderCycle.orders_close_at' }, datetimepicker: 'orderCycle.orders_close_at' } + %input{ id: 'oc{{::orderCycle.id}}_orders_close_at', name: 'oc{{::orderCycle.id}}[orders_close_at]', type: 'text', ng: { if: '!orderCycle.viewing_as_coordinator', model: 'orderCycle.orders_close_at'}, disabled: true } -%tr{class: klass} - %td= link_to order_cycle.name, main_app.edit_admin_order_cycle_path(order_cycle) - %td= order_cycle_form.text_field :orders_open_at, :class => "#{viewing_as_coordinator_of?(order_cycle) ? 'datetimepicker' : ''}", :value => order_cycle.orders_open_at, :disabled => !viewing_as_coordinator_of?(order_cycle) - %td= order_cycle_form.text_field :orders_close_at, :class => "#{viewing_as_coordinator_of?(order_cycle) ? 'datetimepicker' : ''}", :value => order_cycle.orders_close_at, :disabled => !viewing_as_coordinator_of?(order_cycle) - - - unless order_cycles_simple_index - %td.suppliers - - suppliers = order_cycle.suppliers.merge(OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).visible_enterprises) - - supplier_list = suppliers.map(&:name).sort.join ', ' - - if suppliers.count > 3 - %span{'ofn-with-tip' => supplier_list} - = suppliers.count - = t('.suppliers') - - else - = supplier_list - %td= order_cycle.coordinator.name - %td.distributors - - distributors = order_cycle.distributors.merge(OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, order_cycle).visible_enterprises) - - distributor_list = distributors.map(&:name).sort.join ', ' - - if distributors.count > 3 - %span{'ofn-with-tip' => distributor_list} - = distributors.count - = t('.distributors') - - else - = distributor_list + - unless simple_index + %td.producers + %span{'ofn-with-tip' => '{{ orderCycle.producerNames }}', ng: { show: 'orderCycle.producers.length > 3' } } + {{ orderCycle.producers.length }} + = t('.suppliers') + %span{ ng: { hide: 'orderCycle.producers.length > 3', bind: 'orderCycle.producerNames' } } + %td.coordinator + {{ orderCycle.coordinator.name }} + %td.shops + %span{'ofn-with-tip' => '{{ orderCycle.shopNames }}', ng: { show: 'orderCycle.shops.length > 3' } } + {{ orderCycle.shops.length }} + = t('.distributors') + %span{ ng: { hide: 'orderCycle.shops.length > 3', bind: 'orderCycle.shopNames' } } %td.products - %span= "#{order_cycle.variants.count} #{t('.variants')}" + %span + {{orderCycle.variant_count}} + = t('.variants') %td.actions - = link_to '', main_app.edit_admin_order_cycle_path(order_cycle), class: 'edit-order-cycle icon-edit no-text' - %td.actions - = link_to '', main_app.clone_admin_order_cycle_path(order_cycle), class: 'clone-order-cycle icon-copy no-text' - - if can_delete?(order_cycle) - %td.actions - = link_to '', main_app.admin_order_cycle_path(order_cycle), class: 'delete-order-cycle icon-trash no-text', :method => :delete, data: { confirm: t(:are_you_sure) } + %a.edit-order-cycle.icon-edit.no-text{ ng: { href: '{{orderCycle.edit_path}}'} } + %td.actions{ ng: { if: 'orderCycle.viewing_as_coordinator' } } + %a.clone-order-cycle.icon-copy.no-text{ ng: { href: '{{orderCycle.clone_path}}'} } + %td.actions{ ng: { if: 'orderCycle.deletable && orderCycle.viewing_as_coordinator' } } + %a.delete-order-cycle.icon-trash.no-text{ ng: { href: '{{orderCycle.delete_path}}'}, data: { method: 'delete', confirm: t(:are_you_sure) } } diff --git a/app/views/admin/order_cycles/_show_more.html.haml b/app/views/admin/order_cycles/_show_more.html.haml new file mode 100644 index 0000000000..986c80bbc3 --- /dev/null +++ b/app/views/admin/order_cycles/_show_more.html.haml @@ -0,0 +1,4 @@ +.text-center.margin-bottom-50{ ng: { hide: "RequestMonitor.loading" } } + %input{ type: 'button', value: "#{t('admin.show_n_more', num: '30')} #{t(:days)}", ng: { click: 'showMore(30)' } } + or + %input{ type: 'button', value: "#{t('admin.show_n_more', num: '90')} #{t(:days)}", ng: { click: 'showMore(90)' } } diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index 989983b5b0..d8fa8ecd0b 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -4,51 +4,14 @@ = content_for :page_actions do %li#new_order_cycle_link = button_link_to t(:new_order_cycle), main_app.new_admin_order_cycle_path, icon: 'icon-plus', id: 'admin_new_order_cycle_link' - - if @show_more - %li - = button_link_to t(:label_less), main_app.admin_order_cycles_path - - else - %li - = button_link_to t(:label_more), main_app.admin_order_cycles_path(params: { show_more: true }) -= form_for @order_cycle_set, url: main_app.bulk_update_admin_order_cycles_path, html: {"ng-app" => "admin.orderCycles"} do |f| - %table.index#listing_order_cycles - %colgroup - %col - %col{'style' => 'width: 20%;'} - %col{'style' => 'width: 20%;'} - - unless order_cycles_simple_index - %col - %col - %col - %col - %col - %col - %col - - %thead - %tr - %th - =t :name - %th - =t :open - %th - =t :close - - unless order_cycles_simple_index - %th - =t :supplier - %th - =t :coordinator - %th - =t :distributors - %th - =t :products - %th.actions - %th.actions - %th.actions +%form{ name: 'order_cycles_form', ng: { app: "admin.orderCycles", controller: 'OrderCyclesCtrl' } } + %save-bar{ dirty: "order_cycles_form.$dirty", persist: "false" } + %input.red{ type: "button", value: t(:save_changes), ng: { click: "saveAll()", disabled: "!order_cycles_form.$dirty" } } + %table.index#listing_order_cycles{ ng: { show: 'orderCycles.length > 0' } } + = render 'admin/order_cycles/header' #, simple_index: simple_index %tbody - = f.fields_for :collection do |order_cycle_form| - = render 'admin/order_cycles/row', order_cycle_form: order_cycle_form - - = f.submit t :update + = render 'admin/order_cycles/row' #, simple_index: simple_index + = render 'admin/order_cycles/loading_flash' + = render 'admin/order_cycles/show_more' diff --git a/config/locales/en.yml b/config/locales/en.yml index d348d10d48..854735185b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -219,6 +219,8 @@ en: form_invalid: "Form contains missing or invalid fields" clear_filters: Clear Filters clear: Clear + show_more: Show more + show_n_more: Show %{num} more columns: Columns actions: Actions @@ -898,9 +900,13 @@ en: no_payment: no payment methods no_shipping_or_payment: no shipping or payment methods unconfirmed: unconfirmed + days: days + + label_shop: "Shop" label_shops: "Shops" label_map: "Map" + label_producer: "Producer" label_producers: "Producers" label_groups: "Groups" label_about: "About" diff --git a/config/routes.rb b/config/routes.rb index e6b8153eb7..360d0af7c2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -97,7 +97,7 @@ Openfoodnetwork::Application.routes.draw do resources :enterprises do collection do get :for_order_cycle - get :for_line_items + get :visible post :bulk_update, as: :bulk_update end diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index e6ff6b1cea..f95bc6e8da 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -562,18 +562,22 @@ module Admin end end - describe "for_line_items" do - let!(:user) { create(:user) } - let!(:enterprise) { create(:enterprise, sells: 'any', owner: user) } + describe "visible" do + let!(:user) { create(:user, enterprise_limit: 10) } + let!(:visible_enterprise) { create(:enterprise, sells: 'any', owner: user) } + let!(:not_visible_enterprise) { create(:enterprise, sells: 'any') } before do # As a user with permission controller.stub spree_current_user: user + + # :create_variant_overrides does not affect visiblity (at time of writing) + create(:enterprise_relationship, parent: not_visible_enterprise, child: visible_enterprise, permissions_list: [:create_variant_overrides]) end - it "initializes permissions with the existing OrderCycle" do - # expect(controller).to receive(:render_as_json).with([enterprise], {ams_prefix: 'basic', spree_current_user: user}) - spree_get :for_line_items, format: :json + it "uses permissions to determine which enterprises are visible and should be rendered" do + expect(controller).to receive(:render_as_json).with([visible_enterprise], {ams_prefix: 'basic', spree_current_user: user}).and_call_original + spree_get :visible, format: :json end end diff --git a/spec/controllers/admin/order_cycles_controller_spec.rb b/spec/controllers/admin/order_cycles_controller_spec.rb index e91a48694c..4cc643d756 100644 --- a/spec/controllers/admin/order_cycles_controller_spec.rb +++ b/spec/controllers/admin/order_cycles_controller_spec.rb @@ -16,28 +16,41 @@ module Admin let!(:oc1) { create(:simple_order_cycle, orders_close_at: 60.days.ago ) } let!(:oc2) { create(:simple_order_cycle, orders_close_at: 40.days.ago ) } let!(:oc3) { create(:simple_order_cycle, orders_close_at: 20.days.ago ) } + let!(:oc4) { create(:simple_order_cycle, orders_close_at: nil ) } - context "where show_more is set to true" do - it "loads all order cycles" do - spree_get :index, show_more: true - expect(assigns(:collection)).to include oc1, oc2, oc3 + context "html" do + it "doesn't load any data" do + spree_get :index, format: :html + expect(assigns(:collection)).to be_empty end end - context "where show_more is not set" do - context "and q[orders_close_at_gt] is set" do - it "loads order cycles that closed within the past month" do - spree_get :index, q: { orders_close_at_gt: 45.days.ago } - expect(assigns(:collection)).to_not include oc1 - expect(assigns(:collection)).to include oc2, oc3 + context "json" do + context "where ransack conditions are specified" do + it "loads order cycles that closed within the past month, and orders without a close_at date" do + spree_get :index, format: :json + expect(assigns(:collection)).to_not include oc1, oc2 + expect(assigns(:collection)).to include oc3, oc4 end end - context "and q[orders_close_at_gt] is not set" do - it "loads order cycles that closed within the past month" do - spree_get :index - expect(assigns(:collection)).to_not include oc1, oc2 - expect(assigns(:collection)).to include oc3 + context "where q[orders_close_at_gt] is set" do + let(:q) { { orders_close_at_gt: 45.days.ago } } + + it "loads order cycles that closed after the specified date, and orders without a close_at date" do + spree_get :index, format: :json, q: q + expect(assigns(:collection)).to_not include oc1 + expect(assigns(:collection)).to include oc2, oc3, oc4 + end + + context "and other conditions are specified" do + before { q.merge!(id_not_in: [oc2.id, oc4.id]) } + + it "loads order cycles that meet all conditions" do + spree_get :index, format: :json, q: q + expect(assigns(:collection)).to_not include oc1, oc2, oc4 + expect(assigns(:collection)).to include oc3 + end end end end diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index cd0a2389c7..29bb04d253 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -21,12 +21,15 @@ feature %q{ oc1 = create(:order_cycle, name: '1') oc0 = create(:simple_order_cycle, name: '0', orders_open_at: nil, orders_close_at: nil) + oc7 = create(:simple_order_cycle, name: '0', + orders_open_at: 2.months.ago, orders_close_at: 5.weeks.ago) # When I go to the admin order cycles page login_to_admin_section click_link 'Order Cycles' # Then the order cycles should be ordered correctly + expect(page).to have_selector "#listing_order_cycles tr td:first-child", count: 7 page.all('#listing_order_cycles tr td:first-child').map(&:text).should == ['0', '1', '2', '3', '4', '5', '6'] @@ -40,13 +43,12 @@ feature %q{ page.should have_selector "#listing_order_cycles tr.order-cycle-#{oc6.id}.closed" # And I should see all the details for an order cycle - # (the table includes a hidden field between each row, making this nth-child(3) instead of 2) - within('table#listing_order_cycles tbody tr:nth-child(3)') do + within('table#listing_order_cycles tbody tr:nth-child(2)') do # Then I should see the basic fields page.should have_selector 'a', text: oc1.name - page.should have_selector "input[value='#{oc1.orders_open_at}']" - page.should have_selector "input[value='#{oc1.orders_close_at}']" + page.should have_input "oc#{oc1.id}[orders_open_at]", value: oc1.orders_open_at + page.should have_input "oc#{oc1.id}[orders_close_at]", value: oc1.orders_close_at page.should have_content oc1.coordinator.name # And I should see the suppliers and distributors @@ -56,6 +58,11 @@ feature %q{ # And I should see the number of variants page.should have_selector 'td.products', text: '2 variants' end + + # I can load more order_cycles + page.should have_no_selector "#listing_order_cycles tr.order-cycle-#{oc7.id}" + click_button "Show 30 more days" + page.should have_selector "#listing_order_cycles tr.order-cycle-#{oc7.id}" end describe 'listing order cycles with other locales' do @@ -85,15 +92,15 @@ feature %q{ end within("tr.order-cycle-#{oc_de.id}") do - expect(find('input.datetimepicker', match: :first).value).to eq '2012-01-30 00:00' + expect(find('input.datetimepicker', match: :first).value).to eq '2012-01-30 00:00:00' end end end end context "with specific time" do - let(:order_cycle_opening_time) { Time.zone.local(2040, 11, 06, 06, 00, 00) } - let(:order_cycle_closing_time) { Time.zone.local(2040, 11, 13, 17, 00, 00) } + let(:order_cycle_opening_time) { Time.zone.local(2040, 11, 06, 06, 00, 00).strftime("%F %T %z") } + let(:order_cycle_closing_time) { Time.zone.local(2040, 11, 13, 17, 00, 00).strftime("%F %T %z") } scenario "creating an order cycle", js: true do page.driver.resize(1280, 2000) @@ -183,16 +190,17 @@ feature %q{ # Then my order cycle should have been created page.should have_content 'Your order cycle has been created.' + oc = OrderCycle.last + page.should have_selector 'a', text: 'Plums & Avos' - page.should have_selector "input[value='#{order_cycle_opening_time}']" - page.should have_selector "input[value='#{order_cycle_closing_time}']" + page.should have_input "oc#{oc.id}[orders_open_at]", value: order_cycle_opening_time + page.should have_input "oc#{oc.id}[orders_close_at]", value: order_cycle_closing_time page.should have_content 'My coordinator' - page.should have_selector 'td.suppliers', text: 'My supplier' - page.should have_selector 'td.distributors', text: 'My distributor' + page.should have_selector 'td.producers', text: 'My supplier' + page.should have_selector 'td.shops', text: 'My distributor' # And it should have some fees - oc = OrderCycle.last oc.exchanges.incoming.first.enterprise_fees.should == [supplier_fee] oc.coordinator_fees.should == [coordinator_fee] oc.exchanges.outgoing.first.enterprise_fees.should == [distributor_fee] @@ -317,34 +325,35 @@ feature %q{ # Then my order cycle should have been updated page.should have_content 'Your order cycle has been updated.' - page.should have_selector 'a', text: 'Plums & Avos' + oc = OrderCycle.last - page.should have_selector "input[value='#{order_cycle_opening_time}']" - page.should have_selector "input[value='#{order_cycle_closing_time}']" + page.should have_selector 'a', text: 'Plums & Avos' + page.should have_input "oc#{oc.id}[orders_open_at]", value: order_cycle_opening_time + page.should have_input "oc#{oc.id}[orders_close_at]", value: order_cycle_closing_time page.should have_content coordinator.name - page.should have_selector 'td.suppliers', text: 'My supplier' - page.should have_selector 'td.distributors', text: 'My distributor' + page.should have_selector 'td.producers', text: 'My supplier' + page.should have_selector 'td.shops', text: 'My distributor' # And my coordinator fees should have been configured - OrderCycle.last.coordinator_fee_ids.should match_array [coordinator_fee1.id, coordinator_fee2.id] + oc.coordinator_fee_ids.should match_array [coordinator_fee1.id, coordinator_fee2.id] # And my supplier fees should have been configured - OrderCycle.last.exchanges.incoming.last.enterprise_fee_ids.should == [supplier_fee2.id] + oc.exchanges.incoming.last.enterprise_fee_ids.should == [supplier_fee2.id] # And my distributor fees should have been configured - OrderCycle.last.exchanges.outgoing.last.enterprise_fee_ids.should == [distributor_fee2.id] + oc.exchanges.outgoing.last.enterprise_fee_ids.should == [distributor_fee2.id] # And my tags should have been save - OrderCycle.last.exchanges.outgoing.last.tag_list.should == ['wholesale'] + oc.exchanges.outgoing.last.tag_list.should == ['wholesale'] # And it should have some variants selected selected_initial_variants = initial_variants.take initial_variants.size - 1 - OrderCycle.last.variants.map(&:id).should match_array(selected_initial_variants.map(&:id) + [v1.id, v2.id]) + oc.variants.map(&:id).should match_array (selected_initial_variants.map(&:id) + [v1.id, v2.id]) # And the collection details should have been updated - OrderCycle.last.exchanges.where(pickup_time: 'New time 0', pickup_instructions: 'New instructions 0').should be_present - OrderCycle.last.exchanges.where(pickup_time: 'New time 1', pickup_instructions: 'New instructions 1').should be_present + oc.exchanges.where(pickup_time: 'New time 0', pickup_instructions: 'New instructions 0').should be_present + oc.exchanges.where(pickup_time: 'New time 1', pickup_instructions: 'New instructions 1').should be_present end end @@ -492,10 +501,10 @@ feature %q{ all('input').last.set '2040-12-01 12:00:05' end - click_button 'Update' + click_button 'Save Changes' # Then my times should have been saved - flash_message.should == 'Order cycles have been updated.' + expect(page).to have_selector "#save-bar", text: "Order cycles have been updated." OrderCycle.order('id ASC').map { |oc| oc.orders_open_at.sec }.should == [0, 2, 4] OrderCycle.order('id ASC').map { |oc| oc.orders_close_at.sec }.should == [1, 3, 5] end @@ -507,12 +516,14 @@ feature %q{ # When I clone it login_to_admin_section click_link 'Order Cycles' - first('a.clone-order-cycle').click - flash_message.should == "Your order cycle #{oc.name} has been cloned." + within "tr.order-cycle-#{oc.id}" do + find('a.clone-order-cycle').click + end + expect(flash_message).to eq "Your order cycle #{oc.name} has been cloned." # Then I should have clone of the order cycle occ = OrderCycle.last - occ.name.should == "COPY OF #{oc.name}" + expect(occ.name).to eq "COPY OF #{oc.name}" end @@ -619,10 +630,10 @@ feature %q{ page.should_not have_content oc_for_other_user.name # The order cycle should show all enterprises in the order cycle - page.should have_selector 'td.suppliers', text: supplier_managed.name - page.should have_selector 'td.distributors', text: distributor_managed.name - page.should have_selector 'td.suppliers', text: supplier_unmanaged.name - page.should have_selector 'td.distributors', text: distributor_unmanaged.name + page.should have_selector 'td.producers', text: supplier_managed.name + page.should have_selector 'td.shops', text: distributor_managed.name + page.should have_selector 'td.producers', text: supplier_unmanaged.name + page.should have_selector 'td.shops', text: distributor_unmanaged.name end scenario "creating a new order cycle" do @@ -740,8 +751,10 @@ feature %q{ oc = create(:simple_order_cycle, coordinator: distributor_managed) click_link "Order Cycles" - first('a.clone-order-cycle').click - flash_message.should == "Your order cycle #{oc.name} has been cloned." + within "tr.order-cycle-#{oc.id}" do + find('a.clone-order-cycle').click + end + expect(flash_message).to eq "Your order cycle #{oc.name} has been cloned." # Then I should have clone of the order cycle occ = OrderCycle.last @@ -940,12 +953,14 @@ feature %q{ # Then my order cycle should have been created page.should have_content 'Your order cycle has been created.' + + oc = OrderCycle.last + page.should have_selector 'a', text: 'Plums & Avos' - page.should have_selector "input[value='#{Time.zone.local(2040, 10, 17, 06, 00, 00)}']" - page.should have_selector "input[value='#{Time.zone.local(2040, 10, 24, 17, 00, 00)}']" + page.should have_input "oc#{oc.id}[orders_open_at]", value: Time.zone.local(2040, 10, 17, 06, 00, 00).strftime("%F %T %z") + page.should have_input "oc#{oc.id}[orders_close_at]", value: Time.zone.local(2040, 10, 24, 17, 00, 00).strftime("%F %T %z") # And it should have some variants selected - oc = OrderCycle.last oc.exchanges.incoming.first.variants.count.should == 2 oc.exchanges.outgoing.first.variants.count.should == 2 @@ -1028,12 +1043,13 @@ feature %q{ # Then my order cycle should have been updated page.should have_content 'Your order cycle has been updated.' + oc = OrderCycle.last + page.should have_selector 'a', text: 'Plums & Avos' - page.should have_selector "input[value='#{Time.zone.local(2040, 10, 17, 06, 00, 00)}']" - page.should have_selector "input[value='#{Time.zone.local(2040, 10, 24, 17, 00, 00)}']" + page.should have_input "oc#{oc.id}[orders_open_at]", value: Time.zone.local(2040, 10, 17, 06, 00, 00).strftime("%F %T %z") + page.should have_input "oc#{oc.id}[orders_close_at]", value: Time.zone.local(2040, 10, 24, 17, 00, 00).strftime("%F %T %z") # And it should have a variant selected - oc = OrderCycle.last oc.exchanges.incoming.first.variants.should == [v2] oc.exchanges.outgoing.first.variants.should == [v2] diff --git a/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee b/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee index 16a70c8abd..8f8fb5f3fc 100644 --- a/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee @@ -35,9 +35,9 @@ describe "LineItemsCtrl", -> httpBackend.expectGET("/admin/orders.json?q%5Bcompleted_at_gt%5D=SomeDate&q%5Bcompleted_at_lt%5D=SomeDate&q%5Bcompleted_at_not_null%5D=true&q%5Bstate_not_eq%5D=canceled").respond [order] httpBackend.expectGET("/admin/bulk_line_items.json?q%5Border%5D%5Bcompleted_at_gt%5D=SomeDate&q%5Border%5D%5Bcompleted_at_lt%5D=SomeDate&q%5Border%5D%5Bcompleted_at_not_null%5D=true&q%5Border%5D%5Bstate_not_eq%5D=canceled").respond [lineItem] - httpBackend.expectGET("/admin/enterprises/for_line_items.json?ams_prefix=basic&q%5Bsells_in%5D%5B%5D=own&q%5Bsells_in%5D%5B%5D=any").respond [distributor] + httpBackend.expectGET("/admin/enterprises/visible.json?ams_prefix=basic&q%5Bsells_in%5D%5B%5D=own&q%5Bsells_in%5D%5B%5D=any").respond [distributor] httpBackend.expectGET("/admin/order_cycles.json?ams_prefix=basic&as=distributor&q%5Borders_close_at_gt%5D=SomeDate").respond [orderCycle] - httpBackend.expectGET("/admin/enterprises/for_line_items.json?ams_prefix=basic&q%5Bis_primary_producer_eq%5D=true").respond [supplier] + httpBackend.expectGET("/admin/enterprises/visible.json?ams_prefix=basic&q%5Bis_primary_producer_eq%5D=true").respond [supplier] scope.bulk_order_form = jasmine.createSpyObj('bulk_order_form', ['$setPristine']) diff --git a/spec/javascripts/unit/admin/order_cycles/controllers/order_cycles_controller_spec.js.coffee b/spec/javascripts/unit/admin/order_cycles/controllers/order_cycles_controller_spec.js.coffee new file mode 100644 index 0000000000..6e65e3abea --- /dev/null +++ b/spec/javascripts/unit/admin/order_cycles/controllers/order_cycles_controller_spec.js.coffee @@ -0,0 +1,60 @@ +describe "OrderCyclesCtrl", -> + ctrl = scope = httpBackend = Enterprises = OrderCycles = null + coordinator = producer = shop = orderCycle = null + + beforeEach -> + module "admin.orderCycles" + module ($provide) -> + $provide.value 'columns', [] + null + + jasmine.addMatchers + toDeepEqual: (util, customEqualityTesters) -> + compare: (actual, expected) -> + { pass: angular.equals(actual, expected) } + + beforeEach inject(($controller, $rootScope, $httpBackend, _OrderCycles_, _Enterprises_) -> + scope = $rootScope.$new() + ctrl = $controller + httpBackend = $httpBackend + Enterprises = _Enterprises_ + OrderCycles = _OrderCycles_ + spyOn(window, "daysFromToday").and.returnValue "SomeDate" + + coordinator = { id: 3, name: "Coordinator" } + producer = { id: 1, name: "Producer" } + shop = { id: 5, name: "Shop" } + orderCycle = { id: 4, name: "OC1", coordinator: {id: 3}, shops: [{id: 3},{id: 5}], producers: [{id: 1}] } + + httpBackend.expectGET("/admin/enterprises/visible.json?ams_prefix=basic").respond [coordinator, producer, shop] + httpBackend.expectGET("/admin/order_cycles.json?ams_prefix=index&q%5Borders_close_at_gt%5D=SomeDate").respond [orderCycle] + + ctrl "OrderCyclesCtrl", {$scope: scope, Enterprises: Enterprises, OrderCycles: OrderCycles} + ) + + describe "before data is returned", -> + it "the RequestMonitor will have a state of loading", -> + expect(scope.RequestMonitor.loading).toBe true + + describe "after data is returned", -> + beforeEach -> + httpBackend.flush() + + describe "initialisation", -> + it "gets suppliers, adds a blank option as the first in the list", -> + expect(scope.enterprises).toDeepEqual [ { id : '0', name : 'All' }, coordinator, producer, shop ] + + it "stores enterprises in an list that is accessible by id", -> + expect(Enterprises.enterprisesByID["5"]).toDeepEqual shop + + it "gets order cycles, with dereferenced coordinator, shops and producers", -> + oc = OrderCycles.orderCyclesByID["4"] + expect(scope.orderCycles).toDeepEqual [oc] + expect(oc.coordinator).toDeepEqual coordinator + expect(oc.shops).toDeepEqual [coordinator,shop] + expect(oc.producers).toDeepEqual [producer] + expect(oc.shopNames).toEqual "Coordinator, Shop" + expect(oc.producerNames).toEqual "Producer" + + it "the RequestMonitor will not longer have a state of loading", -> + expect(scope.RequestMonitor.loading).toBe false diff --git a/spec/javascripts/unit/admin/order_cycles/services/order_cycles_spec.js.coffee b/spec/javascripts/unit/admin/order_cycles/services/order_cycles_spec.js.coffee index f2ac4983f2..f1e2c95959 100644 --- a/spec/javascripts/unit/admin/order_cycles/services/order_cycles_spec.js.coffee +++ b/spec/javascripts/unit/admin/order_cycles/services/order_cycles_spec.js.coffee @@ -103,10 +103,11 @@ describe "OrderCycles service", -> describe "diff", -> beforeEach -> - OrderCycles.pristineByID = { 23: { id: 23, name: "ent1", is_primary_producer: true } } + OrderCycles.pristineByID = { 23: { id: 23, name: "orderCycle321", orders_open_at: '123' } } - it "returns a list of properties that have been altered", -> - expect(OrderCycles.diff({ id: 23, name: "orderCycle123", is_primary_producer: true })).toEqual ["name"] + it "returns a list of properties that have been altered, if they are in attrsToSave()", -> + spyOn(OrderCycles, "attrsToSave").and.returnValue(["orders_open_at"]) + expect(OrderCycles.diff({ id: 23, name: "orderCycle123", orders_open_at: '321' })).toEqual ["orders_open_at"] describe "resetAttribute", ->