From c0c8b07addf153c963289f1159f74ef9677b335c Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 11 Nov 2016 17:11:39 +1100 Subject: [PATCH 01/31] Let shopping tabs listen to URL changes When clicking on a shopping tab like "contact", it changed the URL. But changing the URL did not change the tab. Listening to URL changes enables manual manipulation of the URL and simple links to "#/contact" to open the contact tab. --- .../controllers/shopping_tabs_controller.js.coffee | 6 +++++- app/views/shop/_messages.html.haml | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/shopping_tabs_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/shopping_tabs_controller.js.coffee index 8daac0212c..b99d169c5b 100644 --- a/app/assets/javascripts/darkswarm/controllers/shopping_tabs_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/shopping_tabs_controller.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.controller "ShoppingTabsCtrl", ($scope, $controller, Navigation) -> +Darkswarm.controller "ShoppingTabsCtrl", ($scope, $controller, Navigation, $location) -> angular.extend this, $controller('TabsCtrl', {$scope: $scope}) $scope.tabs = @@ -6,3 +6,7 @@ Darkswarm.controller "ShoppingTabsCtrl", ($scope, $controller, Navigation) -> producers: { active: Navigation.isActive('/producers') } contact: { active: Navigation.isActive('/contact') } groups: { active: Navigation.isActive('/groups') } + + $scope.$on '$locationChangeStart', (event, url) -> + tab = $location.path().replace(/^\//, '') + $scope.tabs[tab]?.active = true diff --git a/app/views/shop/_messages.html.haml b/app/views/shop/_messages.html.haml index 9e5d3b8e82..8debaff82e 100644 --- a/app/views/shop/_messages.html.haml +++ b/app/views/shop/_messages.html.haml @@ -10,7 +10,7 @@ register: ('' + t('.register') + '').html_safe} - else = t '.require_customer_html', - {contact: ('' + t('.contact') + '').html_safe, + {contact: link_to(t('.contact'), '#contact'), enterprise: current_distributor.name} - elsif @order_cycles and @order_cycles.empty? - if current_distributor.preferred_shopfront_closed_message.present? From d8ce0e7d58d0b68dcb42d2dc888792bbaccebc45 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 24 Nov 2016 13:35:04 +1100 Subject: [PATCH 02/31] A user without enterprises does not have access to the dashboard, so it's not meaningful to spec --- spec/features/admin/overview_spec.rb | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/spec/features/admin/overview_spec.rb b/spec/features/admin/overview_spec.rb index 590c8528e8..ebe6a1d379 100644 --- a/spec/features/admin/overview_spec.rb +++ b/spec/features/admin/overview_spec.rb @@ -3,31 +3,18 @@ require 'spec_helper' feature %q{ As a backend user I want to be given information about the state of my enterprises, products and order cycles -} , js: true do +}, js: true do include AuthenticationWorkflow include AuthorizationHelpers include WebHelper - stub_authorization! - context "as an enterprise user" do - before :each do + before do @enterprise_user = create_enterprise_user Spree::Admin::OverviewController.any_instance.stub(:spree_current_user).and_return @enterprise_user quick_login_as @enterprise_user end - context "with no enterprises" do - it "prompts the user to create a new enteprise" do - visit '/admin' - page.should have_selector ".dashboard_item#enterprises h3", text: "My Enterprises" - page.should have_selector ".dashboard_item#enterprises .list-item", text: "You don't have any enterprises yet" - page.should have_selector ".dashboard_item#enterprises .button.bottom", text: "CREATE A NEW ENTERPRISE" - page.should_not have_selector ".dashboard_item#products" - page.should_not have_selector ".dashboard_item#order_cycles" - end - end - context "with an enterprise" do let(:d1) { create(:distributor_enterprise) } From b7e9ffc9da3ce78610dc92ee0c95c9698da0661f Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 24 Nov 2016 13:35:49 +1100 Subject: [PATCH 03/31] Fix enterprise user being denied access to admin when spree dash configured (as on production) --- app/models/spree/ability_decorator.rb | 2 ++ spec/features/admin/overview_spec.rb | 42 ++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index b10c781fac..13efc7e109 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -57,6 +57,7 @@ class AbilityDecorator def add_group_management_abilities(user) can [:admin, :index], :overview + can [:admin, :sync], :analytic can [:admin, :index], EnterpriseGroup can [:read, :edit, :update], EnterpriseGroup do |group| user.owned_groups.include? group @@ -69,6 +70,7 @@ class AbilityDecorator can [:create, :search], nil can [:admin, :index], :overview + can [:admin, :sync], :analytic can [:admin, :index, :read, :create, :edit, :update_positions, :destroy], ProducerProperty diff --git a/spec/features/admin/overview_spec.rb b/spec/features/admin/overview_spec.rb index ebe6a1d379..30f2cc0336 100644 --- a/spec/features/admin/overview_spec.rb +++ b/spec/features/admin/overview_spec.rb @@ -110,5 +110,45 @@ feature %q{ end end end + + context "with the spree dash configured" do + let(:d1) { create(:distributor_enterprise) } + + before do + stub_jirafe + @enterprise_user.enterprise_roles.build(enterprise: d1).save + end + + around do |example| + with_dash_configured { example.run } + end + + it "has permission to sync analytics" do + visit '/admin' + expect(page).to have_content d1.name + end + end end -end \ No newline at end of file + + private + + def stub_jirafe + stub_request(:post, "https://api.jirafe.com/v1/applications/abc123/resources?token="). + to_return(:status => 200, :body => "", :headers => {}) + end + + def with_dash_configured(&block) + Spree::Dash::Config.preferred_app_id = 'abc123' + Spree::Dash::Config.preferred_site_id = 'abc123' + Spree::Dash::Config.preferred_token = 'abc123' + expect(Spree::Dash::Config.configured?).to be true + + block.call + + ensure + Spree::Dash::Config.preferred_app_id = nil + Spree::Dash::Config.preferred_site_id = nil + Spree::Dash::Config.preferred_token = nil + expect(Spree::Dash::Config.configured?).to be false + end +end From 631b19084a90884d32dcce0239eca559097831bc Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 25 Nov 2016 09:37:05 +1100 Subject: [PATCH 04/31] Fix intermittent failure via FK when deleted taxon is primary taxon on p2 --- spec/models/spree/taxon_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/spree/taxon_spec.rb b/spec/models/spree/taxon_spec.rb index 8fb4b22dc5..3a26c9b2aa 100644 --- a/spec/models/spree/taxon_spec.rb +++ b/spec/models/spree/taxon_spec.rb @@ -7,7 +7,7 @@ module Spree let!(:t2) { create(:taxon) } describe "callbacks" do - let!(:p2) { create(:simple_product, taxons: [t1]) } + let!(:p2) { create(:simple_product, taxons: [t1], primary_taxon: t2) } it "refreshes the products cache on save" do expect(OpenFoodNetwork::ProductsCache).to receive(:product_changed).with(p2) From 338d3cbc386715c592914675c3cf1ffeb2d197f6 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 19 Oct 2016 15:49:27 +1100 Subject: [PATCH 05/31] Delete old commented code --- .../panels/exchange_distributed_products.html.haml | 3 --- .../admin/enterprises/form/_properties.html.haml | 11 ----------- app/views/shared/_copyright.html.haml | 3 --- app/views/spree/checkout/payment/_paypal.html.haml | 1 + 4 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 app/views/shared/_copyright.html.haml diff --git a/app/assets/javascripts/templates/admin/panels/exchange_distributed_products.html.haml b/app/assets/javascripts/templates/admin/panels/exchange_distributed_products.html.haml index 4c6a86e926..44080e3fe0 100644 --- a/app/assets/javascripts/templates/admin/panels/exchange_distributed_products.html.haml +++ b/app/assets/javascripts/templates/admin/panels/exchange_distributed_products.html.haml @@ -14,9 +14,6 @@ .exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle | visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges | orderBy:"name"' } .exchange-product-details %label - -# MASTER_VARIANTS: No longer required - -# = check_box_tag 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', 1, 1, 'ng-hide' => 'product.variants.length > 0', 'ng-model' => 'exchange.variants[product.master_id]', 'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_variants_{{ product.master_id }}', - -# 'ng-disabled' => 'product.variants.length > 0 || !order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(product.master_id) < 0' %img{'ng-src' => '{{ product.image_url }}'} .name {{ product.name }} .supplier {{ product.supplier_name }} diff --git a/app/views/admin/enterprises/form/_properties.html.haml b/app/views/admin/enterprises/form/_properties.html.haml index 795a104f1a..e3271435db 100644 --- a/app/views/admin/enterprises/form/_properties.html.haml +++ b/app/views/admin/enterprises/form/_properties.html.haml @@ -1,12 +1 @@ = render 'admin/producer_properties/form', f: f - -// :javascript -// var properties = #{raw(@properties.to_json)}; -// -// $("#producer_properties input.autocomplete").live("keydown", function() { -// already_auto_completed = $(this).is('ac_input'); -// if (!already_auto_completed) { -// $(this).autocomplete({source: properties}); -// $(this).focus(); -// } -// }); diff --git a/app/views/shared/_copyright.html.haml b/app/views/shared/_copyright.html.haml deleted file mode 100644 index 66ee8d9b4e..0000000000 --- a/app/views/shared/_copyright.html.haml +++ /dev/null @@ -1,3 +0,0 @@ --##copyright.text-center - -#%img.copyright{src: "/assets/logo.png", alt: "Open Food Network"} - -#© Copyright 2013 Open Food Foundation diff --git a/app/views/spree/checkout/payment/_paypal.html.haml b/app/views/spree/checkout/payment/_paypal.html.haml index e69de29bb2..1cc8aa25a7 100644 --- a/app/views/spree/checkout/payment/_paypal.html.haml +++ b/app/views/spree/checkout/payment/_paypal.html.haml @@ -0,0 +1 @@ +-# This file intentionally overrides the view in the spree_paypal_express gem From cbbb047fc118bc52f38bc42c34f2c75dbd868bb3 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sun, 18 Sep 2016 11:06:59 +1000 Subject: [PATCH 06/31] Expunge all mentions of includeBlank and blankOption from angular services --- .../services/enterprises.js.coffee | 2 +- .../line_items_controller.js.coffee | 2 +- .../filters/select_filter.js.coffee | 2 +- .../services/order_cycles.js.coffee | 5 +--- .../utils/services/blank_option.js.coffee | 2 -- .../services/order_cycles_spec.js.coffee | 25 ++++++------------- 6 files changed, 11 insertions(+), 27 deletions(-) delete mode 100644 app/assets/javascripts/admin/utils/services/blank_option.js.coffee diff --git a/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee b/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee index 44dfb503a5..57c6dc4b1a 100644 --- a/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee +++ b/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.enterprises").factory 'Enterprises', ($q, EnterpriseResource, blankOption) -> +angular.module("admin.enterprises").factory 'Enterprises', ($q, EnterpriseResource) -> new class Enterprises enterprisesByID: {} pristineByID: {} 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 f5d11fda58..1f0d6031b4 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 @@ -1,4 +1,4 @@ -angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $http, $q, StatusMessage, Columns, Dereferencer, Orders, LineItems, Enterprises, OrderCycles, blankOption, VariantUnitManager, RequestMonitor) -> +angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $http, $q, StatusMessage, Columns, Dereferencer, Orders, LineItems, Enterprises, OrderCycles, VariantUnitManager, RequestMonitor) -> $scope.initialized = false $scope.RequestMonitor = RequestMonitor $scope.filteredLineItems = [] diff --git a/app/assets/javascripts/admin/line_items/filters/select_filter.js.coffee b/app/assets/javascripts/admin/line_items/filters/select_filter.js.coffee index 6b6428c010..fb91acdeb3 100644 --- a/app/assets/javascripts/admin/line_items/filters/select_filter.js.coffee +++ b/app/assets/javascripts/admin/line_items/filters/select_filter.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.lineItems").filter "selectFilter", (blankOption, RequestMonitor) -> +angular.module("admin.lineItems").filter "selectFilter", (RequestMonitor) -> return (lineItems,selectedSupplier,selectedDistributor,selectedOrderCycle) -> filtered = [] unless RequestMonitor.loading diff --git a/app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee b/app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee index a182f19f12..71076c42c3 100644 --- a/app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee @@ -1,11 +1,9 @@ -angular.module("admin.orderCycles").factory 'OrderCycles', ($q, OrderCycleResource, blankOption) -> +angular.module("admin.orderCycles").factory 'OrderCycles', ($q, OrderCycleResource) -> new class OrderCycles orderCyclesByID: {} pristineByID: {} index: (params={}, callback=null) -> - includeBlank = !!params['includeBlank'] - delete params['includeBlank'] OrderCycleResource.index(params, (data) => for orderCycle in data @orderCyclesByID[orderCycle.id] = orderCycle @@ -13,7 +11,6 @@ angular.module("admin.orderCycles").factory 'OrderCycles', ($q, OrderCycleResour (callback || angular.noop)(data) - data.unshift(blankOption()) if includeBlank data ) diff --git a/app/assets/javascripts/admin/utils/services/blank_option.js.coffee b/app/assets/javascripts/admin/utils/services/blank_option.js.coffee deleted file mode 100644 index f4089aa20d..0000000000 --- a/app/assets/javascripts/admin/utils/services/blank_option.js.coffee +++ /dev/null @@ -1,2 +0,0 @@ -angular.module("admin.utils").value "blankOption", -> - { id: "0", name: "All" } 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 aebdb19f6f..e4d51d508c 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 @@ -37,25 +37,14 @@ describe "OrderCycles service", -> expect(result).toDeepEqual response describe "when no params are passed", -> - describe "where includeBlank param is truthy", -> - beforeEach -> - params = {includeBlank: true, someParam: 'someVal'} - $httpBackend.expectGET('/admin/order_cycles.json?someParam=someVal').respond 200, response - result = OrderCycles.index(params) - $httpBackend.flush() + beforeEach -> + params = { someParam: 'someVal'} + $httpBackend.expectGET('/admin/order_cycles.json?someParam=someVal').respond 200, response + result = OrderCycles.index(params) + $httpBackend.flush() - it "returns an array of orderCycles", -> - expect(result).toDeepEqual [{id: '0', name: 'All'} ,{ id: 5, name: 'OrderCycle 1'}] - - describe "where includeBlank param is falsey", -> - beforeEach -> - params = {includeBlank: false, someParam: 'someVal'} - $httpBackend.expectGET('/admin/order_cycles.json?someParam=someVal').respond 200, response - result = OrderCycles.index(params) - $httpBackend.flush() - - it "returns an array of orderCycles", -> - expect(result).toDeepEqual response + it "returns an array of orderCycles", -> + expect(result).toDeepEqual response describe "#save", -> From 3de69987e61b82710ff905da1d32e6f505cdcf32 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sat, 20 Aug 2016 19:57:31 +1000 Subject: [PATCH 07/31] Changing convention for angular resource services to generic 'byID' object and 'all' array --- .../enterprises/services/enterprises.js.coffee | 4 ++-- .../controllers/line_items_controller.js.coffee | 12 ++++++------ .../line_items/services/line_items.js.coffee | 12 ++++++------ .../order_cycles/services/order_cycles.js.coffee | 15 ++++++++++++--- .../admin/orders/services/orders.js.coffee | 4 ++-- .../admin/taxons/services/taxons.js.coffee | 15 ++++++++------- .../services/enterprises_spec.js.coffee | 4 ++-- .../line_items_controller_spec.js.coffee | 2 +- .../line_items/services/line_items_spec.js.coffee | 12 ++++++------ .../services/order_cycles_spec.js.coffee | 4 ++-- .../admin/orders/services/orders_spec.js.coffee | 4 ++-- 11 files changed, 49 insertions(+), 39 deletions(-) diff --git a/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee b/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee index 57c6dc4b1a..f3b8111253 100644 --- a/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee +++ b/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee @@ -1,12 +1,12 @@ angular.module("admin.enterprises").factory 'Enterprises', ($q, EnterpriseResource) -> new class Enterprises - enterprisesByID: {} + byID: {} pristineByID: {} index: (params={}, callback=null) -> EnterpriseResource.index(params, (data) => for enterprise in data - @enterprisesByID[enterprise.id] = enterprise + @byID[enterprise.id] = enterprise @pristineByID[enterprise.id] = angular.copy(enterprise) (callback || angular.noop)(data) data 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 1f0d6031b4..72e17d0931 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 @@ -22,8 +22,8 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.refreshData = -> unless !$scope.orderCycleFilter? || $scope.orderCycleFilter == 0 - $scope.startDate = OrderCycles.orderCyclesByID[$scope.orderCycleFilter].first_order - $scope.endDate = OrderCycles.orderCyclesByID[$scope.orderCycleFilter].last_order + $scope.startDate = OrderCycles.byID[$scope.orderCycleFilter].first_order + $scope.endDate = OrderCycles.byID[$scope.orderCycleFilter].last_order RequestMonitor.load $scope.orders = Orders.index("q[state_not_eq]": "canceled", "q[completed_at_not_null]": "true", "q[completed_at_gt]": "#{parseDate($scope.startDate)}", "q[completed_at_lt]": "#{parseDate($scope.endDate)}") 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)}") @@ -34,12 +34,12 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, RequestMonitor.load $scope.suppliers = Enterprises.index(action: "for_line_items", 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.enterprisesByID - Dereferencer.dereferenceAttr $scope.orders, "order_cycle", OrderCycles.orderCyclesByID + Dereferencer.dereferenceAttr $scope.orders, "distributor", Enterprises.byID + Dereferencer.dereferenceAttr $scope.orders, "order_cycle", OrderCycles.byID RequestMonitor.load $q.all([$scope.orders.$promise, $scope.suppliers.$promise, $scope.lineItems.$promise]).then -> - Dereferencer.dereferenceAttr $scope.lineItems, "supplier", Enterprises.enterprisesByID - Dereferencer.dereferenceAttr $scope.lineItems, "order", Orders.ordersByID + Dereferencer.dereferenceAttr $scope.lineItems, "supplier", Enterprises.byID + Dereferencer.dereferenceAttr $scope.lineItems, "order", Orders.byID $scope.bulk_order_form.$setPristine() StatusMessage.clear() unless $scope.initialized diff --git a/app/assets/javascripts/admin/line_items/services/line_items.js.coffee b/app/assets/javascripts/admin/line_items/services/line_items.js.coffee index e78389c559..5804bb2996 100644 --- a/app/assets/javascripts/admin/line_items/services/line_items.js.coffee +++ b/app/assets/javascripts/admin/line_items/services/line_items.js.coffee @@ -1,23 +1,23 @@ angular.module("admin.lineItems").factory 'LineItems', ($q, LineItemResource) -> new class LineItems - lineItemsByID: {} + byID: {} pristineByID: {} index: (params={}, callback=null) -> LineItemResource.index params, (data) => @resetData() for lineItem in data - @lineItemsByID[lineItem.id] = lineItem + @byID[lineItem.id] = lineItem @pristineByID[lineItem.id] = angular.copy(lineItem) (callback || angular.noop)(data) resetData: -> - @lineItemsByID = {} + @byID = {} @pristineByID = {} saveAll: -> - for id, lineItem of @lineItemsByID + for id, lineItem of @byID lineItem.errors = {} # removes errors when line_item has been returned to original state @save(lineItem) if !@isSaved(lineItem) @@ -34,7 +34,7 @@ angular.module("admin.lineItems").factory 'LineItems', ($q, LineItemResource) -> deferred.promise allSaved: -> - for id, lineItem of @lineItemsByID + for id, lineItem of @byID return false unless @isSaved(lineItem) true @@ -54,7 +54,7 @@ angular.module("admin.lineItems").factory 'LineItems', ($q, LineItemResource) -> deferred = $q.defer() lineItem.$delete({id: lineItem.id, orders: "orders", order_number: lineItem.order.number}) .then( (data) => - delete @lineItemsByID[lineItem.id] + delete @byID[lineItem.id] delete @pristineByID[lineItem.id] (callback || angular.noop)(data) deferred.resolve(data) diff --git a/app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee b/app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee index 71076c42c3..4716dc1757 100644 --- a/app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee @@ -1,12 +1,21 @@ -angular.module("admin.orderCycles").factory 'OrderCycles', ($q, OrderCycleResource) -> +angular.module("admin.orderCycles").factory 'OrderCycles', ($q, $injector, OrderCycleResource) -> new class OrderCycles - orderCyclesByID: {} + all: [] + byID: {} pristineByID: {} + constructor: -> + if $injector.has('orderCycles') + for orderCycle in $injector.get('orderCycles') + @all.push orderCycle + @byID[orderCycle.id] = orderCycle + @pristineByID[orderCycle.id] = angular.copy(orderCycle) + + index: (params={}, callback=null) -> OrderCycleResource.index(params, (data) => for orderCycle in data - @orderCyclesByID[orderCycle.id] = orderCycle + @byID[orderCycle.id] = orderCycle @pristineByID[orderCycle.id] = angular.copy(orderCycle) (callback || angular.noop)(data) diff --git a/app/assets/javascripts/admin/orders/services/orders.js.coffee b/app/assets/javascripts/admin/orders/services/orders.js.coffee index a7b5bc1b68..238afda8e0 100644 --- a/app/assets/javascripts/admin/orders/services/orders.js.coffee +++ b/app/assets/javascripts/admin/orders/services/orders.js.coffee @@ -1,12 +1,12 @@ angular.module("admin.orders").factory 'Orders', ($q, OrderResource) -> new class Orders - ordersByID: {} + byID: {} pristineByID: {} index: (params={}, callback=null) -> OrderResource.index params, (data) => for order in data - @ordersByID[order.id] = order + @byID[order.id] = order @pristineByID[order.id] = angular.copy(order) (callback || angular.noop)(data) diff --git a/app/assets/javascripts/admin/taxons/services/taxons.js.coffee b/app/assets/javascripts/admin/taxons/services/taxons.js.coffee index 62daf77901..48fd503980 100644 --- a/app/assets/javascripts/admin/taxons/services/taxons.js.coffee +++ b/app/assets/javascripts/admin/taxons/services/taxons.js.coffee @@ -1,19 +1,20 @@ angular.module("admin.taxons").factory "Taxons", (taxons, $filter) -> new class Taxons - taxons: taxons - taxonsByID: {} + all: [] + byID: {} constructor: -> - for taxon in @taxons - @taxonsByID[taxon.id] = taxon + for taxon in taxons + @all.push taxon + @byID[taxon.id] = taxon # For finding a single Taxon findByID: (id) -> - @taxonsByID[id] + @byID[id] # For finding multiple Taxons represented by comma delimited string findByIDs: (ids) -> - @taxonsByID[taxon_id] for taxon_id in ids.split(",") when @taxonsByID[taxon_id] + @byID[taxon_id] for taxon_id in ids.split(",") when @byID[taxon_id] findByTerm: (term) -> - $filter('filter')(@taxons, term) + $filter('filter')(@all, term) diff --git a/spec/javascripts/unit/admin/enterprises/services/enterprises_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/services/enterprises_spec.js.coffee index 5c81f72d97..8c088b1715 100644 --- a/spec/javascripts/unit/admin/enterprises/services/enterprises_spec.js.coffee +++ b/spec/javascripts/unit/admin/enterprises/services/enterprises_spec.js.coffee @@ -26,9 +26,9 @@ describe "Enterprises service", -> result = Enterprises.index() $httpBackend.flush() - it "stores returned data in @enterprisesByID, with ids as keys", -> + it "stores returned data in @byID, with ids as keys", -> # EnterpriseResource returns instances of Resource rather than raw objects - expect(Enterprises.enterprisesByID).toDeepEqual { 5: response[0] } + expect(Enterprises.byID).toDeepEqual { 5: response[0] } it "stores returned data in @pristineByID, with ids as keys", -> expect(Enterprises.pristineByID).toDeepEqual { 5: response[0] } 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 665d41934a..23c42efde0 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 @@ -70,7 +70,7 @@ describe "LineItemsCtrl", -> expect(scope.distributors).toDeepEqual [ distributor ] it "stores enterprises in an list that is accessible by id", -> - expect(Enterprises.enterprisesByID[1]).toDeepEqual supplier + expect(Enterprises.byID[1]).toDeepEqual supplier it "gets order cycles", -> expect(scope.orderCycles).toDeepEqual [ orderCycle ] diff --git a/spec/javascripts/unit/admin/line_items/services/line_items_spec.js.coffee b/spec/javascripts/unit/admin/line_items/services/line_items_spec.js.coffee index ef428377c0..3de04003e5 100644 --- a/spec/javascripts/unit/admin/line_items/services/line_items_spec.js.coffee +++ b/spec/javascripts/unit/admin/line_items/services/line_items_spec.js.coffee @@ -23,9 +23,9 @@ describe "LineItems service", -> result = LineItems.index() $httpBackend.flush() - it "stores returned data in @lineItemsByID, with ids as keys", -> + it "stores returned data in @byID, with ids as keys", -> # LineItemResource returns instances of Resource rather than raw objects - expect(LineItems.lineItemsByID).toDeepEqual { 5: response[0] } + expect(LineItems.byID).toDeepEqual { 5: response[0] } it "stores returned data in @pristineByID, with ids as keys", -> expect(LineItems.pristineByID).toDeepEqual { 5: response[0] } @@ -114,14 +114,14 @@ describe "LineItems service", -> beforeEach -> lineItem = new LineItemResource({ id: 15, order: { number: '12345678'} }) LineItems.pristineByID[15] = lineItem - LineItems.lineItemsByID[15] = lineItem + LineItems.byID[15] = lineItem $httpBackend.expectDELETE('/admin/orders/12345678/line_items/15.json').respond 200, { id: 15, name: 'LineItem 1'} LineItems.delete(lineItem, callback).then( -> resolved = true).catch( -> rejected = true) $httpBackend.flush() it "updates the pristine copy of the lineItem", -> expect(LineItems.pristineByID[15]).toBeUndefined() - expect(LineItems.lineItemsByID[15]).toBeUndefined() + expect(LineItems.byID[15]).toBeUndefined() it "runs the callback", -> expect(callback).toHaveBeenCalled() @@ -139,14 +139,14 @@ describe "LineItems service", -> beforeEach -> lineItem = new LineItemResource({ id: 15, order: { number: '12345678'} }) LineItems.pristineByID[15] = lineItem - LineItems.lineItemsByID[15] = lineItem + LineItems.byID[15] = lineItem $httpBackend.expectDELETE('/admin/orders/12345678/line_items/15.json').respond 422, { error: 'obj' } LineItems.delete(lineItem, callback).then( -> resolved = true).catch( -> rejected = true) $httpBackend.flush() it "does not update the pristine copy of the lineItem", -> expect(LineItems.pristineByID[15]).toBeDefined() - expect(LineItems.lineItemsByID[15]).toBeDefined() + expect(LineItems.byID[15]).toBeDefined() it "does not run the callback", -> expect(callback).not.toHaveBeenCalled() 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 e4d51d508c..f2ac4983f2 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 @@ -26,9 +26,9 @@ describe "OrderCycles service", -> result = OrderCycles.index() $httpBackend.flush() - it "stores returned data in @orderCyclesByID, with ids as keys", -> + it "stores returned data in @byID, with ids as keys", -> # OrderCycleResource returns instances of Resource rather than raw objects - expect(OrderCycles.orderCyclesByID).toDeepEqual { 5: response[0] } + expect(OrderCycles.byID).toDeepEqual { 5: response[0] } it "stores returned data in @pristineByID, with ids as keys", -> expect(OrderCycles.pristineByID).toDeepEqual { 5: response[0] } diff --git a/spec/javascripts/unit/admin/orders/services/orders_spec.js.coffee b/spec/javascripts/unit/admin/orders/services/orders_spec.js.coffee index c3d6646144..3d41ded40a 100644 --- a/spec/javascripts/unit/admin/orders/services/orders_spec.js.coffee +++ b/spec/javascripts/unit/admin/orders/services/orders_spec.js.coffee @@ -23,9 +23,9 @@ describe "Orders service", -> result = Orders.index() $httpBackend.flush() - it "stores returned data in @ordersByID, with ids as keys", -> + it "stores returned data in @byID, with ids as keys", -> # OrderResource returns instances of Resource rather than raw objects - expect(Orders.ordersByID).toDeepEqual { 5: response[0] } + expect(Orders.byID).toDeepEqual { 5: response[0] } it "stores returned data in @pristineByID, with ids as keys", -> expect(Orders.pristineByID).toDeepEqual { 5: response[0] } From 3678d4d01817e9f3925cd1debbea73f8c1340cc1 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sun, 21 Aug 2016 21:07:46 +1000 Subject: [PATCH 08/31] Creating new 'resources' module for holding interdependent ngResource services --- app/assets/javascripts/admin/all.js | 1 + app/assets/javascripts/admin/index_utils/index_utils.js.coffee | 2 +- .../javascripts/admin/order_cycles/order_cycles.js.coffee | 2 +- app/assets/javascripts/admin/resources/resources.js.coffee | 1 + .../resources}/enterprise_resource.js.coffee | 2 +- .../resources}/line_item_resource.js.coffee | 2 +- .../resources}/order_cycle_resource.js.coffee | 2 +- .../services => resources/resources}/order_resource.js.coffee | 2 +- .../{enterprises => resources}/services/enterprises.js.coffee | 2 +- .../{line_items => resources}/services/line_items.js.coffee | 2 +- .../{order_cycles => resources}/services/order_cycles.js.coffee | 2 +- .../admin/{orders => resources}/services/orders.js.coffee | 2 +- 12 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 app/assets/javascripts/admin/resources/resources.js.coffee rename app/assets/javascripts/admin/{enterprises/services => resources/resources}/enterprise_resource.js.coffee (75%) rename app/assets/javascripts/admin/{line_items/services => resources/resources}/line_item_resource.js.coffee (85%) rename app/assets/javascripts/admin/{order_cycles/services => resources/resources}/order_cycle_resource.js.coffee (64%) rename app/assets/javascripts/admin/{orders/services => resources/resources}/order_resource.js.coffee (66%) rename app/assets/javascripts/admin/{enterprises => resources}/services/enterprises.js.coffee (93%) rename app/assets/javascripts/admin/{line_items => resources}/services/line_items.js.coffee (96%) rename app/assets/javascripts/admin/{order_cycles => resources}/services/order_cycles.js.coffee (93%) rename app/assets/javascripts/admin/{orders => resources}/services/orders.js.coffee (92%) diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js index 102cb237ca..e71405ae46 100644 --- a/app/assets/javascripts/admin/all.js +++ b/app/assets/javascripts/admin/all.js @@ -37,6 +37,7 @@ //= require ./order_cycles/order_cycles //= require ./payment_methods/payment_methods //= require ./products/products +//= require ./resources/resources //= require ./shipping_methods/shipping_methods //= require ./side_menu/side_menu //= require ./tag_rules/tag_rules diff --git a/app/assets/javascripts/admin/index_utils/index_utils.js.coffee b/app/assets/javascripts/admin/index_utils/index_utils.js.coffee index 1ea74e614b..a956fd955c 100644 --- a/app/assets/javascripts/admin/index_utils/index_utils.js.coffee +++ b/app/assets/javascripts/admin/index_utils/index_utils.js.coffee @@ -1 +1 @@ -angular.module("admin.indexUtils", ['ngResource', 'ngSanitize', 'templates', 'admin.utils']).config ($httpProvider) -> $httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content"); $httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"; \ No newline at end of file +angular.module("admin.indexUtils", ['admin.resources', 'ngSanitize', 'templates', 'admin.utils']).config ($httpProvider) -> $httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content"); $httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"; \ No newline at end of file diff --git a/app/assets/javascripts/admin/order_cycles/order_cycles.js.coffee b/app/assets/javascripts/admin/order_cycles/order_cycles.js.coffee index 6ea3f76984..99dcbfe4b7 100644 --- a/app/assets/javascripts/admin/order_cycles/order_cycles.js.coffee +++ b/app/assets/javascripts/admin/order_cycles/order_cycles.js.coffee @@ -1,4 +1,4 @@ -angular.module('admin.orderCycles', ['ngResource', 'admin.utils', 'admin.indexUtils', 'ngTagsInput']) +angular.module('admin.orderCycles', ['admin.utils', 'admin.indexUtils', 'ngTagsInput']) .config ($httpProvider) -> $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content') diff --git a/app/assets/javascripts/admin/resources/resources.js.coffee b/app/assets/javascripts/admin/resources/resources.js.coffee new file mode 100644 index 0000000000..9e89237732 --- /dev/null +++ b/app/assets/javascripts/admin/resources/resources.js.coffee @@ -0,0 +1 @@ +angular.module("admin.resources", ['ngResource']) diff --git a/app/assets/javascripts/admin/enterprises/services/enterprise_resource.js.coffee b/app/assets/javascripts/admin/resources/resources/enterprise_resource.js.coffee similarity index 75% rename from app/assets/javascripts/admin/enterprises/services/enterprise_resource.js.coffee rename to app/assets/javascripts/admin/resources/resources/enterprise_resource.js.coffee index b522f5be3b..7cdd5b7bce 100644 --- a/app/assets/javascripts/admin/enterprises/services/enterprise_resource.js.coffee +++ b/app/assets/javascripts/admin/resources/resources/enterprise_resource.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.enterprises").factory 'EnterpriseResource', ($resource) -> +angular.module("admin.resources").factory 'EnterpriseResource', ($resource) -> ignoredAttrs = -> ["$$hashKey", "producer", "package", "producerError", "packageError", "status"] diff --git a/app/assets/javascripts/admin/line_items/services/line_item_resource.js.coffee b/app/assets/javascripts/admin/resources/resources/line_item_resource.js.coffee similarity index 85% rename from app/assets/javascripts/admin/line_items/services/line_item_resource.js.coffee rename to app/assets/javascripts/admin/resources/resources/line_item_resource.js.coffee index 60ca925753..4301f8df82 100644 --- a/app/assets/javascripts/admin/line_items/services/line_item_resource.js.coffee +++ b/app/assets/javascripts/admin/resources/resources/line_item_resource.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.lineItems").factory 'LineItemResource', ($resource) -> +angular.module("admin.resources").factory 'LineItemResource', ($resource) -> $resource('/admin/:orders/:order_number/line_items/:id.json', {}, { 'index': method: 'GET' diff --git a/app/assets/javascripts/admin/order_cycles/services/order_cycle_resource.js.coffee b/app/assets/javascripts/admin/resources/resources/order_cycle_resource.js.coffee similarity index 64% rename from app/assets/javascripts/admin/order_cycles/services/order_cycle_resource.js.coffee rename to app/assets/javascripts/admin/resources/resources/order_cycle_resource.js.coffee index 4a5df3c44a..cd5be31acd 100644 --- a/app/assets/javascripts/admin/order_cycles/services/order_cycle_resource.js.coffee +++ b/app/assets/javascripts/admin/resources/resources/order_cycle_resource.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.orderCycles").factory 'OrderCycleResource', ($resource) -> +angular.module("admin.resources").factory 'OrderCycleResource', ($resource) -> $resource('/admin/order_cycles/:id/:action.json', {}, { 'index': method: 'GET' diff --git a/app/assets/javascripts/admin/orders/services/order_resource.js.coffee b/app/assets/javascripts/admin/resources/resources/order_resource.js.coffee similarity index 66% rename from app/assets/javascripts/admin/orders/services/order_resource.js.coffee rename to app/assets/javascripts/admin/resources/resources/order_resource.js.coffee index ab360a2fc9..317b384485 100644 --- a/app/assets/javascripts/admin/orders/services/order_resource.js.coffee +++ b/app/assets/javascripts/admin/resources/resources/order_resource.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.orders").factory 'OrderResource', ($resource) -> +angular.module("admin.resources").factory 'OrderResource', ($resource) -> $resource('/admin/orders/:id/:action.json', {}, { 'index': method: 'GET' diff --git a/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee b/app/assets/javascripts/admin/resources/services/enterprises.js.coffee similarity index 93% rename from app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee rename to app/assets/javascripts/admin/resources/services/enterprises.js.coffee index f3b8111253..5cdebe02cd 100644 --- a/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee +++ b/app/assets/javascripts/admin/resources/services/enterprises.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.enterprises").factory 'Enterprises', ($q, EnterpriseResource) -> +angular.module("admin.resources").factory 'Enterprises', ($q, EnterpriseResource) -> new class Enterprises byID: {} pristineByID: {} diff --git a/app/assets/javascripts/admin/line_items/services/line_items.js.coffee b/app/assets/javascripts/admin/resources/services/line_items.js.coffee similarity index 96% rename from app/assets/javascripts/admin/line_items/services/line_items.js.coffee rename to app/assets/javascripts/admin/resources/services/line_items.js.coffee index 5804bb2996..d790c826a3 100644 --- a/app/assets/javascripts/admin/line_items/services/line_items.js.coffee +++ b/app/assets/javascripts/admin/resources/services/line_items.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.lineItems").factory 'LineItems', ($q, LineItemResource) -> +angular.module("admin.resources").factory 'LineItems', ($q, LineItemResource) -> new class LineItems byID: {} pristineByID: {} diff --git a/app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee b/app/assets/javascripts/admin/resources/services/order_cycles.js.coffee similarity index 93% rename from app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee rename to app/assets/javascripts/admin/resources/services/order_cycles.js.coffee index 4716dc1757..ad86a7899e 100644 --- a/app/assets/javascripts/admin/order_cycles/services/order_cycles.js.coffee +++ b/app/assets/javascripts/admin/resources/services/order_cycles.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.orderCycles").factory 'OrderCycles', ($q, $injector, OrderCycleResource) -> +angular.module("admin.resources").factory 'OrderCycles', ($q, $injector, OrderCycleResource) -> new class OrderCycles all: [] byID: {} diff --git a/app/assets/javascripts/admin/orders/services/orders.js.coffee b/app/assets/javascripts/admin/resources/services/orders.js.coffee similarity index 92% rename from app/assets/javascripts/admin/orders/services/orders.js.coffee rename to app/assets/javascripts/admin/resources/services/orders.js.coffee index 238afda8e0..69a56b77ca 100644 --- a/app/assets/javascripts/admin/orders/services/orders.js.coffee +++ b/app/assets/javascripts/admin/resources/services/orders.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.orders").factory 'Orders', ($q, OrderResource) -> +angular.module("admin.resources").factory 'Orders', ($q, OrderResource) -> new class Orders byID: {} pristineByID: {} From 1770a67cd9f4ec7c5a4b3c492ded56aeec6f0522 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Sun, 18 Sep 2016 13:42:52 +1000 Subject: [PATCH 09/31] Adding #load function to services for resources, for generic loading of data into byID and pristineByID --- .../resources/services/enterprises.js.coffee | 12 ++++++----- .../resources/services/line_items.js.coffee | 12 ++++++----- .../resources/services/order_cycles.js.coffee | 21 ++++++++----------- .../admin/resources/services/orders.js.coffee | 10 +++++---- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/app/assets/javascripts/admin/resources/services/enterprises.js.coffee b/app/assets/javascripts/admin/resources/services/enterprises.js.coffee index 5cdebe02cd..0b7fa6e870 100644 --- a/app/assets/javascripts/admin/resources/services/enterprises.js.coffee +++ b/app/assets/javascripts/admin/resources/services/enterprises.js.coffee @@ -4,13 +4,15 @@ angular.module("admin.resources").factory 'Enterprises', ($q, EnterpriseResource pristineByID: {} index: (params={}, callback=null) -> - EnterpriseResource.index(params, (data) => - for enterprise in data - @byID[enterprise.id] = enterprise - @pristineByID[enterprise.id] = angular.copy(enterprise) + EnterpriseResource.index params, (data) => + @load(data) (callback || angular.noop)(data) data - ) + + load: (enterprises) -> + for enterprise in enterprises + @byID[enterprise.id] = enterprise + @pristineByID[enterprise.id] = angular.copy(enterprise) save: (enterprise) -> deferred = $q.defer() diff --git a/app/assets/javascripts/admin/resources/services/line_items.js.coffee b/app/assets/javascripts/admin/resources/services/line_items.js.coffee index d790c826a3..182ef81e6a 100644 --- a/app/assets/javascripts/admin/resources/services/line_items.js.coffee +++ b/app/assets/javascripts/admin/resources/services/line_items.js.coffee @@ -5,17 +5,19 @@ angular.module("admin.resources").factory 'LineItems', ($q, LineItemResource) -> index: (params={}, callback=null) -> LineItemResource.index params, (data) => - @resetData() - for lineItem in data - @byID[lineItem.id] = lineItem - @pristineByID[lineItem.id] = angular.copy(lineItem) - + @load(data) (callback || angular.noop)(data) resetData: -> @byID = {} @pristineByID = {} + load: (lineItems) -> + @resetData() + for lineItem in lineItems + @byID[lineItem.id] = lineItem + @pristineByID[lineItem.id] = angular.copy(lineItem) + saveAll: -> for id, lineItem of @byID lineItem.errors = {} # removes errors when line_item has been returned to original state 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 ad86a7899e..728b173e35 100644 --- a/app/assets/javascripts/admin/resources/services/order_cycles.js.coffee +++ b/app/assets/javascripts/admin/resources/services/order_cycles.js.coffee @@ -6,22 +6,19 @@ angular.module("admin.resources").factory 'OrderCycles', ($q, $injector, OrderCy constructor: -> if $injector.has('orderCycles') - for orderCycle in $injector.get('orderCycles') - @all.push orderCycle - @byID[orderCycle.id] = orderCycle - @pristineByID[orderCycle.id] = angular.copy(orderCycle) - + @load($injector.get('orderCycles')) index: (params={}, callback=null) -> - OrderCycleResource.index(params, (data) => - for orderCycle in data - @byID[orderCycle.id] = orderCycle - @pristineByID[orderCycle.id] = angular.copy(orderCycle) - + OrderCycleResource.index params, (data) => + @load(data) (callback || angular.noop)(data) - data - ) + + load: (orderCycles) -> + for orderCycle in orderCycles + @all.push orderCycle + @byID[orderCycle.id] = orderCycle + @pristineByID[orderCycle.id] = angular.copy(orderCycle) save: (order_cycle) -> deferred = $q.defer() diff --git a/app/assets/javascripts/admin/resources/services/orders.js.coffee b/app/assets/javascripts/admin/resources/services/orders.js.coffee index 69a56b77ca..da3f409149 100644 --- a/app/assets/javascripts/admin/resources/services/orders.js.coffee +++ b/app/assets/javascripts/admin/resources/services/orders.js.coffee @@ -5,12 +5,14 @@ angular.module("admin.resources").factory 'Orders', ($q, OrderResource) -> index: (params={}, callback=null) -> OrderResource.index params, (data) => - for order in data - @byID[order.id] = order - @pristineByID[order.id] = angular.copy(order) - + @load(data) (callback || angular.noop)(data) + load: (orders) -> + for order in orders + @byID[order.id] = order + @pristineByID[order.id] = angular.copy(order) + save: (order) -> deferred = $q.defer() order.$update({id: order.number}) From dbbd52cace348462b9fc07c3fdbab1dc45ae6482 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 28 Oct 2016 11:44:26 +1100 Subject: [PATCH 10/31] Fixing broken taxons filter on bulk product edit Was referencing Taxons.taxons instead of Taxons.all --- app/assets/javascripts/admin/bulk_product_update.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 7a9d2677b9..d464a49bf4 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -22,7 +22,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout $scope.producers = producers - $scope.taxons = Taxons.taxons + $scope.taxons = Taxons.all $scope.tax_categories = tax_categories $scope.filterProducers = [{id: "0", name: ""}].concat $scope.producers $scope.filterTaxons = [{id: "0", name: ""}].concat $scope.taxons From 268c8dbcddf5c02a91ff7340fd4fdca45833c72c Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 25 Nov 2016 09:59:32 +1100 Subject: [PATCH 11/31] Moving Customers and CustomerResource services to admin.resource module --- .../customers_controller.js.coffee | 3 +-- .../resources}/customer_resource.js.coffee | 2 +- .../services/customers.js.coffee | 24 +++++++++++++------ .../customers_controller_spec.js.coffee | 15 ++---------- .../services/customers_spec.js.coffee | 23 ++++++++++++++++++ 5 files changed, 44 insertions(+), 23 deletions(-) rename app/assets/javascripts/admin/{customers/services => resources/resources}/customer_resource.js.coffee (86%) rename app/assets/javascripts/admin/{customers => resources}/services/customers.js.coffee (58%) create mode 100644 spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee diff --git a/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee b/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee index 9e956a1977..2506e994ac 100644 --- a/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee +++ b/app/assets/javascripts/admin/customers/controllers/customers_controller.js.coffee @@ -3,8 +3,8 @@ angular.module("admin.customers").controller "customersCtrl", ($scope, $q, $filt $scope.availableCountries = availableCountries $scope.RequestMonitor = RequestMonitor $scope.submitAll = pendingChanges.submitAll - $scope.add = Customers.add $scope.customerLimit = 20 + $scope.customers = Customers.all $scope.columns = Columns.columns $scope.confirmRefresh = (event) -> @@ -16,7 +16,6 @@ angular.module("admin.customers").controller "customersCtrl", ($scope, $q, $filt Customers.index({enterprise_id: $scope.shop_id}).then (data) -> pendingChanges.removeAll() $scope.customers_form.$setPristine() - $scope.customers = data $scope.shop_id = shops[0].id if shops.length == 1 diff --git a/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee b/app/assets/javascripts/admin/resources/resources/customer_resource.js.coffee similarity index 86% rename from app/assets/javascripts/admin/customers/services/customer_resource.js.coffee rename to app/assets/javascripts/admin/resources/resources/customer_resource.js.coffee index 3904d0333d..0a67586374 100644 --- a/app/assets/javascripts/admin/customers/services/customer_resource.js.coffee +++ b/app/assets/javascripts/admin/resources/resources/customer_resource.js.coffee @@ -1,4 +1,4 @@ -angular.module("admin.customers").factory 'CustomerResource', ($resource) -> +angular.module("admin.resources").factory 'CustomerResource', ($resource) -> $resource('/admin/customers/:id.json', {}, { 'index': method: 'GET' diff --git a/app/assets/javascripts/admin/customers/services/customers.js.coffee b/app/assets/javascripts/admin/resources/services/customers.js.coffee similarity index 58% rename from app/assets/javascripts/admin/customers/services/customers.js.coffee rename to app/assets/javascripts/admin/resources/services/customers.js.coffee index f4c6f6f3cd..6b73f5c4db 100644 --- a/app/assets/javascripts/admin/customers/services/customers.js.coffee +++ b/app/assets/javascripts/admin/resources/services/customers.js.coffee @@ -1,19 +1,24 @@ -angular.module("admin.customers").factory "Customers", ($q, InfoDialog, RequestMonitor, CustomerResource, CurrentShop) -> +angular.module("admin.resources").factory "Customers", ($q, InfoDialog, RequestMonitor, CustomerResource, CurrentShop) -> new class Customers - customers: [] + all: [] + byID: {} + pristineByID: {} add: (email) -> params = enterprise_id: CurrentShop.shop.id email: email CustomerResource.create params, (customer) => - @customers.unshift customer if customer.id + if customer.id + @all.unshift customer + @byID[customer.id] = customer + @pristineByID[customer.id] = angular.copy(customer) remove: (customer) -> params = id: customer.id CustomerResource.destroy params, => - i = @customers.indexOf customer - @customers.splice i, 1 unless i < 0 + i = @all.indexOf customer + @all.splice i, 1 unless i < 0 , (response) => errors = response.data.errors if errors? @@ -22,14 +27,19 @@ angular.module("admin.customers").factory "Customers", ($q, InfoDialog, RequestM InfoDialog.open 'error', "Could not delete customer: #{customer.email}" index: (params) -> - request = CustomerResource.index(params, (data) => @customers = data) + request = CustomerResource.index(params, (data) => @load(data)) RequestMonitor.load(request.$promise) request.$promise + load: (customers) -> + for customer in customers + @all.push customer + @byID[customer.id] = customer + @pristineByID[customer.id] = angular.copy(customer) + update: (address, customer, addressType) -> params = id: customer.id customer: "#{addressType}_attributes": address CustomerResource.update params - diff --git a/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee b/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee index 5f3deff550..ce4b967f90 100644 --- a/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/customers/controllers/customers_controller_spec.js.coffee @@ -62,28 +62,17 @@ describe "CustomersCtrl", -> as = scope.findByCode('b') expect(as).toDeepEqual [] - describe "scope.add", -> - it "creates a new customer", -> - email = "customer@example.org" - newCustomer = {id: 6, email: email} - customers.unshift(newCustomer) - http.expectPOST('/admin/customers.json?email=' + email + '&enterprise_id=3').respond 200, newCustomer - scope.add(email) - http.flush() - expect(scope.customers).toDeepEqual customers - describe "scope.deleteCustomer", -> beforeEach -> spyOn(window, 'confirm').and.returnValue(true) it "deletes a customer", -> - expect(scope.customers.length).toBe 2 + expect(scope.customers.length).toBe 1 customer = scope.customers[0] http.expectDELETE('/admin/customers/' + customer.id + '.json').respond 200 scope.deleteCustomer(customer) http.flush() - expect(scope.customers.length).toBe 1 - expect(scope.customers[0]).not.toDeepEqual customer + expect(scope.customers.length).toBe 0 describe "scope.findTags", -> tags = [ diff --git a/spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee b/spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee new file mode 100644 index 0000000000..569701d29c --- /dev/null +++ b/spec/javascripts/unit/admin/customers/services/customers_spec.js.coffee @@ -0,0 +1,23 @@ +describe "Customers", -> + Customers = CurrentShop = customers = $httpBackend = null + + beforeEach -> + module 'admin.customers' + + jasmine.addMatchers + toDeepEqual: (util, customEqualityTesters) -> + compare: (actual, expected) -> + { pass: angular.equals(actual, expected) } + + inject ($q, _$httpBackend_, _Customers_, _CurrentShop_) -> + Customers = _Customers_ + + describe "scope.add", -> + it "creates a new customer", inject ($httpBackend, CurrentShop) -> + email = "customer@example.org" + newCustomer = {id: 6, email: email} + CurrentShop.shop = { id: 3 } + $httpBackend.expectPOST('/admin/customers.json?email=' + email + '&enterprise_id=3').respond 200, newCustomer + Customers.add(email) + $httpBackend.flush() + expect(Customers.all).toDeepEqual [newCustomer] From 7498b7f098361ab7dd1e83a50a773b49dd82733f Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 25 Nov 2016 10:09:07 +1100 Subject: [PATCH 12/31] Moving PaymentMethods service to admin.resource module --- .../payment_methods_controller.js.coffee | 2 +- .../services/payment_methods.js.coffee | 8 -------- .../resources/services/payment_methods.js.coffee | 16 ++++++++++++++++ 3 files changed, 17 insertions(+), 9 deletions(-) delete mode 100644 app/assets/javascripts/admin/payment_methods/services/payment_methods.js.coffee create mode 100644 app/assets/javascripts/admin/resources/services/payment_methods.js.coffee diff --git a/app/assets/javascripts/admin/payment_methods/controllers/payment_methods_controller.js.coffee b/app/assets/javascripts/admin/payment_methods/controllers/payment_methods_controller.js.coffee index ddad6cd259..2def86a25a 100644 --- a/app/assets/javascripts/admin/payment_methods/controllers/payment_methods_controller.js.coffee +++ b/app/assets/javascripts/admin/payment_methods/controllers/payment_methods_controller.js.coffee @@ -1,3 +1,3 @@ angular.module("admin.paymentMethods").controller "paymentMethodsCtrl", ($scope, PaymentMethods) -> $scope.findPaymentMethodByID = (id) -> - $scope.PaymentMethod = PaymentMethods.findByID(id) + $scope.PaymentMethod = PaymentMethods.byID[id] diff --git a/app/assets/javascripts/admin/payment_methods/services/payment_methods.js.coffee b/app/assets/javascripts/admin/payment_methods/services/payment_methods.js.coffee deleted file mode 100644 index c31a20d96f..0000000000 --- a/app/assets/javascripts/admin/payment_methods/services/payment_methods.js.coffee +++ /dev/null @@ -1,8 +0,0 @@ -angular.module("admin.paymentMethods") - .factory "PaymentMethods", (paymentMethods) -> - new class PaymentMethods - paymentMethods: paymentMethods - - findByID: (id) -> - for paymentMethod in @paymentMethods - return paymentMethod if paymentMethod.id is id diff --git a/app/assets/javascripts/admin/resources/services/payment_methods.js.coffee b/app/assets/javascripts/admin/resources/services/payment_methods.js.coffee new file mode 100644 index 0000000000..f84f3a165d --- /dev/null +++ b/app/assets/javascripts/admin/resources/services/payment_methods.js.coffee @@ -0,0 +1,16 @@ +angular.module("admin.resources") + .factory "PaymentMethods", ($injector) -> + new class PaymentMethods + paymentMethods: [] + byID: {} + pristineByID: {} + + constructor: -> + if $injector.has('paymentMethods') + @load($injector.get('paymentMethods')) + + load: (paymentMethods) -> + for paymentMethod in paymentMethods + @paymentMethods.push paymentMethod + @byID[paymentMethod.id] = paymentMethod + @pristineByID[paymentMethod.id] = angular.copy(paymentMethod) From 5e214a32b3a3b5f429a63a526a036a6de1bbca2e Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 25 Nov 2016 10:16:08 +1100 Subject: [PATCH 13/31] Moving ShippingMethods service to admin.resource module --- .../services/shipping_methods.js.coffee | 16 ++++++++++++++++ .../shipping_methods_controller.js.coffee | 2 +- .../services/shipping_methods.js.coffee | 8 -------- 3 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 app/assets/javascripts/admin/resources/services/shipping_methods.js.coffee delete mode 100644 app/assets/javascripts/admin/shipping_methods/services/shipping_methods.js.coffee diff --git a/app/assets/javascripts/admin/resources/services/shipping_methods.js.coffee b/app/assets/javascripts/admin/resources/services/shipping_methods.js.coffee new file mode 100644 index 0000000000..9fde8c9d06 --- /dev/null +++ b/app/assets/javascripts/admin/resources/services/shipping_methods.js.coffee @@ -0,0 +1,16 @@ +angular.module("admin.resources") + .factory "ShippingMethods", ($injector) -> + new class ShippingMethods + shippingMethods: [] + byID: {} + pristineByID: {} + + constructor: -> + if $injector.has('shippingMethods') + @load($injector.get('shippingMethods')) + + load: (shippingMethods) -> + for shippingMethod in shippingMethods + @shippingMethods.push shippingMethod + @byID[shippingMethod.id] = shippingMethod + @pristineByID[shippingMethod.id] = angular.copy(shippingMethod) diff --git a/app/assets/javascripts/admin/shipping_methods/controllers/shipping_methods_controller.js.coffee b/app/assets/javascripts/admin/shipping_methods/controllers/shipping_methods_controller.js.coffee index 9efd2a5fea..c9b85ea76e 100644 --- a/app/assets/javascripts/admin/shipping_methods/controllers/shipping_methods_controller.js.coffee +++ b/app/assets/javascripts/admin/shipping_methods/controllers/shipping_methods_controller.js.coffee @@ -1,3 +1,3 @@ angular.module("admin.shippingMethods").controller "shippingMethodsCtrl", ($scope, ShippingMethods) -> $scope.findShippingMethodByID = (id) -> - $scope.ShippingMethod = ShippingMethods.findByID(id) + $scope.ShippingMethod = ShippingMethods.byID[id] diff --git a/app/assets/javascripts/admin/shipping_methods/services/shipping_methods.js.coffee b/app/assets/javascripts/admin/shipping_methods/services/shipping_methods.js.coffee deleted file mode 100644 index c691f5dae5..0000000000 --- a/app/assets/javascripts/admin/shipping_methods/services/shipping_methods.js.coffee +++ /dev/null @@ -1,8 +0,0 @@ -angular.module("admin.shippingMethods") - .factory "ShippingMethods", (shippingMethods) -> - new class ShippingMethods - shippingMethods: shippingMethods - - findByID: (id) -> - for shippingMethod in @shippingMethods - return shippingMethod if shippingMethod.id is id From 27283c50b828d5a1697705782479ad9f10496bdf Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 25 Nov 2016 12:43:48 +1100 Subject: [PATCH 14/31] Customers service clears array fo each #index request --- .../javascripts/admin/resources/services/customers.js.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/javascripts/admin/resources/services/customers.js.coffee b/app/assets/javascripts/admin/resources/services/customers.js.coffee index 6b73f5c4db..3783097f56 100644 --- a/app/assets/javascripts/admin/resources/services/customers.js.coffee +++ b/app/assets/javascripts/admin/resources/services/customers.js.coffee @@ -27,6 +27,7 @@ angular.module("admin.resources").factory "Customers", ($q, InfoDialog, RequestM InfoDialog.open 'error', "Could not delete customer: #{customer.email}" index: (params) -> + @clear() request = CustomerResource.index(params, (data) => @load(data)) RequestMonitor.load(request.$promise) request.$promise @@ -43,3 +44,6 @@ angular.module("admin.resources").factory "Customers", ($q, InfoDialog, RequestM customer: "#{addressType}_attributes": address CustomerResource.update params + + clear: -> + @all.length = 0 From f4f38b41833b66f6a815b6247ba12cea16319f1f Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 14 Oct 2016 09:56:40 +1100 Subject: [PATCH 15/31] Re-apply shop property filters - including performance regression This reverts commit 7d149ed198a18ce602cf3b35eff93f55645df851. --- .../darkswarm/filters/properties.js.coffee | 8 ++- .../darkswarm/filters/properties_of.js.coffee | 12 ++-- .../darkswarm/_shop-filters.css.sass | 2 +- app/helpers/injection_helper.rb | 2 +- app/helpers/serializer_helper.rb | 6 ++ app/models/producer_property.rb | 8 ++- app/models/spree/property_decorator.rb | 8 ++- app/models/spree/taxon_decorator.rb | 27 ++++---- app/serializers/api/enterprise_serializer.rb | 48 ++++++++------ app/serializers/api/property_serializer.rb | 6 ++ app/views/groups/_hub_filters.html.haml | 21 ------- app/views/groups/show.html.haml | 28 ++++----- app/views/home/_filters.html.haml | 22 ------- app/views/producers/_filters.html.haml | 2 +- app/views/producers/index.html.haml | 2 +- app/views/shop/products/_filters.html.haml | 2 +- app/views/{home => shops}/_fat.html.haml | 0 app/views/shops/_filters.html.haml | 34 ++++++++++ app/views/{home => shops}/_hubs.html.haml | 6 +- .../{home => shops}/_hubs_table.html.haml | 6 +- app/views/{home => shops}/_skinny.html.haml | 0 app/views/shops/index.html.haml | 4 +- config/locales/en.yml | 1 + .../enterprise_injection_data.rb | 8 ++- spec/features/consumer/groups_spec.rb | 39 ++++++++++++ spec/features/consumer/shops_spec.rb | 63 ++++++++++++++++++- spec/models/producer_property_spec.rb | 22 ++++--- spec/models/spree/property_spec.rb | 34 ++++++---- spec/models/spree/taxon_spec.rb | 17 +++-- .../serializers/enterprise_serializer_spec.rb | 3 +- 30 files changed, 299 insertions(+), 142 deletions(-) create mode 100644 app/helpers/serializer_helper.rb delete mode 100644 app/views/groups/_hub_filters.html.haml delete mode 100644 app/views/home/_filters.html.haml rename app/views/{home => shops}/_fat.html.haml (100%) create mode 100644 app/views/shops/_filters.html.haml rename app/views/{home => shops}/_hubs.html.haml (89%) rename app/views/{home => shops}/_hubs_table.html.haml (67%) rename app/views/{home => shops}/_skinny.html.haml (100%) diff --git a/app/assets/javascripts/darkswarm/filters/properties.js.coffee b/app/assets/javascripts/darkswarm/filters/properties.js.coffee index 1453cac053..fe7ed71fa3 100644 --- a/app/assets/javascripts/darkswarm/filters/properties.js.coffee +++ b/app/assets/javascripts/darkswarm/filters/properties.js.coffee @@ -1,13 +1,17 @@ Darkswarm.filter 'properties', -> # Filter anything that responds to object.supplied_properties - (objects, ids) -> + (objects, ids, source) -> objects ||= [] ids ?= [] + + source ||= 'properties' + return [] unless source in ['properties', 'supplied_properties', 'distributed_properties'] + if ids.length == 0 # No properties selected, pass all objects through. objects else objects.filter (obj) -> - properties = obj.supplied_properties || obj.properties + properties = obj[source] properties.some (property) -> property.id in ids diff --git a/app/assets/javascripts/darkswarm/filters/properties_of.js.coffee b/app/assets/javascripts/darkswarm/filters/properties_of.js.coffee index 7eac12bf1e..f3f4f51b5e 100644 --- a/app/assets/javascripts/darkswarm/filters/properties_of.js.coffee +++ b/app/assets/javascripts/darkswarm/filters/properties_of.js.coffee @@ -1,12 +1,12 @@ Darkswarm.filter 'propertiesOf', -> - (objects) -> + (objects, source) -> + source ||= 'properties' + return {} unless source in ['properties', 'supplied_properties', 'distributed_properties'] + properties = {} for object in objects - if object.supplied_properties? - for property in object.supplied_properties - properties[property.id] = property - else - for property in object.properties + if object[source]? + for property in object[source] properties[property.id] = property properties diff --git a/app/assets/stylesheets/darkswarm/_shop-filters.css.sass b/app/assets/stylesheets/darkswarm/_shop-filters.css.sass index 3c4f9a514d..07fea7a0b1 100644 --- a/app/assets/stylesheets/darkswarm/_shop-filters.css.sass +++ b/app/assets/stylesheets/darkswarm/_shop-filters.css.sass @@ -96,7 +96,7 @@ // content. Ensure that the dropdown appears above the content. .filter-row position: relative - z-index: 100 + z-index: 90 .filter-shopfront &.taxon-selectors, &.property-selectors diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 6db2e583bc..dad9cf11f9 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -45,7 +45,7 @@ module InjectionHelper end def inject_properties - inject_json_ams "properties", Spree::Property.all, Api::IdNameSerializer + inject_json_ams "properties", Spree::Property.all, Api::PropertySerializer end def inject_currency_config diff --git a/app/helpers/serializer_helper.rb b/app/helpers/serializer_helper.rb new file mode 100644 index 0000000000..2e48e3741d --- /dev/null +++ b/app/helpers/serializer_helper.rb @@ -0,0 +1,6 @@ +module SerializerHelper + def ids_to_objs(ids) + return [] if ids.blank? + ids.map { |id| {id: id} } + end +end diff --git a/app/models/producer_property.rb b/app/models/producer_property.rb index bf1f083936..b0bb1b6e51 100644 --- a/app/models/producer_property.rb +++ b/app/models/producer_property.rb @@ -8,14 +8,18 @@ class ProducerProperty < ActiveRecord::Base after_destroy :refresh_products_cache_from_destroy - scope :sold_by, ->(shop) { + scope :ever_sold_by, ->(shop) { joins(producer: {supplied_products: {variants: {exchanges: :order_cycle}}}). merge(Exchange.outgoing). merge(Exchange.to_enterprise(shop)). - merge(OrderCycle.active). select('DISTINCT producer_properties.*') } + scope :currently_sold_by, ->(shop) { + ever_sold_by(shop). + merge(OrderCycle.active) + } + def property_name property.name if property diff --git a/app/models/spree/property_decorator.rb b/app/models/spree/property_decorator.rb index 50e450a03b..81577cef4e 100644 --- a/app/models/spree/property_decorator.rb +++ b/app/models/spree/property_decorator.rb @@ -8,14 +8,18 @@ module Spree where('spree_product_properties.product_id IN (?)', enterprise.supplied_product_ids) } - scope :sold_by, ->(shop) { + scope :ever_sold_by, ->(shop) { joins(products: {variants: {exchanges: :order_cycle}}). merge(Exchange.outgoing). merge(Exchange.to_enterprise(shop)). - merge(OrderCycle.active). select('DISTINCT spree_properties.*') } + scope :currently_sold_by, ->(shop) { + ever_sold_by(shop). + merge(OrderCycle.active) + } + after_save :refresh_products_cache diff --git a/app/models/spree/taxon_decorator.rb b/app/models/spree/taxon_decorator.rb index a051de98fa..1878a20e49 100644 --- a/app/models/spree/taxon_decorator.rb +++ b/app/models/spree/taxon_decorator.rb @@ -32,21 +32,24 @@ Spree::Taxon.class_eval do end # Find all the taxons of distributed products for each enterprise, indexed by enterprise. + # May return :all taxons (distributed in open and closed order cycles), + # or :current taxons (distributed in an open order cycle). + # # Format: {enterprise_id => [taxon_id, ...]} - def self.distributed_taxons - taxons = {} + def self.distributed_taxons(which_taxons=:all) + # TODO: Why can't we merge(Spree::Product.with_order_cycles_inner) here? + taxons = Spree::Taxon. + joins(products: {variants_including_master: {exchanges: :order_cycle}}). + merge(Exchange.outgoing). + select('spree_taxons.*, exchanges.receiver_id AS enterprise_id') - Spree::Taxon. - joins(:products). - merge(Spree::Product.with_order_cycles_outer). - where('o_exchanges.incoming = ?', false). - select('spree_taxons.*, o_exchanges.receiver_id AS enterprise_id'). - each do |t| - taxons[t.enterprise_id.to_i] ||= Set.new - taxons[t.enterprise_id.to_i] << t.id - end + taxons = taxons.merge(OrderCycle.active) if which_taxons == :current - taxons + taxons.inject({}) do |ts, t| + ts[t.enterprise_id.to_i] ||= Set.new + ts[t.enterprise_id.to_i] << t.id + ts + end end diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index f68c8be679..3537bcdd24 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -20,7 +20,10 @@ class Api::EnterpriseSerializer < ActiveModel::Serializer end class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer + include SerializerHelper + attributes :orders_close_at, :active + attributes :taxons, :supplied_taxons has_many :supplied_properties, serializer: Api::PropertySerializer has_many :distributed_properties, serializer: Api::PropertySerializer @@ -42,15 +45,37 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer def distributed_properties # This results in 3 queries per enterprise - product_properties = Spree::Property.sold_by(object) - ids = ProducerProperty.sold_by(object).pluck(:property_id) - producer_properties = Spree::Property.where(id: ids) + + if active + product_properties = Spree::Property.currently_sold_by(object) + producer_property_ids = ProducerProperty.currently_sold_by(object).pluck(:property_id) + + else + product_properties = Spree::Property.ever_sold_by(object) + producer_property_ids = ProducerProperty.ever_sold_by(object).pluck(:property_id) + end + + producer_properties = Spree::Property.where(id: producer_property_ids) OpenFoodNetwork::PropertyMerge.merge product_properties, producer_properties end + + def taxons + if active + ids_to_objs options[:data].current_distributed_taxons[object.id] + else + ids_to_objs options[:data].all_distributed_taxons[object.id] + end + end + + def supplied_taxons + ids_to_objs options[:data].supplied_taxons[object.id] + end end class Api::CachedEnterpriseSerializer < ActiveModel::Serializer + include SerializerHelper + cached #delegate :cache_key, to: :object @@ -65,16 +90,7 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer :email_address, :hash, :logo, :promo_image, :path, :pickup, :delivery, :icon, :icon_font, :producer_icon_font, :category, :producers, :hubs - attributes :taxons, :supplied_taxons - has_one :address, serializer: Api::AddressSerializer - def taxons - ids_to_objs options[:data].distributed_taxons[object.id] - end - - def supplied_taxons - ids_to_objs options[:data].supplied_taxons[object.id] - end def pickup services = options[:data].shipping_method_services[object.id] @@ -153,12 +169,4 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer } icon_fonts[object.category] end - - - private - - def ids_to_objs(ids) - return [] if ids.blank? - ids.map { |id| {id: id} } - end end diff --git a/app/serializers/api/property_serializer.rb b/app/serializers/api/property_serializer.rb index 7da4fce990..cdb3f2bae3 100644 --- a/app/serializers/api/property_serializer.rb +++ b/app/serializers/api/property_serializer.rb @@ -1,3 +1,9 @@ class Api::PropertySerializer < ActiveModel::Serializer attributes :id, :name, :presentation + + # Client-side we don't care about the property name. Send the presentation + # since this is what we want to show to the user. + def name + object.presentation + end end diff --git a/app/views/groups/_hub_filters.html.haml b/app/views/groups/_hub_filters.html.haml deleted file mode 100644 index 71924d5e85..0000000000 --- a/app/views/groups/_hub_filters.html.haml +++ /dev/null @@ -1,21 +0,0 @@ -.row - = render partial: 'shared/components/filter_controls' - = render partial: 'shared/components/show_profiles' - -.row.animate-show{"ng-show" => "filtersActive"} - .small-12.columns - .row.filter-box - .small-12.large-9.columns - %h5.tdhead - .light - = t :hubs_filter_by - = t :hubs_filter_type - %filter-selector.small-block-grid-2.medium-block-grid-4.large-block-grid-5{"selector-set" => "filterSelectors", objects: "group_hubs | searchEnterprises:query | shipping:shippingTypes | showHubProfiles:show_profiles | taxonsOf", "active-selectors" => "activeTaxons"} - .small-12.large-3.columns - %h5.tdhead - .light - = t :hubs_filter_by - = t :hubs_filter_delivery - %shipping-type-selector - -= render partial: 'shared/components/filter_box' diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index ef9e281155..90b74c0b42 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -60,23 +60,23 @@ .small-12.columns %h1 = t :groups_producers - = render partial: "shared/components/enterprise_search" - = render partial: "producers/filters" + = render "shared/components/enterprise_search" + = render "producers/filters" .row .small-12.columns .active_table %producer.active_table_node.row.animate-repeat{id: "{{producer.path}}", - "ng-repeat" => "producer in filteredEnterprises = (group_producers | searchEnterprises:query | taxons:activeTaxons | properties:activeProperties)", + "ng-repeat" => "producer in filteredEnterprises = (group_producers | searchEnterprises:query | taxons:activeTaxons | properties:activeProperties:'supplied_properties')", "ng-controller" => "GroupEnterpriseNodeCtrl", "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}", id: "{{producer.hash}}"} .small-12.columns - = render partial: 'producers/skinny' - = render partial: 'producers/fat' + = render "producers/skinny" + = render "producers/fat" - = render partial: 'shared/components/enterprise_no_results' + = render 'shared/components/enterprise_no_results' %tab{heading: t(:groups_hubs), active: "tabs.hubs.active", @@ -87,24 +87,24 @@ %h1 = t :groups_hubs - = render partial: "shared/components/enterprise_search" - = render partial: "hub_filters" + = render "shared/components/enterprise_search" + = render "shops/filters", resource: "group_hubs", property_filters: "| searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles" .row .small-12.columns .active_table %hub.active_table_node.row.animate-repeat{id: "{{hub.hash}}", - "ng-repeat" => "hub in filteredEnterprises = (group_hubs | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])", + "ng-repeat" => "hub in filteredEnterprises = (group_hubs | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | properties:activeProperties:'distributed_properties' | orderBy:['-active', '+orders_close_at'])", "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", "ng-controller" => "GroupEnterpriseNodeCtrl"} .small-12.columns - = render partial: 'home/skinny' - = render partial: 'home/fat' + = render 'shops/skinny' + = render 'shops/fat' - = render partial: 'shared/components/enterprise_no_results' + = render 'shared/components/enterprise_no_results' .small-12.medium-12.large-3.columns - = render partial: 'contact' + = render 'contact' .small-12.columns.pad-top .row.pad-top @@ -122,4 +122,4 @@ %p   -= render partial: "shared/footer" += render "shared/footer" diff --git a/app/views/home/_filters.html.haml b/app/views/home/_filters.html.haml deleted file mode 100644 index bfd13fdf54..0000000000 --- a/app/views/home/_filters.html.haml +++ /dev/null @@ -1,22 +0,0 @@ -.row - = render partial: 'shared/components/filter_controls' - -# .small-12.medium-6.columns   - = render partial: 'shared/components/show_profiles' - -.row.animate-show{"ng-show" => "filtersActive"} - .small-12.columns - .row.filter-box - .small-12.large-9.columns - %h5.tdhead - .light - = t :hubs_filter_by - = t :hubs_filter_type - %filter-selector.small-block-grid-2.medium-block-grid-4.large-block-grid-5{ "selector-set" => "filterSelectors", objects: "visibleMatches | visible | taxonsOf", "active-selectors" => "activeTaxons" } - .small-12.large-3.columns - %h5.tdhead - .light - = t :hubs_filter_by - = t :hubs_filter_delivery - %shipping-type-selector - -= render partial: 'shared/components/filter_box' diff --git a/app/views/producers/_filters.html.haml b/app/views/producers/_filters.html.haml index 771ed932c9..17f260196a 100644 --- a/app/views/producers/_filters.html.haml +++ b/app/views/producers/_filters.html.haml @@ -18,6 +18,6 @@ = t :producers_filter = t :producers_filter_property .filter-shopfront.property-selectors - %single-line-selectors{ selectors: "filterSelectors", objects: "producers_to_filter | searchEnterprises:query | propertiesOf", "active-selectors" => "activeProperties"} + %filter-selector{ "selector-set" => "filterSelectors", objects: "producers_to_filter | searchEnterprises:query | taxons:activeTaxons | propertiesOf:'supplied_properties'", "active-selectors" => "activeProperties"} = render partial: 'shared/components/filter_box' diff --git a/app/views/producers/index.html.haml b/app/views/producers/index.html.haml index 04048ef77e..af6c2e4fd1 100644 --- a/app/views/producers/index.html.haml +++ b/app/views/producers/index.html.haml @@ -16,7 +16,7 @@ .small-12.columns .active_table %producer.active_table_node.row.animate-repeat{id: "{{producer.path}}", - "ng-repeat" => "producer in filteredEnterprises = (Enterprises.producers | visible | searchEnterprises:query | taxons:activeTaxons | properties:activeProperties)", + "ng-repeat" => "producer in filteredEnterprises = (Enterprises.producers | visible | searchEnterprises:query | taxons:activeTaxons | properties:activeProperties:'supplied_properties')", "ng-controller" => "ProducerNodeCtrl", "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}", id: "{{producer.hash}}"} diff --git a/app/views/shop/products/_filters.html.haml b/app/views/shop/products/_filters.html.haml index 7efaba6cc9..10c7c20deb 100644 --- a/app/views/shop/products/_filters.html.haml +++ b/app/views/shop/products/_filters.html.haml @@ -1,5 +1,5 @@ .filter-shopfront.taxon-selectors.text-right - %single-line-selectors{ selectors: "taxonSelectors", objects: "Products.products | products:query | properties: activeProperties | taxonsOf", "active-selectors" => "activeTaxons"} + %single-line-selectors{ selectors: "taxonSelectors", objects: "Products.products | products:query | properties:activeProperties | taxonsOf", "active-selectors" => "activeTaxons"} .filter-shopfront.property-selectors.text-right %single-line-selectors{ selectors: "propertySelectors", objects: "Products.products | products:query | taxons:activeTaxons | propertiesOf", "active-selectors" => "activeProperties"} diff --git a/app/views/home/_fat.html.haml b/app/views/shops/_fat.html.haml similarity index 100% rename from app/views/home/_fat.html.haml rename to app/views/shops/_fat.html.haml diff --git a/app/views/shops/_filters.html.haml b/app/views/shops/_filters.html.haml new file mode 100644 index 0000000000..42e2b2ec28 --- /dev/null +++ b/app/views/shops/_filters.html.haml @@ -0,0 +1,34 @@ +- resource ||= "visibleMatches" +- property_filters ||= "| filter:filterExpression | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles" + +.row + = render 'shared/components/filter_controls' + -# .small-12.medium-6.columns   + = render 'shared/components/show_profiles' + +.row.animate-show.filter-row{"ng-show" => "filtersActive"} + .small-12.columns + .row.filter-box + .small-12.large-9.columns + %h5.tdhead + .light + = t :hubs_filter_by + = t :hubs_filter_type + + %filter-selector.small-block-grid-2.medium-block-grid-4.large-block-grid-5{ "selector-set" => "filterSelectors", objects: "#{resource} | visible | taxonsOf", "active-selectors" => "activeTaxons" } + .small-12.large-3.columns + %h5.tdhead + .light + = t :hubs_filter_by + = t :hubs_filter_delivery + %shipping-type-selector + + .small-12.large-12.columns + %h5.tdhead + .light + = t :hubs_filter_by + = t :hubs_filter_property + .filter-shopfront.property-selectors + %filter-selector{ "selector-set" => "filterSelectors", objects: "#{resource} #{property_filters} | propertiesOf:'distributed_properties'", "active-selectors" => "activeProperties"} + += render 'shared/components/filter_box' diff --git a/app/views/home/_hubs.html.haml b/app/views/shops/_hubs.html.haml similarity index 89% rename from app/views/home/_hubs.html.haml rename to app/views/shops/_hubs.html.haml index e899270efc..6087d2330c 100644 --- a/app/views/home/_hubs.html.haml +++ b/app/views/shops/_hubs.html.haml @@ -7,14 +7,14 @@ = t :hubs_intro = render "shared/components/enterprise_search" - = render "home/filters" + = render "filters" .row .small-12.columns .name-matches{"ng-show" => "nameMatchesFiltered.length > 0"} %h2 = t :hubs_matches - = render "home/hubs_table", enterprises: "nameMatches" + = render "hubs_table", enterprises: "nameMatches" .distance-matches{"ng-if" => "nameMatchesFiltered.length == 0 || distanceMatchesShown"} %h2{"ng-show" => "nameMatchesFiltered.length > 0 || query.length > 0"} @@ -22,7 +22,7 @@ %span{"ng-show" => "nameMatchesFiltered.length > 0"} {{ nameMatchesFiltered[0].name }}... %span{"ng-hide" => "nameMatchesFiltered.length > 0"} {{ query }}... - = render "home/hubs_table", enterprises: "distanceMatches" + = render "hubs_table", enterprises: "distanceMatches" .show-distance-matches{"ng-show" => "nameMatchesFiltered.length > 0 && !distanceMatchesShown"} %a{href: "", "ng-click" => "showDistanceMatches()"} diff --git a/app/views/home/_hubs_table.html.haml b/app/views/shops/_hubs_table.html.haml similarity index 67% rename from app/views/home/_hubs_table.html.haml rename to app/views/shops/_hubs_table.html.haml index edf9eb5ec8..6d5803b477 100644 --- a/app/views/home/_hubs_table.html.haml +++ b/app/views/shops/_hubs_table.html.haml @@ -1,10 +1,10 @@ .active_table - %hub.active_table_node.row{"ng-repeat" => "hub in #{enterprises}Filtered = (#{enterprises} | filter:filterExpression | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+distance', '+orders_close_at'])", + %hub.active_table_node.row{"ng-repeat" => "hub in #{enterprises}Filtered = (#{enterprises} | filter:filterExpression | taxons:activeTaxons | properties:activeProperties:'distributed_properties' | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+distance', '+orders_close_at'])", "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", "ng-controller" => "HubNodeCtrl", id: "{{hub.hash}}"} .small-12.columns - = render 'home/skinny' - = render 'home/fat' + = render 'skinny' + = render 'fat' = render 'shared/components/enterprise_no_results', enterprises: "#{enterprises}Filtered" diff --git a/app/views/home/_skinny.html.haml b/app/views/shops/_skinny.html.haml similarity index 100% rename from app/views/home/_skinny.html.haml rename to app/views/shops/_skinny.html.haml diff --git a/app/views/shops/index.html.haml b/app/views/shops/index.html.haml index 1b3cf547a8..7e288f4157 100644 --- a/app/views/shops/index.html.haml +++ b/app/views/shops/index.html.haml @@ -10,5 +10,5 @@ %p.text-big = t :shops_text -= render partial: "home/hubs" -= render partial: "shared/footer" += render "hubs" += render "shared/footer" diff --git a/config/locales/en.yml b/config/locales/en.yml index 2211243bda..010cd4d586 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -513,6 +513,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using hubs_filter_by: "Filter by" hubs_filter_type: "Type" hubs_filter_delivery: "Delivery" + hubs_filter_property: "Property" hubs_matches: "Did you mean?" hubs_intro: Shop in your local area hubs_distance: Closest to diff --git a/lib/open_food_network/enterprise_injection_data.rb b/lib/open_food_network/enterprise_injection_data.rb index 87516007c6..938c8b3aab 100644 --- a/lib/open_food_network/enterprise_injection_data.rb +++ b/lib/open_food_network/enterprise_injection_data.rb @@ -20,8 +20,12 @@ module OpenFoodNetwork @supplied_taxons ||= Spree::Taxon.supplied_taxons end - def distributed_taxons - @distributed_taxons ||= Spree::Taxon.distributed_taxons + def all_distributed_taxons + @all_distributed_taxons ||= Spree::Taxon.distributed_taxons(:all) + end + + def current_distributed_taxons + @current_distributed_taxons ||= Spree::Taxon.distributed_taxons(:current) end end end diff --git a/spec/features/consumer/groups_spec.rb b/spec/features/consumer/groups_spec.rb index 039322b379..4a5f166d7d 100644 --- a/spec/features/consumer/groups_spec.rb +++ b/spec/features/consumer/groups_spec.rb @@ -54,4 +54,43 @@ feature 'Groups', js: true do end end end + + describe "shops" do + describe "filtering by product property" do + let!(:group) { create(:enterprise_group, enterprises: [d1, d2], on_front_page: true) } + let!(:order_cycle) { create(:simple_order_cycle, distributors: [d1, d2], coordinator: create(:distributor_enterprise)) } + let(:producer) { create(:supplier_enterprise) } + let(:d1) { create(:distributor_enterprise) } + let(:d2) { create(:distributor_enterprise) } + let(:p1) { create(:simple_product, supplier: producer) } + let(:p2) { create(:simple_product, supplier: create(:supplier_enterprise)) } + let(:ex_d1) { order_cycle.exchanges.outgoing.where(receiver_id: d1).first } + let(:ex_d2) { order_cycle.exchanges.outgoing.where(receiver_id: d2).first } + + before do + producer.set_producer_property 'Organic', 'NASAA 12345' + p2.set_property 'Local', 'XYZ 123' + + ex_d1.variants << p1.variants.first + ex_d2.variants << p2.variants.first + + visit group_path(group, anchor: "/hubs") + end + + it "filters" do + toggle_filters + + toggle_filter 'Organic' + + expect(page).to have_content d1.name + expect(page).not_to have_content d2.name + + toggle_filter 'Organic' + toggle_filter 'Local' + + expect(page).not_to have_content d1.name + expect(page).to have_content d2.name + end + end + end end diff --git a/spec/features/consumer/shops_spec.rb b/spec/features/consumer/shops_spec.rb index 2a65a73aef..f8f54a197b 100644 --- a/spec/features/consumer/shops_spec.rb +++ b/spec/features/consumer/shops_spec.rb @@ -47,7 +47,7 @@ feature 'Shops', js: true do it "should show closed shops after clicking the button" do create(:simple_product, distributors: [d1, d2]) visit shops_path - click_link_and_ensure("Show closed shops", -> { page.has_selector? 'hub.inactive' }) + click_link_and_ensure("Show Closed Shops", -> { page.has_selector? 'hub.inactive' }) page.should have_selector 'hub.inactive', text: d2.name end @@ -56,6 +56,67 @@ feature 'Shops', js: true do expect(page).to have_current_path enterprise_shop_path(distributor) end + describe "filtering by product property" do + let!(:order_cycle) { create(:simple_order_cycle, distributors: [d1, d2], coordinator: create(:distributor_enterprise)) } + let!(:p1) { create(:simple_product, supplier: producer) } + let!(:p2) { create(:simple_product, supplier: create(:supplier_enterprise)) } + let(:ex_d1) { order_cycle.exchanges.outgoing.where(receiver_id: d1).first } + let(:ex_d2) { order_cycle.exchanges.outgoing.where(receiver_id: d2).first } + + before do + p2.set_property 'Local', 'XYZ 123' + + ex_d1.variants << p1.variants.first + ex_d2.variants << p2.variants.first + + visit shops_path + end + + it "filters" do + toggle_filters + + toggle_filter 'Organic' + + expect(page).to have_content d1.name + expect(page).not_to have_content d2.name + + toggle_filter 'Organic' + toggle_filter 'Local' + + expect(page).not_to have_content d1.name + expect(page).to have_content d2.name + end + end + + describe "taxon badges" do + let!(:closed_oc) { create(:closed_order_cycle, distributors: [shop], variants: [p_closed.variants.first]) } + let!(:p_closed) { create(:simple_product, taxons: [taxon_closed]) } + let(:shop) { create(:distributor_enterprise) } + let(:taxon_closed) { create(:taxon, name: 'Closed') } + + describe "open shops" do + let!(:open_oc) { create(:open_order_cycle, distributors: [shop], variants: [p_open.variants.first]) } + let!(:p_open) { create(:simple_product, taxons: [taxon_open]) } + let(:taxon_open) { create(:taxon, name: 'Open') } + + it "shows taxons for open order cycles only" do + visit shops_path + expand_active_table_node shop.name + expect(page).to have_selector '.fat-taxons', text: 'Open' + expect(page).not_to have_selector '.fat-taxons', text: 'Closed' + end + end + + describe "closed shops" do + it "shows taxons for any order cycle" do + visit shops_path + click_link 'Show Closed Shops' + expand_active_table_node shop.name + expect(page).to have_selector '.fat-taxons', text: 'Closed' + end + end + end + describe "property badges" do let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) } let(:product) { create(:simple_product, supplier: producer) } diff --git a/spec/models/producer_property_spec.rb b/spec/models/producer_property_spec.rb index 2d0009ce12..0de7aae19b 100644 --- a/spec/models/producer_property_spec.rb +++ b/spec/models/producer_property_spec.rb @@ -8,7 +8,7 @@ describe ProducerProperty do producer.set_producer_property 'Organic Certified', 'NASAA 54321' end - describe ".sold_by" do + describe ".currently_sold_by and .ever_sold_by" do let!(:shop) { create(:distributor_enterprise) } let!(:oc) { create(:simple_order_cycle, distributors: [shop], variants: [product.variants.first]) } let(:product) { create(:simple_product, supplier: producer) } @@ -22,7 +22,8 @@ describe ProducerProperty do describe "with an associated producer property" do it "returns the producer property" do - expect(ProducerProperty.sold_by(shop)).to eq [pp] + expect(ProducerProperty.currently_sold_by(shop)).to eq [pp] + expect(ProducerProperty.ever_sold_by(shop)).to eq [pp] end end @@ -30,7 +31,8 @@ describe ProducerProperty do let!(:exchange) { create(:exchange, order_cycle: oc, incoming: true, sender: producer_other, receiver: oc.coordinator) } it "doesn't return the producer property" do - expect(ProducerProperty.sold_by(shop)).not_to include pp_other + expect(ProducerProperty.currently_sold_by(shop)).not_to include pp_other + expect(ProducerProperty.ever_sold_by(shop)).not_to include pp_other end end @@ -40,7 +42,8 @@ describe ProducerProperty do let!(:exchange) { create(:exchange, order_cycle: oc, incoming: false, sender: oc.coordinator, receiver: shop_other, variants: [product_other.variants.first]) } it "doesn't return the producer property" do - expect(ProducerProperty.sold_by(shop)).not_to include pp_other + expect(ProducerProperty.currently_sold_by(shop)).not_to include pp_other + expect(ProducerProperty.ever_sold_by(shop)).not_to include pp_other end end @@ -49,8 +52,12 @@ describe ProducerProperty do oc.update_attributes! orders_close_at: 1.week.ago end - it "doesn't return the producer property" do - expect(ProducerProperty.sold_by(shop)).not_to include pp + it "doesn't return the producer property for .currently_sold_by" do + expect(ProducerProperty.currently_sold_by(shop)).not_to include pp + end + + it "returns the producer property for .ever_sold_by" do + expect(ProducerProperty.ever_sold_by(shop)).to include pp end end @@ -59,7 +66,8 @@ describe ProducerProperty do let!(:oc) { create(:simple_order_cycle, distributors: [shop], variants: [product.variants.first, product2.variants.first]) } it "doesn't return duplicates" do - expect(ProducerProperty.sold_by(shop).to_a.count).to eq 1 + expect(ProducerProperty.currently_sold_by(shop).to_a.count).to eq 1 + expect(ProducerProperty.ever_sold_by(shop).to_a.count).to eq 1 end end end diff --git a/spec/models/spree/property_spec.rb b/spec/models/spree/property_spec.rb index 6e6ed2d780..29046a8782 100644 --- a/spec/models/spree/property_spec.rb +++ b/spec/models/spree/property_spec.rb @@ -31,42 +31,53 @@ module Spree end end - describe ".sold_by" do + describe ".currently_sold_by and .ever_sold_by" do let!(:shop) { create(:distributor_enterprise) } let!(:shop_other) { create(:distributor_enterprise) } let!(:product) { create(:simple_product) } let!(:product_other_ex) { create(:simple_product) } let!(:product_no_oc) { create(:simple_product) } - let!(:product_closed_oc) { create(:simple_product) } let!(:oc) { create(:simple_order_cycle, distributors: [shop], variants: [product.variants.first]) } - let!(:oc_closed) { create(:closed_order_cycle, distributors: [shop], variants: [product_closed_oc.variants.first]) } let!(:exchange_other_shop) { create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: shop_other, variants: [product_other_ex.variants.first]) } let(:property) { product.properties.last } let(:property_other_ex) { product_other_ex.properties.last } let(:property_no_oc) { product_no_oc.properties.last } - let(:property_closed_oc) { product_closed_oc.properties.last } before do product.set_property 'Organic', 'NASAA 12345' product_other_ex.set_property 'Biodynamic', 'ASDF 12345' product_no_oc.set_property 'Shiny', 'Very' - product_closed_oc.set_property 'Spiffy', 'Ooh yeah' end it "returns the property" do - expect(Property.sold_by(shop)).to eq [property] + expect(Property.currently_sold_by(shop)).to eq [property] + expect(Property.ever_sold_by(shop)).to eq [property] end it "doesn't return the property from another exchange" do - expect(Property.sold_by(shop)).not_to include property_other_ex + expect(Property.currently_sold_by(shop)).not_to include property_other_ex + expect(Property.ever_sold_by(shop)).not_to include property_other_ex end it "doesn't return the property with no order cycle" do - expect(Property.sold_by(shop)).not_to include property_no_oc + expect(Property.currently_sold_by(shop)).not_to include property_no_oc + expect(Property.ever_sold_by(shop)).not_to include property_no_oc end - it "doesn't return the property from a closed order cycle" do - expect(Property.sold_by(shop)).not_to include property_closed_oc + describe "closed order cyces" do + let!(:product_closed_oc) { create(:simple_product) } + let!(:oc_closed) { create(:closed_order_cycle, distributors: [shop], variants: [product_closed_oc.variants.first]) } + let(:property_closed_oc) { product_closed_oc.properties.last } + + before { product_closed_oc.set_property 'Spiffy', 'Ooh yeah' } + + it "doesn't return the property for .currently_sold_by" do + expect(Property.currently_sold_by(shop)).not_to include property_closed_oc + end + + it "returns the property for .ever_sold_by" do + expect(Property.ever_sold_by(shop)).to include property_closed_oc + end end context "with another product in the order cycle" do @@ -78,7 +89,8 @@ module Spree end it "doesn't return duplicates" do - expect(Property.sold_by(shop).to_a.count).to eq 1 + expect(Property.currently_sold_by(shop).to_a.count).to eq 1 + expect(Property.ever_sold_by(shop).to_a.count).to eq 1 end end end diff --git a/spec/models/spree/taxon_spec.rb b/spec/models/spree/taxon_spec.rb index 3a26c9b2aa..a031823bee 100644 --- a/spec/models/spree/taxon_spec.rb +++ b/spec/models/spree/taxon_spec.rb @@ -29,13 +29,18 @@ module Spree end end - describe "finding all distributed taxons" do - let!(:oc) { create(:simple_order_cycle, distributors: [e], variants: [p1.master]) } - let!(:s) { create(:supplier_enterprise) } - let!(:p1) { create(:simple_product, supplier: s, taxons: [t1, t2]) } + describe "finding distributed taxons" do + let!(:oc_open) { create(:open_order_cycle, distributors: [e], variants: [p_open.variants.first]) } + let!(:oc_closed) { create(:closed_order_cycle, distributors: [e], variants: [p_closed.variants.first]) } + let!(:p_open) { create(:simple_product, primary_taxon: t1) } + let!(:p_closed) { create(:simple_product, primary_taxon: t2) } - it "finds taxons" do - Taxon.distributed_taxons.should == {e.id => Set.new(p1.taxons.map(&:id))} + it "finds all distributed taxons" do + expect(Taxon.distributed_taxons(:all)).to eq({e.id => Set.new([t1.id, t2.id])}) + end + + it "finds currently distributed taxons" do + expect(Taxon.distributed_taxons(:current)).to eq({e.id => Set.new([t1.id])}) end end end diff --git a/spec/serializers/enterprise_serializer_spec.rb b/spec/serializers/enterprise_serializer_spec.rb index f5eff4f771..bea2672157 100644 --- a/spec/serializers/enterprise_serializer_spec.rb +++ b/spec/serializers/enterprise_serializer_spec.rb @@ -6,7 +6,8 @@ describe Api::EnterpriseSerializer do let(:taxon) { create(:taxon) } let(:data) { OpenStruct.new(earliest_closing_times: {}, active_distributors: [], - distributed_taxons: {enterprise.id => [123]}, + all_distributed_taxons: {enterprise.id => [123]}, + current_distributed_taxons: {enterprise.id => [123]}, supplied_taxons: {enterprise.id => [456]}, shipping_method_services: {}, relatives: {enterprise.id => {producers: [123], distributors: [456]}}) } From e8848451a54bf392211c773c6462aa2d897d1db2 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 28 Oct 2016 16:26:27 +1100 Subject: [PATCH 16/31] Spacing --- spec/models/enterprise_caching_spec.rb | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/spec/models/enterprise_caching_spec.rb b/spec/models/enterprise_caching_spec.rb index 72b356df21..58317ca2df 100644 --- a/spec/models/enterprise_caching_spec.rb +++ b/spec/models/enterprise_caching_spec.rb @@ -9,8 +9,9 @@ describe Enterprise do describe "with a supplied product" do let(:product) { create(:simple_product, supplier: enterprise) } let!(:classification) { create(:classification, taxon: taxon, product: product) } + it "touches enterprise when a classification on that product changes" do - expect{classification.save!}.to change{enterprise.updated_at} + expect { classification.save! }.to change { enterprise.updated_at } end end @@ -18,37 +19,41 @@ describe Enterprise do let(:product) { create(:simple_product) } let!(:oc) { create(:simple_order_cycle, distributors: [enterprise], variants: [product.master]) } let!(:classification) { create(:classification, taxon: taxon, product: product) } + it "touches enterprise when a classification on that product changes" do - expect{classification.save!}.to change{enterprise.reload.updated_at} + expect { classification.save! }.to change { enterprise.reload.updated_at } end end describe "with relatives" do let(:child_enterprise) { create(:supplier_enterprise) } let!(:er) { create(:enterprise_relationship, parent: enterprise, child: child_enterprise) } + it "touches enterprise when enterprise relationship is updated" do - expect{er.save!}.to change {enterprise.reload.updated_at } + expect { er.save! }.to change { enterprise.reload.updated_at } end end - + describe "with shipping methods" do let(:sm) { create(:shipping_method) } + before do enterprise.shipping_methods << sm end + it "touches enterprise when distributor_shipping_method is updated" do expect { enterprise.distributor_shipping_methods.first.save! - }.to change {enterprise.reload.updated_at} + }.to change { enterprise.reload.updated_at } end it "touches enterprise when shipping method is updated" do - expect{sm.save!}.to change {enterprise.reload.updated_at } + expect { sm.save! }.to change { enterprise.reload.updated_at } end end - + it "touches enterprise when address is updated" do - expect{enterprise.address.save!}.to change {enterprise.reload.updated_at } + expect{ enterprise.address.save! }.to change { enterprise.reload.updated_at } end end end From bd11c6ce14a7dc4d0c4fac5b4dfc608860346c8a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 28 Oct 2016 16:31:42 +1100 Subject: [PATCH 17/31] New hash style --- app/models/exchange.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/models/exchange.rb b/app/models/exchange.rb index 3d0e6417e6..ce152da6d6 100644 --- a/app/models/exchange.rb +++ b/app/models/exchange.rb @@ -2,18 +2,18 @@ class Exchange < ActiveRecord::Base acts_as_taggable belongs_to :order_cycle - belongs_to :sender, :class_name => 'Enterprise' - belongs_to :receiver, :class_name => 'Enterprise' - belongs_to :payment_enterprise, :class_name => 'Enterprise' + belongs_to :sender, class_name: 'Enterprise' + belongs_to :receiver, class_name: 'Enterprise' + belongs_to :payment_enterprise, class_name: 'Enterprise' - has_many :exchange_variants, :dependent => :destroy - has_many :variants, :through => :exchange_variants + has_many :exchange_variants, dependent: :destroy + has_many :variants, through: :exchange_variants - has_many :exchange_fees, :dependent => :destroy - has_many :enterprise_fees, :through => :exchange_fees + has_many :exchange_fees, dependent: :destroy + has_many :enterprise_fees, through: :exchange_fees validates_presence_of :order_cycle, :sender, :receiver - validates_uniqueness_of :sender_id, :scope => [:order_cycle_id, :receiver_id, :incoming] + validates_uniqueness_of :sender_id, scope: [:order_cycle_id, :receiver_id, :incoming] after_save :refresh_products_cache after_destroy :refresh_products_cache_from_destroy From 1ea4f4274c5daa7d663c26cf25ee2392c8f548b9 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 28 Oct 2016 16:33:27 +1100 Subject: [PATCH 18/31] Add enterprise cache invalidation for order cycle changes --- app/models/exchange.rb | 2 +- spec/models/enterprise_caching_spec.rb | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/models/exchange.rb b/app/models/exchange.rb index ce152da6d6..f60c7289b6 100644 --- a/app/models/exchange.rb +++ b/app/models/exchange.rb @@ -3,7 +3,7 @@ class Exchange < ActiveRecord::Base belongs_to :order_cycle belongs_to :sender, class_name: 'Enterprise' - belongs_to :receiver, class_name: 'Enterprise' + belongs_to :receiver, class_name: 'Enterprise', touch: true belongs_to :payment_enterprise, class_name: 'Enterprise' has_many :exchange_variants, dependent: :destroy diff --git a/spec/models/enterprise_caching_spec.rb b/spec/models/enterprise_caching_spec.rb index 58317ca2df..5eceba9160 100644 --- a/spec/models/enterprise_caching_spec.rb +++ b/spec/models/enterprise_caching_spec.rb @@ -17,12 +17,17 @@ describe Enterprise do describe "with a distributed product" do let(:product) { create(:simple_product) } - let!(:oc) { create(:simple_order_cycle, distributors: [enterprise], variants: [product.master]) } + let(:oc) { create(:simple_order_cycle, distributors: [enterprise], variants: [product.variants.first]) } let!(:classification) { create(:classification, taxon: taxon, product: product) } it "touches enterprise when a classification on that product changes" do + oc expect { classification.save! }.to change { enterprise.reload.updated_at } end + + it "touches enterprise when the product's variant is added to order cycle" do + expect { oc }.to change { enterprise.reload.updated_at } + end end describe "with relatives" do From d93fe3cf2cffabbb1e35934a3990bde2d64089ba Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 28 Oct 2016 16:34:01 +1100 Subject: [PATCH 19/31] Cache enterprise supplied and distributed taxons --- app/serializers/api/enterprise_serializer.rb | 31 ++++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index 3537bcdd24..c7b45937f6 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -23,7 +23,6 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer include SerializerHelper attributes :orders_close_at, :active - attributes :taxons, :supplied_taxons has_many :supplied_properties, serializer: Api::PropertySerializer has_many :distributed_properties, serializer: Api::PropertySerializer @@ -59,18 +58,6 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer OpenFoodNetwork::PropertyMerge.merge product_properties, producer_properties end - - def taxons - if active - ids_to_objs options[:data].current_distributed_taxons[object.id] - else - ids_to_objs options[:data].all_distributed_taxons[object.id] - end - end - - def supplied_taxons - ids_to_objs options[:data].supplied_taxons[object.id] - end end class Api::CachedEnterpriseSerializer < ActiveModel::Serializer @@ -90,6 +77,8 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer :email_address, :hash, :logo, :promo_image, :path, :pickup, :delivery, :icon, :icon_font, :producer_icon_font, :category, :producers, :hubs + attributes :taxons, :supplied_taxons + has_one :address, serializer: Api::AddressSerializer def pickup @@ -132,6 +121,22 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer ids_to_objs(relatives.andand[:distributors]) end + def taxons + if active + ids_to_objs options[:data].current_distributed_taxons[object.id] + else + ids_to_objs options[:data].all_distributed_taxons[object.id] + end + end + + def supplied_taxons + ids_to_objs options[:data].supplied_taxons[object.id] + end + + def active + options[:data].active_distributors.andand.include? object + end + # Map svg icons. def icon icons = { From 22080a9a0800b48427965473262d5db342988daa Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 28 Oct 2016 17:21:58 +1100 Subject: [PATCH 20/31] Property / ProducerProperty changes update supplier enterprise cache --- app/models/producer_property.rb | 2 +- app/models/spree/product_property_decorator.rb | 3 +++ spec/models/enterprise_caching_spec.rb | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/models/producer_property.rb b/app/models/producer_property.rb index b0bb1b6e51..a21b5eb5a3 100644 --- a/app/models/producer_property.rb +++ b/app/models/producer_property.rb @@ -1,5 +1,5 @@ class ProducerProperty < ActiveRecord::Base - belongs_to :producer, class_name: 'Enterprise' + belongs_to :producer, class_name: 'Enterprise', touch: true belongs_to :property, class_name: 'Spree::Property' default_scope order("#{self.table_name}.position") diff --git a/app/models/spree/product_property_decorator.rb b/app/models/spree/product_property_decorator.rb index 0f3329c03d..47414e92dc 100644 --- a/app/models/spree/product_property_decorator.rb +++ b/app/models/spree/product_property_decorator.rb @@ -1,8 +1,11 @@ module Spree ProductProperty.class_eval do + belongs_to :product, class_name: "Spree::Product", touch: true + after_save :refresh_products_cache after_destroy :refresh_products_cache + def refresh_products_cache product.refresh_products_cache end diff --git a/spec/models/enterprise_caching_spec.rb b/spec/models/enterprise_caching_spec.rb index 5eceba9160..65ca32bb21 100644 --- a/spec/models/enterprise_caching_spec.rb +++ b/spec/models/enterprise_caching_spec.rb @@ -9,10 +9,25 @@ describe Enterprise do describe "with a supplied product" do let(:product) { create(:simple_product, supplier: enterprise) } let!(:classification) { create(:classification, taxon: taxon, product: product) } + let(:property) { product.product_properties.last } + let(:producer_property) { enterprise.producer_properties.last } + + before do + product.set_property 'Organic', 'NASAA 12345' + enterprise.set_producer_property 'Biodynamic', 'ASDF 4321' + end it "touches enterprise when a classification on that product changes" do expect { classification.save! }.to change { enterprise.updated_at } end + + it "touches enterprise when a property on that product changes" do + expect { property.save! }.to change { enterprise.reload.updated_at } + end + + it "touches enterprise when a producer property on that product changes" do + expect { producer_property.save! }.to change { enterprise.reload.updated_at } + end end describe "with a distributed product" do From 9b656eaf4f3e951f60057dca5b9ebe21a1e0dccf Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 28 Oct 2016 17:26:35 +1100 Subject: [PATCH 21/31] Property / ProducerProperty changes update distributor enterprise cache --- app/models/enterprise.rb | 5 ++++ spec/models/enterprise_caching_spec.rb | 38 ++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 5460ecc94d..40642d12cb 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -87,6 +87,7 @@ class Enterprise < ActiveRecord::Base before_validation :set_unused_address_fields after_validation :geocode_address + after_touch :touch_distributors after_create :relate_to_owners_enterprises # TODO: Later versions of devise have a dedicated after_confirmation callback, so use that after_update :welcome_after_confirm, if: lambda { confirmation_token_changed? && confirmation_token.nil? } @@ -469,4 +470,8 @@ class Enterprise < ActiveRecord::Base def initialize_permalink self.permalink = Enterprise.find_available_permalink(name) end + + def touch_distributors + Enterprise.distributing_product(self.supplied_products).each(&:touch) + end end diff --git a/spec/models/enterprise_caching_spec.rb b/spec/models/enterprise_caching_spec.rb index 65ca32bb21..047fc6ab02 100644 --- a/spec/models/enterprise_caching_spec.rb +++ b/spec/models/enterprise_caching_spec.rb @@ -5,6 +5,7 @@ describe Enterprise do describe "is touched when a(n)" do let(:enterprise) { create(:distributor_enterprise, updated_at: 1.week.ago) } let(:taxon) { create(:taxon) } + let(:supplier2) { create(:supplier_enterprise) } describe "with a supplied product" do let(:product) { create(:simple_product, supplier: enterprise) } @@ -28,16 +29,47 @@ describe Enterprise do it "touches enterprise when a producer property on that product changes" do expect { producer_property.save! }.to change { enterprise.reload.updated_at } end + + it "touches enterprise when the supplier of a product changes" do + expect { + product.update_attributes!(supplier: supplier2) + }.to change { enterprise.updated_at } + end end describe "with a distributed product" do let(:product) { create(:simple_product) } let(:oc) { create(:simple_order_cycle, distributors: [enterprise], variants: [product.variants.first]) } + let(:supplier) { product.supplier } let!(:classification) { create(:classification, taxon: taxon, product: product) } + let(:property) { product.product_properties.last } + let(:producer_property) { supplier.producer_properties.last } - it "touches enterprise when a classification on that product changes" do - oc - expect { classification.save! }.to change { enterprise.reload.updated_at } + before do + product.set_property 'Organic', 'NASAA 12345' + supplier.set_producer_property 'Biodynamic', 'ASDF 4321' + end + + context "with an order cycle" do + before { oc } + + it "touches enterprise when a classification on that product changes" do + expect { classification.save! }.to change { enterprise.reload.updated_at } + end + + it "touches enterprise when a property on that product changes" do + expect { property.save! }.to change { enterprise.reload.updated_at } + end + + it "touches enterprise when a producer property on that product changes" do + expect { producer_property.save! }.to change { enterprise.reload.updated_at } + end + + it "touches enterprise when the supplier of a product changes" do + expect { + product.update_attributes!(supplier: supplier2) + }.to change { enterprise.reload.updated_at } + end end it "touches enterprise when the product's variant is added to order cycle" do From 6030e9a2941b58d2407d3e02b00a8e935a0a67a2 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 28 Oct 2016 17:27:07 +1100 Subject: [PATCH 22/31] Cache enterprise supplied and distributed properties and producer properties --- app/serializers/api/enterprise_serializer.rb | 55 ++++++++++---------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index c7b45937f6..e3829174a2 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -23,8 +23,6 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer include SerializerHelper attributes :orders_close_at, :active - has_many :supplied_properties, serializer: Api::PropertySerializer - has_many :distributed_properties, serializer: Api::PropertySerializer def orders_close_at options[:data].earliest_closing_times[object.id] @@ -33,31 +31,6 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer def active options[:data].active_distributors.andand.include? object end - - def supplied_properties - # This results in 3 queries per enterprise - product_properties = Spree::Property.applied_by(object) - producer_properties = object.properties - - OpenFoodNetwork::PropertyMerge.merge product_properties, producer_properties - end - - def distributed_properties - # This results in 3 queries per enterprise - - if active - product_properties = Spree::Property.currently_sold_by(object) - producer_property_ids = ProducerProperty.currently_sold_by(object).pluck(:property_id) - - else - product_properties = Spree::Property.ever_sold_by(object) - producer_property_ids = ProducerProperty.ever_sold_by(object).pluck(:property_id) - end - - producer_properties = Spree::Property.where(id: producer_property_ids) - - OpenFoodNetwork::PropertyMerge.merge product_properties, producer_properties - end end class Api::CachedEnterpriseSerializer < ActiveModel::Serializer @@ -81,6 +54,9 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer has_one :address, serializer: Api::AddressSerializer + has_many :supplied_properties, serializer: Api::PropertySerializer + has_many :distributed_properties, serializer: Api::PropertySerializer + def pickup services = options[:data].shipping_method_services[object.id] services ? services[:pickup] : false @@ -133,6 +109,31 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer ids_to_objs options[:data].supplied_taxons[object.id] end + def supplied_properties + # This results in 3 queries per enterprise + product_properties = Spree::Property.applied_by(object) + producer_properties = object.properties + + OpenFoodNetwork::PropertyMerge.merge product_properties, producer_properties + end + + def distributed_properties + # This results in 3 queries per enterprise + + if active + product_properties = Spree::Property.currently_sold_by(object) + producer_property_ids = ProducerProperty.currently_sold_by(object).pluck(:property_id) + + else + product_properties = Spree::Property.ever_sold_by(object) + producer_property_ids = ProducerProperty.ever_sold_by(object).pluck(:property_id) + end + + producer_properties = Spree::Property.where(id: producer_property_ids) + + OpenFoodNetwork::PropertyMerge.merge product_properties, producer_properties + end + def active options[:data].active_distributors.andand.include? object end From dabac50128d438ffe7aeef8176aad2f6b9299f47 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 28 Oct 2016 17:44:29 +1100 Subject: [PATCH 23/31] Replace Enterprise.distributing_product and Enterprise.distributing_any_product_of with Enterprise.distributing_products --- app/models/enterprise.rb | 9 ++------- app/models/spree/product_decorator.rb | 2 +- app/views/spree/products/_source.html.haml | 2 +- .../distribution_change_validator.rb | 4 ++-- .../distribution_change_validator_spec.rb | 12 ++++++------ spec/models/enterprise_spec.rb | 14 ++++++-------- 6 files changed, 18 insertions(+), 25 deletions(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 40642d12cb..05ec3596f2 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -162,12 +162,7 @@ class Enterprise < ActiveRecord::Base select('DISTINCT enterprises.*') } - scope :distributing_product, lambda { |product| - with_distributed_products_outer.with_order_cycles_and_exchange_variants_outer. - where('product_distributions.product_id = ? OR spree_variants.product_id = ?', product, product). - select('DISTINCT enterprises.*') - } - scope :distributing_any_product_of, lambda { |products| + scope :distributing_products, lambda { |products| with_distributed_products_outer.with_order_cycles_and_exchange_variants_outer. where('product_distributions.product_id IN (?) OR spree_variants.product_id IN (?)', products, products). select('DISTINCT enterprises.*') @@ -472,6 +467,6 @@ class Enterprise < ActiveRecord::Base end def touch_distributors - Enterprise.distributing_product(self.supplied_products).each(&:touch) + Enterprise.distributing_products(self.supplied_products).each(&:touch) end end diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index f8c177ceac..835a875dbf 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -215,7 +215,7 @@ Spree::Product.class_eval do end def touch_distributors - Enterprise.distributing_product(self).each(&:touch) + Enterprise.distributing_products(self).each(&:touch) end def add_primary_taxon_to_taxons diff --git a/app/views/spree/products/_source.html.haml b/app/views/spree/products/_source.html.haml index 24f66f2add..dfcd091d67 100644 --- a/app/views/spree/products/_source.html.haml +++ b/app/views/spree/products/_source.html.haml @@ -12,7 +12,7 @@ %tbody - order = current_order(false) - validator = DistributionChangeValidator.new(order) - - Enterprise.distributing_product(@product).each do |distributor| + - Enterprise.distributing_products(@product).each do |distributor| - if !order.nil? && distributor == order.distributor %tr.odd %td diff --git a/lib/open_food_network/distribution_change_validator.rb b/lib/open_food_network/distribution_change_validator.rb index 6f7edc2c0f..ba6d21b247 100644 --- a/lib/open_food_network/distribution_change_validator.rb +++ b/lib/open_food_network/distribution_change_validator.rb @@ -1,5 +1,5 @@ class DistributionChangeValidator - + def initialize order @order = order end @@ -29,7 +29,7 @@ class DistributionChangeValidator end def available_distributors_for(product) - distributors = Enterprise.distributing_product(product) + distributors = Enterprise.distributing_products(product) if @order.andand.line_items.present? distributors = available_distributors(distributors) diff --git a/spec/lib/open_food_network/distribution_change_validator_spec.rb b/spec/lib/open_food_network/distribution_change_validator_spec.rb index 7e2a4b3274..8e324f961e 100644 --- a/spec/lib/open_food_network/distribution_change_validator_spec.rb +++ b/spec/lib/open_food_network/distribution_change_validator_spec.rb @@ -87,7 +87,7 @@ describe DistributionChangeValidator do enterprise_with_some_variants.stub(:distributed_variants) { [variant1, variant3] } # Only some variants enterprise_with_some_plus_extras = double(:enterprise) enterprise_with_some_plus_extras.stub(:distributed_variants) { [variant1, variant2, variant3, variant4] } # Only some variants, plus extras - + subject.available_distributors([enterprise_with_some_variants]).should_not include enterprise_with_some_variants subject.available_distributors([enterprise_with_some_plus_extras]).should_not include enterprise_with_some_plus_extras end @@ -97,10 +97,10 @@ describe DistributionChangeValidator do order.stub(:line_item_variants) { line_item_variants } enterprise = double(:enterprise) enterprise.stub(:distributed_variants) { [variant1, variant2, variant3, variant4, variant5] } # Excess variants - + subject.available_distributors([enterprise]).should == [enterprise] end - + it "matches no enterprises when none are provided" do subject.available_distributors([]).should == [] end @@ -201,7 +201,7 @@ describe DistributionChangeValidator do describe "finding available distributors for a product" do it "returns enterprises distributing the product when there's no order" do subject = DistributionChangeValidator.new(nil) - Enterprise.stub(:distributing_product).and_return([1, 2, 3]) + Enterprise.stub(:distributing_products).and_return([1, 2, 3]) subject.should_receive(:available_distributors).never subject.available_distributors_for(product).should == [1, 2, 3] @@ -209,7 +209,7 @@ describe DistributionChangeValidator do it "returns enterprises distributing the product when there's no order items" do order.stub(:line_items) { [] } - Enterprise.stub(:distributing_product).and_return([1, 2, 3]) + Enterprise.stub(:distributing_products).and_return([1, 2, 3]) subject.should_receive(:available_distributors).never subject.available_distributors_for(product).should == [1, 2, 3] @@ -217,7 +217,7 @@ describe DistributionChangeValidator do it "filters by available distributors when there are order items" do order.stub(:line_items) { [1, 2, 3] } - Enterprise.stub(:distributing_product).and_return([1, 2, 3]) + Enterprise.stub(:distributing_products).and_return([1, 2, 3]) subject.should_receive(:available_distributors).and_return([2]) subject.available_distributors_for(product).should == [2] diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index b8360094ec..8c16ac39eb 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -542,40 +542,38 @@ describe Enterprise do end end - describe "distributing_product" do + describe "distributing_products" do it "returns enterprises distributing via a product distribution" do d = create(:distributor_enterprise) p = create(:product, distributors: [d]) - Enterprise.distributing_product(p).should == [d] + Enterprise.distributing_products(p).should == [d] end it "returns enterprises distributing via an order cycle" do d = create(:distributor_enterprise) p = create(:product) oc = create(:simple_order_cycle, distributors: [d], variants: [p.master]) - Enterprise.distributing_product(p).should == [d] + Enterprise.distributing_products(p).should == [d] end - end - describe "distributing_any_product_of" do it "returns enterprises distributing via a product distribution" do d = create(:distributor_enterprise) p = create(:product, distributors: [d]) - Enterprise.distributing_any_product_of([p]).should == [d] + Enterprise.distributing_products([p]).should == [d] end it "returns enterprises distributing via an order cycle" do d = create(:distributor_enterprise) p = create(:product) oc = create(:simple_order_cycle, distributors: [d], variants: [p.master]) - Enterprise.distributing_any_product_of([p]).should == [d] + Enterprise.distributing_products([p]).should == [d] end it "does not return duplicate enterprises" do d = create(:distributor_enterprise) p1 = create(:product, distributors: [d]) p2 = create(:product, distributors: [d]) - Enterprise.distributing_any_product_of([p1, p2]).should == [d] + Enterprise.distributing_products([p1, p2]).should == [d] end end From 44a301edb1e691ed02a819ab50b0ba38f5aae655 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 4 Nov 2016 16:04:13 +1100 Subject: [PATCH 24/31] When touching distributing enterprises, do not touch self -> infinite recursion --- app/models/enterprise.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 05ec3596f2..840477185f 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -467,6 +467,8 @@ class Enterprise < ActiveRecord::Base end def touch_distributors - Enterprise.distributing_products(self.supplied_products).each(&:touch) + Enterprise.distributing_products(self.supplied_products). + where('enterprises.id != ?', self.id). + each(&:touch) end end From c4318030d31d0a8074ac951544c33d6f7a99dfc8 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 4 Nov 2016 16:52:43 +1100 Subject: [PATCH 25/31] Fix spec: Second visit doesn't actually reload the page --- spec/features/consumer/shops_spec.rb | 169 ++++++++++++++------------- 1 file changed, 85 insertions(+), 84 deletions(-) diff --git a/spec/features/consumer/shops_spec.rb b/spec/features/consumer/shops_spec.rb index f8f54a197b..c9e8063e72 100644 --- a/spec/features/consumer/shops_spec.rb +++ b/spec/features/consumer/shops_spec.rb @@ -55,109 +55,110 @@ feature 'Shops', js: true do follow_active_table_node distributor.name expect(page).to have_current_path enterprise_shop_path(distributor) end + end - describe "filtering by product property" do - let!(:order_cycle) { create(:simple_order_cycle, distributors: [d1, d2], coordinator: create(:distributor_enterprise)) } - let!(:p1) { create(:simple_product, supplier: producer) } - let!(:p2) { create(:simple_product, supplier: create(:supplier_enterprise)) } - let(:ex_d1) { order_cycle.exchanges.outgoing.where(receiver_id: d1).first } - let(:ex_d2) { order_cycle.exchanges.outgoing.where(receiver_id: d2).first } + describe "filtering by product property" do + let!(:order_cycle) { create(:simple_order_cycle, distributors: [d1, d2], coordinator: create(:distributor_enterprise)) } + let!(:p1) { create(:simple_product, supplier: producer) } + let!(:p2) { create(:simple_product, supplier: create(:supplier_enterprise)) } + let(:ex_d1) { order_cycle.exchanges.outgoing.where(receiver_id: d1).first } + let(:ex_d2) { order_cycle.exchanges.outgoing.where(receiver_id: d2).first } - before do - p2.set_property 'Local', 'XYZ 123' + before do + ex_d1.variants << p1.variants.first + ex_d2.variants << p2.variants.first - ex_d1.variants << p1.variants.first - ex_d2.variants << p2.variants.first + p2.set_property 'Local', 'XYZ 123' + visit shops_path + end + + it "filters" do + toggle_filters + + toggle_filter 'Organic' + + expect(page).to have_content d1.name + expect(page).not_to have_content d2.name + + toggle_filter 'Organic' + toggle_filter 'Local' + + expect(page).not_to have_content d1.name + expect(page).to have_content d2.name + end + end + + describe "taxon badges" do + let!(:closed_oc) { create(:closed_order_cycle, distributors: [shop], variants: [p_closed.variants.first]) } + let!(:p_closed) { create(:simple_product, taxons: [taxon_closed]) } + let(:shop) { create(:distributor_enterprise) } + let(:taxon_closed) { create(:taxon, name: 'Closed') } + + describe "open shops" do + let!(:open_oc) { create(:open_order_cycle, distributors: [shop], variants: [p_open.variants.first]) } + let!(:p_open) { create(:simple_product, taxons: [taxon_open]) } + let(:taxon_open) { create(:taxon, name: 'Open') } + + it "shows taxons for open order cycles only" do visit shops_path - end - - it "filters" do - toggle_filters - - toggle_filter 'Organic' - - expect(page).to have_content d1.name - expect(page).not_to have_content d2.name - - toggle_filter 'Organic' - toggle_filter 'Local' - - expect(page).not_to have_content d1.name - expect(page).to have_content d2.name + expand_active_table_node shop.name + expect(page).to have_selector '.fat-taxons', text: 'Open' + expect(page).not_to have_selector '.fat-taxons', text: 'Closed' end end - describe "taxon badges" do - let!(:closed_oc) { create(:closed_order_cycle, distributors: [shop], variants: [p_closed.variants.first]) } - let!(:p_closed) { create(:simple_product, taxons: [taxon_closed]) } - let(:shop) { create(:distributor_enterprise) } - let(:taxon_closed) { create(:taxon, name: 'Closed') } - - describe "open shops" do - let!(:open_oc) { create(:open_order_cycle, distributors: [shop], variants: [p_open.variants.first]) } - let!(:p_open) { create(:simple_product, taxons: [taxon_open]) } - let(:taxon_open) { create(:taxon, name: 'Open') } - - it "shows taxons for open order cycles only" do - visit shops_path - expand_active_table_node shop.name - expect(page).to have_selector '.fat-taxons', text: 'Open' - expect(page).not_to have_selector '.fat-taxons', text: 'Closed' - end - end - - describe "closed shops" do - it "shows taxons for any order cycle" do - visit shops_path - click_link 'Show Closed Shops' - expand_active_table_node shop.name - expect(page).to have_selector '.fat-taxons', text: 'Closed' - end + describe "closed shops" do + it "shows taxons for any order cycle" do + visit shops_path + click_link 'Show Closed Shops' + expand_active_table_node shop.name + expect(page).to have_selector '.fat-taxons', text: 'Closed' end end + end - describe "property badges" do - let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) } - let(:product) { create(:simple_product, supplier: producer) } + describe "property badges" do + let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) } + let(:product) { create(:simple_product, supplier: producer) } - before do - product.set_property 'Local', 'XYZ 123' - end + before do + product.set_property 'Local', 'XYZ 123' + end - it "shows property badges" do - # Given a shop with a product with a property - # And the product's producer has a producer property + it "shows property badges" do + # Given a shop with a product with a property + # And the product's producer has a producer property - # When I go to the shops path - visit shops_path + # When I go to the shops path + visit shops_path - # And I open the shop - expand_active_table_node distributor.name + # And I open the shop + expand_active_table_node distributor.name - # Then I should see both properties - expect(page).to have_content 'Local' # Product property + # Then I should see both properties + expect(page).to have_content 'Local' # Product property + expect(page).to have_content 'Organic' # Producer property + end + end + + describe "hub producer modal" do + let!(:product) { create(:simple_product, supplier: producer, taxons: [taxon]) } + let!(:taxon) { create(:taxon, name: 'Fruit') } + let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) } + + it "shows hub producer modals" do + visit shops_path + expand_active_table_node distributor.name + expect(page).to have_content producer.name + open_enterprise_modal producer + modal_should_be_open_for producer + + within ".reveal-modal" do + expect(page).to have_content 'Fruit' # Taxon expect(page).to have_content 'Organic' # Producer property end end - - describe "hub producer modal" do - let!(:product) { create(:simple_product, supplier: producer, taxons: [taxon]) } - let!(:taxon) { create(:taxon, name: 'Fruit') } - let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product.variants.first]) } - - it "shows hub producer modals" do - expand_active_table_node distributor.name - expect(page).to have_content producer.name - open_enterprise_modal producer - modal_should_be_open_for producer - - within ".reveal-modal" do - expect(page).to have_content 'Fruit' # Taxon - expect(page).to have_content 'Organic' # Producer property - end - end - end end describe "viewing closed shops by URL" do From da9a3ce9f33d578ed32834c918f1665e4fcea9a4 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 25 Nov 2016 10:41:04 +1100 Subject: [PATCH 26/31] Refresh products cache when product is deleted (cf. destroyed) --- app/models/spree/product_decorator.rb | 6 ++++-- lib/open_food_network/products_cache.rb | 9 +++++++++ .../open_food_network/products_cache_spec.rb | 20 +++++++++++++++++++ spec/models/spree/product_spec.rb | 5 +++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index 835a875dbf..d7f5651a3c 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -187,9 +187,11 @@ Spree::Product.class_eval do def delete_with_delete_from_order_cycles transaction do - delete_without_delete_from_order_cycles + OpenFoodNetwork::ProductsCache.product_deleted(self) do + ExchangeVariant.where('exchange_variants.variant_id IN (?)', self.variants_including_master_and_deleted).destroy_all - ExchangeVariant.where('exchange_variants.variant_id IN (?)', self.variants_including_master_and_deleted).destroy_all + delete_without_delete_from_order_cycles + end end end alias_method_chain :delete, :delete_from_order_cycles diff --git a/lib/open_food_network/products_cache.rb b/lib/open_food_network/products_cache.rb index f6ef15829f..fc3caec111 100644 --- a/lib/open_food_network/products_cache.rb +++ b/lib/open_food_network/products_cache.rb @@ -28,6 +28,15 @@ module OpenFoodNetwork end end + def self.product_deleted(product, &block) + exchanges = exchanges_featuring_variants(product.reload.variants).to_a + + block.call + + exchanges.each do |exchange| + refresh_cache exchange.receiver, exchange.order_cycle + end + end def self.variant_override_changed(variant_override) exchanges_featuring_variants(variant_override.variant, distributor: variant_override.hub).each do |exchange| diff --git a/spec/lib/open_food_network/products_cache_spec.rb b/spec/lib/open_food_network/products_cache_spec.rb index a395873f45..d48f131087 100644 --- a/spec/lib/open_food_network/products_cache_spec.rb +++ b/spec/lib/open_food_network/products_cache_spec.rb @@ -89,6 +89,26 @@ module OpenFoodNetwork end end + describe "when a product is deleted" do + let(:product) { create(:simple_product) } + let(:variant) { create(:variant, product: product) } + let(:distributor) { create(:distributor_enterprise) } + let!(:oc) { create(:open_order_cycle, distributors: [distributor], variants: [variant]) } + + it "refreshes the cache based on exchanges the variant was in before destruction" do + expect(ProductsCache).to receive(:refresh_cache).with(distributor, oc) + product.delete + end + + it "performs the cache refresh after the product has been removed from the order cycle" do + expect(ProductsCache).to receive(:refresh_cache).with(distributor, oc) do + expect(product.reload.deleted_at).not_to be_nil + end + + product.delete + end + end + describe "when a variant override changes" do let(:variant) { create(:variant) } let(:d1) { create(:distributor_enterprise) } diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 2e188244a9..4d126bd80f 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -170,6 +170,11 @@ module Spree product.save end + it "refreshes the products cache on delete" do + expect(OpenFoodNetwork::ProductsCache).to receive(:product_deleted).with(product) + product.delete + end + # On destroy, all distributed variants are refreshed by a Variant around_destroy # callback, so we don't need to do anything on the product model. end From 57363e2da55878add81c794a510ff7e72e7541d0 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 25 Nov 2016 10:53:08 +1100 Subject: [PATCH 27/31] When a product is deleted, touch the supplier and distributors --- app/models/spree/product_decorator.rb | 4 ++++ spec/models/spree/product_spec.rb | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index d7f5651a3c..40c6a6910b 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -188,6 +188,10 @@ Spree::Product.class_eval do def delete_with_delete_from_order_cycles transaction do OpenFoodNetwork::ProductsCache.product_deleted(self) do + # Touch supplier and distributors as we would on #destroy + self.supplier.touch + touch_distributors + ExchangeVariant.where('exchange_variants.variant_id IN (?)', self.variants_including_master_and_deleted).destroy_all delete_without_delete_from_order_cycles diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 4d126bd80f..568d701ec9 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -177,6 +177,21 @@ module Spree # On destroy, all distributed variants are refreshed by a Variant around_destroy # callback, so we don't need to do anything on the product model. + + describe "touching affected enterprises when the product is deleted" do + let(:product) { create(:simple_product) } + let(:supplier) { product.supplier } + let(:distributor) { create(:distributor_enterprise) } + let!(:oc) { create(:simple_order_cycle, distributors: [distributor], variants: [product.variants.first]) } + + it "touches the supplier" do + expect { product.delete }.to change { supplier.reload.updated_at } + end + + it "touches all distributors" do + expect { product.delete }.to change { distributor.reload.updated_at } + end + end end describe "scopes" do From 6795237a2d61390bc2f0d82f4252e8eddbe66dc2 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 25 Nov 2016 11:18:39 +1100 Subject: [PATCH 28/31] Put timecop into safe mode and fix leaky Timecop.freeze --- Gemfile.lock | 2 +- config/initializers/timecop.rb | 1 + spec/models/spree/product_spec.rb | 7 ++++--- 3 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 config/initializers/timecop.rb diff --git a/Gemfile.lock b/Gemfile.lock index d346f7aa03..0dc7a067ee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -594,7 +594,7 @@ GEM ref thor (0.19.1) tilt (1.4.1) - timecop (0.6.2.2) + timecop (0.8.1) timers (1.1.0) treetop (1.4.15) polyglot diff --git a/config/initializers/timecop.rb b/config/initializers/timecop.rb new file mode 100644 index 0000000000..f14c7d622d --- /dev/null +++ b/config/initializers/timecop.rb @@ -0,0 +1 @@ +Timecop.safe_mode = true diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 568d701ec9..4f63fe882b 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -33,9 +33,10 @@ module Spree end it "defaults available_on to now" do - Timecop.freeze - product = Product.new - product.available_on.should == Time.zone.now + Timecop.freeze do + product = Product.new + product.available_on.should == Time.zone.now + end end describe "tax category" do From 071ba5285d31d0e5167d81ad4c0801eb797e58c1 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 25 Nov 2016 14:28:05 +1100 Subject: [PATCH 29/31] Put timecop config in spec support --- {config/initializers => spec/support}/timecop.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {config/initializers => spec/support}/timecop.rb (100%) diff --git a/config/initializers/timecop.rb b/spec/support/timecop.rb similarity index 100% rename from config/initializers/timecop.rb rename to spec/support/timecop.rb From d4fd66461e65edcd92d5cecdd30147f85c5bd437 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 25 Nov 2016 14:37:47 +1100 Subject: [PATCH 30/31] Add retry to flaky specs --- spec/features/consumer/shopping/checkout_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 7bd540169f..16bdb27ec1 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -feature "As a consumer I want to check out my cart", js: true do +feature "As a consumer I want to check out my cart", js: true, retry: 3 do include AuthenticationWorkflow include ShopWorkflow include CheckoutWorkflow From d0509b54bfb4e2802659b8c17de14fce7cd74f32 Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Wed, 30 Nov 2016 18:30:20 +1100 Subject: [PATCH 31/31] Disabling override the adds coupon field to cart page --- .../orders/edit/promo_cart_coupon_code_field.html.haml.deface | 1 + 1 file changed, 1 insertion(+) create mode 100644 app/overrides/spree/orders/edit/promo_cart_coupon_code_field.html.haml.deface diff --git a/app/overrides/spree/orders/edit/promo_cart_coupon_code_field.html.haml.deface b/app/overrides/spree/orders/edit/promo_cart_coupon_code_field.html.haml.deface new file mode 100644 index 0000000000..dca5725b49 --- /dev/null +++ b/app/overrides/spree/orders/edit/promo_cart_coupon_code_field.html.haml.deface @@ -0,0 +1 @@ +/ disabled