Can someone explain why a custom option of type percent is calculated using the final item price on a quote?

See here:

# File: app/code/core/Mage/Catalog/Model/Product/Option/Value.php
public function getPrice($flag=false)
    if ($flag && $this->getPriceType() == 'percent') {
        $basePrice = $this->getOption()->getProduct()->getFinalPrice();
        $price = $basePrice*($this->_getData('price')/100);
        return $price;
    return $this->_getData('price');

It doesn't seem to make sense to do this. Here's an example to illustrate the problem:

Laptop Computer
  Price: 500.00

  Custom Options
  1-YR Warranty: 20%

As I attempt to add this item to my cart along with the warranty option, my total cost as shown on the product view page will be 600.00. That looks good, because 20% is applied to the final price of 600.00 at that moment (100.00).

But what happens in the cart if this method is called, after the option has already been applied to the item?

According to the getPrice method above, it will show as 120.00 (20% of 600.00) ... clearly this is not right. And curiously, a stock installation of CE 1.x does not render custom option prices in the cart (only row totals).

Has anybody encountered this before? Or maybe more likely, what am I not understanding correctly here?


This shouldn't happen. The method Mage_Catalog_Model_Product_Option_Value::getPrice() is called during calculation of the products final price. At the beginning of this calculation "final_price" is set to the base price (1) and then incrementally modified, this includes adding option prices (2):

     * Get product final price
     * @param   double $qty
     * @param   Mage_Catalog_Model_Product $product
     * @return  double
    public function getFinalPrice($qty = null, $product)
        if (is_null($qty) && !is_null($product->getCalculatedFinalPrice())) {
            return $product->getCalculatedFinalPrice();

(1)===> $finalPrice = $this->getBasePrice($product, $qty); <===
        Mage::dispatchEvent('catalog_product_get_final_price', array('product' => $product, 'qty' => $qty));
        $finalPrice = $product->getData('final_price');

(2)===> $finalPrice = $this->_applyOptionsPrice($product, $qty, $finalPrice); <===
        $finalPrice += $this->getTotalBundleItemsPrice($product, $qty);

        return max(0, $product->getData('final_price'));


For those who don't read comments and want an answer to why the "final price" is used to convert the custom option percent amount: The percentage is actually based upon the base price with applied catalog price rules (or any other rules that custom extensions apply with the event catalog_product_get_final_price. This is not the final final price.

  • Thanks for clarifying. Your answer possibly reveals a mistake in my scenario. I've attempted to extend Mage_Catalog_Model_Product_Option_Value to render item option prices in the cart. At that point the final price has already been calculated. So when I use the getPrice method, I end up with the inflated value as shown in my question. In other words, this class was not designed for use where I'm using it. Although personally I still don't like the idea that calling getPrice multiple times may give you a different result. Feb 6 '15 at 15:08
  • I'm on the fence about accepting this answer, as it doesn't cover why the final price is used to convert the custom option percent amount. It makes more sense to me that 20% is always based on the unit cost of the item (base price). Feb 6 '15 at 15:17
  • It is based upon the base price with applied catalog price rules (or any other rules that custom extensions apply with the event catalog_product_get_final_price. Doesn't that make sense? The side effect of getPrice() is indeed ugly but that's a different topic. Feb 6 '15 at 15:21
  • I hadn't considered the price rules or the reach of that event. In that case, I agree and it does make sense now. As you said, the implementation of getPrice is another story. Your comment is worth adding as part of the answer. Thanks. Feb 6 '15 at 15:28
 $productSku = $_product->getSku();
 $product = Mage::getModel('catalog/product');
 $productId = $product->getIdBySku( $productSku );
 $product = Mage::getModel("catalog/product")->load($_product->getId());
 $attVal = $product->getOptions();
 $optStr = "";
<div class="actions">

    <form action="<?php echo $this->getSubmitUrl($_product) ?>" method="post" id="product_addtocart_form"<?php if($_product->getOptions()): ?> enctype="multipart/form-data"<?php endif; ?>>
        <?php echo $this->getBlockHtml('formkey') ?>
        <div class="no-display">
            <input type="hidden" name="product" value="<?php echo $_product->getId() ?>" />
            <input type="hidden" name="related_product" id="related-products-field" value="" />
       <?php $product = Mage::getModel('catalog/product'); ?>
    <?php $product->load($_product->getId()); ?>

 <?php foreach($attVal as $optionKey => $optionVal)
  $optStr.= "<select style='display:block; clear:both;' id='".$optionVal->getId()."' name='options[".$optionVal->getId()."]'>";
  foreach($optionVal->getValues() as $valuesKey => $valuesVal)
$price = number_format($valuesVal->getPrice(),0);
$optStr.= "<option price='".number_format($valuesVal->getPrice(),0)."' data-label='".$colorarray[$valuesVal->getTitle()]."' value='".$valuesVal->getId()."'>".$valuesVal->getTitle(); if($price != '1'){$optStr.=" - Rs.".number_format($valuesVal->getPrice(),2);}$optStr.="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</option>";
$optStr.= "</select>";
echo($optStr );
 <?php if($_product->isSaleable()):?>
  <input type="image" src="<?php echo $this->getSkinUrl('images/addtocart.png')?>" class="button btn-cart" onclick="addToCart('<?php echo $this->getAddToCartUrl($_product) ?>')">

<?php else:?>
     <p class="availability out-of-stock"><span><?php echo $this->__('Out of stock')?></span></p>
 <?php endif;?>


enter image description here

  • Please don't post "code only" answers if the question asks for an explanation. How is this code supposed to help? Feb 21 '16 at 21:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.