I recently had to solve the same problem. These two blog guides were very helpful in crafting my final solution.
My custom observer on the salesrule_validator_process event looks like this:
class My_SalesRule_Model_Observer
{
// New SalesRule type
const TO_ORIGINAL_PRICE = 'to_original_price';
/**
* Add the new SalesRule type to the admin form.
* @param Varien_Event_Observer $obs
*/
public function adminhtmlBlockSalesruleActionsPrepareform($obs)
{
$field = $obs->getForm()->getElement('simple_action');
$options = $field->getValues();
$options[] = array(
'value' => self::TO_ORIGINAL_PRICE,
'label' => Mage::helper('salesrule')->__('Percent of original product price discount')
);
$field->setValues($options);
}
/**
* Apply the new SalesRule type to eligible cart items.
* @param Varien_Event_Observer $obs
*/
public function salesruleValidatorProcess($obs)
{
/* @var Mage_SalesRule_Model_Rule $rule */
$rule = $obs->getRule();
if ($rule->getSimpleAction() == self::TO_ORIGINAL_PRICE) {
/* @var Mage_Sales_Model_Quote_Item $item */
$item = $obs->getItem();
// Apply rule qty cap if it exists.
$qty = $rule->getDiscountQty()? min($obs->getQty(), $rule->getDiscountQty()) : $obs->getQty();
// Apply rule stepping if specified.
$step = $rule->getDiscountStep();
if ($step) {
$qty = floor($qty / $step) * $step;
}
// Rule discount amount (assumes %).
$ruleDiscountPercent = $rule->getDiscountAmount();
/* @see My_Catalog_Model_Product::getDiscountPercent */
$productDiscountPercent = $item->getProduct()->getDiscountPercent();
// Ensure that the rule does not clobber a larger discount already present on the $cartItem.
// Do not apply the rule if the discount would be less than the price they were already quoted
// from the catalog (i.e. special_price or Catalog Price Rules).
if ($ruleDiscountPercent > $productDiscountPercent) {
// Reduce $ruleDiscountPercent to just the gap required to reach target pct of original price.
// In this way we add the coupon discount to the existing catalog discount (if any).
$ruleDiscountPercent -= $productDiscountPercent;
$pct = $ruleDiscountPercent / 100;
// Apply the discount to the product original price, not the current quote item price.
$discountAmount = ($item->getProduct()->getPrice() * $pct) * $qty;
$item->setDiscountPercent($ruleDiscountPercent);
$obs->getResult()
->setDiscountAmount($discountAmount)
->setBaseDiscountAmount($discountAmount);
}
}
}
}
If you have set up your extension properly, you should see the new rule type appear under:
Promotions -> Shopping Cart Price Rules -> Edit 'New Rule' -> Actions -> Update prices using the following information: Apply [Percent of original product price discount]
Caveats: you’ll notice I implemented this to work on percentages, including creating a method on the product model to calculate the catalog discounted price (i.e. special price and other discounts applicable at the catalog level). You will need to implement that if you wish to do the same, or update this logic to fit your scenario perhaps just referring to $item->getProduct()->getPrice() instead.