diff --git a/app/models/spree/tax_rate_decorator.rb b/app/models/spree/tax_rate_decorator.rb index 42eae86b8c..80bcc450c2 100644 --- a/app/models/spree/tax_rate_decorator.rb +++ b/app/models/spree/tax_rate_decorator.rb @@ -1,20 +1,61 @@ -Spree::TaxRate.class_eval do - class << self - def match_with_sales_tax_registration(order) - return [] if order.distributor && !order.distributor.charges_sales_tax - match_without_sales_tax_registration(order) +module Spree + TaxRate.class_eval do + class << self + def match_with_sales_tax_registration(order) + return [] if order.distributor && !order.distributor.charges_sales_tax + match_without_sales_tax_registration(order) + end + alias_method_chain :match, :sales_tax_registration end - alias_method_chain :match, :sales_tax_registration - end - def adjust_with_included_tax(order) - adjust_without_included_tax(order) + def adjust_with_included_tax(order) + adjust_without_included_tax(order) - order.reload - (order.adjustments.tax + order.price_adjustments).each do |a| - a.set_absolute_included_tax! a.amount + order.reload + (order.adjustments.tax + order.price_adjustments).each do |a| + a.set_absolute_included_tax! a.amount + end + end + alias_method_chain :adjust, :included_tax + + + # Manually apply a TaxRate to a particular amount. TaxRates normally compute against + # LineItems or Orders, so we mock out a line item here to fit the interface + # that our calculator (usually DefaultTax) expects. + def compute_tax(amount) + product = OpenStruct.new tax_category: tax_category + line_item = LineItem.new quantity: 1 + line_item.define_singleton_method(:product) { product } + line_item.define_singleton_method(:price) { amount } + + # Tax on adjustments (represented by the included_tax field) is always inclusive of + # tax. However, there's nothing to stop an admin from setting one up with a tax rate + # that's marked as not inclusive of tax, and that would result in the DefaultTax + # calculator generating a slightly incorrect value. Therefore, we treat the tax + # rate as inclusive of tax for the calculations below, regardless of its original + # setting. + with_tax_included_in_price do + calculator.compute line_item + end + end + + + private + + def with_tax_included_in_price + old_included_in_price = self.included_in_price + + self.included_in_price = true + calculator.calculable.included_in_price = true + + result = yield + + # TODO: ensure + self.included_in_price = old_included_in_price + calculator.calculable.included_in_price = old_included_in_price + + result end end - alias_method_chain :adjust, :included_tax end diff --git a/lib/open_food_network/enterprise_fee_applicator.rb b/lib/open_food_network/enterprise_fee_applicator.rb index 070cb4a91c..4962bc148e 100644 --- a/lib/open_food_network/enterprise_fee_applicator.rb +++ b/lib/open_food_network/enterprise_fee_applicator.rb @@ -35,43 +35,8 @@ module OpenFoodNetwork tax_rates = enterprise_fee.tax_category ? enterprise_fee.tax_category.tax_rates.match(order) : [] tax_rates.sum do |rate| - compute_tax rate, adjustment.amount + rate.compute_tax adjustment.amount end end - - # Apply a TaxRate to a particular amount. TaxRates normally compute against - # LineItems or Orders, so we mock out a line item here to fit the interface - # that our calculator (usually DefaultTax) expects. - def compute_tax(tax_rate, amount) - product = OpenStruct.new tax_category: tax_rate.tax_category - line_item = Spree::LineItem.new quantity: 1 - line_item.define_singleton_method(:product) { product } - line_item.define_singleton_method(:price) { amount } - - # The enterprise fee adjustments for which we're calculating tax are always inclusive of - # tax. However, there's nothing to stop an admin from setting one up with a tax rate - # that's marked as not inclusive of tax, and that would result in the DefaultTax - # calculator generating a slightly incorrect value. Therefore, we treat the tax - # rate as inclusive of tax for the calculations below, regardless of its original - # setting. - with_tax_included_in_price(tax_rate) do - tax_rate.calculator.compute line_item - end - end - - def with_tax_included_in_price(tax_rate) - old_included_in_price = tax_rate.included_in_price - - tax_rate.included_in_price = true - tax_rate.calculator.calculable.included_in_price = true - - result = yield - - tax_rate.included_in_price = old_included_in_price - tax_rate.calculator.calculable.included_in_price = old_included_in_price - - result - end end end - diff --git a/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb b/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb index 6703f844ab..9ad1d222b3 100644 --- a/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb +++ b/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb @@ -65,33 +65,4 @@ module OpenFoodNetwork efa.send(:order_adjustment_label).should == "Whole order - packing fee by distributor Ballantyne" end end - - describe "ensuring that tax rate is marked as tax included_in_price" do - let(:efa) { EnterpriseFeeApplicator.new nil, nil, nil } - let(:tax_rate) { create(:tax_rate, included_in_price: false, calculator: Spree::Calculator::DefaultTax.new) } - - it "sets included_in_price to true" do - efa.send(:with_tax_included_in_price, tax_rate) do - tax_rate.included_in_price.should be_true - end - end - - it "sets the included_in_price value accessible to the calculator to true" do - efa.send(:with_tax_included_in_price, tax_rate) do - tax_rate.calculator.calculable.included_in_price.should be_true - end - end - - it "passes through the return value of the block" do - efa.send(:with_tax_included_in_price, tax_rate) do - 'asdf' - end.should == 'asdf' - end - - it "restores both values to their original afterwards" do - efa.send(:with_tax_included_in_price, tax_rate) {} - tax_rate.included_in_price.should be_false - tax_rate.calculator.calculable.included_in_price.should be_false - end - end end diff --git a/spec/models/spree/tax_rate_spec.rb b/spec/models/spree/tax_rate_spec.rb index 25aad986a0..c10f00ac44 100644 --- a/spec/models/spree/tax_rate_spec.rb +++ b/spec/models/spree/tax_rate_spec.rb @@ -29,5 +29,33 @@ module Spree end end end + + describe "ensuring that tax rate is marked as tax included_in_price" do + let(:tax_rate) { create(:tax_rate, included_in_price: false, calculator: Spree::Calculator::DefaultTax.new) } + + it "sets included_in_price to true" do + tax_rate.send(:with_tax_included_in_price) do + tax_rate.included_in_price.should be_true + end + end + + it "sets the included_in_price value accessible to the calculator to true" do + tax_rate.send(:with_tax_included_in_price) do + tax_rate.calculator.calculable.included_in_price.should be_true + end + end + + it "passes through the return value of the block" do + tax_rate.send(:with_tax_included_in_price) do + 'asdf' + end.should == 'asdf' + end + + it "restores both values to their original afterwards" do + tax_rate.send(:with_tax_included_in_price) {} + tax_rate.included_in_price.should be_false + tax_rate.calculator.calculable.included_in_price.should be_false + end + end end end