Ubercart: modifying checkout panes

Ubercart: modifying checkout panes

Posted by stella on Tue, 2009-11-03 11:56 in

The Ubercart module is one of the best e-commerce options for Drupal currently. It is very user friendly and highly flexible with administrators having control over the product catalogue, payment gateways and email notifications. Site administrators also have control over which checkout panes are displayed during checkout and the order in which they appear. However, as I found out recently, while it is easy to control checkout pane visibility, and even add your own, there's no simple way of modifying the forms contained within a checkout pane. This article will cover one solution on how to overcome this.

For a site I'm working on, I needed to be able to add a new field to contain the user's title (Mr, Mrs, Miss, etc) to the "billing information" ubercart checkout pane. I first implemented hook_form_alter() to add the field and set a custom submit handler. However, while I was able to modify the form, the submit function was never called and I wasn't able to save the value of the new field to the customer's order. This is because the ubercart checkout panes don't fully utilise the FAPI.

My solution was to create a new checkout pane and to entirely recreate the billing pane by pulling in the pane contents from the ubercart module, adding my own changes and returning the merged version.

So first I created a new checkout pane by implementing the hook, hook_checkout_pane():

<?php
function mymodule_checkout_pane() {
 
// Replacement for standard billing address pane.
 
$panes[] = array(
   
'id' => 'mymodule_billing',
   
'callback' => 'mymodule_checkout_pane_mymodule_billing',
   
'title' => t('Billing Address'),
   
'desc' => t('Custom billing address fields.'),
   
'weight' => 2,
   
'process' => TRUE,
   
'collapsible' => FALSE,
  );
  return
$panes;
}
?>

Each pane needs a unique id. I originally used the same id as the ubercart billing information pane in the hope that I could override it, but that didn't work unfortunately. The other two fields to pay particular attention to are callback - the function to call to build the pane and process it, and process - this should be set to TRUE, so your callback function is called with the 'process' operation.

For the callback function, I didn't want to just copy and paste in Ubercart's uc_checkout_pane_billing() function as then I wouldn't be able to avail of any modifications made to the original "billing information" pane by simply upgrading the module - I would have to modify my custom module each time. So for each operation, my checkout pane calls Ubercart's one, makes my custom changes and then returns the merged result. The final result is as follows:

<?php
function mymodule_checkout_pane_mymodule_billing($op, &$arg1, $arg2) {
  require_once(
drupal_get_path('module', 'uc_cart') . '/uc_cart_checkout_pane.inc');

  switch (
$op) {
    case
'view':
     
// This is needed to avoid 'an illegal choice has been made' error.
     
if (isset($_POST['panes']['mymodule_billing']['billing_country'])) {
       
$_POST['panes']['billing']['billing_country'] = $_POST['panes']['mymodule_billing']['billing_country'];
      }
     
$contents = uc_checkout_pane_billing($op, $arg1, $arg2);

     
// Add 'title' or 'salutation' to billing address details.
     
$contents['contents']['billing_title'] = array(
       
'#type' => 'select',
       
'#title' => t('Title'),
       
'#options' => array('Mr', 'Mrs', 'Ms', 'Miss', 'Dr', 'Fr', 'Rev', 'Sr'),
       
'#required' => TRUE,
       
'#weight' => 0,
       
'#default_value' => $arg1->data['billing_title'],
      );
     
// Address history selector doesn't work for this solution, so remove it.
     
unset($contents['contents']['billing_address_select']);

      return
$contents;

    case
'review':
      return
uc_checkout_pane_billing($op, $arg1, $arg2);

    case
'process':
     
$arg1->billing_title = $arg2['billing_title']; // Save our custom field.
     
return uc_checkout_pane_billing($op, $arg1, $arg2);
  }
}
?>

Thanks!! This saved me lots

Thanks!! This saved me lots of time.

Posted by Anonymous (not verified) on Tue, 2010-05-25 17:16
thanks

hi, thanks for this

i have use your solution for my site: http://www.one.nl/

i also need to know if you ever try to integrate a form within checkout pane?
i want to place a login form inside checkout_pane_customer
so basically instead of i send user to user/login page
they can just login through cart/checkout page

i have tried using thickbox module
however, this module did not support in case a user login with wrong username or password

so please share your knowledge if you have any solution for this problem

thank you very much

Posted by erwin yonathan (not verified) on Mon, 2010-06-07 16:50
You could try the popups

You could try the popups module (http://drupal.org/project/popups), though I think ubercart will update the correct user account if they enter in the same email address.

Posted by stella on Mon, 2010-06-07 17:01
thank you for your

thank you for your suggestion
however my boss and project leader really want it to be a form next to fieldset with customer e-mail textfield
i have a solution before using javascript, appending the login form in the predefine area
but since without javascript it will not work anymore
so stella, if you know a solution, you will be a really live saver

thank you

Posted by erwin yonathan (not verified) on Tue, 2010-06-08 15:16
Thanks for the code

Thanks for the code Stella. It works great on my local host site. I will be adding this soon to my live scooter production site.

Thanks

Posted by Scooter (not verified) on Mon, 2010-07-05 16:13
Works like a charm. For the

Works like a charm. For the record: to make this work, create a new module (mymodule.module with both the hooks and a mymodule.info, I gave it the dependancy 'uc_cart' since it is part of the cart-checkout).

Too bad the known-addresses feature doesn't work with this mod. For those who want to keep using it, it might be an option to replace the original uc_checkout_pane_billing() with the posted one.

Posted by Matthijs (not verified) on Sat, 2010-07-17 16:30
Ubercart 2.3 introduces hook_checkout_pane_alter()

As of Ubercart 2.3, there is now a new hook called hook_checkout_pane_alter() in which you can alter the information about the panes.
So now an implementation of hook_checkout_pane() is not needed anymore. However, the callback function from the example, 'mymodule_checkout_pane_mymodule_billing()', is still needed.

Example of implementing hook_checkout_pane_alter() which alters the billing pane:

<?php
/**
* Implementation of hook_checkout_pane_alter().
* Alters billing pane
* @param array $panes
* @return void
*/
function mymodule_checkout_pane_alter(&$panes) {
  foreach (
$panes as $key => $pane) {
    switch (
$pane['id']) {
      case
'billing':
       
$panes[$key]['callback'] = 'mymodule_checkout_pane_mymodule_billing';
        break;
    }
  }
}
?>

Posted by MegaChriz (not verified) on Tue, 2010-07-27 10:41
Great! Thank you

Great! Thank you

Posted by Anonymous (not verified) on Thu, 2011-12-22 14:40
Thanks Stella and MegaChriz

Great! This helped me carry out exactly what I needed in a more future-proof fashion. I hope to write my own blog post when I get a minute, on the issues I've solved with help from both of you. Thanks.

~mccrodp

Posted by mccrodp (not verified) on Sun, 2010-08-29 18:08
Extra Fields Checkout Pane

Anyone who is just looking for adding extra address fields to delivery and/or billing panes, try out the 2.x version of Extra Fields Checkout Pane (http://drupal.org/project/uc_extra_fields_pane). The 2.x version is still in development, but it might already fit your needs.

Posted by MegaChriz (not verified) on Tue, 2010-09-14 15:51
Thanks - quick question

This is just what I need, but the one part missing is the exact places to put the code in the examples above. I would be forever in your debt if you clarified where these functions go. Thanks for taking the time to post this tutorial.
-
spike

Posted by spike13 (not verified) on Sat, 2010-09-18 02:21
trying MegaChriz post

tried MegaChriz's version of this with Ubercart 2.3> returns an error from the module

Parse error: syntax error, unexpected T_FOREACH in /Applications/MAMP/htdocs/xxxxxxx/sites/all/modules/myformmodule/mymodule.module on line 22

Posted by Anonymous (not verified) on Fri, 2010-10-22 10:32
Saving additional field in DB

Hi I managed to get this to work but I don't understand how (and where) the additional information (custom field) is stored in the database.

Thanks

Posted by webnomad (not verified) on Wed, 2011-03-16 15:57
uc_orders table

It ends up in the "data" column in the "uc_orders" table.

Posted by stella on Wed, 2011-03-16 16:25
I have two panes with a

I have two panes with a salutation field :

<?php
/*
* alter the checkout pane, add salutation
*/

function uc_laulas_checkout_pane() {
 
// Replacement for standard billing address pane.
 
$panes[] = array(
   
'id' => 'uc_laulas_billing',
   
'callback' => 'uc_laulas_checkout_pane_uc_laulas_billing',
   
'title' => t('Billing Address'),
   
'desc' => t('Custom billing address fields.'),
   
'weight' => 4,
   
'process' => TRUE,
   
'collapsible' => TRUE,
  );

 
// Replacement for standard delivery address pane.
 
$panes[] = array(
   
'id' => 'uc_laulas_delivery',
   
'callback' => 'uc_laulas_checkout_pane_uc_laulas_delivery',
   
'title' => t('Delivery Address'),
   
'desc' => t('Custom delivery address fields.'),
   
'weight' => 3,
   
'process' => TRUE,
   
'collapsible' => FALSE,
  );

  return
$panes;
}

function
uc_laulas_checkout_pane_uc_laulas_billing($op, &$arg1, $arg2) {
  require_once(
drupal_get_path('module', 'uc_cart') . '/uc_cart_checkout_pane.inc');

  switch (
$op) {
    case
'view':
     
// This is needed to avoid 'an illegal choice has been made' error.
     
if (isset($_POST['panes']['uc_laulas_billing']['billing_country'])) {
       
$_POST['panes']['billing']['billing_country'] = $_POST['panes']['uc_laulas_billing']['billing_country'];
      }
     
$contents = uc_checkout_pane_billing($op, $arg1, $arg2);

     
// Add 'title' or 'salutation' to billing address details.
     
$contents['contents']['billing_title'] = array(
       
'#type' => 'select',
       
'#title' => t('Salutation'),
       
'#options' => array(t('Mr'), t('Mrs')),
       
'#required' => TRUE,
       
'#weight' => 0,
       
'#default_value' => $arg1->data['billing_title'],
      );

     
$contents['contents']['copy_address'] = array(
         
'#type' => 'checkbox',
         
'#title' => t('My billing information is the same as my delivery information.'),
         
'#attributes' => array('onclick' => "uc_cart_copy_address(this.checked, 'uc_laulas_delivery', 'uc_laulas_billing');"),
        ); 

     
// Address history selector doesn't work for this solution, so remove it.
     
unset($contents['contents']['billing_address_select']);

      return
$contents;

    case
'review':
      return
uc_checkout_pane_billing($op, $arg1, $arg2);

    case
'process':
     
$arg1->billing_title = $arg2['billing_title']; // Save our custom field.
     
return uc_checkout_pane_billing($op, $arg1, $arg2);
  }
}


function
uc_laulas_checkout_pane_uc_laulas_delivery($op, &$arg1, $arg2) {
  require_once(
drupal_get_path('module', 'uc_cart') . '/uc_cart_checkout_pane.inc');

  switch (
$op) {
    case
'view':
     
// This is needed to avoid 'an illegal choice has been made' error.
     
if (isset($_POST['panes']['uc_laulas_delivery']['delivery_country'])) {
       
$_POST['panes']['delivery']['delivery_country'] = $_POST['panes']['uc_laulas_delivery']['delivery_country'];
      }
     
$contents = uc_checkout_pane_delivery($op, $arg1, $arg2);

     
// Add 'title' or 'salutation' to delivery address details.
     
$contents['contents']['delivery_title'] = array(
       
'#type' => 'select',
       
'#title' => t('Salutation'),
       
'#options' => array(t('Mr'), t('Mrs')),
       
'#required' => TRUE,
       
'#weight' => 0,
       
'#default_value' => $arg1->data['delivery_title'],
      );
     
// Address history selector doesn't work for this solution, so remove it.
     
unset($contents['contents']['delivery_address_select']);

      return
$contents;

    case
'review':
      return
uc_checkout_pane_delivery($op, $arg1, $arg2);

    case
'process':
     
$arg1->delivery_title = $arg2['delivery_title']; // Save our custom field.
     
return uc_checkout_pane_delivery($op, $arg1, $arg2);
  }
}
?>

in the data column I only see the info from the uc_coupon module

a:1:{s:6:"coupon";s:4:"TEST";}

regards

Posted by webnomad (not verified) on Wed, 2011-03-16 20:53
Try this...

Hmmm you may also need the following if the above is not working:

<?php
/**
* Implements hook_order().
*/
function myorder_order($op, &$arg1, $arg2) {
  switch (
$op) {
    case
'save':
     
$order_id = $arg1->order_id;

     
// Load up the existing data array.
     
$order = db_fetch_object(db_query("SELECT * FROM {uc_orders} WHERE order_id = %d", $order_id));
     
$data = unserialize($order->data);
     
$data['billing_title'] = (!empty($arg1->billing_title) ? $arg1->billing_title : $data['billing_title']);

     
// Save it again.
     
db_query("UPDATE {uc_orders} SET data = '%s' WHERE order_id = %d", serialize($data), $order_id);
  }
}
?>

Posted by stella on Wed, 2011-03-16 21:17
Thanx for your kind and quick

Thanx for your kind and quick support, this worked but then I ran into problems with the copy address checkbox which did not work because it needed the pane-id 'billing' and 'delivery'. I ended up installing the uc Extra Fields Pane 6.2 (http://drupal.org/project/uc_extra_fields_pane) which lets you insert Fields into the existing panes ( billing and delivery), works great for me.

Posted by webnomad (not verified) on Thu, 2011-03-17 22:47
Actually, on process you want

Actually, on process you want to do $arg1->data['delivery_title'] =

Posted by Neil Drumm (not verified) on Fri, 2011-04-15 03:41
It creates two billing panes

It creates two billing panes for me. How to remove one

Posted by Anonymous (not verified) on Tue, 2011-05-10 10:51
You can disable them via the

You can disable them via the Ubercart admin UI - so just disable the built-in one.

Posted by stella on Tue, 2011-05-10 11:13
HELP ME

hy guys! it's all the day that i'm trying to custom my cart pane in the check-out area!
I have 2 attributes that i have to add to my pane. i try with the PANE_ALTER and CHECK_OUT_PANE. No results! with check_out pane i can just create a new pane but not override the old. ok no problem, i will throw away the old pane.
I create a new module, just like above. The program enter in the callback. here I have to put the code to create my pane. I put the same code that define the function uc_checkout_pane_cart (uc_cart_checkout_pane.inc, line 18) and i changed the value of $output (the content of the like i need for my custom table. but not! nothing change. somebody can help me please???? thanks!! i try also using pane_alter but whithout good results!
Stefano italy

Posted by Stefano (not verified) on Mon, 2011-06-06 18:58
How to display Title field?

I followed above, inclusive stella's comment from Wed, 2011-03-16 21:17, and verified the data is saved to DB. But how can I retrive this information on order management screen, print bill page etc.?

Best regards,
Sebastian

Posted by Sebastian (not verified) on Thu, 2011-07-21 12:20
Partial solution

I implemented following funktion to make the title fields available in the order templates (uc_order-admin.tpl.php, uc_order-customer.tpl.php). This way the title get added to the head of adresses and are available as $mymodule_shipping_address and $mymodule_billing_address.

function mymodule_token_values($type, $object = NULL) {
  $values = array();
  switch ($type) {
    case 'order':
      $order = $object;
      $options = array('Frau', 'Herr', 'Mrs', 'Mr');
      $values['mymodule-shipping-address'] = $options[$order->data['shipping_title']].'<br />'.uc_order_address($order, 'delivery');
      $values['mymodule-billing-address']  = $options[$order->data['delivery_title']].'<br />'.uc_order_address($order, 'billing');
    break;
  }
  return $values;
}

But still did not found way to imlement the title display on "Review order" screen (cart/checkout/review) and admin/store/orders/%.

Posted by Sebastian (not verified) on Wed, 2011-07-27 17:11
One more partial fix

To display the titles on cart/checkout/review you need to modify the 'review' section in mymodule_checkout_pane_mymodule_billing() / mymodule_checkout_pane_mymodule_shipping().

Mine looks like:

function lmymodule_checkout_pane_delivery($op, &$arg1, $arg2) {
  require_once(drupal_get_path('module', 'uc_cart') . '/uc_cart_checkout_pane.inc');

  switch ($op) {
[...]
    case 'review':
      $review[] = array('title' => t('Address'), 'data' => mymodule_uc_order_address($arg1, 'delivery', FALSE));
      if (uc_address_field_enabled('phone') && !empty($arg1->delivery_phone)) {
        $review[] = array('title' => t('Phone'), 'data' => check_plain($arg1->delivery_phone));
      }
      return $review;
[...]
}

And helper function to add the title to output:
function mymodule_uc_order_address(&$order, $type) {
  $options = array('Frau', 'Herr', 'Mrs', 'Mr');
  return $options[$order->data[$type.'_title']].'<br />'.uc_order_address($order, $type);
}

Posted by Sebastian (not verified) on Wed, 2011-07-27 17:58
Extra Fields Pane 6.x-2.x adds fields to delivery/billing panes

You don't need to build a separate module anymore to add something like a title field to delivery/billing pane.
Extra Fields Pane 6.x-2.x can do that.
http://drupal.org/project/uc_extra_fields_pane

Posted by MegaChriz (not verified) on Wed, 2011-08-03 08:12
Thanks

Thank you so soooooo much.

Posted by Hasan hafiz (not verified) on Fri, 2011-07-22 13:55
Helpful

Thanks for nice information here. The method given here for modifying the check out panes in ubercart is really of great help to new learner.

Posted by password management software (not verified) on Sun, 2012-03-11 22:12
Merging Cart/Checkout

Hello everyone,

I've done some intensive research on the subject f Ubercart/Drupal 7.12 I've also posted on a few sites and haven't gotten any additional info. I'm looking for a solution to just merge both the cart and the checkout/payment so that both happen on the same page. I'm not a developer, I'm one in progress. If someone can please give me a few steps in the right direction or if there is a solution out there I'd be grateful. I'm using Ubercart (latest) and Drupal 7.12.

Posted by Ramy Asha (not verified) on Thu, 2012-04-12 08:00
I can't really help you with

I can't really help you with Ubercart in Drupal 7 as I've switched to using Drupal Commerce. Maybe others here have an idea, but I would suggest creating a support request in the ubercart module issue queue

Posted by stella on Thu, 2012-08-23 14:03