Changing address on shipping information makes the profile in the inline form stale

Created on 22 December 2021, over 3 years ago
Updated 6 December 2024, 4 months ago

Problem/Motivation

I think I'm seeing a situation on the shipping information pane where the shipping profile cached in the inline form gets out of sync with the actual profile; in particular the profile is saved after being set on the form, and the next time the form's loaded, the cached profile erroneously reports that it's new.

I'm afraid I don't really know much about inline forms. I can see that a call to \Drupal\commerce\Plugin\Commerce\InlineForm\EntityInlineFormInterface::setEntity() clones the passed entity, and that in \Drupal\commerce_shipping\Plugin\Commerce\CheckoutPane\ShippingInformation::buildPaneForm() if there's an explicit call to save the profile, then the saved profile's also passed to the inline form:

// Ensure the profile is saved with the latest address, it's necessary
// to do that in case the profile isn't new, otherwise the shipping profile
// referenced by the shipment won't reflect the updated address.
if (!$shipping_profile->isNew() &&
  $shipping_profile->hasTranslationChanges() &&
  $can_calculate_rates) {
  $shipping_profile->save();
  $inline_form->setEntity($shipping_profile);
}

However it's possible for buildPaneForm() to trigger a profile save later on without updating the inline form: either by saving the shipment, or the order:

// Update the shipments and save the order if no rate was explicitly
// selected, that usually occurs when changing addresses, this will ensure
// the default rate is selected/applied.
if (!$this->hasRateSelected($pane_form, $form_state) && ($recalculate_shipping || $force_packing)) {
  array_map(function (ShipmentInterface $shipment) {
    if (!$shipment->isNew()) {
      $shipment->save();
    }
  }, $shipments);
  $this->order->set('shipments', $shipments);
  $this->order->save();
}

Currently if that happens, the entity associated with the inline form remains unchanged (for example it will still show as new, and therefore won't be resaved in the next call to \Drupal\commerce_shipping\Plugin\Commerce\CheckoutPane\ShippingInformation::buildPaneForm()). Also, the implicit profile save comes after \Drupal\commerce_shipping\PackerManagerInterface::packToShipments() and \Drupal\commerce_shipping\Plugin\Field\FieldWidget\ShippingRateWidget::formElement() which might be problematic (I don't know).

Steps to reproduce

I haven't tried to do a minimal repro, but here's what I'm seeing locally:

  1. On the shipping information page I fill out all the fields. On the AJAX recalculate submission a profile is saved, but it's not updated in the inline form (ie not call to \Drupal\commerce\Plugin\Commerce\InlineForm\EntityInlineFormInterface::setEntity()).
  2. I change the country: on the AJAX request the profile isn't explicitly resaved in \Drupal\commerce_shipping\Plugin\Commerce\CheckoutPane\ShippingInformation::buildPaneForm() because it appears to be a new profile.
  3. I fill out the new country-specific fields and now the profile is saved.

In my situation I have an order processor that responds to the profile address, and I'm seeing it act on stale data in step 2 above.

Proposed resolution

I really know nothing about inline forms. Given that it clones the entity, I wonder if there aren't already existing patterns to follow to help guarantee you don't end up with the actual entity and cloned entity out of sync?

Remaining tasks

Is it ok that the profile might only get saved after \Drupal\commerce_shipping\PackerManagerInterface::packToShipments() and \Drupal\commerce_shipping\Plugin\Field\FieldWidget\ShippingRateWidget::formElement()?

🐛 Bug report
Status

Needs review

Version

2.0

Component

Code

Created by

🇬🇧United Kingdom AndyF

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

Production build 0.71.5 2024