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" .
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.
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,
};
}