From 26b7ff288f849339f6cee1f0b92c95bd5d3b8431 Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 16 Aug 2018 09:55:29 +1200 Subject: [PATCH] Fix duplicate merge to not disregard zero values. Currently a field with 0 in it will not be carried across in a merge. In fact in a checkbox situation the differenct between 0 and null is significant (I chose not to opt in vs I was never presented with a choice). This patch preserves the 0. I would note that further digging is required to see how a confict is handled - ie. if one contact has 0 & the other has 1 is this blocked as a confligct Additional test on conflict handling --- CRM/Dedupe/Merger.php | 7 +++-- tests/phpunit/api/v3/JobTest.php | 53 +++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/CRM/Dedupe/Merger.php b/CRM/Dedupe/Merger.php index afb53df5f5..a3431a9ec3 100644 --- a/CRM/Dedupe/Merger.php +++ b/CRM/Dedupe/Merger.php @@ -619,8 +619,11 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m } $key1 = CRM_Utils_Array::value($key, $mainEvs); $key2 = CRM_Utils_Array::value($key, $otherEvs); - // CRM-17556 Get all non-empty fields, to make comparison easier - if (!empty($key1) || !empty($key2)) { + // We wish to retain '0' as it has a different meaning than NULL on a checkbox. + // However I can't think of a case where an empty string is more meaningful than null + // or where it would be FALSE or something else nullish. + $valuesToIgnore = [NULL, '', []]; + if (!in_array($key1, $valuesToIgnore, TRUE) || !in_array($key2, $valuesToIgnore, TRUE)) { $result['custom'][] = $key; } } diff --git a/tests/phpunit/api/v3/JobTest.php b/tests/phpunit/api/v3/JobTest.php index 9795dfb0f0..b584cccd55 100644 --- a/tests/phpunit/api/v3/JobTest.php +++ b/tests/phpunit/api/v3/JobTest.php @@ -77,6 +77,7 @@ class api_v3_JobTest extends CiviUnitTestCase { // The membershipType create breaks transactions so this extra cleanup is needed. $this->membershipTypeDelete(array('id' => $this->membershipTypeID)); $this->cleanUpSetUpIDs(); + $this->quickCleanup(['civicrm_contact', 'civicrm_address', 'civicrm_email', 'civicrm_website', 'civicrm_phone'], TRUE); } /** @@ -916,7 +917,57 @@ class api_v3_JobTest extends CiviUnitTestCase { $mouse = $this->callAPISuccess('Contact', 'getsingle', $mouseParams); $this->assertEquals('blah', $mouse['custom_' . $customField['id']]); - $this->customFieldDelete($customGroup['id']); + $this->customFieldDelete($customField['id']); + $this->customGroupDelete($customGroup['id']); + } + + /** + * Test the batch merge retains 0 as a valid custom field value. + * + * Note that we set 0 on 2 fields with one on each contact to ensure that + * both merged & mergee fields are respected. + */ + public function testBatchMergeCustomDataZeroValueField() { + $customGroup = $this->CustomGroupCreate(); + $customField = $this->customFieldCreate(array('custom_group_id' => $customGroup['id'], 'default_value' => NULL)); + $customField2 = $this->customFieldCreate(array('custom_group_id' => $customGroup['id'], 'label' => 'field2', 'default_value' => NULL)); + + $mouseParams = ['first_name' => 'Mickey', 'last_name' => 'Mouse', 'email' => 'tha_mouse@mouse.com']; + $this->individualCreate(array_merge($mouseParams, ['custom_' . $customField['id'] => 0])); + $this->individualCreate(array_merge($mouseParams, ['custom_' . $customField2['id'] => 0])); + + $result = $this->callAPISuccess('Job', 'process_batch_merge', array('check_permissions' => 0, 'mode' => 'safe')); + $this->assertEquals(1, count($result['values']['merged'])); + $mouseParams['return'] = 'custom_' . $customField['id']; + $mouse = $this->callAPISuccess('Contact', 'getsingle', $mouseParams); + $this->assertEquals(0, $mouse['custom_' . $customField['id']]); + + $this->customFieldDelete($customField['id']); + $this->customFieldDelete($customField2['id']); + $this->customGroupDelete($customGroup['id']); + } + + /** + * Test the batch merge treats 0 vs 1 as a conflict. + */ + public function testBatchMergeCustomDataZeroValueFieldWithConflict() { + $customGroup = $this->CustomGroupCreate(); + $customField = $this->customFieldCreate(array('custom_group_id' => $customGroup['id'], 'default_value' => NULL)); + + $mouseParams = ['first_name' => 'Mickey', 'last_name' => 'Mouse', 'email' => 'tha_mouse@mouse.com']; + $mouse1 = $this->individualCreate(array_merge($mouseParams, ['custom_' . $customField['id'] => 0])); + $mouse2 = $this->individualCreate(array_merge($mouseParams, ['custom_' . $customField['id'] => 1])); + + $result = $this->callAPISuccess('Job', 'process_batch_merge', array('check_permissions' => 0, 'mode' => 'safe')); + $this->assertEquals(0, count($result['values']['merged'])); + + // Reverse which mouse has the zero to test we still get a conflict. + $this->individualCreate(array_merge($mouseParams, ['id' => $mouse1, 'custom_' . $customField['id'] => 1])); + $this->individualCreate(array_merge($mouseParams, ['id' => $mouse2, 'custom_' . $customField['id'] => 0])); + $result = $this->callAPISuccess('Job', 'process_batch_merge', array('check_permissions' => 0, 'mode' => 'safe')); + $this->assertEquals(0, count($result['values']['merged'])); + + $this->customFieldDelete($customField['id']); $this->customGroupDelete($customGroup['id']); } -- 2.25.1