Personalized checkout and custom promotions with Shopify Scripts
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
By investing 30 minutes of your time, you can unlock the potential for increased sales,...
By Jacqui Sep 11, 2024We appreciate the diverse ways you participate in and engage with the Shopify Communi...
By JasonH Sep 9, 2024Thanks to everyone who participated in our AMA with 2H Media: Marketing Your Shopify St...
By Jacqui Sep 6, 2024