mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Merge branch 'shop-by-distributor'
This commit is contained in:
2
Gemfile
2
Gemfile
@@ -50,7 +50,7 @@ group :test, :development do
|
||||
gem 'turn', '~> 0.8.3', :require => false
|
||||
gem 'rspec-rails'
|
||||
gem 'shoulda-matchers'
|
||||
gem 'factory_girl_rails'
|
||||
gem 'factory_girl_rails', :require => false
|
||||
gem 'faker'
|
||||
gem 'capybara'
|
||||
gem 'database_cleaner', '0.7.1', :require => false
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// the compiled file.
|
||||
//
|
||||
|
||||
//= require 'jquery'
|
||||
//= require store/spree_core
|
||||
//= require store/spree_auth
|
||||
//= require store/spree_promo
|
||||
|
||||
@@ -34,6 +34,7 @@ nav#filters {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Style the product source on the product details page in the
|
||||
* same manner as the product properties table above it.
|
||||
*/
|
||||
@@ -43,3 +44,79 @@ nav#filters {
|
||||
#product-properties td, #product-source td {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
|
||||
/* Apply Spree's ul#products style to ul.product-listing. When viewing products
|
||||
* split by distributor, a separate product listing is displayed for local and
|
||||
* remote products, so the #products id is removed to avoid its duplication.
|
||||
*/
|
||||
ul.product-listing {
|
||||
&:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
line-height: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
|
||||
&.info {
|
||||
height: 35px;
|
||||
margin-top: 5px;
|
||||
font-size: $product_list_name_font_size;
|
||||
color: $product_link_text_color;
|
||||
border-bottom: 1px solid lighten($body_text_color, 60);
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.product-image {
|
||||
border: 1px solid lighten($body_text_color, 60);
|
||||
padding: 5px;
|
||||
min-height: 110px;
|
||||
background-color: $product_background_color;
|
||||
|
||||
&:hover {
|
||||
border-color: $link_text_color;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.price {
|
||||
color: $link_text_color;
|
||||
font-size: $product_list_price_font_size;
|
||||
padding-top: 5px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Highlight local products in distributor-split product listings */
|
||||
#products-local ul {
|
||||
margin-bottom: 1em;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
background-color: #def;
|
||||
}
|
||||
|
||||
|
||||
/* Add to cart form on product details page */
|
||||
#cart-form {
|
||||
.error-distributor {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
.distributor-fixed {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,22 @@ module Spree
|
||||
distributor = Distributor.find params[:id]
|
||||
|
||||
order = current_order(true)
|
||||
order.distributor = distributor
|
||||
order.save!
|
||||
|
||||
if order.can_change_distributor?
|
||||
order.distributor = distributor
|
||||
order.save!
|
||||
end
|
||||
|
||||
redirect_back_or_default(root_path)
|
||||
end
|
||||
|
||||
def deselect
|
||||
order = current_order(true)
|
||||
|
||||
if order.can_change_distributor?
|
||||
order.distributor = nil
|
||||
order.save!
|
||||
end
|
||||
|
||||
redirect_back_or_default(root_path)
|
||||
end
|
||||
|
||||
10
app/controllers/spree/home_controller_decorator.rb
Normal file
10
app/controllers/spree/home_controller_decorator.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
require 'open_food_web/split_products_by_distributor'
|
||||
|
||||
Spree::HomeController.class_eval do
|
||||
include Spree::DistributorsHelper
|
||||
include OpenFoodWeb::SplitProductsByDistributor
|
||||
|
||||
respond_override :index => { :html => { :success => lambda {
|
||||
@products, @products_local, @products_remote = split_products_by_distributor @products, current_distributor
|
||||
} } }
|
||||
end
|
||||
41
app/controllers/spree/orders_controller_decorator.rb
Normal file
41
app/controllers/spree/orders_controller_decorator.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
Spree::OrdersController.class_eval do
|
||||
before_filter :populate_order_distributor, :only => :populate
|
||||
|
||||
def populate_order_distributor
|
||||
@distributor = params.key?(:distributor_id) ? Spree::Distributor.find(params[:distributor_id]) : nil
|
||||
|
||||
if populate_valid? @distributor
|
||||
order = current_order(true)
|
||||
order.distributor = @distributor
|
||||
order.save!
|
||||
|
||||
else
|
||||
redirect_to cart_path
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def populate_valid? distributor
|
||||
# -- Distributor must be specified
|
||||
return false if distributor.nil?
|
||||
|
||||
# -- All products must be available under that distributor
|
||||
params[:products].each do |product_id, variant_id|
|
||||
product = Spree::Product.find product_id
|
||||
return false unless product.distributors.include? distributor
|
||||
end if params[:products]
|
||||
|
||||
params[:variants].each do |variant_id, quantity|
|
||||
variant = Spree::Variant.find variant_id
|
||||
return false unless variant.product.distributors.include? distributor
|
||||
end if params[:variants]
|
||||
|
||||
# -- If products in cart, distributor can't be changed
|
||||
order = current_order(false)
|
||||
if !order.nil? && !order.can_change_distributor? && order.distributor != distributor
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
11
app/controllers/spree/products_controller_decorator.rb
Normal file
11
app/controllers/spree/products_controller_decorator.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
require 'open_food_web/split_products_by_distributor'
|
||||
|
||||
Spree::ProductsController.class_eval do
|
||||
include Spree::DistributorsHelper
|
||||
include OpenFoodWeb::SplitProductsByDistributor
|
||||
|
||||
respond_override :index => { :html => { :success => lambda {
|
||||
@products, @products_local, @products_remote = split_products_by_distributor @products, current_distributor
|
||||
} } }
|
||||
|
||||
end
|
||||
10
app/controllers/spree/taxons_controller_decorator.rb
Normal file
10
app/controllers/spree/taxons_controller_decorator.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
require 'open_food_web/split_products_by_distributor'
|
||||
|
||||
Spree::TaxonsController.class_eval do
|
||||
include Spree::DistributorsHelper
|
||||
include OpenFoodWeb::SplitProductsByDistributor
|
||||
|
||||
respond_override :show => { :html => { :success => lambda {
|
||||
@products, @products_local, @products_remote = split_products_by_distributor @products, current_distributor
|
||||
} } }
|
||||
end
|
||||
@@ -8,6 +8,8 @@ module Spree
|
||||
|
||||
validates :name, :pickup_address, :country_id, :state_id, :city, :post_code, :presence => true
|
||||
|
||||
scope :by_name, order('name')
|
||||
|
||||
after_initialize :initialize_country
|
||||
|
||||
def initialize_country
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
Spree::Order.class_eval do
|
||||
belongs_to :distributor
|
||||
|
||||
def can_change_distributor?
|
||||
# Distributor may not be changed once an item has been added to the cart/order
|
||||
line_items.empty?
|
||||
end
|
||||
|
||||
def distributor=(distributor)
|
||||
raise "You cannot change the distributor of an order with products" unless can_change_distributor?
|
||||
super(distributor)
|
||||
end
|
||||
|
||||
def can_add_product_to_cart?(product)
|
||||
can_change_distributor? || product.distributors.include?(distributor)
|
||||
end
|
||||
|
||||
|
||||
|
||||
# before_validation :shipping_address_from_distributor
|
||||
|
||||
private
|
||||
|
||||
4
app/overrides/add_distributor_to_add_to_cart_form.rb
Normal file
4
app/overrides/add_distributor_to_add_to_cart_form.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
Deface::Override.new(:virtual_path => "spree/products/_cart_form",
|
||||
:replace => "[data-hook='product_price'] .add-to-cart",
|
||||
:partial => "spree/products/add_to_cart",
|
||||
:name => "product_add_to_cart")
|
||||
19
app/overrides/split_products_by_distributor.rb
Normal file
19
app/overrides/split_products_by_distributor.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
Deface::Override.new(:virtual_path => "spree/home/index",
|
||||
:replace => "[data-hook='homepage_products']",
|
||||
:partial => "spree/shared/products_by_distributor",
|
||||
:name => "products_home")
|
||||
|
||||
Deface::Override.new(:virtual_path => "spree/products/index",
|
||||
:replace => "[data-hook='homepage_products']",
|
||||
:partial => "spree/shared/products_by_distributor",
|
||||
:name => "products_products")
|
||||
|
||||
Deface::Override.new(:virtual_path => "spree/products/index",
|
||||
:replace => "[data-hook='search_results']",
|
||||
:partial => "spree/shared/products_by_distributor",
|
||||
:name => "products_search")
|
||||
|
||||
Deface::Override.new(:virtual_path => "spree/taxons/show",
|
||||
:replace => "[data-hook='taxon_products']",
|
||||
:partial => "spree/shared/products_by_distributor",
|
||||
:name => "products_taxon")
|
||||
20
app/views/spree/products/_add_to_cart.html.haml
Normal file
20
app/views/spree/products/_add_to_cart.html.haml
Normal file
@@ -0,0 +1,20 @@
|
||||
.add-to-cart
|
||||
- if !@product.has_stock? && !Spree::Config[:allow_backorders]
|
||||
= content_tag('strong', t(:out_of_stock))
|
||||
|
||||
- elsif current_order(false) && !current_order(false).can_add_product_to_cart?(@product)
|
||||
.error-distributor= "Please complete your order at #{current_distributor.name} before shopping with another distributor."
|
||||
|
||||
- else
|
||||
%p Quantity
|
||||
= number_field_tag (@product.has_variants? ? :quantity : "variants[#{@product.master.id}]"), 1, :class => 'title', :in => 1..@product.on_hand
|
||||
- order = current_order(false)
|
||||
- if order.nil? || order.can_change_distributor?
|
||||
%p Distributor
|
||||
= select_tag "distributor_id", options_from_collection_for_select(@product.distributors, "id", "name", current_distributor.andand.id)
|
||||
- else
|
||||
.distributor-fixed= "Your distributor for this order is #{order.distributor.name}"
|
||||
%br/
|
||||
= button_tag :class => 'large primary', :id => 'add-to-cart-button', :type => :submit do
|
||||
= t(:add_to_cart)
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
%td
|
||||
%strong Supplier
|
||||
%td= @product.supplier.name
|
||||
%br/
|
||||
%table#product-source.table-display{:width => "100%"}
|
||||
%tbody
|
||||
- @product.distributors.each do |distributor|
|
||||
%tr.even
|
||||
%td
|
||||
|
||||
@@ -6,5 +6,12 @@
|
||||
|
||||
%h6.filter_name Shop by Distributor
|
||||
%ul.filter_choices
|
||||
- order = current_order(false)
|
||||
- @distributors.each do |distributor|
|
||||
%li.nowrap= link_to distributor.name, select_distributor_path(distributor)
|
||||
%li.nowrap
|
||||
- if order.nil? || order.can_change_distributor?
|
||||
= link_to distributor.name, select_distributor_path(distributor)
|
||||
- else
|
||||
= distributor.name
|
||||
- if current_distributor && order.can_change_distributor?
|
||||
%li.nowrap= link_to 'Leave distributor', deselect_distributors_path
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<% filters = @taxon ? @taxon.applicable_filters : [] %>
|
||||
<% unless filters.empty? %>
|
||||
<nav id="filters">
|
||||
<% params[:search] ||= {} %>
|
||||
<% filters.each do |filter| %>
|
||||
<% labels = filter[:labels] || filter[:conds].map {|m,c| [m,m]} %>
|
||||
<% next if labels.empty? %>
|
||||
<h6 class="filter_name">Shop by <%= filter[:name] %></h6>
|
||||
<ul class="filter_choices">
|
||||
<% labels.each do |nm,val| %>
|
||||
<li class="nowrap">
|
||||
<% active = params[:search][filter[:scope]] && params[:search][filter[:scope]].include?(val.to_s) %>
|
||||
<%= link_to nm, "?search[#{filter[:scope].to_s}][]=#{CGI.escape(val)}" %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
</nav>
|
||||
<% end %>
|
||||
15
app/views/spree/shared/_filters.html.haml
Normal file
15
app/views/spree/shared/_filters.html.haml
Normal file
@@ -0,0 +1,15 @@
|
||||
- filters = @taxon ? @taxon.applicable_filters : []
|
||||
- unless filters.empty?
|
||||
%nav#filters
|
||||
- params[:search] ||= {}
|
||||
- filters.each do |filter|
|
||||
- labels = filter[:labels] || filter[:conds].map {|m,c| [m,m]}
|
||||
- next if labels.empty?
|
||||
|
||||
%h6.filter_name= "Shop by #{filter[:name]}"
|
||||
|
||||
%ul.filter_choices
|
||||
- labels.each do |nm,val|
|
||||
%li.nowrap
|
||||
- active = params[:search][filter[:scope]] && params[:search][filter[:scope]].include?(val.to_s)
|
||||
= link_to nm, "?search[#{filter[:scope].to_s}][]=#{CGI.escape(val)}"
|
||||
23
app/views/spree/shared/_products.html.haml
Normal file
23
app/views/spree/shared/_products.html.haml
Normal file
@@ -0,0 +1,23 @@
|
||||
- paginated_products = @searcher.retrieve_products if params.key?(:keywords)
|
||||
- paginated_products ||= products
|
||||
|
||||
- if products.empty?
|
||||
= t(:no_products_found)
|
||||
- elsif params.key?(:keywords)
|
||||
%h6.search-results-title= t(:search_results, :keywords => h(params[:keywords]))
|
||||
|
||||
- if products.any?
|
||||
%ul.inline.product-listing{"data-hook" => ""}
|
||||
- reset_cycle('default')
|
||||
- products.each do |product|
|
||||
- if Spree::Config[:show_zero_stock_products] || product.has_stock?
|
||||
%li{:class => "columns three #{cycle("alpha", "secondary", "", "omega secondary")}", "data-hook" => "products_list_item", :id => "product_#{product.id}", :itemscope => "", :itemtype => "http://schema.org/Product"}
|
||||
.product-image
|
||||
= link_to small_image(product, :itemprop => "image"), product, :itemprop => 'url'
|
||||
= link_to truncate(product.name, :length => 50), product, :class => 'info', :itemprop => "name", :title => product.name
|
||||
%span.price.selling{:itemprop => "price"}= number_to_currency product.price
|
||||
|
||||
- if paginated_products.respond_to?(:num_pages)
|
||||
- params.delete(:search)
|
||||
- params.delete(:taxon)
|
||||
= paginate paginated_products
|
||||
@@ -0,0 +1,9 @@
|
||||
- if @products
|
||||
#products= render 'spree/shared/products', :products => @products, :taxon => @taxon
|
||||
- else
|
||||
#products-local
|
||||
%h2= "Products at #{current_distributor.name}"
|
||||
= render 'spree/shared/products', :products => @products_local, :taxon => @taxon
|
||||
#products-remote
|
||||
%h2 Products found elsewhere
|
||||
= render 'spree/shared/products', :products => @products_remote, :taxon => @taxon
|
||||
@@ -16,7 +16,7 @@ Spree.config do |config|
|
||||
# config.shipping_instructions = true
|
||||
config.checkout_zone = 'Australia'
|
||||
config.address_requires_state = true
|
||||
config.default_country_id = 12 # This should be Australia, see:spree/core/db/default/spree/countries.yml
|
||||
config.default_country_id = 12 # This should be Australia, see: spree/core/db/default/spree/countries.yml
|
||||
|
||||
config.searcher_class = OpenFoodWeb::Searcher
|
||||
end
|
||||
|
||||
@@ -8,6 +8,7 @@ Spree::Core::Engine.routes.prepend do
|
||||
resources :suppliers
|
||||
resources :distributors do
|
||||
get :select, :on => :member
|
||||
get :deselect, :on => :collection
|
||||
end
|
||||
|
||||
namespace :admin do
|
||||
|
||||
19
lib/open_food_web/split_products_by_distributor.rb
Normal file
19
lib/open_food_web/split_products_by_distributor.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
module OpenFoodWeb
|
||||
module SplitProductsByDistributor
|
||||
|
||||
# If a distributor is provided, split the list of products into local (at that
|
||||
# distributor) and remote (at another distributor). If a distributor is not
|
||||
# provided, perform no split.
|
||||
def split_products_by_distributor(products, distributor)
|
||||
products_local = products_remote = nil
|
||||
|
||||
if distributor
|
||||
products_local = products.select { |p| p.distributors.include? distributor }
|
||||
products_remote = products.reject { |p| p.distributors.include? distributor }
|
||||
products = nil
|
||||
end
|
||||
|
||||
[products, products_local, products_remote]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,13 +4,69 @@ require 'spree/core/current_order'
|
||||
describe Spree::DistributorsController do
|
||||
include Spree::Core::CurrentOrder
|
||||
|
||||
before do
|
||||
stub!(:before_save_new_order)
|
||||
stub!(:after_save_new_order)
|
||||
end
|
||||
|
||||
|
||||
it "selects distributors" do
|
||||
d = create(:distributor)
|
||||
|
||||
spree_get :select, :id => d.id
|
||||
response.should be_redirect
|
||||
|
||||
order = current_order(false)
|
||||
order.distributor.should == d
|
||||
end
|
||||
|
||||
it "deselects distributors" do
|
||||
d = create(:distributor)
|
||||
order = current_order(true)
|
||||
order.distributor = d
|
||||
order.save!
|
||||
|
||||
spree_get :deselect
|
||||
response.should be_redirect
|
||||
|
||||
order.reload
|
||||
order.distributor.should be_nil
|
||||
end
|
||||
|
||||
context "when a product has been added to the cart" do
|
||||
it "does not allow selecting another distributor" do
|
||||
# Given some distributors and an order with a product
|
||||
d1 = create(:distributor)
|
||||
d2 = create(:distributor)
|
||||
p = create(:product, :distributors => [d1])
|
||||
o = current_order(true)
|
||||
o.distributor = d1
|
||||
o.add_variant(p.master, 1)
|
||||
o.save!
|
||||
|
||||
# When I attempt to select a distributor
|
||||
spree_get :select, :id => d2.id
|
||||
|
||||
# Then my distributor should remain unchanged
|
||||
o.reload
|
||||
o.distributor.should == d1
|
||||
end
|
||||
|
||||
it "does not allow deselecting distributors" do
|
||||
# Given a distributor and an order with a product
|
||||
d = create(:distributor)
|
||||
p = create(:product, :distributors => [d])
|
||||
o = current_order(true)
|
||||
o.distributor = d
|
||||
o.add_variant(p.master, 1)
|
||||
o.save!
|
||||
|
||||
# When I attempt to deselect the distributor
|
||||
spree_get :deselect
|
||||
|
||||
# Then my distributor should remain unchanged
|
||||
o.reload
|
||||
o.distributor.should == d
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
28
spec/controllers/home_controller_spec.rb
Normal file
28
spec/controllers/home_controller_spec.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Spree::HomeController do
|
||||
it "loads products" do
|
||||
product = create(:product)
|
||||
spree_get :index
|
||||
assigns(:products).should == [product]
|
||||
assigns(:products_local).should be_nil
|
||||
assigns(:products_remote).should be_nil
|
||||
end
|
||||
|
||||
it "splits products by local/remote distributor when distributor is selected" do
|
||||
# Given two distributors with a product under each
|
||||
d1 = create(:distributor)
|
||||
d2 = create(:distributor)
|
||||
p1 = create(:product, :distributors => [d1])
|
||||
p2 = create(:product, :distributors => [d2])
|
||||
|
||||
# And the first distributor is selected
|
||||
controller.stub(:current_distributor).and_return(d1)
|
||||
|
||||
# When I fetch the home page, the products should be split by local/remote distributor
|
||||
spree_get :index
|
||||
assigns(:products).should be_nil
|
||||
assigns(:products_local).should == [p1]
|
||||
assigns(:products_remote).should == [p2]
|
||||
end
|
||||
end
|
||||
112
spec/controllers/orders_controller_spec.rb
Normal file
112
spec/controllers/orders_controller_spec.rb
Normal file
@@ -0,0 +1,112 @@
|
||||
require 'spec_helper'
|
||||
require 'spree/core/current_order'
|
||||
|
||||
describe Spree::OrdersController do
|
||||
include Spree::Core::CurrentOrder
|
||||
|
||||
def current_user
|
||||
controller.current_user
|
||||
end
|
||||
|
||||
|
||||
context "adding the first product to the cart" do
|
||||
it "does not add the product if the user does not specify a distributor" do
|
||||
create(:distributor)
|
||||
p = create(:product)
|
||||
|
||||
expect do
|
||||
spree_put :populate, :variants => {p.id => 1}
|
||||
end.to change(Spree::LineItem, :count).by(0)
|
||||
end
|
||||
|
||||
it "does not add the product if the user specifies a distributor that the product is not available at" do
|
||||
distributor_product = create(:distributor)
|
||||
distributor_no_product = create(:distributor)
|
||||
p = create(:product, :distributors => [distributor_product])
|
||||
|
||||
expect do
|
||||
spree_put :populate, :variants => {p.id => 1}, :distributor_id => distributor_no_product.id
|
||||
end.to change(Spree::LineItem, :count).by(0)
|
||||
end
|
||||
|
||||
it "adds the product and sets the distributor even if the order has a different distributor set" do
|
||||
distributor_product = create(:distributor)
|
||||
distributor_no_product = create(:distributor)
|
||||
p = create(:product, :distributors => [distributor_product])
|
||||
|
||||
order = current_order(true)
|
||||
order.distributor = distributor_no_product
|
||||
order.save!
|
||||
|
||||
expect do
|
||||
spree_put :populate, :variants => {p.id => 1}, :distributor_id => distributor_product.id
|
||||
end.to change(Spree::LineItem, :count).by(1)
|
||||
|
||||
order.reload.distributor.should == distributor_product
|
||||
end
|
||||
|
||||
it "sets the order's distributor" do
|
||||
# Given a product in a distributor
|
||||
d = create(:distributor)
|
||||
p = create(:product, :distributors => [d])
|
||||
|
||||
# When we add the product to our cart
|
||||
spree_put :populate, :variants => {p.id => 1}, :distributor_id => d.id
|
||||
|
||||
# Then our order should have its distributor set to the chosen distributor
|
||||
current_order(false).distributor.should == d
|
||||
end
|
||||
end
|
||||
|
||||
context "adding a subsequent product to the cart" do
|
||||
before(:each) do
|
||||
# Given a product and a distributor
|
||||
@distributor = create(:distributor)
|
||||
@product = create(:product, :distributors => [@distributor])
|
||||
|
||||
# And the product is in the cart
|
||||
spree_put :populate, :variants => {@product.id => 1}, :distributor_id => @distributor.id
|
||||
current_order(false).line_items.reload.map { |li| li.product }.should == [@product]
|
||||
current_order(false).distributor.reload.should == @distributor
|
||||
end
|
||||
|
||||
it "does not add the product if the product is not available at the order's distributor" do
|
||||
# Given a product at another distributor
|
||||
d2 = create(:distributor)
|
||||
p2 = create(:product, :distributors => [d2])
|
||||
|
||||
# When I attempt to add the product to the cart
|
||||
spree_put :populate, :variants => {p2.id => 1}, :distributor_id => d2.id
|
||||
|
||||
# Then the product should not be added to the cart
|
||||
current_order(false).line_items.reload.map { |li| li.product }.should == [@product]
|
||||
current_order(false).distributor.reload.should == @distributor
|
||||
end
|
||||
|
||||
it "does not add the product if the product is not available at the given distributor" do
|
||||
# Given a product at another distributor
|
||||
d2 = create(:distributor)
|
||||
p2 = create(:product, :distributors => [d2])
|
||||
|
||||
# When I attempt to add the product to the cart with a fake distributor_id
|
||||
spree_put :populate, :variants => {p2.id => 1}, :distributor_id => @distributor.id
|
||||
|
||||
# Then the product should not be added to the cart
|
||||
current_order(false).line_items.reload.map { |li| li.product }.should == [@product]
|
||||
current_order(false).distributor.reload.should == @distributor
|
||||
end
|
||||
|
||||
it "does not add the product if the chosen distributor is different from the order's distributor" do
|
||||
# Given a product that's available at the chosen distributor and another distributor
|
||||
d2 = create(:distributor)
|
||||
p2 = create(:product, :distributors => [@distributor, d2])
|
||||
|
||||
# When I attempt to add the product to the cart with the alternate distributor
|
||||
spree_put :populate, :variants => {p2.id => 1}, :distributor_id => d2
|
||||
|
||||
# Then the product should not be added to the cart
|
||||
current_order(false).line_items.reload.map { |li| li.product }.should == [@product]
|
||||
current_order(false).distributor.reload.should == @distributor
|
||||
end
|
||||
end
|
||||
end
|
||||
30
spec/lib/open_food_web/split_products_by_distributor_spec.rb
Normal file
30
spec/lib/open_food_web/split_products_by_distributor_spec.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe OpenFoodWeb::SplitProductsByDistributor do
|
||||
let(:products_splitter) { Class.new { include OpenFoodWeb::SplitProductsByDistributor } }
|
||||
let(:subject) { products_splitter.new }
|
||||
|
||||
|
||||
it "does nothing when no distributor is selected" do
|
||||
orig_products = (1..3).map { |i| build(:product) }
|
||||
|
||||
products, products_local, products_remote = subject.split_products_by_distributor orig_products, nil
|
||||
|
||||
products.should == orig_products
|
||||
products_local.should be_nil
|
||||
products_remote.should be_nil
|
||||
end
|
||||
|
||||
it "splits products when a distributor is selected" do
|
||||
d1 = build(:distributor)
|
||||
d2 = build(:distributor)
|
||||
orig_products = [build(:product, :distributors => [d1]),
|
||||
build(:product, :distributors => [d2])]
|
||||
|
||||
products, products_local, products_remote = subject.split_products_by_distributor orig_products, d1
|
||||
|
||||
products.should be_nil
|
||||
products_local.should == [orig_products[0]]
|
||||
products_remote.should == [orig_products[1]]
|
||||
end
|
||||
end
|
||||
47
spec/models/order_spec.rb
Normal file
47
spec/models/order_spec.rb
Normal file
@@ -0,0 +1,47 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Spree::Order do
|
||||
it "reveals permission for changing distributor" do
|
||||
p = build(:product)
|
||||
|
||||
subject.can_change_distributor?.should be_true
|
||||
subject.add_variant(p.master, 1)
|
||||
subject.can_change_distributor?.should be_false
|
||||
end
|
||||
|
||||
it "raises an exception if distributor is changed without permission" do
|
||||
p = build(:product)
|
||||
subject.add_variant(p.master, 1)
|
||||
subject.can_change_distributor?.should be_false
|
||||
|
||||
expect do
|
||||
subject.distributor = nil
|
||||
end.to raise_error "You cannot change the distributor of an order with products"
|
||||
end
|
||||
|
||||
it "reveals permission for adding products to the cart" do
|
||||
d1 = create(:distributor)
|
||||
d2 = create(:distributor)
|
||||
|
||||
p_first = create(:product, :distributors => [d1])
|
||||
p_subsequent_same_dist = create(:product, :distributors => [d1])
|
||||
p_subsequent_other_dist = create(:product, :distributors => [d2])
|
||||
|
||||
# We need to set distributor, since order.add_variant does not, and
|
||||
# we also need to save the order so that line items can be added to
|
||||
# the association.
|
||||
subject.distributor = d1
|
||||
subject.save!
|
||||
|
||||
# The first product in the cart can be added
|
||||
subject.can_add_product_to_cart?(p_first).should be_true
|
||||
subject.add_variant(p_first.master, 1)
|
||||
|
||||
# A subsequent product can be added if the distributor matches
|
||||
subject.can_add_product_to_cart?(p_subsequent_same_dist).should be_true
|
||||
subject.add_variant(p_subsequent_same_dist.master, 1)
|
||||
|
||||
# And cannot be added if it does not match
|
||||
subject.can_add_product_to_cart?(p_subsequent_other_dist).should be_false
|
||||
end
|
||||
end
|
||||
87
spec/requests/consumer/add_to_cart_spec.rb
Normal file
87
spec/requests/consumer/add_to_cart_spec.rb
Normal file
@@ -0,0 +1,87 @@
|
||||
require 'spec_helper'
|
||||
|
||||
feature %q{
|
||||
As a consumer
|
||||
I want to choose a distributor when adding products to my cart
|
||||
So that I can avoid making an order from many different distributors
|
||||
} do
|
||||
include AuthenticationWorkflow
|
||||
include WebHelper
|
||||
|
||||
scenario "adding the first product to the cart" do
|
||||
# Given a product and some distributors
|
||||
d1 = create(:distributor)
|
||||
d2 = create(:distributor)
|
||||
p = create(:product, :distributors => [d1])
|
||||
|
||||
# When I choose a distributor
|
||||
visit spree.root_path
|
||||
click_link d2.name
|
||||
|
||||
# When I add an item to my cart from a different distributor
|
||||
visit spree.product_path p
|
||||
select d1.name, :from => 'distributor_id'
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# Then the item should be in my cart
|
||||
order = Spree::Order.last
|
||||
order.line_items.first.product.should == p
|
||||
|
||||
# And my order should have its distributor set to the chosen distributor
|
||||
order.distributor.should == d1
|
||||
end
|
||||
|
||||
it "does not allow the user to change distributor after a product has been added to the cart" do
|
||||
# Given a product and some distributors
|
||||
d1 = create(:distributor)
|
||||
d2 = create(:distributor)
|
||||
p = create(:product, :distributors => [d1])
|
||||
|
||||
# When I add a product to my cart (which sets my distributor)
|
||||
visit spree.product_path p
|
||||
click_button 'Add To Cart'
|
||||
page.should have_content "You are shopping at #{d1.name}"
|
||||
|
||||
# Then I should not be able to change distributor
|
||||
visit spree.root_path
|
||||
page.should_not have_selector 'a', :text => d1.name
|
||||
page.should_not have_selector 'a', :text => d2.name
|
||||
page.should_not have_selector 'a', :text => 'Leave distributor'
|
||||
end
|
||||
|
||||
context "adding a subsequent product to the cart" do
|
||||
it "does not allow the user to choose a distributor" do
|
||||
# Given a product under a distributor
|
||||
d = create(:distributor)
|
||||
p = create(:product, :distributors => [d])
|
||||
|
||||
# And a product in my cart
|
||||
visit spree.product_path p
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# When I go to add it again, I should not have a choice of distributor
|
||||
visit spree.product_path p
|
||||
page.should_not have_selector 'select#distributor_id'
|
||||
page.should have_selector '.distributor-fixed', :text => "Your distributor for this order is #{d.name}"
|
||||
end
|
||||
|
||||
it "does not allow the user to add a product from another distributor" do
|
||||
# Given two products, each at a different distributor
|
||||
d1 = create(:distributor)
|
||||
d2 = create(:distributor)
|
||||
p1 = create(:product, :distributors => [d1])
|
||||
p2 = create(:product, :distributors => [d2])
|
||||
|
||||
# When I add one of them to my cart
|
||||
visit spree.product_path p1
|
||||
click_button 'Add To Cart'
|
||||
|
||||
# And I attempt to add the other
|
||||
visit spree.product_path p2
|
||||
|
||||
# Then I should not be allowed to add the product
|
||||
page.should_not have_selector "button#add-to-cart-button"
|
||||
page.should have_content "Please complete your order at #{d1.name} before shopping with another distributor."
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,12 +8,10 @@ feature %q{
|
||||
include AuthenticationWorkflow
|
||||
include WebHelper
|
||||
|
||||
background do
|
||||
scenario "viewing a list of distributors" do
|
||||
# Given some distributors
|
||||
3.times { create(:distributor) }
|
||||
end
|
||||
|
||||
scenario "viewing a list of distributors" do
|
||||
# When I go to the home page
|
||||
visit spree.root_path
|
||||
|
||||
@@ -37,33 +35,93 @@ feature %q{
|
||||
page.should have_selector '#current-distributor', :text => 'You are shopping at Melb Uni Co-op'
|
||||
end
|
||||
|
||||
it "splits the product listing by local/remote distributor"
|
||||
it "splits the product listing by local/remote distributor" do
|
||||
# Given two distributors, with a product under each, and each product under a taxon
|
||||
taxonomy = Spree::Taxonomy.find_by_name('Products') || create(:taxonomy, :name => 'Products')
|
||||
taxonomy_root = taxonomy.root
|
||||
taxon = create(:taxon, :name => 'Taxon one', :parent_id => taxonomy_root.id)
|
||||
d1 = create(:distributor)
|
||||
d2 = create(:distributor)
|
||||
p1 = create(:product, :distributors => [d1], :taxons => [taxon])
|
||||
p2 = create(:product, :distributors => [d2], :taxons => [taxon])
|
||||
|
||||
it "allows the user to leave the distributor"
|
||||
# When I select the first distributor
|
||||
visit spree.root_path
|
||||
click_link d1.name
|
||||
|
||||
context "viewing a product" do
|
||||
it "provides a choice of distributor when adding to cart" # Test product at remote distributor
|
||||
it "displays the local distributor as the default choice when available for the current product"
|
||||
# Then I should see products split by local/remote distributor
|
||||
# on the home page, the products page, the search results page and the taxon page
|
||||
[spree.root_path,
|
||||
spree.products_path,
|
||||
spree.products_path(:keywords => 'Product'),
|
||||
spree.nested_taxons_path(taxon.permalink)
|
||||
].each do |path|
|
||||
|
||||
visit path
|
||||
page.should_not have_selector '#products'
|
||||
page.should have_selector '#products-local', :text => p1.name
|
||||
page.should have_selector '#products-remote', :text => p2.name
|
||||
end
|
||||
end
|
||||
|
||||
it "allows the user to leave the distributor" do
|
||||
# Given a distributor
|
||||
d = create(:distributor, :name => 'Melb Uni Co-op')
|
||||
|
||||
# When I select the distributor and then leave it
|
||||
visit spree.root_path
|
||||
click_link d.name
|
||||
click_link 'Leave distributor'
|
||||
|
||||
# Then I should have left the distributor
|
||||
page.should_not have_selector '#current-distributor', :text => 'You are shopping at Melb Uni Co-op'
|
||||
end
|
||||
|
||||
context "viewing a product, it provides a choice of distributor when adding to cart" do
|
||||
it "works when no distributor is chosen" do
|
||||
# Given a distributor and a product under it
|
||||
distributor = create(:distributor)
|
||||
product = create(:product, :distributors => [distributor])
|
||||
|
||||
# When we view the product
|
||||
visit spree.product_path(product)
|
||||
|
||||
# Then we should see a choice of distributor, with no default
|
||||
page.should have_selector "select#distributor_id option", :text => distributor.name
|
||||
page.should_not have_selector "select#distributor_id option[selected='selected']"
|
||||
end
|
||||
|
||||
it "displays the local distributor as the default choice when available for the current product" do
|
||||
# Given a distributor and a product under it
|
||||
distributor = create(:distributor)
|
||||
product = create(:product, :distributors => [distributor])
|
||||
|
||||
# When we select the distributor and view the product
|
||||
visit spree.root_path
|
||||
click_link distributor.name
|
||||
visit spree.product_path(product)
|
||||
|
||||
# Then we should see our distributor as the default option when adding the item to our cart
|
||||
page.should have_selector "select#distributor_id option[value='#{distributor.id}'][selected='selected']"
|
||||
end
|
||||
|
||||
it "works when viewing a product from a remote distributor" do
|
||||
# Given two distributors and a product under one
|
||||
distributor_product = create(:distributor)
|
||||
distributor_no_product = create(:distributor)
|
||||
product = create(:product, :distributors => [distributor_product])
|
||||
|
||||
# When we select the distributor without the product and then view the product
|
||||
visit spree.root_path
|
||||
click_link distributor_no_product.name
|
||||
visit spree.product_path(product)
|
||||
|
||||
# Then we should see a choice of distributor,
|
||||
# with no default and no option for the distributor that the product does not belong to
|
||||
page.should have_selector "select#distributor_id option", :text => distributor_product.name
|
||||
page.should_not have_selector "select#distributor_id option", :text => distributor_no_product.name
|
||||
page.should_not have_selector "select#distributor_id option[selected='selected']"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
# scenario "browsing products by distributor" do
|
||||
# # Given a product at each of two distributors
|
||||
# d1 = create(:distributor)
|
||||
# d2 = create(:distributor)
|
||||
# p1 = create(:product, :distributors => [d1])
|
||||
# p2 = create(:product, :distributors => [d2])
|
||||
|
||||
# # When I go to the home page, I should see both products
|
||||
# visit spree.root_path
|
||||
# page.should have_content p1.name
|
||||
# page.should have_content p2.name
|
||||
|
||||
# # When I filter by one distributor, I should see only the product from that distributor
|
||||
# click_link d1.name
|
||||
# page.should have_content p1.name
|
||||
# page.should_not have_content p2.name
|
||||
# end
|
||||
end
|
||||
|
||||
@@ -3,15 +3,15 @@ require 'spork'
|
||||
#uncomment the following line to use spork with the debugger
|
||||
#require 'spork/ext/ruby-debug'
|
||||
|
||||
ENV["RAILS_ENV"] ||= 'test'
|
||||
require File.expand_path("../../config/environment", __FILE__)
|
||||
require 'rspec/rails'
|
||||
require 'rspec/autorun'
|
||||
require 'capybara'
|
||||
require 'database_cleaner'
|
||||
|
||||
|
||||
Spork.prefork do
|
||||
ENV["RAILS_ENV"] ||= 'test'
|
||||
require File.expand_path("../../config/environment", __FILE__)
|
||||
require 'rspec/rails'
|
||||
require 'rspec/autorun'
|
||||
require 'capybara'
|
||||
require 'database_cleaner'
|
||||
|
||||
# Requires supporting ruby files with custom matchers and macros, etc,
|
||||
# in spec/support/ and its subdirectories.
|
||||
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
|
||||
@@ -67,11 +67,18 @@ Spork.prefork do
|
||||
config.include Spree::UrlHelpers
|
||||
config.include Spree::Core::TestingSupport::ControllerRequests, :type => :controller
|
||||
config.include Devise::TestHelpers, :type => :controller
|
||||
config.include FactoryGirl::Syntax::Methods
|
||||
end
|
||||
end
|
||||
|
||||
Spork.each_run do
|
||||
# Dir["#{File.dirname(__FILE__)}/../app/**/*.rb"].each {|f| load f}
|
||||
# Rails.application.reload_routes!
|
||||
Dir["#{File.dirname(__FILE__)}/../app/**/*.rb"].each {|f| load f}
|
||||
Dir["#{File.dirname(__FILE__)}/../lib/**/*.rb"].each {|f| load f}
|
||||
|
||||
Rails.application.reload_routes!
|
||||
|
||||
require 'factory_girl_rails'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.include FactoryGirl::Syntax::Methods
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user