开发者

Is there a way to refresh Magento *Catalog* (not Shopping Cart) prices with a Coupon code?

开发者 https://www.devze.com 2023-02-08 06:41 出处:网络
Been searching high and low for a question similar to this both here and in Google and I\'m surprised that I haven\'t been able to find anything similar.

Been searching high and low for a question similar to this both here and in Google and I'm surprised that I haven't been able to find anything similar.

I'm familiar with Customer Groups and Tiered Pricing but it doesn't fit the objective that my client has set.

What we want is for a user to come to our Magento store and see the regular homepage with regular prices. At that point we want a prominent text field for the user to add a Coupon Code upon which the site will refresh and display the new discounted prices with the regular price struck out (or "slashed" by some other visual method.

Customer Groups / Tiered Pricing aren't the solution because they require that the customer log in. The NOT LOGGED IN group wouldn't help either since all users would see the discount.

This can't happen in the Shopping Cart either because by then it's too late, this needs to happen on the Catalog level.

We are currently using OSCommerce and transitioning over to Magento shortly. Right now what we're doing to emulate this behavior is to have a text field on our regular website on the Store Access page where a user can click on a region or enter a coupon code. IF they enter a code they are redirected to a custom store that has special pricing.

I know that it's easy to recreate our current method in Magento by creating a store view and then using the same functionality but it seems a shame to do this when the idea of moving to a new platform that so much more powerful.

I haven't seen any extensions that do this. Does anyone have any insight as to whether something like 开发者_如何学JAVAthis can be accomplish and if so, how?


I am curious Jonathon how you did it, I didn't take your approach, mine is a little more complex. Mine does allow someone to post the coupon code in the url as well though and I set a cookie and all that for it. I basically setup my own form in the header that a user can put in a coupon code and have that applied as well as putting the coupon in the url for email campaigns.

It would take me a while to go back over it in detail, so I will post some code snippets that maybe can help you get going, a long with trying the way Jonathan says as well.

Override the cart controller and add your own action.

 public function couponExternalPostAction()
{
        $quote =  $this->_getQuote();
        $couponCode = (string) $this->getRequest()->getParam('coupon_code');
        $validateCoupon = Mage::getModel('package_module/coupon');
        $json = $validateCoupon->addCouponCode($couponCode, $quote, $this->getRequest());

        echo $json;
        return;
}

I also had to override the couponPostAction() to get things to work the normal way correctly.

I have an addCoupon method in my own model

 public function addCouponCode($code, $quote, $request){
    $couponCode = (string) $code;
    $removed = false;

    if ($request->getParam('remove') == 1) {
        $couponCode = '';
        $removed = true;
    }

    $oldCouponCode = $quote->getCouponCode();

    /* No point in applying the rule again if it is the same coupon code that is in the quote */
    if ($couponCode === $oldCouponCode) {
        $json = $this->_getResponseJson($removed, $couponCode, $quote, false, true);
        return $json;
    }
    // Set the code get the rule base on validation even if it doesn't validate (false), which will also add it to the session,  then get our response
    $quote->setCouponCode(strlen($couponCode) ? $couponCode : '');
    $rule = $this->_validateCoupon($quote,$couponCode);
    // add coupon code to cookie, so we can delete from quote if the user closes their browser and comes back
    if($rule && !$removed){
        Mage::getModel('core/cookie')->set('coupon_code', $couponCode, 0, '/', null, null, null, false);
    }else{
       Mage::getModel('core/cookie')->delete('coupon_code');
    }
    $json = $this->_getResponseJson($removed, $couponCode, $quote, $rule);

    //See if the quote id is set  before saving
    $quoteId = $quote->getQuoteId();

    //Save the quote since everything has been set if not the data wont be set on page refresh
    $quote->save();

    //Set the quote id if it wasn't set before saving the quote. This makes sure we work off the same quote and a new one isn't created.
    if(empty($quoteId)){
        $this->_setQuoteId($quote);
    }

    return $json;
}

Validating the coupon

 protected function _validateCoupon($quote,$couponCode){
    $store = Mage::app()->getStore($quote->getStoreId());
    $validator = Mage::getModel('package_module/validator');
    $validator->init($store->getWebsiteId(), $quote->getCustomerGroupId(), $quote->getCouponCode());

    return $validator->isValidExternalCode($couponCode, $quote->getShippingAddress(),false);
}

I extended Mage_SalesRule_Model_Validator with my own validator function

 public function isValidExternalCode($couponCode, $address, $setCoupon = true){
    foreach ($this->_getRules() as $rule) {
        if ($rule->getCode() && (in_array(strtolower($couponCode),explode(',',strtolower($rule->getCode()))))) {
            if($setCoupon){
                $address->setCouponCode($couponCode);
            }
            return $rule;
        }
    }
    return false;
}

Here I generate the json response

rotected function _getResponseJson($removed, $couponCode, $quote, $rule = false, $isDup = false){
    $json = '{"Response":{';
    if($removed){
        $json .= '"success":"Promotional code was cancelled successfully."';
        Mage::getSingleton('checkout/session')->setData('coupon_rule',null);
    }
    if(!$removed && $isDup){
        $json .= '"error":"' . $couponCode . ' is already applied"';
    }else if(!$removed && $rule){
        $json .= '"success":"Promotional code ' . $couponCode . ' has been applied",';
        $json .= '"couponMessage":"<span>' . $rule->getName() . '</span>"';
        Mage::getSingleton('checkout/session')->setData('coupon_rule','<span>' . $rule->getName() .'</span>');
    }else if(!$removed){
        $json .= '"error":"' . $couponCode . ' is not valid"';
        $quote->setCouponCode('');
    }
    $json .= '}}';
    return $json;
}

I also had to override the collect method in Mage_SalesRule_Model_Quote_Discount

public function collect(Mage_Sales_Model_Quote_Address $address)
{
    Mage_Sales_Model_Quote_Address_Total_Abstract::collect($address);
    $quote = $address->getQuote();
    $store = Mage::app()->getStore($quote->getStoreId());


    $eventArgs = array(
        'website_id'        => $store->getWebsiteId(),
        'customer_group_id' => $quote->getCustomerGroupId(),
        'coupon_code'       => $quote->getCouponCode(),
    );

    $this->_calculator->init($store->getWebsiteId(), $quote->getCustomerGroupId(), $quote->getCouponCode());

    $items = $address->getAllItems();
    /* EDITS
     * Moved the if statement for no items in cart down past these previous methods and then if the address type is shipping and the coupon is set
     * add the coupon code to the address to allow the validation to still pick up the coupon code
     */
    if($quote->getCouponCode() && ($address->getAddressType() == Mage_Sales_Model_Quote_Address::TYPE_SHIPPING)){
        $address->setCouponCode($quote->getCouponCode());
    }
    if (!count($items)) {
        return $this;
    }

    $address->setDiscountDescription(array());

    foreach ($items as $item) {
        if ($item->getNoDiscount()) {
            $item->setDiscountAmount(0);
            $item->setBaseDiscountAmount(0);
        }
        else {
            /**
             * Child item discount we calculate for parent
             */
            if ($item->getParentItemId()) {
                continue;
            }

            $eventArgs['item'] = $item;
            Mage::dispatchEvent('sales_quote_address_discount_item', $eventArgs);

            if ($item->getHasChildren() && $item->isChildrenCalculated()) {
                foreach ($item->getChildren() as $child) {
                    $this->_calculator->process($child);
                    $eventArgs['item'] = $child;
                    Mage::dispatchEvent('sales_quote_address_discount_item', $eventArgs);
                    $this->_aggregateItemDiscount($child);
                }
            } else {
                $this->_calculator->process($item);
                $this->_aggregateItemDiscount($item);
            }
        }
    }

    /**
     * Process shipping amount discount
     */
    $address->setShippingDiscountAmount(0);
    $address->setBaseShippingDiscountAmount(0);
    if ($address->getShippingAmount()) {
        $this->_calculator->processShippingAmount($address);
        $this->_addAmount(-$address->getShippingDiscountAmount());
        $this->_addBaseAmount(-$address->getBaseShippingDiscountAmount());
    }

    $this->_calculator->prepareDescription($address);
    return $this;
}


This can definitely be achieved. It involves writing a custom module (start here and here) with a controller that accepts the value of your coupon field, initiates a checkout session for that user ($session = Mage::getSingleton('checkout/session')) and stores the coupon code in the checkout session ($session->setData('coupon_code',$coupon).

You would then extend the price model to check for coupon code in the session. You can override Mage_Catalog_Model_Product_Type_Price in your own module using the <rewrite> syntax. Retrieve the coupon code ($couponCode = Mage::getSingleton("checkout/session")->getData("coupon_code");). Note that the Price object is different for Bundle and other non-Simple product types.

If you need more info, I can post code samples.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号