Extract tag_rule filtering into separate service

This commit is contained in:
Matt-Yorkley
2019-10-04 10:07:12 +01:00
parent e96252f2ed
commit f134cd9473
3 changed files with 210 additions and 84 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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