Add tag_rules logic to main query before pagination

This commit is contained in:
Matt-Yorkley
2019-10-02 13:47:22 +01:00
parent 06e1f56ae9
commit e96252f2ed
4 changed files with 112 additions and 20 deletions

View File

@@ -2,9 +2,10 @@
# The stock-checking includes on_demand and stock level overrides from variant_overrides.
class OrderCycleDistributedProducts
def initialize(distributor, order_cycle)
def initialize(distributor, order_cycle, customer)
@distributor = distributor
@order_cycle = order_cycle
@customer = customer
end
def products_relation
@@ -12,26 +13,97 @@ class OrderCycleDistributedProducts
end
def variants_relation
@order_cycle.
variants_distributed_by(@distributor).
order_cycle.
variants_distributed_by(distributor).
merge(stocked_variants_and_overrides)
end
private
attr_reader :distributor, :order_cycle, :customer
def stocked_products
@order_cycle.
variants_distributed_by(@distributor).
order_cycle.
variants_distributed_by(distributor).
merge(stocked_variants_and_overrides).
select("DISTINCT spree_variants.product_id")
end
def stocked_variants_and_overrides
Spree::Variant.
stocked_variants = Spree::Variant.
joins("LEFT OUTER JOIN variant_overrides ON variant_overrides.variant_id = spree_variants.id
AND variant_overrides.hub_id = #{@distributor.id}").
AND variant_overrides.hub_id = #{distributor.id}").
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
end
def query_stock_with_overrides
@@ -42,6 +114,22 @@ 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

@@ -4,9 +4,10 @@ module OpenFoodNetwork
class ProductsRenderer
class NoProducts < RuntimeError; end
def initialize(distributor, order_cycle, params = {})
def initialize(distributor, order_cycle, customer, params = {})
@distributor = distributor
@order_cycle = order_cycle
@customer = customer
@params = params
end
@@ -28,7 +29,7 @@ module OpenFoodNetwork
private
attr_reader :order_cycle, :distributor, :params
attr_reader :order_cycle, :distributor, :customer, :params
def products
return unless order_cycle
@@ -44,7 +45,7 @@ module OpenFoodNetwork
end
def distributed_products
OrderCycleDistributedProducts.new(distributor, order_cycle)
OrderCycleDistributedProducts.new(distributor, order_cycle, customer)
end
def taxon_order

View File

@@ -6,7 +6,8 @@ module OpenFoodNetwork
let(:distributor) { create(:distributor_enterprise) }
let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) }
let(:exchange) { order_cycle.exchanges.to_enterprises(distributor).outgoing.first }
let(:pr) { ProductsRenderer.new(distributor, order_cycle) }
let(:customer) { create(:customer) }
let(:pr) { ProductsRenderer.new(distributor, order_cycle, customer) }
describe "sorting" do
let(:t1) { create(:taxon) }
@@ -89,7 +90,7 @@ module OpenFoodNetwork
let!(:v2) { create(:variant, product: p, unit_value: 5) } # Not in exchange
let!(:v3) { create(:variant, product: p, unit_value: 7, inventory_items: [create(:inventory_item, enterprise: hub, visible: true)]) }
let!(:v4) { create(:variant, product: p, unit_value: 9, inventory_items: [create(:inventory_item, enterprise: hub, visible: false)]) }
let(:pr) { ProductsRenderer.new(hub, oc) }
let(:pr) { ProductsRenderer.new(hub, oc, customer) }
let(:variants) { pr.send(:variants_for_shop_by_id) }
it "scopes variants to distribution" do

View File

@@ -5,13 +5,14 @@ describe OrderCycleDistributedProducts do
let(:distributor) { create(:distributor_enterprise) }
let(:product) { create(:product) }
let(:variant) { product.variants.first }
let(:customer) { create(:customer) }
let(:order_cycle) do
create(:simple_order_cycle, distributors: [distributor], variants: [variant])
end
describe "product distributed by distributor in the OC" do
it "returns products" do
expect(described_class.new(distributor, order_cycle).products_relation).to eq([product])
expect(described_class.new(distributor, order_cycle, customer).products_relation).to eq([product])
end
end
@@ -25,7 +26,7 @@ describe OrderCycleDistributedProducts do
end
it "does not return product" do
expect(described_class.new(distributor, order_cycle).products_relation).to_not include product
expect(described_class.new(distributor, order_cycle, customer).products_relation).to_not include product
end
end
@@ -36,19 +37,19 @@ describe OrderCycleDistributedProducts do
end
it "does not return product" do
expect(described_class.new(distributor, order_cycle).products_relation).to_not include product
expect(described_class.new(distributor, order_cycle, customer).products_relation).to_not include product
end
end
describe "filtering products that are out of stock" do
context "with regular variants" do
it "returns product when variant is in stock" do
expect(described_class.new(distributor, order_cycle).products_relation).to include product
expect(described_class.new(distributor, order_cycle, customer).products_relation).to include product
end
it "does not return product when variant is out of stock" do
variant.update_attribute(:on_hand, 0)
expect(described_class.new(distributor, order_cycle).products_relation).to_not include product
expect(described_class.new(distributor, order_cycle, customer).products_relation).to_not include product
end
end
@@ -56,13 +57,13 @@ describe OrderCycleDistributedProducts do
let!(:override) { create(:variant_override, hub: distributor, variant: variant, count_on_hand: 0) }
it "does not return product when an override is out of stock" do
expect(described_class.new(distributor, order_cycle).products_relation).to_not include product
expect(described_class.new(distributor, order_cycle, customer).products_relation).to_not include product
end
it "returns product when an override is in stock" do
variant.update_attribute(:on_hand, 0)
override.update_attribute(:count_on_hand, 10)
expect(described_class.new(distributor, order_cycle).products_relation).to include product
expect(described_class.new(distributor, order_cycle, customer).products_relation).to include product
end
end
end
@@ -71,12 +72,13 @@ describe OrderCycleDistributedProducts do
describe "#variants_relation" do
let(:distributor) { create(:distributor_enterprise) }
let(:oc) { create(:simple_order_cycle, distributors: [distributor], variants: [v1, v3]) }
let(:customer) { create(:customer) }
let(:product) { create(:simple_product) }
let!(:v1) { create(:variant, product: product) }
let!(:v2) { create(:variant, product: product) }
let!(:v3) { create(:variant, product: product) }
let!(:vo) { create(:variant_override, hub: distributor, variant_id: v3.id, count_on_hand: 0) }
let(:variants) { described_class.new(distributor, oc).variants_relation }
let(:variants) { described_class.new(distributor, oc, customer).variants_relation }
it "returns variants in the oc" do
expect(variants).to include v1