Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ def self.included(klass)
end

def applicable?(promotable)
if preferred_line_item_applicable == false
Spree.deprecator.warn <<~MSG
Setting `#{self.class.name}#preferred_line_item_applicable` to false is deprecated.
Please use a suitable condition that only checks the order instead, such as `OrderProduct`,
`OrderTaxon`, or `OrderOptionValue`. If you have included the `LineItemApplicableOrderLevelCondition` module
yourself, create a new condition that only checks orders:
```
class MyCondition < SolidusPromotions::Condition
def order_eligible?(order, _options = {})
# your logic here
end
end
```
MSG
end
promotable.is_a?(Spree::LineItem) ? preferred_line_item_applicable && super : super
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module SolidusPromotions
module Conditions
module OptionValueCondition
def self.included(base)
base.preference :eligible_values, :hash
base.remove_method :preferred_eligible_values
end

def preferred_eligible_values
values = preferences[:eligible_values] || {}
values.keys.map(&:to_i).zip(
values.values.map do |value|
(value.is_a?(Array) ? value : value.split(",")).map(&:to_i)
end
).to_h
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

module SolidusPromotions
module Conditions
module ProductCondition
def self.included(base)
base.has_many :condition_products,
dependent: :destroy,
foreign_key: :condition_id,
class_name: "SolidusPromotions::ConditionProduct",
inverse_of: :condition
base.has_many :products, class_name: "Spree::Product", through: :condition_products
end

def preload_relations
[:products]
end

def product_ids_string
product_ids.join(",")
end

def product_ids_string=(product_ids)
self.product_ids = product_ids.to_s.split(",").map(&:strip)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

module SolidusPromotions
module Conditions
module TaxonCondition
def self.included(base)
base.has_many :condition_taxons,
class_name: "SolidusPromotions::ConditionTaxon",
foreign_key: :condition_id,
dependent: :destroy,
inverse_of: :condition
base.has_many :taxons, through: :condition_taxons, class_name: "Spree::Taxon"
end

def preload_relations
[:taxons]
end

def taxon_ids_string
taxon_ids.join(",")
end

def taxon_ids_string=(taxon_ids)
taxon_ids = taxon_ids.to_s.split(",").map(&:strip)
self.taxons = Spree::Taxon.find(taxon_ids)
end

private

# ids of taxons conditions and taxons conditions children
def condition_taxon_ids_with_children
taxons.flat_map { |taxon| taxon.self_and_descendants.ids }.uniq
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class LineItemOptionValue < Condition
# TODO: Remove in Solidus 5
include LineItemLevelCondition

preference :eligible_values, :hash
include OptionValueCondition

def line_item_eligible?(line_item, _options = {})
pid = line_item.product.id
Expand All @@ -15,15 +15,6 @@ def line_item_eligible?(line_item, _options = {})
product_ids.include?(pid) && (value_ids(pid) & ovids).present?
end

def preferred_eligible_values
values = preferences[:eligible_values] || {}
values.keys.map(&:to_i).zip(
values.values.map do |value|
(value.is_a?(Array) ? value : value.split(",")).map(&:to_i)
end
).to_h
end

private

def product_ids
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,12 @@ class LineItemProduct < Condition
# TODO: Remove in Solidus 5
include LineItemLevelCondition

MATCH_POLICIES = %w[include exclude].freeze
include ProductCondition

has_many :condition_products,
dependent: :destroy,
foreign_key: :condition_id,
class_name: "SolidusPromotions::ConditionProduct",
inverse_of: :condition
has_many :products,
class_name: "Spree::Product",
through: :condition_products
MATCH_POLICIES = %w[include exclude].freeze

preference :match_policy, :string, default: MATCH_POLICIES.first

def preload_relations
[:products]
end

def line_item_eligible?(line_item, _options = {})
order_includes_product = product_ids.include?(line_item.variant.product_id)
success = inverse? ? !order_includes_product : order_includes_product
Expand All @@ -40,14 +29,6 @@ def line_item_eligible?(line_item, _options = {})
success
end

def product_ids_string
product_ids.join(",")
end

def product_ids_string=(product_ids)
self.product_ids = product_ids.to_s.split(",").map(&:strip)
end

private

def inverse?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,14 @@ class LineItemTaxon < Condition
# TODO: Remove in Solidus 5
include LineItemLevelCondition

has_many :condition_taxons,
class_name: "SolidusPromotions::ConditionTaxon",
foreign_key: :condition_id,
dependent: :destroy,
inverse_of: :condition
has_many :taxons, through: :condition_taxons, class_name: "Spree::Taxon"
include TaxonCondition

MATCH_POLICIES = %w[include exclude].freeze

validates :preferred_match_policy, inclusion: { in: MATCH_POLICIES }

preference :match_policy, :string, default: MATCH_POLICIES.first

def preload_relations
[:taxons]
end

def line_item_eligible?(line_item, _options = {})
found = Spree::Classification.where(
product_id: line_item.variant.product_id,
Expand All @@ -38,26 +29,6 @@ def line_item_eligible?(line_item, _options = {})
raise "unexpected match policy: #{preferred_match_policy.inspect}"
end
end

def taxon_ids_string
taxons.pluck(:id).join(",")
end

def taxon_ids_string=(taxon_ids)
taxon_ids = taxon_ids.to_s.split(",").map(&:strip)
self.taxons = Spree::Taxon.find(taxon_ids)
end

def updateable?
true
end

private

# ids of taxons conditions and taxons conditions children
def condition_taxon_ids_with_children
taxons.flat_map { |taxon| taxon.self_and_descendants.ids }.uniq
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,15 @@ module Conditions
class OptionValue < Condition
include LineItemApplicableOrderLevelCondition

preference :eligible_values, :hash
include OptionValueCondition

def order_eligible?(order, _options = {})
order.line_items.any? { |item| line_item_eligible?(item) }
OrderOptionValue.new(preferred_eligible_values: preferred_eligible_values).eligible?(order)
end

def line_item_eligible?(line_item, _options = {})
LineItemOptionValue.new(preferred_eligible_values: preferred_eligible_values).eligible?(line_item)
end

def preferred_eligible_values
values = preferences[:eligible_values] || {}
values.keys.map(&:to_i).zip(
values.values.map do |value|
(value.is_a?(Array) ? value : value.split(",")).map(&:to_i)
end
).to_h
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

module SolidusPromotions
module Conditions
class OrderOptionValue < Condition
include OptionValueCondition

def order_eligible?(order, _options = {})
order.line_items.any? do |line_item|
LineItemOptionValue.new(preferred_eligible_values: preferred_eligible_values).eligible?(line_item)
end
end

def to_partial_path
"solidus_promotions/admin/condition_fields/option_value"
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# frozen_string_literal: true

module SolidusPromotions
module Conditions
# A condition to limit a promotion based on products in the order. Can
# require all or any of the products to be present. Valid products
# either come from assigned product group or are assingned directly to
# the condition.
class OrderProduct < Condition
include ProductCondition

MATCH_POLICIES = %w[any all none only].freeze

validates :preferred_match_policy, inclusion: { in: MATCH_POLICIES }

preference :match_policy, :string, default: MATCH_POLICIES.first

# scope/association that is used to test eligibility
def eligible_products
products
end

def order_eligible?(order)
return true if eligible_products.empty?

case preferred_match_policy
when "all"
unless eligible_products.all? { |product| order_products(order).include?(product) }
eligibility_errors.add(:base, eligibility_error_message(:missing_product), error_code: :missing_product)
end
when "any"
unless order_products(order).any? { |product| eligible_products.include?(product) }
eligibility_errors.add(:base, eligibility_error_message(:no_applicable_products),
error_code: :no_applicable_products)
end
when "none"
unless order_products(order).none? { |product| eligible_products.include?(product) }
eligibility_errors.add(:base, eligibility_error_message(:has_excluded_product),
error_code: :has_excluded_product)
end
when "only"
unless order_products(order).all? { |product| eligible_products.include?(product) }
eligibility_errors.add(:base, eligibility_error_message(:has_excluded_product),
error_code: :has_excluded_product)
end
end

eligibility_errors.empty?
end

def to_partial_path
"solidus_promotions/admin/condition_fields/product"
end

private

def order_products(order)
order.line_items.map(&:variant).map(&:product)
end
end
end
end
Loading
Loading