This issue has popped up in various other places, mainly relating to Discounts (such as #2464525: Coupon is removed during Checkout), however the patch at #1268472: Recursion in price calculation causes cart to skip calculation rules made this bug much more obvious.

Steps to reproduce:
(first: apply patch in #1268472: Recursion in price calculation causes cart to skip calculation rules)
1. Create a product-level discount with a coupon.
2. Add a product to cart and proceed to Checkout.
3. Apply coupon to order.
4. Revisit product display page
5. Coupon will be removed

During my testing, it appears that the cause of this is due to the fact that Commerce Discounts, during the cart refresh, removes the discount price components and Coupon then requires Rules price calculation actions to reapply those components. However, there appears to be a race condition, as in this situation Coupon will be checking price components on each line item, but they will not have been added to the line item's price data array at this time.

The reason this appears to be the case is the patch above removed an extra call to commerce_order_load() which calls an additional order_refresh, which would apply the price components to the line item, allowing the condition to pass. But it seems like we can't always guarantee that those price components will be available.

Is this even required? The function commerce_coupon_order_coupon_code_discounts() looks for a commerce_discount_reference value, and then sends the $order to commerce_coupon_order_discount_ids() to look for the price data components, which evaluates to FALSE since those values were not added - because we did not refresh the order first.

It seems a bit like a chicken-and-egg situation. I think the patch in #1268472: Recursion in price calculation causes cart to skip calculation rules is worth keeping, but in the process it revealed a major shortcoming with how the Coupons module checks for a product-level discount. It seems likely that other corner cases may run into this same problem, especially if we are relying on Rules and commerce_cart_order_refresh()'s "delete-and-reapply" at every step.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

torgosPizza created an issue. See original summary.

Scott Robertson’s picture

Can confirm that I am having the same issue using Commerce Discount 1.0-alpha7 and the latest dev release of Commerce Coupon. I also don't even have #1268472: Recursion in price calculation causes cart to skip calculation rules applied, but it may have something else to do with my setup.

For the time being, I've been forced to comment out most of the logic in commerce_coupon_commerce_cart_order_refresh(), which is far from ideal, but solves the problem for now as I have a client trying to run a Black Friday sale.

With a temporary fix in place, I'll now try to investigate this further to come up with a better solution.

torgosPizza’s picture

@Scott: Thanks for confirming. A couple of quick questions:

Does your Commerce Discount have the "compatibility settings" feature in it? We have come to the conclusion that this feature still needs some work - especially if you are using product-level discounts. See #2621526: Compatibility settings other than "all" causes Discount+Coupon to be removed which this may end up being a duplicate of. I would consider reverting to the previous release (or at least reverting the patch that introduced this feature) and give that a try.

One other test for you: if you recreate these discounts as order discounts (assuming they are product discounts right now) does the problem persist? In my testing it seems like mainly product discounts are the ones affected (as described above).

I'd also be curious to know if the discount goes away only when there is a Coupon attached to it. Not ideal of course!

Scott Robertson’s picture

@torgosPizza I am indeed running a version of Commerce Discount with the "compatibility settings" feature. Like you, I also seem to have come to conclusion that it has some issues. I'm also having trouble with product discounts; I haven't done much testing with order discounts yet.

One thing I did notice is that the commerce_discount_compatibility_check() function sets a $applied_discounts variable by calling commerce_discount_get_discounts_applied_to_price(), and the discount that is being checked for compatibility is sometimes included IN the list of applied discounts.

This causes the compatibility check to fail when the discount has compatibility settings configured on it, because it ends up conflicting with itself! I.e. it thinks that another discount has already been applied to the order, and removes itself.

I'm currently testing out a figure where I add the following code:

foreach ($applied_discounts as $applied_discount_id => $applied_discount_name) {
    if ($applied_discount_name == $discount_name) {
      unset($applied_discounts[$applied_discount_id]);
    }
  }

right before this line:

// Ensure none of them indicate they are incompatible with the current one.
  foreach ($applied_discounts as $applied_discount_id => $applied_discount_name) {

In the commerce_discount_compatibility_check().

So far, this seems to have solved the issue of the discount disappearing and the coupon being removed, but I need to do some more testing to be sure. It doesn't really seem like a very elegant solution, either.

torgosPizza’s picture

Hi Scott,

Thanks for the additional information. That addition of yours actually seems pretty reasonable - it does seem strange that a discount could be conflicting with itself.

Do you have some additional steps to reproduce this? We're going to be running a Black Friday deal today as well and I'd love to give your stop-gap a try. It might actually end up being the most feasible - unless there is a fix further up with how the applied discount rules are firing.

Thanks!

torgosPizza’s picture

Issue tags: +commerce-sprint

Tagging for sprint. Could use some more eyes on this as it relates to other issues within the Discount ecosystem.

Scott Robertson’s picture

@torgosPizza We're using a fairly custom set up, so I've got to try and take some time today or tomorrow to get a better list of steps to reproduce using Commerce Kickstart. For our set up, we just have do create a product discount tied to a coupon, and that's enough to start causing problems during checkout where the coupon/discount disappears.

Using the fix I noted above, I was able to stop the coupon/discount from disappearing, but we still had all kinds of issues with the "compatibility" settings in the latest version of Commerce Discount. We had to change all of our discounts back to the default "compatible with all other discounts" options to have the discounts work correctly.

torgosPizza’s picture

Ah! That makes sense. So you were using a custom compatibility setting for each of them. I just wanted to double check that because honestly we haven't been using those settings since discovering the issue, but I'd be happy to dig in more. And I do agree with your temp fix - it doesn't make sense to compare the current discount to itself! Otherwise I imagine you'd have to set it to be compatible with itself and that seems a bit weird :)

Feel free to post again, but I think you may want to chime into the issue at #2621526: Compatibility settings other than "all" causes Discount+Coupon to be removed as that seems to be the issue you're having. And if we discover that this issue is a duplicate of that one we may end up closing this since so far I'm unable to reproduce this issue with our default configs, which is nice.

I have discovered some weird issues with "Discounts that have Disabled Coupons attached" being applied without requiring the coupon, but that is probably unrelated. I'll post a new issue about that soon.

jptaranto’s picture

I've used the fix in #4 to fix the same issue with coupons dropping off orders at the review stage.

It seems to be working for now. Should we get this into a patch?

Mschudders’s picture

I've attached a patch.

Confirmed working with latest dev and latest dev of commerce coupons.

torgosPizza’s picture

@mschudders: Thanks for the patch! However it is very similar to one of the earlier patches from #2621526: Compatibility settings other than "all" causes Discount+Coupon to be removed which has several more advanced versions to work around other peculiarities, which in turn spawned other issues to try and dig into the root cause of this problem. I highly recommend checking that issue out - and if possible, helping us test some of the child issues there, so we can try and get this resolved for real. Thanks for taking it on!

jmary’s picture

I confirm that #10 solves my issue as announced.
I confirmed that the patch mentionned at #11 works also.

vasike’s picture

Project: Commerce Coupon » Commerce Discount
Version: 7.x-2.x-dev » 7.x-1.x-dev

from the patch it looks it should be on Commerce Discount issue queue not Commerce Coupon.

joelpittet’s picture

Status: Active » Needs work

Patch in #10 the comment says to make sure discount is not removed, and what it does is unset/remove them... this is bizarre, could you explain?

gmrmedia’s picture

Version: 7.x-1.x-dev » 7.x-1.0-alpha8

Patch from comment#10 works form me in 7.x.1.0-alpha8 module version.
In my case i lost information about discount for product on cart/checout page refresh.

alibama’s picture

not working for me with latest devs of both

joelpittet’s picture

Version: 7.x-1.0-alpha8 » 7.x-1.x-dev

All patches apply against dev

alibama’s picture

the patch applies fine - it just doesn't resolve the issue. I'm running the latest devs of discount and coupon
i'm going to try a fresh install and see whether the patch referenced in #11 solves it

rszrama’s picture

Status: Needs work » Needs review

If I'm not mistaken, #2621526: Compatibility settings other than "all" causes Discount+Coupon to be removed seems to have resolved this issue as a side effect. I just attempted the OP to reproduce the bug and my coupon remained even after visiting the product page and adding to cart again.

If someone else can test this, just move to "Closed (outdated)" if you confirm it's resolved.

attiks’s picture

Status: Needs review » Needs work

Tested with latest versions of commerce, discount and coupon

Scenario:
- Order discount A with a coupon
- Order discount B without a coupon

Whenever commerce_discount_commerce_cart_order_refresh get called the discount reference on the order to B is removed,

rules_invoke_event('commerce_discount_order', $order_wrapper); triggers the executing of both rules for A and B, but only the reference to A is saved on the order

attiks’s picture

#20 This is caused by another error

commerce_discount_percentage contains the following line

      $delta = $wrapper->commerce_discounts->count();
      $order->commerce_discounts[LANGUAGE_NONE][$delta]['target_id'] = $discount_wrapper->discount_id->value();

if you change it to

      $order->commerce_discounts[LANGUAGE_NONE][]['target_id'] = $discount_wrapper->discount_id->value();

the reference stays, so this problem was caused by the offer type, I'll create a new issue

attiks’s picture

Status: Needs work » Needs review