Skip to content

Commit

Permalink
Add handler to create weighted adjustments
Browse files Browse the repository at this point in the history
We'll be introducing a new calculator for promotions that will allow
order-level promotion values to be applied through line item
adjustments.

This class keeps some of the more complicated logic out of the
calculator as it has to reach up to the order and calculate values on
multiple line items.
  • Loading branch information
graygilmore committed May 23, 2017
1 parent ef63800 commit f3d600e
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 0 deletions.
43 changes: 43 additions & 0 deletions core/app/models/spree/distributed_amounts_handler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module Spree
class DistributedAmountsHandler
attr_reader :line_item, :order, :total_amount

def initialize(line_item, total_amount)
@line_item = line_item
@order = line_item.order
@total_amount = total_amount
end

# @return [Float] the weighted adjustment for the initialized line item
def amount
distributed_amounts[@line_item.id].to_f
end

private

# @private
# @return [Hash<Integer, BigDecimal>] a hash of line item IDs and their
# corresponding weighted adjustments
def distributed_amounts
remaining_amount = @total_amount

@order.line_items.each_with_index.map do |line_item, i|
if i == @order.line_items.length - 1
# If this is the last line item on the order we want to use the
# remaining preferred amount to ensure our total adjustment is what
# has been set as the preferred amount.
[line_item.id, remaining_amount]
else
# Calculate the weighted amount by getting this line item's share of
# the order's total and multiplying it with the preferred amount.
weighted_amount = ((line_item.amount / @order.item_total) * total_amount).round(2)

# Subtract this line item's weighted amount from the total.
remaining_amount -= weighted_amount

[line_item.id, weighted_amount]
end
end.to_h
end
end
end
79 changes: 79 additions & 0 deletions core/spec/models/spree/distributed_amounts_handler_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
require "spec_helper"

RSpec.describe Spree::DistributedAmountsHandler, type: :model do
let(:order) do
FactoryGirl.create(
:order_with_line_items,
line_items_attributes: line_items_attributes
)
end

describe "#amount" do
let(:total_amount) { 15 }

subject { described_class.new(line_item, total_amount).amount }

context "when there is only one line item" do
let(:line_items_attributes) { [{ price: 100 }] }
let(:line_item) { order.line_items.first }

it "applies the entire amount to the line item" do
expect(subject).to eq(15)
end
end

context "when there are multiple line items" do
let(:line_items_attributes) do
[{ price: 50 }, { price: 50 }, { price: 50 }]
end

context "and the line items are equally priced" do
it "evenly distributes the total amount" do
expect(
[
described_class.new(order.line_items[0], total_amount).amount,
described_class.new(order.line_items[1], total_amount).amount,
described_class.new(order.line_items[2], total_amount).amount
]
).to eq(
[5, 5, 5]
)
end

context "and the total amount cannot be equally distributed" do
let(:total_amount) { 10 }

it "applies the remainder of the total amount to the last item" do
expect(
[
described_class.new(order.line_items[0], total_amount).amount,
described_class.new(order.line_items[1], total_amount).amount,
described_class.new(order.line_items[2], total_amount).amount
]
).to eq(
[3.33, 3.33, 3.34]
)
end
end
end

context "and the line items are not equally priced" do
let(:line_items_attributes) do
[{ price: 150 }, { price: 50 }, { price: 100 }]
end

it "distributes the total amount relative to the item's price" do
expect(
[
described_class.new(order.line_items[0], total_amount).amount,
described_class.new(order.line_items[1], total_amount).amount,
described_class.new(order.line_items[2], total_amount).amount
]
).to eq(
[7.5, 2.5, 5]
)
end
end
end
end
end

0 comments on commit f3d600e

Please sign in to comment.