Solved

Script for Buy 2 for $40 that uses a discount code

megs330
Visitor
2 1 0

I'm looking for help on how to modify a script so that it only runs when a discount code is applied. The script is for Buy a specific quantity of a product for a specified amount (ex: Buy 2 necklaces for $40). I'm not a coder, so I'm looking for help with modifying this script in the Shopify script library to include a specific discount code. Appreciate any help!

Accepted Solution (1)
megs330
Visitor
2 1 0

This is an accepted solution.

Thanks, appreciate your idea! We already found a different work around that seems to be working:

 

BUY_X_GET_Y_FOR_Z_WITH_DISCOUNT_CODE = [
{
discount_code_match_type: :exact,
discount_codes: ["CODE"],
product_selector_match_type: :include,
product_selector_type: :tag,
product_selectors: ["2for40"],
quantity_to_buy: 2,
final_price: 40,
discount_message: 'SET MESSAGE HERE'
}
]

# ================================================================
# DiscountCodeSelector
#
# Finds whether the supplied discount code matches any of the
# entered codes.
# ================================================================
class DiscountCodeSelector
def initialize(match_type, discount_codes)
@comparator = match_type == :exact ? '==' : 'include?'
@discount_codes = discount_codes.map { |discount_code| discount_code.upcase.strip }
end

def match?(discount_code)
@discount_codes.any? { |code| discount_code.code.upcase.send(@comparator, code) }
end
end


# ================================================================
# 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

# ================================================================
# DollarDiscountApplicator
#
# Applies the entered discount to the supplied line item.
# ================================================================
class DollarDiscountApplicator
def initialize(discount_message)
@discount_message = discount_message
end

def apply(line_item, discount_amount)
new_line_price = line_item.line_price - discount_amount
line_item.change_line_price(new_line_price, message: @discount_message)
end
end


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

def run(cart)
return if cart.discount_code.nil?

@campaigns.each do |campaign|
discount_code_selector = DiscountCodeSelector.new(
campaign[:discount_code_match_type],
campaign[:discount_codes]
)

next unless discount_code_selector.match?(cart.discount_code)

product_selector = ProductSelector.new(
campaign[:product_selector_match_type],
campaign[:product_selector_type],
campaign[:product_selectors],
)

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

next if eligible_items.nil?

eligible_item_count = 2
quantity_to_buy = campaign[:quantity_to_buy]
number_of_offers = (eligible_item_count / quantity_to_buy).floor

next unless number_of_offers > 0

number_of_discountable_items = number_of_offers * quantity_to_buy
total_offer_price = Money.new(cents: 100) * (number_of_offers * campaign[:final_price])
discount_applicator = DollarDiscountApplicator.new(campaign[:discount_message])

self.loop_items(cart, eligible_items, number_of_discountable_items, total_offer_price, discount_applicator)
end
end

def loop_items(cart, line_items, num_to_discount, total_price, discount_applicator)
current_price = Money.zero
avg_price = total_price * (1 / num_to_discount)

line_items = line_items.sort_by { |line_item| line_item.variant.price }

line_items.each do |line_item|
break if num_to_discount <= 0

if line_item.quantity > num_to_discount
split_line_item = line_item.split(take: num_to_discount)
discount_amount = split_line_item.line_price - (total_price - current_price)
discount_applicator.apply(split_line_item, discount_amount)
position = cart.line_items.find_index(line_item)
cart.line_items.insert(position + 1, split_line_item)
break
elsif line_item.quantity == num_to_discount
discount_amount = line_item.line_price - (total_price - current_price)
discount_applicator.apply(line_item, discount_amount)
break
else
if line_item.variant.price <= avg_price
current_price += line_item.line_price
else
discount_amount = (line_item.variant.price - avg_price) * line_item.quantity
current_price += (line_item.line_price - discount_amount)
discount_applicator.apply(line_item, discount_amount)
end

num_to_discount -= line_item.quantity
end
end
end
end

CAMPAIGNS = [
BuyXofYforZwithDiscountCodeCampaign.new(BUY_X_GET_Y_FOR_Z_WITH_DISCOUNT_CODE),
]

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

Output.cart = Input.cart

View solution in original post

Replies 2 (2)

Kani
Shopify Partner
468 125 225

Hi @megs330 

 

simply do it like this

 

#run only code applied
if  Input.cart.discount_code != nil and Input.cart.discount_code.code == "Your code"
    CAMPAIGNS.each do |campaign|
      campaign.run(Input.cart)
    end
end

Kani_0-1661357321274.png

 

Hey!! ʕ ᵔᴥᵔ ʔ
Please click 'Like' and ' Accept as Solution' to encourage me to continue sharing my expertise. ♡
If you need any technical assistance, feel free to send me a DM. You no longer have to search for answers without getting a response. 🙂
megs330
Visitor
2 1 0

This is an accepted solution.

Thanks, appreciate your idea! We already found a different work around that seems to be working:

 

BUY_X_GET_Y_FOR_Z_WITH_DISCOUNT_CODE = [
{
discount_code_match_type: :exact,
discount_codes: ["CODE"],
product_selector_match_type: :include,
product_selector_type: :tag,
product_selectors: ["2for40"],
quantity_to_buy: 2,
final_price: 40,
discount_message: 'SET MESSAGE HERE'
}
]

# ================================================================
# DiscountCodeSelector
#
# Finds whether the supplied discount code matches any of the
# entered codes.
# ================================================================
class DiscountCodeSelector
def initialize(match_type, discount_codes)
@comparator = match_type == :exact ? '==' : 'include?'
@discount_codes = discount_codes.map { |discount_code| discount_code.upcase.strip }
end

def match?(discount_code)
@discount_codes.any? { |code| discount_code.code.upcase.send(@comparator, code) }
end
end


# ================================================================
# 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

# ================================================================
# DollarDiscountApplicator
#
# Applies the entered discount to the supplied line item.
# ================================================================
class DollarDiscountApplicator
def initialize(discount_message)
@discount_message = discount_message
end

def apply(line_item, discount_amount)
new_line_price = line_item.line_price - discount_amount
line_item.change_line_price(new_line_price, message: @discount_message)
end
end


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

def run(cart)
return if cart.discount_code.nil?

@campaigns.each do |campaign|
discount_code_selector = DiscountCodeSelector.new(
campaign[:discount_code_match_type],
campaign[:discount_codes]
)

next unless discount_code_selector.match?(cart.discount_code)

product_selector = ProductSelector.new(
campaign[:product_selector_match_type],
campaign[:product_selector_type],
campaign[:product_selectors],
)

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

next if eligible_items.nil?

eligible_item_count = 2
quantity_to_buy = campaign[:quantity_to_buy]
number_of_offers = (eligible_item_count / quantity_to_buy).floor

next unless number_of_offers > 0

number_of_discountable_items = number_of_offers * quantity_to_buy
total_offer_price = Money.new(cents: 100) * (number_of_offers * campaign[:final_price])
discount_applicator = DollarDiscountApplicator.new(campaign[:discount_message])

self.loop_items(cart, eligible_items, number_of_discountable_items, total_offer_price, discount_applicator)
end
end

def loop_items(cart, line_items, num_to_discount, total_price, discount_applicator)
current_price = Money.zero
avg_price = total_price * (1 / num_to_discount)

line_items = line_items.sort_by { |line_item| line_item.variant.price }

line_items.each do |line_item|
break if num_to_discount <= 0

if line_item.quantity > num_to_discount
split_line_item = line_item.split(take: num_to_discount)
discount_amount = split_line_item.line_price - (total_price - current_price)
discount_applicator.apply(split_line_item, discount_amount)
position = cart.line_items.find_index(line_item)
cart.line_items.insert(position + 1, split_line_item)
break
elsif line_item.quantity == num_to_discount
discount_amount = line_item.line_price - (total_price - current_price)
discount_applicator.apply(line_item, discount_amount)
break
else
if line_item.variant.price <= avg_price
current_price += line_item.line_price
else
discount_amount = (line_item.variant.price - avg_price) * line_item.quantity
current_price += (line_item.line_price - discount_amount)
discount_applicator.apply(line_item, discount_amount)
end

num_to_discount -= line_item.quantity
end
end
end
end

CAMPAIGNS = [
BuyXofYforZwithDiscountCodeCampaign.new(BUY_X_GET_Y_FOR_Z_WITH_DISCOUNT_CODE),
]

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

Output.cart = Input.cart