From f134cd9473b9bdc7be819f9b5775adf4f707ecd6 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 4 Oct 2019 10:07:12 +0100 Subject: [PATCH] Extract tag_rule filtering into separate service --- .../order_cycle_distributed_products.rb | 85 +------------- app/services/product_tag_rules_filterer.rb | 111 ++++++++++++++++++ .../product_tag_rules_filterer_spec.rb | 98 ++++++++++++++++ 3 files changed, 210 insertions(+), 84 deletions(-) create mode 100644 app/services/product_tag_rules_filterer.rb create mode 100644 spec/services/product_tag_rules_filterer_spec.rb diff --git a/app/services/order_cycle_distributed_products.rb b/app/services/order_cycle_distributed_products.rb index 3f8d3ddc9b..306233ea3c 100644 --- a/app/services/order_cycle_distributed_products.rb +++ b/app/services/order_cycle_distributed_products.rb @@ -36,74 +36,7 @@ class OrderCycleDistributedProducts joins(:stock_items). where(query_stock_with_overrides) - if distributor_rules.any? - stocked_variants = apply_tag_rules(stocked_variants) - end - - stocked_variants - end - - def apply_tag_rules(stocked_variants) - stocked_variants.where(query_with_tag_rules) - end - - def distributor_rules - @distributor_rules ||= TagRule::FilterProducts.prioritised.for(distributor) - end - - def customer_tag_list - customer.andand.tag_list || [] - end - - def default_rule_tags - default_rules.map(&:preferred_variant_tags) - end - - def hide_rule_tags - hide_rules.map(&:preferred_variant_tags) - end - - def show_rule_tags - show_rules.map(&:preferred_variant_tags) - end - - def overrides_to_hide - @overrides_to_hide = VariantOverride.where(hub_id: distributor.id). - tagged_with(default_rule_tags + hide_rule_tags, any: true). - pluck(:id) - end - - def overrides_to_show - @overrides_to_show = VariantOverride.where(hub_id: distributor.id). - tagged_with(show_rule_tags, any: true). - pluck(:id) - end - - def customer_applicable_rules - # Rules which apply specifically to the current customer - @customer_applicable_rules ||= non_default_rules.select{ |rule| customer_tagged?(rule) } - end - - def default_rules - # These rules hide a variant_override with tag X - distributor_rules.select(&:is_default?) - end - - def non_default_rules - # These rules show or hide a variant_override with tag X for customer with tag Y - distributor_rules.reject(&:is_default?) - end - - def hide_rules - @hide_rules ||= customer_applicable_rules.select{ |rule| rule.preferred_matched_variants_visibility == 'hidden'} - end - - def show_rules - customer_applicable_rules - hide_rules - end - - def customer_tagged?(rule) - customer_tag_list.include? rule.preferred_customer_tags + ProductTagRulesFilterer.new(distributor, customer, stocked_variants).call end def query_stock_with_overrides @@ -114,22 +47,6 @@ class OrderCycleDistributedProducts AND #{variant_not_on_demand} AND #{variant_in_stock} ) )" end - def query_with_tag_rules - "#{variant_not_overriden} OR ( #{variant_overriden} - AND ( #{override_not_hidden_by_rule} - OR #{override_shown_by_rule} ) )" - end - - def override_not_hidden_by_rule - return "FALSE" unless overrides_to_hide.any? - "variant_overrides.id NOT IN (#{overrides_to_hide.join(',')})" - end - - def override_shown_by_rule - return "FALSE" unless overrides_to_show.any? - "variant_overrides.id IN (#{overrides_to_show.join(',')})" - end - def variant_not_overriden "variant_overrides.id IS NULL" end diff --git a/app/services/product_tag_rules_filterer.rb b/app/services/product_tag_rules_filterer.rb new file mode 100644 index 0000000000..b58cbc2c43 --- /dev/null +++ b/app/services/product_tag_rules_filterer.rb @@ -0,0 +1,111 @@ +# Takes a Spree::Variant AR object and filters results based on applicable tag rules. +# Tag rules exists in the context of enterprise, customer, and variant_overrides, +# and are applied to variant_overrides only. Returns a Spree::Variant AR object. + +class ProductTagRulesFilterer + def initialize(distributor, customer, variants_relation) + @distributor = distributor + @customer = customer + @variants_relation = variants_relation + end + + def call + return variants_relation unless distributor_rules.any? + + filter(variants_relation) + end + + private + + attr_accessor :distributor, :customer, :variants_relation + + def distributor_rules + @distributor_rules ||= TagRule::FilterProducts.prioritised.for(distributor).all + end + + def filter(variants_relation) + return variants_relation unless overrides_to_hide.any? + + variants_relation.where(query_with_tag_rules) + end + + def query_with_tag_rules + "#{variant_not_overriden} OR ( #{variant_overriden} + AND ( #{override_not_hidden_by_rule} + OR #{override_shown_by_rule} ) )" + end + + def variant_not_overriden + "variant_overrides.id IS NULL" + end + + def variant_overriden + "variant_overrides.id IS NOT NULL" + end + + def override_not_hidden_by_rule + return "FALSE" unless overrides_to_hide.any? + "variant_overrides.id NOT IN (#{overrides_to_hide.join(',')})" + end + + def override_shown_by_rule + return "FALSE" unless overrides_to_show.any? + "variant_overrides.id IN (#{overrides_to_show.join(',')})" + end + + def overrides_to_hide + @overrides_to_hide ||= VariantOverride.where(hub_id: distributor.id). + tagged_with(default_rule_tags + hide_rule_tags, any: true). + pluck(:id) + end + + def overrides_to_show + @overrides_to_show ||= VariantOverride.where(hub_id: distributor.id). + tagged_with(show_rule_tags, any: true). + pluck(:id) + end + + def default_rule_tags + default_rules.map(&:preferred_variant_tags) + end + + def hide_rule_tags + hide_rules.map(&:preferred_variant_tags) + end + + def show_rule_tags + show_rules.map(&:preferred_variant_tags) + end + + def default_rules + # These rules hide a variant_override with tag X and apply to all customers + distributor_rules.select(&:is_default?) + end + + def non_default_rules + # These rules show or hide a variant_override with tag X for customer with tag Y + distributor_rules.reject(&:is_default?) + end + + def customer_applicable_rules + # Rules which apply specifically to the current customer + @customer_applicable_rules ||= non_default_rules.select{ |rule| customer_tagged?(rule) } + end + + def hide_rules + @hide_rules ||= customer_applicable_rules. + select{ |rule| rule.preferred_matched_variants_visibility == 'hidden' } + end + + def show_rules + customer_applicable_rules - hide_rules + end + + def customer_tagged?(rule) + customer_tag_list.include? rule.preferred_customer_tags + end + + def customer_tag_list + customer.andand.tag_list || [] + end +end diff --git a/spec/services/product_tag_rules_filterer_spec.rb b/spec/services/product_tag_rules_filterer_spec.rb new file mode 100644 index 0000000000..026e0ec03b --- /dev/null +++ b/spec/services/product_tag_rules_filterer_spec.rb @@ -0,0 +1,98 @@ +require "spec_helper" + +describe ProductTagRulesFilterer do + describe "filtering by tag rules" do + let!(:distributor) { create(:distributor_enterprise) } + let(:product) { create(:product, supplier: distributor) } + let(:v1) { create(:variant, product: product) } + let(:v2) { create(:variant, product: product) } + let(:v3) { create(:variant, product: product) } + let(:v4) { create(:variant, product: product) } + let(:variant_hidden_by_default) { create(:variant_override, variant: v1, hub: distributor) } + let(:variant_hidden_by_rule) { create(:variant_override, variant: v2, hub: distributor) } + let(:variant_shown_by_rule) { create(:variant_override, variant: v3, hub: distributor) } + let(:variant_hidden_for_another_customer) { create(:variant_override, variant: v4, hub: distributor) } + let(:customer) { create(:customer, enterprise: distributor) } + let(:variants_relation) { + Spree::Variant.joins(:product).where("spree_products.supplier_id = ?", distributor.id) + } + let(:default_hide_rule) { + create(:filter_products_tag_rule, + enterprise: distributor, + is_default: true, + preferred_variant_tags: "hide_these_variants_from_everyone", + preferred_matched_variants_visibility: "hidden") + } + let!(:hide_rule) { + create(:filter_products_tag_rule, + enterprise: distributor, + preferred_variant_tags: "hide_these_variants", + preferred_customer_tags: "hide_from_these_customers", + preferred_matched_variants_visibility: "hidden" ) + } + let!(:show_rule) { + create(:filter_products_tag_rule, + enterprise: distributor, + preferred_variant_tags: "show_these_variants", + preferred_customer_tags: "show_for_these_customers", + preferred_matched_variants_visibility: "visible" ) + } + let!(:non_applicable_rule) { + create(:filter_products_tag_rule, + enterprise: distributor, + preferred_variant_tags: "hide_these_other_variants", + preferred_customer_tags: "hide_from_other_customers", + preferred_matched_variants_visibility: "hidden" ) + } + let(:filterer) { described_class.new(distributor, customer, variants_relation) } + + context "when the distributor has no rules" do + it "returns the relation unchanged" do + expect(filterer.call).to eq variants_relation + end + end + + describe "#customer_applicable_rules" do + it "returns a list of tags that apply to the current customer" do + customer.update_attribute(:tag_list, show_rule.preferred_customer_tags) + + customer_applicable_rules = filterer.__send__(:customer_applicable_rules) + expect(customer_applicable_rules).to eq [show_rule] + end + end + + describe "#overrides_to_hide" do + context "with default rules" do + it "lists overrides tagged as hidden for this customer" do + variant_hidden_by_default.update_attribute(:tag_list, default_hide_rule.preferred_variant_tags) + + overrides_to_hide = filterer.__send__(:overrides_to_hide) + expect(overrides_to_hide).to eq [variant_hidden_by_default.id] + end + end + + context "with default and specific rules" do + it "lists overrides tagged as hidden for this customer" do + customer.update_attribute(:tag_list, hide_rule.preferred_customer_tags) + variant_hidden_by_default.update_attribute(:tag_list, default_hide_rule.preferred_variant_tags) + variant_hidden_by_rule.update_attribute(:tag_list, hide_rule.preferred_variant_tags) + variant_hidden_for_another_customer.update_attribute(:tag_list, non_applicable_rule.preferred_variant_tags) + + overrides_to_hide = filterer.__send__(:overrides_to_hide) + expect(overrides_to_hide).to eq [variant_hidden_by_default.id, variant_hidden_by_rule.id] + end + end + + end + + describe "#overrides_to_show" do + it "lists overrides tagged as visible for this customer" do + customer.update_attribute(:tag_list, show_rule.preferred_customer_tags) + variant_shown_by_rule.update_attribute(:tag_list, show_rule.preferred_variant_tags) + + overrides_to_show = filterer.__send__(:overrides_to_show) + expect(overrides_to_show).to eq [variant_shown_by_rule.id] + end + end + end +end