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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
module SolidusPromotions
module Benefits
module LineItemBenefit
def self.included(_base)
Spree.deprecator.warn("Including #{name} is deprecated.")
end

def can_discount?(object)
object.is_a? Spree::LineItem
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
module SolidusPromotions
module Benefits
module OrderBenefit
def self.included(_base)
Spree.deprecator.warn("Including #{name} is deprecated.")
end

def can_discount?(_)
false
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
module SolidusPromotions
module Benefits
module ShipmentBenefit
def self.included(_base)
Spree.deprecator.warn("Including #{name} is deprecated.")
end

def can_discount?(object)
object.is_a?(Spree::Shipment) || object.is_a?(Spree::ShippingRate)
end
Expand Down
84 changes: 65 additions & 19 deletions promotions/app/models/solidus_promotions/benefit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ module SolidusPromotions
# Subclasses specialize the discounting target (orders, line items, or
# shipments) and usually include one of the following mixins to integrate with
# Solidus' adjustment system:
# - SolidusPromotions::Benefits::OrderBenefit
# - SolidusPromotions::Benefits::LineItemBenefit
# - SolidusPromotions::Benefits::ShipmentBenefit
# - SolidusPromotions::Benefits::AdjustLineItem
# - SolidusPromotions::Benefits::AdjustShipment
# - SolidusPromotions::Benefits::CreateDiscountedItem
#
# A benefit can discount any object for which {#can_discount?} returns true.
# Implementors must provide a calculator via Spree::CalculatedAdjustments and
Expand Down Expand Up @@ -67,7 +67,7 @@ class Benefit < Spree::Base
# Returns relations that should be preloaded for this condition.
#
# Override this method in subclasses to specify associations that should be eager loaded
# to avoid N+1 queries when evaluating conditions.
# to avoid N+1 queries when computing discounts or performing automations.
#
# @return [Array<Symbol>] An array of association names to preload
def preload_relations
Expand All @@ -81,13 +81,11 @@ def preload_relations
#
# @param object [Object] a potential adjustable (order, line item, or shipment)
# @return [Boolean]
# @raise [NotImplementedError] when not implemented by the subclass/mixin
# @see SolidusPromotions::Benefits::OrderBenefit,
# SolidusPromotions::Benefits::LineItemBenefit,
# SolidusPromotions::Benefits::ShipmentBenefit
# @see SolidusPromotions::Benefits::AdjustLineItem,
# SolidusPromotions::Benefits::AdjustShipment,
# SolidusPromotions::Benefits::CreateDiscountedItem
def can_discount?(object)
raise NotImplementedError, "Please implement the correct interface, or include one of the `SolidusPromotions::Benefits::OrderBenefit`, " \
"`SolidusPromotions::Benefits::LineItemBenefit` or `SolidusPromotions::Benefits::ShipmentBenefit` modules"
respond_to?(discount_method_for(object))
end

# Calculates and returns a discount for the given adjustable object.
Expand All @@ -96,7 +94,7 @@ def can_discount?(object)
# an ItemDiscount object representing the discount to be applied. If the computed
# amount is zero, no discount is returned.
#
# @param adjustable [Object] The object to calculate the discount for (e.g., LineItem, Order, Shipment)
# @param adjustable [Object] The object to calculate the discount for (e.g., LineItem, Shipment, ShippingRate)
# @param ... [args, kwargs] Additional arguments passed to the calculator's compute method
#
# @return [SolidusPromotions::ItemDiscount, nil] An ItemDiscount object if a discount applies, nil if the amount is zero
Expand All @@ -108,14 +106,58 @@ def can_discount?(object)
# @see #compute_amount
# @see #adjustment_label
def discount(adjustable, ...)
amount = compute_amount(adjustable, ...)
return if amount.zero?
ItemDiscount.new(
item: adjustable,
label: adjustment_label(adjustable),
amount: amount,
source: self
)
if can_discount?(adjustable)
send(discount_method_for(adjustable), adjustable, ...)
else
raise NotImplementedError, "Please implement #{discount_method_for(adjustable)} in your condition"
end
end

def self.inherited(klass)
def klass.method_added(method_added)
if method_added == :discount
Spree.deprecator.warn <<~MSG
Please refactor `#{name}`. You're defining `#discount`. Instead, define a method for each type of discountable
that your benefit can discount. For example:
```
class MyBenefit < SolidusPromotions::Benefit
def can_discount?(discountable)
discountable.is_a?(Spree::LineItem)
end

def discount(order, _options = {})
amount = compute_amount(line_item, ...)
return if amount.zero?

ItemDiscount.new(
item: line_item,
label: adjustment_label(line_item),
amount: amount,
source: self
)
end
```
can now become
```
class MyBenefit < SolidusPromotions::Benefit
def discount_line_item(order, ...)
amount = compute_amount(line_item, ...)
return if amount.zero?

ItemDiscount.new(
item: line_item,
label: adjustment_label(line_item),
amount: amount,
source: self
)
end
end
```
MSG
end
super
end
super
end

# Computes the discount amount for the given adjustable.
Expand Down Expand Up @@ -231,6 +273,10 @@ def possible_conditions

private

def discount_method_for(adjustable)
:"discount_#{adjustable.class.name.demodulize.underscore}"
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 @@ -3,11 +3,26 @@
module SolidusPromotions
module Benefits
class AdjustLineItem < Benefit
include SolidusPromotions::Benefits::LineItemBenefit
def discount_line_item(line_item, ...)
amount = compute_amount(line_item, ...)
return if amount.zero?

ItemDiscount.new(
item: line_item,
label: adjustment_label(line_item),
amount: amount,
source: self
)
end

def possible_conditions
super + SolidusPromotions.config.line_item_conditions
end

def level
:line_item
end
deprecate :level, deprecator: Spree.deprecator
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,38 @@
module SolidusPromotions
module Benefits
class AdjustShipment < Benefit
include SolidusPromotions::Benefits::ShipmentBenefit
def discount_shipment(shipment, ...)
amount = compute_amount(shipment, ...)
return if amount.zero?

ItemDiscount.new(
item: shipment,
label: adjustment_label(shipment),
amount: amount,
source: self
)
end

def discount_shipping_rate(shipping_rate, ...)
amount = compute_amount(shipping_rate, ...)
return if amount.zero?

ItemDiscount.new(
item: shipping_rate,
label: adjustment_label(shipping_rate),
amount: amount,
source: self
)
end

def possible_conditions
super + SolidusPromotions.config.shipment_conditions
end

def level
:shipment
end
deprecate :level, deprecator: Spree.deprecator
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,39 @@
module SolidusPromotions
module Benefits
class CreateDiscountedItem < Benefit
include OrderBenefit
preference :variant_id, :integer
preference :quantity, :integer, default: 1
preference :necessary_quantity, :integer, default: 1

def perform(order)
line_item = find_item(order) || build_item(order)
set_quantity(line_item, determine_item_quantity(order))
line_item.current_discounts << discount(line_item)
line_item.current_discounts << discount_line_item(line_item)
end

def remove_from(order)
find_item(order)&.mark_for_destruction
end

def level
:order
end
deprecate :level, deprecator: Spree.deprecator

private

def discount_line_item(line_item, ...)
amount = compute_amount(line_item, ...)
return if amount.zero?

ItemDiscount.new(
item: line_item,
label: adjustment_label(line_item),
amount: amount,
source: self
)
end

def find_item(order)
order.line_items.detect { |line_item| line_item.managed_by_order_benefit == self }
end
Expand Down
Loading
Loading