The code in commerce_order_commerce_customer_profile_presave() checks whether values of a customer profile entity are equal to the original entity.
This behavior is used by the Commerce Addressbook module in order to assign an earlier entered customer profile to a new order.
The beauty of this is that it avoids creation of duplicate customer profiles in case the address info is always the same.
However a problem arises when a custom field has been added to the customer profile (e.g. VAT number).
The following code in the function checks for equality of the field data:
elseif ($profile->{$field_name} != $unchanged_profile->{$field_name}) {
$field_change = TRUE;
break;
}
However, upon testing this with a custom field, there is a difference between the 2 fields anyway:
$profile->field_vat_number = array(
und => array(
0 => array(
'value' => 'VAT #'
),
),
);
vs.
$unchanged_profile->field_vat_number = array(
und => array(
0 => array(
'value' => 'VAT #',
'format' => NULL,
'safe_value' => 'VAT #'
),
),
);
I.e. the extra array elements cause the profile to always be marked as different...
I suppose this isn't an easy problem to fix, and I haven't had the time to check how this could be achieved.
Hopefully someone has a better view on the problem and can come up with a solution.
Sven
| Comment | File | Size | Author |
|---|---|---|---|
| #6 | 1308754-improve-field-comparison.patch | 4.41 KB | bojanz |
| #2 | 1308754-improve-field-comparison.patch | 3.95 KB | bojanz |
| #5 | 1308754-improve-field-comparison.patch | 4.4 KB | bojanz |
Comments
Comment #1
rszrama commentedHow did your data change between the two instances? Is it that on save the additional parameters are added into it but we don't have those bits of data when we submit the checkout form?
Comment #2
bojanz commentedThe current code is just not good enough.
It has advanced comparison for addressfields (comparing added deltas, common columns), but for ordinary fields (which could just as easily have multiple deltas and columns) it just does a simple comparison.
Cases where it fails and causes a profile duplication:
1) When the "Use as shipping address" checkbox provided by Commerce Shipping is present on the form.
2) For any custom multivalue and / or multicolumn field (including other addressfields, such as here: #1361330: Customer profile duplication with more than one addressfield).
3) When a field is submitted empty. One entity has 'field_name' = array(), the other 'field_name' => array(LANGUAGE_NONE => array()), which fails the comparison.
4) The case from #0 (columns added by the system messing up the comparison)
Attaching a patch that makes the code much more bullet proof and fixes the problems listed above.
EDIT: Needs a bit of tweaking to account for removed values from custom multivalue fields. Will reroll after lunch.
Comment #3
it-cruHello together,
sounds a little bit like my problem (Drupal Commerce 7.x-1.2).
I try to save a profile loaded from an order in the hook_commerce_checkout_complete() to set their two fields with some external crm values. Everytime on commerce_customer_profile_save a new costomer profile revision is created in the database and so my added crm data will not be saved in the customer profile of the order. But the description of commerce_customer_profile_save() says that the profile is updated when a profile_id is present or missed I something in the $profile object to save it without a revision?
Perhaps someone have a little hint or idea.
Greetz
Steffen
Comment #4
it-cruOk find another solution by myself: hook_commerce_customer_profile_presave($profile) seems to be my friend to complete my extra profile fields with the data from the external data source =)
But the problem is, that this will possible create new data in the external data base before the order is completed...
Comment #5
bojanz commented@IT-Cru
Feel free to open a new issue.
I'm here focused on fixing a very specific function, anything else is unrelated.
Here's a new patch. Simpler than the last one, and covers the case when a value has been removed from a multivalue field.
Should fix all our problems :)
Comment #6
bojanz commentedIntroduced a notice.
It's amazing how we need this code:
Since empty fields can vary between $field_name => array() and $field_name => array($langcode => array()).
It's a proof of how bad Field API DX can actually be.
Comment #7
lisa.lite commentedcommerce-7.x-1.2
commerce_addressbook-7.x-2.0-rc1
commerce_shipping-7.x-2.0-beta1
applied patch #6
when checking "Use billing address as shipping address" the Shipping information customer profile is still duplicated
Comment #8
jazzdrive3 commentedI can also verify that profiles are duplicated when using the "Use billing address as shipping address" functionality of addressbook.
Comment #9
helior commentedTagging to make sure we review this for a Commerce 7.x-1.3 release.
Comment #10
helior commentedI also experienced duplicate profiles when using the "Use billing address as shipping address" feature on Commerce Shipping, but I think that's an unintentional flaw on the Commerce Shipping module. That checkbox value is saved along side the profile because of the nature in which it was implemented, but it is in fact just a setting that does not (and should not) persist on any profile. This patch is rock solid; I think there should be a follow-up issue on Commerce Shipping to rethink the way that feature is implemented.
Comment #11
bojanz commentedMy patch should only compare the common columns if I remember it correctly. Shipping only adds that column to one of the addressfields.
I'll do a quick checkup tonight to see if I missed something.
EDIT: Confirmed that everything is fine. Even when using that checkbox, you will get one billing and one shipping profile, that's expected and this patch doesn't change that.
Comment #12
rszrama commentedAlrighty, read it through and can't find any fault with the new logic. Test bot says it's clean, Helior says it's good, my test went fine... committing!
And for reference, here's a discussion I kicked off on fixing the "copy address" functionality. The way it's implemented isn't good, as it's really just a UI element tacked onto the form... not some bit of data that should be contained inside a profile object or an address field value (especially since copying an "address" may actually include more than just the address field): #1410022: Improve copy address functionality - create a more generic solution
Comment #14
mile23Project page still links to this patch: http://drupal.org/project/commerce_addressbook
Comment #15
asauterChicago commentedAny updates on this? This issue still persists in the latest commerce. Also, none of the patches here work anymore since it's patches commerce_order, which has since been changed fairly significantly since the last patch 2 years ago.
Comment #16
bojanz commented@sauterChicago
All fixes from this issue have been committed and are present in the last 7 or more releases.
Comment #17
tararowell commentedI am running 7.x-1.10 and I am still getting duplicated addressbook entries because of added fields on the customer profile form.
I am running Commerce Addressbook 7.x-2.0-rc7.
Thoughts?
Comment #18
jphelan commentedI'm also still getting duplicate address book entries for every order. Just test it after updating to Commerce Kickstart 7.x-2.19 and Commerce Addressbook 7.x-2.0-rc8. It's still duplicating both. Am I missing something?
Comment #19
WillsCreative commentedI'm receiving duplicates still as well.
Comment #20
Anonymous (not verified) commentedSame here
Comment #21
dmsmidtI thought this issue was still a problem, but in essence it seems fixed indeed.
My problem was caused by a module extending addressfield: addressfield_link.
A fix is here: https://www.drupal.org/node/2493505
Since that module borrows a lot from addressfield_phone, I guess that module is also problematic.
In general I would advice to check for problems in modules that add field types or extend them.
Remove fields from the profile and/or disable the extra modules and see if the problem persists