diff --git a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee index d2f18b6362..521859f7ab 100644 --- a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee @@ -67,7 +67,7 @@ angular.module('Darkswarm').controller "ProductsCtrl", ($scope, $sce, $filter, $ page: page || $scope.page, per_page: $scope.per_page, 'q[name_or_meta_keywords_or_variants_display_as_or_variants_display_name_or_supplier_name_cont]': $scope.query, - 'q[properties_id_or_supplier_properties_id_in_any][]': $scope.activeProperties, + 'q[with_properties][]': $scope.activeProperties, 'q[primary_taxon_id_in_any][]': $scope.activeTaxons } diff --git a/app/controllers/api/v0/order_cycles_controller.rb b/app/controllers/api/v0/order_cycles_controller.rb index 110edc7920..e1c39b2a47 100644 --- a/app/controllers/api/v0/order_cycles_controller.rb +++ b/app/controllers/api/v0/order_cycles_controller.rb @@ -81,8 +81,7 @@ module Api def permitted_ransack_params [:name_or_meta_keywords_or_variants_display_as_or_variants_display_name_or_supplier_name_cont, - :properties_id_or_supplier_properties_id_in_any, - :primary_taxon_id_in_any] + :with_properties, :primary_taxon_id_in_any] end def distributor diff --git a/app/models/spree/product.rb b/app/models/spree/product.rb index da70fce5cb..f07dff0375 100755 --- a/app/models/spree/product.rb +++ b/app/models/spree/product.rb @@ -31,7 +31,7 @@ module Spree searchable_attributes :supplier_id, :primary_taxon_id, :meta_keywords searchable_associations :supplier, :properties, :primary_taxon, :variants, :master - searchable_scopes :active + searchable_scopes :active, :with_properties has_many :product_option_types, dependent: :destroy # We have an after_destroy callback on Spree::ProductOptionType. However, if we @@ -69,6 +69,19 @@ module Spree has_many :stock_items, through: :variants + has_many :supplier_properties, through: :supplier, source: :properties + + scope :with_properties, ->(*property_ids) { + left_outer_joins(:product_properties). + left_outer_joins(:supplier_properties). + where(inherits_properties: true). + where(producer_properties: { property_id: property_ids }). + or( + where(spree_product_properties: { property_id: property_ids }) + ). + distinct + } + delegate_belongs_to :master, :sku, :price, :currency, :display_amount, :display_price, :weight, :height, :width, :depth, :is_master, :cost_currency, :price_in, :amount_in, :unit_value, :unit_description diff --git a/spec/controllers/api/v0/order_cycles_controller_spec.rb b/spec/controllers/api/v0/order_cycles_controller_spec.rb index 27e04ee456..8faac2bd71 100644 --- a/spec/controllers/api/v0/order_cycles_controller_spec.rb +++ b/spec/controllers/api/v0/order_cycles_controller_spec.rb @@ -65,11 +65,32 @@ module Api context "with property filters" do it "filters by product property" do api_get :products, id: order_cycle.id, distributor: distributor.id, - q: { properties_id_or_supplier_properties_id_in_any: [property1.id, property2.id] } + q: { with_properties: [property1.id, property2.id] } + expect(response.status).to eq 200 expect(product_ids).to include product1.id, product2.id expect(product_ids).to_not include product3.id end + + context "with supplier properties" do + let!(:supplier_property) { create(:property, presentation: 'Certified Organic') } + let!(:supplier) { create(:supplier_enterprise, properties: [supplier_property]) } + + before do + product1.update!(supplier: supplier) + product2.update!(supplier: supplier) + product3.update!(supplier: supplier, inherits_properties: false) + end + + it "filter out the product that don't inherits from supplier properties" do + api_get :products, id: order_cycle.id, distributor: distributor.id, + q: { with_properties: [supplier_property.id] } + + expect(response.status).to eq 200 + expect(product_ids).to include product1.id, product2.id + expect(product_ids).to_not include product3.id + end + end end context "with taxon filters" do diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 4ca904fa14..65781c515b 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -199,6 +199,51 @@ module Spree end end + describe "supplier properties" do + subject { create(:product) } + + it "has no supplier properties to start with" do + expect(subject.supplier_properties).to eq [] + end + + it "doesn't include product properties" do + subject.set_property("certified", "organic") + expect(subject.supplier_properties).to eq [] + end + + it "includes the supplier's properties" do + subject.supplier.set_producer_property("certified", "yes") + expect(subject.supplier_properties.map(&:presentation)).to eq ["certified"] + end + end + + describe ".with_properties scope" do + let!(:product_without_wanted_property_on_supplier) { create(:product, supplier: supplier_without_wanted_property) } + let!(:product_with_wanted_property_on_supplier) { create(:product, supplier: supplier_with_wanted_property) } + let!(:product_with_wanted_property) { create(:product, properties: [wanted_property]) } + let!(:product_without_wanted_property_property) { create(:product, properties: [unwanted_property]) } + let!(:product_with_wanted_property_and_on_supplier) { create(:product, properties: [wanted_property], supplier: supplier_with_wanted_property) } + let!(:product_ignoring_property) { create(:product, supplier: supplier_with_wanted_property, inherits_properties: false) } + let(:supplier_with_wanted_property) { create(:supplier_enterprise, properties: [wanted_property]) } + let(:supplier_without_wanted_property) { create(:supplier_enterprise, properties: [unwanted_property]) } + let(:wanted_property) { create(:property, presentation: 'Certified Organic') } + let(:unwanted_property) { create(:property, presentation: 'Latest Hype') } + + it "returns no products without a property id" do + expect(Spree::Product.with_properties([])).to eq [] + end + + it "returns only products with the wanted property set both on supplier and on the product itself" do + expect( + Spree::Product.with_properties([wanted_property.id]) + ).to match_array [ + product_with_wanted_property_on_supplier, + product_with_wanted_property, + product_with_wanted_property_and_on_supplier + ] + end + end + # Regression tests for Spree #2352 context "classifications and taxons" do it "is joined through classifications" do diff --git a/spec/system/consumer/shopping/shopping_spec.rb b/spec/system/consumer/shopping/shopping_spec.rb index e22b841bcf..2539e4476c 100644 --- a/spec/system/consumer/shopping/shopping_spec.rb +++ b/spec/system/consumer/shopping/shopping_spec.rb @@ -242,6 +242,30 @@ describe "As a consumer I want to shop with a distributor", js: true do expect(page).not_to have_content product.name end + context "when supplier uses property" do + let(:product3) { create(:simple_product, supplier: supplier, inherits_properties: false) } + + before do + add_variant_to_order_cycle(exchange, product3.variants.first) + property = create(:property, presentation: 'certified') + supplier.update!(properties: [property]) + end + + it "filters product by properties" do + visit shop_path + + expect(page).to have_content product2.name + expect(page).to have_content product3.name + + expect(page).to have_selector ".sticky-shop-filters-container .property-selectors span", text: "certified" + find(".sticky-shop-filters-container .property-selectors span", text: 'certified').click + expect(page).to have_content "Results for certified" + + expect(page).to have_content product2.name + expect(page).not_to have_content product3.name + end + end + it "returns search results for products where the search term matches one of the product's variant names" do visit shop_path fill_in "search", with: "Badg" # For variant with display_name "Badgers"