I'm implementing a check for Commerce Coupon where I wan't to prevent users from applying a coupon to an order that is already discounted. In order to do so I needed an easy way to figure out whether a discount had already been applied. I've added a new condition and a function that evaluates the order based on its discount reference.

Comments

adamgerthel’s picture

joelpittet’s picture

It doesn't need to know which discount?

adamgerthel’s picture

Good question. Not in my use case (see #2496085: Deny coupon if discount is applied) but maybe for others. Hmm.. I guess it could be useful for determining which discount/coupon is the best, but that's extremely complicated since you can add free products etc. It'll be hard to programmatically find our which is most valued by the customer. Or did you have something else in mind?

joelpittet’s picture

Was thinking leave yours as default but provide an optional list parameter for discount references to check for?

It could be more complicated granted but that may cover off a few more use-cases, eh?

joelpittet’s picture

Status: Active » Needs review
joelpittet’s picture

Issue tags: +Needs tests

I've turned on the automated testing so we should probably add tests once the feature is decided.

adamgerthel’s picture

I've never written a test, that'll be interesting :)

I can add an optional discount list but calculating the most beneficial discount is way over my league, and in my opinion a bit more than what this issue/feature is for isn't it?

joelpittet’s picture

"Most beneficial discount" is totally out of scope but interesting.

Tests aren't to hard there are lots of tests you can rip off in there already. Maybe give it a try and if you want just pseudo code the test and I can try and help write one.

adamgerthel’s picture

Updated patch with support for product discounts. Apparently only order discounts get referenced by commerce_discounts. The condition now also loops through all price components and searches for discount components.

No tests have been added yet.

joelpittet’s picture

Status: Needs review » Needs work
  1. +++ b/commerce_discount.rules.inc
    @@ -120,6 +120,17 @@ function commerce_discount_rules_condition_info() {
    +          'label' => t('Order')
    

    Nit: needs a comma at the end of each array item on it's own line. Coding Standards

  2. +++ b/commerce_discount.rules.inc
    @@ -484,6 +495,33 @@ function commerce_order_has_specific_quantity_products_build(EntityDrupalWrapper
    +  // Check whether there is an order discount reference on the order.
    +  if (!empty($order->commerce_discounts)) {
    ...
    +  // Check whether there is a discount price component on the order.
    

    Is there any case where this check wouldn't suffice on it's own? And if not, do we also need to check each line item's components as well then?

  3. +++ b/commerce_discount.rules.inc
    @@ -484,6 +495,33 @@ function commerce_order_has_specific_quantity_products_build(EntityDrupalWrapper
    +    return true;
    ...
    +        return true;
    ...
    +    return false;
    

    NIT: true to TRUE and false to FALSE for coding standards.

  4. +++ b/commerce_discount.rules.inc
    @@ -484,6 +495,33 @@ function commerce_order_has_specific_quantity_products_build(EntityDrupalWrapper
    +    foreach ($order->commerce_order_total['und'][0]['data']['components'] as $value) {
    

    NIT: 'und' needs to be LANGUAGE_NONE constant please.

  5. +++ b/commerce_discount.rules.inc
    @@ -484,6 +495,33 @@ function commerce_order_has_specific_quantity_products_build(EntityDrupalWrapper
    +      $array = str_getcsv($value['name'], '|');
    

    That's an interesting approach:) We've mostly been looping the components for this kind of check.

And lastly, this could totally use a test or two to make sure it's working and keeps working but I do like the idea and willing to commit it as I've heard this requested before regarding coupons.

adamgerthel’s picture

Updated patch with fixes from #10:

  1. Too much switching between JS and PHP :)
  2. I tried that at first, but product discounts don't end up as order references. They are only referenced via the line items.
  3. See 1.
  4. Fixed?
  5. :)

Still no tests, I guess I need some starting point as to how to write one.

adamgerthel’s picture

Status: Needs work » Needs review
mglaman’s picture

+++ b/commerce_discount.rules.inc
@@ -484,6 +495,33 @@ function commerce_order_has_specific_quantity_products_build(EntityDrupalWrapper
+      $array = str_getcsv($value['name'], '|');
+      $component_has_discount = in_array('discount', $array);
+      if ($component_has_discount) {

Just a thought, why not:

strpos($value['name'], 'discount|') !== FALSE

Also, not sure how we could test the Rules integration, beyond forcing this file to be included and manually testing the function?

goz’s picture

May be https://www.drupal.org/project/commerce_discount_cumulative could help you ?

It's built from another way. You do not define a condition : "Do not apply if another discount is already applied", but add cumulative option to each discount, so "Do not apply another discount after this discount", or "Remove other discounts, and only take this one".

If you really want "Do not apply if another discount is already applied", may be cumulative module can be updated to take care of this.

adamgerthel’s picture

@Goz interesting approach and def. useful. But how would it be used to prevent discounts and coupons from being combined?

And - wouldn't the changes in this issue's patch be useful to your module?

goz’s picture

Do you really needs to put condition on coupon and not on discount ?
To my point of view, coupon is used to activate a discount, and it's the discount which should be configured to not be applied if another discount is used.

I think there is an issue in the way you are solving this issue right now. Each time cart is rebuilt, discounts are reseted and applied again, ordered by the rules weight.
* Create discount A + coupon with : "Do not apply if another discount is already applied" condition
* Create a discount B
If you apply coupon on your cart, i think both discounts will apply since discount B is created before discount A.

I create a module : https://www.drupal.org/project/commerce_discount_weight to handle discounts weight in discount edit form.

With commerce discount cumulative, you can only use 3 ways (also based on discount weight) :
* Allow to apply discounts after this discount
* Do not apply other discounts after this one (but keep previous)
* Do not apply other discounts after this one and remove previous

So, does adding "Do not apply this discount if another discount is already applied" option on discount (linked to you coupon) can match and solve your case ?

adamgerthel’s picture

I think there is an issue in the way you are solving this issue right now. Each time cart is rebuilt, discounts are reseted and applied again, ordered by the rules weight.

Have you tried my implementation? I use it when the user tries to add a coupon code in the checkout process. It def. should work even if there are multiple discounts added already. What I did in my use case is that I disallow the coupon and notify the users that he/she couldn't add the coupon because there's already a discount on the order.

In either case, this issue is not necessarily about preventing multiple discounts, it's used in order to see if an order is discounted. That's something that could be used in many different scenarios.

rszrama’s picture

Status: Needs review » Needs work

Please check out #2496051: Add rules condition that checks if order has discount and the API function it adds, commerce_discount_get_discounts_applied_to_price(). This inspects any price field to determine what discount related price components it contains. One thing it will not do is let you differentiate between discounts applied to an order vs. a product, but a condition based on this function could.

(Note: the reason I took that route instead of depending on the commerce_discounts field is because product level discounts still needed to be able to specify incompatibility with order level discounts.)

@AdamGerthel, the compatibility system alone may satisfy your initial requirements, regardless of whether or not we had a standalone condition for determining if an order was already discounted. Give it a whirl! : )

torgospizza’s picture

@rszrama: Your link is one back to this issue :)

rszrama’s picture

adamgerthel’s picture

@GoZ:

To my point of view, coupon is used to activate a discount, and it's the discount which should be configured to not be applied if another discount is used.

I see, I'm actually using v1 of Commerce Coupon so there's no connection there between discounts and coupons.

@rszrama:

Your patch looks useful, but how would it be used in my use-case in regards to coupons? Even if I were using v2 of Commerce Coupon, it's still possible to have coupons that have no relation at all to any discount.

The way I see it is this: Most real life coupons have a small text on them that say something like "Cannot be used in combination with other offers". And that's basically what I've created.

Now - if we go back to where this whole issue started, then that's only one use case for this Rules condition. I'm sure there could be others as well though. Rules compatibility is always useful.

rszrama’s picture

I'm not sure what you mean. In the latest version of Discount / Coupon, coupons and discounts are always tied together. You even create coupon codes using IEF in the discount add / edit form. I can't imagine there will be any work on branches that aren't the latest branch at this point in time.

adamgerthel’s picture

@rsrama This patch is for Commerce Discount and not for Commerce Coupon, so I didn't consider that, and I didn't know that v2 of Discount had changed that much.

rszrama’s picture

Ahh, yeah, no worries. I was imprecise in my comment - Commerce Coupon 2.x is the latest version that should be used over 1.x, and it works with the latest release of Commerce Discount 1.x as I described above. I'll be working to get maintainer access on the Commerce Coupon project to fix up the messaging and recommended version ASAP.

mbatterton’s picture

#11 Patch works really well (thank you @AdamGerthel) until we updated the module. Is there any chance of rolling this into the next version of comerce Comerce Discounts for D7.