Our Partner & Developer boards on the community are moving to a brand new home: the .dev community forums! While you can still access past discussions here, for all your future app and storefront building questions, head over to the new forums.

CartTransform Merge Operations seem unstable with quantities greater than 1 in-cart

CartTransform Merge Operations seem unstable with quantities greater than 1 in-cart

nelsonmaze
Shopify Partner
5 0 0

Describe the bug

Line Item issues begin to occur with any multiple of a CartTransform Merge "bundle" being added to the cart (either by incrementing via form submission on the cart page or via cart AJAX API). Adding any other line items and bundles only exacerbates the issue.

 

Partner app dashboard logs indicate that function invocations are occurring where the cart line item inputs are being modified outside of the function's execution, along with potentially extra function invocations being made along the way (accelerating the duplication issues).

 

Steps to reproduce

 

Assumptions

  • Latest Dawn theme installed (little to no modifications to it that would impact this)
  • You have deployed a Function that uses MergeOperation to implement bundles in-cart
  • You have a Function configuration active that can create a bundle in-cart with multiple variants
    • For the purposes of this example, assume the bundle can be created from 3 products, with any of their 4 variants each, being valid components of the bundle.
    • You have access to your Function's log outputs from the Shopify Partner Dashboard


Steps

 

  1. Add 1 of each of the component products to the cart to manually create the bundle in-cart.
    • (2/3 bundle products added to cart)
      324933434-249ce8ed-6eb3-4bdd-91ac-300895a9e70a.png

       

    • (3/3 bundle products added to cart)
      324933722-fbbbfd6a-a8f3-448f-99e1-07cb3cf8bebb.png

       



    • Note that this first invocation/interaction properly results in a single bundle being rendered in cart. This is also evident in the partner logs. (3 products input results in 1 Merge Operation with the 3 required component line items in the correct quantities).

      FirstBundleInput.json
      FirstBundleOutput.json


  2. From the cart page, increment the bundle to 2 via the "+" button. The page will update and result in:

    • Another line item being added with a price of $0.00 and quantity of 2 

    • The first bundle's line item being updated to a quantity of 2

      324939246-0571c18f-9d75-4604-8bdc-b7ab4c3de05e.png

       

    • Note that this is not the theme rendering things incorrectly - the returned response from the Cart AJAX API clearly returns an "empty" bundle line item at $0.00: CartResponse_2_Bundles.json

    • This is also not the result of incorrect Function output, as the logs correctly display 2 merge operations, each for a group of 3 line items:
      SecondBundleInput.json
      SecondBundleOutput.json

    • Note that the "empty" line item is not found anywhere in the Function input/output logs.


  3. Proceed to checkout via the checkout button on the cart page with the current cart of 2 bundles and 2 "empty" bundles.

    • Note that this results in 2 function invocations, each with completely different inputs that seem to indicate line items were further cloned/duplicated between each invocation:

      First Checkout Visit, First Input: FirstCheckoutVisitInput.json
      First Checkout Visit, First Output: FirstCheckoutVisitOutput.json

      First Checkout Visit, Second Input: FirstCheckoutVisit_SecondInput.json
      First Checkout Visit, Second Output: FirstCheckoutVisit_SecondOutput.json
    • These function invocations with modified inputs result in the cart now being completely different from even the original "2" line items (Cart AJAX API line items JSON found here

      324948697-6f38028e-9084-4c0c-a664-eadcdb884b60.png

    • The expected outputs of the function are still technically correct - but the input varying is what is resulting in this behavior between invocations, not the function execution itself.

    • The only user interaction here between function invocations was the visit to checkout.


  4. Navigate back to the cart page, and attempt to "correct" the cart by deleting a line item (any of them)

    • This only results in a continuation of the duplication issues, which after 2 attempts finally results in such a large quantity of line items that the runtime instruction count limit is exceeded, finally resulting in all bundles being broken up, as shown in the cart.

      324953785-9bdc997b-187f-46da-8752-693ad1f696eb.png
    •  The Function input/output logs of this are below:
      DeleteAttemptInput: DeleteAttemptInput.json
      DeleteAttemptOutput: DeleteAttemptOutput.json

Expected behavior

  • Adding/deleting multiple mergeOperation bundles to the cart should behave the same as adding/deleting multiple "normal" variants to the cart (via form submission or cart AJAX API).

  • Incrementing and decrementing the quantity of mergeOperation bundles should behave the same as with "normal" variants and should increment/decrement the underlying component variants in the cart properly.

  • Line items should not duplicate without user interaction and the pricing should be consistent with multiple bundles added to the cart.

  • Merge Operations should not result in bug/artifact line items being added to the cart response JSON.

 

Environment

  • Latest Product Discount Function API for Rust (04/2024) - tested newest RC and unstable. Same situation.
  • Latest Version Dawn theme (for frontend testing purposes)
  • Latest Shopify CLI
  • Shopify Plus Sandbox Store

 

Additional context


This is occurring in multiple Shopify Plus environments.

 

The issue is not the code - this is a simple step-through similar to the function example for merge operations. Duplication is also not possible with the existing API, as documented at time of writing.

 

I have passed various combinations of parameters required by Merge Operation, including the image, parent variant, title, etc. No changes here impact the behavior described above.

 

The metafield configuration being passed is not the issue, it is simply compressed in the above examples. The Function above panics with an invalid configuration.

 

 

 

Replies 12 (12)

siddhantbajaj
Shopify Staff
6 1 1

Hey @nelsonmaze 

as the logs correctly display 2 merge operations, each for a group of 3 line items

I would actually expect the function to output only 1 merge operation that specify the bundle composition. You don't need additional merge operation every time the component quantity is increased. We will perform the calculation on end to determine the quantity of parent by looking at the bundle composition from merge operation and the quantity of component present in the cart.

For example in your case, just outputting a single merge operation like below should be enough:

{parent_variant_id: ID, cart_lines: [{id: ID, quantity: 1},{id: ID, quantity: 1}] }

To learn more visit the Shopify Help Center or the Community Blog.

nelsonmaze
Shopify Partner
5 0 0

Hi @siddhantbajaj ,

 

This example simplifies the issue a bit, but the reason I expect it to output multiple MergeOperations is for the use case of two bundles of distinct makeups being handled by the same Function.

 

If I have 3 products I'd like to have qualify for this bundle, each with size variations S/M, I would expect the same Function invocation to be able to handle 2 distinct combinations of these product variants. 

 

I've added a check to the code to prevent the same operation from being added in this sense, but the same duplication issues still occur.

 

Implementing something that outputs a single Merge for two distinct makeups results in something like the below output, which does not result in the two distinct bundles, but a single "combined" one:

 

{
  "operations": [
    {
      "merge": {
        "attributes": [
          {
            "key": "_bundle_id",
            "value": "string_id_3"
          }
        ],
        "cartLines": [
          {
            "cartLineId": "gid://shopify/CartLine/ee4bfc32-07e7-42ba-9fc4-83fa4ea15aec",
            "quantity": 1
          },
          {
            "cartLineId": "gid://shopify/CartLine/5a4ea6cd-f483-4105-8519-3ab4e0f9ad48",
            "quantity": 2
          },
          {
            "cartLineId": "gid://shopify/CartLine/6df9c4f9-4bdc-45d8-a48b-0c2423875e3d",
            "quantity": 2
          },
          {
            "cartLineId": "gid://shopify/CartLine/79c88b3f-6ebf-4268-990d-e2870645a64b",
            "quantity": 1
          }
        ],
        "parentVariantId": "gid://shopify/ProductVariant/45722712867048",
        "title": "Total Robot Bundle Product"
      }
    }
  ]
}

 

nelsonmaze
Shopify Partner
5 0 0

Seeing now that this approach seems in conflict with the docs:

 

nelsonmaze_0-1714527338078.png

 

 

However, even when assigning line item properties to distinguish "duplicate" bundle components, the same duplication issues still occur with quantities greater than 1 for any merged bundle.

 

Is there any reason merge operations must be limited by the cart line target instead of remaining quantities on the cart line targets?

 

Also - is there any limitation on the amount of CartTransforms that can be active on a store at any given time?

 

 

siddhantbajaj
Shopify Staff
6 1 1

You can handle that case in multiple iterations. In the first function invocation, you can output a single merge operations targeting a limited quantity of a cart line. This will split the line into 2 parts, one part becomes a part of the bundle. In the next function execution, your input will now have two lines for the same variant and now you can out 2 merge operations for each line respectively.

Yes we do have a limitation of maximum 1 cart transform per shop for a single app. 

To learn more visit the Shopify Help Center or the Community Blog.

nelsonmaze
Shopify Partner
5 0 0

Even with added checks to prevent duplicate bundle merge operations, there are still pricing issues in-cart, with the cart object requiring checkout visits to finally show the correct pricing. In this example, the two bundles in-cart have their quantities incrementing/decrementing fine, but the pricing for the second bundle gets zeroed out upon any cart modification.

nelsonmaze_0-1714683796643.png

 

Even modifying the unrelated non-bundle product quantity results in the second unique bundle's pricing to go to $0.00.

 

Meanwhile, visiting checkout results in the correct pricing being calculated:

nelsonmaze_1-1714683858616.png

 

Navigating back to cart from checkout is the only way to get the "correct" pricing to display properly.

 

sevenminds
Shopify Partner
13 0 3

I can confirm incorrect prices on bundles (when there are a few of them in the cart) and that navigating to checkout fixes them. We have customers complaining about this issue.

siddhantbajaj
Shopify Staff
6 1 1

I have a hunch that your function is outputting two merge operations that target the same cart line, which is causing these price inconsistencies. Can you confirm if that is the case?

To learn more visit the Shopify Help Center or the Community Blog.

sevenminds
Shopify Partner
13 0 3

In our case, we have four cart lines. The first two are merged into one bundle, and the remaining two are merged into another. The second bundle has an incorrect price until we navigate to the checkout page.

nelsonmaze
Shopify Partner
5 0 0

This is indeed the case for these scenarios - but I don't see distinguishing cart lines with unique properties as a feasible solution to allowing customers to build bundles manually.

 

In the situation where a user manually constructs an example bundle of:

- T-shirt (S) x 1

- Hat (M) x 1

 

and triggers a bundle, and then follows up with another addition of:

 

- T-shirt (L) x 1

- Hat (M) x 1

 

In order to prevent the two hats from causing a cart line overlap issue, a unique property could be assigned, but that would result in every hat the customer adds to the cart being in their own cart line, which is undesirable here.

 

Forcing the checkout form to trigger function invocations to be able to fetch the corrected pricing also does not sound like a great solution to this, but might be able to serve as a workaround.

 

The ideal state here is that the cart additions above result in two separate bundle line items, each with their own T-Shirt/Hat bundle components, despite Hat being pulled from a single cart line.

 

SteveKaran
Shopify Partner
21 0 6

I've been experiencing similar issues when attempting to add more than quantity of 1 of a bundle to the cart. I opened ticket 49060285 with Partner Support months ago, and have yet to receive any updates 😠

 

I am experiencing similar duplication issues, and results are often inconsistent, but never correct. I even go as far as bypassing the theme by using the Cart AJAX API (add/change/update) to update quantities, but things go sideways very quickly. Here's a few screenshots to help explain my issue...

 

Using latest version of Dawn 15.0.2, I create a custom "bundle" product template for customers to build their bundle. This template shows a dropdown for each bundle component with a list of variants to select from.

 

SteveKaran_0-1723750543535.png

I also customized the Add To Cart button so that both selected variants are added to the cart using a single Cart AJAX API call. When I navigate to the cart, I see the following

 

SteveKaran_1-1723750694974.png

 

Here is the corresponding function input and output, showing the variants were correctly merged into a bundle

Cart Transform Input & Output 

Cart JSON - retrieved with:

fetch(routes.cart_url + ".js", { method: "GET", headers: { accept: "application/json" } })

 

Now, this is where things go sideways. If I click the "plus" button to increase the quantity from 1 to 2, I get the following:

 

SteveKaran_2-1723752221615.png

My function currently has two merge operations, but look at the price difference! The first bundle is half the price of the second! Yet the percentage decrease from the Cart Transform Output is the same. How can this be?!?!? Also, looking at the Cart JSON, the line item keys are the same! Shouldn't they be different?

Cart Transform Input/Output - after quantity increase

Cart JSON - after quantity increase 

 

For fun (and as part of my debugging), I even output the Liquid object {{ cart.items.item_components }} found here:

Liquid Object cart.items.item_components 

 

Now, I go to checkout... Where did all these extra items come from?!?!?!

 

SteveKaran_3-1723753416827.png

SteveKaran_4-1723753440897.png

Here's the corresponding Cart Transform Input/Output (there seems to be two function invocations upon checkout)

Cart Transform Input/Output upon Checkout #1

Cart Transform Input/Output upon Checkout #2 

 

What on earth is going on here? I even followed the example from the function-examples repo on Github, all to no avail. There are some minor differences with the implementation of my function:

  1. I defined a "component_reference" metafield on the bundle product, not the bundle product variant. Should that really matter?
  2. My function is written in JS vs Rust. Are there are any known issues with JS functions?

@siddhantbajajAs I have opened a ticket with Partner Support months ago, all with no updates, I'd really appreciate your support here. My customer's primary selling strategy is to sell bundles, so without a properly working Cart Transform function, their business is severely impacted!

SteveKaran
Shopify Partner
21 0 6

After much troubleshooting and trial and error, I finally resolved the issue. What lead me down the rabbit whole was my interpretation of the "quantity" field used by the cart transform. For a merge operation, the "quantity" field is defined as:

 

Cart Transform - Merge - CartLine Fields 

quantity (Int!) Required
The quantity of the cart line to be merged.The max quantity is 2000.

 

I interpreted this to mean "bundle the quantity selected in the cart". So if someone selects quantity 2+ of a bundle in the cart, cart transform will add quantity 2+ of the item, and create 2+ bundles accordingly. However, doing so leads to all kinds of unpredictable results, leading to item duplication (as per previous screenshots) and cart miscalculations.

 

After much troubleshooting, I realized the definition of the "quantity" field should actually mean "how many of this cart line item should be included in ONE bundle?". With that, I set my cart transform merge function quantity field to 1 (instead of the quantity received from the input query). So now when selecting quantity 2+ of an item in the cart, the bundle quantity is also 2+, the quantity of the bundled items are a correct multiple of the bundle quantity, and cart line price and total price are calculated correctly!

 

This kind of overly succinct description of the "quantity" field is completely inaccurate and misleading, which is what lead to my misinterpretation. I have submitted feedback on the Cart Transform documentation, and also requested the same in the ticket I had opened 49060285.

 

I also discovered that the cart transform output and the cart DO NOT HAVE TO MATCH. From @siddhantbajaj 's comment about how Shopify does additional operations post cart transform and upon checkout (specifically calculating the correcting bundle quantities, cart line price, and total cart price), it lead to me to testing my cart transform with "quantity" set to 1, which is ultimately what resolved my issue.

 

A couple examples to help clarify further:

  1. A bundle will include 1 shirt and 1 pair of pants: set the quantity field for both the shirt and pants to 1
  2. A bundle will include 2 shirts and 1 pair of pants: set the quantity field for the shirt to 2, and the pants to 1
  3. A bundle will include 2 shirts and 2 pair of pants: set the quantity field for both the shirt and pants to 2

Hope this helps!

Moutasim1
Shopify Partner
4 0 1

This is important information that you have shared 
I have been doing the same mistake, trying to add current input quantity in the lineitem quantity for merge operation and the function was showing very unexpected results 
Thanks @SteveKaran it really helped a lot