Implement and use scopes for finding products by distributor via order cycle or product distribution

This commit is contained in:
Rohan Mitchell
2013-03-08 14:10:40 +11:00
parent 265336b70c
commit e326e590db
5 changed files with 109 additions and 25 deletions

View File

@@ -1,3 +1,5 @@
require 'open_food_web/queries_product_distribution'
class ApplicationController < ActionController::Base
protect_from_forgery
@@ -12,7 +14,7 @@ class ApplicationController < ActionController::Base
def load_data_for_sidebar
@suppliers = Enterprise.is_primary_producer
@order_cycles = OrderCycle.active
@distributors = QueriesProductDistribution.active_distributors
@distributors = OpenFoodWeb::QueriesProductDistribution.active_distributors
end
# All render calls within the block will be performed with the specified format

View File

@@ -7,7 +7,12 @@ module Spree
css_class = (current_taxon && current_taxon.self_and_ancestors.include?(taxon)) ? 'current' : nil
products = Product.in_taxon(taxon)
products = products.in_distributor(current_distributor) if current_distributor
#products = products.in_distributor(current_distributor) if current_distributor
#products = products.in_order_cycle(current_order_cycle) if current_order_cycle
products = OpenFoodWeb::QueriesProductDistribution.products_available_for(products, current_distributor, current_order_cycle)
num_products = products.count
content_tag :li, :class => css_class do

View File

@@ -10,36 +10,48 @@ Spree::Product.class_eval do
validates_presence_of :supplier
# -- Joins
scope :with_product_distributions_outer, joins('LEFT OUTER JOIN product_distributions ON product_distributions.product_id = spree_products.id')
scope :with_order_cycles_outer, joins('LEFT OUTER JOIN spree_variants ON (spree_variants.product_id = spree_products.id)').
joins('LEFT OUTER JOIN exchange_variants ON (exchange_variants.variant_id = spree_variants.id)').
joins('LEFT OUTER JOIN exchanges ON (exchanges.id = exchange_variants.exchange_id)').
joins('LEFT OUTER JOIN order_cycles ON (order_cycles.id = exchanges.order_cycle_id)')
scope :with_order_cycles_inner, joins('INNER JOIN spree_variants ON (spree_variants.product_id = spree_products.id)').
joins('INNER JOIN exchange_variants ON (exchange_variants.variant_id = spree_variants.id)').
joins('INNER JOIN exchanges ON (exchanges.id = exchange_variants.exchange_id)').
joins('INNER JOIN order_cycles ON (order_cycles.id = exchanges.order_cycle_id)')
# -- Scopes
scope :in_supplier, lambda { |supplier| where(:supplier_id => supplier) }
scope :in_distributor, lambda { |distributor| joins(:product_distributions).where('product_distributions.distributor_id = ?', (distributor.respond_to?(:id) ? distributor.id : distributor.to_i)) }
# Find products that are distributed via the given distributor EITHER through a product distribution OR through an order cycle
scope :in_distributor, lambda { |distributor|
distributor = distributor.respond_to?(:id) ? distributor.id : distributor.to_i
scope :in_supplier_or_distributor, lambda { |enterprise| select('distinct spree_products.*').
joins('LEFT OUTER JOIN product_distributions ON product_distributions.product_id=spree_products.id').
where('supplier_id=? OR product_distributions.distributor_id=?',
enterprise.respond_to?(:id) ? enterprise.id : enterprise.to_i,
enterprise.respond_to?(:id) ? enterprise.id : enterprise.to_i) }
scope :in_order_cycle_distributor, lambda { |distributor|
joins('INNER JOIN spree_variants ON (spree_variants.product_id = spree_products.id)').
joins('INNER JOIN exchange_variants ON (exchange_variants.variant_id=spree_variants.id)').
joins('INNER JOIN exchanges ON (exchanges.id = exchange_variants.exchange_id)').
joins('INNER JOIN order_cycles ON (order_cycles.id = exchanges.order_cycle_id)').
where('exchanges.sender_id = order_cycles.coordinator_id').
where('exchanges.receiver_id = ?', (distributor.respond_to?(:id) ? distributor.id : distributor.to_i))
with_product_distributions_outer.with_order_cycles_outer.
where('product_distributions.distributor_id = ? OR (exchanges.sender_id = order_cycles.coordinator_id AND exchanges.receiver_id = ?)', distributor, distributor).
select('distinct spree_products.*')
}
scope :in_supplier_or_order_cycle_distributor, lambda { |enterprise|
enterprise_id = enterprise.respond_to?(:id) ? enterprise.id : enterprise.to_i
# Find products that are supplied by a given enterprise or distributed via that enterprise EITHER through a product distribution OR through an order cycle
scope :in_supplier_or_distributor, lambda { |enterprise|
enterprise = enterprise.respond_to?(:id) ? enterprise.id : enterprise.to_i
select('distinct spree_products.*').
joins('LEFT OUTER JOIN spree_variants ON (spree_variants.product_id = spree_products.id)').
joins('LEFT OUTER JOIN exchange_variants ON (exchange_variants.variant_id=spree_variants.id)').
joins('LEFT OUTER JOIN exchanges ON (exchanges.id = exchange_variants.exchange_id)').
joins('LEFT OUTER JOIN order_cycles ON (order_cycles.id = exchanges.order_cycle_id)').
where('(exchanges.sender_id = order_cycles.coordinator_id AND exchanges.receiver_id = ?) OR (spree_products.supplier_id=?)', enterprise_id, enterprise_id)
with_product_distributions_outer.with_order_cycles_outer.
where('spree_products.supplier_id = ? OR product_distributions.distributor_id = ? OR (exchanges.sender_id = order_cycles.coordinator_id AND exchanges.receiver_id = ?)', enterprise, enterprise, enterprise).
select('distinct spree_products.*')
}
# Find products that are distributed by the given order cycle
scope :in_order_cycle, lambda { |order_cycle| with_order_cycles_inner.
where('exchanges.sender_id = order_cycles.coordinator_id').
where('order_cycles.id = ?', order_cycle) }
# -- Methods
def shipping_method_for_distributor(distributor)
distribution = self.product_distributions.find_by_distributor_id(distributor)

View File

@@ -4,6 +4,12 @@ module OpenFoodWeb
(active_distributors_for_product_distributions + active_distributors_for_order_cycles).sort_by { |d| d.name }.uniq
end
def self.products_available_for(products, distributor=nil, order_cycle=nil)
products = products.in_distributor(distributor) if distributor
products = products.in_order_cycle(order_cycle) if order_cycle
products
end
private
@@ -12,6 +18,16 @@ module OpenFoodWeb
end
def self.active_distributors_for_order_cycles
# Can I create this with merge scopes?
# ie. Enterprise.is_distributor.join(:order_cycle_distributor).merge(OrderCycle.active)
# Try this:
Enterprise.joins('LEFT INNER JOIN exchanges ON (exchanges.receiver_id = enterprises.id)').joins('LEFT INNER JOIN order_cycles ON (order_cycles.id = exchanges.order_cycle.id)').
merge(OrderCycle.active).select('DISTINCT enterprises.*')
# Then I can make each of these methods into a scope on Enterprise, and ultimately a single scope
# Then we don't need this class.
OrderCycle.active.map { |oc| oc.distributors }.flatten.uniq
end

View File

@@ -8,7 +8,15 @@ feature %q{
include AuthenticationWorkflow
include WebHelper
scenario "viewing product counts when no distributor selected" do
# How should this work with distributors/order cycles?
# - No distributor or OC selected - all shown
# - Distributor selected - any from that distributor in any OC
# - OC selected - any in that OC from any distributor
# - Both selected - filter for both
# Also keep specs for distributors outside order cycles.
scenario "viewing product counts when no distributor or order cycle is selected" do
# Given some taxons and some products
taxonomy = Spree::Taxonomy.find_by_name('Products') || create(:taxonomy, :name => 'Products')
taxonomy_root = taxonomy.root
@@ -48,15 +56,56 @@ feature %q{
2.times { create(:product, :taxons => [taxon_three], :distributors => [other_distributor]) }
2.times { create(:product, :taxons => [taxon_three], :distributors => [my_distributor]) }
p = create(:product, :taxons => [taxon_one])
oc = create(:simple_order_cycle, distributors: [my_distributor], variants: [p.master])
# When I visit the home page and select my distributor
visit spree.select_distributor_order_path(my_distributor)
within('nav#filters') { click_link my_distributor.name }
page.should have_content 'You are shopping at My Distributor'
# Then I should see distributor-scoped product counts next to the taxons
page.should have_selector 'nav#taxonomies li', :text => 'Taxon one (1)'
page.should have_selector 'nav#taxonomies li', :text => 'Taxon two (0)'
page.should have_selector 'nav#taxonomies li', :text => 'Taxon three (2)'
end
scenario "viewing product counts when an order cycle is selected" do
# Given some taxons and some products and some order cycles
taxonomy = Spree::Taxonomy.find_by_name('Products') || create(:taxonomy, :name => 'Products')
taxonomy_root = taxonomy.root
taxon_one = create(:taxon, :name => 'Taxon one', :parent_id => taxonomy_root.id)
taxon_two = create(:taxon, :name => 'Taxon two', :parent_id => taxonomy_root.id)
taxon_three = create(:taxon, :name => 'Taxon three', :parent_id => taxonomy_root.id)
supplier = create(:supplier_enterprise, :name => 'My Supplier')
distributor = create(:distributor_enterprise, :name => 'My Distributor')
t1p1 = create(:product, :taxons => [taxon_one])
t2p1 = create(:product, :taxons => [taxon_two])
t2p2 = create(:product, :taxons => [taxon_two])
t3p1 = create(:product, :taxons => [taxon_three])
t3p2 = create(:product, :taxons => [taxon_three])
oc1 = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], variants: [t1p1.master, t2p1.master, t2p2.master])
oc2 = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], variants: [t3p1.master, t3p2.master])
# When I visit the home page and select my order cycle
visit root_path
choose oc2.name
click_button 'Choose Order Cycle'
page.should have_content 'Your order cycle has been selected.'
pending "TODO: Test that products by ProductDistribution are not shown"
# Then I should see order cycle-scoped product counts next to the taxons
page.should have_selector 'nav#taxonomies li', :text => 'Taxon one (0)'
page.should have_selector 'nav#taxonomies li', :text => 'Taxon two (0)'
page.should have_selector 'nav#taxonomies li', :text => 'Taxon three (2)'
end
scenario "viewing product counts when both a distributor and an order cycle are selected"
end