From e15e9a147697b3052b94d8e9005b97435d0c15da Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Mon, 19 Aug 2013 09:27:18 +1000 Subject: [PATCH] Charge order cycle fees --- app/models/order_cycle.rb | 50 ++++++++++++++++++++ app/models/spree/order_decorator.rb | 14 +++++- spec/models/order_cycle_spec.rb | 73 +++++++++++++++++++++++++++++ spec/models/order_spec.rb | 42 +++++++++++++++++ 4 files changed, 177 insertions(+), 2 deletions(-) diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index 51af905060..3ff37bbdf4 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -58,4 +58,54 @@ class OrderCycle < ActiveRecord::Base end + # -- Fees + def ensure_correct_adjustments_for(line_item) + EnterpriseFee.clear_all_adjustments_for line_item + create_adjustments_for line_item + end + + + private + + # -- Fees + def create_adjustments_for(line_item) + fees_for(line_item).each { |fee| create_adjustment_for_fee line_item, fee[:enterprise_fee], fee[:label], fee[:role] } + end + + def fees_for(line_item) + fees = [] + + # If there are multiple distributors with this variant, won't this mean that we get a fee charged for each of them? + # We just want the one matching line_item.order.distributor + + exchanges_carrying(line_item.variant).each do |exchange| + exchange.enterprise_fees.each do |enterprise_fee| + role = exchange.incoming? ? 'supplier' : 'distributor' + fees << {enterprise_fee: enterprise_fee, + label: adjustment_label_for(line_item, enterprise_fee, role), + role: role} + end + end + + coordinator_fees.each do |enterprise_fee| + fees << {enterprise_fee: enterprise_fee, + label: adjustment_label_for(line_item, enterprise_fee, 'coordinator'), + role: 'coordinator'} + end + + fees + end + + def create_adjustment_for_fee(line_item, enterprise_fee, label, role) + a = enterprise_fee.create_adjustment(label, line_item.order, line_item, true) + AdjustmentMetadata.create! adjustment: a, enterprise: enterprise_fee.enterprise, fee_name: enterprise_fee.name, fee_type: enterprise_fee.fee_type, enterprise_role: role + end + + def adjustment_label_for(line_item, enterprise_fee, role) + "#{line_item.variant.product.name} - #{enterprise_fee.fee_type} fee by #{role} #{enterprise_fee.enterprise.name}" + end + + def exchanges_carrying(variant) + exchanges.with_variant(variant) + end end diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index b4d94eb45e..0058c8db49 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -55,8 +55,13 @@ Spree::Order.class_eval do def update_distribution_charge! line_items.each do |line_item| - pd = product_distribution_for line_item - pd.ensure_correct_adjustment_for line_item if pd + if provided_by_order_cycle? line_item + order_cycle.ensure_correct_adjustments_for line_item + + else + pd = product_distribution_for line_item + pd.ensure_correct_adjustment_for line_item if pd + end end end @@ -96,6 +101,11 @@ Spree::Order.class_eval do end end + def provided_by_order_cycle?(line_item) + order_cycle_variants = order_cycle.andand.variants || [] + order_cycle_variants.include? line_item.variant + end + def product_distribution_for(line_item) line_item.variant.product.product_distribution_for self.distributor end diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 14c17bce04..cacc074724 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -140,4 +140,77 @@ describe OrderCycle do @oc.products.sort.should == [@p0, @p1, @p2] end end + + describe "ensuring that a line item has the correct adjustment" do + let(:oc) { OrderCycle.new } + let(:line_item) { double(:line_item) } + + it "clears all enterprise fee adjustments on the line item" do + EnterpriseFee.should_receive(:clear_all_adjustments_for).with(line_item) + oc.stub(:create_adjustments_for) + oc.ensure_correct_adjustments_for line_item + end + + it "creates an adjustment on the line item" do + EnterpriseFee.stub(:clear_all_adjustments_for) + oc.should_receive(:create_adjustments_for).with(line_item) + oc.ensure_correct_adjustments_for line_item + end + end + + describe "creating adjustments for a line item" do + let(:oc) { OrderCycle.new } + let(:line_item) { double(:line_item, variant: 123) } + + it "creates adjustment for each fee" do + fee = {enterprise_fee: 'ef', label: 'label', role: 'role'} + oc.stub(:fees_for) { [fee] } + oc.should_receive(:create_adjustment_for_fee).with(line_item, 'ef', 'label', 'role') + + oc.send(:create_adjustments_for, line_item) + end + + it "finds fees for a line item" do + ef1 = double(:enterprise_fee) + ef2 = double(:enterprise_fee) + ef3 = double(:enterprise_fee) + incoming_exchange = double(:exchange, enterprise_fees: [ef1], incoming?: true) + outgoing_exchange = double(:exchange, enterprise_fees: [ef2], incoming?: false) + oc.stub(:exchanges_carrying) { [incoming_exchange, outgoing_exchange] } + oc.stub(:coordinator_fees) { [ef3] } + oc.stub(:adjustment_label_for) { 'label' } + + oc.send(:fees_for, line_item).should == + [{enterprise_fee: ef1, label: 'label', role: 'supplier'}, + {enterprise_fee: ef2, label: 'label', role: 'distributor'}, + {enterprise_fee: ef3, label: 'label', role: 'coordinator'}] + end + + it "creates an adjustment for a fee" do + line_item = create(:line_item) + enterprise_fee = create(:enterprise_fee) + + oc.send(:create_adjustment_for_fee, line_item, enterprise_fee, 'label', 'role') + + adjustment = Spree::Adjustment.last + adjustment.label.should == 'label' + adjustment.adjustable.should == line_item.order + adjustment.source.should == line_item + adjustment.originator.should == enterprise_fee + adjustment.should be_mandatory + + md = adjustment.metadata + md.enterprise.should == enterprise_fee.enterprise + md.fee_name.should == enterprise_fee.name + md.fee_type.should == enterprise_fee.fee_type + md.enterprise_role.should == 'role' + end + + it "makes adjustment labels" do + line_item = double(:line_item, variant: double(:variant, product: double(:product, name: 'Bananas'))) + enterprise_fee = double(:enterprise_fee, fee_type: 'packing', enterprise: double(:enterprise, name: 'Ballantyne')) + + oc.send(:adjustment_label_for, line_item, enterprise_fee, 'distributor').should == "Bananas - packing fee by distributor Ballantyne" + end + end end diff --git a/spec/models/order_spec.rb b/spec/models/order_spec.rb index fe8051ee3c..2dcff1d92b 100644 --- a/spec/models/order_spec.rb +++ b/spec/models/order_spec.rb @@ -21,6 +21,7 @@ describe Spree::Order do it "ensures the correct adjustment(s) are created for the product distribution" do line_item = double(:line_item) subject.stub(:line_items) { [line_item] } + subject.stub(:provided_by_order_cycle?) { false } product_distribution = double(:product_distribution) product_distribution.should_receive(:ensure_correct_adjustment_for).with(line_item) @@ -32,12 +33,53 @@ describe Spree::Order do it "skips line items that don't have a product distribution" do line_item = double(:line_item) subject.stub(:line_items) { [line_item] } + subject.stub(:provided_by_order_cycle?) { false } subject.stub(:product_distribution_for) { nil } subject.send(:update_distribution_charge!) end + it "ensures the correct adjustment(s) are created for order cycles" do + line_item = double(:line_item) + subject.stub(:line_items) { [line_item] } + subject.stub(:provided_by_order_cycle?) { true } + + order_cycle = double(:order_cycle) + order_cycle.should_receive(:ensure_correct_adjustments_for).with(line_item) + subject.stub(:order_cycle) { order_cycle } + + subject.send(:update_distribution_charge!) + end + + describe "looking up whether a line item can be provided by an order cycle" do + it "returns true when the variant is provided" do + v = double(:variant) + line_item = double(:line_item, variant: v) + order_cycle = double(:order_cycle, variants: [v]) + subject.stub(:order_cycle) { order_cycle } + + subject.send(:provided_by_order_cycle?, line_item).should be_true + end + + it "returns false otherwise" do + v = double(:variant) + line_item = double(:line_item, variant: v) + order_cycle = double(:order_cycle, variants: []) + subject.stub(:order_cycle) { order_cycle } + + subject.send(:provided_by_order_cycle?, line_item).should be_false + end + + it "returns false when there is no order cycle" do + v = double(:variant) + line_item = double(:line_item, variant: v) + subject.stub(:order_cycle) { nil } + + subject.send(:provided_by_order_cycle?, line_item).should be_false + end + end + it "looks up product distribution enterprise fees for a line item" do product = double(:product) variant = double(:variant, product: product)