Skip to content

Commit

Permalink
Add DistributedAmount calculator for line item adjustments
Browse files Browse the repository at this point in the history
This calculator allows us to use order level promotions ("Save $50 on
your order") and spread that $50 over all of the line items. This
calculator shoots logic off to another class to handle the calculations.

Each line item needs to call out to the DistributedAmountsHandler
separately which is annoying but each line item needs to be aware of the
order as a whole.

The calculator spreads the discount amount among each of the line items
relative to their price. This means that more expensive items will
receive a greater share of the discount.
  • Loading branch information
graygilmore committed May 23, 2017
1 parent f3d600e commit 4b00354
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<%= fields_for "#{prefix}[calculator_attributes]", calculator do |f| %>
<%= f.label :preferred_amount %>
<%= render "spree/admin/shared/number_with_currency", f: f, amount_attr: :preferred_amount, currency_attr: :preferred_currency %>
<% end %>

<div class="field">
<p>
<%= admin_hint(
calculator.model_name.human,
"""
<p>
This amount will distributed to line items weighted relative to their
price. More expensive line items will receive a greater share of the
adjustment.
</p>
<p>
For example, with three line items and a preferred amount of $15 we
would end up with the following distribution:
</p>
<table>
<thead>
<tr>
<th></th>
<th>Price</th>
<th>Weighted Adj.</th>
</tr>
</thead>
<tbody>
<tr>
<td>Socks</td>
<td>$5</td>
<td>-$1.5</td>
</tr>
<tr>
<td>Shoes</td>
<td>$30</td>
<td>-$9</td>
</tr>
<tr>
<td>Slippers</td>
<td>$15</td>
<td>-$4.5</td>
</tr>
</tbody>
</table>
"""
) %>

This amount will be the <strong>total</strong> discount spread amongst all
of the line items.
</p>
</div>
24 changes: 24 additions & 0 deletions core/app/models/spree/calculator/distributed_amount.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require_dependency 'spree/calculator'

# This is a calculator for line item adjustment actions. It accepts a line item
# and calculates its weighted adjustment amount based on the value of the
# preferred amount and the price of the other line items. More expensive line
# items will receive a greater share of the preferred amount.

module Spree
class Calculator::DistributedAmount < Calculator
preference :amount, :decimal, default: 0
preference :currency, :string, default: -> { Spree::Config[:currency] }

def compute_line_item(line_item)
if line_item && preferred_currency.casecmp(line_item.currency).zero?
Spree::DistributedAmountsHandler.new(
line_item,
preferred_amount
).amount
else
0
end
end
end
end
3 changes: 3 additions & 0 deletions core/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,9 @@ en:
spree/calculator/default_tax:
one: Default Tax
other: Default Tax
spree/calculator/distributed_amount:
one: Distributed Amount
other: Distributed Amount
spree/calculator/flat_percent_item_total:
one: Flat Percent
other: Flat Percent
Expand Down
3 changes: 2 additions & 1 deletion core/lib/spree/core/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ class Engine < ::Rails::Engine
]

app.config.spree.calculators.promotion_actions_create_item_adjustments = %w[
Spree::Calculator::PercentOnLineItem
Spree::Calculator::DistributedAmount
Spree::Calculator::FlatRate
Spree::Calculator::FlexiRate
Spree::Calculator::PercentOnLineItem
Spree::Calculator::TieredPercent
]

Expand Down
32 changes: 32 additions & 0 deletions core/spec/models/spree/calculator/distributed_amount_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
require 'spec_helper'
require 'shared_examples/calculator_shared_examples'

describe Spree::Calculator::DistributedAmount, type: :model do
describe "#compute_line_item" do
subject { calculator.compute_line_item(order.line_items.first) }

let(:calculator) { Spree::Calculator::DistributedAmount.new }

let(:order) do
FactoryGirl.create(
:order_with_line_items,
line_items_attributes: [{ price: 50 }, { price: 50 }, { price: 50 }]
)
end

before do
calculator.preferred_amount = 15
calculator.preferred_currency = currency
end

context "when the order currency matches the store's currency" do
let(:currency) { "USD" }
it { is_expected.to eq 5 }
end

context "when the order currency does not match the store's currency" do
let(:currency) { "CAD" }
it { is_expected.to eq 0 }
end
end
end

0 comments on commit 4b00354

Please sign in to comment.