Shopify Scripts Bulk Discounts & Other Shopify Discounts

Hello,

Perhaps a noob question but we are just getting started with Scripts.

We have applied the script for ‘bulk discounts’:

  • 2 items get 10%

  • 3 items 12.5%

  • 4+ items 15%

However, I have 2 main issues:

  1. Customers can now combine the bulk discounts with the other codes e.g. WELCOME10
    • I only want discounts to apply to single-quantity orders and to display the message 'This code is only valid with single-item orders
  2. During Sale periods i want our Sale discount code to override the volume discounts so they can use SALE20 and get 20% off only when ordering any quantity

Any help would be great

Here is the script for the bulk discounts

================================ Customizable Settings ================================

================================================================

Tiered Product Discount by Quantity

If the total quantity of matching items is greater than (or

equal to) an entered threshold, the associated discount is

applied to each matching item.

- ‘product_selector_match_type’ determines whether we look for

products that do or don’t match the entered selectors. Can

be:

- ‘:include’ to check if the product does match

- ‘:exclude’ to make sure the product doesn’t match

- ‘product_selector_type’ determines how eligible products

will be identified. Can be either:

- ‘:tag’ to find products by tag

- ‘:type’ to find products by type

- ‘:vendor’ to find products by vendor

- ‘:product_id’ to find products by ID

- ‘:variant_id’ to find products by variant ID

- ‘:subscription’ to find subscription products

- ‘:all’ for all products

- ‘product_selectors’ is a list of identifiers (from above) for

qualifying products. Product/Variant ID lists should only

contain numbers (ie. no quotes). If ‘:all’ is used, this

can also be ‘nil’.

- ‘tiers’ is a list of tiers where:

- ‘quantity’ is the minimum quantity you need to buy to

qualify

- ‘discount_type’ is the type of discount to provide. Can be

either:

- ‘:percent’

- ‘:dollar’

- ‘discount_amount’ is the percentage/dollar discount to

apply (per item)

- ‘discount_message’ is the message to show when a discount

is applied

================================================================

PRODUCT_DISCOUNT_TIERS = [
{
product_selector_match_type: :include,
product_selector_type: :all,
tiers: [
{

quantity: 2,
discount_type: :percent,
discount_amount: 10,
discount_message: ‘You saved 10%! Why not add 1 more item to SAVE 12.5%?’
},
{
quantity: 3,
discount_type: :percent,
discount_amount: 12.5,
discount_message: ‘You saved 12.5%! Why not add 1 more item to SAVE 15%’,
},
{
quantity: 4,
discount_type: :percent,
discount_amount: 15,
discount_message: ‘Congrats! You unlocked our biggest discount!’,
},
],
},
]

================================ Script Code (do not edit) ================================

================================================================

ProductSelector

Finds matching products by the entered criteria.

================================================================

class ProductSelector
def initialize(match_type, selector_type, selectors)
@match _type = match_type
@comparator = match_type == :include ? ‘any?’ : ‘none?’
@Selector _type = selector_type
@selectors = selectors
end

def match?(line_item)
if self.respond_to?(@selector_type)
self.send(@selector_type, line_item)
else
raise RuntimeError.new(‘Invalid product selector type’)
end
end

def tag(line_item)
product_tags = line_item.variant.product.tags.map { |tag| tag.downcase.strip }
@selectors = @selectors.map { |selector| selector.downcase.strip }
(@selectors & product_tags).send(@comparator)
end

def type(line_item)
@selectors = @selectors.map { |selector| selector.downcase.strip }
(@match_type == :include) == @selectors.include?(line_item.variant.product.product_type.downcase.strip)
end

def vendor(line_item)
@selectors = @selectors.map { |selector| selector.downcase.strip }
(@match_type == :include) == @selectors.include?(line_item.variant.product.vendor.downcase.strip)
end

def product_id(line_item)
(@match_type == :include) == @selectors.include?(line_item.variant.product.id)
end

def variant_id(line_item)
(@match_type == :include) == @selectors.include?(line_item.variant.id)
end

def subscription(line_item)
!line_item.selling_plan_id.nil?
end

def all(line_item)
true
end
end

================================================================

DiscountApplicator

Applies the entered discount to the supplied line item.

================================================================

class DiscountApplicator
def initialize(discount_type, discount_amount, discount_message)
@discount _type = discount_type
@discount _message = discount_message

@discount _amount = if discount_type == :percent
1 - (discount_amount * 0.01)
else
Money.new(cents: 100) * discount_amount
end
end

def apply(line_item)
new_line_price = if @discount _type == :percent
line_item.line_price * @discount _amount
else
[line_item.line_price - (@discount_amount * line_item.quantity), Money.zero].max
end

line_item.change_line_price(new_line_price, message: @discount _message)
end
end

================================================================

TieredProductDiscountByQuantityCampaign

If the total quantity of matching items is greater than (or

equal to) an entered threshold, the associated discount is

applied to each matching item.

================================================================

class TieredProductDiscountByQuantityCampaign
def initialize(campaigns)
@campaigns = campaigns
end

def run(cart)
@campaigns.each do |campaign|
product_selector = ProductSelector.new(
campaign[:product_selector_match_type],
campaign[:product_selector_type],
campaign[:product_selectors],
)

applicable_items = cart.line_items.select { |line_item| product_selector.match?(line_item) }

next if applicable_items.nil?

total_applicable_quantity = applicable_items.map(&:quantity).reduce(0, :+)
tiers = campaign[:tiers].sort_by { |tier| tier[:quantity] }.reverse
applicable_tier = tiers.find { |tier| tier[:quantity] <= total_applicable_quantity }

next if applicable_tier.nil?

discount_applicator = DiscountApplicator.new(
applicable_tier[:discount_type],
applicable_tier[:discount_amount],
applicable_tier[:discount_message]
)

applicable_items.each do |line_item|
discount_applicator.apply(line_item)
end
end
end
end

CAMPAIGNS = [
TieredProductDiscountByQuantityCampaign.new(PRODUCT_DISCOUNT_TIERS),
]

CAMPAIGNS.each do |campaign|
campaign.run(Input.cart)
end

Output.cart = Input.cart