}
/**
- * Compare 2 addresses to see if they are the same.
+ * Compare 2 addresses to see if they are the effectively the same.
+ *
+ * Being the same would mean same location type and any populated fields that describe the locationn match.
+ *
+ * Metadata fields such as is_primary, on_hold, manual_geocode may differ.
*
* @param array $mainAddress
* @param array $comparisonAddress
return TRUE;
}
+ /**
+ * Does the location array have valid data.
+ *
+ * While not UI-creatable some sites wind up with email or address rows with no actual email or address
+ * through non core-UI processes.
+ *
+ * @param array $location
+ *
+ * @return bool
+ */
+ public static function locationHasData($location) {
+ return !empty(self::getLocationDataFields($location));
+ }
+
+ /**
+ * Get the location data from a location array, filtering out metadata.
+ *
+ * This returns data like street_address but not metadata like is_primary, on_hold etc.
+ *
+ * @param array $location
+ *
+ * @return mixed
+ */
+ public static function getLocationDataFields($location) {
+ $keysToIgnore = array_merge(self::ignoredFields(), ['display', 'location_type_id']);
+ foreach ($location as $field => $value) {
+ if (in_array($field, $keysToIgnore, TRUE)) {
+ unset($location[$field]);
+ }
+ }
+ return $location;
+ }
+
/**
* A function to build an array of information about location blocks that is
* required when merging location fields
// If it exists on the 'main' contact already, skip it. Otherwise
// if the location type exists already, log a conflict.
foreach ($migrationInfo['main_details']['location_blocks'][$fieldName] as $mainAddressKey => $mainAddressRecord) {
+ if (!self::locationHasData($mainAddressRecord)) {
+ // Go ahead & overwrite the main address - it has no data in it.
+ // if it is the primary address then pass that honour to the address that actually has data.
+ $migrationInfo['location_blocks'][$fieldName][$mainAddressKey]['set_other_primary'] = $mainAddressRecord['is_primary'];
+ continue;
+ }
if (self::locationIsSame($addressRecord, $mainAddressRecord)) {
unset($migrationInfo[$key]);
- break;
+ continue;
}
- elseif ($addressRecordLocTypeId == $mainAddressRecord['location_type_id']) {
+ if ($addressRecordLocTypeId == $mainAddressRecord['location_type_id']) {
$conflicts[$key] = NULL;
- break;
+ continue;
}
}
}
}
+ /**
+ * Test that a blank location does not overwrite a location with data.
+ *
+ * This is a poor data edge case where a contact has an address record with no meaningful data.
+ * This record should be removed in favour of the one with data.
+ *
+ * @dataProvider getBooleanDataProvider
+ *
+ * @param bool $isReverse
+ *
+ * @throws \CRM_Core_Exception
+ */
+ public function testMergeWithBlankLocationData($isReverse) {
+ $this->createLoggedInUser();
+ $this->ids['contact'][0] = $this->callAPISuccess('contact', 'create', $this->_params)['id'];
+ $this->ids['contact'][1] = $this->callAPISuccess('contact', 'create', $this->_params)['id'];
+ $contactIDWithBlankAddress = ($isReverse ? $this->ids['contact'][1] : $this->ids['contact'][0]);
+ $contactIDWithoutBlankAddress = ($isReverse ? $this->ids['contact'][0] : $this->ids['contact'][1]);
+ $this->callAPISuccess('Address', 'create', [
+ 'contact_id' => $contactIDWithBlankAddress,
+ 'location_type_id' => 1,
+ ]);
+ $this->callAPISuccess('Address', 'create', [
+ 'country_id' => 'MX',
+ 'contact_id' => $contactIDWithoutBlankAddress,
+ 'street_address' => 'First on the left after you cross the border',
+ 'postal_code' => 90210,
+ 'location_type_id' => 1,
+ ]);
+
+ $contact = $this->doMerge($isReverse);
+ $this->assertEquals('Mexico', $contact['country']);
+ $this->assertEquals('90210', $contact['postal_code']);
+ $this->assertEquals('First on the left after you cross the border', $contact['street_address']);
+ }
+
/**
* Test merging 2 contacts with custom fields.
*
$this->callAPISuccess('Contact', 'delete', ['id' => $contact2, 'skip_undelete' => 1]);
}
+ /**
+ * Do the merge on the 2 contacts.
+ *
+ * @param bool $isReverse
+ *
+ * @return array|int
+ * @throws \CRM_Core_Exception
+ */
+ protected function doMerge($isReverse = FALSE) {
+ $this->callAPISuccess('Contact', 'merge', [
+ 'to_keep_id' => $isReverse ? $this->ids['contact'][1] : $this->ids['contact'][0],
+ 'to_remove_id' => $isReverse ? $this->ids['contact'][0] : $this->ids['contact'][1],
+ 'auto_flip' => FALSE,
+ ]);
+ return $this->callAPISuccessGetSingle('Contact', ['id' => $isReverse ? $this->ids['contact'][1] : $this->ids['contact'][0]]);
+ }
+
}