From 46abc2fc42771aedbe4d04d5aac9169aee6ab919 Mon Sep 17 00:00:00 2001 From: Rob H Date: Fri, 7 Dec 2012 11:56:40 +1100 Subject: [PATCH] Add ability to change distributor based on whether products in the cart are available --- .../spree/orders_controller_decorator.rb | 2 +- app/models/spree/order_decorator.rb | 34 ++++++++-- app/overrides/replace_delivery_address.rb | 11 +++- .../_other_available_distributors.html.erb | 16 +++++ .../spree/products/_source_sidebar.html.haml | 2 +- spec/models/order_spec.rb | 62 ++++++++++++++++++- 6 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 app/views/spree/checkout/_other_available_distributors.html.erb diff --git a/app/controllers/spree/orders_controller_decorator.rb b/app/controllers/spree/orders_controller_decorator.rb index 798eedd60a..2342665975 100644 --- a/app/controllers/spree/orders_controller_decorator.rb +++ b/app/controllers/spree/orders_controller_decorator.rb @@ -35,7 +35,7 @@ Spree::OrdersController.class_eval do order = current_order(true) - if order.can_change_distributor? + if order.can_change_to_distributor?(distributor) order.distributor = distributor order.save! end diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 36f552c0df..14b626a189 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -2,17 +2,43 @@ Spree::Order.class_eval do belongs_to :distributor, :class_name => 'Enterprise' before_validation :shipping_address_from_distributor - validate :can_change_distributor?, :if => :distributor_id_changed? + validate :change_distributor_validation, :if => :distributor_id_changed? + attr_accessible :distributor_id after_create :set_default_shipping_method + def change_distributor_validation + # Check that the line_items in the current order are available from a newly selected distributor + errors.add(:distributor_id, "The products in your cart are not available from '" + distributor.name + "'") unless can_change_to_distributor? distributor + end + + def can_change_to_distributor? distributor + # Distributor may not be changed once an item has been added to the cart/order, unless all items are available from the specified distributor + line_items.empty? || (available_distributors || []).include?(distributor) + end + def can_change_distributor? # Distributor may not be changed once an item has been added to the cart/order line_items.empty? end + def available_distributors + # Find all other enterprises which offer all product variants contained within the current order + distributors_with_all_variants = get_distributors_with_all_variants(Enterprise.all) + end + + def get_distributors_with_all_variants(enterprises) + variants_in_current_order = line_items.map{ |li| li.variant } + distributors_with_all_variants = [] + enterprises.each do |e| + variants_available_from_enterprise = ProductDistribution.find_all_by_distributor_id( e.id ).map{ |pd| pd.product.variants }.flatten + distributors_with_all_variants << e if ( variants_in_current_order - variants_available_from_enterprise ).empty? + end + distributors_with_all_variants + end + def distributor=(distributor) - raise "You cannot change the distributor of an order with products" unless distributor == self.distributor || can_change_distributor? + raise "You cannot change the distributor of an order with products" unless distributor == self.distributor || can_change_to_distributor?(distributor) super(distributor) end @@ -21,9 +47,9 @@ Spree::Order.class_eval do save! end - def can_add_product_to_cart?(product) - can_change_distributor? || product.distributors.include?(distributor) + # Products may be added if no line items are currently in the cart or if the product is available from the current distributor + line_items.empty? || product.distributors.include?(distributor) end def set_variant_attributes(variant, attributes) diff --git a/app/overrides/replace_delivery_address.rb b/app/overrides/replace_delivery_address.rb index 2b13785b1c..60b9883d5f 100644 --- a/app/overrides/replace_delivery_address.rb +++ b/app/overrides/replace_delivery_address.rb @@ -1,4 +1,9 @@ Deface::Override.new(:virtual_path => "spree/checkout/_address", - :replace => "[data-hook='shipping_fieldset_wrapper']", - :partial => "spree/checkout/distributor", - :name => "delivery_address") + :replace => "[data-hook='shipping_fieldset_wrapper']", + :partial => "spree/checkout/distributor", + :name => "replace_shipping_form") + +Deface::Override.new(:virtual_path => "spree/checkout/edit", + :insert_after => "[data-hook='checkout_summary_box']", + :partial => "spree/checkout/other_available_distributors", + :name => "other_available_distributors") diff --git a/app/views/spree/checkout/_other_available_distributors.html.erb b/app/views/spree/checkout/_other_available_distributors.html.erb new file mode 100644 index 0000000000..51ed8bce0e --- /dev/null +++ b/app/views/spree/checkout/_other_available_distributors.html.erb @@ -0,0 +1,16 @@ +
+ <% + other_available_distributors = @order.available_distributors + other_available_distributors.delete(@order.distributor) + unless other_available_distributors.empty? + %> + <%= form_for(@order) do |f| %> + <%= f.label :distributor_label, "Alternative distributors for this order:" %> + <% available_distributors_array = other_available_distributors.map { |distributor| [distributor.name + ": " + distributor.address.address1 + ", " + distributor.address.city, distributor.id.to_i] } %> + <%= f.select :distributor_id, options_for_select( available_distributors_array ) %> + <%= f.submit "Change Distributor" %> + <% end %> + <% else %> + No alternative distributors available. + <% end %> +
\ No newline at end of file diff --git a/app/views/spree/products/_source_sidebar.html.haml b/app/views/spree/products/_source_sidebar.html.haml index 41da860f88..e5a03d2896 100644 --- a/app/views/spree/products/_source_sidebar.html.haml +++ b/app/views/spree/products/_source_sidebar.html.haml @@ -11,7 +11,7 @@ - order = current_order(false) - @distributors.each do |distributor| %li.nowrap - - if order.nil? || order.can_change_distributor? + - if order.nil? || order.can_change_to_distributor?(distributor) = link_to distributor.name, select_distributor_order_path(distributor) - elsif order.distributor == distributor = link_to distributor.name, [main_app, distributor] diff --git a/spec/models/order_spec.rb b/spec/models/order_spec.rb index 335c67a574..f80c1d1fa9 100644 --- a/spec/models/order_spec.rb +++ b/spec/models/order_spec.rb @@ -26,7 +26,7 @@ describe Spree::Order do subject.can_change_distributor?.should be_false end - it "raises an exception if distributor is changed without permission" do + it "checks that distributor is available when changing, and raises an exception if distributor is changed without permission" do d = create(:distributor_enterprise) p = create(:product, :distributors => [d]) subject.distributor = d @@ -34,6 +34,7 @@ describe Spree::Order do subject.add_variant(p.master, 1) subject.can_change_distributor?.should be_false + subject.should_receive(:available_distributors) expect do subject.distributor = nil @@ -79,4 +80,63 @@ describe Spree::Order do li = Spree::LineItem.last li.max_quantity.should == 3 end + + context "finding alternative distributors" do + it "checks that variants are available" do + distributors_with_all_variants = double(:distributors_with_all_variants) + subject.should_receive(:get_distributors_with_all_variants).with(Enterprise.all) + subject.available_distributors + end + + context "finding distributors which have the same variants" do + before(:each) do + @enterprise1 = FactoryGirl.create(:enterprise, id: 1) + subject.distributor = @enterprise1 + @product1 = FactoryGirl.create(:product) + @product2 = FactoryGirl.create(:product) + @product3 = FactoryGirl.create(:product) + variant11 = FactoryGirl.create(:variant, product: @product1) + variant12 = FactoryGirl.create(:variant, product: @product1) + variant21 = FactoryGirl.create(:variant, product: @product2) + variant31 = FactoryGirl.create(:variant, product: @product3) + variant32 = FactoryGirl.create(:variant, product: @product3) + + # Product Distributions + # Enterprise 1 sells product 1 and product 3 + FactoryGirl.create(:product_distribution, product: @product1, distributor: @enterprise1) + FactoryGirl.create(:product_distribution, product: @product3, distributor: @enterprise1) + + # Build the current order + line_item1 = FactoryGirl.create(:line_item, order: subject, variant: variant11) + line_item2 = FactoryGirl.create(:line_item, order: subject, variant: variant12) + line_item3 = FactoryGirl.create(:line_item, order: subject, variant: variant31) + subject.line_items = [line_item1,line_item2,line_item3] + end + + it "matches the distributor enterprise of the current order" do + subject.get_distributors_with_all_variants([@enterprise1]).should == [@enterprise1] + end + + it "does not match enterprises with no products available" do + test_enterprise = FactoryGirl.create(:enterprise, id: 2) + subject.get_distributors_with_all_variants([@enterprise1, test_enterprise]).should_not include test_enterprise + end + + it "does not match enterprises with only some of the same variants in the current order available" do + test_enterprise = FactoryGirl.create(:enterprise, id: 2) + # Test Enterprise sells only product 1 + FactoryGirl.create(:product_distribution, product: @product1, distributor: test_enterprise) + subject.get_distributors_with_all_variants([@enterprise1, test_enterprise]).should_not include test_enterprise + end + + it "matches enteprises which offer all products in the current order" do + test_enterprise = FactoryGirl.create(:enterprise, id: 2) + # Enterprise 3 Sells Products 1, 2 and 3 + FactoryGirl.create(:product_distribution, product: @product1, distributor: test_enterprise) + FactoryGirl.create(:product_distribution, product: @product2, distributor: test_enterprise) + FactoryGirl.create(:product_distribution, product: @product3, distributor: test_enterprise) + subject.get_distributors_with_all_variants([@enterprise1, test_enterprise]).should include test_enterprise + end + end + end end