# frozen_string_literal: true module Spree class Promotion < Spree::Base module Rules # A rule 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 rule. class Product < PromotionRule has_many :product_promotion_rules, dependent: :destroy, foreign_key: :promotion_rule_id, class_name: 'Spree::ProductPromotionRule' has_many :products, class_name: 'Spree::Product', through: :product_promotion_rules def preload_relations [:products] end MATCH_POLICIES = %w(any all none) validates_inclusion_of :preferred_match_policy, 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 applicable?(promotable) promotable.is_a?(Spree::Order) end def eligible?(order, _options = {}) 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 else raise "unexpected match policy: #{preferred_match_policy.inspect}" end eligibility_errors.empty? end def actionable?(line_item) case preferred_match_policy when 'any', 'all' product_ids.include? line_item.variant.product_id when 'none' product_ids.exclude? line_item.variant.product_id else raise "unexpected match policy: #{preferred_match_policy.inspect}" end 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 order_products(order) order.line_items.map(&:variant).map(&:product) end end end end end