Tuesday, June 28, 2011

Replicate Category Tree from one Magento to Other Magento (Import category in Magento)

Recently I was working on Magento migration where I have to migrate magento shop from one server to other and create an identical shop on other server. Here time was the concern. So instead of creating categories manually I decided to create to replicate whole category tree with same category ids on other server. As next turn was to import product and keep in the same category. I used Magento API to get category tree from old Magento and traversed through it to create categories in new Magento. Check the following code.

I used recursive function to traverse the whole tree.


$flag = 1;
$client=new SoapClient('http://www.oldmagentodomain.com/api/soap/?wsdl');
$sessionId = $client->login('myAPIUserName','myApiKey');

$categoryTree= $client->call($sessionId, 'category.tree');

$currentCategory = $categoryTree['children'];


importCategory($currentCategory,$client); //This starts the import.



function importCategory($currentCategory,$client)
{

      $count= count($currentCategory);
      for($i=0; $i<$count; $i++)
      {
           $categoryId = $currentCategory[$i]['category_id'];
           $categoryInfo = $client->call($sessionId, 'category.info',$id);
   
           if($flag == 1)
           {
               $rootParentCategory = 1;
               $flag = 0;
           }
           else
           {
                $rootCategory = $categoryInfo ['parent_id'];
           }

                             
           $newCategory = new Mage_Catalog_Model_Category();                      
           $newCategory ->setName($categoryInfo ['name']);
           $newCategory ->setUrlKey($categoryInfo ['url_key']);
           $newCategory ->setLevel($categoryInfo ['level']);
           $newCategory ->setIsActive($categoryInfo ['is_active']);
           $newCategory ->setIsAnchor($categoryInfo ['is_anchor']);
           $newCategory ->setId($categoryInfo ['category_id']);

                               
           $parentCategory = Mage::getModel('catalog/category')->load($rootCategory);
           if($parentCategory)
           {
                $newCategory ->setPath($parentCategory->getPath()."/");                              
           }
            else
           {      
                 $newCategory ->setPath('');                                
            }

           $newCategory ->save(); 

           $childCategories = $currentCategory[$i]['children'];

           if($childCategories)
           {
               importCategory($childCategories,$client);
           }



     }
}

I hope this will help you. Now next post would be about importing products via Magento API. Stay tuned.

Thanks.

Saturday, June 25, 2011

Small Tips for ExtJs Programming

Hello,

Here I am sharing some tips that would be useful for you while working with ExtJs. These tips are about issues I faced while working on ExtJs projects.

1) Get-Set ExtJs Panel HTML

I don't know how many of you faced this issue. While working on one project, requirement was to set panel HTML dynamically. If you check Panel class there is not any method like getHtml() setHtml(). Following is the solution for it.

var dynamicHtml = '<div> This is dynamic HTML</div>';
myPanelObj.body.dom.innerHTML = dynamicHtml;

This will set panel HTML. Same way you can get panel HTML using myPanelObj.body.dom.innerHTML.

2) ExtJs Window me.Events are undefined error

If you are extending ExtJs window and on creating object of extended control, if you get above error then you forgot to call base class constructor. Call the base class constructor using following code.

ExtendedWindowObject.superclass.constructor.call(this, config);

3) Set padding while using ExtJs panel table layout.

When you use table layout in ExtJs panel layout. All the controls are arranged in a table. Sometimes it does not look good if you don't give padding to it. It looks very clumsy. So to give padding give id to panel and create a class in CSS with name of that id. See the example below.


#myPanelObj td{
    padding:5px;
},

4) Use renderer to format data in ExtJs grid cell.

If you want to format your data in grid cell use renderer. For example if you are displaying currency in ExtJs grid and you want to append $ sign to it. 

renderer: function(value, cell, record, rowIndex, colIndex, store)
{
          return value + '$';
}

5) Dynamically add event to ExtJs controls. 

Sometimes it may happen that you have to sue some ExtJs controls and you are not allowed to change the control and you need to add some listeners. Following is the code to add listeners dynamically.

myExtJsObj.on('hide',function(){ 
//function body.
});

6) Add Panel Click event

ExtJs panel does not support click event directly. However using following code you can make clickable panel.

listeners: {
                    render: function(p) {                                                                            
                                          p.getEl().on('click', handleProductPanelClick.createDelegate(null, p, true,this.id));
                     }
}

7) Another use of renderer to give style.

You can also use renderer  to give some styles to cells.

renderer: function(value, metaData, r) { 
                  metaData.attr = ' style="color:red;" ';
                  return value;
}

This will assign red color to cell.

That's it for now. I will update this post whenever I will have some more tips.

Monday, June 20, 2011

ExtJs Grid Paging - Go To Last Page After Inserting New Record

Hello,

Recently I was working on a ExtJs editor grid where user can enter new row by pressing button in the top toolbar. A row is inserted at bottom of grid and user can insert data in row. When user tab out or click on Save, the data goes to database and grid store reloaded. See the image below.


Now here is the issue. Consider grid page size as 25 and number of records are 35 and you are on page 1. That means you are viewing record 1 to 25. Now when you insert the row and when grid is refreshed, newly inserted record is in page 2 and you are still on page 1 so sometimes user feels that data is not inserted. So how to overcome this. I will describe solution that I have used. There may be other better solution. If you have any suggestion please add a comment.

First you need to add afteredit event in you grid. See the code below.


listeners:{
            afteredit:function(e){                       
                         Ext.Ajax.request({
                                    url : 'http://www.example.com/insertData.php',
                                    method:'GET',
                                    params:{                                 
                                        id : e.record.id,
                                        value : e.value
                                    },
                                    success:function(resp,opt){                                     
                                    },
                                    failure:function(resp,opt){
                                       
                                    }
                                });
             }
}


When you tab out of editor or Click on Save this event will be fired and it will send data to database using ajax request. Now we all know that for paging in MySql we have to use start and limit. For example.

select * from myTable limit 0,25

Above query will give first 25 records. to fetch next page use following query

select * from myTable limit 25,25

here first 25 indicates first row number of second page. So my trick was to return first row index of last page of table in which new record is inserted. Check the following code.


$start = 25;
$result = $start;


$output[ 'success' ] = true;
$output[ 'result' ] = $result; 
echo json_encode($output);

Above code will return JSON data back to browser. Now use that to reload the store. Following code will come in Success methods of Ajax Request mentioned in above code.


var returnValue= eval('(' + resp.responseText + ')');                                  
var start = returnValue.result;
if(start){
              myGrid.store.baseParams.start = start;                                      
}
myGrid.getView().refresh();
myGrid.store.reload();

ExtJs grid store uses start and limit parameter along with paging toolbar. So trick was to set start in base parameters of grid store and reload the grid. This will lead you to the last page in grid after inserting new record so user can see newly added record.


Saturday, June 11, 2011

ExtJs Dynamic Script Loading

Hello,

When we are working with ExtJs or other JavaScript framework, we regularly face problem of dynamic script loading. If you have may JavaScript files and you don't want to load all the files at the same time. Sometimes on particular user action we want to load JavaScript files.  When you are working in multilingual sites, you want to load only user selected language file. (How to build multilingual site using JavaScript? Refer to my earlier post for it click here )

So how to load JavaScript files as per  demand? We can use ScriptLoader for that. This is a component, which you can use in any of your web application. You just need to attach component JavaScript file to your app and then you can use it.

You can check this file on Google code. Download the ScriptLoader

Download this file and attach it to your web app.

<script type="text/javascript" src="ScriptLoader.js"/>

When this file is loaded it will create object of ScriptLoader.  Check the last line if JS file.

Ext.ux.ScriptMgr = new Ext.ux.ScriptLoaderMgr();

Now you can use above object to download any JavaScript file you want.

Ext.ux.ScriptMgr.ScriptMgr.load({
                             scripts : [
                                             'ExampleFile1.js',
                                             'ExampleFile2.js',
                                             'ExampleFile3.js'
                                         ],
                             callback : function() {
                                                     
                                            }
});

You can give any number of files you want in array. Callback function will be invloked when all the files are downloaded successfully. Remember that this is an asynchronous process, hence callback function is necessary if you want to trigger some events on successful download.

ScriptLoder can be used to dynamically load CSS files as well.

Saturday, June 4, 2011

Update Completed Order Details in Magento

Hello,

Recently I faced a very tough challenge because of stupid mistake made by one of my team member. We were importing orders in Magento through custom script. While importing orders, one supposed to add parent configurable product and a product option. Instead of that he added all the child products and because of that all the orders and its invoices were created with zero price. It was very frustrating situation for me as total number of such orders were around 1,35,0000. So I have to update all those orders. After spending almost a day I came up with a solution and created a PHP script to load that order and update it's details. Following it the code. I will explain all the code segments one by one.


$transaction = Mage::getModel('core/resource_transaction');
$order = Mage::getModel("sales/order")->loadByIncrementId(1000000);
$subTotal = 0.000;
$date=$order->created_at;
$order_id=$order->getId();
 
$arr_sku=array();
$arr_price=array();
$arr_par_sku=array();
$arr_qty=array();


Above code will load an order specified by increment id and get the order created date as we need that date to update some table entries to change data in invoice. Also above array variables are used to hold product prices and skus. Later we need that to change some table entries.


foreach ($order->getItemsCollection() as $orderItem) {    
       
        $sku = $orderItem->getSku();
        $qty = $orderItem->getQtyOrdered();
        $arr_qty[]=$qty;
        $original_sku = $sku;
        $arr_sku[]=$original_sku;
        $product = Mage::getModel('catalog/product');
        $newProd = Mage::getModel('catalog/product');
        $newProd = $newProd->load($newProd->getIdBySku($sku));
                           
        $parentIdArray = $newProd->loadParentProductIds()->getData('parent_product_ids');
       
        $parent_id=$parentIdArray[0];                                      
        $product = $product->load($parent_id);
     
        $attribute_value = "";
        $childProducts = Mage::getModel('catalog/product_type_configurable')->getUsedProducts(null,$product);
        $par_sku=$product->getSku();
        $arr_par_sku[]=$par_sku;
        $price = $product->getPrice();
        $arr_price[]=$price;
        foreach($childProducts as $childProduct)
        {
            if($childProduct->sku == $original_sku){
                $attribute_value = $childProduct->color;                              
                $attribute_text=$childProduct->getAttributeText('color');
             
               $attribute_data=array('info_buyRequest'=>array(
                                                'product'=>$childProduct->getId(),
                                                'super_attribute'=>array(
                                                                          '80'=>$attribute_value,
                                                                            )
                                                ),
                                        'attributes_info'=>array(
                                                               '0'=>array(
                                                                        'label'=>'Color',
                                                                        'value'=>$attribute_text
                                                                            )                                                                    
                                                                ),
                                         'simple_name'=>  $childProduct->getName(),
                                         'simple_sku'=>$childProduct->getSku(),
                                         'product_calculations' =>1,
                                         'shipment_type'=>0
                        );  
                     
                $orderItem->setProductOptions($attribute_data);
                $orderItem->setProductId($product->getId());
                $orderItem->setProductType('configurable');
                $orderItem->setQtyOrdered($qty);
                $orderItem->setName($product->getName());
                $orderItem->setSku($product->getSku());
                $orderItem->setPrice($product->getPrice());
                $orderItem->setBasePrice($product->getPrice());
                $orderItem->setOriginalPrice($product->getPrice());
                $orderItem->setRowTotal($product->getPrice());
                $orderItem->setBaseRowTotal($product->getPrice());              
                $subTotal += $product->getPrice();
                break;
            }
        } 
    }


Above code will get each order item which is child product in my case and it replaces that order item with parent product and product option which was color in my case.


$price2=$subTotal+6.95;
    $order->setSubtotal($subTotal)
            ->setBaseSubtotal($subTotal)
            ->setGrandTotal($subTotal)
            ->setBaseGrandTotal($subTotal);
    $transaction->addObject($order);
    $transaction->addCommitCallback(array($order, 'place'));
    $transaction->addCommitCallback(array($order, 'save'));
    $transaction->save();

Above code will save the order. Now if you open order in Magento admin panel. You can see product is changed but if you check order sub total, total paid etc, it will be still zero. Because Magento did not update it automatically. You have to explicitly update it from database. That I will explain you in other post.

I hope this post helps you if you face same situation like me. Do post you suggesstions in comments. Thanks