magentolayoutgridcouponadminhtml

Admin grid columns change after mass action or page switch - Magento 1.9


Problem: My new columns to Coupon grid disappear after AJAX pagination or delete calls.

My goal: I want to extend Coupons grid in Shopping Cart Rule edit page by adding some columns.

How to: If you create a shopping cart rule and set "Autogenerate coupons" checkbox to true and save, you will be able to go to Manage Coupons tab afterwards and generate coupons from there for this rule.

My magento store is using coupons that can be used only once, so I need to save a record on when a particular coupon has been used. As magento coupons didn't have that attribute, I added a column date_used in salesrule_coupon table using install script:

<?php
$installer = $this;

$installer->startSetup();

$installer->run("
  ALTER TABLE {$this->getTable('salesrule_coupon')} ADD date_used DATE NULL after created_at;
");

$installer->endSetup();

date_used is created and I am able to see it in database. Next in order to add a new column to coupons grid I do the following:

  1. I extend the Grid block class at code/core/Mage/Adminhtml/Block/Promo/Quote/Edit/Tab/Coupons/Grid.php and create my own code/local/Company/Coupons/Block/Adminhtml/Promo/Quote/Edit/Tab/Coupons/Grid.php

Here is the code:

<?php

class Company_Coupons_Block_Adminhtml_Promo_Quote_Edit_Tab_Coupons_Grid extends Mage_Adminhtml_Block_Promo_Quote_Edit_Tab_Coupons_Grid {
    protected function _prepareColumns() {
        parent::_prepareColumns();

        // adding a column with date_used index
        $this->addColumn('date_used', array(
            'header' => Mage::helper('salesrule')->__('Date Used'),
            'index'  => 'date_used',
            'width'  => '50',
            'type'   => 'date',
        ));
        return $this;
    }
}
  1. Create a layout file coupons.xml and place it in design design/adminhtml/company/default/layout/coupons.xml. In this layout I am adding my own coupongrid block as company_coupons/adminhtml_promo_quote_edit_tab_coupons_grid and removing magento's promo_quote_edit_tab_coupons_grid. As you can see, everything happens inside adminhtml_promo_quote_edit block.

Layout file:

<!-- adding own grid block and removing magento core grid from this block-->
<layout>
    <adminhtml_promo_quote_edit>
        <reference name="promo_quote_edit_tab_coupons">
            <block type="company_coupons/adminhtml_promo_quote_edit_tab_coupons_grid" name="coupongrid" after="promo_quote_edit_tab_coupons_grid"/>
            <remove name="promo_quote_edit_tab_coupons_grid"/>
        </reference>
    </adminhtml_promo_quote_edit>
</layout>
  1. Finally, I register these changes in my etc/config.xml file.

Config file:

<?xml version="1.0"?>
<config>
    <modules>
        <Company_Coupons>
            <version>0.1.2</version>
        </Company_Coupons>
    </modules>
    <global>
        <helpers>
            <company_coupons>
                <class>Company_Coupons_Helper</class>
            </company_coupons>
        </helpers>
        <blocks>
            <company_coupons>
                <class>Company_Coupons_Block</class>
            </company_coupons>
        </blocks>
        <models>
            <company_coupons>
                <class>Company_Coupons_Model</class>
            </company_coupons>
        </models>
        <resources>
            <coupons_setup>
                <setup>
                    <module>Company_Coupons</module>
                </setup>
                <connection>
                    <use>core_setup</use>
                </connection>
            </coupons_setup>
            <coupons_write>
                <connection>
                    <use>core_write</use>
                </connection>
            </coupons_write>
            <coupons_read>
                <connection>
                    <use>core_read</use>
                </connection>
            </coupons_read>
        </resources>
        <events>
            <sales_order_place_after>
                <observers>
                    <coupons>
                        <class>company_coupons/observer</class>
                        <method>saveCouponUsageInfo</method>
                    </coupons>
                </observers>
            </sales_order_place_after>
        </events>
    </global>
    <adminhtml>
        <layout>
            <updates>
                <coupons>
                    <file>coupons.xml</file>
                </coupons>
            </updates>
        </layout>
    </adminhtml>
</config> 
  1. I also added saveCouponUsageInfo(Varien_Event_Observer $observer) method in Model/Observer.php, but I won't include that as it works just fine.

My solution works and I can clearly see the new column in my grid: Coupon grid in Rule Edit page, Manage Coupons tab

BUT! If I do ajax operations, like request the second page of grid, or mass delete, my column just disappears and grid contains its old set of columns. Of course, if I reload the entire page, date_used column comes back to grid. See below (image is cropped, 20 coupons are present on page 2):

enter image description here


I used the debugger in PhpStorm and noticed that coupon grid html, after mass delete, is requested by:

/index.php/admin/promo_quote/couponsGrid/id/64/key/1eccc1c40033fa8bf64007738a22dc1a?ajax=true&isAjax=true

Same request used for pagination, but adding page number into URL:

/index.php/admin/promo_quote/couponsGrid/id/64/key/1eccc1c40033fa8bf64007738a22dc1a/page/2/?ajax=true&isAjax=true

As you probably have guessed from URL, during those calls couponsGridAction() method in Mage_Adminhtml_Promo_QuoteController is triggered. The problem is that my implementation of _prepareColumns() is never called, as debugger revealed. Thus date_used column isn't included anymore.

Anyone faced this problem before? I spent an entire day on this little bug and would be extremely grateful for any hint or guidance. Thanks.


Solution

  • If you look at the controller action executed, i.e. Mage_Adminhtml_Promo_QuoteController::couponsGridAction() you'll see that it does the usual $this->loadLayout()->renderLayout();, which means that there is a layout defined for this AJAX action.
    Open app/design/adminhtml/default/default/layout/promo.xml and at the bottom you'll see the layout defined for this particular action:

        <adminhtml_promo_quote_couponsgrid>
            <block type="core/text_list" name="root" output="toHtml">
                <block type="adminhtml/promo_quote_edit_tab_coupons_grid" name="promo_quote_edit_tab_coupons_grid" />
            </block>
        </adminhtml_promo_quote_couponsgrid>
    

    So you need to define a handle for this action in your layout coupons.xml file, something like this:

        <adminhtml_promo_quote_couponsgrid>
            <block type="core/text_list" name="root" output="toHtml">
                <block type="company_coupons/adminhtml_promo_quote_edit_tab_coupons_grid" name="promo_quote_edit_tab_coupons_grid" />
            </block>
        </adminhtml_promo_quote_couponsgrid>
    

    As you can see, I simply changed adminhtml with company_coupons in the block's type declaration and updated the path to your module's class, and it actually work (as your question was very well documented I actually could reproduce it here in local and this code is actually tested), as simple as that :)