mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Add VariantTagRuleFilterer to filter variants by tag rule
This commit is contained in:
109
app/services/variant_tag_rules_filterer.rb
Normal file
109
app/services/variant_tag_rules_filterer.rb
Normal file
@@ -0,0 +1,109 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Takes a Spree::Variant AR object and filters results based on applicable tag rules.
|
||||
# Tag rules exists in the context of enterprise, customer, and variants.
|
||||
# Returns a Spree::Variant AR object.
|
||||
|
||||
# The filtering is somewhat not intuitive when they are conflicting rules in play:
|
||||
# * When a variant is hidden by a default rule, It will apply the "show rule" if any
|
||||
# * When there is no default rule, it will apply the "show rule" over any "hide rule"
|
||||
#
|
||||
class VariantTagRulesFilterer
|
||||
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::FilterVariants.for(distributor).all
|
||||
end
|
||||
|
||||
def filter(variants_relation)
|
||||
return variants_relation unless variants_to_hide.any?
|
||||
|
||||
variants_relation.where(query_with_tag_rules)
|
||||
end
|
||||
|
||||
def query_with_tag_rules
|
||||
"#{variant_not_hidden_by_rule} OR #{variant_shown_by_rule}"
|
||||
end
|
||||
|
||||
def variant_not_hidden_by_rule
|
||||
return "FALSE" unless variants_to_hide.any?
|
||||
|
||||
"spree_variants.id NOT IN (#{variants_to_hide.join(',')})"
|
||||
end
|
||||
|
||||
def variant_shown_by_rule
|
||||
return "FALSE" unless variants_to_show.any?
|
||||
|
||||
"spree_variants.id IN (#{variants_to_show.join(',')})"
|
||||
end
|
||||
|
||||
def variants_to_hide
|
||||
@variants_to_hide ||= Spree::Variant.where(supplier: distributor)
|
||||
.tagged_with(default_rule_tags + hide_rule_tags, any: true)
|
||||
.pluck(:id)
|
||||
end
|
||||
|
||||
def variants_to_show
|
||||
@variants_to_show ||= Spree::Variant.where(supplier: distributor)
|
||||
.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 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 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&.tag_list || []
|
||||
end
|
||||
end
|
||||
119
spec/services/variant_tag_rules_filterer_spec.rb
Normal file
119
spec/services/variant_tag_rules_filterer_spec.rb
Normal file
@@ -0,0 +1,119 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe VariantTagRulesFilterer do
|
||||
subject(:filterer) { described_class.new(distributor:, customer:, variants_relation:) }
|
||||
|
||||
let(:distributor) { create(:distributor_enterprise) }
|
||||
let(:product) { create(:product) }
|
||||
let!(:variant_hidden_by_default) { create(:variant, product:, supplier: distributor) }
|
||||
let!(:variant_hidden_by_rule) { create(:variant, product:, supplier: distributor) }
|
||||
let!(:variant_shown_by_rule) { create(:variant, product:, supplier: distributor) }
|
||||
let!(:variant_hidden_for_another_customer) { create(:variant, product:, supplier: distributor) }
|
||||
let(:customer) { create(:customer, enterprise: distributor) }
|
||||
let(:variants_relation) { Spree::Variant.where(supplier: distributor) }
|
||||
|
||||
describe "#call" do
|
||||
let!(:hide_rule) {
|
||||
create(:filter_variants_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_variants_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_variants_tag_rule,
|
||||
enterprise: distributor,
|
||||
preferred_variant_tags: "hide_these_other_variants",
|
||||
preferred_customer_tags: "hide_from_other_customers",
|
||||
preferred_matched_variants_visibility: "hidden" )
|
||||
}
|
||||
|
||||
context "when the distributor has no rules" do
|
||||
it "returns the relation unchanged" do
|
||||
expect(filterer.call).to eq variants_relation
|
||||
end
|
||||
end
|
||||
|
||||
context "with hide rule" do
|
||||
it "hides the variant matching the rule" do
|
||||
customer.update_attribute(:tag_list, hide_rule.preferred_customer_tags)
|
||||
variant_hidden_by_rule.update_attribute(:tag_list, hide_rule.preferred_variant_tags)
|
||||
|
||||
expect(filterer.call).not_to include(variant_hidden_by_rule)
|
||||
end
|
||||
|
||||
context "with mutiple conflicting rules" do
|
||||
it "applies the show rule" do
|
||||
# Customer has show rule tag and hide rule tag
|
||||
customer.update_attribute(
|
||||
:tag_list, [hide_rule.preferred_customer_tags, show_rule.preferred_customer_tags]
|
||||
)
|
||||
# Variant has show rule tag and hide rule tag
|
||||
variant_hidden_by_rule.update_attribute(
|
||||
:tag_list, [hide_rule.preferred_variant_tags, show_rule.preferred_variant_tags,]
|
||||
)
|
||||
expect(filterer.call).to include(variant_hidden_by_rule)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with variant hidden by default" do
|
||||
let(:default_hide_rule) {
|
||||
create(:filter_variants_tag_rule,
|
||||
enterprise: distributor,
|
||||
is_default: true,
|
||||
preferred_variant_tags: "hide_these_variants_from_everyone",
|
||||
preferred_matched_variants_visibility: "hidden")
|
||||
}
|
||||
|
||||
before do
|
||||
variant_hidden_by_default.update_attribute(
|
||||
:tag_list, default_hide_rule.preferred_variant_tags
|
||||
)
|
||||
end
|
||||
|
||||
it "excludes variant hidden by default" do
|
||||
expect(filterer.call).not_to include(variant_hidden_by_default)
|
||||
end
|
||||
|
||||
context "with variant rule overriding default rule" do
|
||||
it "includes variant hidden by default" do
|
||||
customer.update_attribute(:tag_list, show_rule.preferred_customer_tags)
|
||||
# Variant has default rule tag and show rule tag
|
||||
variant_hidden_by_default.update_attribute(
|
||||
:tag_list, [default_hide_rule.preferred_variant_tags, show_rule.preferred_variant_tags]
|
||||
)
|
||||
|
||||
expect(filterer.call).to include(variant_hidden_by_default)
|
||||
end
|
||||
|
||||
context "with mutiple conflicting rules applying to same variant" do
|
||||
it "applies the show rule" do
|
||||
# customer has show rule and hide rule tag
|
||||
customer.update_attribute(
|
||||
:tag_list, [show_rule.preferred_customer_tags, hide_rule.preferred_customer_tags]
|
||||
)
|
||||
|
||||
# Variant has default rule tag and show rule tag and hide rule tag
|
||||
variant_hidden_by_default.update_attribute(
|
||||
:tag_list,
|
||||
[default_hide_rule.preferred_variant_tags, show_rule.preferred_variant_tags,
|
||||
hide_rule.preferred_variant_tags]
|
||||
)
|
||||
|
||||
expect(filterer.call).to include(variant_hidden_by_default)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user