Skip to content

Commit

Permalink
Switch Contact import to use new v4 dedupe lookup
Browse files Browse the repository at this point in the history
  • Loading branch information
eileenmcnaughton committed Aug 29, 2022
1 parent 7af1299 commit 00aae2c
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 17 deletions.
21 changes: 10 additions & 11 deletions CRM/Contact/Import/Parser/Contact.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*/

use Civi\Api4\Contact;
use Civi\Api4\DedupeRuleGroup;
use Civi\Api4\RelationshipType;
use Civi\Api4\StateProvince;

Expand Down Expand Up @@ -1050,29 +1051,27 @@ private function getSuccessMessage(): string {
*
* @param array $params
* @param int|null $extIDMatch
* @param int|null $dedupeRuleID
* @param int|string $dedupeRuleID
*
* @return int|null
* IDs of a possible.
*
* @throws \CRM_Core_Exception
* @throws \CiviCRM_API3_Exception
*/
protected function getPossibleContactMatch(array $params, ?int $extIDMatch, ?int $dedupeRuleID): ?int {
$checkParams = ['check_permissions' => FALSE, 'match' => $params, 'dedupe_rule_id' => $dedupeRuleID];
$possibleMatches = civicrm_api3('Contact', 'duplicatecheck', $checkParams);
protected function getPossibleContactMatch(array $params, ?int $extIDMatch, $dedupeRuleID): ?int {
$possibleMatches = $this->getPossibleMatchesByDedupeRule($params, $dedupeRuleID);
if (!$extIDMatch) {
if (count($possibleMatches['values']) === 1) {
return array_key_last($possibleMatches['values']);
if (count($possibleMatches) === 1) {
return array_key_last($possibleMatches);
}
if (count($possibleMatches['values']) > 1) {
throw new CRM_Core_Exception(ts('Record duplicates multiple contacts: ') . implode(',', array_keys($possibleMatches['values'])), CRM_Import_Parser::ERROR);

if (count($possibleMatches) > 1) {
throw new CRM_Core_Exception(ts('Record duplicates multiple contacts: ') . implode(',', array_keys($possibleMatches)), CRM_Import_Parser::ERROR);
}
return NULL;
}
if ($possibleMatches['count']) {
if (array_key_exists($extIDMatch, $possibleMatches['values'])) {
if (count($possibleMatches) > 0) {
if (array_key_exists($extIDMatch, $possibleMatches)) {
return $extIDMatch;
}
throw new CRM_Core_Exception(ts('Matching this contact based on the de-dupe rule would cause an external ID conflict'), CRM_Import_Parser::ERROR);
Expand Down
92 changes: 91 additions & 1 deletion CRM/Import/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Civi\Api4\Campaign;
use Civi\Api4\Contact;
use Civi\Api4\CustomField;
use Civi\Api4\DedupeRuleGroup;
use Civi\Api4\Event;
use Civi\Api4\UserJob;
use Civi\UserJob\UserJobInterface;
Expand Down Expand Up @@ -947,6 +948,33 @@ protected function checkContactDuplicate(&$formatValues) {
return ['is_error' => 0];
}

/**
* Get the default dedupe rule name for the contact type.
*
* @param string $contactType
*
* @return string
*/
protected function getDefaultRuleForContactType(string $contactType): string {
return $contactType . '.Unsupervised';
}

/**
* Get the dedupe rule name.
*
* @param int $id
*
* @return string
*
* @throws \CRM_Core_Exception
*/
protected function getDedupeRuleName(int $id): string {
return DedupeRuleGroup::get(FALSE)
->addWhere('id', '=', $id)
->addSelect('name')
->execute()->first()['name'];
}

/**
* This function adds the contact variable in $values to the
* parameter list $params. For most cases, $values should have length 1. If
Expand Down Expand Up @@ -1592,7 +1620,7 @@ protected function getContactMatchingFields(): array {
* @throws \API_Exception
*/
protected function getFieldEntity(string $fieldName) {
if ($fieldName === 'do_not_import') {
if ($fieldName === 'do_not_import' || $fieldName === '') {
return '';
}
if (in_array($fieldName, ['email_greeting_id', 'postal_greeting_id', 'addressee_id'], TRUE)) {
Expand Down Expand Up @@ -2026,4 +2054,66 @@ protected function lookupExternalIdentifier(?string $externalIdentifier, string
return (int) $foundContact['id'];
}

/**
* Get contacts that match the input parameters, using a dedupe rule.
*
* @param array $params
* @param int|null $dedupeRuleID
*
* @return array
*
* @throws \CRM_Core_Exception
*/
protected function getPossibleMatchesByDedupeRule(array $params, $dedupeRuleID): array {
foreach (['email', 'address', 'phone', 'im'] as $locationEntity) {
if (array_key_exists($locationEntity, $params)) {
// Prefer primary
if (array_key_exists('Primary', $params[$locationEntity])) {
$locationParams = $params[$locationEntity]['Primary'];
}
else {
// Chose the first one - at least they can manipulate the order.
$locationParams = reset($params[$locationEntity]);
}
foreach ($locationParams as $key => $locationParam) {
// Even though we might not be using 'primary' we 'pretend' here
// since the apiv4 code expects that...
$params[$locationEntity . '_primary' . '.' . $key] = $locationParam;
}
unset($params[$locationEntity]);
}
}
foreach ($params as $key => $value) {
if (strpos($key, 'custom_') === 0) {
$params[$this->getApi4Name($key)] = $value;
unset($params[$key]);
}
}
$dedupeRule = $dedupeRuleID ? $this->getDedupeRuleName($dedupeRuleID) : $this->getDefaultRuleForContactType($params['contact_type']);
$possibleMatches = Contact::getDuplicates(FALSE)
->setValues($params)
->setDedupeRule($dedupeRule)
->execute();

$matchIDs = [];
foreach ($possibleMatches as $possibleMatch) {
$matchIDs[(int) $possibleMatch['id']] = (int) $possibleMatch['id'];
}
return $matchIDs;
}

/**
* Get the Api4 name of a custom field.
*
* @param string $key
*
* @throws \CRM_Core_Exception
*/
protected function getApi4Name(string $key): string {
return Contact::getFields(FALSE)
->addWhere('custom_field_id', '=', $this->getFieldMetadata($key)['custom_field_id'])
->addSelect('name')
->execute()->first()['name'];
}

}
5 changes: 1 addition & 4 deletions tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,7 @@ public function tearDown(): void {
/**
* Test that import parser will add contact with employee of relationship.
*
* @throws \API_Exception
* @throws \CRM_Core_Exception
* @throws \CiviCRM_API3_Exception
*/
public function testImportParserWithEmployeeOfRelationship(): void {
$this->organizationCreate([
Expand Down Expand Up @@ -561,7 +559,6 @@ public function testContactLocationBlockHandling(): void {
$this->callAPISuccessGetCount('IM', ['contact_id' => $id], 1);
}


/**
* Test whether importing a contact using email match will match a non-primary.
*
Expand Down Expand Up @@ -2277,7 +2274,7 @@ protected function importValues($userJobID, array $values, string $expected): vo
$parser->import($values);
$dataSource = new CRM_Import_DataSource_SQL($userJobID);
$row = $dataSource->getRow();
$this->assertEquals($expected, $row['_status']);
$this->assertEquals($expected, $row['_status'], print_r($row, TRUE));
}

}
2 changes: 1 addition & 1 deletion tests/phpunit/CRMTraits/Import/ParserTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ protected function importCSV(string $csv, array $fieldMappings, array $submitted
'errorMode' => CRM_Queue_Runner::ERROR_ABORT,
]);
$result = $runner->runAll();
$this->assertEquals(TRUE, $result, $result === TRUE ? '' : $result['exception']->getMessage());
$this->assertEquals(TRUE, $result, $result === TRUE ? '' : CRM_Core_Error::formatTextException($result['exception']));
}
}

Expand Down

0 comments on commit 00aae2c

Please sign in to comment.