Skip to content

Instantly share code, notes, and snippets.

@FLWallace105
Last active November 13, 2024 04:38
Show Gist options
  • Save FLWallace105/2e1f346c7d37f303f24c097262da3d6d to your computer and use it in GitHub Desktop.
Save FLWallace105/2e1f346c7d37f303f24c097262da3d6d to your computer and use it in GitHub Desktop.
Shopify function 2 for 25 How To

Create a Shopify Function to do 2 for $25 Items

Requirements: You want a Shopify Function to do a 2 for $25 discount like what is availble in Shopify Scripts (soon to be removed).

Your products can have different prices, some might be $29.99, some might be $25.99, others might be $19.99. You don't want to change the original prices. You want every combination of eligible produccts to be discounted to $12.50, i.e. "2 for $25" and if you have an odd total: say a cart has 2 of one eligble product and 3 of other eligible products, the most expensive one is not fully discounted (don't discount one of them). You also want a call to action to the customer to add another item in the not fully discounted line item to get the full discount.

Your eligible products are identified with a tag "2FOR25" .

Run.graphql

Your run.graphql should look like this:

query RunInput {
  cart {
    lines {
      id
      quantity
      cost {
        amountPerQuantity {
          amount
          currencyCode
        }
        subtotalAmount {
          amount
          currencyCode
        }
      }
      
      merchandise {
        __typename
        ...on ProductVariant {
            id
            product {
              two_for_tag: hasAnyTag(tags: ["2FOR25"])
            }
        }
      }
    }
  }
}

You want the quantity, and the cost per unit so you can sort the end array and adjust the discount amount if required.

Run.js

Here is what your run.rs file should look like:

// @ts-check
import { DiscountApplicationStrategy } from "../generated/api";

// Use JSDoc annotations for type safety
/**
 * @typedef {import("../generated/api").RunInput} RunInput
 * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
 * @typedef {import("../generated/api").Target} Target
 * @typedef {import("../generated/api").ProductVariant} ProductVariant
 */

/**
 * @type {FunctionRunResult}
 */
const EMPTY_DISCOUNT = {
  discountApplicationStrategy: DiscountApplicationStrategy.All,
  discounts: [],
};

const DISCOUNT_MESSAGE = "2 for $25 Discount limited time only!";

var customDiscount = {
  discountApplicationStrategy: DiscountApplicationStrategy.All,
  discounts: [],
  //message: DISCOUNT_MESSAGE,

};

var bigArray = [];

/**
 * @param {RunInput} input
 * @returns {FunctionRunResult}
 */
export function run(input) {
  //console.error("function running");

  const targets = input.cart.lines
    // Only include cart lines with a quantity of two or more
    .filter((line) =>  line.merchandise.__typename == "ProductVariant" && line.merchandise.product.two_for_tag == true)
    .map((line) => {
      return /** @type {Target} */ ({
        // Use the cart line ID to create a discount target
        cartLine: {
          id: line.id,
        },
        lineitemPrice: line.cost.amountPerQuantity.amount,
        lineitemQty: line.quantity,
        // value: {
        //   fixedAmount: {
        //     amount: "10.0",
        //   },
        // },
      });
    });
  if (!targets.length) {
    // You can use STDERR for debug logs in your function
    //console.error("No cart lines qualify for volume discount.");
    return EMPTY_DISCOUNT;
  }

  // is it even

  var totalIsEven = false;

  var myTotalEligible = targets.reduce((sum, cartli) => sum + cartli.lineitemQty, 0);

  totalIsEven = isEven(myTotalEligible);

 
  function isEven(n) {
    return n % 2 == 0;
  }

  //console.error("total is " + myTotalEligible);
  //console.error("totalIsEven = " + totalIsEven);

  // Sort targets here.
  // only if totalIsEven == false

  if ( totalIsEven == false ) {
    // Sort array

    function compare( a, b ) {
      if ( parseFloat(a.lineitemPrice).toFixed(2) > parseFloat(b.lineitemPrice) ){
        return -1;
      }
      if ( parseFloat(a.lineitemPrice).toFixed(2) < parseFloat(b.lineitemPrice).toFixed(2) ){
        return 1;
      }
      return 0;
    }

    targets.sort(compare);

  


  }


  targets.forEach(tempFunc);
  
  function tempFunc(value, index) {

    //console.error("value is " + JSON.stringify(value));
    //console.error("index is " + index);

    

    if (totalIsEven == true ) {

    var obj = new Object();
    //console.error("starting even branch ...");

    obj.targets = [{ cartLine: value.cartLine}] ;
    //parseFloat(yourString).toFixed(2)
    var temp_discount = ( (parseFloat(value.lineitemPrice).toFixed(2) - 12.50) * value.lineitemQty ).toFixed(2);
    //num.toLocaleString('en-US', {minimumFractionDigits: 8, useGrouping: false})
    var temp_discount_str = temp_discount.toLocaleString('en-US', {minimumFractionDigits: 2, useGrouping: false});
    obj.message = DISCOUNT_MESSAGE;
    obj.value = { "fixedAmount": {amount: temp_discount_str}};

    

    //obj.targets[value]
    // var temp_json = JSON.stringify(obj);
    // console.error("temp_json = " + JSON.parse(temp_json));
    customDiscount.discounts.push(obj);
    } else {

      //console.error("not even branch ...");

      
      
      if (index == 0 && value.lineitemQty > 1 ){

        //console.error("first index branch ... ");

        var obj = new Object();
        obj.targets = [{ cartLine: value.cartLine}] ;

        var temp_discount = ( (parseFloat(value.lineitemPrice).toFixed(2) - 12.50) * ( value.lineitemQty - 1) ).toFixed(2);
        //num.toLocaleString('en-US', {minimumFractionDigits: 8, useGrouping: false})
        var temp_discount_str = temp_discount.toLocaleString('en-US', {minimumFractionDigits: 2, useGrouping: false});
        obj.message = "Add one more to get full discount!";
        obj.value = { "fixedAmount": {amount: temp_discount_str}};
        
        customDiscount.discounts.push(obj);

        


      } else if (index == 0 && value.lineitemQty == 1 ) {
        // Do nothing, do not push to the array.
        //console.error("the quantity for this is one, quantity = " + value.lineitemQty);

        var obj = new Object();
        obj.targets = [{ cartLine: value.cartLine}] ;

        // var temp_discount = ( (parseFloat(value.lineitemPrice).toFixed(2) - 12.50) * (value.lineitemQty - 1 ) ).toFixed(2);
        //num.toLocaleString('en-US', {minimumFractionDigits: 8, useGrouping: false})
        // var temp_discount_str = temp_discount.toLocaleString('en-US', {minimumFractionDigits: 2, useGrouping: false});
        // Note we are not needing to calculate the discount here as it will always be zero and we are just calling to action
        // customer to andd one more to get full discount

        obj.message = "Add one more to get full discount!"
        obj.value = { "fixedAmount": {amount: "0.00"}};
        
        customDiscount.discounts.push(obj);
        

      } else {

        //console.error("not first index branch ... ");

        var obj = new Object();
        obj.targets = [{ cartLine: value.cartLine}] ;

        var temp_discount = ( (parseFloat(value.lineitemPrice).toFixed(2) - 12.50) * value.lineitemQty ).toFixed(2);
        //num.toLocaleString('en-US', {minimumFractionDigits: 8, useGrouping: false})
        var temp_discount_str = temp_discount.toLocaleString('en-US', {minimumFractionDigits: 2, useGrouping: false});
        
        obj.message = DISCOUNT_MESSAGE;
        obj.value = { "fixedAmount": {amount: temp_discount_str}};
        customDiscount.discounts.push(obj);


      }


    }

  };

  


  return {
    
    discounts: customDiscount.discounts,

    discountApplicationStrategy: DiscountApplicationStrategy.All,
  };
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment