From 52c52431e58127763de41b5c1fba8103aa9dd0ff Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Sun, 18 Sep 2022 16:02:11 -0400 Subject: [PATCH] Afform - Add ContactDedupe Behavior This allow existing contacts to be updated per form rules --- .../Civi/Afform/Behavior/ContactDedupe.php | 76 +++++++++++++++++++ ext/afform/core/afform.php | 1 + .../phpunit/api/v4/AfformContactUsageTest.php | 55 ++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 ext/afform/core/Civi/Afform/Behavior/ContactDedupe.php diff --git a/ext/afform/core/Civi/Afform/Behavior/ContactDedupe.php b/ext/afform/core/Civi/Afform/Behavior/ContactDedupe.php new file mode 100644 index 0000000000..aec3c24e9f --- /dev/null +++ b/ext/afform/core/Civi/Afform/Behavior/ContactDedupe.php @@ -0,0 +1,76 @@ + ['onAfformSubmit', 101], + ]; + } + + public static function getEntities():array { + return \CRM_Contact_BAO_ContactType::basicTypes(); + } + + public static function getTitle():string { + return E::ts('Duplicate Matching'); + } + + public static function getDescription():string { + return E::ts('Update existing contact instead of creating a new one based on a dedupe rule.'); + } + + public static function getModes(string $entityName):array { + $modes = []; + $dedupeRuleGroups = \Civi\Api4\DedupeRuleGroup::get(FALSE) + ->addWhere('contact_type', '=', $entityName) + ->addOrderBy('used', 'DESC') + ->addOrderBy('title', 'ASC') + ->execute(); + foreach ($dedupeRuleGroups as $rule) { + // Use the generic API name for supervised/unsupervised rules as it's more portable + $name = ($rule['used'] === 'General' ? $rule['name'] : $entityName . '.' . $rule['used']); + $modes[] = [ + 'name' => $name, + 'label' => $rule['title'], + ]; + } + return $modes; + } + + public static function onAfformSubmit(AfformSubmitEvent $event) { + $entity = $event->getEntity(); + $dedupeMode = $entity['contact-dedupe'] ?? NULL; + if ($event->getEntityType() !== 'Contact' || !$dedupeMode) { + return; + } + // Apply dedupe rule if contact isn't already identified + foreach ($event->records as $index => $record) { + $supportedJoins = ['Address', 'Email', 'Phone', 'IM']; + $values = $record['fields'] ?? []; + foreach ($supportedJoins as $joinEntity) { + if (!empty($record['joins'][$joinEntity][0])) { + $values += \CRM_Utils_Array::prefixKeys($record['joins'][$joinEntity][0], strtolower($joinEntity) . '_primary.'); + } + } + $match = Contact::getDuplicates(FALSE) + ->setValues($values) + ->setDedupeRule($dedupeMode) + ->execute()->first(); + if (!empty($match['id'])) { + $event->setEntityId($index, $match['id']); + } + } + } + +} diff --git a/ext/afform/core/afform.php b/ext/afform/core/afform.php index c06facd1e8..c546334c83 100644 --- a/ext/afform/core/afform.php +++ b/ext/afform/core/afform.php @@ -58,6 +58,7 @@ function afform_civicrm_config(&$config) { $dispatcher->addListener('civi.afform.get', ['\Civi\Api4\Action\Afform\Get', 'getCustomGroupBlocks']); $dispatcher->addSubscriber(new \Civi\Api4\Subscriber\AutocompleteSubscriber()); $dispatcher->addSubscriber(new \Civi\Afform\Behavior\ContactAutofill()); + $dispatcher->addSubscriber(new \Civi\Afform\Behavior\ContactDedupe()); // Register support for email tokens if (CRM_Extension_System::singleton()->getMapper()->isActiveModule('authx')) { diff --git a/ext/afform/mock/tests/phpunit/api/v4/AfformContactUsageTest.php b/ext/afform/mock/tests/phpunit/api/v4/AfformContactUsageTest.php index 4d39f29771..f338f5980e 100644 --- a/ext/afform/mock/tests/phpunit/api/v4/AfformContactUsageTest.php +++ b/ext/afform/mock/tests/phpunit/api/v4/AfformContactUsageTest.php @@ -355,4 +355,59 @@ EOHTML; $this->assertEquals($locationType, $submission['data']['Organization1'][0]['_joins']['Email'][0]['location_type_id']); } + public function testDedupeIndividual(): void { + $layout = << + +
+ + + +
+ +
+
+ +EOHTML; + $this->useValues([ + 'layout' => $layout, + 'permission' => CRM_Core_Permission::ALWAYS_ALLOW_PERMISSION, + ]); + + $lastName = uniqid(__FUNCTION__); + $contact = \Civi\Api4\Contact::create(FALSE) + ->addValue('first_name', 'Bob') + ->addValue('last_name', $lastName) + ->addValue('email_primary.email', '123@example.com') + ->execute()->single(); + + $locationType = CRM_Core_BAO_LocationType::getDefault()->id; + $values = [ + 'Individual1' => [ + [ + 'fields' => [ + 'first_name' => 'Bob', + 'middle_name' => 'New', + 'last_name' => $lastName, + ], + 'joins' => [ + 'Email' => [ + ['email' => '123@example.com', 'location_type_id' => $locationType], + ], + ], + ], + ], + ]; + Civi\Api4\Afform::submit() + ->setName($this->formName) + ->setValues($values) + ->execute(); + + // Check that the contact was updated per dedupe rule + $result = \Civi\Api4\Contact::get(FALSE) + ->addWhere('id', '=', $contact['id']) + ->execute()->single(); + $this->assertEquals('New', $result['middle_name']); + } + } -- 2.25.1