Pages

Thursday 3 July 2014

Advanced Shipping Method Module Part - 1 in magento


In this tutorial, we are going to see how to create a advanced shipping method with form fields.
For example, you want to show user without no charges of your shipping method. This type of feature is not supported in magneto by default, so we would to edit many files. This tutorial is in continuation of a previous tutorial written, so please go through it before reading this one. The basic files need for creating a shipping method is not explained here since its already done in the previous tutorial.

Now let’s see the steps required to achieve this. Also the name of the module used is Easylife_Pickup.

Step1: Create Basic Shipping Method

This step is basically what we have already seen in the previous tutorial, so you need to add the necessary code in config.xml, system.xml and the Carrier Model.

in system.xml file have below code

<?xml version="1.0"?>
<config>
    <sections>
        <carriers>
            <groups>
                <msmultiflat translate="label" module="shipping">
                    <label>Multiple Flat rates </label>
                    <frontend_type>text</frontend_type>
                    <sort_order>1</sort_order>
                    <show_in_default>1</show_in_default>
                    <show_in_website>1</show_in_website>
                    <show_in_store>1</show_in_store>
                    <fields>
                        <active translate="label">
                            <label>Enabled</label>
                            <frontend_type>select</frontend_type>
                            <source_model>adminhtml/system_config_source_yesno</source_model>
                            <sort_order>1</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </active>
                        <sort_order translate="label">
                            <label>Sort order</label>
                            <frontend_type>text</frontend_type>
                            <sort_order>100</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </sort_order>
                        <title translate="label">
                            <label>Title</label>
                            <frontend_type>text</frontend_type>
                            <sort_order>2</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </title>
                        <name translate="label">
                            <label>Allowed Methods</label>
                            <frontend_type>multiselect</frontend_type>
                            <sort_order>91</sort_order>
                            <source_model>Easylife_Pickup_Model_Shipping_Carrier_Custom_Source_Method</source_model>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </name>
                        <price translate="label">
                            <label>• Price (0.00)</label>
                            <frontend_type>text</frontend_type>
                            <sort_order>5</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </price>
                        <details translate="label">
                            <label>• Details</label>
                            <frontend_type>textarea</frontend_type>
                            <sort_order>6</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </details>
                        <type translate="label">
                            <label>• Type</label>
                            <frontend_type>select</frontend_type>
                            <source_model>adminhtml/system_config_source_shipping_flatrate</source_model>
                            <sort_order>7</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>0</show_in_store>
                        </type>
                        <min_shipping translate="label">
                            <label>• Minimum order amount</label>
                            <frontend_type>text</frontend_type>
                            <sort_order>8</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>0</show_in_store>
                        </min_shipping>
                        <max_shipping translate="label">
                            <label>• Maximum order amount</label>
                            <frontend_type>text</frontend_type>
                            <sort_order>9</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>0</show_in_store>
                        </max_shipping>
                        <sallowspecific translate="label">
                            <label>Ship to applicable countries</label>
                            <frontend_type>select</frontend_type>
                            <sort_order>44</sort_order>
                            <frontend_class>shipping-applicable-country</frontend_class>
                            <source_model>adminhtml/system_config_source_shipping_allspecificcountries</source_model>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </sallowspecific>
                        <specificcountry translate="label">
                            <label>Ship to Specific countries</label>
                            <frontend_type>multiselect</frontend_type>
                            <sort_order>91</sort_order>
                            <source_model>adminhtml/system_config_source_country</source_model>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </specificcountry>
                        <showmethod translate="label">
                            <label>Show method if not applicable</label>
                            <frontend_type>select</frontend_type>
                            <sort_order>92</sort_order>
                            <source_model>adminhtml/system_config_source_yesno</source_model>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </showmethod>
                        <specificerrmsg translate="label">
                            <label>Displayed Error Message</label>
                            <frontend_type>textarea</frontend_type>
                            <sort_order>80</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </specificerrmsg>
                    </fields>
                </msmultiflat>
            </groups>
        </carriers>
    </sections>
</config>


from above code name fields are dynamically loading for this we need to create the below list of shipping methods

<?php
class Easylife_Pickup_Model_Shipping_Carrier_Custom_Source_Method
{

    public function toOptionArray()
    {


          return array(
              array(
                  'label' => 'Major',
                  'value' => array(
                      array('value' => 'test1', 'label' => 'Test 1'),
                      array('value' => 'test2', 'label' => 'Test 2')
                  )
              ),
              array(
                  'label' => 'Major2',
                  'value' => array(
                      array('value' => 'test3', 'label' => 'Test 3'),
                      array('value' => 'test4', 'label' => 'Test 4')
                  )
              )
          );

    }
}

Advanced Shipping Method Module


But in our case we are going to make some changes to the carrier model, so lets see the code

<?php

class Easylife_Pickup_Model_Carrier_Pickup extends Mage_Shipping_Model_Carrier_Abstract implements Mage_Shipping_Model_Carrier_Interface
{
    protected $_code = 'msmultiflat';

    public function getFormBlock(){
        return 'pickup/pickup';
    }

   public function collectRates(Mage_Shipping_Model_Rate_Request $request)
    {

        if (!Mage::getStoreConfig('carriers/'.$this->_code.'/active')) {
            return false;
        }

        $handling = Mage::getStoreConfig('carriers/'.$this->_code.'/handling');
        $result = Mage::getModel('shipping/rate_result');

        $results = $this->getConfigData('name');
        $results_one = split(',',$results);

        $count =  count($results_one);
        $session = Mage::getSingleton("core/session");
        $session->setData('shp_count',$count);

        foreach($results_one as $one){
            switch($one)
            {
                case 'test1' :
                    $name = 'Test 1';
                    $method = Mage::getModel('shipping/rate_result_method');
                    $method->setCarrier($this->_code);
                    $method->setMethod($one);
                    $method->setCarrierTitle('Major');
                    $method->setMethodTitle($name);
                    $method->setPrice($this->getConfigData('price'));
                    $method->setCost($this->getConfigData('price'));
                    $result->append($method);
                    break;
                case 'test2' :
                    $name = Test 2;
                    $method = Mage::getModel('shipping/rate_result_method');
                    $method->setCarrier($this->_code);
                    $method->setMethod($one);
                    $method->setCarrierTitle('Major');
                    $method->setMethodTitle($name);
                    $method->setPrice($this->getConfigData('price'));
                    $method->setCost($this->getConfigData('price'));
                    $result->append($method);
                    break;
                case 'test3' :
                    $name = 'Test 3';
                    $method = Mage::getModel('shipping/rate_result_method');
                    $method->setCarrier($this->_code);
                    $method->setMethod($one);
                    $method->setCarrierTitle('Major 2');
                    $method->setMethodTitle($name);
                    $method->setPrice($this->getConfigData('price'));
                    $method->setCost($this->getConfigData('price'));
                    $result->append($method);
                    break;
                case 'test4' :
                    $name = 'Test 4';
                    $method = Mage::getModel('shipping/rate_result_method');
                    $method->setCarrier($this->_code);
                    $method->setMethod($one);
                    $method->setCarrierTitle('Major 2');
                    $method->setMethodTitle($name);
                    $method->setPrice($this->getConfigData('price'));
                    $method->setCost($this->getConfigData('price'));
                    $result->append($method);
                    break;
            }


        }

        return $result;
    }

    public function getAllowedMethods()
    {
        return array('msmultiflat'=>$this->getConfigData('name'));
    }

}

as you can see I have added a new function called getFormBlock() which we will use later.

Step2
We need to make changes to the shipping html, so that we are able to display our form. For this we need to edit the checkout\onepage\shipping_method\available.phtml file. So we will override this pthml file. In your module’s layout file pickup.xml put in this code

<checkout_onepage_index>
        <reference name='head'>
            <reference name="head">
                <action method="addItem"><type>js</type><name>pickup/jquery-1.6.4.min.js</name></action>
                <action method="addItem"><type>js</type><name>pickup/noconflict.js</name></action>
            </reference>
        </reference>
        <reference name='checkout.onepage.shipping_method.available'>
            <action method='setTemplate'><template>pickup/checkout/onepage/shipping_method/available.phtml</template></action>
        </reference>
    </checkout_onepage_index>
    <checkout_onepage_shippingmethod>
        <reference name='root'>
            <action method='setTemplate'><template>pickup/checkout/onepage/shipping_method/available.phtml</template></action>
        </reference>
    </checkout_onepage_shippingmethod>


Here we have changed the template path of the shipping_method/avaiable.phtml file to our module.

Also we have added jquery to the checkout page.
Next the we need to add code to the available.phtml file to show our form. We will place this after the <label> tag get’s finished.
from starting on wards add below code
<?php
$a=0;
$b=0;
$c=0;
$d=0;
$isValue = false;
?>


and after around 40th line add below code

<?php
                                $method_name = $_rate->getMethodTitle();
                                if($method_name == "Test 1" || $method_name == "Test 2"){
                                    if($a == 0  ){
                                        echo $this->escapeHtml('Major') ;
                                        $a++;
                                    }
                                    $c++;
                                    $isValue=true;
                                    echo '<p/>';
                                }
                                if($method_name == "Test 3" || $method_name == "Test 4"){
                                    if($b == 0 ){
                                        echo $this->escapeHtml('Major 2') ;
                                        $b++;
                                    }
                                    echo '<p/>';
                                    $c++;
                                    $isValue=true;
                                }

                                ?>


after 83rd line add below code

<?php
                                $session = Mage::getSingleton("core/session");
                               $shp_method_count = $session->getData('shp_count');

                                ?>
                                <?php if(($isValue == true) && ($_rate->getCode() == "msmultiflat_test1"
                                        || $_rate->getCode() == "msmultiflat_test2" || $_rate->getCode() == "msmultiflat_test3"
                                        || $_rate->getCode() == "msmultiflat_test4"
                                    ) ){ ?>
                                    <ul class="form-list" id="shipping_form_<?php echo $_rate->getCode() ?>" >
                                        <li>
                                            <label for="store" class="required"><em>*</em>
                                                <?php echo $this->__('Account Number') ?></label>
                                            <span class="input-box">
                                                <input class="required-entry"  type="text" name="custom[account]"
                                                        value="<?php echo $this->htmlEscape($this->getQuote()->getThirdpartyAccountNo()) ?>"
                                                        title="<?php echo $this->__('Account No') ?>"
                                                        id="billing:account" />
                                            </span>
                                                                        <label for="_store" class="required">
                                                                            <?php echo $this->__("Phone Number") ?></label>
                                            <span class="input-box">
                                                <input  type="text" name="custom[phone]"
                                                        value="<?php echo $this->htmlEscape($this->getQuote()->getThirdpartyPhoneNo()) ?>"
                                                        title="<?php echo $this->__('Phone No') ?>"
                                                        id="billing:phone" />
                                            </span>
                                                                    </li>
                                                                    <li>
                                                                        <label for="store" class="required">
                                                                            <?php echo $this->__('Special Instruction') ?></label>
                                            <span class="input-box">
                                                <textarea  name="custom[comment]"
                                                           value="<?php echo $this->htmlEscape($this->getQuote()->getThirdpartyComment()) ?>"
                                                           title="<?php echo $this->__('Comment') ?>"
                                                           id="billing:comment"></textarea>
                                            </span>
                                        </li>
                                    </ul>
                                    <?php $d++;$isValue=false; ?>
                                <?php  } ?>


Here as you can see I have added two form fields, one is a drop down and other is a text field. Also by default the style has been set to display:none so this form will not show up, so we need to add the javascript code to make the shipping method form hide/show on click on the shipping methods.

Step 3
Here is the javascript code that we need to add to the available.phtml file


  jQuery(document).ready(function(){
            hideShippingAll();
            jQuery('input[type="radio"][name="shipping_method"]').click(function(){
                hideShippingAll();
                var code = jQuery(this).val();
                if(jQuery(this).is(':checked')){
                    showShipping(code);
                }
            });
            jQuery('input[type="radio"][name="shipping_method"]').each(function(){
                var code = jQuery(this).val();
                if(jQuery(this).is(":checked")){
                    showShipping(code);
                }
            });
        });
        function showShipping(code){
            if(jQuery('#'+'shipping_form_'+code).length != 0){
                jQuery('#'+'shipping_form_'+code).show();
                jQuery(this).find('.required-entry').attr('disabled','false');
            }
        }
        function hideShippingAll(){
            jQuery('input[type="radio"][name="shipping_method"]').each(function(){
                var code = jQuery(this).val();
                jQuery('#'+'shipping_form_'+code).hide();
                jQuery(this).find('.required-entry').attr('disabled','true');
            });
        }


Advanced Shipping Method Module


Step 4
Now we need to save these fields form to the database, when ever an order is placed all these fields needs to be saved to the database.  For this we will have to create a new fields.


 <?php
/**
 * Created by JetBrains PhpStorm.
 * User: Administrator
 * Date: 7/3/14
 * Time: 10:38 AM
 * To change this template use File | Settings | File Templates.
 */
/* @var $installer Mage_Core_Model_Resource_Setup */

require_once('app/Mage.php');
$installer = $this;
$installer->startSetup();

Mage::app()->setCurrentStore(Mage::getModel('core/store')->load(Mage_Core_Model_App::ADMIN_STORE_ID));
$installer = new Mage_Sales_Model_Resource_Setup('core_setup');

$attribute  = array(
    'type' => 'varchar',
    'backend_type' => 'varchar',
    'frontend_input' => 'varchar',
    'is_user_defined' => true,
    'label' => '',
    'visible' => true,
    'required' => false,
    'user_defined' => false,
    'default' => '0',
    'comparable' => false,
    'searchable' => false,
    'filterable' => false
);

$installer->addAttribute('order','thirdparty_account_no',$attribute);
$installer->addAttribute('quote','thirdparty_account_no',$attribute);

$installer->addAttribute('order','thirdparty_phone_no',$attribute);
$installer->addAttribute('quote','thirdparty_phone_no',$attribute);

$installer->addAttribute('order','thirdparty_comment',$attribute);
$installer->addAttribute('quote','thirdparty_comment',$attribute);

$installer->endSetup();

Next we add some event observer for when order is saved checkout_type_onepage_save_orderand when the continue button is pressed on the shipping method step sales_quote_save_before. Below are all the events used

<events>
            <checkout_type_onepage_save_order>
                <observers>
                    <checkout>
                        <type>singleton</type>
                        <class>Easylife_Pickup_Model_Observer</class>
                        <method>customDataSave</method>
                    </checkout>
                </observers>
            </checkout_type_onepage_save_order>
            <sales_quote_save_before>
                <observers>
                    <save_before>
                        <type>singleton</type>
                        <class>Easylife_Pickup_Model_Observer</class>
                        <method>saveQuoteBefore</method>
                    </save_before>
                </observers>
            </sales_quote_save_before>
        </events>


Next we need to create the observer class

<?php
class Easylife_Pickup_Model_Observer extends Varien_Object
{

    public function customDataSave($observer){
        $session = Mage::getSingleton("core/session");
        $account_no =$session->getData('account_no');
        $phone_no =$session->getData('phone_no');
        $comment =$session->getData('comment');

        $event = $observer->getEvent();
        $order = $event->getOrder();
        $order->setThirdpartyAccountNo($account_no);
        $order->setThirdpartyPhoneNo($phone_no);
        $order->setThirdpartyComment($comment);

    }
    public function saveQuoteBefore($evt){
        $session = Mage::getSingleton("core/session");
        $quote = $evt->getQuote();
        $post = Mage::app()->getFrontController()->getRequest()->getPost();

        if(isset($post['custom']['account'])){
            $var = $post['custom']['account'];
            $quote->setThirdpartyAccountNo($var);
            $session->setData('account_no',$post['custom']['account']);

        }
        if(isset($post['custom']['phone'])){
            $var = $post['custom']['phone'];
            $quote->setThirdpartyPhoneNo($var);
            $session->setData('phone_no',$post['custom']['phone']);

        }
        if(isset($post['custom']['comment'])){
            $var = $post['custom']['comment'];
            $quote->setThirdpartyComment($var);
            $session->setData('comment',$post['custom']['comment']);

        }

    }

}

As you can see in the observer class we have used the model Mage::getModel(‘pickup/pickup’). You will find details of this models code in the source code.

Step 5
The last step is show the information of the form in the order email, order admin view, invoices etc. Doing this very easy because magento accesses shipping information from a central function getShippingDescription() in the order model. So will override the order model and put in our own code

<sales>
                <rewrite>
                    <order>Easylife_Pickup_Model_Sales_Order</order>
                </rewrite>
            </sales>


and the code in the order class would be

<?php
class Easylife_Pickup_Model_Sales_Order extends Mage_Sales_Model_Order
{
    public function getShippingDescription(){
        $desc = parent::getShippingDescription();
        $pickupObject = $this->getPickupObject();
        if($pickupObject){
            $desc .= '<br/><b>Account Number     </b>: '.$pickupObject->getThirdpartyAccountNo();
            $desc .= '<br/><b>Phone Number        </b>: '.$pickupObject->getThirdpartyPhoneNo();
            $desc .= '<br/><b>Special Instructions</b>: '.$pickupObject->getThirdpartyComment();
            $desc .= '<br/>';
        }
        return $desc;
    }
}


Here we have added the third party details. These are all the steps to add a shipping method with form field. All fields are not explain in the tutorial, many basic files you can find in the module’s source code. Here I have explained front end basics/functionality, So I will continue this article next part -2.

if you have any queries please leave a comment.

No comments:

Post a Comment