Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 46 additions & 8 deletions promotions/app/models/solidus_promotions/benefit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,48 @@ def compute_amount(adjustable, ...)

# Builds the localized label for adjustments created by this benefit.
#
# @param adjustable [Object]
# @return [String]
def adjustment_label(adjustable)
I18n.t(
"solidus_promotions.adjustment_labels.#{adjustable.class.name.demodulize.underscore}",
promotion: SolidusPromotions::Promotion.model_name.human,
promotion_customer_label: promotion.customer_label
)
# This method attempts to use a calculator-specific label method if available,
# falling back to a localized string key based on the adjustable's class name.
#
# ## Calculator Override
#
# Calculators can provide custom labels by implementing a method named after the
# adjustable type. For example, a calculator that discounts line items could
# implement `line_item_adjustment_label`:
#
# @example Custom calculator with adjustment label
# class MyCalculator < Spree::Calculator
# def compute(adjustable, *args)
# # calculation logic
# end
#
# def line_item_adjustment_label(line_item, *args)
# "Custom discount for #{line_item.product.name}"
# end
# end
#
# The method name follows the pattern: `{adjustable_type}_adjustment_label`
# where `{adjustable_type}` is the underscored class name of the adjustable
# (e.g., `line_item`, `shipment`, `shipping_rate`).
#
# If the calculator does not respond to the expected method, the benefit will
# fall back to using an i18n translation key based on the adjustable's class.
#
# @param adjustable [Object] the object being discounted (e.g., Spree::LineItem, Spree::Shipment)
# @param ... [args, kwargs] additional arguments forwarded to the calculator's label method
# @return [String] a localized label suitable for display in adjustments
#
# @see #adjustment_label_method_for
def adjustment_label(adjustable, ...)
if calculator.respond_to?(adjustment_label_method_for(adjustable))
calculator.send(adjustment_label_method_for(adjustable), adjustable, ...)
else
I18n.t(
"solidus_promotions.adjustment_labels.#{adjustable.class.name.demodulize.underscore}",
promotion: SolidusPromotions::Promotion.model_name.human,
promotion_customer_label: promotion.customer_label
)
end
end

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

def adjustment_label_method_for(adjustable)
:"#{adjustable.class.name.demodulize.underscore}_adjustment_label"
end

# Prevents destroying a benefit when it has adjustments on completed orders.
#
# Adds an error and aborts the destroy callback chain when such adjustments exist.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ def update_adjustment(item, discount_item)
adjustment ||= item.adjustments.new(
source: discount_item.source,
order_id: item.is_a?(Spree::Order) ? item.id : item.order_id,
label: discount_item.label
)
adjustment.amount = discount_item.amount
adjustment.label = discount_item.label
adjustment
end
end
Expand Down
38 changes: 38 additions & 0 deletions promotions/spec/models/solidus_promotions/benefit_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,42 @@ def discount_line_item(order, ...)
expect { subject }.to raise_exception(NotImplementedError)
end
end

describe "#adjustment_label" do
let(:benefit_class) do
Class.new(described_class) do
end
end

let(:calculator_class) do
Class.new(Spree::Calculator) do
include SolidusPromotions::Calculators::PromotionCalculator
end
end

let(:calculator) { calculator_class.new }

let(:promotion) { build_stubbed(:solidus_promotion, customer_label: "Winter Sale") }

let(:benefit) { benefit_class.new(calculator:, promotion:) }
let(:adjustable) { Spree::LineItem.new }

subject { benefit.adjustment_label(adjustable) }

it { is_expected.to eq("Promotion (Winter Sale)") }

context "if the calculator implements #line_item_adjustment_label" do
let(:calculator_class) do
Class.new(Spree::Calculator) do
include SolidusPromotions::Calculators::PromotionCalculator

def line_item_adjustment_label(_line_item, _options = {})
"Something entirely different"
end
end
end

it { is_expected.to eq("Something entirely different") }
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@
expect(adjustable.current_discounts).to be_empty
end

it "updates adjustment labels" do
subject
expect_any_instance_of(SolidusPromotions::Benefit).to receive(:adjustment_label).and_return("Changed Adjustment Label")
expect { order_adjuster.call }
.to change { order.line_items.first.adjustments.first.label }
.from("Promotion (Because we like you)")
.to("Changed Adjustment Label")
end

context "if order is complete but not shipped" do
let(:line_item) { order.line_items.first }
let(:order) { create(:order_ready_to_ship) }
Expand Down
Loading