From 03390e26e0dbe54336e2d2221d060120b75a99dd Mon Sep 17 00:00:00 2001 From: yashodha Date: Sat, 3 May 2014 00:50:13 +0530 Subject: [PATCH] CRM-14134 : allow option to configure dedupe rule per event --- CRM/Contact/Form/DedupeRules.php | 2 +- CRM/Dedupe/BAO/RuleGroup.php | 27 +++- CRM/Event/Form/ManageEvent/Registration.php | 145 +++++++++++++++++- CRM/Event/Form/Registration/Register.php | 32 ++-- CRM/Upgrade/Incremental/php/FourFive.php | 1 + .../Incremental/sql/4.5.alpha1.mysql.tpl | 5 + .../Event/Form/ManageEvent/Registration.hlp | 10 +- .../Event/Form/ManageEvent/Registration.tpl | 32 ++-- xml/schema/Event/Event.xml | 23 +++ 9 files changed, 228 insertions(+), 49 deletions(-) diff --git a/CRM/Contact/Form/DedupeRules.php b/CRM/Contact/Form/DedupeRules.php index e803024133..534af8525a 100644 --- a/CRM/Contact/Form/DedupeRules.php +++ b/CRM/Contact/Form/DedupeRules.php @@ -196,7 +196,7 @@ UPDATE civicrm_dedupe_rule_group AND used = %2"; $queryParams = array( 1 => array($this->_contactType, 'String'), - 2 => array($this->_options[$used], 'String'), + 2 => array($values['used'], 'String'), ); CRM_Core_DAO::executeQuery($query, $queryParams); diff --git a/CRM/Dedupe/BAO/RuleGroup.php b/CRM/Dedupe/BAO/RuleGroup.php index 20fbcc0cd9..825c2b3b59 100644 --- a/CRM/Dedupe/BAO/RuleGroup.php +++ b/CRM/Dedupe/BAO/RuleGroup.php @@ -375,8 +375,14 @@ class CRM_Dedupe_BAO_RuleGroup extends CRM_Dedupe_DAO_RuleGroup { */ static function dedupeRuleFieldsWeight($params) { $rgBao = new CRM_Dedupe_BAO_RuleGroup(); - $rgBao->used = $params['used']; $rgBao->contact_type = $params['contact_type']; + if (CRM_Utils_Array::value('id', $params)) { + // accept an ID if provided + $rgBao->id = $params['id']; + } + else { + $rgBao->used = $params['used']; + } $rgBao->find(TRUE); $ruleBao = new CRM_Dedupe_BAO_Rule(); @@ -390,6 +396,25 @@ class CRM_Dedupe_BAO_RuleGroup extends CRM_Dedupe_DAO_RuleGroup { return array($ruleFields, $rgBao->threshold); } + /** + * Get all of the combinations of fields that would work with a rule + */ + + static function combos($rgFields, $threshold, &$combos, $running = array()) { + foreach ($rgFields as $rgField => $weight) { + unset($rgFields[$rgField]); + $diff = $threshold - $weight; + $runningnow = $running; + $runningnow[] = $rgField; + if ($diff > 0) { + self::combos($rgFields, $diff, $combos, $runningnow); + } + else { + $combos[] = $runningnow; + } + } + } + /** * Get an array of rule group id to rule group name * for all th groups for that contactType. If contactType diff --git a/CRM/Event/Form/ManageEvent/Registration.php b/CRM/Event/Form/ManageEvent/Registration.php index 02e2b7b337..de33780b5d 100644 --- a/CRM/Event/Form/ManageEvent/Registration.php +++ b/CRM/Event/Form/ManageEvent/Registration.php @@ -284,10 +284,16 @@ class CRM_Event_Form_ManageEvent_Registration extends CRM_Event_Form_ManageEvent $this->addElement('checkbox', 'allow_same_participant_emails', - ts('Allow multiple registrations from the same email address?') + ts('Same email address?') ); $this->assign('ruleFields', json_encode($ruleFields)); + $dedupeRules = array( + '' => '- Unsupervised rule -', + ); + $dedupeRules += CRM_Dedupe_BAO_RuleGroup::getByType('Individual'); + $this->add('select', 'dedupe_rule_group_id', ts('Duplicate matching rule'), $dedupeRules); + $participantStatuses = CRM_Event_PseudoConstant::participantStatus(); if (in_array('Awaiting approval', $participantStatuses) and in_array('Pending from approval', $participantStatuses) and in_array('Rejected', $participantStatuses)) { $this->addElement('checkbox', @@ -335,7 +341,7 @@ class CRM_Event_Form_ManageEvent_Registration extends CRM_Event_Form_ManageEvent /** * Subroutine to insert a Profile Editor widget * depends on getProfileSelectorTypes - * + * * @param array &$form * @param int $count unique index * @param string $prefix dom element ID prefix @@ -493,6 +499,7 @@ class CRM_Event_Form_ManageEvent_Registration extends CRM_Event_Form_ManageEvent self::addMultipleProfiles($additionalProfileIds, $values, 'additional_custom_post_id_multiple'); $isProfileComplete = self::isProfileComplete($profileIds); $isAdditionalProfileComplete = self::isProfileComplete($additionalProfileIds); + //Check main profiles have an email address available if 'send confirmation email' is selected if ($values['is_email_confirm']) { $emailFields = self::getEmailFields($profileIds); @@ -675,6 +682,86 @@ class CRM_Event_Form_ManageEvent_Registration extends CRM_Event_Form_ManageEvent return $profileComplete; } + /** + * Check if the profiles collect enough information to dedupe + * + * @return boolean + */ + + function canProfilesDedupe($profileIds, $rgId = 0) { + + // find the unsupervised rule + + $rgParams = array( + 'used' => 'Unsupervised', + 'contact_type' => 'Individual', + ); + if ($rgId > 0) { + $rgParams['id'] = $rgId; + } + $activeRg = CRM_Dedupe_BAO_RuleGroup::dedupeRuleFieldsWeight($rgParams); + + // get the combinations that could be a match for the rule + $okCombos = $combos = array(); + CRM_Dedupe_BAO_RuleGroup::combos($activeRg[0], $activeRg[1], $combos); + + // create an index of what combinations involve each field + $index = array(); + foreach ($combos as $comboid => $combo) { + foreach ($combo as $cfield) { + $index[$cfield][$comboid] = true; + } + $combos[$comboid] = array_fill_keys($combo, 0); + $okCombos[$comboid] = array_fill_keys($combo, 2); + } + + // get profiles and see if they have the necessary combos + $profileReqFields = array(); + foreach ($profileIds as $profileId) { + if ($profileId && is_numeric($profileId)) { + $fields = CRM_Core_BAO_UFGroup::getFields($profileId); + + // walk through the fields in the profile + foreach ($fields as $field) { + + // check each of the fields in the index against the profile field + foreach ($index as $ifield => $icombos) { + if(strpos($field['name'], $ifield) !== false) { + + // we found the field in the profile, now record it in the index + foreach ($icombos as $icombo => $dontcare) { + $combos[$icombo][$ifield] = ($combos[$icombo][$ifield] != 2 && !$field['is_required']) ? 1 : 2; + + if ($combos[$icombo] == $okCombos[$icombo]) { + // if any combo is complete with 2s (all fields are present and required), we can go home + return 2; + } + } + } + } + } + } + } + + // check the combos to see if everything is > 0 + foreach ($combos as $comboid => $combo) { + $complete = false; + foreach ($combo as $cfield) { + if ($cfield > 0) { + $complete = true; + } + else { + // this combo isn't complete--skip to the next combo + continue 2; + } + } + if ($complete) { return 1; } + } + + // no combo succeeded + return 0; + } + /** * Add additional profiles from the form to an array of profile ids. * @@ -824,6 +911,60 @@ class CRM_Event_Form_ManageEvent_Registration extends CRM_Event_Form_ManageEvent } } + // get the profiles to evaluate what they collect + $profileIds = array( + CRM_Utils_Array::value('custom_pre_id', $params), + CRM_Utils_Array::value('custom_post_id', $params), + ); + $additionalProfileIds = array( + CRM_Utils_Array::value('additional_custom_pre_id', $params), + CRM_Utils_Array::value('additional_custom_post_id', $params), + ); + // additional profile fields default to main if not set + if (!is_numeric($additionalProfileIds[0])) { + $additionalProfileIds[0] = $profileIds[0]; + } + if (!is_numeric($additionalProfileIds[1])) { + $additionalProfileIds[1] = $profileIds[1]; + } + //add multiple profiles if set + self::addMultipleProfiles($profileIds, $params, 'custom_post_id_multiple'); + self::addMultipleProfiles($additionalProfileIds, $params, 'additional_custom_post_id_multiple'); + + $cantDedupe = false; + $rgId = CRM_Utils_Array::value('dedupe_rule_group_id', $params, 0); + + switch (self::canProfilesDedupe($profileIds, $rgId)) { + case 0: + $dedupeTitle = 'Duplicate Matching Impossible'; + $cantDedupe = ts("The selected profiles do not contain the fields necessary to match registrations with existing contacts. This means all anonymous registrations will result in a new contact."); + break; + case 1: + $dedupeTitle = 'Duplicate Contacts Possible'; + $cantDedupe = ts("The selected profiles can collect enough information to match registrations with existing contacts, but not all of the relevant fields are required. Anonymous registrations may result in duplicate contacts."); + } + if (!empty($params['is_multiple_registrations'])) { + switch(self::canProfilesDedupe($additionalProfileIds, $rgId)) { + case 0: + $dedupeTitle = 'Duplicate Matching Impossible'; + if ($cantDedupe) { + $cantDedupe = ts("The selected profiles do not contain the fields necessary to match registrations with existing contacts. This means all anonymous registrations will result in a new contact."); + } + else { + $cantDedupe = ts("The selected profiles do not contain the fields necessary to match additional participants with existing contacts. This means all additional participants will result in a new contact."); + } + break; + case 1: + if (!$cantDedupe) { + $dedupeTitle = 'Duplicate Contacts Possible'; + $cantDedupe = ts("The selected profiles can collect enough information to match additional participants with existing contacts, but not all of the relevant fields are required. This may result in duplicate contacts."); + } + } + } + if ($cantDedupe) { + CRM_Core_Session::setStatus($cantDedupe, $dedupeTitle, 'alert dedupenotify', array('expires' => 0)); + } + // Update tab "disabled" css class $this->ajaxResponse['tabValid'] = !empty($params['is_online_registration']); diff --git a/CRM/Event/Form/Registration/Register.php b/CRM/Event/Form/Registration/Register.php index 3cc97a84ae..01c295f602 100644 --- a/CRM/Event/Form/Registration/Register.php +++ b/CRM/Event/Form/Registration/Register.php @@ -1400,33 +1400,21 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration { if (!$contactID && is_array($fields) && $fields) { - //CRM-6996 - //as we are allowing w/ same email address, - //lets check w/ other contact params. - if ($self->_values['event']['allow_same_participant_emails']) { - $params = $fields; - $level = ($isAdditional) ? 'Supervised' : 'Unsupervised'; + //CRM-14134 use Unsupervised rule for everyone + $dedupeParams = CRM_Dedupe_Finder::formatParams($fields, 'Individual'); - $dedupeParams = CRM_Dedupe_Finder::formatParams($params, 'Individual'); + // disable permission based on cache since event registration is public page/feature. + $dedupeParams['check_permission'] = FALSE; - // disable permission based on cache since event registration is public page/feature. - $dedupeParams['check_permission'] = FALSE; - $ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, 'Individual', $level); - $contactID = CRM_Utils_Array::value(0, $ids); + // find event dedupe rule + if (CRM_Utils_Array::value('dedupe_rule_group_id', $self->_values['event'], 0) > 0) { + $ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, 'Individual', 'Unsupervised', array(), $self->_values['event']['dedupe_rule_group_id']); } else { - foreach ($fields as $fieldname => $fieldvalue) { - if (substr($fieldname, 0, 6) == 'email-') { - $emailString = trim($fieldvalue); - if (!empty($emailString)) { - $match = CRM_Contact_BAO_Contact::matchContactOnEmail($emailString, 'Individual'); - if (!empty($match)) { - $contactID = $match->contact_id; - } - } - } - } + $ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, 'Individual', 'Unsupervised'); } + $contactID = CRM_Utils_Array::value(0, $ids); + } if ($returnContactId) { diff --git a/CRM/Upgrade/Incremental/php/FourFive.php b/CRM/Upgrade/Incremental/php/FourFive.php index 961a0fe2d2..8d5bef7f17 100644 --- a/CRM/Upgrade/Incremental/php/FourFive.php +++ b/CRM/Upgrade/Incremental/php/FourFive.php @@ -65,6 +65,7 @@ class CRM_Upgrade_Incremental_php_FourFive { if ($rev == '4.5.alpha1') { $postUpgradeMessage .= '

' . ts('Default versions of the following System Workflow Message Templates have been modified to handle new functionality: If you have modified these templates, please review the new default versions and implement updates as needed to your copies (Administer > Communications > Message Templates > System Workflow Messages).'); $postUpgradeMessage .= '

' . ts('This release allows you to view and edit multiple-record custom field sets in a table format which will be more usable in some cases. You can try out the format by navigating to Administer > Custom Data & Screens > Custom Fields. Click Settings for a custom field set and change Display Style to "Tab with Tables".'); + $postUpgradeMessage .= '

' . ts('This release changes the way that anonymous event registrations match participants with existing contacts. By default, all event participants will be matched with existing individuals using the Unsupervised rule, even if multiple registrations with the same email address are allowed. However, you can now select a different matching rule to use for each event. Please review your events to make sure you choose the appropriate matching rule and collect sufficient information for it to match contacts.'); } } diff --git a/CRM/Upgrade/Incremental/sql/4.5.alpha1.mysql.tpl b/CRM/Upgrade/Incremental/sql/4.5.alpha1.mysql.tpl index cbe6623559..f9b5197cfc 100644 --- a/CRM/Upgrade/Incremental/sql/4.5.alpha1.mysql.tpl +++ b/CRM/Upgrade/Incremental/sql/4.5.alpha1.mysql.tpl @@ -379,3 +379,8 @@ DELETE FROM civicrm_option_group WHERE id = @option_group_id_case_type; UPDATE civicrm_event SET is_confirm_enabled = 0 + +-- CRM-11182 +ALTER TABLE civicrm_event + ADD COLUMN dedupe_rule_group_id int(10) unsigned DEFAULT NULL COMMENT 'Rule to use when matching registrations for this event', + ADD CONSTRAINT `FK_civicrm_event_dedupe_rule_group_id` FOREIGN KEY (`dedupe_rule_group_id`) REFERENCES `civicrm_dedupe_rule_group` (`id`); diff --git a/templates/CRM/Event/Form/ManageEvent/Registration.hlp b/templates/CRM/Event/Form/ManageEvent/Registration.hlp index 02213b8099..18a509600b 100644 --- a/templates/CRM/Event/Form/ManageEvent/Registration.hlp +++ b/templates/CRM/Event/Form/ManageEvent/Registration.hlp @@ -71,8 +71,14 @@ {ts}Allow Shared Email{/ts} {/htxt} {htxt id="id-allow_same_email"} -

{ts}Check this box to allow a user to register multiple participants using the same email address. If you want additional participants to be registered without requiring an email address to be entered for each person - check the "Register multiple participants" option, AND include a profile in this registration form which includes First Name and Last Name fields.{/ts}

-

{ts}You will also need to set the default supervised duplicate contact matching rule for Individuals to one that includes First Name and Last Name fields (Contacts » Find and Merge Duplicate Contacts).{/ts}

+

{ts}Check this box to allow a user to register multiple participants using the same email address. Alternatively, if you want additional participants to be registered without requiring an email address to be entered for each person - check the "Register multiple participants" option, AND include a profile in this registration form which includes First Name and Last Name fields.{/ts}

+{/htxt} + +{htxt id="id-dedupe_rule_group_id-title"} + {ts}Duplicate Matching Rule{/ts} +{/htxt} +{htxt id="id-dedupe_rule_group_id"} +

{ts}By default, your event will use the Unsupervised duplicate matching rule to match participants in anonymous registrations with existing individuals. You may select another rule to use for this event instead. Make sure that your included profile(s) contain the fields needed by your matching rule.{/ts}

{/htxt} {htxt id="id-requires_approval-title"} diff --git a/templates/CRM/Event/Form/ManageEvent/Registration.tpl b/templates/CRM/Event/Form/ManageEvent/Registration.tpl index bd9dea58a4..f5cb13bbd1 100644 --- a/templates/CRM/Event/Form/ManageEvent/Registration.tpl +++ b/templates/CRM/Event/Form/ManageEvent/Registration.tpl @@ -91,6 +91,10 @@ {$form.allow_same_participant_emails.label} {$form.allow_same_participant_emails.html} {help id="id-allow_same_email"} + + {$form.dedupe_rule_group_id.label} + {$form.dedupe_rule_group_id.html} {help id="id-dedupe_rule_group_id"} + {if $form.requires_approval} {$form.requires_approval.label} @@ -403,21 +407,9 @@ invert = 0 {/if} diff --git a/xml/schema/Event/Event.xml b/xml/schema/Event/Event.xml index fc7efaa9c2..c0e04fad70 100644 --- a/xml/schema/Event/Event.xml +++ b/xml/schema/Event/Event.xml @@ -814,4 +814,27 @@ Select + + dedupe_rule_group_id + int unsigned + Dedupe Rule + NULL + Rule to use when matching registrations for this event + + civicrm_dedupe_rule_group
+ id + name + title +
+ 4.5 + + Select + +
+ + dedupe_rule_group_id + civicrm_dedupe_rule_group
+ id + 4.5 +
-- 2.25.1