7

I am working on a one page checkout and I'm trying to get the cart review pane updated with a shipping price while user selects a shipping option.

I have attached #ajax information to the shipping options, to update the cart review form when a shipping option is saved.

Modifying the commerce shipping ajax callback on checkout

if($form_id == 'commerce_checkout_form_checkout') {
  $form['commerce_shipping']['shipping_service']['#ajax']['callback'] = 'mshop_shipping_pane_service_details_refresh';
}

Ajax callback

function mshop_shipping_pane_service_details_refresh($form, $form_state) {
  // Update shipping form
  $commands[] = ajax_command_replace('#' . $form['commerce_shipping']['service_details']['#id'], render($form['commerce_shipping']['service_details']));

  // Update checkout cart review
  list($view_id, $display_id) = explode('|', variable_get('commerce_cart_contents_pane_view', 'commerce_cart_summary|default'));
  $commands[] = ajax_command_replace('.view-mshop-cart-shopping-cart-summary', commerce_embed_view($view_id, $display_id, array(arg(1))));

  return array('#type' => 'ajax', '#commands' => $commands);
}

The issue : The AJAX is working, the updated cart review view is updated, but the shipping option is not applied.

My guess is that, as the form is only updated and not submitted, the order is not saved. So as the cart review's view is based on the order, and the shipping option is not saved yet in the order, it does not display the right shipping information in the cart review.

Any workaround?

EDIT:

Thanks to milkovsky comment I have been able to save the order with the shipping rate applied to the order. Then the cart review view displays correctly with the updated cart rate applied.

/**
 * Ajax callback: Returns the shipping details form elements that match the
 * currently selected shipping service.
 */
function mshop_shipping_pane_service_details_refresh($form, &$form_state, $checkout_pane, $order) {

  // Get order from form
  list($order, $checkout_pane) = $form_state['build_info']['args'];

  // Load up to date order
  $order = commerce_order_load($order->order_id);

  // Get selected 
  $service_name = $form['commerce_shipping']['shipping_service']['#default_value'];

  commerce_shipping_service_rate_order($service_name, $order);

  // Delete any existing shipping line items from the order.
  commerce_shipping_delete_shipping_line_items($order, TRUE);

  // Extract the unit price from the calculated rate.
  $rate_line_item = $order->shipping_rates[$service_name];
  $rate_line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $rate_line_item);
  $unit_price = $rate_line_item_wrapper->commerce_unit_price->value();

  // Create a new shipping line item with the calculated rate from the form.
  $line_item = commerce_shipping_line_item_new($service_name, $unit_price, $order->order_id, $rate_line_item->data, $rate_line_item->type);

  // Save and add the line item to the order.
  $new_line_item = commerce_shipping_add_shipping_line_item($line_item, $order, TRUE);
  commerce_order_save($order);


  // Update shipping form
  $commands[] = ajax_command_replace('#' . $form['commerce_shipping']['service_details']['#id'], render($form['commerce_shipping']['service_details']));


  // Update checkout cart review
  list($view_id, $display_id) = explode('|', variable_get('commerce_cart_contents_pane_view', 'commerce_cart_summary|default'));
  $commands[] = ajax_command_replace('.view-mshop-cart-shopping-cart-summary', commerce_embed_view($view_id, $display_id, array($order->order_id)));

  $commands[] = ajax_command_after('.main form', theme('status_messages'));

  // $commands[] = ajax_command_after('.'.$form['#attributes']['class'][1].':eq(0)',theme('status_messages'));
  return array('#type' => 'ajax', '#commands' => $commands);
}
  • Check if you don't override existing callback using $form['commerce_shipping']['shipping_service']['#ajax']['callback'] = ... – milkovsky Jan 23 '15 at 11:46
  • I can't get it to work - is the code if($form_id == 'commerce_checkout_form_checkout') { $form['commerce_shipping']['shipping_service']['#ajax']['callback'] = 'mshop_shipping_pane_service_details_refresh'; } implementing hook_form_alter or some special commerce hook? – Dalmaz Feb 9 '15 at 20:01
  • Yes i'm implementing a basic hook_form_alter(). On my side it works on the latest version of Drupal Commerce. Is your callback executed? – E. de Saint Chamas Feb 13 '15 at 6:11
4
+50

You are right. Shipping rates are applied only after you submit the form (Click "Next").

On submit all your shipping rules(or flat rates) applied and order is saved with new line items (Shipping is implemented via custom line item type).

The calculation is executed in the commerce_shipping_pane_checkout_form_submit()

So you can try to execute this function (or some parts of it) in the ajax callback before the cart is updated.

Here is an example how to apply shipping rates if you are using using Flat Rate Module: Adding shipping to order programmatically via module.

  • 1
    Thanks, your second solution works like a charm. I preferred your first solution that did not require to load and save the order, in the middle of the checkout process, but did not succeed to implement correctly commerce_shipping_pane_checkout_form_submit() as it requires 2 variables I can't access in this function. I'll update my post with the solution. – E. de Saint Chamas Jan 26 '15 at 16:46
  • 1
    Epic answer and edit! Thx – Michiel Jul 9 '15 at 11:27
  • So, is there a way to force this through the UI? – nizz0k Mar 2 '17 at 6:26
2

Firstly, thanks for posting this solution!

I was getting this 500 error from the AJAX response: "EntityMetadataWrapperException: Missing data values. in EntityMetadataWrapper->value().

I wanted to offer free shipping on orders over $100 so enabled the commerce_order_total module (https://www.drupal.org/sandbox/joshmiller/2718047) which highlighted the error when selecting a shipping method.

After some debugging, I found it was only occurring when the commerce_order_save() line was included.

Since I have set my checkout Panes to all appear on the same page and my Review Pane is disabled, I wrapped this line in an IF statement:

//get checkout panes
$panes = commerce_checkout_panes();

//check if the Review pane is enabled
if ($panes['checkout_review']['enabled']){
  //review is enabled
  commerce_order_save($order);
}

Comments/improvements are encouraged!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.