Create line item adjustments for product distributions

This commit is contained in:
Rohan Mitchell
2013-08-08 10:38:59 +10:00
parent 8c9a3c8a91
commit a338c974f8
5 changed files with 204 additions and 0 deletions

View File

@@ -7,4 +7,30 @@ class ProductDistribution < ActiveRecord::Base
validates_presence_of :product_id, :on => :update
validates_presence_of :distributor_id, :shipping_method_id
validates_uniqueness_of :product_id, :scope => :distributor_id
def ensure_correct_adjustment_for(line_item)
if enterprise_fee
adjustment = adjustment_on line_item
create_adjustment_on line_item unless adjustment
end
end
def adjustment_on(line_item)
adjustments = line_item.adjustments.where(source_id: enterprise_fee)
raise "Multiple adjustments for this enterprise fee on this line item. This method is not designed to deal with this scenario." if adjustments.count > 1
adjustments.first
end
def create_adjustment_on(line_item)
enterprise_fee.create_adjustment(adjustment_label, line_item, enterprise_fee, true)
end
def adjustment_label
"Product distribution by #{distributor.name}"
end
end

View File

@@ -13,6 +13,8 @@ Spree::Order.class_eval do
before_save :update_line_item_shipping_methods
after_create :set_default_shipping_method
register_update_hook :update_distribution_charge!
# -- Scopes
scope :managed_by, lambda { |user|
if user.has_spree_role?('admin')
@@ -22,6 +24,7 @@ Spree::Order.class_eval do
end
}
# -- Methods
def products_available_from_new_distribution
# Check that the line_items in the current order are available from a newly selected distribution
@@ -56,6 +59,13 @@ Spree::Order.class_eval do
save!
end
def update_distribution_charge!
line_items.each do |line_item|
pd = product_distribution_for line_item
pd.ensure_correct_adjustment_for line_item
end
end
def set_variant_attributes(variant, attributes)
line_item = find_line_item_by_variant(variant)
@@ -113,4 +123,9 @@ Spree::Order.class_eval do
self.update!
end
end
def product_distribution_for(line_item)
line_item.variant.product.product_distribution_for self.distributor
end
end

View File

@@ -89,6 +89,7 @@ FactoryGirl.define do
product { |pd| Spree::Product.first || FactoryGirl.create(:product) }
distributor { |pd| Enterprise.is_distributor.first || FactoryGirl.create(:distributor_enterprise) }
shipping_method { |pd| Spree::ShippingMethod.where("name != 'Delivery'").first || FactoryGirl.create(:shipping_method) }
enterprise_fee { |pd| FactoryGirl.create(:enterprise_fee, enterprise: pd.distributor) }
end
factory :itemwise_shipping_method, :parent => :shipping_method do

View File

@@ -28,6 +28,37 @@ describe Spree::Order do
li.max_quantity.should == 3
end
describe "updating the distribution charge" do
let(:order) { build(:order) }
it "updates distribution charge after save" do
order.should_receive(:update_distribution_charge!).at_least(:once)
order.save!
end
it "ensures the correct adjustment(s) are created for the product distribution" do
line_item = double(:line_item)
subject.stub(:line_items) { [line_item] }
product_distribution = double(:product_distribution)
product_distribution.should_receive(:ensure_correct_adjustment_for).with(line_item)
subject.stub(:product_distribution_for) { product_distribution }
subject.send(:update_distribution_charge!)
end
it "looks up product distribution enterprise fees for a line item" do
product = double(:product)
variant = double(:variant, product: product)
line_item = double(:line_item, variant: variant)
product_distribution = double(:product_distribution)
product.should_receive(:product_distribution_for).with(subject.distributor) { product_distribution }
subject.send(:product_distribution_for, line_item).should == product_distribution
end
end
describe "setting the distributor" do
it "sets the distributor when no order cycle is set" do
d = create(:distributor_enterprise)

View File

@@ -20,4 +20,135 @@ describe ProductDistribution do
pd2 = build(:product_distribution, :product => new_product, :distributor => new_distributor)
pd2.should be_valid
end
describe "adjusting orders" do
context "integration" do
it "creates an adjustment for product distributions" do
# Given an order
distributor = create(:distributor_enterprise)
order = create(:order, distributor: distributor)
# And a product with a product distribution with an enterprise fee
product = create(:product)
enterprise_fee = create(:enterprise_fee, calculator: build(:calculator))
enterprise_fee.calculator.preferred_amount = 1.23
enterprise_fee.calculator.save!
create(:product_distribution, product: product, distributor: distributor, enterprise_fee: enterprise_fee)
# When I add the product to the order, an adjustment should be made
expect do
op = Spree::OrderPopulator.new order, 'AU'
op.populate products: {product.id => product.master.id}, quantity: 1, distributor_id: distributor.id
end.to change(Spree::Adjustment, :count).by(1)
# And it should have the correct data
order.reload
order.line_items.count.should == 1
order.line_items.last.adjustments.count.should == 1
adjustment = order.line_items.last.adjustments.last
adjustment.source.should == enterprise_fee
adjustment.originator.should == enterprise_fee
adjustment.label.should == "Product distribution by #{distributor.name}"
adjustment.amount.should == 1.23
# And it should have some associated metadata
pending 'Needs metadata spec'
end
end
describe "ensuring that a line item has the correct adjustment" do
let(:enterprise_fee) { EnterpriseFee.new }
let(:pd) { ProductDistribution.new enterprise_fee: enterprise_fee }
let(:line_item) { double(:line_item) }
let(:adjustment) { double(:adjustment) }
# TODO: This spec will go away once enterprise_fee is required
it "does nothing if there is no enterprise fee set" do
pd.enterprise_fee = nil
pd.should_receive(:adjustment_on).never
pd.ensure_correct_adjustment_for line_item
end
describe "adding items to cart" do
it "creates an adjustment for the new item" do
pd.stub(:adjustment_on) { nil }
pd.should_receive(:create_adjustment_on).with(line_item)
pd.ensure_correct_adjustment_for line_item
end
it "makes no change to the adjustment of existing items" do
pd.stub(:adjustment_on) { adjustment }
pd.should_receive(:create_adjustment_on).never
pd.ensure_correct_adjustment_for line_item
end
end
describe "changing distributor" do
it "clears and re-creates the adjustment on the line item"
end
end
describe "finding our adjustment on a line item" do
it "returns nil when not present" do
line_item = build(:line_item)
pd = ProductDistribution.new
pd.send(:adjustment_on, line_item).should be_nil
end
it "returns the adjustment when present" do
# TODO: This spec can be simplified (ie. use the default ProductDistribution factory)
# once Spree's calculators are compatible with LineItem targets, not just Orders.
distributor = create(:distributor_enterprise)
enterprise_fee = create(:enterprise_fee, enterprise: distributor, calculator: build(:calculator))
pd = create(:product_distribution, distributor: distributor, enterprise_fee: enterprise_fee)
line_item = create(:line_item)
adjustment = pd.enterprise_fee.create_adjustment('foo', line_item, pd.enterprise_fee, true)
pd.send(:adjustment_on, line_item).should == adjustment
end
it "raises an error when there are multiple adjustments for this enterprise fee" do
# TODO: This spec can be simplified (ie. use the default ProductDistribution factory)
# once Spree's calculators are compatible with LineItem targets, not just Orders.
distributor = create(:distributor_enterprise)
enterprise_fee = create(:enterprise_fee, enterprise: distributor, calculator: build(:calculator))
pd = create(:product_distribution, distributor: distributor, enterprise_fee: enterprise_fee)
line_item = create(:line_item)
pd.enterprise_fee.create_adjustment('one', line_item, pd.enterprise_fee, true)
pd.enterprise_fee.create_adjustment('two', line_item, pd.enterprise_fee, true)
expect do
pd.send(:adjustment_on, line_item)
end.to raise_error "Multiple adjustments for this enterprise fee on this line item. This method is not designed to deal with this scenario."
end
end
describe "creating an adjustment on a line item" do
it "creates the adjustment via the enterprise fee" do
# TODO: This spec can be simplified (ie. use the default ProductDistribution factory)
# once Spree's calculators are compatible with LineItem targets, not just Orders.
distributor = create(:distributor_enterprise)
enterprise_fee = create(:enterprise_fee, enterprise: distributor, calculator: build(:calculator))
pd = create(:product_distribution, distributor: distributor, enterprise_fee: enterprise_fee)
pd.stub(:adjustment_label) { 'label' }
line_item = create(:line_item)
expect { pd.send(:create_adjustment_on, line_item) }.to change(Spree::Adjustment, :count).by(1)
adjustment = Spree::Adjustment.last
adjustment.label.should == 'label'
adjustment.adjustable.should == line_item
adjustment.source.should == enterprise_fee
adjustment.originator.should == enterprise_fee
adjustment.should be_mandatory
end
end
end
end