I have a Bundle Discount script (created with the Script Creator) where customers get % off when buying a combination of certain products (pid). In the BundleDiscount object there are bundle_products which form the bundle. I then have the CAMPAIGNS array that contains all the bundle instances.
The way the script works is it first applies the discount on the BundleDiscount object that has the lowest index in the CAMPAIGNS array (the first object in the array). So that becomes a problem when users are on a particular bundle page that contains several products forming a discount (let’s call it Bundle 1). Once added to the cart, they get the discount as Bundle 1, however, if you add other single products to the cart later on that are also part of a pack (Bundle 2), they might break the first bundle if one of the products is in both bundles, and if Bundle 2 is mentioned before Bundle 1 in the array (because the products will regroup accordingly):
CAMPAIGNS = [
Bundle 2,
Bundle 1
]
This is a no-go, because if users picked the products from a Bundle page, they would want to keep them grouped as they check out.
So how i want to solve it is creating tags only for the bundle product pages, then If a user is on product pages that contain those tags, I want to push the BundleDiscount object that contains these products in the beginning of the CAMPAIGNS array, meaning that this will be the first bundle to get the discount.
I’ve been trying for a week now, but I am completely clueless on how to approach this problem in Ruby..
Some of the code:
class Campaign
def initialize(condition, *qualifiers)
@condition = (condition.to_s + '?').to_sym
@qualifiers = PostCartAmountQualifier ? [] : [] rescue qualifiers.compact
@line_item_selector = qualifiers.last unless @line_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|
@post_amount_qualifier = nested_q if nested_q.is_a?(PostCartAmountQualifier)
@qualifiers << qualifier
end
else
@post_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 @post_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 @li_match_type.nil?
cart.line_items.send(@li_match_type) { |item| qualifier.match?(item) }
else
qualifier.match?(cart, @line_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)
@discount.apply_final_discount if @discount && @discount.respond_to?(:apply_final_discount)
revert_changes(cart) unless @post_amount_qualifier.nil? || @post_amount_qualifier.match?(cart)
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)
@bundle_products = bundle_products
@discount = discount
@full_bundles_only = full_bundles_only
@split_items = []
@bundle_items = []
end
def check_bundles(cart)
bundled_items = @bundle_products.map do |bitem|
quantity_required = bitem[:quantity].to_i
qualifiers = bitem[:qualifiers]
type = bitem[:type].to_sym
case type
when :pid
qualifiers.map!(&:to_i)
items = cart.line_items.select { |item| qualifiers.include?(item.variant.product.id) && !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
false
end
def split_out_extra_quantity(cart, items, total_quantity, quantity_required)
#some (irrelevant for now) code that split out extra quantity out of a pack
end
class PercentageDiscount
def initialize(percent, message)
@discount = (100 - percent) / 100.0
@message = message
end
def apply(line_item)
line_item.change_line_price(line_item.line_price * @discount, message: @message)
end
end
CAMPAIGNS = [
# Single bundle
BundleDiscount.new(
:any,
nil,
nil,
PercentageDiscount.new(
12.01,
"Bundle discount applied"
),
true,
[
{:type => "pid", :qualifiers => ["2631126679652"], :quantity => "1"},
{:type => "pid", :qualifiers => ["2631131365476"], :quantity => "1"}
]
)
].freeze
CAMPAIGNS.each do |campaign|
campaign.run_with_hooks(Input.cart)
end
Output.cart = Input.cart