Shopify Discount Function Does not Stack with Auto BOGO discount properly

Shopify Discount Function Does not Stack with Auto BOGO discount properly

fwallace
Shopify Partner
40 0 4

Hi all --

 

Our set up is somewhat complex, but I think other people will have the same set up and issues.

 

We have "membership" pricing for our Subscription oriented store, any non-logged in user can purchase a recurring subscription of a product at a lower price than a one time purchase. They can also purchase it as a one time at a MUCH higher price. When logged in with the tag "Active Subscriber" our subscribers (we use Recharge and Recharge adds the tag "Active Subscriber" to those with recurring subscriptions) can purchase from that same product as a one time, with a substantial reduction in price (the benefit for subscribing).

 

This is a key factor in our subscription retention strategy -- subscribers can purchase one-time products at much lower prices than non-subscribers.

 

We don't have duplicate products -- it became too much of a problem for our limited staff to manage every month (we are monthly based). Instead the discount for subscribers purchasing one times is done through Shopify scripts. Shopify Scripts will go away soon, and the  script we have is buggy and runs out of memory when we have big promotions and lots of purchases.

 

However the Shopify Script will stack with BOGO discounts, for example, if I have the Membership Discount product and one other product that SHOULD give me a free other product. However ONLY when I add another of the other product will the cart and checkout update to the free product with the Shopify Script disabled and the Shopify function active:

 

cart_example.png

the Discount is set up like this:

 

discount_example.png

 

Note, again this works with the Shopify Script, but not with the Shopify Function which duplicates the functionality of the script.

 

The script was installed to stack:

 

discount_configuration.png

 

So what am I doing wrong? Do I need to push in the app another function that does the BOGO? Is there a trick to setting up a Discount manually in Shopify as BOGO to make it work with a Shopify Function discount?

 

Here is mutation used to install the discount on the (test/staging) store:

 

mutation {
  discountAutomaticAppCreate(automaticAppDiscount: {
    title: "Member Pricing",
    functionId: "the_id_of_the_function",
    startsAt: "2024-03-10T00:00:00",
    combinesWith: {
                       orderDiscounts: true,
                       productDiscounts: true,
                       shippingDiscounts: true
                     },
  }) {
     automaticAppDiscount {
      discountId
     }
     userErrors {
      field
      message
     }
  }
}

Note the function's discountApplicationStrategy is set to All :

 

discountApplicationStrategy: DiscountApplicationStrategy.All

 

No doubt someone has run into this before, any help greatly appreciated.

Replies 2 (2)

SomeUsernameHe
Shopify Partner
495 55 101

Hello again! 

Um, this one might be a little harder, so here is an function a wrote a little bit ago that does work with other discounts. 

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

/**
 * @typedef {import("../generated/api").RunInput} RunInput
 * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
 * @typedef {import("../generated/api").ProductVariant} ProductVariant
 */

const EMPTY_DISCOUNT = {
  discountApplicationStrategy: DiscountApplicationStrategy.All,
  discounts: [],
};

/**
 * @Anonymous {RunInput} input
 * @returns {FunctionRunResult}
 */
export function run(input) {
  const configuration = JSON.parse(input?.shop?.metafield?.value ?? "{}");
  const discounts = [];

  // Check for customer tags
  const customerTags = input.cart.buyerIdentity.customer.hasTags;
  let discountTag = null;

  if (customerTags.some(tagInfo => tagInfo.tag === "vip" && tagInfo.hasTag)) {
    discountTag = "VIP";
  } else if (customerTags.some(tagInfo => tagInfo.tag === "Pro" && tagInfo.hasTag)) {
    discountTag = "Pro";
  }

  if (!discountTag) {
    return EMPTY_DISCOUNT;
  }

  input.cart.lines.forEach(line => {
    if (line.merchandise.__typename === "ProductVariant") {
      const variant = /** @type {ProductVariant} */ (line.merchandise);
      const product = variant.product;

      product.inCollections.forEach(collection => {
        if (collection.isMember) {
          const collectionDiscount = configuration[discountTag].collections[collection.collectionId];
          if (collectionDiscount) {
            const discountPercentage = parseInt(collectionDiscount.replace('%', ''), 10);
            discounts.push({
              message: `${collectionDiscount} off`,
              targets: [{ productVariant: { id: variant.id } }],
              value: {
                percentage: { value: discountPercentage }
              }
            });
          }
        }
      });
    }
  });

  return discounts.length > 0 ? { discountApplicationStrategy: DiscountApplicationStrategy.All, discounts } : EMPTY_DISCOUNT;
};


Without seeing your discount function it would be really hard to troubleshoot, so maybe you can use my code to fix your issue? 🙂 

Have I helped? Consider putting coffee in my mouth!
Buy Me a Coffee
fwallace
Shopify Partner
40 0 4

Hi My discount works, but excludes the Membership Discount function.

 

This is my run.js function:

 

// @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: [],
};

// The configured entrypoint for the 'purchase.product-discount.run' extension target
/**
* @Anonymous {RunInput} input
* @returns {FunctionRunResult}
*/
export function run(input) {
  const customer1 = input.cart.buyerIdentity?.customer?.active_sub;
  console.error("customer1 value = " + customer1);
  const targets = input.cart.lines
  // Only include cart lines with a quantity of two or more
  // and a targetable product variant
  .filter(line => line.quantity >= 1 &&
    line.merchandise.__typename == "ProductVariant" && customer1 == true && line.merchandise.product.member_price == true && line.sellingPlanAllocation?.sellingPlan.name != "Delivery every 1 Month" && line.sellingPlanAllocation?.sellingPlan.name != "Delivery every 1 Month, Charge every 3 Months")
  .map(line => {
    const variant = /** @type {ProductVariant} */ (line.merchandise);
    return /** @type {Target} */ ({
      // Use the variant ID to create a discount target
      productVariant: {
        id: variant.id
      }
    });
  });

  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;
  }

  // The @Shopify/shopify_function package applies JSON.stringify() to your function result
  // and writes it to STDOUT
  return {
    discounts: [
      {
        // Apply the discount to the collected targets
        targets,
        // Define a percentage-based discount
        value: {
          percentage: {
            value: "44.47"
          }
        }
      }
    ],
    discountApplicationStrategy: DiscountApplicationStrategy.All
  };
};

 

Do I need to include the BOGO function in the run.js as well? If so, how would I go about doing that? Is there some template or code base with an example of multi-discount functions? I know an app can have up to 100 functions, do I need to create another function and push that to the App?

 

I am new to Shopify functions, this is my first attempt.

 

Thanks.