diff --git a/backend/app/views/spree/admin/promotions/calculators/weighted_rate/_fields.html.erb b/backend/app/views/spree/admin/promotions/calculators/weighted_rate/_fields.html.erb new file mode 100644 index 0000000000..05f7308b33 --- /dev/null +++ b/backend/app/views/spree/admin/promotions/calculators/weighted_rate/_fields.html.erb @@ -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 %> + +
+

+ <%= admin_hint( + calculator.model_name.human, + """ +

+ This amount will distributed to line items weighted relative to their + price. More expensive line items will receive a greater share of the + adjustment. +

+ +

+ For example, with three line items and a preferred amount of $15 we + would end up with the following distribution: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
PriceWeighted Adj.
Socks$5-$1.5
Shoes$30-$9
Slippers$15-$4.5
+ """ + ) %> + + This amount will be the total discount spread amongst all + of the line items. +

+
diff --git a/core/app/models/spree/calculator/distributed_amount.rb b/core/app/models/spree/calculator/distributed_amount.rb new file mode 100644 index 0000000000..aff818e79a --- /dev/null +++ b/core/app/models/spree/calculator/distributed_amount.rb @@ -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 diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index ccdc31968b..d7ce8c3abf 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -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 diff --git a/core/lib/spree/core/engine.rb b/core/lib/spree/core/engine.rb index 3f7ae0af6c..f098b013fc 100644 --- a/core/lib/spree/core/engine.rb +++ b/core/lib/spree/core/engine.rb @@ -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 ] diff --git a/core/spec/models/spree/calculator/distributed_amount_spec.rb b/core/spec/models/spree/calculator/distributed_amount_spec.rb new file mode 100644 index 0000000000..9b6da114b6 --- /dev/null +++ b/core/spec/models/spree/calculator/distributed_amount_spec.rb @@ -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