Expected: price stored in uc_order_products price is locked-in altered price at time of checkout so altering functions can change and not effect display on orders.
Actual: original (un-altered) price is stored

My initial thinking was to not alter the price on order_product context... since this should be locked in. After creating an alterer and running some test orders, I noticed the price stored in the uc_order_products was the original price and that was being shown on the order (when not altering).

The hunt...

uc_cart.pages.inc -> uc_cart_checkout_form_validate()
The code below basically says take $order->products[$key]->price, calculate the 'original' context of the price as $price, and if different store the 'original' context price as $order->products[$key]->data['altered_price']... confused yet? should be... let it play for now.

The next question that comes up is.. well what context revision is the $item prices in $order->products?
answer: it comes from $form_state['values']['cart_contents'] which is a hidden field that gets save in checkout AND that comes from uc_cart_get_contents(). uc_cart_get_contents() calls no uc_price in any context so the price is the original unaltered sell_price as determined by the node. If you're up on the price api, you might say but isn't there a 'cart_item' context type, yes but its not called there. It is correctly called on the checkout form with hook_cart_display() -> uc_product_cart_display.

Boiling it down...

Should uc_product have a hook_cart_item() to call uc_price with 'cart_item' context so that what comes out of uc_cart_get_contents() is the altered price?

  • keep in mind calculating the 'original' price in uc_cart_checkout_form_validate is still confusing on nomenclature and what it should be doing

-- OR --

Should the $price calculated in uc_cart_checkout_form_validate be the 'altered' price based on uc_cart_get_contents returning the orignal, and then stored altered as $order->products[$key]->price and original as $order->products[$key]->data['original_price']

/** uc_cart.pages.inc -> uc_cart_checkout_form_validate() **/
  $context = array(
    'revision' => 'original',
    'type' => 'order_product',
  );
  foreach ($order->products as $key => $item) {
    $price_info = array(
      'price' => $item->price,
      'qty' => $item->qty,
    );
    $context['subject'] = array(
      'order' => $order,
      'product' => $item,
      'node' => node_load($item->nid),
    );

    // Get the altered price per unit, as ordered products have a locked-in
    // price. Price altering rules may change over time, but the amount paid
    // by the customer does not after the fact.
    $price = uc_price($price_info, $context) / $item->qty;
    if ($order->products[$key]->price != $price) {
      $order->products[$key]->data['altered_price'] = $price;
    }
  }

Comments

recrit’s picture

no developer can at least comment on this?

longwave’s picture

I just found this code when working on uc_vat, remembered that there was supposed to be a price "lock in" mechanism and wondered why it never worked. It turns out the altered price is never stored anyway - $context['revision'] = 'original' so this call to uc_price() returns the unaltered price every time.

Though I doubt this will ever be fixed now, subscribing just in case; a change here could either benefit or break uc_vat.

longwave’s picture

longwave’s picture

With price alterers gone in D7, I think this means this has to be fixed in D6 for the upgrade path to work, otherwise prices will change to their unaltered versions after the upgrade - and it's not feasible to fix that at upgrade time as far as I can see.

longwave’s picture

See #1097668: Multicurrency support for a fix for this in D6, which will also need a hook_update_N() function to lock in order prices once and for all.

longwave’s picture

fenstrat’s picture

The 3rd patch in #1097668: Multicurrency support works well to lock prices in at their altered price at checkout time.

As longwave mentioned in #5 it needs a hook_update_N() which would probably have to to load all orders and resave them with their prices altered. Question is, is that the correct approach?

longwave’s picture

I cannot see a way of doing this without breaking backward compatibility with existing price handlers in some contexts, but there are so many calls to uc_price() in custom modules that we cannot change too much in this branch. This is a difficult situation to deal with, as ideally it would be good to streamline uc_price() somewhat - the initial design didn't really work out - but as there will be unknown custom modules implementing price handlers, doing this in the 6.x-2.x release cycle seems like a dangerous decision to make.

fenstrat’s picture

Agreed, it appears changes to uc_price() in the 6.x-2.x release would break compatibility with too many things. So it appears we're stuck with this behaviour.

longwave’s picture

Status: Active » Closed (won't fix)

Far too late to do anything about this now.