Applying percentage discounts can sometimes lead to

EntityMetadataWrapperException: Unable to get the data property commerce_unit_price as the parent data structure is not set. in EntityStructureWrapper->getPropertyValue() (line 438 of /Users/Scott/Sites/wauwaa/sites/all/modules/entity/includes/entity.wrapper.inc).

After walking this through the issue seems to happen (in this instance) when related to commerce shipping/ commerce flat rate. The shipping calculation rules are firing and altering the shipping line items on the order at the same time as commerce discount is altering the discounted line items. There is a lot of conflict going on and its not merely a case of altering the rule weightings.

Traced this through into

      foreach ($wrapper->commerce_line_items as $line_item_wrapper) {
      
          if (!empty($line_item_types[$line_item_wrapper->type->value()])) {
            $line_item_total = commerce_price_wrapper_value($line_item_wrapper, 'commerce_total', TRUE);
            $calculated_discount += $line_item_total['amount'] * $rate;
          
        }
      }

And the trouble is we can't always access $line_item_wrapper->type->value() on the line item. Therefore we need a check around this

something like

      foreach ($wrapper->commerce_line_items as $line_item_wrapper) {
        if ($line_item_wrapper->value()) {
          if (!empty($line_item_types[$line_item_wrapper->type->value()])) {
            $line_item_total = commerce_price_wrapper_value($line_item_wrapper, 'commerce_total', TRUE);
            $calculated_discount += $line_item_total['amount'] * $rate;
          }
        }
      }

This appears to fix my issue whether its the right generic approach I'm not sure.

Comments

scotthooker’s picture

StatusFileSize
new1.07 KB
thehideki’s picture

Thanks for the patch, seems to solve our issue.

joelpittet’s picture

Status: Active » Needs work
+++ commerce_discount.rules.inc
@@ -173,9 +173,11 @@ function commerce_discount_percentage(EntityDrupalWrapper $wrapper, $discount_na
-        if (!empty($line_item_types[$line_item_wrapper->type->value()])) {
...
+        if ($line_item_wrapper->value()) {
+          if (!empty($line_item_types[$line_item_wrapper->type->value()])) {

I did a quick check around commerce and other commerce modules to see how this is used and usually it's like this:

if (in_array($line_item_wrapper->type->value(), commerce_product_line_item_types())) {

So I'm guessing there is a deeper problem here, or this needs to change in most of the other modules including commerce core.

At the very least you could do what commerce_shipping does and put both conditions in the same if statement.

eg:

if ($line_item_wrapper->value() && !empty($line_item_types[$line_item_wrapper->type->value()])) {

What do you think?

kscheirer’s picture

Use a try/catch block instead of testing for individual values. So the example above becomes

try {
  if (!empty($line_item_types[$line_item_wrapper->type->value()])) { ... }
}
catch (Exception $e) {
  // No line item type found, do something else?
  continue;
}
joelpittet’s picture

@kscheirer what about

  if (!empty($line_item_types[$line_item_wrapper->getBundle()])) {

And that bigger problem I mentioned in #3 I think may be commerce_entitycache related.

joelpittet’s picture

@scotthooker could you try this patch out? #2538812: When discounts are deleted on existing carts I hope it resolves your problem as well as they are very similar.

mglaman’s picture

Status: Needs work » Postponed (maintainer needs more info)

Postponing until we can see if #2538812: When discounts are deleted on existing carts resolved the issue reported in summary.

Patch in #1 doesn't match reported error of Unable to get the data property commerce_unit_price as the parent data structure is not set. in EntityStructureWrapper->getPropertyValue(). It does work around the total.

joelpittet’s picture

Status: Postponed (maintainer needs more info) » Closed (outdated)

Closing to triage the issue queue, reopen if you feel this still needs to be addressed.

If this is re-opened we need to add a test to ensure this issue doesn't re-surface.

subhojit777’s picture

I am having the same problem. Although doing this:

function commerce_discount_commerce_cart_order_pre_refresh($order) {
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
  $discount_types = commerce_discount_types();
  $discount_types = array_keys($discount_types);
  foreach ($order_wrapper->commerce_line_items as $line_item_wrapper) {
    // NEW CODE ----
    if (!$line_item_wrapper->value()) {
      continue;
    }
    // -----

    $changed = commerce_discount_remove_discount_components($line_item_wrapper->commerce_unit_price, $discount_types);
    $changed = $changed || commerce_discount_remove_discount_components($line_item_wrapper->commerce_total, $discount_types);
    if ($changed) {
      $line_item_wrapper->save();
    }
  }
  if (isset($order_wrapper->commerce_order_total)) {
    $changed = commerce_discount_remove_discount_components($order_wrapper->commerce_order_total, $discount_types);
    if ($changed) {
      $order_wrapper->save();
    }
  }
}

Is supressing the exception, still I am trying to figure out why an empty line item is created in the first place.

Steps to reproduce:
- Create an order discount coupon
- Apply that coupon during checkout
- Refresh the checkout page
- You will see

EntityMetadataWrapperException: Unable to get the data property commerce_unit_price as the parent data structure is not set. in EntityStructureWrapper->getPropertyValue() (line 457)

And mysteriously a new null line item is created everytime you refersh the checkout page.

subhojit777’s picture

Status: Closed (outdated) » Needs work
Issue tags: +Needs tests
StatusFileSize
new748 bytes

After much debugging I found that it is happening because commerce_shipping adds a line item, and then deletes it. There are commerce_shipping rules which does that. I tried altering the weights of those rules and the discount rule, but it is not working.

Since, the order update causes causes commerce_discount_commerce_cart_order_pre_refresh() to invoke, therefore, altering the priority of the rules is not going to work.

This is the patch that should fix the issue.

czigor’s picture

Status: Needs work » Postponed (maintainer needs more info)

@subhojit777 Does that mean that we need commerce_shipping to reproduce the bug? I tried the steps in #9 but everything seemed to be fine, even with commerce_shipping enabled. I have the coupon redeem form on the first checkout step. Can you provide step-by-step instructions on how to reproduce this on a clean drupal site?

subhojit777’s picture

Yes you need commerce_shipping to see the error. I have set breakpoints in the code and found that commerce_shipping add and later removes a line item. And commerce_discount getting a null line item throws. I saw this error in an existing site. I will try to reproduce the error on a fresh setup.