skip to Main Content

The script is a template that comes with the script editor app in Shopify. I need to make it work so that if you buy one product from a collection, you get another one free from that collection. This script works only for buying the same product. Here is the script:

PAID_ITEM_COUNT = 2
DISCOUNTED_ITEM_COUNT = 1

# Returns the integer amount of items that must be discounted next
# given the amount of items seen
#
def discounted_items_to_find(total_items_seen, discounted_items_seen)
  Integer(total_items_seen / (PAID_ITEM_COUNT + DISCOUNTED_ITEM_COUNT) * DISCOUNTED_ITEM_COUNT) - discounted_items_seen
end

# Partitions the items and returns the items that are to be discounted.
#
# Arguments
# ---------
#
# * cart
#   The cart to which split items will be added (typically Input.cart).
#
# * line_items
#   The selected items that are applicable for the campaign.
#
def partition(cart, line_items)
  # Sort the items by price from high to low
  sorted_items = line_items.sort_by{|line_item| line_item.variant.price}.reverse
  # Create an array of items to return
  discounted_items = []
  # Keep counters of items seen and discounted, to avoid having to recalculate on each iteration
  total_items_seen = 0
  discounted_items_seen = 0

  # Loop over all the items and find those to be discounted
  sorted_items.each do |line_item|
    total_items_seen += line_item.quantity
    # After incrementing total_items_seen, see if any items must be discounted
    count = discounted_items_to_find(total_items_seen, discounted_items_seen)
    # If there are none, skip to the next item
    next if count <= 0

    if count >= line_item.quantity
      # If the full item quantity must be discounted, add it to the items to return
      # and increment the count of discounted items
      discounted_items.push(line_item)
      discounted_items_seen += line_item.quantity
    else
      # If only part of the item must be discounted, split the item
      discounted_item = line_item.split(take: count)
      # Insert the newly-created item in the cart, right after the original item
      position = cart.line_items.find_index(line_item)
      cart.line_items.insert(position + 1, discounted_item)
      # Add it to the list of items to return
      discounted_items.push(discounted_item)
      discounted_items_seen += discounted_item.quantity
    end
  end

  # Return the items to be discounted
  discounted_items
end

eligible_items = Input.cart.line_items.select do |line_item|
  product = line_item.variant.product
  !product.gift_card? && product.id == 11380899340
end

discounted_line_items = partition(Input.cart, eligible_items)
discounted_line_items.each do |line_item|
  line_item.change_line_price(Money.zero, message: "Buy one Bolur, get one Bolur free")
end

Output.cart = Input.cart

I tried changing, what seems to be the relevant code:

eligible_items = Input.cart.line_items.select do |line_item|
  product = line_item.variant.product
  !product.gift_card? && product.id == 11380899340
end

to this:

eligible_items = Input.cart.line_items.select do |line_item|     
  product = line_item.variant.product      
  !product.gift_card? && **collection.id** == 123  
end

but I get an error:

undefined method ‘collection’ for main (Your Cart)

undefined method’collection’ for main (No Customer)

2

Answers


  1. Two things here:

    1. line_item.variant.product does not have the property collections. For that, you want to use line_item.product (docs) – which (should…see point two) expose all of the methods and properties of the product object.

    2. However, in my attempt to do something similar to you (discount based on product) I tried iterating over line_item.variant – and am always hitting the error of undefined method 'product' for #<LineItem:0x7f9c97f9fff0>. Which I interpret as “line_items accessed in cart scripts can only be at the variant level”.

    So, I wonder if this is because the cart only contains variants (product/color/size) – so we aren’t actually able to access the line_items by product, and only by variant.

    I tired iterating over line_item.product_id, which also throws a similar error. I think we just have to try to do some hacky thing at the variant level.

    I am going to see if I can access the product by the variant ID…back to the docs!

    Login or Signup to reply.
  2. You actually can’t do a collection, so you’d need to modify the script to work with a product type or tags. That script will need to be heavily modified to work for a number of products and not multiples of the same

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search