mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Merge branch 'master' into bom
This commit is contained in:
@@ -2,6 +2,7 @@ angular.module("Checkout").controller "CheckoutCtrl", ($scope, $rootScope) ->
|
||||
$scope.require_ship_address = false
|
||||
$scope.shipping_method = -1
|
||||
$scope.payment_method = -1
|
||||
$scope.same_as_billing = true
|
||||
|
||||
$scope.shippingMethodChanged = ->
|
||||
$scope.require_ship_address = $("#order_shipping_method_id_" + $scope.shipping_method).attr("data-require-ship-address")
|
||||
|
||||
@@ -2,3 +2,10 @@ angular.module("Shop").controller "ProductsCtrl", ($scope, $rootScope, Product,
|
||||
$scope.data = Product.data
|
||||
$scope.order_cycle = OrderCycle.order_cycle
|
||||
Product.update()
|
||||
|
||||
$scope.productPrice = (product) ->
|
||||
if product.variants.length > 0
|
||||
prices = (v.price for v in product.variants)
|
||||
Math.min.apply(null, prices)
|
||||
else
|
||||
product.price
|
||||
|
||||
@@ -11,10 +11,11 @@ class Shop::ShopController < BaseController
|
||||
|
||||
def products
|
||||
unless @products = current_order_cycle.andand
|
||||
.products_distributed_by(@distributor).andand
|
||||
.select(&:has_stock?).andand
|
||||
.products_distributed_by(current_distributor).andand
|
||||
.select { |p| p.has_stock_for_distribution?(current_order_cycle, current_distributor) }.andand
|
||||
.sort_by {|p| p.name }
|
||||
render json: "", status: 404
|
||||
|
||||
render json: "", status: 404
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -94,8 +94,11 @@ class OrderCycle < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def variants_distributed_by(distributor)
|
||||
self.exchanges.where(:sender_id => self.coordinator, :receiver_id => distributor).
|
||||
map(&:variants).flatten.uniq
|
||||
Spree::Variant.
|
||||
joins(:exchanges).
|
||||
merge(Exchange.outgoing).
|
||||
where('exchanges.order_cycle_id = ?', self).
|
||||
where('exchanges.receiver_id = ?', distributor)
|
||||
end
|
||||
|
||||
def products_distributed_by(distributor)
|
||||
@@ -145,6 +148,11 @@ class OrderCycle < ActiveRecord::Base
|
||||
|
||||
|
||||
# -- Fees
|
||||
|
||||
# TODO: The boundary of this class is ill-defined here. OrderCycle should not know about
|
||||
# EnterpriseFeeApplicator. Clients should be able to query it for relevant EnterpriseFees.
|
||||
# This logic would fit better in another service object.
|
||||
|
||||
def fees_for(variant, distributor)
|
||||
per_item_enterprise_fee_applicators_for(variant, distributor).sum do |applicator|
|
||||
# Spree's Calculator interface accepts Orders or LineItems,
|
||||
|
||||
@@ -15,7 +15,7 @@ class AbilityDecorator
|
||||
end
|
||||
|
||||
can [:admin, :index, :read, :create, :edit, :update, :search, :destroy], Spree::Variant
|
||||
can [:admin, :index, :read, :create, :edit], Spree::ProductProperty
|
||||
can [:admin, :index, :read, :create, :edit, :destroy], Spree::ProductProperty
|
||||
can [:admin, :index, :read, :create, :edit], Spree::Image
|
||||
|
||||
can [:admin, :index, :read, :search], Spree::Taxon
|
||||
|
||||
@@ -99,12 +99,27 @@ Spree::Product.class_eval do
|
||||
def product_distribution_for(distributor)
|
||||
self.product_distributions.find_by_distributor_id(distributor)
|
||||
end
|
||||
|
||||
|
||||
def variants_for(order_cycle, distributor)
|
||||
self.variants.where('spree_variants.id IN (?)', order_cycle.variants_distributed_by(distributor))
|
||||
end
|
||||
|
||||
# overriding to check self.on_demand as well
|
||||
def has_stock?
|
||||
has_variants? ? variants.any?(&:in_stock?) : (on_demand || master.in_stock?)
|
||||
end
|
||||
|
||||
def has_stock_for_distribution?(order_cycle, distributor)
|
||||
# This product has stock for a distribution if it is available on-demand
|
||||
# or if one of its variants in the distribution is in stock
|
||||
(!has_variants? && on_demand) ||
|
||||
variants_distributed_by(order_cycle, distributor).any? { |v| v.in_stock? }
|
||||
end
|
||||
|
||||
def variants_distributed_by(order_cycle, distributor)
|
||||
order_cycle.variants_distributed_by(distributor).where(product_id: self)
|
||||
end
|
||||
|
||||
# Build a product distribution for each distributor
|
||||
def build_product_distributions_for_user user
|
||||
Enterprise.is_distributor.managed_by(user).each do |distributor|
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
Spree::Variant.class_eval do
|
||||
has_many :exchange_variants
|
||||
has_many :exchanges, through: :exchange_variants
|
||||
|
||||
attr_accessible :unit_value, :unit_description
|
||||
|
||||
validates_presence_of :unit_value,
|
||||
@@ -11,6 +14,8 @@ Spree::Variant.class_eval do
|
||||
|
||||
after_save :update_units
|
||||
|
||||
scope :in_stock, where('spree_variants.count_on_hand > 0 OR spree_variants.on_demand=?', true)
|
||||
|
||||
|
||||
def price_with_fees(distributor, order_cycle)
|
||||
price + fees_for(distributor, order_cycle)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/ insert_bottom "[data-hook='admin_shipping_method_form_availability_fields'] > fieldset"
|
||||
|
||||
= f.field_container :shipping_requirements do
|
||||
= f.label :require_ship_address, "Requires shipping address?"
|
||||
= f.check_box :require_ship_address
|
||||
@@ -21,20 +21,27 @@
|
||||
= f.fields_for :bill_address do |ba|
|
||||
.row
|
||||
.large-12.columns
|
||||
= ba.text_field :address1, label: "Billing Address"
|
||||
= ba.text_field :address1, label: "Billing Address",
|
||||
"ng-model" => "billing_address1"
|
||||
.row
|
||||
.large-12.columns
|
||||
= ba.text_field :address2
|
||||
= ba.text_field :address2,
|
||||
"ng-model" => "billing_address2"
|
||||
.row
|
||||
.large-6.columns
|
||||
= ba.text_field :city
|
||||
= ba.text_field :city,
|
||||
"ng-model" => "billing_city"
|
||||
|
||||
.large-6.columns
|
||||
= ba.select :country_id, Spree::Country.order(:name).select([:id, :name]).map{|c| [c.name, c.id]}
|
||||
= ba.select :country_id, Spree::Country.order(:name).select([:id, :name]).map{|c| [c.name, c.id]},
|
||||
"ng-model" => "billing_country_id"
|
||||
.row
|
||||
.large-6.columns
|
||||
= ba.select :state_id, Spree::State.order(:name).select([:id, :name]).map{|c| [c.name, c.id]}
|
||||
= ba.select :state_id, Spree::State.order(:name).select([:id, :name]).map{|c| [c.name, c.id]},
|
||||
"ng-model" => "billing_state_id"
|
||||
.large-6.columns.right
|
||||
= ba.text_field :zipcode
|
||||
= ba.text_field :zipcode,
|
||||
"ng-model" => "billing_zipcode"
|
||||
|
||||
%fieldset#shipping
|
||||
|
||||
@@ -52,26 +59,42 @@
|
||||
|
||||
= f.fields_for :ship_address do |sa|
|
||||
#ship_address{"ng-show" => "require_ship_address"}
|
||||
.row
|
||||
.large-12.columns
|
||||
= sa.text_field :address1
|
||||
.row
|
||||
.large-12.columns
|
||||
= sa.text_field :address2
|
||||
%label
|
||||
= check_box_tag :same_as_billing, true, true,
|
||||
"ng-model" => "same_as_billing"
|
||||
Shipping address same as billing address?
|
||||
|
||||
%div.visible{"ng-show" => "!same_as_billing"}
|
||||
.row
|
||||
.large-12.columns
|
||||
= sa.text_field :address1
|
||||
.row
|
||||
.large-12.columns
|
||||
= sa.text_field :address2
|
||||
|
||||
.row
|
||||
.large-6.columns
|
||||
= sa.text_field :city
|
||||
.large-6.columns
|
||||
= sa.select :country_id, Spree::Country.order(:name).select([:id, :name]).map{|c| [c.name, c.id]}
|
||||
.row
|
||||
.large-6.columns.right
|
||||
= sa.text_field :zipcode
|
||||
.row
|
||||
.large-6.columns
|
||||
= sa.text_field :firstname
|
||||
.large-6.columns
|
||||
= sa.text_field :lastname
|
||||
|
||||
#ship_address_hidden{"ng-show" => "same_as_billing"}
|
||||
= sa.hidden_field :address1, "ng-value" => "billing_address1"
|
||||
= sa.hidden_field :address2, "ng-value" => "billing_address2"
|
||||
= sa.hidden_field :city, "ng-value" => "billing_city"
|
||||
= sa.hidden_field :country_id, "ng-value" => "billing_country_id"
|
||||
= sa.hidden_field :zipcode, "ng-value" => "billing_zipcode"
|
||||
= sa.hidden_field :firstname, "ng-value" => "billing_firstname"
|
||||
= sa.hidden_field :lastname, "ng-value" => "billing_lastname"
|
||||
|
||||
.row
|
||||
.large-6.columns
|
||||
= sa.text_field :city
|
||||
.large-6.columns
|
||||
= sa.select :country_id, Spree::Country.order(:name).select([:id, :name]).map{|c| [c.name, c.id]}
|
||||
.row
|
||||
.large-6.columns.right
|
||||
= sa.text_field :zipcode
|
||||
.row
|
||||
.large-6.columns
|
||||
= sa.text_field :firstname
|
||||
.large-6.columns
|
||||
= sa.text_field :lastname
|
||||
%fieldset#payment
|
||||
%legend Payment Details
|
||||
- current_order.available_payment_methods.each do |method|
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
%th.bulk Bulk
|
||||
%th.price.text-right Price
|
||||
%tbody{"ng-repeat" => "product in data.products | filter:query"}
|
||||
%tr.product
|
||||
%tr{"class" => "product product-{{ product.id }}"}
|
||||
%td.name
|
||||
%img{"ng-src" => "{{ product.master.images[0].small_url }}"}
|
||||
%div
|
||||
@@ -46,9 +46,9 @@
|
||||
name: "variant_attributes[{{product.master.id}}][max_quantity]"}
|
||||
%td.price.text-right
|
||||
%small{"ng-show" => "(product.variants.length > 0)"} from
|
||||
{{ product.price | currency }}
|
||||
{{ productPrice(product) | currency }}
|
||||
%tr.product-description
|
||||
%td{colspan: 2}{{ product.notes | truncate:80 }}
|
||||
%tr{"ng-repeat" => "variant in product.variants", "ng-show" => "product.show_variants"}
|
||||
%tr.variant{"ng-repeat" => "variant in product.variants", "ng-show" => "product.show_variants"}
|
||||
= render partial: "shop/shop/variant"
|
||||
%input.button.right{type: :submit, value: "Check Out"}
|
||||
|
||||
@@ -22,19 +22,17 @@ child :master => :master do
|
||||
end
|
||||
end
|
||||
|
||||
child :variants => :variants do |variant|
|
||||
attributes :id, :is_master, :count_on_hand, :options_text, :count_on_hand, :on_demand, :group_buy
|
||||
node do |variant|
|
||||
{
|
||||
price: variant.price_with_fees(current_distributor, current_order_cycle)
|
||||
node :variants do |product|
|
||||
product.variants_for(current_order_cycle, current_distributor).in_stock.map do |v|
|
||||
{id: v.id,
|
||||
is_master: v.is_master,
|
||||
count_on_hand: v.count_on_hand,
|
||||
options_text: v.options_text,
|
||||
on_demand: v.on_demand,
|
||||
price: v.price_with_fees(current_distributor, current_order_cycle),
|
||||
images: v.images.map { |i| {id: i.id, alt: i.alt, small_url: i.attachment.url(:small, false)} }
|
||||
}
|
||||
end
|
||||
child :images => :images do
|
||||
attributes :id, :alt
|
||||
node do |img|
|
||||
{:small_url => img.attachment.url(:small, false)}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
child :taxons => :taxons do |taxon|
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
%input{ 'ng-model' => 'product.master.unit_value_with_description', :name => 'master_unit_value_with_description', 'ofn-track-product' => 'master.unit_value_with_description', :type => 'text', :placeholder => 'value', 'ng-show' => "!hasVariants(product) && hasUnit(product)" }
|
||||
%input{ 'ng-model' => 'product.variant_unit_name', :name => 'variant_unit_name', 'ofn-track-product' => 'variant_unit_name', :placeholder => 'unit', 'ng-show' => "product.variant_unit_with_scale == 'items'", :type => 'text' }
|
||||
%td{ 'ng-show' => 'columns.price.visible' }
|
||||
%input{ 'ng-model' => 'product.price', 'ofn-decimal' => :true, :name => 'price', 'ofn-track-product' => 'price', :type => 'text' }
|
||||
%input{ 'ng-model' => 'product.price', 'ofn-decimal' => :true, :name => 'price', 'ofn-track-product' => 'price', :type => 'text', 'ng-hide' => 'hasVariants(product)' }
|
||||
%td{ 'ng-show' => 'columns.on_hand.visible' }
|
||||
%span{ 'ng-bind' => 'product.on_hand', :name => 'on_hand', 'ng-show' => '!hasOnDemandVariants(product) && (hasVariants(product) || product.on_demand)' }
|
||||
%input.field{ 'ng-model' => 'product.on_hand', :name => 'on_hand', 'ofn-track-product' => 'on_hand', 'ng-hide' => 'hasVariants(product) || product.on_demand', :type => 'number' }
|
||||
|
||||
@@ -85,18 +85,21 @@ feature %q{
|
||||
page.should have_field "available_on", with: p2.available_on.strftime("%F %T")
|
||||
end
|
||||
|
||||
it "displays a price input for each product (ie. for master variant)" do
|
||||
it "displays a price input for each product without variants (ie. for master variant)" do
|
||||
p1 = FactoryGirl.create(:product)
|
||||
p2 = FactoryGirl.create(:product)
|
||||
p1.price = 22.00
|
||||
p2.price = 44.00
|
||||
p1.save!
|
||||
p2.save!
|
||||
p3 = FactoryGirl.create(:product)
|
||||
v = FactoryGirl.create(:variant, product: p3)
|
||||
|
||||
p1.update_attribute :price, 22.0
|
||||
p2.update_attribute :price, 44.0
|
||||
p3.update_attribute :price, 66.0
|
||||
|
||||
visit '/admin/products/bulk_edit'
|
||||
|
||||
page.should have_field "price", with: "22.0"
|
||||
page.should have_field "price", with: "44.0"
|
||||
page.should_not have_field "price", with: "66.0", visible: true
|
||||
end
|
||||
|
||||
it "displays an on hand count input for each product (ie. for master variant) if no regular variants exist" do
|
||||
|
||||
@@ -70,6 +70,14 @@ feature 'shipping methods' do
|
||||
login_to_admin_as enterprise_user
|
||||
end
|
||||
|
||||
it "lets me choose whether a shipping address is required" do
|
||||
click_link "Enterprises"
|
||||
within(".enterprise-#{distributor1.id}") { click_link 'Shipping Methods' }
|
||||
click_link 'New Shipping Method'
|
||||
|
||||
page.should have_content "Requires shipping address?"
|
||||
end
|
||||
|
||||
it "creates shipping methods" do
|
||||
click_link 'Enterprises'
|
||||
within(".enterprise-#{distributor1.id}") { click_link 'Shipping Methods' }
|
||||
|
||||
@@ -83,4 +83,34 @@ feature %q{
|
||||
page.should_not have_field "variant_unit_value"
|
||||
page.should_not have_field "variant_unit_description"
|
||||
end
|
||||
|
||||
|
||||
context "as an enterprise user" do
|
||||
before(:each) do
|
||||
@new_user = create_enterprise_user
|
||||
@supplier = create(:supplier_enterprise)
|
||||
@new_user.enterprise_roles.build(enterprise: @supplier).save
|
||||
|
||||
login_to_admin_as @new_user
|
||||
end
|
||||
|
||||
scenario "deleting product properties", js: true do
|
||||
# Given a product with a property
|
||||
p = create(:simple_product, supplier: @supplier)
|
||||
p.set_property('fooprop', 'fooval')
|
||||
|
||||
# When I navigate to the product properties page
|
||||
visit spree.admin_product_product_properties_path(p)
|
||||
page.should have_field 'product_product_properties_attributes_0_property_name', with: 'fooprop', visible: true
|
||||
page.should have_field 'product_product_properties_attributes_0_value', with: 'fooval', visible: true
|
||||
|
||||
# And I delete the property
|
||||
page.all('a.remove_fields').first.click
|
||||
wait_until { p.reload.property('fooprop').nil? }
|
||||
|
||||
# Then the property should have been deleted
|
||||
page.should_not have_field 'product_product_properties_attributes_0_property_name', with: 'fooprop', visible: true
|
||||
page.should_not have_field 'product_product_properties_attributes_0_value', with: 'fooval', visible: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -133,15 +133,35 @@ feature "As a consumer I want to check out my cart", js: true do
|
||||
page.should have_content "Donkeys"
|
||||
end
|
||||
|
||||
it "doesn't show ship address forms " do
|
||||
it "doesn't show ship address forms when a shipping method wants no address" do
|
||||
choose(sm2.name)
|
||||
find("#ship_address").visible?.should be_false
|
||||
end
|
||||
|
||||
it "shows ship address forms when selected shipping method requires one" do
|
||||
context "When shipping method requires an address" do
|
||||
before do
|
||||
choose(sm1.name)
|
||||
end
|
||||
it "shows the hidden ship address fields by default" do
|
||||
check "Shipping address same as billing address?"
|
||||
find("#ship_address_hidden").visible?.should be_true
|
||||
find("#ship_address > div.visible").visible?.should be_false
|
||||
end
|
||||
|
||||
it "shows ship address forms when 'same as billing address' is unchecked" do
|
||||
uncheck "Shipping address same as billing address?"
|
||||
find("#ship_address_hidden").visible?.should be_false
|
||||
find("#ship_address > div.visible").visible?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
it "copies billing address to hidden shipping address fields" do
|
||||
choose(sm1.name)
|
||||
save_and_open_page
|
||||
find("#ship_address").visible?.should be_true
|
||||
check "Shipping address same as billing address?"
|
||||
fill_in "Billing Address", with: "testy"
|
||||
within "#ship_address_hidden" do
|
||||
find("#order_ship_address_attributes_address1").value.should == "testy"
|
||||
end
|
||||
end
|
||||
|
||||
describe "with payment methods" do
|
||||
@@ -204,7 +224,6 @@ def select_order_cycle
|
||||
end
|
||||
|
||||
def add_product_to_cart
|
||||
|
||||
fill_in "variants[#{product.master.id}]", with: 5
|
||||
fill_in "variants[#{product.master.id}]", with: product.master.on_hand - 1
|
||||
first("form.custom > input.button.right").click
|
||||
end
|
||||
|
||||
@@ -12,6 +12,7 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
visit "/"
|
||||
click_link distributor.name
|
||||
end
|
||||
|
||||
it "shows a distributor" do
|
||||
visit shop_path
|
||||
page.should have_text distributor.name
|
||||
@@ -23,7 +24,7 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
first("#about img")['src'].should == distributor.promo_image.url(:large)
|
||||
end
|
||||
|
||||
describe "With products in order cycles" do
|
||||
describe "with products in order cycles" do
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
let(:product) { create(:product, supplier: supplier) }
|
||||
let(:order_cycle) { create(:order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise)) }
|
||||
@@ -112,16 +113,18 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
end
|
||||
end
|
||||
|
||||
describe "After selecting an order cycle with products visible" do
|
||||
describe "after selecting an order cycle with products visible" do
|
||||
let(:oc) { create(:simple_order_cycle, distributors: [distributor]) }
|
||||
let(:product) { create(:simple_product) }
|
||||
let(:variant) { create(:variant, product: product) }
|
||||
let(:product) { create(:simple_product, price: 10) }
|
||||
let(:variant1) { create(:variant, product: product, price: 20) }
|
||||
let(:variant2) { create(:variant, product: product, price: 30) }
|
||||
let(:exchange) { Exchange.find(oc.exchanges.to_enterprises(distributor).outgoing.first.id) }
|
||||
|
||||
before do
|
||||
exchange.update_attribute :pickup_time, "frogs"
|
||||
exchange.variants << product.master
|
||||
exchange.variants << variant
|
||||
exchange.variants << variant1
|
||||
exchange.variants << variant2
|
||||
visit shop_path
|
||||
select "frogs", :from => "order_cycle_id"
|
||||
exchange
|
||||
@@ -132,14 +135,14 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
end
|
||||
|
||||
it "collapses variants by default" do
|
||||
page.should_not have_text variant.options_text
|
||||
page.should_not have_text variant1.options_text
|
||||
end
|
||||
|
||||
it "expands variants" do
|
||||
find(".expand").trigger "click"
|
||||
page.should have_text variant.options_text
|
||||
page.should have_text variant1.options_text
|
||||
find(".collapse").trigger "click"
|
||||
page.should_not have_text variant.options_text
|
||||
page.should_not have_text variant1.options_text
|
||||
end
|
||||
|
||||
it "uses the adjusted price" do
|
||||
@@ -150,17 +153,35 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
|
||||
visit shop_path
|
||||
select "frogs", :from => "order_cycle_id"
|
||||
page.should have_content "$#{(product.price + 23.00)}"
|
||||
|
||||
# All prices are as above plus $23 in fees
|
||||
|
||||
# Page should not have product.price (with or without fee)
|
||||
page.should_not have_selector 'tr.product > td', text: "from $10.00"
|
||||
page.should_not have_selector 'tr.product > td', text: "from $33.00"
|
||||
|
||||
# Page should have variant prices (with fee)
|
||||
find(".expand").trigger 'click'
|
||||
page.should have_selector 'tr.variant > td.price', text: "$43.00"
|
||||
page.should have_selector 'tr.variant > td.price', text: "$53.00"
|
||||
|
||||
# Product price should be listed as the lesser of these
|
||||
page.should have_selector 'tr.product > td', text: "from $43.00"
|
||||
end
|
||||
end
|
||||
|
||||
describe "Filtering on hand and on demand products" do
|
||||
describe "filtering on hand and on demand products" do
|
||||
let(:oc) { create(:simple_order_cycle, distributors: [distributor]) }
|
||||
let(:p1) { create(:simple_product, on_demand: false) }
|
||||
let(:p2) { create(:simple_product, on_demand: true) }
|
||||
let(:p3) { create(:simple_product, on_demand: false) }
|
||||
let(:p4) { create(:simple_product, on_demand: false) }
|
||||
let(:v1) { create(:variant, product: p4) }
|
||||
let(:p5) { create(:simple_product, on_demand: false) }
|
||||
let(:v1) { create(:variant, product: p4, unit_value: 2) }
|
||||
let(:v2) { create(:variant, product: p4, unit_value: 3, on_demand: false) }
|
||||
let(:v3) { create(:variant, product: p4, unit_value: 4, on_demand: true) }
|
||||
let(:v4) { create(:variant, product: p5) }
|
||||
let(:v5) { create(:variant, product: p5) }
|
||||
|
||||
before do
|
||||
p1.master.count_on_hand = 1
|
||||
@@ -169,26 +190,47 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
p2.master.update_attribute(:count_on_hand, 0)
|
||||
p3.master.update_attribute(:count_on_hand, 0)
|
||||
v1.update_attribute(:count_on_hand, 1)
|
||||
v2.update_attribute(:count_on_hand, 0)
|
||||
v3.update_attribute(:count_on_hand, 0)
|
||||
v4.update_attribute(:count_on_hand, 1)
|
||||
v5.update_attribute(:count_on_hand, 0)
|
||||
exchange = Exchange.find(oc.exchanges.to_enterprises(distributor).outgoing.first.id)
|
||||
exchange.update_attribute :pickup_time, "frogs"
|
||||
exchange.variants << p1.master
|
||||
exchange.variants << p2.master
|
||||
exchange.variants << p3.master
|
||||
exchange.variants << v1
|
||||
exchange.variants << v1
|
||||
exchange.variants << v2
|
||||
exchange.variants << v3
|
||||
# v4 is in stock but not in distribution
|
||||
# v5 is out of stock and in the distribution
|
||||
# Neither should display, nor should their product, p5
|
||||
exchange.variants << v5
|
||||
visit shop_path
|
||||
select "frogs", :from => "order_cycle_id"
|
||||
exchange
|
||||
end
|
||||
|
||||
it "shows on hand products" do
|
||||
it "filters products based on availability" do
|
||||
# It shows on hand products
|
||||
page.should have_content p1.name
|
||||
page.should have_content p4.name
|
||||
end
|
||||
it "shows on demand products" do
|
||||
|
||||
# It shows on demand products
|
||||
page.should have_content p2.name
|
||||
end
|
||||
it "does not show products that are neither on hand or on demand" do
|
||||
|
||||
# It does not show products that are neither on hand or on demand
|
||||
page.should_not have_content p3.name
|
||||
|
||||
# It shows on demand variants
|
||||
within(".product-#{p4.id}") { find(".expand", visible: true).trigger "click" }
|
||||
page.should have_content v3.options_text
|
||||
|
||||
# It does not show variants that are neither on hand or on demand
|
||||
page.should_not have_content v2.options_text
|
||||
|
||||
# It does not show products that have no available variants in this distribution
|
||||
page.should_not have_content p5.name
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ describe 'All controllers', ->
|
||||
ctrl = null
|
||||
scope = null
|
||||
event = null
|
||||
rootScope = null
|
||||
Product = null
|
||||
|
||||
beforeEach ->
|
||||
@@ -12,29 +11,38 @@ describe 'All controllers', ->
|
||||
all: ->
|
||||
update: ->
|
||||
data: "testy mctest"
|
||||
OrderCycle =
|
||||
order_cycle: {}
|
||||
|
||||
inject ($controller, $rootScope) ->
|
||||
rootScope = $rootScope
|
||||
scope = $rootScope.$new()
|
||||
ctrl = $controller 'ProductsCtrl', {$scope: scope, Product : Product}
|
||||
inject ($controller) ->
|
||||
scope = {}
|
||||
ctrl = $controller 'ProductsCtrl', {$scope: scope, Product: Product, OrderCycle: OrderCycle}
|
||||
|
||||
it 'Fetches products from Product', ->
|
||||
it 'fetches products from Product', ->
|
||||
expect(scope.data).toEqual 'testy mctest'
|
||||
|
||||
describe "determining the price to display for a product", ->
|
||||
it "displays the product price when the product does not have variants", ->
|
||||
product = {variants: [], price: 12.34}
|
||||
expect(scope.productPrice(product)).toEqual 12.34
|
||||
|
||||
it "displays the minimum variant price when the product has variants", ->
|
||||
product =
|
||||
price: 11
|
||||
variants: [{price: 22}, {price: 33}]
|
||||
expect(scope.productPrice(product)).toEqual 22
|
||||
|
||||
describe 'OrderCycleCtrl', ->
|
||||
ctrl = null
|
||||
scope = null
|
||||
event = null
|
||||
rootScope = null
|
||||
product_ctrl = null
|
||||
OrderCycle = null
|
||||
|
||||
beforeEach ->
|
||||
module 'Shop'
|
||||
scope = {}
|
||||
inject ($controller, $rootScope) ->
|
||||
rootScope = $rootScope
|
||||
scope = $rootScope.$new()
|
||||
inject ($controller) ->
|
||||
scope = {}
|
||||
ctrl = $controller 'OrderCycleCtrl', {$scope: scope}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ module Spree
|
||||
end
|
||||
|
||||
it "should be able to read/write their enterprises' product properties" do
|
||||
should have_ability([:admin, :index, :read, :create, :edit], for: Spree::ProductProperty)
|
||||
should have_ability([:admin, :index, :read, :create, :edit, :destroy], for: Spree::ProductProperty)
|
||||
end
|
||||
|
||||
it "should be able to read/write their enterprises' product images" do
|
||||
|
||||
@@ -303,6 +303,42 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
describe "finding variants for an order cycle and hub" do
|
||||
let(:oc) { create(:simple_order_cycle) }
|
||||
let(:s) { create(:supplier_enterprise) }
|
||||
let(:d1) { create(:distributor_enterprise) }
|
||||
let(:d2) { create(:distributor_enterprise) }
|
||||
|
||||
let(:p1) { create(:simple_product) }
|
||||
let(:p2) { create(:simple_product) }
|
||||
let(:v1) { create(:variant, product: p1) }
|
||||
let(:v2) { create(:variant, product: p2) }
|
||||
|
||||
let(:p_external) { create(:simple_product) }
|
||||
let(:v_external) { create(:variant, product: p_external) }
|
||||
|
||||
let!(:ex_in) { create(:exchange, order_cycle: oc, sender: s, receiver: oc.coordinator,
|
||||
variants: [v1, v2]) }
|
||||
let!(:ex_out1) { create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d1,
|
||||
variants: [v1]) }
|
||||
let!(:ex_out2) { create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d2,
|
||||
variants: [v2]) }
|
||||
|
||||
it "returns variants in the order cycle and distributor" do
|
||||
p1.variants_for(oc, d1).should == [v1]
|
||||
p2.variants_for(oc, d2).should == [v2]
|
||||
end
|
||||
|
||||
it "does not return variants in the order cycle but not the distributor" do
|
||||
p1.variants_for(oc, d2).should be_empty
|
||||
p2.variants_for(oc, d1).should be_empty
|
||||
end
|
||||
|
||||
it "does not return variants not in the order cycle" do
|
||||
p_external.variants_for(oc, d1).should be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe "variant units" do
|
||||
context "when the product initially has no variant unit" do
|
||||
let!(:p) { create(:simple_product,
|
||||
@@ -441,12 +477,65 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
describe "Stock filtering" do
|
||||
describe "stock filtering" do
|
||||
it "considers products that are on_demand as being in stock" do
|
||||
product = create(:simple_product, on_demand: true)
|
||||
product.master.update_attribute(:count_on_hand, 0)
|
||||
product.has_stock?.should == true
|
||||
end
|
||||
|
||||
describe "finding products in stock for a particular distribution" do
|
||||
it "returns in-stock products without variants" do
|
||||
p = create(:simple_product)
|
||||
p.master.update_attribute(:count_on_hand, 1)
|
||||
d = create(:distributor_enterprise)
|
||||
oc = create(:simple_order_cycle, distributors: [d])
|
||||
oc.exchanges.outgoing.first.variants << p.master
|
||||
|
||||
p.should have_stock_for_distribution(oc, d)
|
||||
end
|
||||
|
||||
it "returns on-demand products" do
|
||||
p = create(:simple_product, on_demand: true)
|
||||
p.master.update_attribute(:count_on_hand, 0)
|
||||
d = create(:distributor_enterprise)
|
||||
oc = create(:simple_order_cycle, distributors: [d])
|
||||
oc.exchanges.outgoing.first.variants << p.master
|
||||
|
||||
p.should have_stock_for_distribution(oc, d)
|
||||
end
|
||||
|
||||
it "returns products with in-stock variants" do
|
||||
p = create(:simple_product)
|
||||
v = create(:variant, product: p)
|
||||
v.update_attribute(:count_on_hand, 1)
|
||||
d = create(:distributor_enterprise)
|
||||
oc = create(:simple_order_cycle, distributors: [d])
|
||||
oc.exchanges.outgoing.first.variants << v
|
||||
|
||||
p.should have_stock_for_distribution(oc, d)
|
||||
end
|
||||
|
||||
it "returns products with on-demand variants" do
|
||||
p = create(:simple_product)
|
||||
v = create(:variant, product: p, on_demand: true)
|
||||
v.update_attribute(:count_on_hand, 0)
|
||||
d = create(:distributor_enterprise)
|
||||
oc = create(:simple_order_cycle, distributors: [d])
|
||||
oc.exchanges.outgoing.first.variants << v
|
||||
|
||||
p.should have_stock_for_distribution(oc, d)
|
||||
end
|
||||
|
||||
it "does not return products that have stock not in the distribution" do
|
||||
p = create(:simple_product)
|
||||
p.master.update_attribute(:count_on_hand, 1)
|
||||
d = create(:distributor_enterprise)
|
||||
oc = create(:simple_order_cycle, distributors: [d])
|
||||
|
||||
p.should_not have_stock_for_distribution(oc, d)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,6 +2,25 @@ require 'spec_helper'
|
||||
|
||||
module Spree
|
||||
describe Variant do
|
||||
describe "scopes" do
|
||||
describe "finding variants in stock" do
|
||||
before do
|
||||
p = create(:product)
|
||||
@v_in_stock = create(:variant, product: p)
|
||||
@v_on_demand = create(:variant, product: p, on_demand: true)
|
||||
@v_no_stock = create(:variant, product: p)
|
||||
|
||||
@v_in_stock.update_attribute(:count_on_hand, 1)
|
||||
@v_on_demand.update_attribute(:count_on_hand, 0)
|
||||
@v_no_stock.update_attribute(:count_on_hand, 0)
|
||||
end
|
||||
|
||||
it "returns variants in stock or on demand, but not those that are neither" do
|
||||
Variant.where(is_master: false).in_stock.should == [@v_in_stock, @v_on_demand]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "calculating the price with enterprise fees" do
|
||||
it "returns the price plus the fees" do
|
||||
distributor = double(:distributor)
|
||||
|
||||
Reference in New Issue
Block a user