3
votes

I'm working on a project where the entire store products have a special price different (lowered) from the original price.

I want to be able to apply shopping cart discounts By Percentage of the Original Price just like Catalog Discount Price Rules does.

Right now, if I apply a shopping cart “Percentage of product price discount” rule, it will apply the discount to the special price instead of the original price.

Where is the function for Shopping Cart Rules at? Any details on modifying it to apply discounts on original price will be appreciated.

1

1 Answers

0
votes

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.