I have successfully created a 2 for $25 discount, to mirror the sort of discounts available in the soon to be going away Shopify Functions.
The Gist can be found here:
This allows a discount for products with the tags “2FOR25” that have different prices, all discounted to 12.25 each in multiples of two.
Features: removes a discount from the highest price when the total quantity of eligble products in cart is odd. Allows different products to be purchased with full discount as long as total is even. Call to action message for customer on line item to add one to get full discount. Unique discount per cartline per product.
Here is the Run.graphql:
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"])
}
}
}
}
}
}
Here is the run.js:
// @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.sort((a, b) => {
// console.error("a is " + JSON.stringify(a));
// console.error("b is " + JSON.stringify(b));
// const titleA = parseFloat(a.lineitemPrice).toFixed(2);
// const titleB = parseFloat(b.lineitemPrice).toFixed(2);
// if (titleA > titleB) {
// return -1;
// }
// if (titleA < titleB) {
// return 1;
// }
// // must be equal
// return 0;
// });
}
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 {
//customDiscount,
discounts: customDiscount.discounts,
// discounts: [
// {
// // Apply the discount to the collected targets
// targets,
// // // Define a percentage-based discount
// // value: {
// // fixedAmount: {
// // amount: "10.0",
// // },
// // },
// },
// ],
// message: customDiscount.message,
discountApplicationStrategy: DiscountApplicationStrategy.All,
};
}```