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
Comment #1
recrit commentedno developer can at least comment on this?
Comment #2
longwaveI 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.
Comment #3
longwaveMarked #1060042: Fundamental flaw in invoicing affecting multiple sub modules as duplicate
Comment #4
longwaveWith 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.
Comment #5
longwaveSee #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.
Comment #6
longwaveClosed #660044: Price handler problems with carts becoming orders and already saved orders as duplicate
Comment #7
fenstratThe 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?
Comment #8
longwaveI 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.
Comment #9
fenstratAgreed, 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.
Comment #10
longwaveFar too late to do anything about this now.