Merge branch 'master' into bom

This commit is contained in:
Rob H
2014-03-06 12:52:36 +11:00
21 changed files with 368 additions and 87 deletions

View File

@@ -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")

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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|

View File

@@ -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)

View File

@@ -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

View File

@@ -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|

View File

@@ -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"}

View File

@@ -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|

View File

@@ -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' }

View File

@@ -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

View File

@@ -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' }

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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}

View File

@@ -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

View File

@@ -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

View File

@@ -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)