Welcome Mass Importer Pro: Price Importer Ver 1.1.0 with new features

August 8, 2014  |  1 Comments  |  by Raj (MagePsycho)  |  Latest, Magento, Updates

Mass Importer Pro: Price Importer is the fastest price import tool available for Magento CE/EE.
Recently MagePsycho team released the most awaited features in Mass Importer Pro: Price Importer Ver 1.1.0

ChangeLog (0.1.5 – 1.1.0)

  • Compatible with Magento CE 1.3.x – 1.9.x & EE 1.12.x – 1.14.x
  • Added cron job based price importing with logging feature
  • Added shell/command line based price importing with logging feature
  • Added price rounding feature (normal rounding, rounding to nearest)
  • Added relative(fixed & percentage) pricing for special, tier & group prices.
  • Changed store view based pricing import to website based(as prices have either global or website level scope)
  • Added store wise price export
  • Added option for deleting special_price, special_from_date, special_to_date by marking the value with ‘x’ or ‘X’
  • Added button for checking csv file format (beside of Price Import button)
  • Added Change log file: Changelog.txt
  • Added Uninstallation file: Uninstall.txt
  • Refactored the code

New Features

1. Cron Job based price importing

To configure this, go to Mass Importer Pro > Manage Settings > Price Settings
You will see the following setting:

Price Importer: Cron Settings

Price Importer: Cron Settings

FieldDescription
Enable CronYes
File Import DirectoryPath from where CSV files will be imported via cron.
Default: var/magepsycho/massimporterpro/price_importerr/cron
Process Imported FilesWhat to do after the files are imported?
Available options are:
No Action
Move To Archive Folder
Delete
Start TimeAt what time cron will run.
If ‘Start Time’ = 00 00 00 and ‘Frequency’ = Daily then equivalent cron expression will be 0 0 * * * (which means cron will be run once a day at midnight)
REF: http://en.wikipedia.org/wiki/Cron
FrequencyFrequency at which cron will run. Options:
Daily
Weekly
Monthly
Error Email RecipientError email will be sent to this address in case of failure
Error Email SenderError Email Sender
Error Email TemplateEmail Template

Notes:
Make sure that cron is enabled for your Magento store.
Refer to the following article for more info:
http://www.magentocommerce.com/wiki/1_-_installation_and_configuration/how_to_setup_a_cron_job

2. Shell/Command line based price importing

There is no any backend settings for Shell based price importing.
You just have to open the terminal and run the following commands.
1. Go to Magento root directory:

cd /path/to/your/magento/root

2. Price Importer help command:

php -f shell/priceimporter.php --help

3. Run price import (without reindexing)

php -f shell/priceimporter.php -- -import "path/to/csv/file.csv"

4. Run price import (with reindexing)

php -f shell/priceimporter.php -- -import "path/to/csv/file.csv" -reindex
Command line price import (with reindexing)

Command line price import (with reindexing)

Command line price import (without reindexing)

Command line price import (without reindexing)

So far in Mass Importer Pro, Prices can be imported via Web Forms, Cron Jobs & Command Line. From wherever you run import, You can view the import history from menu ‘Mass Importer Pro’ > ‘Price Importer’ > ‘Import History’ Tab > Filter the results by ‘Imported Via’.

Price Importer: Import History

Price Importer: Import History

3. Price rounding feature

There are three types of options for price rounding:

Price Importer: Price Rounding

Price Importer: Price Rounding

Rounding TypeDescription
No RoundingNo action with the price
Round NormallyUses php’s round() function. Examples:
9.43 -> 9.00
9.63 -> 10.00
Round to NearestYou have to set Rounding value for this. Examples:
9.43 -> 9.00 + (If Rounding Value = 0.99) = 9.99
9.43 -> 9.00 + (If Rounding Value = 0.50) = 9.50
This option is useful if you want to update your prices ending with .99, .50 etc.

4. Relative(fixed & percentage) based price importing

You can self increment or decrement the prices by fixed value or percentage. Also you can set ‘special_price’, ‘tier_price’ & ‘group_price’ to be certain percentage of ‘price’.
Valid Examples:

ValueDescription
10Fixed Value
+10Increment current value by 10
-10Decrement current value by 10
10%10% of base price (note that there is no any sign in front)
Note: This applies only for price types: special_price, tier_price & group_price
+10%Increment current price by 10%
 -10% Decrement current price by 10%

5. Option to validate your CSV data

You can validate the CSV data before importing any prices.
Go to menu ‘Mass Importer Pro’ > ‘Price Importer’ > Select the import file > Click on the ‘Check Import’ button which will give you the check results.

Price Importer: Check Import Data

Price Importer: Check Import Data

6. Website wise price Import

Before Ver 1.1.0 You had to use combination of sku + store_id as unique identifier to update prices. Since Magento has either global or website level scope for prices, we have replaced ‘store_id’ by ‘website_id’.

In order to find the website_id, go to System > Manage Stores > Click on the related Website > Note the website_id from URL.
By default website_id = 0 (global)

7. Website wise price Export

You can export prices in format compatible with Mass Importer Pro from menu ‘Catalog’ > ‘Manage Products’.
How to export prices is clear from the below screenshot:

Price Importer: Export Prices

Price Importer: Export Prices

Also see: Importing regular, special, tier & group prices using ‘Mass Importer Pro: Price Importer’ Extension

Those were the major updates on newer version. Besides we have fixed some bugs & improved the functionality.
If you have any queries/issues regarding the extension, please do not hesitate to Contact Us.

How to set product attribute values to take values from default store?

February 7, 2014  |  No Comments  |  by Raj (MagePsycho)  |  Latest, Magento, Mysql

Have you ever ticked a ‘Use Default Value’ checkbox for Price (in store view level) and noticed the changes in database?

If you tick the ‘Use Default Value’ checkbox for any product attribute (say Price) in store view level and save it. This will delete the related row in the database for that attribute, for the specific product, for that store id. And attribute will now take the default value.

Lets explain this scenario in detail.

1st Approach

Use Default Value - Before Tick

Fig: Before Checkbox Tick

Use Default Value Checkbox

Fig: After Checkbox Tick

So if you want to set the default value for any attribute, you can just tick the checkbox and save the product.
This process can be overhead in case of hundreds of product.

2nd Approach

Alternatively, you can use SQL approach which is actually the background operation.
1 Find entity_type_id

SELECT entity_type_id FROM eav_entity_type WHERE entity_type_code = 'catalog_product';

2 Find attribute_id

SELECT attribute_id FROM eav_attribute WHERE entity_type_id = 10 AND attribute_code = 'price';

Suppose attribute_code = price and entity_type_id = 10
3 Check if row for that attribute exists

SELECT * FROM catalog_product_entity_decimal WHERE entity_type_id = 10 AND attribute_id = 99 AND store_id = 3 AND entity_id = 123;

4 Delete that particular row

DELETE FROM catalog_product_entity_decimal WHERE entity_type_id = 10 AND attribute_id = 99 AND store_id = 3 AND entity_id = 123;

Now try to run SQL #3, will now give you the empty result.

So in general, You can use the following SQL

DELETE FROM {table_name}
WHERE entity_type_id = {entity_type_id}
AND attribute_id = {attribute_id}
AND store_id = {store_id}
AND entity_id = {entity_id}

In case you want to set default value for all the products in that store just remove the ‘AND entity_id = {entity_id}’ filter.

Caution:
In order to save your *** from worst case scenario, just take a backup of your DB.

At last but the not least, must thank @MariusStrajeru for his valuable suggestions and willing to help nature.

Sending shipment email when tracking info is added

January 22, 2014  |  No Comments  |  by Raj (MagePsycho)  |  Latest, Magento

From the topic of this blog post it’s clear that we are going to discuss on ‘How to send shipment email when tracking info is added to the system?’
This may by useful if carrier tracking info is pushed to Magento by some third party system and want to send shipment email.

As usual, instead of rewriting core classes we will be using event-observer pattern.
And events used here will be: sales_order_shipment_save_before & sales_order_shipment_save_after

1. Register the events: sales_order_shipment_save_before & sales_order_shipment_save_after
File: app/code/local/MagePsycho/Shipmentemail/etc/config.xml

...
<global>
    <events>
        <sales_order_shipment_save_before>
            <observers>
                <magepsycho_shipmentemail_sales_order_shipment_save_before>
                    <type>singleton</type>
                    <class>magepsycho_shipmentemail/observer</class>
                    <method>salesOrderShipmentSaveBefore</method>
                </magepsycho_shipmentemail_sales_order_shipment_save_before>
            </observers>
        </sales_order_shipment_save_before>
        <sales_order_shipment_save_after>
            <observers>
                <magepsycho_shipmentemail_sales_order_shipment_save_after>
                    <type>singleton</type>
                    <class>magepsycho_shipmentemail/observer</class>
                    <method>salesOrderShipmentSaveAfter</method>
                </magepsycho_shipmentemail_sales_order_shipment_save_after>
            </observers>
        </sales_order_shipment_save_after>
    </events>
</global>
...

2. Send email using observer model
File: app/code/local/MagePsycho/Shipmentemail/Model/Observer.php

<?php
/**
 * @category   MagePsycho
 * @package    MagePsycho_Shipmentemail
 * @author     magepsycho@gmail.com
 * @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 */
class MagePsycho_Shipmentemail_Model_Observer
{
    protected function _isValidForShipmentEmail($shipment)
    {
        $trackingNumbers = array();
        foreach ($shipment->getAllTracks() as $track) {
            $trackingNumbers[] = $track->getNumber();
        };
        // send shipment email only when carrier tracking info is added
        if (count($trackingNumbers) > 0) {
            return true;
        } else {
            return false;
        }
    }
    
    public function salesOrderShipmentSaveBefore(Varien_Event_Observer $observer)
    {
        if (Mage::registry('salesOrderShipmentSaveBeforeTriggered')) {
            return $this;
        }

        /* @var $shipment Mage_Sales_Model_Order_Shipment */
        $shipment = $observer->getEvent()->getShipment();
        if ($shipment) {
            if ($this->_isValidForShipmentEmail($shipment)) {
                $shipment->setEmailSent(true);
                Mage::register('salesOrderShipmentSaveBeforeTriggered', true);
            }
        }
        return $this;
    }
    
    public function salesOrderShipmentSaveAfter(Varien_Event_Observer $observer)
    {
        if (Mage::registry('salesOrderShipmentSaveAfterTriggered')) {
            return $this;
        }
       
        /* @var $shipment Mage_Sales_Model_Order_Shipment */
        $shipment = $observer->getEvent()->getShipment();
        if ($shipment) {
            if ($this->_isValidForShipmentEmail($shipment)) {
                $shipment->sendEmail();
                Mage::register('salesOrderShipmentSaveAfterTriggered', true);
            }
        }
        return $this;
    }
}

Notes:

  • We have used both events: sales_order_shipment_save_before & sales_order_shipment_save_after. _before is used to set the email_sent flag to true and _after event to send email.
  • If you combine methods: $shipment->setEmailSent(true) and $shipment->sendEmail() in _before event, you will get shipment email but without shipment number.
  • If you combine methods: $shipment->setEmailSent(true) and $shipment->sendEmail() in _after event, it will go in recursive infinite loop sending you the millions of email.

Thanks for reading & sharing.

Product List: Show out of stock products in last

December 18, 2013  |  No Comments  |  by Raj (MagePsycho)  |  Latest, Magento

In this blog post we will be sharing how to show out of stock products at last of product listing page.

Assumption: MagePsycho_Productsort is the skeleton module

Steps:
1. Register event: catalog_product_collection_load_before
File: app/code/local/MagePsycho/Productsort/etc/config.xml

...
<frontend>
    <events>
        <catalog_product_collection_load_before>
            <observers>
                <magepsycho_productsort_catalog_product_collection_load_before>
                    <type>singleton</type>
                    <class>magepsycho_productsort/observer</class>
                    <method>catalogProductCollectionLoadBefore</method>
                </magepsycho_productsort_catalog_product_collection_load_before>
            </observers>
        </catalog_product_collection_load_before>
    </events>
</frontend>
...

2. Implement observer model:
File: app/code/local/MagePsycho/Productsort/Model/Observer.php

<?php
/**
 * @category   MagePsycho
 * @package    MagePsycho_Productsort
 * @author     magepsycho@gmail.com
 * @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 */
class MagePsycho_Productsort_Model_Observer
{

    public function catalogProductCollectionLoadBefore(Varien_Event_Observer $observer)
    {
        $collection = $observer->getCollection();
        $collection->getSelect()->joinLeft(
            array('_inventory_table'=>$collection->getTable('cataloginventory/stock_item')),
            "_inventory_table.product_id = e.entity_id",
            array('is_in_stock', 'manage_stock')
        );
        $collection->addExpressionAttributeToSelect(
            'on_top',
            '(CASE WHEN (((_inventory_table.use_config_manage_stock = 1) AND (_inventory_table.is_in_stock = 1)) OR  ((_inventory_table.use_config_manage_stock = 0) AND (1 - _inventory_table.manage_stock + _inventory_table.is_in_stock >= 1))) THEN 1 ELSE 0 END)',
            array()
        );
        $collection->getSelect()->order('on_top DESC');
        // Make sure on_top is the first order directive
        $order = $collection->getSelect()->getPart('order');
        array_unshift($order, array_pop($order));
        $collection->getSelect()->setPart('order', $order);
    }
}

3. Bingo!
Out of Stock products

Extend Magento sales order grid using event observer

November 17, 2013  |  No Comments  |  by Raj (MagePsycho)  |  Magento

You can add new columns to the Sales Order grid easily by extending the block class: Mage_Adminhtml_Block_Sales_Order_Grid
But this is not the preferred way due to conflict issues.

Here I will show you how to extend order grid by using events(core_block_abstract_to_html_before & sales_order_grid_collection_load_before) only.

For example purpose we will be adding Payment Method to sales order grid and MagePsycho_Gridextend will be the skeleton extension.
1. Register the events:

...
<adminhtml>
    <events>
        <core_block_abstract_to_html_before>
            <observers>
                <magepsycho_gridextend_core_block_abstract_to_html_before>
                    <class>magepsycho_gridextend/observer</class>
                    <method>coreBlockAbstractToHtmlBefore</method>
                </magepsycho_gridextend_core_block_abstract_to_html_before>
            </observers>
        </core_block_abstract_to_html_before>
        <sales_order_grid_collection_load_before>
            <observers>
                <magepsycho_gridextend_sales_order_grid_collection_load_before>
                    <class>magepsycho_gridextend/observer</class>
                    <method>salesOrderGridCollectionLoadBefore</method>
                </magepsycho_gridextend_sales_order_grid_collection_load_before>
            </observers>
        </sales_order_grid_collection_load_before>
    </events>
</adminhtml>
...

2. Create the observer model:

<?php
/**
 * @category   MagePsycho
 * @package    MagePsycho_Gridextend
 * @author     magepsycho@gmail.com
 * @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 */
class MagePsycho_Gridextend_Model_Observer {

    /**
     * Moved to Block class
     * @param Varien_Event_Observer $observer
     */
    public function coreBlockAbstractToHtmlBefore(Varien_Event_Observer $observer)
    {
        /** @var $block Mage_Core_Block_Abstract */
        $block = $observer->getEvent()->getBlock();
        if ($block->getId() == 'sales_order_grid') {
            
            //add new column: payment method
            $paymentArray = Mage::getSingleton('payment/config')->getActiveMethods();
            $paymentMethods = array();
            foreach ($paymentArray as $code => $payment) {
                // not sure why ops_dl was not in the loop so tweaked it
                $paymentTitle = Mage::getStoreConfig('payment/'.$code.'/title');
                $paymentMethods[$code] = $paymentTitle;
            }
            $block->addColumnAfter(
                'payment_method',
                array(
                    'header'   => Mage::helper('sales')->__('Payment Method'),
                    'align'    => 'left',
                    'type'     => 'options',
                    'options'  => $paymentMethods,
                    'index'    => 'payment_method',
                    'filter_index'    => 'payment.method',
                ),
                'shipping_name'
            );
            
            //similary you can addd new columns
            //...

            // Set the new columns order.. otherwise our column would be the last one
            $block->sortColumnsByOrder();
        }
    }

    /**
     * Moved to block class
     * @param Varien_Event_Observer $observer
     */
    public function salesOrderGridCollectionLoadBefore(Varien_Event_Observer $observer)
    {
        $collection = $observer->getOrderGridCollection();
        $select = $collection->getSelect();
        $select->joinLeft(array('payment' => $collection->getTable('sales/order_payment')), 'payment.parent_id=main_table.entity_id',array('payment_method' => 'method'));
    }
}

3. Refresh the Sales > Order page, you will see page similar to:
Sales Order Grid

Similarly, you can extend any admin grid like sales invoice, customer etc.