In this blog, we will see how to add custom fields to magento onepage checkout steps.
This method can be used to add custom field to any step in the checkout page, although in the current blog i have demonstrated how to add it to the billing step. This method will work on all magento versions. The tutorial is quite long since many files are required, here is source code of the module created in the tutorial.Let start step by step, on how to add a single field on the billing step.
Step1: Adding HTML
This is the most simple step. I will add a text field to the billing.phtml file.For magento 1.6(-) the file to edit is checkout/onepage/billing.phtml and for magento 1.6(+) you need to edit file persistent/checkout/onepage/billing.phtml. Open the phtml file, find the code
<?php
if
(
$this
->canShip()): ?>
and just above the line add this
<li
class
=
"fields"
>
<div
class
=
"field"
>
<label
for
=
"billing:ssn"
class
=
"required"
><em>*</em><?php
echo
$this
->__(
'SSN'
) ?></label>
<div
class
=
"input-box"
>
<input type=
"text"
name=
"custom[ssn]"
value=
"<?php echo $this->htmlEscape($this->getQuote()->getSsn()) ?>"
title=
"<?php echo $this->__('SSN') ?>"
class
=
"input-text required-entry"
id=
"billing:ssn"
/>
</div>
</div>
</li>
and onestep\shipping_method.phtml
<div>
<!--Custom Field-->
<fieldset>
<ul class="form-list">
<li class="control">
<input type="checkbox"
name="custom[shp_method]"
value="1"
title="<?php echo $this->__('Third Party') ?>"
id="shp_method:shp_method"
class="checkbox" />
<label for="shp_method:shp_method"><?php echo $this->__('Third Party') ?></label>
</li>
<li class="fields">
<div class="field">
<label for="billing:account" class="required">
<em>*</em><?php echo $this->__('Account No') ?></label>
<div class="input-box">
<input type="text" name="custom[account]"
value="<?php echo $this->htmlEscape($this->getQuote()->getAccount()) ?>"
title="<?php echo $this->__('Account No') ?>"
id="billing:account" />
</div>
</div>
</li>
<li class="fields">
<div class="field">
<label for="billing:phone" class="required">
<em>*</em><?php echo $this->__('Phone No') ?></label>
<div class="input-box">
<input type="text" name="custom[phone]"
value="<?php echo $this->htmlEscape($this->getQuote()->getPhone()) ?>"
title="<?php echo $this->__('Phone No') ?>"
id="billing:phone" />
</div>
</div>
</li>
<li class="fields">
<div class="field">
<label for="billing:comment" class="required">
<em>*</em><?php echo $this->__('Comment ') ?></label>
<div class="input-box">
<!-- <input type="text" />-->
<textarea name="custom[comment]"
value="<?php echo $this->htmlEscape($this->getQuote()->getComment()) ?>"
title="<?php echo $this->__('Comment') ?>"
id="billing:comment"></textarea>
</div>
</div>
</li>
</ul>
</fieldset>
<!--Custom Field end-->
</div>
So, as you can see we are simply adding a text with name as custom[ssn] and value as $this->getQuote()->getSsn()
After doing this, you should see a field in billing.phtml as shown
. You can add this field on any checkout step, it doesn’t matter for my code.
Step2: Save the field in Quote and Order Tables
To save, out field to the quote object and database he are the things to do. First i will create a database table “sales_quote_custom”. Put the following sql in your modules mysql_install file.
<?php
$installer
=
$this
;
$installer
->startSetup();
$installer
->run("
CREATE TABLE IF NOT EXISTS {
$this
->getTable(
'sales_quote_custom'
)} (
`id` int(11) unsigned NOT NULL auto_increment,
`quote_id` int(11) unsigned NOT NULL,
`key` varchar(255) NOT NULL,
`value` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS {
$this
->getTable(
'sales_order_custom'
)} (
`id` int(11) unsigned NOT NULL auto_increment,
`order_id` int(11) unsigned NOT NULL,
`key` varchar(255) NOT NULL,
`value` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
");
$installer
->endSetup();
Here we are creating two tables, one for quote and other for order. I will explain the use of order table later. Now open phpmyadmin and confirm if the quote and order tables are created.
Next we need to write some event observers to capture quote save, and load events. So open your modules config.xml file and inside the <global> tag put in
<?xml version="1.0"?>
<config>
<modules>
<Excellence_Custom>
<version>0.1.0</version>
</Excellence_Custom>
</modules>
<frontend>
<routers>
<custom>
<use>standard</use>
<args>
<module>Excellence_Custom</module>
<frontName>custom</frontName>
</args>
</custom>
</routers>
<layout>
<updates>
<custom>
<file>custom.xml</file>
</custom>
</updates>
</layout>
</frontend>
<admin>
<routers>
<custom>
<use>admin</use>
<args>
<module>Excellence_Custom</module>
<frontName>custom</frontName>
</args>
</custom>
</routers>
</admin>
<adminhtml>
<layout>
<updates>
<custom>
<file>custom.xml</file>
</custom>
</updates>
</layout>
</adminhtml>
<global>
<events>
<sales_quote_save_before> <!-- Name of Event -->
<observers>
<save_before>
<type>singleton</type>
<class>Excellence_Custom_Model_Observer</class>
<!-- Over Model Class -->
<method>saveQuoteBefore</method>
<!-- name of function -->
</save_before>
</observers>
</sales_quote_save_before>
<sales_quote_save_after> <!-- Name of Event -->
<observers>
<save_after>
<type>singleton</type>
<class>Excellence_Custom_Model_Observer</class>
<!-- Over Model Class -->
<method>saveQuoteAfter</method>
<!-- name of function -->
</save_after>
</observers>
</sales_quote_save_after>
<sales_quote_load_after>
<observers>
<load_after>
<type>singleton</type>
<class>Excellence_Custom_Model_Observer</class>
<!-- Over Model Class -->
<method>loadQuoteAfter</method>
<!-- name of function -->
</load_after>
</observers>
</sales_quote_load_after>
<sales_model_service_quote_submit_after>
<observers>
<sales_model_service_quote_submit_after>
<type>singleton</type>
<class>Excellence_Custom_Model_Observer</class>
<!-- Over Model Class -->
<method>saveOrderAfter</method>
<!-- name of function -->
</sales_model_service_quote_submit_after>
</observers>
</sales_model_service_quote_submit_after>
<sales_order_load_after>
<observers>
<sales_order_load_after>
<type>singleton</type>
<class>Excellence_Custom_Model_Observer</class>
<!-- Over Model Class -->
<method>loadOrderAfter</method>
<!-- name of function -->
</sales_order_load_after>
</observers>
</sales_order_load_after>
<!--<payment_form_block_to_html_before>
<observers>
<custom>
<type>model</type>
<class>custom/observer</class>
<method>paymentFormBlockToHtmlBefore</method>
</custom>
</observers>
</payment_form_block_to_html_before>-->
</events>
<models>
<sales>
<rewrite>
<order>Excellence_Custom_Model_Sales_Order</order>
</rewrite>
</sales>
<custom>
<class>Excellence_Custom_Model</class>
<resourceModel>custom_mysql4</resourceModel>
</custom>
<custom_mysql4>
<class>Excellence_Custom_Model_Mysql4</class>
<entities>
<custom_quote>
<table>sales_quote_custom</table>
</custom_quote>
<custom_order>
<table>sales_order_custom</table>
</custom_order>
</entities>
</custom_mysql4>
</models>
<resources>
<custom_setup>
<setup>
<module>Excellence_Custom</module>
</setup>
<connection>
<use>core_setup</use>
</connection>
</custom_setup>
<custom_write>
<connection>
<use>core_write</use>
</connection>
</custom_write>
<custom_read>
<connection>
<use>core_read</use>
</connection>
</custom_read>
</resources>
<blocks>
<custom>
<class>Excellence_Custom_Block</class>
</custom>
</blocks>
<helpers>
<custom>
<class>Excellence_Custom_Helper</class>
</custom>
</helpers>
</global>
</config>
and next we need to create our Observer classes in Model folder. So create an Observer.php in your modules Model folder.
<?php
class Excellence_Custom_Model_Observer
{
public function paymentFormBlockToHtmlBefore(Varien_Event $event)
{
//$quote = Mage::getModel('sales/quote')->getQuote()->getShippingAddress()->getShippingInclTax();
//$order = Mage::getModel('sales/order');
//$inclTax = $event->getQuote()->getShippingAddress()->getShippingInclTax();
//Mage::log($inclTax);
Mage::log('Test');
}
public function saveQuoteBefore($evt){
$quote = $evt->getQuote();
$post = Mage::app()->getFrontController()->getRequest()->getPost();
if(isset($post['custom']['ssn'])){
$var = $post['custom']['ssn'];
$quote->setSsn($var);
}
if(isset($post['custom']['account'])){
$var = $post['custom']['account'];
$quote->setAccount($var);
}
if(isset($post['custom']['phone'])){
$var = $post['custom']['phone'];
$quote->setPhone($var);
}
if(isset($post['custom']['comment'])){
$var = $post['custom']['comment'];
$quote->setComment($var);
}
}
public function saveQuoteAfter($evt){
$quote = $evt->getQuote();
if($quote->getSsn()){
$var = $quote->getSsn();
if(!empty($var)){
$model = Mage::getModel('custom/custom_quote');
$model->deteleByQuote($quote->getId(),'ssn');
$model->setQuoteId($quote->getId());
$model->setKey('ssn');
$model->setValue($var);
$model->save();
}
}
if($quote->getAccount()){
$var = $quote->getAccount();
if(!empty($var)){
$model = Mage::getModel('custom/custom_quote');
$model->deteleByQuote($quote->getId(),'account');
$model->setQuoteId($quote->getId());
$model->setKey('account');
$model->setValue($var);
$model->save();
}
}
if($quote->getPhone()){
$var = $quote->getPhone();
if(!empty($var)){
$model = Mage::getModel('custom/custom_quote');
$model->deteleByQuote($quote->getId(),'phone');
$model->setQuoteId($quote->getId());
$model->setKey('phone');
$model->setValue($var);
$model->save();
}
}
if($quote->getComment()){
$var = $quote->getComment();
if(!empty($var)){
$model = Mage::getModel('custom/custom_quote');
$model->deteleByQuote($quote->getId(),'comment');
$model->setQuoteId($quote->getId());
$model->setKey('comment');
$model->setValue($var);
$model->save();
}
}
}
public function loadQuoteAfter($evt){
$quote = $evt->getQuote();
$model = Mage::getModel('custom/custom_quote');
$data = $model->getByQuote($quote->getId());
foreach($data as $key => $value){
$quote->setData($key,$value);
}
}
public function saveOrderAfter($evt){
$order = $evt->getOrder();
$quote = $evt->getQuote();
if($quote->getSsn()){
$var = $quote->getSsn();
if(!empty($var)){
$model = Mage::getModel('custom/custom_order');
$model->deleteByOrder($order->getId(),'ssn');
$model->setOrderId($order->getId());
$model->setKey('ssn');
$model->setValue($var);
$order->setSsn($var);
$model->save();
}
}
if($quote->getAccount()){
$var = $quote->getAccount();
if(!empty($var)){
$model = Mage::getModel('custom/custom_order');
$model->deleteByOrder($order->getId(),'account');
$model->setOrderId($order->getId());
$model->setKey('account');
$model->setValue($var);
$order->setAccount($var);
$model->save();
}
}
if($quote->getPhone()){
$var = $quote->getPhone();
if(!empty($var)){
$model = Mage::getModel('custom/custom_order');
$model->deleteByOrder($order->getId(),'phone');
$model->setOrderId($order->getId());
$model->setKey('phone');
$model->setValue($var);
$order->setPhone($var);
$model->save();
}
}
if($quote->getComment()){
$var = $quote->getComment();
if(!empty($var)){
$model = Mage::getModel('custom/custom_order');
$model->deleteByOrder($order->getId(),'comment');
$model->setOrderId($order->getId());
$model->setKey('comment');
$model->setValue($var);
$order->setComment($var);
$model->save();
}
}
}
public function loadOrderAfter($evt){
$order = $evt->getOrder();
$model = Mage::getModel('custom/custom_order');
$data = $model->getByOrder($order->getId());
foreach($data as $key => $value){
$order->setData($key,$value);
}
}
}
//order.php
<?php
class Excellence_Custom_Model_Sales_Order extends Mage_Sales_Model_Order
{
public function hasCustomFields()
{
$var = $this->getSsn();
$var .= $this->getAccount();
$var .= $this->getPhone();
$var .= $this->getComment();
if($var && !empty($var)){
return true;
}else{
return false;
}
}
public function getFieldHtml(){
$var = $this->getSsn();
$var .= $this->getAccount();
$var .= $this->getPhone();
$var .= $this->getComment();
//Mage::log($var);
$html = '<b>SSN:</b>'.$var.'<br/>';
$html .= '<b>Account No:</b>'.$var.'<br/>';
$html .= '<b>Phone No:</b>'.$var.'<br/>';
$html .= '<b>Comment:</b>'.$var.'<br/>';
// Mage::log($html);
return $html;
}
}
//
<?php
class Excellence_Custom_Model_Mysql4_Custom_Quote extends Mage_Core_Model_Mysql4_Abstract
{
public function _construct()
{
$this->_init('custom/custom_quote', 'id');
}
public function deteleByQuote($quote_id,$var)
{
$table = $this->getMainTable();
$where = $this->_getWriteAdapter()->quoteInto('quote_id = ? AND ', $quote_id)
.$this->_getWriteAdapter()->quoteInto('`key` = ? ', $var);
$this->_getWriteAdapter()->delete($table,$where);
}
public function getByQuote($quote_id,$var = '')
{
$table = $this->getMainTable();
$where = $this->_getReadAdapter()->quoteInto('quote_id = ?', $quote_id);
if(!empty($var)){
$where .= $this->_getReadAdapter()->quoteInto(' AND `key` = ? ', $var);
}
$sql = $this->_getReadAdapter()->select()->from($table)->where($where);
$rows = $this->_getReadAdapter()->fetchAll($sql);
$return = array();
foreach($rows as $row){
$return[$row['key']] = $row['value'];
}
return $return;
}
}
//
<?php
class Excellence_Custom_Model_Mysql4_Custom_Order extends Mage_Core_Model_Mysql4_Abstract{
public function _construct()
{
$this->_init('custom/custom_order', 'id');
}
public function deteleByOrder($order_id,$var){
$table = $this->getMainTable();
$where = $this->_getWriteAdapter()->quoteInto('order_id = ? AND ', $order_id)
.$this->_getWriteAdapter()->quoteInto('`key` = ? ', $var);
$this->_getWriteAdapter()->delete($table,$where);
}
public function getByOrder($order_id,$var = ''){
$table = $this->getMainTable();
$where = $this->_getReadAdapter()->quoteInto('order_id = ?', $order_id);
if(!empty($var)){
$where .= $this->_getReadAdapter()->quoteInto(' AND `key` = ? ', $var);
}
$sql = $this->_getReadAdapter()->select()->from($table)->where($where);
$rows = $this->_getReadAdapter()->fetchAll($sql);
$return = array();
foreach($rows as $row){
$return[$row['key']] = $row['value'];
}
return $return;
}
}
//
<?php
class Excellence_Custom_Model_Mysql4_Custom_Quote_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
{
public function _construct()
{
parent::_construct();
$this->_init('custom/custom_quote');
}
}
//
<?php
class Excellence_Custom_Model_Mysql4_Custom_Order_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
{
public function _construct()
{
parent::_construct();
$this->_init('custom/custom_order');
}
}
//
<?php
class Excellence_Custom_Model_Custom_Order extends Mage_Core_Model_Abstract{
public function _construct()
{
parent::_construct();
$this->_init('custom/custom_order');
}
public function deleteByOrder($order_id,$var){
$this->_getResource()->deteleByOrder($order_id,$var);
}
public function getByOrder($order_id,$var = ''){
return $this->_getResource()->getByOrder($order_id,$var);
}
}
//
<?php
class Excellence_Custom_Model_Custom_Quote extends Mage_Core_Model_Abstract{
public function _construct()
{
parent::_construct();
$this->_init('custom/custom_quote');
}
public function deteleByQuote($quote_id,$var){
$this->_getResource()->deteleByQuote($quote_id,$var);
}
public function getByQuote($quote_id,$var = ''){
return $this->_getResource()->getByQuote($quote_id,$var);
}
}
//
So, what we have done above is captured the quote events sales_quote_save_before,sales_quote_save_after and sales_quote_load_after. Using these events saved our custom field to database. Details of functions are given in comments.
We have already create model classes for this
Excellence_Custom_Model_Custom_Order
Excellence_Custom_Model_Custom_Quote
which you will find in the source code.
Detailed Explanation
- The first event captured by our module is sales_quote_save_before and saveQuoteBefore() function is called in our Observer class. What this function does, just before $quote->save() function is called, it reads our custom variable value ‘ssn’ from FORM POST data and simply sets it in the $quote object.
- The next event captured by our module is sales_quote_save_after and saveQuoteAfter() function is called in our Observer class. What this function does, just after $quote->save() function is called i.e the quote object get stored in database, it store our custom variable to our database table “sales_quote_custom”
- The next event is sales_quote_load_after and function called is loadQuoteAfter(). When the $quote->load($id) is called, i.e quote is created from database entries, our function is called and this read the values of the custom variable from our database table “sales_quote_custom” and sets it back again in the quote object.
- The next event captured in sales_model_service_quote_submit_after and function called is saveOrderAfter(). This event is called, just after an order is successfully placed. In this event, we read our custom field value from quote object and put in order table.
- The next event is sales_order_load_after and function called is loadOrderAfter(). This event is fired when $order->load() function is called, we read our fields values from database and place it back in the order object.
Step3: Display Our Field in My Account -> View Order
After the order is placed by the customer, our custom fields needs to be visible in My Account. So to do this in our module fronted layout file, in my case its custom.xml.
<?
xml
version
=
"1.0"
?>
<
layout
version
=
"0.1.0"
>
<
sales_order_view
>
<
reference
name
=
"my.account.wrapper"
>
<
block
type
=
"custom/custom_order"
name
=
"custom.order"
template
=
"custom/order.phtml"
after
=
'sales.order.info'
/>
</
reference
>
</
sales_order_view
>
</
layout
>
This layout file is simple and understandable. Basically, we are taking reference of my.account.wrapper block and adding a child block to it. Our block type is custom/order_view and template file is custom/order.phtml.
So we need to create our block class
<?php
class
Excellence_Custom_Block_Custom_Order
extends
Mage_Core_Block_Template{
public
function
getCustomVars(){
$model
= Mage::getModel(
'custom/custom_order'
);
return
$model
->getByOrder(
$this
->getOrder()->getId());
}
public
function
getOrder()
{
return
Mage::registry(
'current_order'
);
}
}
and our order.phtml file
<div
class
=
"col-set order-info-box"
>
<div
class
=
"col"
>
<div
class
=
"box"
>
<div
class
=
"box-title"
>
<h2><?php
echo
$this
->__(
'Custom Fields'
) ?></h2>
</div>
<div
class
=
"box-content"
>
<?php
$custom
=
$this
->getCustomVars();
foreach
(
$custom
as
$key
=>
$value
){
?>
<b><?php
echo
$this
->__(
$key
);?></b> : <?php
echo
$value
;?> <br/>
<?php } ?>
</div>
</div>
</div>
</div>
After doing this, you should be able to see your custom field in My Account page as shown here
Step4: Admin Order View Page
Next we need to show our custom field in Admin -> Order -> View Order page. To do this, in our adminhtml layout file, in my case custom.xml we will put in this code.
<?
xml
version
=
"1.0"
?>
<
layout
version
=
"0.1.0"
>
<
adminhtml_sales_order_view
>
<
reference
name
=
"order_info"
>
<
action
method
=
'setTemplate'
><
template
>custom/sales/order/info.phtml</
template
></
action
>
<
block
type
=
"adminhtml/sales_order_view_info"
name
=
"order_info2"
template
=
"sales/order/view/info.phtml"
></
block
>
<
block
type
=
"custom/adminhtml_custom_order"
name
=
"custom.order"
template
=
'custom/order.phtml'
/>
</
reference
>
</
adminhtml_sales_order_view
>
</
layout
>
So basically here i have added my block “custom.order” to the existing order_info block. So the code for block class custom/adminhtml_custom_order would be
<?php
class
Excellence_Custom_Block_Adminhtml_Custom_Order
extends
Mage_Adminhtml_Block_Sales_Order_Abstract{
public
function
getCustomVars(){
$model
= Mage::getModel(
'custom/custom_order'
);
return
$model
->getByOrder(
$this
->getOrder()->getId());
}
}
and next we need to create their phtml files
custom/order.phtml
<?php
$_order
=
$this
->getOrder() ?>
<div
class
=
"box-left"
>
<div
class
=
"entry-edit"
>
<div
class
=
"entry-edit-head"
>
<h4
class
=
"icon-head head-account"
><?php
echo
Mage::helper(
'sales'
)->__(
'Custom Fields'
) ?></h4>
</div>
<div
class
=
"fieldset"
>
<table cellspacing=
"0"
class
=
"form-list"
>
<?php
$custom
=
$this
->getCustomVars();
foreach
(
$custom
as
$key
=>
$value
){
?>
<tr>
<td style=
"width:10%"
class
=
"label"
><strong><?php
echo
Mage::helper(
'sales'
)->__(
$key
) ?></strong></td>
<td
class
=
"value"
><?php
echo
$value
;?></td>
</tr>
<?php } ?>
</table>
</div>
</div>
</div>
<div
class
=
"clear"
></div>
custom/sales/order/info.phtml
<?php
echo
$this
->getChildHtml(
'order_info2'
);?>
<?php
echo
$this
->getChildHtml(
'custom.order'
);?>
//checkout\controllers\onpagecontroller.php
public function saveShippingMethodAction()
{
$post = $this->getRequest()->getParams();
$session = Mage::getSingleton("core/session");
//Mage::log($post);
//Mage::log($post['custom']['shp_method']);
if($post['custom']['shp_method'] == 1){
$session->setData("third_party", 1);
Mage::getSingleton('checkout/session')->getQuote()->getShippingAddress()->collectShippingRates()->save();
//$session->setData("third_party", 0);
}
else{
$session->setData("third_party", 0);
//Mage::log($post['custom']['shp_method']);
Mage::getSingleton('checkout/session')->getQuote()->getShippingAddress()->collectShippingRates()->save();
}
}
//Mage\Shipping\Model\Carrier\Abstract.php
public function getMethodPrice($cost, $method = '')
{
//get the session
$session = Mage::getSingleton('core/session');
$myDeviceId = $session->getData("third_party");
//Mage::log('Session Id started');
//Mage::log($myDeviceId);
//Mage::log('Session Id ended');
if($myDeviceId == 1){
$cost = 0.00;
}
return $method == $this->getConfigData($this->_freeMethod)
&& $this->getConfigFlag('free_shipping_enable')
&& $this->getConfigData('free_shipping_subtotal') <= $this->_rawRequest->getBaseSubtotalInclTax()
? '0.00'
: $this->getFinalPriceWithHandlingFee($cost);
}
//Mage\Checkout\Block\Onepage\Progress.php
public function getShippingPriceExclTax()
{
$session = Mage::getSingleton('core/session');
$myDeviceId = $session->getData("third_party");
if($myDeviceId == 1){
$this->getQuote()->getShippingAddress()->setShippingAmount(0);
}
return $this->formatPrice($this->getQuote()->getShippingAddress()->getShippingAmount());
}
//opcheckout.js
//shipping method ->validate section
var flag = $('shp_method:shp_method').checked;
var account = $('billing:account').value;
var phone = $('billing:phone').value;
//alert(flag);
if (flag) {
if(account == ''){
alert(Translator.translate('Please specify account Number.').stripTags());
return;
}
else if(phone == ''){
alert(Translator.translate('Please specify phone Number.').stripTags());
return;
}
}
flag = 0;
Thats it, we are done. Hopefully i have covered all sections related to order. If i missed something please comment below and i will add it.
No comments:
Post a Comment