Commit | Line | Data |
---|---|---|
0622d221 | 1 | <?php |
0622d221 | 2 | |
3 | /** | |
4 | * Class CRM_Dedupe_DedupeMergerTest | |
763a0fec | 5 | * |
acb109b7 | 6 | * @group headless |
0622d221 | 7 | */ |
8 | class CRM_Dedupe_MergerTest extends CiviUnitTestCase { | |
9 | ||
10 | protected $_groupId; | |
763a0fec | 11 | |
12 | protected $_contactIds = []; | |
0622d221 | 13 | |
2f10fa02 | 14 | /** |
15 | * Tear down. | |
16 | * | |
17 | * @throws \Exception | |
18 | */ | |
3308aac0 | 19 | public function tearDown() { |
763a0fec | 20 | $this->quickCleanup([ |
21 | 'civicrm_contact', | |
22 | 'civicrm_group_contact', | |
23 | 'civicrm_group', | |
24 | ]); | |
3308aac0 | 25 | parent::tearDown(); |
26 | } | |
27 | ||
0622d221 | 28 | public function createDupeContacts() { |
29 | // create a group to hold contacts, so that dupe checks don't consider any other contacts in the DB | |
763a0fec | 30 | $params = [ |
31 | 'name' => 'Test Dupe Merger Group', | |
32 | 'title' => 'Test Dupe Merger Group', | |
33 | 'domain_id' => 1, | |
34 | 'is_active' => 1, | |
0622d221 | 35 | 'visibility' => 'Public Pages', |
763a0fec | 36 | ]; |
87a56b12 | 37 | |
38 | $result = $this->callAPISuccess('group', 'create', $params); | |
0622d221 | 39 | $this->_groupId = $result['id']; |
40 | ||
41 | // contact data set | |
42 | ||
43 | // make dupe checks based on based on following contact sets: | |
44 | // FIRST - LAST - EMAIL | |
45 | // --------------------------------- | |
46 | // robin - hood - robin@example.com | |
47 | // robin - hood - robin@example.com | |
48 | // robin - hood - hood@example.com | |
49 | // robin - dale - robin@example.com | |
50 | // little - dale - dale@example.com | |
51 | // little - dale - dale@example.com | |
52 | // will - dale - dale@example.com | |
53 | // will - dale - will@example.com | |
54 | // will - dale - will@example.com | |
763a0fec | 55 | $params = [ |
56 | [ | |
0622d221 | 57 | 'first_name' => 'robin', |
58 | 'last_name' => 'hood', | |
59 | 'email' => 'robin@example.com', | |
60 | 'contact_type' => 'Individual', | |
763a0fec | 61 | ], |
62 | [ | |
0622d221 | 63 | 'first_name' => 'robin', |
64 | 'last_name' => 'hood', | |
65 | 'email' => 'robin@example.com', | |
66 | 'contact_type' => 'Individual', | |
763a0fec | 67 | ], |
68 | [ | |
0622d221 | 69 | 'first_name' => 'robin', |
70 | 'last_name' => 'hood', | |
71 | 'email' => 'hood@example.com', | |
72 | 'contact_type' => 'Individual', | |
763a0fec | 73 | ], |
74 | [ | |
0622d221 | 75 | 'first_name' => 'robin', |
76 | 'last_name' => 'dale', | |
77 | 'email' => 'robin@example.com', | |
78 | 'contact_type' => 'Individual', | |
763a0fec | 79 | ], |
80 | [ | |
0622d221 | 81 | 'first_name' => 'little', |
82 | 'last_name' => 'dale', | |
83 | 'email' => 'dale@example.com', | |
84 | 'contact_type' => 'Individual', | |
763a0fec | 85 | ], |
86 | [ | |
0622d221 | 87 | 'first_name' => 'little', |
88 | 'last_name' => 'dale', | |
89 | 'email' => 'dale@example.com', | |
90 | 'contact_type' => 'Individual', | |
763a0fec | 91 | ], |
92 | [ | |
0622d221 | 93 | 'first_name' => 'will', |
94 | 'last_name' => 'dale', | |
95 | 'email' => 'dale@example.com', | |
96 | 'contact_type' => 'Individual', | |
763a0fec | 97 | ], |
98 | [ | |
0622d221 | 99 | 'first_name' => 'will', |
100 | 'last_name' => 'dale', | |
101 | 'email' => 'will@example.com', | |
102 | 'contact_type' => 'Individual', | |
763a0fec | 103 | ], |
104 | [ | |
0622d221 | 105 | 'first_name' => 'will', |
106 | 'last_name' => 'dale', | |
107 | 'email' => 'will@example.com', | |
108 | 'contact_type' => 'Individual', | |
763a0fec | 109 | ], |
110 | ]; | |
0622d221 | 111 | |
112 | $count = 1; | |
113 | foreach ($params as $param) { | |
114 | $param['version'] = 3; | |
115 | $contact = civicrm_api('contact', 'create', $param); | |
116 | $this->_contactIds[$count++] = $contact['id']; | |
117 | ||
763a0fec | 118 | $grpParams = [ |
0622d221 | 119 | 'contact_id' => $contact['id'], |
763a0fec | 120 | 'group_id' => $this->_groupId, |
121 | 'version' => 3, | |
122 | ]; | |
87a56b12 | 123 | $this->callAPISuccess('group_contact', 'create', $grpParams); |
0622d221 | 124 | } |
125 | } | |
126 | ||
93ac19cd | 127 | /** |
128 | * Delete all created contacts. | |
129 | */ | |
0622d221 | 130 | public function deleteDupeContacts() { |
0622d221 | 131 | foreach ($this->_contactIds as $contactId) { |
93ac19cd | 132 | $this->contactDelete($contactId); |
0622d221 | 133 | } |
87a56b12 | 134 | $this->groupDelete($this->_groupId); |
0622d221 | 135 | } |
136 | ||
c8ec0753 | 137 | /** |
a354251e | 138 | * Test the batch merge. |
c8ec0753 | 139 | */ |
0622d221 | 140 | public function testBatchMergeSelectedDuplicates() { |
141 | $this->createDupeContacts(); | |
142 | ||
143 | // verify that all contacts have been created separately | |
144 | $this->assertEquals(count($this->_contactIds), 9, 'Check for number of contacts.'); | |
145 | ||
146 | $dao = new CRM_Dedupe_DAO_RuleGroup(); | |
147 | $dao->contact_type = 'Individual'; | |
148 | $dao->name = 'IndividualSupervised'; | |
149 | $dao->is_default = 1; | |
150 | $dao->find(TRUE); | |
151 | ||
152 | $foundDupes = CRM_Dedupe_Finder::dupesInGroup($dao->id, $this->_groupId); | |
153 | ||
154 | // ------------------------------------------------------------------------- | |
155 | // Name and Email (reserved) Matches ( 3 pairs ) | |
156 | // -------------------------------------------------------------------------- | |
157 | // robin - hood - robin@example.com | |
158 | // robin - hood - robin@example.com | |
159 | // little - dale - dale@example.com | |
160 | // little - dale - dale@example.com | |
161 | // will - dale - will@example.com | |
162 | // will - dale - will@example.com | |
163 | // so 3 pairs for - first + last + mail | |
164 | $this->assertEquals(count($foundDupes), 3, 'Check Individual-Supervised dupe rule for dupesInGroup().'); | |
165 | ||
166 | // Run dedupe finder as the browser would | |
39b959db SL |
167 | //avoid invalid key error |
168 | $_SERVER['REQUEST_METHOD'] = 'GET'; | |
0622d221 | 169 | $object = new CRM_Contact_Page_DedupeFind(); |
170 | $object->set('gid', $this->_groupId); | |
171 | $object->set('rgid', $dao->id); | |
172 | $object->set('action', CRM_Core_Action::UPDATE); | |
64fe2fe0 | 173 | $object->setEmbedded(TRUE); |
0622d221 | 174 | @$object->run(); |
175 | ||
176 | // Retrieve pairs from prev next cache table | |
763a0fec | 177 | $select = ['pn.is_selected' => 'is_selected']; |
b1679439 | 178 | $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($dao->id, $this->_groupId); |
0622d221 | 179 | $pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select); |
0622d221 | 180 | $this->assertEquals(count($foundDupes), count($pnDupePairs), 'Check number of dupe pairs in prev next cache.'); |
181 | ||
182 | // mark first two pairs as selected | |
183 | CRM_Core_DAO::singleValueQuery("UPDATE civicrm_prevnext_cache SET is_selected = 1 WHERE id IN ({$pnDupePairs[0]['prevnext_id']}, {$pnDupePairs[1]['prevnext_id']})"); | |
184 | ||
185 | $pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select); | |
186 | $this->assertEquals($pnDupePairs[0]['is_selected'], 1, 'Check if first record in dupe pairs is marked as selected.'); | |
187 | $this->assertEquals($pnDupePairs[0]['is_selected'], 1, 'Check if second record in dupe pairs is marked as selected.'); | |
188 | ||
189 | // batch merge selected dupes | |
d13a105a | 190 | $result = CRM_Dedupe_Merger::batchMerge($dao->id, $this->_groupId, 'safe', 5, 1); |
0622d221 | 191 | $this->assertEquals(count($result['merged']), 2, 'Check number of merged pairs.'); |
192 | ||
e13fa54b | 193 | $stats = $this->callAPISuccess('Dedupe', 'getstatistics', [ |
194 | 'group_id' => $this->_groupId, | |
195 | 'rule_group_id' => $dao->id, | |
196 | 'check_permissions' => TRUE, | |
197 | ])['values']; | |
198 | $this->assertEquals(['merged' => 2, 'skipped' => 0], $stats); | |
199 | ||
0622d221 | 200 | // retrieve pairs from prev next cache table |
201 | $pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select); | |
202 | $this->assertEquals(count($pnDupePairs), 1, 'Check number of remaining dupe pairs in prev next cache.'); | |
203 | ||
204 | $this->deleteDupeContacts(); | |
205 | } | |
206 | ||
c8ec0753 | 207 | /** |
a354251e | 208 | * Test the batch merge. |
c8ec0753 | 209 | */ |
0622d221 | 210 | public function testBatchMergeAllDuplicates() { |
211 | $this->createDupeContacts(); | |
212 | ||
213 | // verify that all contacts have been created separately | |
214 | $this->assertEquals(count($this->_contactIds), 9, 'Check for number of contacts.'); | |
215 | ||
216 | $dao = new CRM_Dedupe_DAO_RuleGroup(); | |
217 | $dao->contact_type = 'Individual'; | |
218 | $dao->name = 'IndividualSupervised'; | |
219 | $dao->is_default = 1; | |
220 | $dao->find(TRUE); | |
221 | ||
222 | $foundDupes = CRM_Dedupe_Finder::dupesInGroup($dao->id, $this->_groupId); | |
223 | ||
224 | // ------------------------------------------------------------------------- | |
225 | // Name and Email (reserved) Matches ( 3 pairs ) | |
226 | // -------------------------------------------------------------------------- | |
227 | // robin - hood - robin@example.com | |
228 | // robin - hood - robin@example.com | |
229 | // little - dale - dale@example.com | |
230 | // little - dale - dale@example.com | |
231 | // will - dale - will@example.com | |
232 | // will - dale - will@example.com | |
233 | // so 3 pairs for - first + last + mail | |
234 | $this->assertEquals(count($foundDupes), 3, 'Check Individual-Supervised dupe rule for dupesInGroup().'); | |
235 | ||
236 | // Run dedupe finder as the browser would | |
39b959db SL |
237 | //avoid invalid key error |
238 | $_SERVER['REQUEST_METHOD'] = 'GET'; | |
0622d221 | 239 | $object = new CRM_Contact_Page_DedupeFind(); |
240 | $object->set('gid', $this->_groupId); | |
241 | $object->set('rgid', $dao->id); | |
242 | $object->set('action', CRM_Core_Action::UPDATE); | |
64fe2fe0 | 243 | $object->setEmbedded(TRUE); |
0622d221 | 244 | @$object->run(); |
245 | ||
246 | // Retrieve pairs from prev next cache table | |
763a0fec | 247 | $select = ['pn.is_selected' => 'is_selected']; |
b1679439 | 248 | $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($dao->id, $this->_groupId); |
0622d221 | 249 | $pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select); |
250 | ||
251 | $this->assertEquals(count($foundDupes), count($pnDupePairs), 'Check number of dupe pairs in prev next cache.'); | |
252 | ||
253 | // batch merge all dupes | |
d13a105a | 254 | $result = CRM_Dedupe_Merger::batchMerge($dao->id, $this->_groupId, 'safe', 5, 2); |
0622d221 | 255 | $this->assertEquals(count($result['merged']), 3, 'Check number of merged pairs.'); |
256 | ||
e13fa54b | 257 | $stats = $this->callAPISuccess('Dedupe', 'getstatistics', [ |
258 | 'rule_group_id' => $dao->id, | |
259 | 'group_id' => $this->_groupId, | |
260 | ]); | |
0622d221 | 261 | // retrieve pairs from prev next cache table |
262 | $pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select); | |
263 | $this->assertEquals(count($pnDupePairs), 0, 'Check number of remaining dupe pairs in prev next cache.'); | |
264 | ||
265 | $this->deleteDupeContacts(); | |
266 | } | |
267 | ||
bf17fa88 | 268 | /** |
269 | * The goal of this function is to test that all required tables are returned. | |
270 | */ | |
271 | public function testGetCidRefs() { | |
272 | $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, 'Contacts'); | |
273 | $this->assertEquals(array_merge($this->getStaticCIDRefs(), $this->getHackedInCIDRef()), CRM_Dedupe_Merger::cidRefs()); | |
274 | $this->assertEquals(array_merge($this->getCalculatedCIDRefs(), $this->getHackedInCIDRef()), CRM_Dedupe_Merger::cidRefs()); | |
275 | } | |
276 | ||
277 | /** | |
278 | * Get the list of not-really-cid-refs that are currently hacked in. | |
279 | * | |
280 | * This is hacked into getCIDs function. | |
281 | * | |
282 | * @return array | |
283 | */ | |
284 | public function getHackedInCIDRef() { | |
763a0fec | 285 | return [ |
286 | 'civicrm_entity_tag' => [ | |
bf17fa88 | 287 | 0 => 'entity_id', |
763a0fec | 288 | ], |
289 | ]; | |
bf17fa88 | 290 | } |
291 | ||
2988f5c7 | 292 | /** |
293 | * Test function that gets duplicate pairs. | |
294 | * | |
763a0fec | 295 | * It turns out there are 2 code paths retrieving this data so my initial |
296 | * focus is on ensuring they match. | |
2988f5c7 | 297 | */ |
298 | public function testGetMatches() { | |
299 | $this->setupMatchData(); | |
2988f5c7 | 300 | |
ed4bbf3b | 301 | $pairs = $this->callAPISuccess('Dedupe', 'getduplicates', [ |
302 | 'rule_group_id' => 1, | |
303 | ])['values']; | |
763a0fec | 304 | $this->assertEquals([ |
305 | 0 => [ | |
08cde01f | 306 | 'srcID' => $this->contacts[1]['id'], |
2988f5c7 | 307 | 'srcName' => 'Mr. Mickey Mouse II', |
08cde01f | 308 | 'dstID' => $this->contacts[0]['id'], |
2988f5c7 | 309 | 'dstName' => 'Mr. Mickey Mouse II', |
310 | 'weight' => 20, | |
311 | 'canMerge' => TRUE, | |
763a0fec | 312 | ], |
313 | 1 => [ | |
08cde01f | 314 | 'srcID' => $this->contacts[3]['id'], |
3308aac0 | 315 | 'srcName' => 'Mr. Minnie Mouse II', |
08cde01f | 316 | 'dstID' => $this->contacts[2]['id'], |
3308aac0 | 317 | 'dstName' => 'Mr. Minnie Mouse II', |
318 | 'weight' => 20, | |
319 | 'canMerge' => TRUE, | |
763a0fec | 320 | ], |
321 | ], $pairs); | |
2988f5c7 | 322 | } |
323 | ||
bc0f3965 | 324 | /** |
325 | * Test function that gets organization pairs. | |
326 | * | |
763a0fec | 327 | * Note the rule will match on organization_name OR email - hence lots of |
328 | * matches. | |
2f10fa02 | 329 | * |
330 | * @throws \Exception | |
bc0f3965 | 331 | */ |
332 | public function testGetOrganizationMatches() { | |
333 | $this->setupMatchData(); | |
763a0fec | 334 | $ruleGroups = $this->callAPISuccessGetSingle('RuleGroup', [ |
335 | 'contact_type' => 'Organization', | |
336 | 'used' => 'Supervised', | |
337 | ]); | |
bc0f3965 | 338 | |
339 | $pairs = CRM_Dedupe_Merger::getDuplicatePairs( | |
340 | $ruleGroups['id'], | |
341 | NULL, | |
342 | TRUE, | |
343 | 25, | |
344 | FALSE | |
345 | ); | |
346 | ||
763a0fec | 347 | $expectedPairs = [ |
348 | 0 => [ | |
bc0f3965 | 349 | 'srcID' => $this->contacts[5]['id'], |
350 | 'srcName' => 'Walt Disney Ltd', | |
351 | 'dstID' => $this->contacts[4]['id'], | |
352 | 'dstName' => 'Walt Disney Ltd', | |
353 | 'weight' => 20, | |
354 | 'canMerge' => TRUE, | |
763a0fec | 355 | ], |
356 | 1 => [ | |
bc0f3965 | 357 | 'srcID' => $this->contacts[7]['id'], |
358 | 'srcName' => 'Walt Disney', | |
359 | 'dstID' => $this->contacts[6]['id'], | |
360 | 'dstName' => 'Walt Disney', | |
361 | 'weight' => 10, | |
362 | 'canMerge' => TRUE, | |
763a0fec | 363 | ], |
364 | 2 => [ | |
bc0f3965 | 365 | 'srcID' => $this->contacts[6]['id'], |
366 | 'srcName' => 'Walt Disney', | |
367 | 'dstID' => $this->contacts[4]['id'], | |
368 | 'dstName' => 'Walt Disney Ltd', | |
369 | 'weight' => 10, | |
370 | 'canMerge' => TRUE, | |
763a0fec | 371 | ], |
372 | 3 => [ | |
bc0f3965 | 373 | 'srcID' => $this->contacts[6]['id'], |
374 | 'srcName' => 'Walt Disney', | |
375 | 'dstID' => $this->contacts[5]['id'], | |
376 | 'dstName' => 'Walt Disney Ltd', | |
377 | 'weight' => 10, | |
378 | 'canMerge' => TRUE, | |
763a0fec | 379 | ], |
380 | ]; | |
381 | usort($pairs, [__CLASS__, 'compareDupes']); | |
382 | usort($expectedPairs, [__CLASS__, 'compareDupes']); | |
72475b30 TO |
383 | $this->assertEquals($expectedPairs, $pairs); |
384 | } | |
385 | ||
386 | /** | |
387 | * Function to sort $duplicate records in a stable way. | |
388 | * | |
389 | * @param array $a | |
390 | * @param array $b | |
763a0fec | 391 | * |
72475b30 TO |
392 | * @return int |
393 | */ | |
394 | public static function compareDupes($a, $b) { | |
763a0fec | 395 | foreach (['srcName', 'dstName', 'srcID', 'dstID'] as $field) { |
72475b30 TO |
396 | if ($a[$field] != $b[$field]) { |
397 | return ($a[$field] < $b[$field]) ? 1 : -1; | |
398 | } | |
399 | } | |
400 | return 0; | |
bc0f3965 | 401 | } |
402 | ||
403 | /** | |
404 | * Test function that gets organization duplicate pairs. | |
2f10fa02 | 405 | * |
406 | * @throws \Exception | |
bc0f3965 | 407 | */ |
408 | public function testGetOrganizationMatchesInGroup() { | |
409 | $this->setupMatchData(); | |
763a0fec | 410 | $ruleGroups = $this->callAPISuccessGetSingle('RuleGroup', [ |
411 | 'contact_type' => 'Organization', | |
412 | 'used' => 'Supervised', | |
413 | ]); | |
bc0f3965 | 414 | |
763a0fec | 415 | $groupID = $this->groupCreate(['title' => 'she-mice']); |
bc0f3965 | 416 | |
763a0fec | 417 | $this->callAPISuccess('GroupContact', 'create', [ |
418 | 'group_id' => $groupID, | |
419 | 'contact_id' => $this->contacts[4]['id'], | |
420 | ]); | |
bc0f3965 | 421 | |
422 | $pairs = CRM_Dedupe_Merger::getDuplicatePairs( | |
423 | $ruleGroups['id'], | |
424 | $groupID, | |
425 | TRUE, | |
426 | 25, | |
427 | FALSE | |
428 | ); | |
429 | ||
763a0fec | 430 | $this->assertEquals([ |
431 | 0 => [ | |
bc0f3965 | 432 | 'srcID' => $this->contacts[5]['id'], |
433 | 'srcName' => 'Walt Disney Ltd', | |
434 | 'dstID' => $this->contacts[4]['id'], | |
435 | 'dstName' => 'Walt Disney Ltd', | |
436 | 'weight' => 20, | |
437 | 'canMerge' => TRUE, | |
763a0fec | 438 | ], |
439 | 1 => [ | |
bc0f3965 | 440 | 'srcID' => $this->contacts[6]['id'], |
441 | 'srcName' => 'Walt Disney', | |
442 | 'dstID' => $this->contacts[4]['id'], | |
443 | 'dstName' => 'Walt Disney Ltd', | |
444 | 'weight' => 10, | |
445 | 'canMerge' => TRUE, | |
763a0fec | 446 | ], |
447 | ], $pairs); | |
be61083d | 448 | |
763a0fec | 449 | $this->callAPISuccess('GroupContact', 'create', [ |
450 | 'group_id' => $groupID, | |
451 | 'contact_id' => $this->contacts[5]['id'], | |
452 | ]); | |
be61083d | 453 | CRM_Core_DAO::executeQuery("DELETE FROM civicrm_prevnext_cache"); |
454 | $pairs = CRM_Dedupe_Merger::getDuplicatePairs( | |
455 | $ruleGroups['id'], | |
456 | $groupID, | |
457 | TRUE, | |
458 | 25, | |
459 | FALSE | |
460 | ); | |
461 | ||
763a0fec | 462 | $this->assertEquals([ |
463 | 0 => [ | |
be61083d | 464 | 'srcID' => $this->contacts[5]['id'], |
465 | 'srcName' => 'Walt Disney Ltd', | |
466 | 'dstID' => $this->contacts[4]['id'], | |
467 | 'dstName' => 'Walt Disney Ltd', | |
468 | 'weight' => 20, | |
469 | 'canMerge' => TRUE, | |
763a0fec | 470 | ], |
471 | 1 => [ | |
be61083d | 472 | 'srcID' => $this->contacts[6]['id'], |
473 | 'srcName' => 'Walt Disney', | |
474 | 'dstID' => $this->contacts[4]['id'], | |
475 | 'dstName' => 'Walt Disney Ltd', | |
476 | 'weight' => 10, | |
477 | 'canMerge' => TRUE, | |
763a0fec | 478 | ], |
479 | 2 => [ | |
be61083d | 480 | 'srcID' => $this->contacts[6]['id'], |
481 | 'srcName' => 'Walt Disney', | |
482 | 'dstID' => $this->contacts[5]['id'], | |
483 | 'dstName' => 'Walt Disney Ltd', | |
484 | 'weight' => 10, | |
485 | 'canMerge' => TRUE, | |
763a0fec | 486 | ], |
487 | ], $pairs); | |
bc0f3965 | 488 | } |
489 | ||
3308aac0 | 490 | /** |
491 | * Test function that gets duplicate pairs. | |
492 | * | |
763a0fec | 493 | * It turns out there are 2 code paths retrieving this data so my initial |
494 | * focus is on ensuring they match. | |
3308aac0 | 495 | */ |
496 | public function testGetMatchesInGroup() { | |
497 | $this->setupMatchData(); | |
498 | ||
763a0fec | 499 | $groupID = $this->groupCreate(['title' => 'she-mice']); |
3308aac0 | 500 | |
763a0fec | 501 | $this->callAPISuccess('GroupContact', 'create', [ |
502 | 'group_id' => $groupID, | |
503 | 'contact_id' => $this->contacts[3]['id'], | |
504 | ]); | |
3308aac0 | 505 | |
506 | $pairs = CRM_Dedupe_Merger::getDuplicatePairs( | |
507 | 1, | |
508 | $groupID, | |
509 | TRUE, | |
510 | 25, | |
511 | FALSE | |
512 | ); | |
513 | ||
763a0fec | 514 | $this->assertEquals([ |
515 | 0 => [ | |
3308aac0 | 516 | 'srcID' => $this->contacts[3]['id'], |
517 | 'srcName' => 'Mr. Minnie Mouse II', | |
518 | 'dstID' => $this->contacts[2]['id'], | |
519 | 'dstName' => 'Mr. Minnie Mouse II', | |
520 | 'weight' => 20, | |
521 | 'canMerge' => TRUE, | |
763a0fec | 522 | ], |
523 | ], $pairs); | |
3308aac0 | 524 | } |
525 | ||
0608e1e0 | 526 | /** |
527 | * Test the special info handling is unchanged after cleanup. | |
528 | * | |
763a0fec | 529 | * Note the handling is silly - we are testing to lock in over short term |
530 | * changes not to imply any contract on the function. | |
0608e1e0 | 531 | */ |
2f10fa02 | 532 | public function testGetRowsElementsAndInfoSpecialInfo() { |
763a0fec | 533 | $contact1 = $this->individualCreate([ |
534 | 'preferred_communication_method' => [], | |
535 | 'communication_style_id' => 'Familiar', | |
536 | 'prefix_id' => 'Mrs.', | |
537 | 'suffix_id' => 'III', | |
538 | ]); | |
539 | $contact2 = $this->individualCreate([ | |
540 | 'preferred_communication_method' => [ | |
541 | 'SMS', | |
542 | 'Fax', | |
543 | ], | |
544 | 'communication_style_id' => 'Formal', | |
545 | 'gender_id' => 'Female', | |
546 | ]); | |
0608e1e0 | 547 | $rowsElementsAndInfo = CRM_Dedupe_Merger::getRowsElementsAndInfo($contact1, $contact2); |
548 | $rows = $rowsElementsAndInfo['rows']; | |
763a0fec | 549 | $this->assertEquals([ |
550 | 'main' => 'Mrs.', | |
551 | 'other' => 'Mr.', | |
552 | 'title' => 'Individual Prefix', | |
553 | ], $rows['move_prefix_id']); | |
554 | $this->assertEquals([ | |
555 | 'main' => 'III', | |
556 | 'other' => 'II', | |
557 | 'title' => 'Individual Suffix', | |
558 | ], $rows['move_suffix_id']); | |
559 | $this->assertEquals([ | |
560 | 'main' => '', | |
561 | 'other' => 'Female', | |
562 | 'title' => 'Gender', | |
563 | ], $rows['move_gender_id']); | |
564 | $this->assertEquals([ | |
565 | 'main' => 'Familiar', | |
566 | 'other' => 'Formal', | |
567 | 'title' => 'Communication Style', | |
568 | ], $rows['move_communication_style_id']); | |
0608e1e0 | 569 | $this->assertEquals(1, $rowsElementsAndInfo['migration_info']['move_communication_style_id']); |
763a0fec | 570 | $this->assertEquals([ |
571 | 'main' => '', | |
572 | 'other' => 'SMS, Fax', | |
573 | 'title' => 'Preferred Communication Method', | |
574 | ], $rows['move_preferred_communication_method']); | |
0608e1e0 | 575 | $this->assertEquals('\ 14\ 15\ 1', $rowsElementsAndInfo['migration_info']['move_preferred_communication_method']); |
576 | } | |
577 | ||
c231c0dd JP |
578 | /** |
579 | * Test migration of Membership. | |
580 | */ | |
581 | public function testMergeMembership() { | |
582 | // Contacts setup | |
583 | $this->setupMatchData(); | |
584 | $originalContactID = $this->contacts[0]['id']; | |
585 | $duplicateContactID = $this->contacts[1]['id']; | |
586 | ||
587 | //Add Membership for the duplicate contact. | |
588 | $memTypeId = $this->membershipTypeCreate(); | |
2f10fa02 | 589 | $this->callAPISuccess('Membership', 'create', [ |
c231c0dd JP |
590 | 'membership_type_id' => $memTypeId, |
591 | 'contact_id' => $duplicateContactID, | |
592 | ]); | |
593 | //Assert if 'add new' checkbox is enabled on the merge form. | |
594 | $rowsElementsAndInfo = CRM_Dedupe_Merger::getRowsElementsAndInfo($originalContactID, $duplicateContactID); | |
595 | foreach ($rowsElementsAndInfo['elements'] as $element) { | |
596 | if (!empty($element[3]) && $element[3] == 'add new') { | |
597 | $checkedAttr = ['checked' => 'checked']; | |
598 | $this->checkArrayEquals($element[4], $checkedAttr); | |
599 | } | |
600 | } | |
601 | ||
602 | //Merge and move the mem to the main contact. | |
603 | $this->mergeContacts($originalContactID, $duplicateContactID, [ | |
604 | 'move_rel_table_memberships' => 1, | |
39b959db | 605 | 'operation' => ['move_rel_table_memberships' => ['add' => 1]], |
c231c0dd JP |
606 | ]); |
607 | ||
608 | //Check if membership is correctly transferred to original contact. | |
609 | $originalContactMembership = $this->callAPISuccess('Membership', 'get', [ | |
610 | 'membership_type_id' => $memTypeId, | |
611 | 'contact_id' => $originalContactID, | |
612 | ]); | |
613 | $this->assertEquals(1, $originalContactMembership['count']); | |
614 | } | |
615 | ||
0946ab3f | 616 | /** |
617 | * CRM-19653 : Test that custom field data should/shouldn't be overriden on | |
618 | * selecting/not selecting option to migrate data respectively | |
619 | */ | |
620 | public function testCustomDataOverwrite() { | |
dba77ae8 | 621 | // Create Custom Field |
763a0fec | 622 | $createGroup = $this->setupCustomGroupForIndividual(); |
dba77ae8 CR |
623 | $createField = $this->setupCustomField('Graduation', $createGroup); |
624 | $customFieldName = "custom_" . $createField['id']; | |
625 | ||
626 | // Contacts setup | |
0946ab3f | 627 | $this->setupMatchData(); |
628 | ||
629 | $originalContactID = $this->contacts[0]['id']; | |
39b959db SL |
630 | // used as duplicate contact in 1st use-case |
631 | $duplicateContactID1 = $this->contacts[1]['id']; | |
632 | // used as duplicate contact in 2nd use-case | |
633 | $duplicateContactID2 = $this->contacts[2]['id']; | |
0946ab3f | 634 | |
0946ab3f | 635 | // update the text custom field for original contact with value 'abc' |
763a0fec | 636 | $this->callAPISuccess('Contact', 'create', [ |
0946ab3f | 637 | 'id' => $originalContactID, |
dba77ae8 | 638 | "{$customFieldName}" => 'abc', |
763a0fec | 639 | ]); |
dba77ae8 CR |
640 | $this->assertCustomFieldValue($originalContactID, 'abc', $customFieldName); |
641 | ||
0946ab3f | 642 | // update the text custom field for duplicate contact 1 with value 'def' |
763a0fec | 643 | $this->callAPISuccess('Contact', 'create', [ |
0946ab3f | 644 | 'id' => $duplicateContactID1, |
dba77ae8 | 645 | "{$customFieldName}" => 'def', |
763a0fec | 646 | ]); |
dba77ae8 CR |
647 | $this->assertCustomFieldValue($duplicateContactID1, 'def', $customFieldName); |
648 | ||
0946ab3f | 649 | // update the text custom field for duplicate contact 2 with value 'ghi' |
763a0fec | 650 | $this->callAPISuccess('Contact', 'create', [ |
0946ab3f | 651 | 'id' => $duplicateContactID2, |
dba77ae8 | 652 | "{$customFieldName}" => 'ghi', |
763a0fec | 653 | ]); |
dba77ae8 | 654 | $this->assertCustomFieldValue($duplicateContactID2, 'ghi', $customFieldName); |
0946ab3f | 655 | |
656 | /*** USE-CASE 1: DO NOT OVERWRITE CUSTOM FIELD VALUE **/ | |
763a0fec | 657 | $this->mergeContacts($originalContactID, $duplicateContactID1, [ |
39b959db | 658 | "move_{$customFieldName}" => NULL, |
763a0fec | 659 | ]); |
dba77ae8 | 660 | $this->assertCustomFieldValue($originalContactID, 'abc', $customFieldName); |
0946ab3f | 661 | |
662 | /*** USE-CASE 2: OVERWRITE CUSTOM FIELD VALUE **/ | |
763a0fec | 663 | $this->mergeContacts($originalContactID, $duplicateContactID2, [ |
dba77ae8 | 664 | "move_{$customFieldName}" => 'ghi', |
763a0fec | 665 | ]); |
dba77ae8 CR |
666 | $this->assertCustomFieldValue($originalContactID, 'ghi', $customFieldName); |
667 | ||
668 | // cleanup created custom set | |
763a0fec | 669 | $this->callAPISuccess('CustomField', 'delete', ['id' => $createField['id']]); |
670 | $this->callAPISuccess('CustomGroup', 'delete', ['id' => $createGroup['id']]); | |
dba77ae8 CR |
671 | } |
672 | ||
1f0138dd SL |
673 | /** |
674 | * Creatd Date merge cases | |
675 | * @return array | |
676 | */ | |
677 | public function createdDateMergeCases() { | |
678 | $cases = []; | |
679 | // Normal pattern merge into the lower id | |
680 | $cases[] = [0, 1]; | |
681 | // Check if we flipped the contacts that it still does right thing | |
682 | $cases[] = [1, 0]; | |
683 | return $cases; | |
684 | } | |
685 | ||
686 | /** | |
687 | * dev/core#996 Ensure that the oldest created date is retained even if duplicates have been flipped | |
688 | * @dataProvider createdDateMergeCases | |
689 | */ | |
690 | public function testCreatedDatePostMerge($keepContactKey, $duplicateContactKey) { | |
691 | $this->setupMatchData(); | |
692 | $lowerContactCreatedDate = $this->callAPISuccess('Contact', 'getsingle', [ | |
693 | 'id' => $this->contacts[0]['id'], | |
694 | 'return' => ['created_date'], | |
695 | ])['created_date']; | |
696 | // Assume contats have been flipped in the UL so merging into the higher id | |
697 | $this->mergeContacts($this->contacts[$keepContactKey]['id'], $this->contacts[$duplicateContactKey]['id'], []); | |
698 | $this->assertEquals($lowerContactCreatedDate, $this->callAPISuccess('Contact', 'getsingle', ['id' => $this->contacts[$keepContactKey]['id'], 'return' => ['created_date']])['created_date']); | |
699 | } | |
700 | ||
dba77ae8 CR |
701 | /** |
702 | * Verifies that when a contact with a custom field value is merged into a | |
703 | * contact without a record int its corresponding custom group table, and none | |
704 | * of the custom fields of that custom table are selected, the value is not | |
705 | * merged in. | |
706 | */ | |
707 | public function testMigrationOfUnselectedCustomDataOnEmptyCustomRecord() { | |
708 | // Create Custom Fields | |
763a0fec | 709 | $createGroup = $this->setupCustomGroupForIndividual(); |
dba77ae8 CR |
710 | $customField1 = $this->setupCustomField('TestField', $createGroup); |
711 | ||
c1955865 J |
712 | // Create multi-value custom field |
713 | $multiGroup = $this->CustomGroupMultipleCreateByParams(); | |
763a0fec | 714 | $multiField = $this->customFieldCreate([ |
c1955865 J |
715 | 'custom_group_id' => $multiGroup['id'], |
716 | 'label' => 'field_1' . $multiGroup['id'], | |
717 | 'in_selector' => 1, | |
763a0fec | 718 | ]); |
c1955865 | 719 | |
dba77ae8 CR |
720 | // Contacts setup |
721 | $this->setupMatchData(); | |
722 | $originalContactID = $this->contacts[0]['id']; | |
723 | $duplicateContactID = $this->contacts[1]['id']; | |
724 | ||
725 | // Update the text custom fields for duplicate contact | |
763a0fec | 726 | $this->callAPISuccess('Contact', 'create', [ |
dba77ae8 CR |
727 | 'id' => $duplicateContactID, |
728 | "custom_{$customField1['id']}" => 'abc', | |
c1955865 | 729 | "custom_{$multiField['id']}" => 'def', |
763a0fec | 730 | ]); |
dba77ae8 | 731 | $this->assertCustomFieldValue($duplicateContactID, 'abc', "custom_{$customField1['id']}"); |
c1955865 | 732 | $this->assertCustomFieldValue($duplicateContactID, 'def', "custom_{$multiField['id']}"); |
dba77ae8 | 733 | |
c1955865 | 734 | // Merge, and ensure that no value was migrated |
763a0fec | 735 | $this->mergeContacts($originalContactID, $duplicateContactID, [ |
ee3b1d86 | 736 | "move_custom_{$customField1['id']}" => NULL, |
c1955865 | 737 | "move_rel_table_custom_{$multiGroup['id']}" => NULL, |
763a0fec | 738 | ]); |
dba77ae8 | 739 | $this->assertCustomFieldValue($originalContactID, '', "custom_{$customField1['id']}"); |
c1955865 | 740 | $this->assertCustomFieldValue($originalContactID, '', "custom_{$multiField['id']}"); |
dba77ae8 CR |
741 | |
742 | // cleanup created custom set | |
763a0fec | 743 | $this->callAPISuccess('CustomField', 'delete', ['id' => $customField1['id']]); |
744 | $this->callAPISuccess('CustomGroup', 'delete', ['id' => $createGroup['id']]); | |
745 | $this->callAPISuccess('CustomField', 'delete', ['id' => $multiField['id']]); | |
746 | $this->callAPISuccess('CustomGroup', 'delete', ['id' => $multiGroup['id']]); | |
dba77ae8 CR |
747 | } |
748 | ||
749 | /** | |
750 | * Tests that if only part of the custom fields of a custom group are selected | |
751 | * for a merge, only those values are merged, while all other fields of the | |
752 | * custom group retain their original value, specifically for a contact with | |
753 | * no records on the custom group table. | |
754 | */ | |
755 | public function testMigrationOfSomeCustomDataOnEmptyCustomRecord() { | |
756 | // Create Custom Fields | |
763a0fec | 757 | $createGroup = $this->setupCustomGroupForIndividual(); |
dba77ae8 CR |
758 | $customField1 = $this->setupCustomField('Test1', $createGroup); |
759 | $customField2 = $this->setupCustomField('Test2', $createGroup); | |
760 | ||
c1955865 J |
761 | // Create multi-value custom field |
762 | $multiGroup = $this->CustomGroupMultipleCreateByParams(); | |
763a0fec | 763 | $multiField = $this->customFieldCreate([ |
c1955865 J |
764 | 'custom_group_id' => $multiGroup['id'], |
765 | 'label' => 'field_1' . $multiGroup['id'], | |
766 | 'in_selector' => 1, | |
763a0fec | 767 | ]); |
c1955865 | 768 | |
dba77ae8 CR |
769 | // Contacts setup |
770 | $this->setupMatchData(); | |
771 | $originalContactID = $this->contacts[0]['id']; | |
772 | $duplicateContactID = $this->contacts[1]['id']; | |
773 | ||
774 | // Update the text custom fields for duplicate contact | |
763a0fec | 775 | $this->callAPISuccess('Contact', 'create', [ |
dba77ae8 CR |
776 | 'id' => $duplicateContactID, |
777 | "custom_{$customField1['id']}" => 'abc', | |
778 | "custom_{$customField2['id']}" => 'def', | |
c1955865 | 779 | "custom_{$multiField['id']}" => 'ghi', |
763a0fec | 780 | ]); |
dba77ae8 CR |
781 | $this->assertCustomFieldValue($duplicateContactID, 'abc', "custom_{$customField1['id']}"); |
782 | $this->assertCustomFieldValue($duplicateContactID, 'def', "custom_{$customField2['id']}"); | |
c1955865 | 783 | $this->assertCustomFieldValue($duplicateContactID, 'ghi', "custom_{$multiField['id']}"); |
dba77ae8 CR |
784 | |
785 | // Perform merge | |
763a0fec | 786 | $this->mergeContacts($originalContactID, $duplicateContactID, [ |
ee3b1d86 | 787 | "move_custom_{$customField1['id']}" => NULL, |
dba77ae8 | 788 | "move_custom_{$customField2['id']}" => 'def', |
c1955865 | 789 | "move_rel_table_custom_{$multiGroup['id']}" => '1', |
763a0fec | 790 | ]); |
dba77ae8 CR |
791 | $this->assertCustomFieldValue($originalContactID, '', "custom_{$customField1['id']}"); |
792 | $this->assertCustomFieldValue($originalContactID, 'def', "custom_{$customField2['id']}"); | |
c1955865 | 793 | $this->assertCustomFieldValue($originalContactID, 'ghi', "custom_{$multiField['id']}"); |
dba77ae8 CR |
794 | |
795 | // cleanup created custom set | |
763a0fec | 796 | $this->callAPISuccess('CustomField', 'delete', ['id' => $customField1['id']]); |
797 | $this->callAPISuccess('CustomField', 'delete', ['id' => $customField2['id']]); | |
798 | $this->callAPISuccess('CustomGroup', 'delete', ['id' => $createGroup['id']]); | |
799 | $this->callAPISuccess('CustomField', 'delete', ['id' => $multiField['id']]); | |
800 | $this->callAPISuccess('CustomGroup', 'delete', ['id' => $multiGroup['id']]); | |
dba77ae8 CR |
801 | } |
802 | ||
4c7e5001 PF |
803 | /** |
804 | * Test that ContactReference fields are updated to point to the main contact | |
805 | * after a merge is performed and the duplicate contact is deleted. | |
806 | */ | |
807 | public function testMigrationOfContactReferenceCustomField() { | |
808 | // Create Custom Fields | |
809 | $contactGroup = $this->setupCustomGroupForIndividual(); | |
810 | $activityGroup = $this->customGroupCreate([ | |
811 | 'name' => 'test_group_activity', | |
812 | 'extends' => 'Activity', | |
813 | ]); | |
814 | $refFieldContact = $this->customFieldCreate([ | |
815 | 'custom_group_id' => $contactGroup['id'], | |
816 | 'label' => 'field_1' . $contactGroup['id'], | |
817 | 'data_type' => 'ContactReference', | |
818 | 'default_value' => NULL, | |
819 | ]); | |
820 | $refFieldActivity = $this->customFieldCreate([ | |
821 | 'custom_group_id' => $activityGroup['id'], | |
822 | 'label' => 'field_1' . $activityGroup['id'], | |
823 | 'data_type' => 'ContactReference', | |
824 | 'default_value' => NULL, | |
825 | ]); | |
826 | ||
827 | // Contacts setup | |
828 | $this->setupMatchData(); | |
829 | $originalContactID = $this->contacts[0]['id']; | |
830 | $duplicateContactID = $this->contacts[1]['id']; | |
831 | ||
832 | // create a contact that won't be merged but has a ContactReference field | |
833 | // pointing to the duplicate (to be deleted) contact | |
834 | $unrelatedContact = $this->individualCreate([ | |
835 | 'first_name' => 'Unrelated', | |
836 | 'first_name' => 'Contact', | |
837 | 'email' => 'unrelated@example.com', | |
838 | "custom_{$refFieldContact['id']}" => $duplicateContactID, | |
839 | ]); | |
840 | // also create an activity with a ContactReference custom field | |
841 | $activity = $this->activityCreate([ | |
842 | 'target_contact_id' => $unrelatedContact, | |
843 | "custom_{$refFieldActivity['id']}" => $duplicateContactID, | |
844 | ]); | |
845 | ||
846 | // verify that the fields were set | |
847 | $this->assertCustomFieldValue($unrelatedContact, $duplicateContactID, "custom_{$refFieldContact['id']}"); | |
848 | $this->assertEntityCustomFieldValue('Activity', $activity['id'], $duplicateContactID, "custom_{$refFieldActivity['id']}_id"); | |
849 | ||
850 | // Perform merge | |
851 | $this->mergeContacts($originalContactID, $duplicateContactID, []); | |
852 | ||
853 | // verify that the ContactReference fields were updated to point to the surviving contact post-merge | |
854 | $this->assertCustomFieldValue($unrelatedContact, $originalContactID, "custom_{$refFieldContact['id']}"); | |
855 | $this->assertEntityCustomFieldValue('Activity', $activity['id'], $originalContactID, "custom_{$refFieldActivity['id']}_id"); | |
856 | ||
857 | // cleanup created custom set | |
858 | $this->callAPISuccess('CustomField', 'delete', ['id' => $refFieldContact['id']]); | |
859 | $this->callAPISuccess('CustomGroup', 'delete', ['id' => $contactGroup['id']]); | |
860 | $this->callAPISuccess('CustomField', 'delete', ['id' => $refFieldActivity['id']]); | |
861 | $this->callAPISuccess('CustomGroup', 'delete', ['id' => $activityGroup['id']]); | |
862 | } | |
863 | ||
dba77ae8 CR |
864 | /** |
865 | * Calls merge method on given contacts, with values given in $params array. | |
866 | * | |
867 | * @param $originalContactID | |
868 | * ID of target contact | |
869 | * @param $duplicateContactID | |
870 | * ID of contact to be merged | |
871 | * @param $params | |
872 | * Array of fields to be merged from source into target contact, of the form | |
873 | * ['move_<fieldName>' => <fieldValue>] | |
2f10fa02 | 874 | * |
875 | * @throws \CRM_Core_Exception | |
876 | * @throws \CiviCRM_API3_Exception | |
dba77ae8 CR |
877 | */ |
878 | private function mergeContacts($originalContactID, $duplicateContactID, $params) { | |
879 | $rowsElementsAndInfo = CRM_Dedupe_Merger::getRowsElementsAndInfo($originalContactID, $duplicateContactID); | |
880 | ||
763a0fec | 881 | $migrationData = [ |
0946ab3f | 882 | 'main_details' => $rowsElementsAndInfo['main_details'], |
883 | 'other_details' => $rowsElementsAndInfo['other_details'], | |
763a0fec | 884 | ]; |
dba77ae8 CR |
885 | |
886 | // Migrate data of duplicate contact | |
887 | CRM_Dedupe_Merger::moveAllBelongings($originalContactID, $duplicateContactID, array_merge($migrationData, $params)); | |
888 | } | |
889 | ||
890 | /** | |
891 | * Checks if the expected value for the given field corresponds to what is | |
892 | * stored in the database for the given contact ID. | |
893 | * | |
894 | * @param $contactID | |
895 | * @param $expectedValue | |
896 | * @param $customFieldName | |
897 | */ | |
898 | private function assertCustomFieldValue($contactID, $expectedValue, $customFieldName) { | |
4c7e5001 PF |
899 | $this->assertEntityCustomFieldValue('Contact', $contactID, $expectedValue, $customFieldName); |
900 | } | |
901 | ||
902 | /** | |
903 | * Check if the custom field of the given field and entity id matches the | |
904 | * expected value | |
905 | * | |
906 | * @param $entity | |
907 | * @param $id | |
908 | * @param $expectedValue | |
909 | * @param $customFieldName | |
910 | */ | |
911 | private function assertEntityCustomFieldValue($entity, $id, $expectedValue, $customFieldName) { | |
912 | $data = $this->callAPISuccess($entity, 'getsingle', [ | |
913 | 'id' => $id, | |
763a0fec | 914 | 'return' => [$customFieldName], |
915 | ]); | |
0946ab3f | 916 | |
dba77ae8 CR |
917 | $this->assertEquals($expectedValue, $data[$customFieldName], "Custom field value was supposed to be '{$expectedValue}', '{$data[$customFieldName]}' found."); |
918 | } | |
919 | ||
920 | /** | |
921 | * Creates a custom group to run tests on contacts that are individuals. | |
922 | * | |
923 | * @return array | |
924 | * Data for the created custom group record | |
925 | */ | |
926 | private function setupCustomGroupForIndividual() { | |
763a0fec | 927 | $customGroup = $this->callAPISuccess('custom_group', 'get', [ |
dba77ae8 | 928 | 'name' => 'test_group', |
763a0fec | 929 | ]); |
dba77ae8 CR |
930 | |
931 | if ($customGroup['count'] > 0) { | |
763a0fec | 932 | $this->callAPISuccess('CustomGroup', 'delete', ['id' => $customGroup['id']]); |
dba77ae8 CR |
933 | } |
934 | ||
763a0fec | 935 | $customGroup = $this->callAPISuccess('custom_group', 'create', [ |
dba77ae8 CR |
936 | 'title' => 'Test_Group', |
937 | 'name' => 'test_group', | |
763a0fec | 938 | 'extends' => ['Individual'], |
dba77ae8 CR |
939 | 'style' => 'Inline', |
940 | 'is_multiple' => FALSE, | |
941 | 'is_active' => 1, | |
763a0fec | 942 | ]); |
dba77ae8 CR |
943 | |
944 | return $customGroup; | |
945 | } | |
946 | ||
947 | /** | |
948 | * Creates a custom field on the provided custom group with the given field | |
949 | * label. | |
950 | * | |
951 | * @param $fieldLabel | |
952 | * @param $createGroup | |
953 | * | |
954 | * @return array | |
955 | * Data for the created custom field record | |
956 | */ | |
957 | private function setupCustomField($fieldLabel, $createGroup) { | |
763a0fec | 958 | return $this->callAPISuccess('custom_field', 'create', [ |
dba77ae8 CR |
959 | 'label' => $fieldLabel, |
960 | 'data_type' => 'Alphanumeric', | |
961 | 'html_type' => 'Text', | |
962 | 'custom_group_id' => $createGroup['id'], | |
763a0fec | 963 | ]); |
0946ab3f | 964 | } |
965 | ||
3308aac0 | 966 | /** |
967 | * Set up some contacts for our matching. | |
968 | */ | |
2988f5c7 | 969 | public function setupMatchData() { |
763a0fec | 970 | $fixtures = [ |
971 | [ | |
2988f5c7 | 972 | 'first_name' => 'Mickey', |
973 | 'last_name' => 'Mouse', | |
974 | 'email' => 'mickey@mouse.com', | |
763a0fec | 975 | ], |
976 | [ | |
2988f5c7 | 977 | 'first_name' => 'Mickey', |
978 | 'last_name' => 'Mouse', | |
979 | 'email' => 'mickey@mouse.com', | |
763a0fec | 980 | ], |
981 | [ | |
2988f5c7 | 982 | 'first_name' => 'Minnie', |
983 | 'last_name' => 'Mouse', | |
984 | 'email' => 'mickey@mouse.com', | |
763a0fec | 985 | ], |
986 | [ | |
3308aac0 | 987 | 'first_name' => 'Minnie', |
988 | 'last_name' => 'Mouse', | |
989 | 'email' => 'mickey@mouse.com', | |
763a0fec | 990 | ], |
991 | ]; | |
2988f5c7 | 992 | foreach ($fixtures as $fixture) { |
993 | $contactID = $this->individualCreate($fixture); | |
763a0fec | 994 | $this->contacts[] = array_merge($fixture, ['id' => $contactID]); |
4c7e5001 | 995 | sleep(2); |
bc0f3965 | 996 | } |
763a0fec | 997 | $organizationFixtures = [ |
998 | [ | |
bc0f3965 | 999 | 'organization_name' => 'Walt Disney Ltd', |
1000 | 'email' => 'walt@disney.com', | |
763a0fec | 1001 | ], |
1002 | [ | |
bc0f3965 | 1003 | 'organization_name' => 'Walt Disney Ltd', |
1004 | 'email' => 'walt@disney.com', | |
763a0fec | 1005 | ], |
1006 | [ | |
bc0f3965 | 1007 | 'organization_name' => 'Walt Disney', |
1008 | 'email' => 'walt@disney.com', | |
763a0fec | 1009 | ], |
1010 | [ | |
bc0f3965 | 1011 | 'organization_name' => 'Walt Disney', |
1012 | 'email' => 'walter@disney.com', | |
763a0fec | 1013 | ], |
1014 | ]; | |
bc0f3965 | 1015 | foreach ($organizationFixtures as $fixture) { |
1016 | $contactID = $this->organizationCreate($fixture); | |
763a0fec | 1017 | $this->contacts[] = array_merge($fixture, ['id' => $contactID]); |
2988f5c7 | 1018 | } |
1019 | } | |
1020 | ||
bf17fa88 | 1021 | /** |
1022 | * Get the list of tables that refer to the CID. | |
1023 | * | |
1024 | * This is a statically maintained (in this test list). | |
1025 | * | |
763a0fec | 1026 | * There is also a check against an automated list but having both seems to |
1027 | * add extra stability to me. They do not change often. | |
bf17fa88 | 1028 | */ |
1029 | public function getStaticCIDRefs() { | |
763a0fec | 1030 | return [ |
1031 | 'civicrm_acl_cache' => [ | |
bf17fa88 | 1032 | 0 => 'contact_id', |
763a0fec | 1033 | ], |
1034 | 'civicrm_acl_contact_cache' => [ | |
dbb4e4f9 | 1035 | 0 => 'contact_id', |
763a0fec | 1036 | ], |
1037 | 'civicrm_action_log' => [ | |
bf17fa88 | 1038 | 0 => 'contact_id', |
763a0fec | 1039 | ], |
1040 | 'civicrm_activity_contact' => [ | |
bf17fa88 | 1041 | 0 => 'contact_id', |
763a0fec | 1042 | ], |
1043 | 'civicrm_address' => [ | |
bf17fa88 | 1044 | 0 => 'contact_id', |
763a0fec | 1045 | ], |
1046 | 'civicrm_batch' => [ | |
bf17fa88 | 1047 | 0 => 'created_id', |
1048 | 1 => 'modified_id', | |
763a0fec | 1049 | ], |
1050 | 'civicrm_campaign' => [ | |
bf17fa88 | 1051 | 0 => 'created_id', |
1052 | 1 => 'last_modified_id', | |
763a0fec | 1053 | ], |
1054 | 'civicrm_case_contact' => [ | |
bf17fa88 | 1055 | 0 => 'contact_id', |
763a0fec | 1056 | ], |
1057 | 'civicrm_contact' => [ | |
bf17fa88 | 1058 | 0 => 'primary_contact_id', |
1059 | 1 => 'employer_id', | |
763a0fec | 1060 | ], |
1061 | 'civicrm_contribution' => [ | |
bf17fa88 | 1062 | 0 => 'contact_id', |
763a0fec | 1063 | ], |
1064 | 'civicrm_contribution_page' => [ | |
bf17fa88 | 1065 | 0 => 'created_id', |
763a0fec | 1066 | ], |
1067 | 'civicrm_contribution_recur' => [ | |
bf17fa88 | 1068 | 0 => 'contact_id', |
763a0fec | 1069 | ], |
1070 | 'civicrm_contribution_soft' => [ | |
bf17fa88 | 1071 | 0 => 'contact_id', |
763a0fec | 1072 | ], |
1073 | 'civicrm_custom_group' => [ | |
bf17fa88 | 1074 | 0 => 'created_id', |
763a0fec | 1075 | ], |
1076 | 'civicrm_dashboard_contact' => [ | |
bf17fa88 | 1077 | 0 => 'contact_id', |
763a0fec | 1078 | ], |
1079 | 'civicrm_dedupe_exception' => [ | |
bf17fa88 | 1080 | 0 => 'contact_id1', |
1081 | 1 => 'contact_id2', | |
763a0fec | 1082 | ], |
1083 | 'civicrm_domain' => [ | |
bf17fa88 | 1084 | 0 => 'contact_id', |
763a0fec | 1085 | ], |
1086 | 'civicrm_email' => [ | |
bf17fa88 | 1087 | 0 => 'contact_id', |
763a0fec | 1088 | ], |
1089 | 'civicrm_event' => [ | |
bf17fa88 | 1090 | 0 => 'created_id', |
763a0fec | 1091 | ], |
1092 | 'civicrm_event_carts' => [ | |
bf17fa88 | 1093 | 0 => 'user_id', |
763a0fec | 1094 | ], |
1095 | 'civicrm_financial_account' => [ | |
bf17fa88 | 1096 | 0 => 'contact_id', |
763a0fec | 1097 | ], |
1098 | 'civicrm_financial_item' => [ | |
bf17fa88 | 1099 | 0 => 'contact_id', |
763a0fec | 1100 | ], |
1101 | 'civicrm_grant' => [ | |
bf17fa88 | 1102 | 0 => 'contact_id', |
763a0fec | 1103 | ], |
1104 | 'civicrm_group' => [ | |
bf17fa88 | 1105 | 0 => 'created_id', |
1106 | 1 => 'modified_id', | |
763a0fec | 1107 | ], |
1108 | 'civicrm_group_contact' => [ | |
bf17fa88 | 1109 | 0 => 'contact_id', |
763a0fec | 1110 | ], |
1111 | 'civicrm_group_contact_cache' => [ | |
bf17fa88 | 1112 | 0 => 'contact_id', |
763a0fec | 1113 | ], |
1114 | 'civicrm_group_organization' => [ | |
bf17fa88 | 1115 | 0 => 'organization_id', |
763a0fec | 1116 | ], |
1117 | 'civicrm_im' => [ | |
bf17fa88 | 1118 | 0 => 'contact_id', |
763a0fec | 1119 | ], |
1120 | 'civicrm_log' => [ | |
bf17fa88 | 1121 | 0 => 'modified_id', |
763a0fec | 1122 | ], |
1123 | 'civicrm_mailing' => [ | |
bf17fa88 | 1124 | 0 => 'created_id', |
1125 | 1 => 'scheduled_id', | |
1126 | 2 => 'approver_id', | |
763a0fec | 1127 | ], |
1128 | 'civicrm_file' => [ | |
ae2c7e00 | 1129 | 'created_id', |
763a0fec | 1130 | ], |
1131 | 'civicrm_mailing_abtest' => [ | |
bf17fa88 | 1132 | 0 => 'created_id', |
763a0fec | 1133 | ], |
1134 | 'civicrm_mailing_event_queue' => [ | |
bf17fa88 | 1135 | 0 => 'contact_id', |
763a0fec | 1136 | ], |
1137 | 'civicrm_mailing_event_subscribe' => [ | |
bf17fa88 | 1138 | 0 => 'contact_id', |
763a0fec | 1139 | ], |
1140 | 'civicrm_mailing_recipients' => [ | |
bf17fa88 | 1141 | 0 => 'contact_id', |
763a0fec | 1142 | ], |
1143 | 'civicrm_membership' => [ | |
bf17fa88 | 1144 | 0 => 'contact_id', |
763a0fec | 1145 | ], |
1146 | 'civicrm_membership_log' => [ | |
bf17fa88 | 1147 | 0 => 'modified_id', |
763a0fec | 1148 | ], |
1149 | 'civicrm_membership_type' => [ | |
bf17fa88 | 1150 | 0 => 'member_of_contact_id', |
763a0fec | 1151 | ], |
1152 | 'civicrm_note' => [ | |
bf17fa88 | 1153 | 0 => 'contact_id', |
763a0fec | 1154 | ], |
1155 | 'civicrm_openid' => [ | |
bf17fa88 | 1156 | 0 => 'contact_id', |
763a0fec | 1157 | ], |
1158 | 'civicrm_participant' => [ | |
bf17fa88 | 1159 | 0 => 'contact_id', |
39b959db SL |
1160 | //CRM-16761 |
1161 | 1 => 'transferred_to_contact_id', | |
763a0fec | 1162 | ], |
1163 | 'civicrm_payment_token' => [ | |
bf17fa88 | 1164 | 0 => 'contact_id', |
1165 | 1 => 'created_id', | |
763a0fec | 1166 | ], |
1167 | 'civicrm_pcp' => [ | |
bf17fa88 | 1168 | 0 => 'contact_id', |
763a0fec | 1169 | ], |
1170 | 'civicrm_phone' => [ | |
bf17fa88 | 1171 | 0 => 'contact_id', |
763a0fec | 1172 | ], |
1173 | 'civicrm_pledge' => [ | |
bf17fa88 | 1174 | 0 => 'contact_id', |
763a0fec | 1175 | ], |
1176 | 'civicrm_print_label' => [ | |
bf17fa88 | 1177 | 0 => 'created_id', |
763a0fec | 1178 | ], |
1179 | 'civicrm_relationship' => [ | |
bf17fa88 | 1180 | 0 => 'contact_id_a', |
1181 | 1 => 'contact_id_b', | |
763a0fec | 1182 | ], |
1183 | 'civicrm_report_instance' => [ | |
bf17fa88 | 1184 | 0 => 'created_id', |
1185 | 1 => 'owner_id', | |
763a0fec | 1186 | ], |
1187 | 'civicrm_setting' => [ | |
bf17fa88 | 1188 | 0 => 'contact_id', |
1189 | 1 => 'created_id', | |
763a0fec | 1190 | ], |
1191 | 'civicrm_subscription_history' => [ | |
bf17fa88 | 1192 | 0 => 'contact_id', |
763a0fec | 1193 | ], |
1194 | 'civicrm_survey' => [ | |
bf17fa88 | 1195 | 0 => 'created_id', |
1196 | 1 => 'last_modified_id', | |
763a0fec | 1197 | ], |
1198 | 'civicrm_tag' => [ | |
bf17fa88 | 1199 | 0 => 'created_id', |
763a0fec | 1200 | ], |
1201 | 'civicrm_uf_group' => [ | |
bf17fa88 | 1202 | 0 => 'created_id', |
763a0fec | 1203 | ], |
1204 | 'civicrm_uf_match' => [ | |
bf17fa88 | 1205 | 0 => 'contact_id', |
763a0fec | 1206 | ], |
1207 | 'civicrm_value_testgetcidref_1' => [ | |
bf17fa88 | 1208 | 0 => 'entity_id', |
763a0fec | 1209 | ], |
1210 | 'civicrm_website' => [ | |
bf17fa88 | 1211 | 0 => 'contact_id', |
763a0fec | 1212 | ], |
1213 | ]; | |
bf17fa88 | 1214 | } |
1215 | ||
1216 | /** | |
1217 | * Get a list of CIDs that is calculated off the schema. | |
1218 | * | |
763a0fec | 1219 | * Note this is an expensive and table locking query. Should be safe in tests |
1220 | * though. | |
bf17fa88 | 1221 | */ |
1222 | public function getCalculatedCIDRefs() { | |
763a0fec | 1223 | $cidRefs = []; |
bf17fa88 | 1224 | $sql = " |
1225 | SELECT | |
1226 | table_name, | |
1227 | column_name | |
1228 | FROM information_schema.key_column_usage | |
1229 | WHERE | |
1230 | referenced_table_schema = database() AND | |
1231 | referenced_table_name = 'civicrm_contact' AND | |
1232 | referenced_column_name = 'id'; | |
1233 | "; | |
1234 | $dao = CRM_Core_DAO::executeQuery($sql); | |
1235 | while ($dao->fetch()) { | |
1236 | $cidRefs[$dao->table_name][] = $dao->column_name; | |
1237 | } | |
1238 | // Do specific re-ordering changes to make this the same as the ref validated one. | |
1239 | // The above query orders by FK alphabetically. | |
1240 | // There might be cleverer ways to do this but it shouldn't change much. | |
1241 | $cidRefs['civicrm_contact'][0] = 'primary_contact_id'; | |
1242 | $cidRefs['civicrm_contact'][1] = 'employer_id'; | |
dbb4e4f9 | 1243 | $cidRefs['civicrm_acl_contact_cache'][0] = 'contact_id'; |
bf17fa88 | 1244 | $cidRefs['civicrm_mailing'][0] = 'created_id'; |
1245 | $cidRefs['civicrm_mailing'][1] = 'scheduled_id'; | |
1246 | $cidRefs['civicrm_mailing'][2] = 'approver_id'; | |
1247 | return $cidRefs; | |
1248 | } | |
1249 | ||
0622d221 | 1250 | } |