I'm trying to create a coupon that can only be applied on a per product basis.

I've added a reference field to the basic coupon which matches the product. When I come to setup a rule I can't find any combination of events that will expose both coupon and line item. Has anyone tried to do this? Is there some rule event I'm missing?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

anibal’s picture

Im trying also to do it, but cant figure out the logic to do it using rules..

muschpusch’s picture

I think this will be a nice feature. We could add a VBO view for referencing multiple products. Did anyone figured out how to do the rules logic?

78doog’s picture

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

Not sure if it is still relevant but I did this using http://drupal.org/project/commerce_coupon_fixed_amount and adding the condition "Order contains a particular product" in the conditions area of the rule "Calculate coupon with fixed amount"... using dev version of commerce coupon. Strangely I cannot get it to work with multiple products though.

seanbfuller’s picture

Status: Active » Needs review
FileSize
3 KB

I spent some time banging my head against this and it seemed like trying to do this with only the rules interface was more difficult than it needed to be. I realize that there are some bigger fish to fry for this module, so I thought I'd try to pitch in. I rolled a quick patch to add a new rule condition callback that tests to see if the coupon being added has a product reference.

To test this:

  1. Add a "field_product_reference" field to your coupon type.
  2. Import the rules component listed below.
  3. Import the reaction rule listed below.
  4. Create a coupon referencing product A. Purchase that product. It should go through as normal.
  5. Create a coupon referencing product B. Try to purchase product A using the coupon for product B. It should throw an error.

Note that if the coupon does not have a product reference, the rule should be ignored.

Here's the component rule:

{ "rules_validate_coupon_product_reference" : {
    "LABEL" : "Coupon Validation: Coupon product reference",
    "PLUGIN" : "rule",
    "REQUIRES" : [ "commerce_coupon", "rules" ],
    "USES VARIABLES" : {
      "commerce_order" : { "label" : "Commerce Order", "type" : "commerce_order" },
      "commerce_coupon" : { "label" : "Commerce Coupon", "type" : "commerce_coupon" }
    },
    "IF" : [
      { "NOT commerce_coupon_order_has_referenced_product" : {
          "commerce_order" : [ "commerce_order" ],
          "commerce_coupon" : [ "commerce_coupon" ]
        }
      }
    ],
    "DO" : [
      { "drupal_message" : {
          "message" : "That coupon is not valid for any of these products.",
          "type" : "error"
        }
      },
      { "commerce_coupon_action_is_invalid_coupon" : [  ] }
    ]
  }
}

(Note that this follows the same logic from the "Coupon Validation: Set the coupon as invalid if number of uses is reached" component rule.)

Here's the reaction rule that just calls this component when it is time to validate a coupon:

{ "rules_coupon_validation_check_product_reference" : {
    "LABEL" : "Coupon validation: Coupon product reference",
    "PLUGIN" : "reaction rule",
    "REQUIRES" : [ "rules", "commerce_coupon" ],
    "ON" : [ "commerce_coupon_validate" ],
    "DO" : [
      { "component_rules_validate_coupon_product_reference" : {
          "commerce_order" : [ "commerce_order" ],
          "commerce_coupon" : [ "coupon" ]
        }
      }
    ]
  }
}

If this patch is going down the right path and it makes sense for this functionality to be in this module, then getting those into rules defaults would probably make sense.

Two questions I had in the patch: I wasn't sure how language should be handled. It might make sense to look up product reference field names (similar to some code I remember seeing in commerce product display manager). Otherwise, needs review and probably some heavy testing.

For easy reference, here are the other issues I found that seem to deal with product specific coupons (and maybe a few other old ones that were trying to put a coupon reference onto a product?):

Thanks in advance!

sher1’s picture

I followed these instructions and when a product that wasn't referenced was in the cart, it denied as expected. When I had a product that was referenced, this is the error I got
Recoverable fatal error: Object of class CommerceCouponType could not be converted to string in commerce_coupon_basic_apply_percentage_coupon_to_item_line() (line 79 of /public_html/hpc/sites/all/modules/commerce_coupon/modules/basic/commerce_coupon_basic.rules.inc).

Also, after applying the patch(which had to be done before importing the rule or component) I had to clear the cache. That was expected for me but not be for some people.
So in order, what I did was

  1. Apply patch
  2. Clear cache
  3. Add Product Reference field to coupon
  4. Create coupon and add product references
  5. Import Component
  6. Import Rule
  7. Attempt to purchase product with coupon that doesn't work and it was denied
  8. Attempt to purchase product with coupon that should work and it generated the above error

So my question is, did I miss a step?
Great work by the way!! This seems like exactly the kind of additional functionality that is needed for coupons. At least it has been something I have looked for when doing e-commerce.

sher1’s picture

Looks like that completely breaks my checkout process. Will wait to hear what can be done

seanbfuller’s picture

Status: Needs review » Needs work

Are you using a percentage-based coupon? I think I was only testing against fixed amount coupons.

Also, during additional testing we found an issue where if the coupon is worth more than the product, some problems arise. Fox example: trying to use a $200 coupon on a $100 product. I think we'd also need an action that adjusts the amount of the coupon to be equal to the product amount when this happens.

Marking this as needs work based on those two items.

sher1’s picture

Yes, I am using a percentage based coupon.
I would think we could create a rule that just zeroed out any orders that had a negative balance. It seems like that would be a catchall for any situation that could arise in credits and would also serve to disable the ability for a system to issue a credit if the system didn't already protect against that.

sher1’s picture

I looked over the code and it didn't look like it was doing anything that would specify total over percentage. I also can't see why this shouldn't work. I am going to try un-installing the module and see if that works.

ahimsauzi’s picture

Hi sher1,

Where you able to use the patch with percent coupon while restricting multiple products?

If this works, I can't wait to see it added to this module. It is a well needed functionality!

~Uzi

sher1’s picture

I have not been able to verify. My rules are currently in a bad state. I have a backup that I would like to roll back to and try again but I don't have ready access to it this weekend. I will post it as soon as I can try that.

seanbfuller’s picture

I might be wrong, but I think one of the subtle distinctions regarding using this method with a percentage-based coupon is that the coupon applies to the total purchase. It does not apply to the price of a specific product in the cart. For example: If you set up a coupon for 10% that references product x, then this method would essentially be applying 10% off your whole purchase when you buy product x. So if you have products x, y and z, then it will apply 10% off the total of those three. If you just have products y and z in your cart, it would not be valid. (Edit: To be clear, I just mean that it wouldn't apply 10% against the price of product x.)

Again, this method was only tested with fixed-price coupons. For that, the issue described in #7 of a coupon value greater than the referenced product price still stands.

deggertsen’s picture

#4 works for me with the most recent dev version (2012-May-22). I did manually apply the patch and then imported the competent and rule and made a few modifications to make it so that if the product reference field is empty it wouldn't make the coupon invalid, but instead allow any product.

So in my opinion this part of the patch is the only thing that still needs work.

function commerce_coupon_order_has_reference_product($order, $commerce_coupon) {
  // Hard code language as none (und) for now
  // @todo: Neither element has a language element. If they did, which should we use here?
  $lang = isset($commerce_coupon->language) ? $commerce_coupon->language : LANGUAGE_NONE;
  // Get the product reference from this coupon
  // @todo: Lookup product reference field names instead of using this hard-coded value?
  if (isset($commerce_coupon->field_limit_products[$lang][0]['product_id'])) {
    // Get the referenced products
    $ref_pids = array();
    foreach($commerce_coupon->field_limit_products[$lang] as $i => $ref) {
      $ref_pids[] = $ref['product_id'];
    }

You'll notice that I had to change the hard coded field to match the field name I am using, otherwise the coupon works regardless of what is in your product reference field. I think as soon as we fix that so it is instead referencing a field from the UI instead of the hard-coded field this should be committed. If I knew how to do that I would have included the patch here, but unfortunately I'm not that skilled in php.

Thanks for the work seanbfuller!

BJ___’s picture

Does anyone have a suggestion for how to create a percentage based coupon limited to a particular product ?

I got #4 working thanks very much @seanbfuller.

However....when using a percentage coupon the percentage amount still applies to the entire order instead of just one product.

I guess this means editing the rule "Apply percentage coupons to product line item" ? But I haven't found the right combination for this yet.

For anyone that's interested I ended up solving this by creating my own custom rules action based on the "Apply percentage coupons to product line item" in the coupon percentage module. In that rule I compared the line item product to the referenced field in the coupon. It seems to work great !

generalconsensus’s picture

I encountered a problem with my checkout system in which shipping line items were being called with the foreach and not weeded out. I created an if statement that weeds out those pesky shipping line items by posting the type to a variable and checking that the variable type is equal to product.

function commerce_coupon_order_has_reference_product($order, $commerce_coupon) {
  // Hard code language as none (und) for now
  // @todo: Neither element has a language element. If they did, which should we use here?
  $lang = isset($commerce_coupon->language) ? $commerce_coupon->language : LANGUAGE_NONE;
  // Get the product reference from this coupon
  // @todo: Lookup product reference field names instead of using this hard-coded value?
  if (isset($commerce_coupon->field_product_reference[$lang][0]['product_id'])) {
    // Get the referenced products
    $ref_pids = array();
    foreach($commerce_coupon->field_product_reference[$lang] as $i => $ref) {
      $ref_pids[] = $ref['product_id'];
    }
    // Go through products in the order looking for a match
    $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
    foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
      $type = $line_item_wrapper->type->value();
        if ($type == 'product'){
      if (isset($line_item_wrapper->commerce_product->product_id)) {
        $this_pid = $line_item_wrapper->commerce_product->product_id->value();
        if (in_array($this_pid, $ref_pids)) {
          return TRUE;    
          }
        }
      }
    }
    // If we got down here, then no product matched, so validation failed.
    return FALSE;
  }
  else {
    // If no product reference was found, then we ignore this rule.
    return TRUE;
  }
}
rtdean93’s picture

BJ - i am interested in how you did this.

I have modified the "Apply a percentage coupon to a product line item" action... but don't know which Line Item Data Selector to use.

Thanks in advance...

Bobby

BJ___’s picture

@rtdean93.
First I added a product reference field to the percentage coupon.
Then I cloned the existing rule. "Apply percentage coupons to product line item" and edited it.
In the loop section of that rule where it says. "Apply a percentage coupon to a product line item"
This is a custom rules action that is provided by the "commerce_coupon_pct" module.
What I did was create my own rules action (Based mostly on the one provided) and then adjust it to check for a specific product by comparing the line-item product id to the coupon product id.
This could be further expanded to allow for multiple products, limits etc.
Cheers

~BJ

rtdean93’s picture

BJ - can you export and paste your component here? I have built the new rule set... but passing the product to the action is causing me trouble.

BJ___’s picture

@rtdean93. It's not a component per say but a "Custom rules action".
The file "commerce_coupon_pct.rules.inc" in the commerce coupon module will give you a nice example you can then modify yourself.
If you're able to wait I have plans to create a little bonus module that will have this functionality.
I can't promise anything but my plan is to do it very soon.

rtdean93’s picture

BJ -

Thank you for this... and yes, I can wait and will be looking for your contribution when you get around to it.

Thank you

Bobby

cruzeazy’s picture

I used #4 and it worked. Thanks seanbfuller. I was also able to import coupon codes that referenced a particular product using commerce_feeds. My problem is that some coupon codes need to reference multiple products and when I import using for example: "PROD-01 , PROD-02" as the product reference in the csv, I get the error A product with SKU PROD-01 , PROD-02 could not be found. Please check that the product exists or import it first.
I'm able to manually set one coupon code to reference multiple products one-by-one with the fixed coupon module, but I can't import.

AndyF’s picture

Status: Needs work » Needs review
FileSize
3.96 KB

Hi,

I also wanted to have a % coupon that only applied to certain products as specified by a multi-value reference field. I've slightly modified #4: it now uses entity_metadata_wrapper to hopefully avoid any language issues, and the reference field to use can be specified as an argument to the condition. It works with both product references and entity references, but I think only the latter can be treated as a list by Rules (could be wrong there) so I find using that easier. If you do use entity references you can do all the rest without any custom coding: I'll put up a sandbox with a field and Rules in just a sec.

Thanks

AndyF’s picture

Right, I've got some code and instructions up at http://drupal.org/sandbox/andy/1689808.

HTH

AndyF’s picture

pam.pkrweb@gmail.com’s picture

Hi I need help can not get it to work and test it. I saw you instruction on the sandbox page. I did the following steps. I applied the patch (for beta5) above. I add a Product Reference field of product reference to my percentage coupon, it setup has checkbox - so multiple products can be selected. Went to the rule Apply percentage coupons to product line items, removed the loops - Apply a percentage coupon to a product line item. But now I do not see the new rule Apply coupon to line item (checking product reference) to add. What I am I missing. I read this posting several times and the information on the sandbox page that is posted. I am at a loss now. Please help. Thank you.

AndyF’s picture

@pamreed have created an issue in the sandbox for you and will follow up there, thanks: #1713328: Can't find 'Apply coupon to line item (checking product reference)'

pcambra’s picture

Status: Needs review » Needs work

AndyF, many thanks for taking care of this, the patch looks fine (needs a reroll against latest version though), but I'm not sure if you want to keep this code in your own sandbox or you want it tol live in coupon module.

Setting to needs work for clarification.

AndyF’s picture

Status: Needs work » Needs review

@pcambra Completely up to you, whatever you think makes the most sense.

pcambra’s picture

Status: Needs review » Needs work

Keeping needs work as at least the patch needs to be rerolled against last -dev

Maybe you want to keep the module, that's perfectly fine by me, I'm not moving big thins at the moment as the 2.x coupon branch will depend on Commerce Discount and everything will change soon.

AndyF’s picture

Status: Needs work » Needs review

@pcambra I don't know if my git-fu is failing me, but I've just rolled a new patch with the latest from git and it's identical to the old one...? (Tentatively putting back to needs review!)

I'm happy to keep that simple feature as a separate module then. If I understand you correctly I should put a warning on the project page that this is not the future of coupons?

Thanks

pcambra’s picture

Status: Needs review » Fixed

Let's keep it as a separate module then, marking this as fixed, I've informed it in the product page.

Many thanks to all involved, specially Andy.

AndyF’s picture

I've promoted the sandbox: http://drupal.org/project/commerce_couponprodref

Thanks

donapis’s picture

Great. Thanks

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.