Product Bundle cart discount not applied when bundle contains products with variations

Summary

When a product bundle contains products that have variations (configurable products with child variation products), the bundle’s cart discount is never applied. The promotion validation silently fails because the onHowManyBundlesCanBeInCartBeforeGettingProductAmounts hook incorrectly inflates the required product amount by summing all child variation link entries.

Bundles with simple products (no variations) work correctly.


Root Cause

File: app/addons/product_bundles/src/HookHandlers/ProductBundlesHookHandler.php
Method: onHowManyBundlesCanBeInCartBeforeGettingProductAmounts (line ~303)

When a bundle is saved with any_variation = Y, the onUpdateLinks hook creates entries in cscart_product_bundle_product_links for the parent product AND each child variation — all with amount = 1.

For example, a bundle with 2 products:

  • Product A (parent, 23 child variations) → 24 link entries (1 parent + 23 children), each amount = 1
  • Product B (parent, 6 child variations) → 7 link entries (1 parent + 6 children), each amount = 1

The howManyBundlesCanBeInCart method loads these link entries into $bundle_products. Then the hook consolidates child entries to the parent:

// Line ~310 — THE BUG
$bundle_products[$parent_id]['amount'] += $bundle_products[$product_id]['amount'];
unset($bundle_products[$product_id]);

This sums ALL child amounts to the parent:

  • Product A: 1 (parent) + 23 × 1 (children) = 24
  • Product B: 1 (parent) + 6 × 1 (children) = 7

Later, the validation checks:

$is_bundle_complete = $cart_product_amounts[$product_id] >= $bundle_product['amount'];

The cart has 1 of each product, but the required amount is 24 and 7. The check fails (1 >= 24 → false), and the bundle is never considered complete. The promotion is not applied.


Steps to Reproduce

  1. Create two configurable products with variations:

    • Product A: a product with at least 2 child variations (e.g., different colors)
    • Product B: a product with at least 2 child variations
  2. Create a product bundle:

    • Add Product A with any_variation = Y, amount = 1, any discount type
    • Add Product B with any_variation = Y, amount = 1, any discount type
    • Save the bundle
  3. Verify the links table:

    SELECT product_id, amount, all_variants
    FROM cscart_product_bundle_product_links
    WHERE bundle_id = <your_bundle_id>;
    

    You’ll see entries for the parent AND each child variation, all with amount = 1.

  4. Add products to cart on the storefront:

    • Add any variation of Product A (quantity 1)
    • Add any variation of Product B (quantity 1)
  5. Go to checkout / cart page

Expected: Bundle discount is applied to the order total.
Actual: No discount applied. The bundle promotion is silently skipped.

  1. For comparison, create a bundle with simple products (no variations, no any_variation). The same discount type and values. Add those products to cart.

Result: Discount IS applied correctly for simple products.


Fix

In app/addons/product_bundles/src/HookHandlers/ProductBundlesHookHandler.php, method onHowManyBundlesCanBeInCartBeforeGettingProductAmounts, the child link consolidation should NOT sum amounts to the parent. The child entries should simply be removed:

Before (broken):

foreach (array_keys($bundle_products) as $product_id) {
    if (
        $product_id_map->isChildProduct($product_id)
        && isset($bundle_products[$product_id_map->getParentProductId($product_id)])
        && YesNo::toBool($bundle_products[$product_id_map->getParentProductId($product_id)]['all_variants'])
    ) {
        $bundle_products[$product_id_map->getParentProductId($product_id)]['amount'] += $bundle_products[$product_id]['amount'];
        unset($bundle_products[$product_id]);
    }
}

After (fixed):

foreach (array_keys($bundle_products) as $product_id) {
    if (
        $product_id_map->isChildProduct($product_id)
        && isset($bundle_products[$product_id_map->getParentProductId($product_id)])
        && YesNo::toBool($bundle_products[$product_id_map->getParentProductId($product_id)]['all_variants'])
    ) {
        unset($bundle_products[$product_id]);
    }
}

The child link entries exist in the links table solely for product-to-bundle lookup (finding which bundles contain a given product). They should not inflate the parent’s required amount during the cart completeness check. The parent entry already has the correct amount value from the bundle configuration.


Impact

  • Any product bundle using any_variation = Y with configurable products that have child variations will fail to apply the cart discount
  • The more child variations a product has, the higher the falsely inflated required amount
  • The bundle displays correctly on the product page (storefront display is unaffected)
  • Only the cart/checkout discount application is broken
  • Bundles with simple products (no variations) are not affected
2 Likes

Thanks for the report!

This is a known issue, but unfortunately there is no official fix for it at the moment.

I have added your report to the existing task.

What is wrong with this fix? I’ve applied it and everything seems to work correctly? If you have any additional info can you share it?

Btw, I’ve been applying some improvements to product bundles addon and fixing some bugs. You can check this gist. There are some other improvements related to product variations in product bundles:

1 Like

Thanks for following up, and again, I really appreciate you sharing both the issue and your fix.

Just to clarify, I didn’t mean to imply that there was anything wrong with your solution. The development team hasn’t reviewed it yet, so I can’t confirm whether it covers all scenarios or if there are any edge cases.

We need to go through the usual internal process of defining the expected behaviour, implementing (or validating) the fix and testing it across different use cases. Until that’s done, I can’t confirm whether there are any potential issues.

That said, your solution is definitely valuable and, as you mentioned, others in the community can use it at their own discretion until we provide an official fix. Even if the final solution is similar, it will undergo full review and testing on our side.

As soon as we receive feedback or an official update from the developers, I will let you know.

Thanks again for the detailed investigation and the fix!

Thank you for sharing this. We really appreciate the effort you’ve put into it.

I’ll pass your improvements and the gist on to our product managers and developers for review.

At the same time, I’d like to be clear about what can be achieved: I can’t promise that these changes will be included in the product in their current form (or at all). However, I hope they will be reviewed in the near future and the team will provide some feedback.

Thanks again for taking the time to dig into this and share your work - it’s definitely valuable!

I can confirm that in version 4.16.1, the fix for applying discounts in Product Bundles (including products with variations) is working correctly.
You simply need to comment out (//) the specific line of code mentioned above.

However, I’ve noticed a usability issue:

When a customer clicks “Add all to cart” for a bundle, the cart initially shows the original (non-discounted) prices, instead of the discounted ones. This can be confusing, as users expect to immediately see the final discounted total.

Ideally, it should behave similarly to the “Quantity Discount” feature, where both the unit price and total are displayed with the discount already applied upon adding to the cart.

Has anyone implemented an easy fix or workaround for this?