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