title: How to add Custom Column in sales/order grid in Magento?
body: <p>I am trying to add custom columns to my sales/order grid. My columns will be the tracking number and the tile. The title is basically the courier code which shows you through which courier you have send you product.
   So i have done some following things for this.</p>
  
  <ol>
  <li>I have copied the file from <code>magento/app/code/core/Mage/Adminhtml/Block/Sales/Order/Grid.php</code> to</li>
  </ol>
  
  <p>magento/app/code/local/Mage/Adminhtml/Block/Sales/Order/Grid.php</p>
  
  <p>so that i can add columns and customize my sales/order grid.</p>
  
  <ol start="2">
  <li>In the file Grid.php, There is _prepareCollections() functions.</li>
  </ol>
  
  <p>Here the code goes into it. </p>
  
  <pre><code>protected function _prepareCollection()
      {
          $collection = Mage::getResourceModel($this-&gt;_getCollectionClass());
          $this-&gt;setCollection($collection);
          $collection-&gt;getSelect();
          $collection-&gt;getSelect()-&gt;join('sales_flat_shipment_track', 'main_table.entity_id =sales_flat_shipment_track.order_id',array('track_number'=&gt; new Zend_Db_Expr('group_concat(sales_flat_shipment_track.track_number SEPARATOR ",")'),'title' =&gt; new Zend_Db_Expr('group_concat(sales_flat_shipment_track.title SEPARATOR ",")')));
          return parent::_prepareCollection();    
      }
  </code></pre>
  
  <p>And Now i will add my columns here in my _prepareColumns() function. Code for that is</p>
  
  <pre><code>protected function _prepareColumns()
      {
          $this-&gt;addColumn('track_number', array(
              'header'=&gt; Mage::helper('sales')-&gt;__(' Track Number'),
              'width' =&gt; '80px',
              'type'  =&gt; 'text',
              'index' =&gt; 'track_number',
              ));
          $this-&gt;addColumn('title', array(
  
              'header'=&gt; Mage::helper('sales')-&gt;__('Title'),
              'width' =&gt; '80px',
              'index' =&gt; 'title',
              ));
  </code></pre>
  
  <p>The file Grid.php is here.</p>
  
  <pre><code>&lt;?php
  /**
   * Magento
   *
   * NOTICE OF LICENSE
   *
   * This source file is subject to the Open Software License (OSL 3.0)
   * that is bundled with this package in the file LICENSE.txt.
   * It is also available through the world-wide-web at this URL:
   * http://opensource.org/licenses/osl-3.0.php
   * If you did not receive a copy of the license and are unable to
   * obtain it through the world-wide-web, please send an email
   * to license@magentocommerce.com so we can send you a copy immediately.
   *
   * DISCLAIMER
   *
   * Do not edit or add to this file if you wish to upgrade Magento to newer
   * versions in the future. If you wish to customize Magento for your
   * needs please refer to http://www.magentocommerce.com for more information.
   *
   * @category    Mage
   * @package     Mage_Adminhtml
   * @copyright   Copyright (c) 2014 Magento Inc. (http://www.magentocommerce.com)
   * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
   */
  
  /**
   * Adminhtml sales orders grid
   *
   * @category   Mage
   * @package    Mage_Adminhtml
   * @author      Magento Core Team &lt;core@magentocommerce.com&gt;
   */
  class Mage_Adminhtml_Block_Sales_Order_Grid extends Mage_Adminhtml_Block_Widget_Grid
  {
  
      public function __construct()
      {
          parent::__construct();
          $this-&gt;setId('sales_order_grid');
          $this-&gt;setUseAjax(true);
          $this-&gt;setDefaultSort('created_at');
          $this-&gt;setDefaultDir('DESC');
          $this-&gt;setSaveParametersInSession(true);
      }
  
      /**
       * Retrieve collection class
       *
       * @return string
       */
      protected function _getCollectionClass()
      {
          return 'sales/order_grid_collection';
      }
  
      protected function _prepareCollection()
      {
          $collection = Mage::getResourceModel($this-&gt;_getCollectionClass());
          $this-&gt;setCollection($collection);
          $collection-&gt;getSelect();
          $collection-&gt;getSelect()-&gt;join('sales_flat_shipment_track', 'main_table.entity_id =sales_flat_shipment_track.order_id',array('track_number'=&gt; new Zend_Db_Expr('group_concat(sales_flat_shipment_track.track_number SEPARATOR ",")'),'title' =&gt; new Zend_Db_Expr('group_concat(sales_flat_shipment_track.title SEPARATOR ",")')));
          return parent::_prepareCollection();    
      }
  
      protected function _prepareColumns()
      {
          $this-&gt;addColumn('track_number', array(
              'header'=&gt; Mage::helper('sales')-&gt;__(' Track Number'),
              'width' =&gt; '80px',
              'type'  =&gt; 'text',
              'index' =&gt; 'track_number',
              ));
          $this-&gt;addColumn('title', array(
  
              'header'=&gt; Mage::helper('sales')-&gt;__('Title'),
              'width' =&gt; '80px',
              'index' =&gt; 'title',
              ));
  
          $this-&gt;addColumn('real_order_id', array(
              'header'=&gt; Mage::helper('sales')-&gt;__('Order #'),
              'width' =&gt; '80px',
              'type'  =&gt; 'text',
              'index' =&gt; 'increment_id',
          ));
  
          if (!Mage::app()-&gt;isSingleStoreMode()) {
              $this-&gt;addColumn('store_id', array(
                  'header'    =&gt; Mage::helper('sales')-&gt;__('Purchased From (Store)'),
                  'index'     =&gt; 'store_id',
                  'type'      =&gt; 'store',
                  'store_view'=&gt; true,
                  'display_deleted' =&gt; true,
              ));
          }
  
          $this-&gt;addColumn('created_at', array(
              'header' =&gt; Mage::helper('sales')-&gt;__('Purchased On'),
              'index' =&gt; 'created_at',
              'type' =&gt; 'datetime',
              'width' =&gt; '100px',
          ));
  
          $this-&gt;addColumn('billing_name', array(
              'header' =&gt; Mage::helper('sales')-&gt;__('Bill to Name'),
              'index' =&gt; 'billing_name',
          ));
  
          $this-&gt;addColumn('shipping_name', array(
              'header' =&gt; Mage::helper('sales')-&gt;__('Ship to Name'),
              'index' =&gt; 'shipping_name',
          ));
  
          $this-&gt;addColumn('base_grand_total', array(
              'header' =&gt; Mage::helper('sales')-&gt;__('G.T. (Base)'),
              'index' =&gt; 'base_grand_total',
              'type'  =&gt; 'currency',
              'currency' =&gt; 'base_currency_code',
          ));
  
          $this-&gt;addColumn('grand_total', array(
              'header' =&gt; Mage::helper('sales')-&gt;__('G.T. (Purchased)'),
              'index' =&gt; 'grand_total',
              'type'  =&gt; 'currency',
              'currency' =&gt; 'order_currency_code',
          ));
  
          $this-&gt;addColumn('status', array(
              'header' =&gt; Mage::helper('sales')-&gt;__('Status'),
              'index' =&gt; 'status',
              'type'  =&gt; 'options',
              'width' =&gt; '70px',
              'options' =&gt; Mage::getSingleton('sales/order_config')-&gt;getStatuses(),
          ));
  
          if (Mage::getSingleton('admin/session')-&gt;isAllowed('sales/order/actions/view')) {
              $this-&gt;addColumn('action',
                  array(
                      'header'    =&gt; Mage::helper('sales')-&gt;__('Action'),
                      'width'     =&gt; '50px',
                      'type'      =&gt; 'action',
                      'getter'     =&gt; 'getId',
                      'actions'   =&gt; array(
                          array(
                              'caption' =&gt; Mage::helper('sales')-&gt;__('View'),
                              'url'     =&gt; array('base'=&gt;'*/sales_order/view'),
                              'field'   =&gt; 'order_id',
                              'data-column' =&gt; 'action',
                          )
                      ),
                      'filter'    =&gt; false,
                      'sortable'  =&gt; false,
                      'index'     =&gt; 'stores',
                      'is_system' =&gt; true,
              ));
          }
          $this-&gt;addRssList('rss/order/new', Mage::helper('sales')-&gt;__('New Order RSS'));
  
          $this-&gt;addExportType('*/*/exportCsv', Mage::helper('sales')-&gt;__('CSV'));
          $this-&gt;addExportType('*/*/exportExcel', Mage::helper('sales')-&gt;__('Excel XML'));
  
          return parent::_prepareColumns();
      }
  
      protected function _prepareMassaction()
      {
          $this-&gt;setMassactionIdField('entity_id');
          $this-&gt;getMassactionBlock()-&gt;setFormFieldName('order_ids');
          $this-&gt;getMassactionBlock()-&gt;setUseSelectAll(false);
  
          if (Mage::getSingleton('admin/session')-&gt;isAllowed('sales/order/actions/cancel')) {
              $this-&gt;getMassactionBlock()-&gt;addItem('cancel_order', array(
                   'label'=&gt; Mage::helper('sales')-&gt;__('Cancel'),
                   'url'  =&gt; $this-&gt;getUrl('*/sales_order/massCancel'),
              ));
          }
  
          if (Mage::getSingleton('admin/session')-&gt;isAllowed('sales/order/actions/hold')) {
              $this-&gt;getMassactionBlock()-&gt;addItem('hold_order', array(
                   'label'=&gt; Mage::helper('sales')-&gt;__('Hold'),
                   'url'  =&gt; $this-&gt;getUrl('*/sales_order/massHold'),
              ));
          }
  
          if (Mage::getSingleton('admin/session')-&gt;isAllowed('sales/order/actions/unhold')) {
              $this-&gt;getMassactionBlock()-&gt;addItem('unhold_order', array(
                   'label'=&gt; Mage::helper('sales')-&gt;__('Unhold'),
                   'url'  =&gt; $this-&gt;getUrl('*/sales_order/massUnhold'),
              ));
          }
  
          $this-&gt;getMassactionBlock()-&gt;addItem('pdfinvoices_order', array(
               'label'=&gt; Mage::helper('sales')-&gt;__('Print Invoices'),
               'url'  =&gt; $this-&gt;getUrl('*/sales_order/pdfinvoices'),
          ));
  
          $this-&gt;getMassactionBlock()-&gt;addItem('pdfshipments_order', array(
               'label'=&gt; Mage::helper('sales')-&gt;__('Print Packingslips'),
               'url'  =&gt; $this-&gt;getUrl('*/sales_order/pdfshipments'),
          ));
  
          $this-&gt;getMassactionBlock()-&gt;addItem('pdfcreditmemos_order', array(
               'label'=&gt; Mage::helper('sales')-&gt;__('Print Credit Memos'),
               'url'  =&gt; $this-&gt;getUrl('*/sales_order/pdfcreditmemos'),
          ));
  
          $this-&gt;getMassactionBlock()-&gt;addItem('pdfdocs_order', array(
               'label'=&gt; Mage::helper('sales')-&gt;__('Print All'),
               'url'  =&gt; $this-&gt;getUrl('*/sales_order/pdfdocs'),
          ));
  
          $this-&gt;getMassactionBlock()-&gt;addItem('print_shipping_label', array(
               'label'=&gt; Mage::helper('sales')-&gt;__('Print Shipping Labels'),
               'url'  =&gt; $this-&gt;getUrl('*/sales_order_shipment/massPrintShippingLabel'),
          ));
  
          return $this;
      }
  
      public function getRowUrl($row)
      {
          if (Mage::getSingleton('admin/session')-&gt;isAllowed('sales/order/actions/view')) {
              return $this-&gt;getUrl('*/sales_order/view', array('order_id' =&gt; $row-&gt;getId()));
          }
          return false;
      }
  
      public function getGridUrl()
      {
          return $this-&gt;getUrl('*/*/grid', array('_current'=&gt;true));
      }
  
  }
  </code></pre>
  
  <p>Now I am getting the columns Tracking Number and Title on my Grid. But in my tracking number ,its showing me the duplicate entries. 
  For example, If my tracking number is 12345678, It shows me two values which are same, Like this. 12345678,12345678. </p>
  
  <p>Also in my title, It shows Federal Express,Federal express for two times. </p>
  
  <p>What i want is two things, </p>
  
  <p>a. First There can be 2 tracking nos, or more than that. But it should be shown like this. ex. 12345678,12345678900. It should be distinct. </p>
  
  <p>For some of my order no it shows distinct. But most of them have duplicate entries.</p>
  
  <p>b. Secondly if the courier is send by federal express and then if the product returns and we send by bluedart
  then it should show federal express, bluedart . 
  But what i am getting is federal express,federal express, blue dart,blue dart. </p>
  
  <p>It is showing me 4 times. </p>
  
  <p>I don't know what exactly problem i am facing. Is der any database problem or the query i have written. </p>
  
  <p>Please let me know whether  </p>
  
  <pre><code>protected function _prepareCollection()
          {
              $collection = Mage::getResourceModel($this-&gt;_getCollectionClass());
              $this-&gt;setCollection($collection);
              $collection-&gt;getSelect();
              $collection-&gt;getSelect()-&gt;join('sales_flat_shipment_track', 'main_table.entity_id =sales_flat_shipment_track.order_id',array('track_number'=&gt; new Zend_Db_Expr('group_concat(sales_flat_shipment_track.track_number SEPARATOR ",")'),'title' =&gt; new Zend_Db_Expr('group_concat(sales_flat_shipment_track.title SEPARATOR ",")')));
              return parent::_prepareCollection();    
          }
  </code></pre>
  
  <p>This function is correct and also the query written in it. </p>
  
  <p><strong>Edited PArt</strong></p>
  
  <p>I also want to filter the track_number and title in my grid.php. I have tried some of the things. </p>
  
  <p>This code goes in grid.php, </p>
  
  <pre><code>protected function spaceSeparatedFilter($collection, $column)
  {
      $value = $column-&gt;getFilter()-&gt;getValue();
      if (!$value) {
  
          return $this;
      }
      //if there was a space input
          else if(preg_match('/s+/', $value))
      {
          //explode by space, getting array of IDs
          $val = explode(" ", $value);
          //filter the collection, where collection index (order_id) is present in $val array
          $this-&gt;getCollection()-&gt;addAttributeToFilter($column-&gt;getData('index'), array('in'=&gt;$val));
      }
      else
      {
      //else use default grid filter functionality (like $value input)
      $this-&gt;getCollection()-&gt;addAttributeToFilter($column-&gt;getData('index'), array('like' =&gt; '%'.$value.'%'));
      }
      return $this;
  }
  </code></pre>
  
  <p>Now i have added filter condition to my addcolumn,</p>
  
  <pre><code>$this-&gt;addColumn('track_number', array(
              'header'=&gt; Mage::helper('sales')-&gt;__(' Track Number'),
              'width' =&gt; '80px',
              'type'  =&gt; 'text',
              'index' =&gt; 'track_number',
              'filter_condition_callback' =&gt; array($this, 'spaceSeparatedFilter'),
              ));
  
          $this-&gt;addColumn('title', array(
              'header'=&gt; Mage::helper('sales')-&gt;__('Title'),
              'width' =&gt; '80px',
              'index' =&gt; 'title',
              'filter_condition_callback' =&gt; array($this, 'spaceSeparatedFilter'),
              ));
  </code></pre>
  
  <p>But i  am not able to filter in my sales/order grid. Please let me know the solution for this.</p>
  
  <p>Thanks and Regards.</p>