Ubercart: Creating Line Items Tutorial

I've recently created a new module for Drupal's Ubercart e-commerce solution that required me to add a new line item to the checkout process. It was a huge headache, as the documentation is incomplete, and there are no clear explanations/tutorials on how to do this process from start to finish. So I have decided to post the process here for three reasons:

  1. To save others from having to go through the same process of having to figure it all out like I did
  2. To save the process in a central area so that I can refer back to it myself!
  3. To get feedback on parts of the process I've done incorrectly, or to add parts that I may have missed

Disclaimer: I make no claims that this tutorial is exhaustive. As of the time of writing, it is the process that I used in order to implement new line items for the purpose I needed. There may be a few parts that I didn't need for my own purposes that can be implemented in line items. Please feel free to add them in the comments. Please include where in the process the step should occur, and as much detail as you can.

So on to the tutorial.

Step 1: Create a new pane for the checkout page

The first thing we need to do is create a new pane on the checkout page where we can select our new line item. This step may not be required if your line item is determined somewhere before the checkout process. However, if your line item is determined by clicking a checkbox or selecting from a list, this step will be required. In my module, I was adding a gift wrapping option to the checkout process. As such, I needed to add a checkbox to the checkout page, where the user could choose to have the order gift wrapped (wrapping came with a 3 pound surcharge).

  1. Implement hook_checkout_pane(). hook_checkout_pane() is used to define the new pane that will appear on the checkout page.

    The key to this hook is the callback function that is defined. Here is an example of the hook definition:

    <?php
    function my_module_checkout_pane()
    {
      
    $panes[] = array
      (
        
    'id' => 'gift-wrap'
        
    'callback' => 'my_module_checkout_pane_wrapping_options'// This is the title of the callback function
        
    'title' => t('Wrapping Options'), // This will be the title of the pane as displayed on the checkout page
        
    'desc' => t('Wrapping options added during checkout'), // The description of the pane. Will appear in admin pages
        
    'weight' => -1// Determines the default location of the pane on the page
        
    'default' => TRUE
        
    'process' => TRUE,
        
    'collapsible' => TRUE,
      );
      return 
    $panes;
    }
    ?>
  2. Next we will look at the callback function for the hook defined above. The callback function will is passed three variables, $op, &$arg1, and $arg2. Inside the function, we need to deal with two values for $op: 'view' and 'process'. First, the callback function definition:

    <?php
    function my_module_checkout_pane_wrapping_options($op, &$arg1$arg2)
    {
      switch(
    $op)
      {
        case 
    'view':
          
    // Here we create the form elements that will be added into the checkout pane
          
    break;
        case 
    'process':
          
    // Here we tell Ubercart what to do with the submitted values for the form elements added when $op == 'view'
          
    break;
      }
    }
    ?>

    Let's look at an example of what to add when $op == 'view':

    <?php
    // First define the form fields:
    $form['gift_wrap'] = array
    (
      
    '#type' => 'checkbox',
      
    '#title' => t('Gift Wrap this Order? ($3.00 surcharge)'),
    );
    // Next we return the form fields according to the following pattern
    return array
    (
      
    'contents' => $form,
      
    'next-button' => TRUE,
    );
    ?>

    Now let's look at an example of how we will handle the above form fields upon checkout form submission. $arg2 holds the values from the submitted form items, and $arg1 holds the order details. So we have to examine the submitted values in $arg2, and add whatever data we want to $arg1, which will then be passed through the rest of the payment process.

    <?php
    // $arg 2 holds the submitted data. In this example, the submitted value will either be zero (don't wrap) or one (wrap). Regardless of the submitted value, I will add it to the $order, and will use that to determine whether or not the surcharge should be added to the total later on in the module
    $arg1->gift_wrap $arg2['gift_wrap'];
    ?>

    So now we have finished defining the new pane in the checkout process. Our code will look like this:

    <?php
    function my_module_checkout_pane()
    {
      
    $panes[] = array
      (
        
    'id' => 'special-options'
        
    'callback' => 'my_module_checkout_pane_wrapping_options'// This is the title of the callback function
        
    'title' => t('Wrapping Options'), // This will be the title of the pane as displayed on the checkout page
        
    'desc' => t('Wrapping options added during checkout'), // The description of the pane. Will appear in admin pages
        
    'weight' => -1// Determines the default location of the pane on the page
        
    'default' => TRUE
        
    'process' => TRUE,
        
    'collapsible' => TRUE,
      );
      return 
    $panes;
    }
    function 
    my_module_checkout_pane_wrapping_options($op, &$arg1, &$arg2)
    {
      switch(
    $op)
      {
        case 
    'view':
          
    // First define the form fields:
          
    $form['gift_wrap'] = array
          (
            
    '#type' => 'checkbox',
            
    '#title' => t('Gift Wrap this Order? ($3.00 surcharge)'),
          );
          
    // Next we return the form fields according to the following pattern
          
    return array
          (
            
    'contents' => $form,
            
    'next-button' => TRUE,
          );
        break;
        case 
    'process':
          
    // Here we tell Ubercart what to do with the submitted values for the form elements added when $op == 'view'
          
    $arg1->gift_wrap $arg2['gift_wrap'];
          break;
      }
    }
    ?>

Step 2: Create the line item

We need to add some javascript so that if the user selects the line item from the pane define above, the total is updated in real time on the checkout page. This will involve some custom javascript.

  1. First we define the line item by implementing hook_line_item().

    <?php
    function my_module_line_item()
    {
      
    $items[] = array
      (
        
    'id' => 'gift-wrap'// You will use this ID in the javascript 
        
    'title' => t('Gift Wrapping'), // This is the text that will be displayed on the line item in the subtotal
        
    'callback' => 'my_module_line_item_callback'// This is the callback function
        
    'weight' => 2// This is the position of the line item in the total process
        
    'default' => TRUE,
        
    'stored' => TRUE// This tells Ubercart to store the submitted data
        
    'calculated' => TRUE// This is for line items that need to be calculated and not just displayed. For this tutorial this value is required to be true
        
    'add_list' => TRUE,
        
    'display_only' => FALSE,
      );
      return 
    $items;
    }
    ?>
  2. Next we will look at the callback function for the hook defined above. The callback function in this tutorial is used for adding the javascript to the page. The callback function will is passed two variables, $op and &$arg1. $arg1 contains the products in the order and their total. Inside the function, we need to deal with one value for $op: 'cart-preview'. There are other values of $op for the callback function, but I did not require them for my module, and as of the time of writing, I'm not sure what they are used for. First, the callback function definition:

    <?php
    function my_module_line_item_callback($op, &$arg1)
    {
      if(
    $op == 'cart-preview')
      {
        
    // First we need to determine the value of the discount. In this tutorial, the discount is a fixed price, but your module may require the discount value to be calculated. In my real module, the value of the discount was a percentage discount. The percentag wasset on the settings page. So I used variable_get() to get the percentage, then used the values contained in $arg1 to calculate the order total, then multiplied this by the percentage to get the amount of the discount. This tutorial is much simpler however and just uses a hard-coded value of 3 (3.00 pounds). 
        
    $vars = array('giftWrappingPrice' => 3)); // This is the amount of the discount
        
    drupal_add_js(array('myModule' => $vars), 'setting'); This passes the discount to the javascriptso that it can be recovered in the javascript script
        $path 
    drupal_get_path('module''my_module') . '/'We get the path to the javascript script
        drupal_add_js
    ($path 'scripts/gift_wrap.js');  // I keep my scripts in a folder inside the module called 'scripts'. In this case the path to the script is [MODULES_FOLDER]/my_module/scripts/gift_wrap.js
      
    }
    }
    ?>

    So now our line item hook definition will look like this:

    <?php
    function my_module_line_item()
    {
      
    $items[] = array
      (
        
    'id' => 'gift-wrap'// You will use this ID in the javascript 
        
    'title' => t('Gift Wrapping'), // This is the text that will be displayed on the line item in the subtotal
        
    'callback' => 'my_module_line_item_callback'// This is the callback function
        
    'weight' => 2// This is the position of the line item in the total process
        
    'default' => TRUE,
        
    'stored' => TRUE// This tells Ubercart to store the submitted data
        
    'calculated' => TRUE// This is for line items that need to be calculated and not just displayed. For this tutorial this value is required to be true
        
    'add_list' => TRUE,
        
    'display_only' => FALSE,
      );
      return 
    $items;
    }
    function 
    my_module_line_item_callback($op, &$arg1)
    {
      if(
    $op == 'cart-preview')
      {
        
    // First we need to determine the value of the line item. In this tutorial, the line item is a fixed price, but your module may require the line item value to be calculated. In my real module, the value of the line item was a percentage discount. The percentage was set on the settings page. So I used variable_get() to get the percentage, then used the values contained in $arg1 to calculate the order total, then multiplied this by the percentage to get the amount of the discount. This tutorial is much simpler however and just uses a hard-coded value of 3 (3.00 pounds). 
        
    $vars = array('giftWrappingPrice' => 3)); // This is the amount of the discount
        
    drupal_add_js(array('myModule' => $vars), 'setting'); This passes the discount to the javascriptso that it can be recovered in the javascript script
        $path 
    drupal_get_path('module''my_module') . '/'We get the path to the javascript script
        drupal_add_js
    ($path 'scripts/gift_wrap.js');  // I keep my scripts in a folder inside the module called 'scripts'. In this case the path to the script is MODULES_FOLDER/my_module/scripts/gift_wrap.js
      
    }
    }
    ?>
  3. Finally we create the javascript script. This example in this tutorial is quite simple, as there is only one checkbox. Your own function will almost definitely be different and probably more complex than this, but regardless, the primary goal of the javascript file is to use two functions. The first function, set_line_item() is used to add the line item to the total when the checkbox is selected, and the second function, remove_line_item() is used to remove the line item if the checkbox is deselected. This example shows how those two functions would be used with this example of a single checkbox. Let's look at the script:

    // first I create an empty object as the namespace for my script. All functions will be added to this namespace to prevent conflicts with other modules.
    var myModule = {}; 
     
    // Next, I create the onload function for the script. Drupal.behaviors is the Drupal version of an onload function, and I add myModule in order to create an onload namespace and prevent onload conflicts with other modules. All code executed inside this function will be executed onload.
    Drupal.behaviors.myModule = function()  
    {
      myModule.checkboxListener(); // I will create this function below. 
    };
     
    myModule.checkboxListener = function() // This is the function called by my onload definition, and this function is part of the namespace defined at the top of the script
    {
      $("#edit-gift-wrap").click(function()
      {
        switch($(this).attr("checked"))
        {
          case false: // This indicates that the checkbox has been unchecked, so we remove the line item
            remove_line_item("gift-wrap"); // this value has to be the same as the ID defined in hook_line_item() or it will not update the total
            break;
          case true: // This indicates that the checkbox has been checked, so we add the line item
            // The definition for add_line_item() is: set_line_item(key, title, value, weight)
            set_line_item
            (
              "gift-wrap", // this value has to be the same as the ID defined in hook_line_item() or it will not update the total
              Drupal.t("Gift Wrapping"), // This is the text that will show up in the order total. I run it through Drupal.t() which is the javascript version of the t() translation function
              Drupal.settings.myModule.giftWrappingPrice // Drupal.settings.myModule.giftWrappingPrice is the value of the discount that I passed from the PHP function
            );
            break;
        }
      });
    };

Step 3: Handle the line item in the order process

While we have created the line item, and caused it to be able to be added to the display on the checkout process by checking the checkbox, this value will not yet carry over to the order review page. We will take care of this step in the process here.

  1. Implement hook_order(). Hook order is passed three arguments: $op, $arg1, and $arg2. We will deal with three values of $op: 'save', 'total' and 'submit'. Let's look at the hook_definition:

    <?php
    function my_module_order($op, &$arg1$arg2)
    {
      switch(
    $op)
      {
        case 
    'save':
          
    // This is called when the user clicks 'review order' on the checkout page. We add our line item to the order in this step.
          
    break;
        case 
    'total':
          
    // Here we return a value that will be added to the order total. For discounts we would return a negative value, for surcharges we return a positive value. We do NOT return the new total, only the change in the total.
          
    break;
        case 
    'submit':
          
    // Here we perform any actions that our module requires for the module itself - i.e. database updates. In this tutorial there isn't actually anything to be saved to the database, but in my real module I had to save text for a card that would be attached to the order if the user chose gift wrapping. I did all my database manipulation, saving the message to the database, in this section.
          
    break;
      }
    }
    ?>
  2. First we will look at the section when $op equals 'save'. In this section, if the line item needs to be added, we will use the function :

    <?php
    // The first thing we do is remove the line item if it exists. Since users can go back and forth from the order review page to the checkout page, they may make different selections each time. By defaulting to having the line item removed, we prevent accidentally adding it multiple times, as well as preventing adding it if the user hasn't selected it when reviewing the page. So we loop through the existing line items searching for our specific line item, and removing it if it exists. We will add it afterwards if it needs to be added.
    foreach($arg1->line_items as $key => $line_item)
    {
      if(
    $line_item['type'] == 'gift_wrap'// 'gift_wrap' is the line item ID that will be defined after this loop.
      
    {
        unset(
    $arg1->line_items[$key]);
        
    db_query('DELETE FROM {uc_order_line_items} WHERE order_id = %d AND type = "%s"'$arg1->order_id'gift_wrap');
        break;
      }
    }
    // We added the value of the checkbox to the order in our hook_checkout_pane callback function when <code>$op == 'process'</code>. If the checkbox was selected, the value is 1 (true). If the checkbox was not selected, the value is 0 (false). So we check the value, and if the checkbox was selected, we add the line item to the order.
    if($arg1->gift_wrap
    {
      
    // We will enter this conditional when the checkbox was selected. 
      // In this tutorial, the amount of the line item is a fixed amount, 3 pounds, but in my real tutorial the value was a percentage of the total, as I explained above in the hook_line_order() explanation. You will most likely need to re-calculate the value of the line item again here, however in this tutorial it is a fixed amount.
      
    $line_item_value 3;
      
    // Next we add the line item
      
    uc_order_line_item_add
      
    (
        
    $arg1->order_id// This is the order ID. 
        
    'gift_wrap'// This is the line item ID. We used this ID to remove the line item in the loop above.
        
    t('Gift Wrapping'), // This is the text that will be shown on the order review page to describe what the line item is
        
    3// This is the value of the line item. Discounts will be a negative number, surcharges a positive number
        
    NULL// This is the weight of the line item
        
    array() // Here you can pass any data that your module will use for itself when the order is submitted. In the case of my real module, I passed the value of the message that should be attached to the order when the user purchases gift wrapping. In this module there is no custom value so I actually wouldn't even pass the empty array normally. I just put it here to explain how it can be used.
      
    );
    }
    ?>
  3. Next we add the code when $op equals 'total'. In this section we pass the amount that the order should be changed by. In the case of a discount, it will be a negative amount, in the case of a surcharge it will be a positive amount.
    <?php
    // We loop through each of the line items to see if ours has been added. If it has, we adjust the order total accordingly.
    foreach($arg1->line_items as $line_item)
    {
      if(
    $line_item['type'] == 'gift_wrap'// 'gift_wrap' is the ID of the line item as defined when $op equals 'save'
      
    {
        
    // If we enter this, the line item exists, so we need to return the value of the line item. This will adjust the order total.
        
    return $line_item['amount'];
      }
    }
    ?>
  4. As I explained in the original hook_order() definition, if our module needs to interact with the database, we would add some code when $op equals 'submit'. However this tutorial does not save any custom information to the database, so I will not be adding that step. So lets look at our final hook_order() definition (as it applies to this tutorial):

    <?php
    function my_module_order($op, &$arg1$arg2)
    {
      switch(
    $op)
      {
        case 
    'save':
          
    // This is called when the user clicks 'review order' on the checkout page. We add our line item to the order in this step.
          // The first thing we do is remove the line item if it exists. Since users can go back and forth from the order review page to the checkout page, they may make different selections each time. By defaulting to having the line item removed, we prevent accidentally adding it multiple times, as well as preventing adding it if the user hasn't selected it when reviewing the page. So we loop through the existing line items searching for our specific line item, and removing it if it exists. We will add it afterwards if it needs to be added.
          
    foreach($arg1->line_items as $key => $line_item)
          {
            if(
    $line_item['type'] == 'gift_wrap'// 'gift_wrap' is the line item ID that will be defined after this loop.
            
    {
              unset(
    $arg1->line_items[$key]);
              
    db_query('DELETE FROM {uc_order_line_items} WHERE order_id = %d AND type = "%s"'$arg1->order_id'gift_wrap');
              break;
            }
          }
          
    // We added the value of the checkbox to the order in our hook_checkout_pane callback function when <code>$op == 'process'</code>. If the checkbox was selected, the value is 1 (true). If the checkbox was not selected, the value is 0 (false). So we check the value, and if the checkbox was selected, we add the line item to the order.
          
    if($arg1->gift_wrap
          {
            
    // We will enter this conditional when the checkbox was selected. 
            // In this tutorial, the amount of the line item is a fixed amount, 3 pounds, but in my real tutorial the value was a percentage of the total, as I explained above in the hook_line_order() explanation. You will most likely need to re-calculate the value of the line item again here, however in this tutorial it is a fixed amount.
            
    $line_item_value 3;
            
    // Next we add the line item
            
    uc_order_line_item_add
            
    (
              
    $arg1->order_id// This is the order ID. 
              
    'gift_wrap'// This is the line item ID. We used this ID to remove the line item in the loop above.
              
    t('Gift Wrapping'), // This is the text that will be shown on the order review page to describe what the line item is
              
    3// This is the value of the line item. Discounts will be a negative number, surcharges a positive number
              
    NULL// This is the weight of the line item
              
    array() // Here you can pass any data that your module will use for itself when the order is submitted. In the case of my real module, I passed the value of the message that should be attached to the order when the user purchases gift wrapping. In this module there is no custom value so I actually wouldn't even pass the empty array normally. I just put it here to explain how it can be used.
            
    );
          }
          break;
        case 
    'total':
          
    // Here we return a value that will be added to the order total. For discounts we would return a negative value, for surcharges we return a positive value. We do NOT return the new total, only the change in the total.
          // We loop through each of the line items to see if ours has been added. If it has, we adjust the order total accordingly.
          
    foreach($arg1->line_items as $line_item)
          {
            if(
    $line_item['type'] == 'gift_wrap'// 'gift_wrap' is the ID of the line item as defined when $op equals 'save'
            
    {
              
    // If we enter this, the line item exists, so we need to return the value of the line item. This will adjust the order total.
              
    return $line_item['amount'];
            }
          }
          break;
        case 
    'submit':
          
    // Here we perform any actions that our module requires for the module itself - i.e. database updates. In this tutorial there isn't actually anything to be saved to the database, but in my real module I had to save text for a card that would be attached to the order if the user chose gift wrapping. I did all my database manipulation, saving the message to the database, in this section.
          
    break;
      }
    }
    ?>

Step 4: Add any custom data to the order status page

This tutorial doesn't actually use this step. However, if your module needs to have some data that will be viewable on the order status page, you would add it using hook_order_pane() with an $op of 'customer'. In my real module this was the text that would be applied to the message card that when the user gets gift wrapping. This is a little out of the scope of my tutorial though, so I will leave this out for now unless someone specifically requests it.

Summary

So there you have it. In this tutorial we performed three steps:

  1. We defined a pane to be displayed on the checkout page
  2. We added javascript to update the total in real time if the user selects the line item
  3. We dealt with the line item throughout the order process, altering the total on the order review page

Hopefully this will save you some of the many troubles and headaches I had trying to figure out how this all worked. Good luck, and please feel free to leave any comments below!

Comments:

Thanks, this might be exactly what I'm looking for!

I'm going to use these steps for a project of mine. The shop owner wants to give visitors a free extra gift on orders above € 50,=, but they have to choose which gift they want, and which size.

I'll let you know if I have any success implementing this.

mission accomplished! I succeeded implementing this tutorial in my project.

Thanks a lot again for this very well documented tut!!!

Glad to help! yes

I wanted to thank you for this- it was a HUGE help to me as I needed to do exactly this.  I couldn't find any other resources that explained things as well as this-  your tutorial was perfect because I still am a "newbie" with respect to Drupal hooks, how arguements work, etc.  Regardless, I've got this working at www.bestdeckrailingproducts.com-  i had to make a lot of changes to the code for the specific "delivery options" pane you see there; however, your tutorial helped me immensley.  Thank you!  

Have you thought about contributing this to some Drupal.org or Ubercart documentation or handbooks?  This is even better than some of what they have there (at least specifically with respect to Ubercart and the pane and line item hooks)

I'm glad it helped. I agree with you that it was hard to find any kind of explanation on ubercart.org. Fortunately, I've got a pretty solid handle on how Drupal works as a whole, so it was just a matter of figuring out what hooks were being called, when they were being called, and what the values being passed were. The step where I had to use $arg2 to get the submitted values from the pane was particularly difficult to figure out. So I'm glad to hear that this helped!

I considered putting this on ubercart.org, but I decided against it. I've got a few modules up on drupal.org, and it's a fair bit of effort to maintiain them properly. There is a responsibility that goes along with being responsible for code on other people's sites. Same with putting this tutorial up on drupal.org - before contributing there, I want to wait until I have a pretty good handle on ubercart as a whole. I basically see how it works now, but I'm still relatively new to it, as I'm just finishing my first site that incorporates Ubercart into it.

So for now, I'll just keep my tutorial here. I'll come back to it at some point when I have a little more experience with it, and check that it all looks valid.

I understand and agree with your answer, thanks for sharing.  I certainly identify with what you're saying, as I now (thanks to your tutorial as well as struggling through a lot of trial & error in my own module development - especially since my module was a bit different than yours) feel that I have a decent understanding of how things are working (at least better than before!).  However, i still have a bunch of questions. 

If you don't mind, i thought I'd share some of my thoughts and questions here -  I'd certainly love to hear your input if you ever have time.

Question 1:

If i use

    function mymodule_pane_callback_function($op, &$arg1, &$arg2) {

    instead of

    function mymodule_pane_callback_function($op, &$arg1, $arg2) {

    I get this error upon checkout page load:

    Fatal error: Cannot pass parameter 3 by reference in /home/site/public_html/sites/all/modules/ubercart/uc_cart/uc_cart.pages.inc  on line 177

    Why?  What does the "&" preceding $arg2 do and is it needed?

I'm sure this is a result of a bit of ignorance on my part- with respect to standard variable passing between functions.   In your example you show that, however in my module i omitted the preceding "&" and everything works as expected.

 

Question 2:

In your module, you defined a callback function in your line item hook, and included your javascript, etc in the <span style="color: rgb(0, 0, 0);"><span style="color: rgb(221, 0, 0);">&#39;cart-preview&#39; case.</span></span>

In my module I eventually realized i didn't HAVE TO define a callback function for my line items, as i included the javascript etc. in the "view" case for my checkout pane callback function.  This was because i couldn't get anything to work ustin gthe 'cart-preview' case... but everything worked fine using the 'view' case.

Am i correct in assuming the "<span style="color: rgb(0, 0, 0);"><span style="color: rgb(221, 0, 0);">cart-preview&quot;</span></span> case for the line item hook callback function is only for the "cart" page pane (rather than the checkout page pane)? 

 

Question 3:

The module i created specifically created 2 new line items within the same line item hook function that are then displayed within the same pane.  I ideally wanted to add these line items to the already-existing Delivery Information Pane at the bottom...

do you know of a way to add line items & form fields to pre-existing checkout panes (e.g. panes in ubercart core, or panes created by other modules?)  ... i couldn't find any relevant information or hooks that would allow this in my research and reading.

 

Question 4:

http://www.ubercart.org/docs/api/hook_line_item shows the set_line_item() function to only accept 4 arguements:

set_line_item(key, title, value, weight)

Howerver, upon looking at code for example usage i see many cases where 6 arguements are supplied.  This is also referenced here as well: http://www.ubercart.org/forum/development/13958/new_hook_line_item_requi...

Do you know anything about the mysterious, undocumented 5th, and 6th arguments that are accepted by the set_line_item() function?

 

Thanks again for your contribution - it has been a pleasure learning and beginning to grasp Drupal hooks!

Do you have an example of a site? I think that this may be what I need to.

Thanks

If you go to http://www.pantspeople.com/ (may not be safe for work - its an underwear site) and look in their cart you can see an example with the gift wrapping options.

Im impressed. Youre truly well informed and very intelligent. You wrote something that people could understand and made the subject intriguing for everyone. Im saving this for future use.

Vivian
 

Hey Jay,

thanks for the tutorial.

Can u plz help me with this:

 

Order total in "order total preview" doesnt calculate correctly?

what might be wrong.

 

I am giving two types of discounts.

also adding GST

the calculation without tax is correct but doesnt add the GST to total after subtacting discounts.

I'm sorry, I'm not sure exactly what that would be, and unfortunately that's the kind of problem I would probably need direct access to the site plus an hour or two to properly diagnose.

Maybe it's something set on your conditional actions for taxes?

Thanks for the write up - very useful summary of line items, and saved a lot of research!

Some slight adjustments I had to make when implementing this -

  • In my case the corresponding javascript hook was not #edit-gift-wrap, but #edit-panes-gift-wrap-gift-wrap!
  • In the javascript, the set_line_item function was not working for me. This may have been a conflict with taxes. I resolved this by enforcing a weight value (the fourth argument) so the form is -
            // The definition for add_line_item() is: set_line_item(key, title, value, weight)
            set_line_item
            (
              "gift-wrap", // this value has to be the same as the ID defined in hook_line_item() or it will not update the total
              Drupal.t("Gift Wrapping"), // This is the text that will show up in the order total. I run it through Drupal.t() which is the javascript version of the t() translation function
              Drupal.settings.myModule.giftWrappingPrice, // Drupal.settings.myModule.giftWrappingPrice is the value of the discount that I passed from the PHP function
              0 //*the additional weight argument
            );
    

Thanks for the feedback!

awesome tutorial.  Im always happy to see anything that clears up some of the confusing docs from ubercart.  One gotcha to watch out for is the 'submit' op in hook_order.  It seems like a good place to do one-time processing on an order that just went through checkout, but some googling reveals that its real purpose is to do some validating during checkout (I know, validating during a submit op...).  This becomes a huge problem when you turn on uc_paypal, which not only calls hook_order with the submit op, but its called from a form building function, which gets run multiple times... 

Learning that fact almost ruined my weekend...

In the future, all processing for orders that just left checkout is going into hook_uc_checkout_complete()

Well Well Well. Awesome tutorial. Thanks a lot Man, helped me a lot. I owe you a MUG OF BEER. Cheers

Thanks very much for that.

Thank for the tutorial.

I need to add a line item but I don't need a new pane, because my line item price is based on Delivary Address.

I actually create the Line Item and it is adding an initial value that is fine, but when the delivery address is modified, it is not updating the Line Item price.

Really appreciate that you've shared this tutorial with the community. It's a excellent resouce. Great work.

Would it be possible for you to go a little further and include the steps necessary to add custom data to the order status page, as you've indicated having done in your module with the text applied to the message card by users getting gift wrapping.

thanks in advance.

It looks like some of the API changes in ubercart 2.6 might have changed this. A pre-save hook was added as well. 

When I use uc coupons or my custom discount, everything works. When I combine them, something break horribly!

I hope no one else is experiencing any issues from the latest changes.

I can confirm that I needed to switch to hook_order $op = presave to have this work now in ubercart 2.6. If anyone else is getting weird behavior, try that.

Hey thanks for a good article. You are right: you saved me a bunch of time Smile Didn't use exactly as prescribed, ( I still haven't bothered with Drupal JS too much because jQuery is so easy) but basically, yes. Part of a much bigger function for displaying and passing in a line item for delivery from multiple vendors with different delivery rates for different zip codes. I display a table of fees in real time, and process the fee on form submit, just like you did. If no javascript: no problem, they just don't get the real time info.Of course I had to add a couple of classes for support.

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
X
Loading