From d5920b1d3541cca18fc07513a0195c0e03560852 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Mon, 29 Aug 2022 15:42:42 +1200 Subject: [PATCH] Switch Contact import to use new v4 dedupe lookup --- CRM/Contact/Import/Parser/Contact.php | 37 +++++++---- CRM/Import/Parser.php | 63 ++++++++++++++++++- .../CRM/Contact/Import/Parser/ContactTest.php | 1 - 3 files changed, 88 insertions(+), 13 deletions(-) diff --git a/CRM/Contact/Import/Parser/Contact.php b/CRM/Contact/Import/Parser/Contact.php index 4db1a240ecac..349b64e80e28 100644 --- a/CRM/Contact/Import/Parser/Contact.php +++ b/CRM/Contact/Import/Parser/Contact.php @@ -10,6 +10,7 @@ */ use Civi\Api4\Contact; +use Civi\Api4\DedupeRuleGroup; use Civi\Api4\RelationshipType; use Civi\Api4\StateProvince; @@ -1050,7 +1051,7 @@ 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. @@ -1058,21 +1059,19 @@ private function getSuccessMessage(): string { * @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) { + if (count($possibleMatches) > 1) { throw new CRM_Core_Exception(ts('Record duplicates multiple contacts: ') . implode(',', array_keys($possibleMatches['values'])), 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); @@ -1635,7 +1634,7 @@ protected function lookupContactID(array $params, bool $isMainContact): ?int { if (isset($params['relationship'])) { unset($params['relationship']); } - $id = $this->getPossibleContactMatch($params, $extIDMatch, $this->getSubmittedValue('dedupe_rule_id') ?: NULL); + $id = $this->getPossibleContactMatch($params, $extIDMatch, $this->getDedupeRuleName()); if ($id && $isMainContact && $this->isSkipDuplicates()) { throw new CRM_Core_Exception(ts('Contact matched by dedupe rule already exists in the database.'), CRM_Import_Parser::DUPLICATE); } @@ -1644,6 +1643,22 @@ protected function lookupContactID(array $params, bool $isMainContact): ?int { return NULL; } + /** + * Get the unique name of the rule group to use. + * + * @return string + * + * @throws \API_Exception + */ + protected function getDedupeRuleName(): string { + if ($this->getSubmittedValue('dedupe_rule_id')) { + return DedupeRuleGroup::get(FALSE) + ->addWhere('id', '=', $this->getSubmittedValue('dedupe_rule_id')) + ->addSelect('name')->execute()->first()['name']; + } + return $this->getContactType() . '.Unsupervised'; + } + /** * @param array $params * @param array $formatted diff --git a/CRM/Import/Parser.php b/CRM/Import/Parser.php index 70e266ed99a7..4b7cfea1ecba 100644 --- a/CRM/Import/Parser.php +++ b/CRM/Import/Parser.php @@ -1592,7 +1592,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)) { @@ -2026,4 +2026,65 @@ 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 string|int $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]); + } + } + $possibleMatches = Contact::getDuplicates(FALSE) + ->setValues($params) + ->setDedupeRule($dedupeRuleID) + ->execute(); + + $matchIDs = []; + foreach ($possibleMatches as $possibleMatch) { + $matchIDs[$possibleMatch['id']] = $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']; + } + } diff --git a/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php b/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php index c2d54066018b..fe124e3f3e9d 100644 --- a/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php +++ b/tests/phpunit/CRM/Contact/Import/Parser/ContactTest.php @@ -561,7 +561,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. *