@@ -11,9 +11,9 @@ module SolidusPromotions
1111 # Subclasses specialize the discounting target (orders, line items, or
1212 # shipments) and usually include one of the following mixins to integrate with
1313 # Solidus' adjustment system:
14- # - SolidusPromotions::Benefits::OrderBenefit
15- # - SolidusPromotions::Benefits::LineItemBenefit
16- # - SolidusPromotions::Benefits::ShipmentBenefit
14+ # - SolidusPromotions::Benefits::AdjustLineItem
15+ # - SolidusPromotions::Benefits::AdjustShipment
16+ # - SolidusPromotions::Benefits::CreateDiscountedItem
1717 #
1818 # A benefit can discount any object for which {#can_discount?} returns true.
1919 # Implementors must provide a calculator via Spree::CalculatedAdjustments and
@@ -67,7 +67,7 @@ class Benefit < Spree::Base
6767 # Returns relations that should be preloaded for this condition.
6868 #
6969 # Override this method in subclasses to specify associations that should be eager loaded
70- # to avoid N+1 queries when evaluating conditions .
70+ # to avoid N+1 queries when computing discounts or performing automations .
7171 #
7272 # @return [Array<Symbol>] An array of association names to preload
7373 def preload_relations
@@ -81,13 +81,11 @@ def preload_relations
8181 #
8282 # @param object [Object] a potential adjustable (order, line item, or shipment)
8383 # @return [Boolean]
84- # @raise [NotImplementedError] when not implemented by the subclass/mixin
85- # @see SolidusPromotions::Benefits::OrderBenefit,
86- # SolidusPromotions::Benefits::LineItemBenefit,
87- # SolidusPromotions::Benefits::ShipmentBenefit
84+ # @see SolidusPromotions::Benefits::AdjustLineItem,
85+ # SolidusPromotions::Benefits::AdjustShipment,
86+ # SolidusPromotions::Benefits::CreateDiscountedItem
8887 def can_discount? ( object )
89- raise NotImplementedError , "Please implement the correct interface, or include one of the `SolidusPromotions::Benefits::OrderBenefit`, " \
90- "`SolidusPromotions::Benefits::LineItemBenefit` or `SolidusPromotions::Benefits::ShipmentBenefit` modules"
88+ respond_to? ( discount_method_for ( object ) )
9189 end
9290
9391 # Calculates and returns a discount for the given adjustable object.
@@ -96,7 +94,7 @@ def can_discount?(object)
9694 # an ItemDiscount object representing the discount to be applied. If the computed
9795 # amount is zero, no discount is returned.
9896 #
99- # @param adjustable [Object] The object to calculate the discount for (e.g., LineItem, Order, Shipment )
97+ # @param adjustable [Object] The object to calculate the discount for (e.g., LineItem, Shipment, ShippingRate )
10098 # @param ... [args, kwargs] Additional arguments passed to the calculator's compute method
10199 #
102100 # @return [SolidusPromotions::ItemDiscount, nil] An ItemDiscount object if a discount applies, nil if the amount is zero
@@ -108,14 +106,58 @@ def can_discount?(object)
108106 # @see #compute_amount
109107 # @see #adjustment_label
110108 def discount ( adjustable , ...)
111- amount = compute_amount ( adjustable , ...)
112- return if amount . zero?
113- ItemDiscount . new (
114- item : adjustable ,
115- label : adjustment_label ( adjustable ) ,
116- amount : amount ,
117- source : self
118- )
109+ if can_discount? ( adjustable )
110+ send ( discount_method_for ( adjustable ) , adjustable , ...)
111+ else
112+ raise NotImplementedError , "Please implement #{ discount_method_for ( adjustable ) } in your condition"
113+ end
114+ end
115+
116+ def self . inherited ( klass )
117+ def klass . method_added ( method_added )
118+ if method_added == :discount
119+ Spree . deprecator . warn <<~MSG
120+ Please refactor `#{ name } `. You're defining `#discount`. Instead, define a method for each type of discountable
121+ that your benefit can discount. For example:
122+ ```
123+ class MyBenefit < SolidusPromotions::Benefit
124+ def can_discount?(discountable)
125+ discountable.is_a?(Spree::LineItem)
126+ end
127+
128+ def discount(order, _options = {})
129+ amount = compute_amount(line_item, ...)
130+ return if amount.zero?
131+
132+ ItemDiscount.new(
133+ item: line_item,
134+ label: adjustment_label(line_item),
135+ amount: amount,
136+ source: self
137+ )
138+ end
139+ ```
140+ can now become
141+ ```
142+ class MyBenefit < SolidusPromotions::Benefit
143+ def discount_line_item(order, ...)
144+ amount = compute_amount(line_item, ...)
145+ return if amount.zero?
146+
147+ ItemDiscount.new(
148+ item: line_item,
149+ label: adjustment_label(line_item),
150+ amount: amount,
151+ source: self
152+ )
153+ end
154+ end
155+ ```
156+ MSG
157+ end
158+ super
159+ end
160+ super
119161 end
120162
121163 # Computes the discount amount for the given adjustable.
@@ -231,6 +273,10 @@ def possible_conditions
231273
232274 private
233275
276+ def discount_method_for ( adjustable )
277+ :"discount_#{ adjustable . class . name . demodulize . underscore } "
278+ end
279+
234280 # Prevents destroying a benefit when it has adjustments on completed orders.
235281 #
236282 # Adds an error and aborts the destroy callback chain when such adjustments exist.
0 commit comments