Round FlatPercentItemTotal calcs per-item before summing full quantity

This commit is contained in:
Rohan Mitchell
2016-08-10 12:17:10 +10:00
parent 7b2f65a965
commit b0eebc2e45
4 changed files with 56 additions and 4 deletions

View File

@@ -0,0 +1,18 @@
Spree::Calculator::FlatPercentItemTotal.class_eval do
# Spree's calculator sums all amounts, and then calculates a percentage on them.
# In the cart, we display line item individual amounts rounded, so to have consistent
# calculations we do the same internally. Here, we round adjustments at the individual
# item level first, then multiply by the item quantity.
def compute(object)
line_items_for(object).sum do |li|
unless li.price.present? && li.quantity.present?
raise ArgumentError, "object must respond to #price and #quantity"
end
value = (li.price * BigDecimal(self.preferred_flat_percent.to_s) / 100.0).round(2)
value * li.quantity
end
end
end

View File

@@ -13,16 +13,35 @@ feature "full-page cart", js: true do
let!(:order_cycle) { create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], coordinator: create(:distributor_enterprise), variants: [product_tax.variants.first, product_fee.variants.first]) }
let(:enterprise_fee) { create(:enterprise_fee, amount: 11.00, tax_category: product_tax.tax_category) }
let(:product_tax) { create(:taxed_product, supplier: supplier, zone: zone, price: 110.00, tax_rate_amount: 0.1) }
let(:product_fee) { create(:simple_product, supplier: supplier, price: 0.86) }
let(:product_fee) { create(:simple_product, supplier: supplier, price: 0.86, on_hand: 100) }
let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor) }
before do
add_enterprise_fee enterprise_fee
set_order order
end
describe "fees" do
let(:percentage_fee) { create(:enterprise_fee, calculator: Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 20)) }
before do
add_enterprise_fee percentage_fee
add_product_to_cart order, product_fee, quantity: 8
visit spree.cart_path
end
it "rounds fee calculations correctly" do
# $0.86 + 20% = $1.032
# Fractional cents should be immediately rounded down and not carried through
expect(page).to have_selector '.cart-item-price', text: '$1.03'
expect(page).to have_selector '.cart-item-total', text: '$8.24'
expect(page).to have_selector '.order-total.item-total', text: '$8.24'
expect(page).to have_selector '.order-total.grand-total', text: '$8.24'
end
end
describe "tax" do
before do
add_enterprise_fee enterprise_fee
add_product_to_cart order, product_tax
visit spree.cart_path
end

View File

@@ -0,0 +1,15 @@
module Spree
describe Calculator::FlatPercentItemTotal do
let(:calculator) { Calculator::FlatPercentItemTotal.new preferred_flat_percent: 20 }
it "calculates for a simple line item" do
line_item = LineItem.new price: 50, quantity: 2
expect(calculator.compute(line_item)).to eq 20
end
it "rounds fractional cents before summing" do
line_item = LineItem.new price: 0.86, quantity: 8
expect(calculator.compute(line_item)).to eq 1.36
end
end
end

View File

@@ -16,9 +16,9 @@ module ShopWorkflow
ApplicationController.any_instance.stub(:session).and_return({order_id: order.id, access_token: order.token})
end
def add_product_to_cart(order, product)
def add_product_to_cart(order, product, quantity: 1)
populator = Spree::OrderPopulator.new(order, order.currency)
populator.populate(variants: {product.variants.first.id => 1})
populator.populate(variants: {product.variants.first.id => quantity})
# Recalculate fee totals
order.update_distribution_charge!