Right now, if you have a shipping line item on your order already and visit the checkout page, in the cart contents pane you'll see a record of that line item in the order totals section. However, if you change the shipping service, this stays the same. It's not a huge deal, because it's fairly understood there as the representation of the cart in its current state, but it would be nice if this area could be changed to reflect the current shipping selection. This would have to happen via fiddling with a copy of the order total field data - the price components would have to be manually adjusted, the field re-rendered, and then an AJAX command issued via the refresh callback to swap in the new table.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

emptyvoid’s picture

Having the same problem, I've spent about 3 - 10 hour days trying to wire up the panel updates via ajax calls. Because there is little to no documentation on the AJAX framework nor the panel factory I'm left to tracing execution paths using xdebug. Sadly I still doesn't work even after hacking addressfield and commerce_shipping.

We've added two shipping types and I can get the shipping methods to render and the shipping options to display on initial page load for the Checkout page. However, Selecting a country or state doesn't rebuild the shipping methods nor the shipping options in the shipping panels.

I've resorted to client-side events to force the form to be submitted to it self to process the address changes. This forces the panels to be recalculated and rendered correctly on the next page load. Obviously, this isn't the desired result. Rather we would like the panels to be updated on change.

In the addressfield/plugins/format/address.inc

line 83
// Those countries generally use their administrative division in postal addresses.
  if (in_array($address['country'], array('AR', 'AU', 'BR', 'BS', 'BY', 'BZ', 'CA', 'CN', 'DO', 'EG', 'ES', 'FJ', 'FM', 'GB', 'HN', 'ID', 'IE', 'IN', 'IT', 'JO', 'JP', 'KI', 'KN', 'KR', 'KW', 'KY', 'KZ', 'MX', 'MY', 'MZ', 'NG', 'NI', 'NR', 'NZ', 'OM', 'PA', 'PF', 'PG', 'PH', 'PR', 'PW', 'RU', 'SM', 'SO', 'SR', 'SV', 'TH', 'TW', 'UA', 'US', 'UY', 'VE', 'VI', 'VN', 'YU', 'ZA'))) {
    $format['locality_block']['administrative_area'] = array(
      '#title' => t('State'),
      '#size' => 10,
      '#required' => TRUE,
      '#prefix' => ' ',
      '#attributes' => array('class' => array('state')),
      '#ajax' => array(
        'callback' => 'commerce_shipping_pane_checkout_form_shipping_method_refresh',
        'wrapper' => 'edit-commerce-shipping-shipping-method',
        'method' => 'replace',
      ),
    );
  }

We added an ajax callback to rebuild the shipping methods section. Sadly it just renders the state of the methods when the page loaded. It doesn't appear to pick up on the new "changed" state in the browser.

Another issue is if you have different shipping methods based on rules where on set of shipping methods are used for US orders while another shipping method is used for International orders. The shipping methods panel doesn't validate the rules sets, nor update appropriately.

Any suggestions would be greatly appreciated.

arun_ms’s picture

Hello all,

I also have the same issue . Any help would be appreciated.

Subscribe.

Thanks

acidpotato’s picture

This feature is a requirement in my opinion for one page checkout config. Users need to see the total amount that will be charged on their card before submitting account info, billing info and cart contents. Shopify has this feature in their checkout process..

sportel’s picture

I´d like to see this feature to very much. I´ve configured some rules for adding a fee to the total price, depending on the selected payment method. It all works, except for the cart content pane isn't updated. So that's the only thing missing for some great ux. Unfortunately I'm not a developer, so I'm hoping someone can add this feature.

Thanks,

Mike.

developer_426’s picture

i want to update my cart pane content total when i select any of shipping method on same page...
can you tell me how i can update my cart content pane total ??

Thanks,
Jyoti

waltercat’s picture

Subscribing, I think this is pretty critical when it comes to the user experience. Is it possible to update the shipping line item when an address is changed?

donquixote’s picture

So, it seems the challenge here is to put the shipping line item into the view, before it is actually created?

So, either create a temporary line item as soon as someone switches the shipping service radios. And delete it when switching to another shipping service. And update it when the shipping cost for this service changes.

Or, do it all on client side. But this would be hard to do in a generic way.

donquixote’s picture

This one seems to be a solution. Can you try and evaluate?
https://github.com/donquixote/drupal-ajaxcart

loparr’s picture

Issue summary: View changes

Hi, works great. Thank you very much.

akosipax’s picture

It would be nice to have a hook_alter in the ajax callback so we can extend it to refresh the commerce_order_total. I attached a patch for this.

With this patch, I could then have a module that simply updates the commerce order total components (note: not the whole order summary table since that would be unnecessary)

// ajax_wrapper
define('ORDER_TOTAL_COMPONENTS_WRAPPER', 'order-total-components-ajax-wrapper');

/**
 * Implements hook_commerce_shipping_pane_service_details_refresh_alter().
 */
function my_module_commerce_shipping_pane_service_details_refresh_alter(&$commands, $form, $form_state) {
  // Load a fresh copy of the order stored in the form.
  $order = commerce_order_load($form_state['order']->order_id);
  $shipping_pane = commerce_checkout_pane_load('commerce_shipping');

  // Validate shipping pane
  if ($callback = commerce_checkout_pane_callback($shipping_pane, 'checkout_form_validate')) {
    $validate = $callback($form, $form_state, $shipping_pane, $order);
  }

  // Submit the pane if it validated.
  if ($validate && $callback = commerce_checkout_pane_callback($shipping_pane, 'checkout_form_submit')) {
    $callback($form, $form_state, $shipping_pane, $order);
    // Prepare a display settings array.
    $display = array(
      'label' => 'hidden',
      'type' => 'commerce_price_formatted_components',
      'settings' => array(
        'calculation' => FALSE,
      ),
    );

    $field = field_view_field('commerce_order', $order, 'commerce_order_total', $display);
    $commands[] = ajax_command_replace('#' . ORDER_TOTAL_COMPONENTS_WRAPPER, drupal_render($field));
  }
}

/**
 * Implements hook_field_attach_view_alter().
 */
function my_module_field_attach_view_alter(&$output, $context) {
  // Alter commerce_order_total output
  if (isset($output['commerce_order_total']) && 'commerce_order_total' == $output['commerce_order_total']['#field_name']) {
    if ('commerce_price_formatted_components' == $output['commerce_order_total']['#formatter']) {
      $output['commerce_order_total']['#prefix'] = '<div id="' . ORDER_TOTAL_COMPONENTS_WRAPPER . '">';
      $output['commerce_order_total']['#suffix'] = '</div>';
    }
  }
}
akosipax’s picture

Status: Active » Needs review
googletorp’s picture

Status: Needs review » Needs work

I don't like this approach:

$commands[] = ajax_command_replace('#' . $form_state['triggering_element']['#ajax']['wrapper'], drupal_render($form['commerce_shipping']['service_details']));

I have a sneaking suspicion that this will break in some cases. Unless we can find a more solid solution, I would opt for using hook_form_alter in cases where you want to do this and know that it wont break.

torgosPizza’s picture

I think @donquixote's sandbox module mentioned in #8 should be considered for integration (or at least, inclusion) with Commerce. That approach allows us to see a refreshed order total in the cart contents pane at Checkout when Shipping is selected.

torgosPizza’s picture

Upon further inspection, it seems like the sandbox module doesn't quite work exactly as needed. It only updates the value in the cart contents pane, but the issue is bigger than that: by default, when a shipping form is created, no relevant shipping line item is created for the order. The result is that, even if shipping changes, and the cart contents pane is refreshed, because of the way commerce_checkout_form() works it's possible that we'll still have panes with outdated values.

In our example we're using Commerce Stripe's iframe widget, which uses Javascript to pop up a checkout form. This form uses values passed in via JS / Drupal.settings etc. to build elements such as the total amount to be charged.

If a line item were to be created by default (using the default value / 1st shipping service available, or recreating the shipping line item when a service is selected) that would allow us to save the order to be current, and then update each pane in succession to have all the newest values. So far Commerce Shipping seems to be the only module that doesn't do this in a way that allows us to have more flexible (Ajaxified) checkout pages.

Unless I've totally missed some more correct way of doing this, I think Commerce Shipping should be updated so that a shipping line item is created or updated at the same time as the shipping services form is created and interacted with.

joelpittet’s picture

@torgosPizza #14 that sounds very reasonable & thorough. Any chance you can post a patch that would push this issue in that direction?

torgosPizza’s picture

I can definitely give it a shot. I'm looking at the way the Sandbox module from @donquixote works, as well as the Commerce Checkout Ajax module that is (was?) sponsored by Commerce Guys - not sure if the latter might solve this issue, though I suspect due to this issue being more or less at Commerce Shipping, even with that module enabled the problem would most likely persist.

Basically I have come to the realization that contrib modules for Commerce should do the following two things:

1. NEVER load an $order simply from the pane form / pane values, but instead load a fresh order (unless and until #1804592: During AJAX form submission in checkout, the $order argument passed by the Form API is incorrect. gets committed!)
2. If the module's Checkout pane adjusts the order in any way, the pane itself should update the order every time it is interacted with, that way when commerce_checkout_form() gets called again, the latest version of $order bubbles up to the rest of the checkout panes and their forms.

The inherent problem I can see from the Sandbox module mentioned above is that it doesn't do anything in a commerce_checkout_form() callback, so when that function is fired (and all of its dynamic $callbacks are then subsequently fired) they are out of scope, since the sandbox doesn't have a checkout form callback implementation, and taht is where our current issue seems to reside.

Sorry for the extra info, I'm kind of doing a brain-dump from hours of testing and attempting to fix. I'll see if the other module helps things at all, and will also attempt to fix the issue at the core - by having the Shipping pane also save the $order in the same way other contrib Checkout panes do. :)

torgosPizza’s picture

Priority: Normal » Major
Status: Needs work » Needs review
FileSize
1.78 KB

Here's my first attempt, and it seems to work.

When shipping rates are found (and a default is found along with it) a line item for the shipping rate is added to the order.

This is an expected behavior for customer UX and so I'm setting priority to Major.

Unfortunately there is one minor issue that is introduced. Here are the problematic steps:

1. Add a digital AND physical product to a cart;
2. Visit /checkout so shipping rates are calculated and applied;
3. Click "cancel" to go back to /cart
4. Delete the physical item from the cart;
5. Go back to checkout.

The "shipping" line item and fee will still be there, because the shipping pane is no longer being built, which deletes shipping line items with every build.

One way around this might be to update the "default" Rule that is included with Commerce Shipping to delete all shipping line items when an order is moved into a Cart status, to include a check for the Checkout: Checkout state as well.

Feedback and additional contributions are welcome :)

torgosPizza’s picture

Issue tags: +commerce-sprint

Tagging for Commerce Sprint. To me this is unexpected behavior and so having the default shipping service selected and saved to the order immediately after fetching it seems like a no-brainer.

Also I updated the default Rule as mentioned above; I can export that here (or include in patch) for testing. It essentially checks to make sure the updated order is in Checkout : Checkout (from a different status) and if so it deletes the shipping line items, allowing them to be updated again (much like the default). It seems to work perfectly in our case (a one-page checkout) but of course more helping would be nice.

torgosPizza’s picture

FileSize
3.97 KB

Here's a patch that sets the default shipping service, but also updates the default Rule to check for both Cart and Checkout as new statuses. Might still not work for all cases but I think the majority should be fine here. Though I am wondering if checking for a new "commerce_shipping" status might work best.

mdupree’s picture

Just to make sure I've read this thread right. The problem was the shipping line item not being updated in the order total ( after switching the shipping item ) correct ? I have tested this including torgosPizza's patch from #19, the adjustments are now happening to the order totals on the review page.

msti’s picture

The patch at #19 creates the line item but does not refresh the cart.

This patch combines the parts of the sandbox module from #8 with the patch from #19 so that the cart is refreshed via ajax when the shipping changes.

mdupree’s picture

@msti

Using commerce_kickstart install, commerce_shipping 7.x-2.x-dev.

+++ b/commerce_shipping.module
@@ -1188,3 +1189,68 @@ function commerce_shipping_recalculate_services($form, $form_state = NULL, $igno
+    if (isset($form['commerce_shipping']['shipping_service'])) {
+      $form['commerce_shipping']['shipping_service']['#ajax'] = array(
+        'callback' => '_commerce_shipping_ajax_service_has_changed',
+      );
+    }

This part never gets reached.. my form doesn't contain ['commerce_shipping'] at the point of this if check. What am I missing here...
The carts updating shipping on change.

torgosPizza’s picture

@mdupree: Does your form have a shipping charges / services pane on it? That's when that function would be called. Unless Kickstart does something special with that pane, which is always a possibility.

mdupree’s picture

@torgosPizza: It does not, at least not at the point where the functions called. Is there a Commerce build other then Kickstart that would be good for testing this ticket? Sorry I know this is off topic from the issue queue.
Thanks !

mdupree’s picture

Ok so I made some adjustments and I can now reach the code. I am going to review and will update this thread with my findings !
Thanks

Update:
After further testing, I can confirm the patch from #21.
Pane updates on shipping services change.
So far I give this a thumbs up.

googletorp’s picture

Status: Needs review » Needs work

I don't like where this patch is moving.

I think we are making too many assumptions about the configuration of the store and it seems a lot of what's going on here should not happen in the shipping module.

I'm fine with keeping the patch here for people to use, but I don't think we can get such a patch into the shipping module. It's to frail (it's easy to break with small changes in commerce) and I think it's doing too for me to maintain it in the future.

Ultimately, it should be the cart module that handles updating the cart if something changes, or atleast exposed some ajax/API to do this.

Sardis’s picture

Status: Needs work » Needs review
FileSize
8.99 KB

For those, who decided to use #21 patch.
Here comes minor improvement.
The thing is that when you have discount, applied to the order, and select another shipping service, discount's beign removed from cart contents.
Also small CS fixes included.

Sardis’s picture

Further improvement.
Cart contents do not update on initial page load.
Assuming we use single checkout page. And upon changing locality (shipping services may be different depending on city) recalculation of shipping services takes place. Cart contents do not update even though shipping services changed.
Applying patch, that triggers cart contents update upon shipping recalculation.

P.S. Would be good to hear any ideas about how to do this better (maybe?). And to trigger changes only if changed value of selected value in dropdown is different from the initial value.

P.P.S. Check mode changes in my patch. It's not intentionally.

arunkumark’s picture

Hi All,

The patch #28 [https://www.drupal.org/node/1287126#comment-11354777] is properly updating the cart information based on shipping information changes.

But, Shipping service is may depend on the Shipping information. There is a configuration for this to update at admin/commerce/config/checkout/form/pane/commerce_shipping. For this scenario, we need to update cart information when shipping information changes.

shipping_information

I have updated the patch to support both scenarios.

arunkumark’s picture

Issue summary: View changes
JulienF’s picture

The patch in #29 is great, I'm in a situation where I want to have a one page checkout and needed to have these Ajax updates properly handled.

I amended it a little bit so that it will also support updating payment methods according to two conditions:
1. When we change the shipping method we trigger an update of the payment method (in case a payment method isn't available for a particular shipping method) --> this is a case that we faced we some customers and want to support that case

2. When we refresh the shipping methods (on country change) we also need to refresh the payment methods. This particular piece of code doesn't really belong to commerce_shipping, but it's a two liner and I didn't know where to place it. I've put it there for two reasons though, 1st if somebody else wants to have a one page checkout setup, with this patch these cases will be covered out of the box, 2nd so you can have a look at it and let me know where should that go (cf. Line 210 of includes/commerce_shipping.checkout_pane.inc file to see what I mean)

JulienF’s picture

Previous patch wasn't including changes in the js and rules files.

Corrected that in this last patch

JulienF’s picture

I've added a specific name for the recalculate shipping fee button so the focus (after the Ajax triggered) doesn't wrongly get assigned to the last input of the form (Cancel submit) but gets assigned to the button itself, and doesn't get assigned anywhere if the button is not present.

This is to correct a UX issue that when you change any field in the address field, the focus is being made on the last input field that has the same name as the triggering element.

This issue is still present if not shipping rates are returned given that the button is showing on screen and the browser is thus giving focus to it.

If you know how to stop this Focus from happening that is more than welcome as this is a major User experience issue (you fill in a field in your shipping address and you get moved from this field to that button... you have to scroll back up to continue filling in the form till you get a valid address that returns shipping rates...

JulienF’s picture

Actually a quick way to fix the refocus when no shipping rates are found is to simply hide the button.

This works like a charm but the patch should be rewritten to include some conditions so that the display none trick is only applied if the pane is configured to update the rates through Ajax... in that case showing this button doesn't make any sense since rates should be refreshed when there is a change in the address of the customer.

Let me know if you have any feedback and feel free to enhance it.

JulienF’s picture

It seems the work made on #27 is not enough to keep on showing the discount(s) added to the order. The cart needs to be refreshed at each Ajax call so the discounts stays visible after an Ajax call.

This new patch fixes this issue

JulienF’s picture

This new update on the patch fixes the issue of showing the wrong button at the bottom of the checkout page when editing the address.

Sardis’s picture

  1. +++ b/commerce_shipping.module
    @@ -1210,3 +1224,93 @@
    +  //to fix the problem where the coupon/discount's gets removed from cart contents ¶
    ...
    +  ¶
    ...
    +  $payment_pane = drupal_render($form['commerce_payment']);  ¶
    ...
    \ No newline at end of file
    
    +++ b/commerce_shipping.rules_defaults.inc
    @@ -16,25 +16,49 @@
    +  ¶
    ...
    +  ¶
    ...
    +  ¶
    ...
    +    ¶
    
    +++ b/includes/commerce_shipping.checkout_pane.inc
    @@ -129,6 +129,30 @@
    +    ¶
    ...
    +    ¶
    
    @@ -173,7 +197,32 @@
    +  ¶
    ...
    +    $payment_pane = drupal_render($form['commerce_payment']);  ¶
    

    There are a couple of spaces on empty lines & at the end of lines. Consider doing phpcbf on these modifications in order to fix all CS issues.

  2. +++ b/js/commerce_shipping.js
    @@ -36,9 +63,9 @@
    diff --git a/nbproject/project.properties b/nbproject/project.properties
    
    +++ b/nbproject/project.properties
    @@ -0,0 +1,7 @@
    diff --git a/nbproject/project.xml b/nbproject/project.xml
    

    What these files are for?

vlad.dancer’s picture

Status: Needs review » Needs work
JorgenSandstrom’s picture

Updated patch that applies cleanly to dev and current 7.x-2.3. Also linted.

fadi.assaad’s picture

This update changed the way shipping recalculation is made. Instead of invoking recalculating after each shipping profile field change, its triggered manually through a save button.

This save button will be available in add/edit shipping profile fieldset.

fadi.assaad’s picture

Stopped updating the continue button value.