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/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/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 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/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/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index f5d11fda58..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 @@ -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 = [] @@ -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/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/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/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/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/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/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/customers/services/customers.js.coffee b/app/assets/javascripts/admin/resources/services/customers.js.coffee similarity index 56% rename from app/assets/javascripts/admin/customers/services/customers.js.coffee rename to app/assets/javascripts/admin/resources/services/customers.js.coffee index f4c6f6f3cd..3783097f56 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,10 +27,17 @@ 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) + @clear() + 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 @@ -33,3 +45,5 @@ angular.module("admin.customers").factory "Customers", ($q, InfoDialog, RequestM "#{addressType}_attributes": address CustomerResource.update params + clear: -> + @all.length = 0 diff --git a/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee b/app/assets/javascripts/admin/resources/services/enterprises.js.coffee similarity index 74% rename from app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee rename to app/assets/javascripts/admin/resources/services/enterprises.js.coffee index 44dfb503a5..0b7fa6e870 100644 --- a/app/assets/javascripts/admin/enterprises/services/enterprises.js.coffee +++ b/app/assets/javascripts/admin/resources/services/enterprises.js.coffee @@ -1,16 +1,18 @@ -angular.module("admin.enterprises").factory 'Enterprises', ($q, EnterpriseResource, blankOption) -> +angular.module("admin.resources").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 - @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/line_items/services/line_items.js.coffee b/app/assets/javascripts/admin/resources/services/line_items.js.coffee similarity index 81% 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 e78389c559..182ef81e6a 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,23 +1,25 @@ -angular.module("admin.lineItems").factory 'LineItems', ($q, LineItemResource) -> +angular.module("admin.resources").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 - @pristineByID[lineItem.id] = angular.copy(lineItem) - + @load(data) (callback || angular.noop)(data) resetData: -> - @lineItemsByID = {} + @byID = {} @pristineByID = {} + load: (lineItems) -> + @resetData() + for lineItem in lineItems + @byID[lineItem.id] = lineItem + @pristineByID[lineItem.id] = angular.copy(lineItem) + 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 +36,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 +56,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/resources/services/order_cycles.js.coffee similarity index 63% 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 a182f19f12..728b173e35 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,21 +1,24 @@ -angular.module("admin.orderCycles").factory 'OrderCycles', ($q, OrderCycleResource, blankOption) -> +angular.module("admin.resources").factory 'OrderCycles', ($q, $injector, OrderCycleResource) -> new class OrderCycles - orderCyclesByID: {} + all: [] + byID: {} pristineByID: {} + constructor: -> + if $injector.has('orderCycles') + @load($injector.get('orderCycles')) + index: (params={}, callback=null) -> - includeBlank = !!params['includeBlank'] - delete params['includeBlank'] - OrderCycleResource.index(params, (data) => - for orderCycle in data - @orderCyclesByID[orderCycle.id] = orderCycle - @pristineByID[orderCycle.id] = angular.copy(orderCycle) - + OrderCycleResource.index params, (data) => + @load(data) (callback || angular.noop)(data) - - data.unshift(blankOption()) if includeBlank 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/orders/services/orders.js.coffee b/app/assets/javascripts/admin/resources/services/orders.js.coffee similarity index 76% rename from app/assets/javascripts/admin/orders/services/orders.js.coffee rename to app/assets/javascripts/admin/resources/services/orders.js.coffee index a7b5bc1b68..da3f409149 100644 --- a/app/assets/javascripts/admin/orders/services/orders.js.coffee +++ b/app/assets/javascripts/admin/resources/services/orders.js.coffee @@ -1,16 +1,18 @@ -angular.module("admin.orders").factory 'Orders', ($q, OrderResource) -> +angular.module("admin.resources").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 - @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}) 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) 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 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/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/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/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/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/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/enterprise.rb b/app/models/enterprise.rb index 5460ecc94d..840477185f 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? } @@ -161,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.*') @@ -469,4 +465,10 @@ class Enterprise < ActiveRecord::Base def initialize_permalink self.permalink = Enterprise.find_available_permalink(name) end + + def touch_distributors + Enterprise.distributing_products(self.supplied_products). + where('enterprises.id != ?', self.id). + each(&:touch) + end end diff --git a/app/models/exchange.rb b/app/models/exchange.rb index 3d0e6417e6..f60c7289b6 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', touch: true + 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 diff --git a/app/models/producer_property.rb b/app/models/producer_property.rb index bf1f083936..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") @@ -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/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/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index f8c177ceac..40c6a6910b 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -187,9 +187,15 @@ 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 + # 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 + 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 @@ -215,7 +221,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/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/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/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 diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index f68c8be679..e3829174a2 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -20,9 +20,9 @@ class Api::EnterpriseSerializer < ActiveModel::Serializer end 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] @@ -31,26 +31,11 @@ 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 - product_properties = Spree::Property.sold_by(object) - ids = ProducerProperty.sold_by(object).pluck(:property_id) - producer_properties = Spree::Property.where(id: ids) - - OpenFoodNetwork::PropertyMerge.merge product_properties, producer_properties - end end class Api::CachedEnterpriseSerializer < ActiveModel::Serializer + include SerializerHelper + cached #delegate :cache_key, to: :object @@ -68,13 +53,9 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer 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 + has_many :supplied_properties, serializer: Api::PropertySerializer + has_many :distributed_properties, serializer: Api::PropertySerializer def pickup services = options[:data].shipping_method_services[object.id] @@ -116,6 +97,47 @@ 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 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 + # Map svg icons. def icon icons = { @@ -153,12 +175,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/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/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/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/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? 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/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 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/config/locales/en.yml b/config/locales/en.yml index ef8245bfd1..6f04c26284 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/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/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/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/features/admin/overview_spec.rb b/spec/features/admin/overview_spec.rb index 590c8528e8..30f2cc0336 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) } @@ -123,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 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/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 diff --git a/spec/features/consumer/shops_spec.rb b/spec/features/consumer/shops_spec.rb index 2a65a73aef..c9e8063e72 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 @@ -55,46 +55,108 @@ feature 'Shops', js: true do follow_active_table_node distributor.name expect(page).to have_current_path enterprise_shop_path(distributor) 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 "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 - product.set_property 'Local', 'XYZ 123' - end + before do + ex_d1.variants << p1.variants.first + ex_d2.variants << p2.variants.first - it "shows property badges" do - # Given a shop with a product with a property - # And the product's producer has a producer property + p2.set_property 'Local', 'XYZ 123' - # When I go to the shops path + 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 - - # 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 - expect(page).to have_content 'Organic' # Producer property + 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 "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]) } + 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 - 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 + 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) } - within ".reveal-modal" do - expect(page).to have_content 'Fruit' # Taxon - expect(page).to have_content 'Organic' # Producer property - 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 + + # When I go to the shops path + visit shops_path + + # 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 + 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 end 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] 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 aebdb19f6f..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] } @@ -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", -> 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] } 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/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/enterprise_caching_spec.rb b/spec/models/enterprise_caching_spec.rb index 72b356df21..047fc6ab02 100644 --- a/spec/models/enterprise_caching_spec.rb +++ b/spec/models/enterprise_caching_spec.rb @@ -5,50 +5,107 @@ 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) } 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} + 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 + + 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.master]) } + let(:oc) { create(:simple_order_cycle, distributors: [enterprise], variants: [product.variants.first]) } + let(:supplier) { product.supplier } 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} + let(:property) { product.product_properties.last } + let(:producer_property) { supplier.producer_properties.last } + + 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 + expect { oc }.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 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 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/product_spec.rb b/spec/models/spree/product_spec.rb index 2e188244a9..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 @@ -170,8 +171,28 @@ 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. + + 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 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 8fb4b22dc5..a031823bee 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) @@ -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]}}) } diff --git a/spec/support/timecop.rb b/spec/support/timecop.rb new file mode 100644 index 0000000000..f14c7d622d --- /dev/null +++ b/spec/support/timecop.rb @@ -0,0 +1 @@ +Timecop.safe_mode = true