Drupal 7 Commerce's Customer Profiles Seem to Endlessly Duplicate

In Drupal 7's Commerce module there's an annoying feature that clones a shipping and billing address when the smallest changes are performed on an existing order. This is how to fix that.

Without further ado...

If you want to hack the Commerce code for a quick fix then find the code in commerce_customer.module (Version: 7.x-1.14) at line 1152 right after 

1
2
3
// TODO: Trap it on error, rebuild the form with error messages.
// Notify field widgets to save the field data.
field_attach_submit('commerce_customer_profile', $profile, $element['profiles'][$key], $form_state);

and add this right below

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
if ($profile->entity_context['entity_type'] == 'commerce_order') {
  $order = commerce_order_load($profile->entity_context['entity_id']);
  if (isset($order->commerce_customer_billing['und'])) {
    if ($profile->type == 'billing') {
      $billing_profile_id = $order->commerce_customer_billing['und'][0]['profile_id'];
      $billing_profile = commerce_customer_profile_load($billing_profile_id);
      $billing_profile->commerce_customer_address = $profile->commerce_customer_address;
      commerce_customer_profile_save($billing_profile);
 
      // Add the profile ID to the current value of the reference field.
      $value[] = array('profile_id' => $billing_profile->profile_id);
    }
  }
 
  if (isset($order->commerce_customer_shipping['und'])) {
    if ($profile->type == 'shipping') {
      $shipping_profile_id = $order->commerce_customer_shipping['und'][0]['profile_id'];
      $shipping_profile = commerce_customer_profile_load($shipping_profile_id);
      $shipping_profile->commerce_customer_address = $profile->commerce_customer_address;
      commerce_customer_profile_save($shipping_profile);
 
      // Add the profile ID to the current value of the reference field.
      $value[] = array('profile_id' => $shipping_profile->profile_id);
    }
  }
 
  if (!isset($order->commerce_customer_shipping['und']) && !isset($order->commerce_customer_billing['und'])) {
    commerce_customer_profile_save($profile);
 
    // Add the profile ID to the current value of the reference field.
    $value[] = array('profile_id' => $profile->profile_id);
  }
}

Note: This code was only tested on the back-end admin order screen or the /admin/commerce/orders/%/edit/. Works on admin and customer facing order screens.

One last change. Locate commerce_order.module (Version: 7.x-1.14) on line 566. You should see

1
2
3
4
5
6
7
if ($field_change && !commerce_order_commerce_customer_profile_can_delete($profile)) {
   // Update various properties of the profile so it gets saved as new.
   $profile->profile_id = '';
   $profile->revision_id = '';
   $profile->created = REQUEST_TIME;
   $profile->log = '';
}

Comment out this entire if statement to where it should look like this

1
2
3
4
5
6
7
//if ($field_change && !commerce_order_commerce_customer_profile_can_delete($profile)) {
//   // Update various properties of the profile so it gets saved as new.
//   $profile->profile_id = '';
//   $profile->revision_id = '';
//   $profile->created = REQUEST_TIME;
//   $profile->log = '';
//}

Save and continue on!

Background Information

The code above basically does two things

  1. Asks the current order if it has any shipping or billing profile ids attached already, if so, it merges the recent billing/shipping profile changes to those.
  2. Next, and this is key, in commerce_order.module this bit of code is nested deep inside the Commerce controller. It's a last ditch effort to verify if the code should update an existing record or create a new one. Commenting out that if statement stops the save controller from inserting a new record. 

I had to write this code because I didn't want my customers to get confused on why the system would generate sooooo many shipping and billing profiles. Why not update the current one and call it a day?

 

A better solution

As time goes on, I'll update this page if I find a much better solution. I might explore one solution posted by another Drupal developer where he deactivated the previous profile ids rather then hacking the code like we did today.  To read what he did, follow this link.  I think Drupal Commerce should leave this as an option to override rather then building it in as a forced feature.  I'm comfortable having my clients access a previous order and making changes to address information without there being duplicates.  This type of model has worked well for QuickBooks which allows a user to change invoice data after it's been paid. Why not with Drupal Commerce?

Hacking source means you have to keep good records of where the changes were and be alert to updates that will override your custom code.

Helpful article

A Drupal developer disabled old or previous address profiles rather then deleting like I did. Here's the link Customer Profiles Endlessly Duplicate!

 

Leave a comment