diff --git a/app/assets/javascripts/admin/index_utils/services/paged_fetcher.js.coffee b/app/assets/javascripts/admin/index_utils/services/paged_fetcher.js.coffee index 82487996ae..96836ef9cc 100644 --- a/app/assets/javascripts/admin/index_utils/services/paged_fetcher.js.coffee +++ b/app/assets/javascripts/admin/index_utils/services/paged_fetcher.js.coffee @@ -2,19 +2,24 @@ angular.module("admin.indexUtils").factory "PagedFetcher", (dataFetcher) -> new class PagedFetcher # Given a URL like http://example.com/foo?page=::page::&per_page=20 # And the response includes an attribute pages with the number of pages to fetch - # Fetch each page async, and call the processData callback with the resulting data - fetch: (url, processData, onLastPageComplete) -> - dataFetcher(@urlForPage(url, 1)).then (data) => - processData data + # Fetch each page async, and call the pageCallback callback with the resulting data + # Developer note: this class should not be re-used! + page: 1 + last_page: 1 - if data.pages > 1 - for page in [2..data.pages] - lastPromise = dataFetcher(@urlForPage(url, page)).then (data) -> - processData data - onLastPageComplete && lastPromise.then onLastPageComplete - return - else - onLastPageComplete && onLastPageComplete() + fetch: (url, pageCallback) -> + @fetchPages(url, @page, pageCallback) urlForPage: (url, page) -> url.replace("::page::", page) + + fetchPages: (url, page, pageCallback) -> + dataFetcher(@urlForPage(url, page)).then (data) => + @page++ + @last_page = data.pages + + pageCallback(data) if pageCallback + + if @page <= @last_page + @fetchPages(url, @page, pageCallback) + diff --git a/app/assets/javascripts/admin/variant_overrides/controllers/variant_overrides_controller.js.coffee b/app/assets/javascripts/admin/variant_overrides/controllers/variant_overrides_controller.js.coffee index c37ba72071..b890d2ffd5 100644 --- a/app/assets/javascripts/admin/variant_overrides/controllers/variant_overrides_controller.js.coffee +++ b/app/assets/javascripts/admin/variant_overrides/controllers/variant_overrides_controller.js.coffee @@ -43,12 +43,11 @@ angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl", $scope.fetchProducts = -> url = "/api/products/overridable?page=::page::;per_page=100" - PagedFetcher.fetch url, (data) => $scope.addProducts data.products + PagedFetcher.fetch url, $scope.addProducts - - $scope.addProducts = (products) -> - $scope.products = $scope.products.concat products - VariantOverrides.ensureDataFor hubs, products + $scope.addProducts = (data) -> + $scope.products = $scope.products.concat data.products + VariantOverrides.ensureDataFor hubs, data.products $scope.displayDirty = -> if DirtyVariantOverrides.count() > 0 diff --git a/app/controllers/api/products_controller.rb b/app/controllers/api/products_controller.rb index a45e9a8aea..854d820a47 100644 --- a/app/controllers/api/products_controller.rb +++ b/app/controllers/api/products_controller.rb @@ -69,12 +69,12 @@ module Api end def overridable - producers = OpenFoodNetwork::Permissions.new(current_api_user). - variant_override_producers.by_name + producer_ids = OpenFoodNetwork::Permissions.new(current_api_user). + variant_override_producers.by_name.select('enterprises.id') - @products = paged_products_for_producers producers + @products = paged_products_for_producers producer_ids - render_paged_products @products + render_paged_products @products, ::Api::Admin::ProductSimpleSerializer end # POST /api/products/:product_id/clone @@ -118,19 +118,20 @@ module Api ] end - def paged_products_for_producers(producers) + def paged_products_for_producers(producer_ids) Spree::Product.scoped. merge(product_scope). - where(supplier_id: producers). + includes(variants: [:product, :default_price, :stock_items]). + where(supplier_id: producer_ids). by_producer.by_name. ransack(params[:q]).result. page(params[:page]).per(params[:per_page]) end - def render_paged_products(products) + def render_paged_products(products, product_serializer = ::Api::Admin::ProductSerializer) serializer = ActiveModel::ArraySerializer.new( products, - each_serializer: ::Api::Admin::ProductSerializer + each_serializer: product_serializer ) render text: { diff --git a/app/models/spree/stock/quantifier.rb b/app/models/spree/stock/quantifier.rb new file mode 100644 index 0000000000..8c437dc580 --- /dev/null +++ b/app/models/spree/stock/quantifier.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Spree + module Stock + class Quantifier + attr_reader :stock_items + + def initialize(variant) + @variant = variant + @stock_items = fetch_stock_items + end + + def total_on_hand + stock_items.sum(&:count_on_hand) + end + + def backorderable? + stock_items.any?(&:backorderable) + end + + def can_supply?(required) + total_on_hand >= required || backorderable? + end + + private + + def fetch_stock_items + # Don't re-fetch associated stock items from the DB if we've already eager-loaded them + return @variant.stock_items.to_a if @variant.stock_items.loaded? + + Spree::StockItem.joins(:stock_location). + where(:variant_id => @variant, Spree::StockLocation.table_name => { active: true }) + end + end + end +end diff --git a/app/serializers/api/admin/product_simple_serializer.rb b/app/serializers/api/admin/product_simple_serializer.rb new file mode 100644 index 0000000000..6b968ab8d3 --- /dev/null +++ b/app/serializers/api/admin/product_simple_serializer.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Api + module Admin + class ProductSimpleSerializer < ActiveModel::Serializer + attributes :id, :name, :producer_id + + has_many :variants, key: :variants, serializer: Api::Admin::VariantSimpleSerializer + + def producer_id + object.supplier_id + end + end + end +end diff --git a/app/serializers/api/admin/variant_simple_serializer.rb b/app/serializers/api/admin/variant_simple_serializer.rb new file mode 100644 index 0000000000..d94421321f --- /dev/null +++ b/app/serializers/api/admin/variant_simple_serializer.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Api + module Admin + class VariantSimpleSerializer < ActiveModel::Serializer + attributes :id, :name, :import_date, + :options_text, :unit_value, :unit_description, :unit_to_display, + :display_as, :display_name, :name_to_display, + :price, :on_demand, :on_hand + + has_many :variant_overrides + + def name + if object.full_name.present? + "#{object.name} - #{object.full_name}" + else + object.name + end + end + + def on_hand + return 0 if object.on_hand.nil? + + object.on_hand + end + + def price + object.price.nil? ? 0.to_f : object.price + end + end + end +end diff --git a/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee b/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee index 5724fe1353..1c860e8e91 100644 --- a/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/controllers/variant_overrides_controller_spec.js.coffee @@ -51,9 +51,9 @@ describe "VariantOverridesCtrl", -> it "adds products", -> spyOn(VariantOverrides, "ensureDataFor") expect(scope.products).toEqual [] - scope.addProducts ['a', 'b'] + scope.addProducts { products: ['a', 'b'] } expect(scope.products).toEqual ['a', 'b'] - scope.addProducts ['c', 'd'] + scope.addProducts { products: ['c', 'd'] } expect(scope.products).toEqual ['a', 'b', 'c', 'd'] expect(VariantOverrides.ensureDataFor).toHaveBeenCalled()