Skip to content

Commit 662b0a7

Browse files
committed
Allow calculators to implement adjustment labels by adjustable type
Prior to this, the `adjustment_label` method on `SolidusPromotions::Benefit` could only output a relatively static string. This commit allows calculators to define one of these methods: - `#line_item_adjustment_label(line_item, options = {})` - `#shipment_adjustment_label(shipment, options = {})` - `#shipping_rate_adjustment_label(shipping_rate, options = {})` - `#price_adjustment_label(price, options = {})` These have access to the inner workings of the calculator, so they can provide dynamic labels (to reflect e.g. which tier a promotion chooses, making that transparent to the customer.
1 parent f8779d6 commit 662b0a7

File tree

2 files changed

+84
-8
lines changed

2 files changed

+84
-8
lines changed

promotions/app/models/solidus_promotions/benefit.rb

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,48 @@ def compute_amount(adjustable, ...)
175175

176176
# Builds the localized label for adjustments created by this benefit.
177177
#
178-
# @param adjustable [Object]
179-
# @return [String]
180-
def adjustment_label(adjustable)
181-
I18n.t(
182-
"solidus_promotions.adjustment_labels.#{adjustable.class.name.demodulize.underscore}",
183-
promotion: SolidusPromotions::Promotion.model_name.human,
184-
promotion_customer_label: promotion.customer_label
185-
)
178+
# This method attempts to use a calculator-specific label method if available,
179+
# falling back to a localized string key based on the adjustable's class name.
180+
#
181+
# ## Calculator Override
182+
#
183+
# Calculators can provide custom labels by implementing a method named after the
184+
# adjustable type. For example, a calculator that discounts line items could
185+
# implement `line_item_adjustment_label`:
186+
#
187+
# @example Custom calculator with adjustment label
188+
# class MyCalculator < Spree::Calculator
189+
# def compute(adjustable, *args)
190+
# # calculation logic
191+
# end
192+
#
193+
# def line_item_adjustment_label(line_item, *args)
194+
# "Custom discount for #{line_item.product.name}"
195+
# end
196+
# end
197+
#
198+
# The method name follows the pattern: `{adjustable_type}_adjustment_label`
199+
# where `{adjustable_type}` is the underscored class name of the adjustable
200+
# (e.g., `line_item`, `shipment`, `shipping_rate`).
201+
#
202+
# If the calculator does not respond to the expected method, the benefit will
203+
# fall back to using an i18n translation key based on the adjustable's class.
204+
#
205+
# @param adjustable [Object] the object being discounted (e.g., Spree::LineItem, Spree::Shipment)
206+
# @param ... [args, kwargs] additional arguments forwarded to the calculator's label method
207+
# @return [String] a localized label suitable for display in adjustments
208+
#
209+
# @see #adjustment_label_method_for
210+
def adjustment_label(adjustable, ...)
211+
if calculator.respond_to?(adjustment_label_method_for(adjustable))
212+
calculator.send(adjustment_label_method_for(adjustable), adjustable, ...)
213+
else
214+
I18n.t(
215+
"solidus_promotions.adjustment_labels.#{adjustable.class.name.demodulize.underscore}",
216+
promotion: SolidusPromotions::Promotion.model_name.human,
217+
promotion_customer_label: promotion.customer_label
218+
)
219+
end
186220
end
187221

188222
# Partial path used for admin forms for this benefit type.
@@ -277,6 +311,10 @@ def discount_method_for(adjustable)
277311
:"discount_#{adjustable.class.name.demodulize.underscore}"
278312
end
279313

314+
def adjustment_label_method_for(adjustable)
315+
:"#{adjustable.class.name.demodulize.underscore}_adjustment_label"
316+
end
317+
280318
# Prevents destroying a benefit when it has adjustments on completed orders.
281319
#
282320
# Adds an error and aborts the destroy callback chain when such adjustments exist.

promotions/spec/models/solidus_promotions/benefit_spec.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,4 +265,42 @@ def discount_line_item(order, ...)
265265
expect { subject }.to raise_exception(NotImplementedError)
266266
end
267267
end
268+
269+
describe "#adjustment_label" do
270+
let(:benefit_class) do
271+
Class.new(described_class) do
272+
end
273+
end
274+
275+
let(:calculator_class) do
276+
Class.new(Spree::Calculator) do
277+
include SolidusPromotions::Calculators::PromotionCalculator
278+
end
279+
end
280+
281+
let(:calculator) { calculator_class.new }
282+
283+
let(:promotion) { build_stubbed(:solidus_promotion, customer_label: "Winter Sale") }
284+
285+
let(:benefit) { benefit_class.new(calculator:, promotion:) }
286+
let(:adjustable) { Spree::LineItem.new }
287+
288+
subject { benefit.adjustment_label(adjustable) }
289+
290+
it { is_expected.to eq("Promotion (Winter Sale)") }
291+
292+
context "if the calculator implements #line_item_adjustment_label" do
293+
let(:calculator_class) do
294+
Class.new(Spree::Calculator) do
295+
include SolidusPromotions::Calculators::PromotionCalculator
296+
297+
def line_item_adjustment_label(_line_item, _options = {})
298+
"Something entirely different"
299+
end
300+
end
301+
end
302+
303+
it { is_expected.to eq("Something entirely different") }
304+
end
305+
end
268306
end

0 commit comments

Comments
 (0)