Hello, I’m trying to exclude subscription products (managed via Recharge) from a custom bundle discount, all sold via the Shopify checkout.
With the current discount script added to “script” editor". The bundle discount is applied if a product is added a subscription. But we only want this on “one-time” purchases.
Does anyone know a good way to filter out the subscription products? - The issue I have just now is that they have the same product ID, so my idea to exclude that is out the window.
Our code currently is:
# BUNDLE DEAL CODE
class Campaign
def initialize(condition, *qualifiers)
@condition = (condition.to_s + '?').to_sym
@qualifiers = PostCartAmountQualifier ? [] : [] rescue qualifiers.compact
_item_selector = qualifiers.last unless _item_selector
qualifiers.compact.each do |qualifier|
is_multi_select = qualifier.instance_variable_get(:@conditions).is_a?(Array)
if is_multi_select
qualifier.instance_variable_get(:@conditions).each do |nested_q|
_amount_qualifier = nested_q if nested_q.is_a?(PostCartAmountQualifier)
@qualifiers << qualifier
end
else
_amount_qualifier = qualifier if qualifier.is_a?(PostCartAmountQualifier)
@qualifiers << qualifier
end
end if @qualifiers.empty?
end
def qualifies?(cart)
return true if @qualifiers.empty?
@unmodified_line_items = cart.line_items.map do |item|
new_item = item.dup
new_item.instance_variables.each do |var|
val = item.instance_variable_get(var)
new_item.instance_variable_set(var, val.dup) if val.respond_to?(:dup)
end
new_item
end if _amount_qualifier
@qualifiers.send(@condition) do |qualifier|
is_selector = false
if qualifier.is_a?(Selector) || qualifier.instance_variable_get(:@conditions).any? { |q| q.is_a?(Selector) }
is_selector = true
end rescue nil
if is_selector
raise "Missing line item match type" if _match_type.nil?
cart.line_items.send(@li_match_type) do |item|
next false if item.nil?
qualifier.match?(item)
end
else
qualifier.match?(cart, _item_selector)
end
end
end
def run_with_hooks(cart)
before_run(cart) if respond_to?(:before_run)
run(cart)
after_run(cart)
end
def after_run(cart)
.apply_final_discount if && .respond_to?(:apply_final_discount)
revert_changes(cart) unless _amount_qualifier.nil? || _amount_qualifier.match?(cart)
end
def revert_changes(cart)
cart.instance_variable_set(:@line_items, @unmodified_line_items)
end
end
class BundleDiscount < Campaign
def initialize(condition, customer_qualifier, cart_qualifier, discount, full_bundles_only, bundle_products)
super(condition, customer_qualifier, cart_qualifier, nil)
_products = bundle_products
= discount
_bundles_only = full_bundles_only
@split_items = []
_items = []
end
def check_bundles(cart)
bundled_items = _products.map do |bitem|
quantity_required = bitem[:quantity].to_i
qualifiers = bitem[:qualifiers]
type = bitem[:type].to_sym
case type
when :ptype
items = cart.line_items.select { |item| qualifiers.include?(item.variant.product.product_type) && !item.discounted? }
when :ptag
items = cart.line_items.select { |item| (qualifiers & item.variant.product.tags).length > 0 && !item.discounted? }
when :pid
qualifiers.map!(&:to_i)
items = cart.line_items.select { |item| qualifiers.include?(item.variant.product.id) && !item.discounted? }
when :vid
qualifiers.map!(&:to_i)
items = cart.line_items.select { |item| qualifiers.include?(item.variant.id) && !item.discounted? }
when :vsku
items = cart.line_items.select { |item| (qualifiers & item.variant.skus).length > 0 && !item.discounted? }
end
total_quantity = items.reduce(0) { |total, item| total + item.quantity }
{
has_all: total_quantity >= quantity_required,
total_quantity: total_quantity,
quantity_required: quantity_required,
total_possible: (total_quantity / quantity_required).to_i,
items: items
}
end
max_bundle_count = bundled_items.map{ |bundle| bundle[:total_possible] }.min if _bundles_only
if bundled_items.all? { |item| item[:has_all] }
if _bundles_only
bundled_items.each do |bundle|
bundle_quantity = bundle[:quantity_required] * max_bundle_count
split_out_extra_quantity(cart, bundle[:items], bundle[:total_quantity], bundle_quantity)
end
else
bundled_items.each do |bundle|
bundle[:items].each do |item|
_items << item
cart.line_items.delete(item)
end
end
end
return true
end
false
end
def split_out_extra_quantity(cart, items, total_quantity, quantity_required)
items_to_split = quantity_required
items.each do |item|
break if items_to_split == 0
if item.quantity > items_to_split
_items << item.split({take: items_to_split})
@split_items << item
items_to_split = 0
else
_items << item
split_quantity = item.quantity
items_to_split -= split_quantity
end
cart.line_items.delete(item)
end
cart.line_items.concat(@split_items)
@split_items.clear
end
def run(cart)
raise "Campaign requires a discount" unless
return unless qualifies?(cart)
if check_bundles(cart)
_items.each { |item| .apply(item) }
end
_items.reverse.each { |item| cart.line_items.prepend(item) }
end
end
class PercentageDiscount
def initialize(percent, message)
= (100 - percent) / 100.0
@message = message
end
def apply(line_item)
line_item.change_line_price(line_item.line_price * , message: @message)
end
end
CAMPAIGNS = [
BundleDiscount.new(
:all,
nil,
nil,
PercentageDiscount.new(
30,
"TEST A bundle offer"
),
true,
[{:type => "pid", :qualifiers => ["2339250048"], :quantity => "1"}, {:type => "pid", :qualifiers => ["7462945667198","738321258462"], :quantity => "1"}]
),
BundleDiscount.new(
:all,
nil,
nil,
PercentageDiscount.new(
30,
"TEST B bundle offer"
),
true,
[{:type => "pid", :qualifiers => ["45603345126"], :quantity => "1"}, {:type => "pid", :qualifiers => ["369131353","8859257937054","119250048"], :quantity => "1"}]
)
].freeze
CAMPAIGNS.each do |campaign|
campaign.run_with_hooks(Input.cart)
end
Output.cart = Input.cart