d6fa72b76dd88420618c6bee685a9de02b978d58
[civicrm-core.git] / tests / phpunit / api / v3 / ContactTest.php
1 <?php
2 /**
3 * @file
4 * File for the TestContact class.
5 *
6 * (PHP 5)
7 *
8 * @author Walt Haas <walt@dharmatech.org> (801) 534-1262
9 * @copyright Copyright CiviCRM LLC (C) 2009
10 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html
11 * GNU Affero General Public License version 3
12 * @version $Id: ContactTest.php 31254 2010-12-15 10:09:29Z eileen $
13 * @package CiviCRM
14 *
15 * This file is part of CiviCRM
16 *
17 * CiviCRM is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU Affero General Public License
19 * as published by the Free Software Foundation; either version 3 of
20 * the License, or (at your option) any later version.
21 *
22 * CiviCRM is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU Affero General Public License for more details.
26 *
27 * You should have received a copy of the GNU Affero General Public
28 * License along with this program. If not, see
29 * <http://www.gnu.org/licenses/>.
30 */
31
32 /**
33 * Test APIv3 civicrm_contact* functions
34 *
35 * @package CiviCRM_APIv3
36 * @subpackage API_Contact
37 * @group headless
38 */
39 class api_v3_ContactTest extends CiviUnitTestCase {
40
41 use CRMTraits_Custom_CustomDataTrait;
42
43 public $DBResetRequired = FALSE;
44
45 protected $_apiversion;
46
47 protected $_entity;
48
49 protected $_params;
50
51 protected $_contactID;
52
53 protected $_financialTypeId = 1;
54
55 /**
56 * Entity to be extended.
57 *
58 * @var string
59 */
60 protected $entity = 'Contact';
61
62 /**
63 * Test setup for every test.
64 *
65 * Connect to the database, truncate the tables that will be used
66 * and redirect stdin to a temporary file
67 */
68 public function setUp() {
69 // Connect to the database.
70 parent::setUp();
71 $this->_entity = 'contact';
72 $this->_params = [
73 'first_name' => 'abc1',
74 'contact_type' => 'Individual',
75 'last_name' => 'xyz1',
76 ];
77 }
78
79 /**
80 * Restore the DB for the next test.
81 *
82 * @throws \Exception
83 */
84 public function tearDown() {
85 $this->_apiversion = 3;
86 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => TRUE]);
87 // truncate a few tables
88 $tablesToTruncate = [
89 'civicrm_email',
90 'civicrm_contribution',
91 'civicrm_line_item',
92 'civicrm_website',
93 'civicrm_relationship',
94 'civicrm_uf_match',
95 'civicrm_phone',
96 'civicrm_address',
97 'civicrm_acl_contact_cache',
98 'civicrm_activity_contact',
99 'civicrm_activity',
100 'civicrm_group',
101 'civicrm_group_contact',
102 'civicrm_saved_search',
103 'civicrm_group_contact_cache',
104 'civicrm_prevnext_cache',
105 ];
106
107 $this->quickCleanup($tablesToTruncate, TRUE);
108 parent::tearDown();
109 }
110
111 /**
112 * Test civicrm_contact_create.
113 *
114 * Verify that attempt to create individual contact with only
115 * first and last names succeeds
116 *
117 * @param int $version
118 *
119 * @dataProvider versionThreeAndFour
120 *
121 * @throws \CRM_Core_Exception
122 */
123 public function testAddCreateIndividual($version) {
124 $this->_apiversion = $version;
125 $oldCount = CRM_Core_DAO::singleValueQuery('select count(*) from civicrm_contact');
126 $params = [
127 'first_name' => 'abc1',
128 'contact_type' => 'Individual',
129 'last_name' => 'xyz1',
130 ];
131
132 $contact = $this->callAPISuccess('contact', 'create', $params);
133 $this->assertTrue(is_numeric($contact['id']));
134 $this->assertTrue($contact['id'] > 0);
135 $newCount = CRM_Core_DAO::singleValueQuery('select count(*) from civicrm_contact');
136 $this->assertEquals($oldCount + 1, $newCount);
137
138 $this->assertDBState('CRM_Contact_DAO_Contact',
139 $contact['id'],
140 $params
141 );
142 }
143
144 /**
145 * Test that it is possible to prevent cache clearing via option.
146 *
147 * Cache clearing is bypassed if 'options' => array('do_not_reset_cache' => 1 is used.
148 *
149 * @throws \CRM_Core_Exception
150 */
151 public function testCreateIndividualNoCacheClear() {
152
153 $contact = $this->callAPISuccess('contact', 'create', $this->_params);
154 $groupID = $this->groupCreate();
155
156 $this->putGroupContactCacheInClearableState($groupID, $contact);
157
158 $this->callAPISuccess('contact', 'create', ['id' => $contact['id']]);
159 $this->assertEquals(0, CRM_Core_DAO::singleValueQuery("SELECT count(*) FROM civicrm_group_contact_cache"));
160
161 // Rinse & repeat, but with the option.
162 $this->putGroupContactCacheInClearableState($groupID, $contact);
163 CRM_Core_Config::setPermitCacheFlushMode(FALSE);
164 $this->callAPISuccess('contact', 'create', ['id' => $contact['id']]);
165 $this->assertEquals(1, CRM_Core_DAO::singleValueQuery("SELECT count(*) FROM civicrm_group_contact_cache"));
166 CRM_Core_Config::setPermitCacheFlushMode(TRUE);
167 }
168
169 /**
170 * Test for international string acceptance (CRM-10210).
171 * Requires the database to be in utf8.
172 *
173 * @dataProvider getInternationalStrings
174 *
175 * @param string $string
176 * String to be tested.
177 *
178 * Bool to see if we should check charset.
179 *
180 * @throws \CRM_Core_Exception
181 */
182 public function testInternationalStrings($string) {
183 $this->callAPISuccess('Contact', 'create', array_merge(
184 $this->_params,
185 ['first_name' => $string]
186 ));
187
188 $result = $this->callAPISuccessGetSingle('Contact', ['first_name' => $string]);
189 $this->assertEquals($string, $result['first_name']);
190
191 $organizationParams = [
192 'organization_name' => $string,
193 'contact_type' => 'Organization',
194 ];
195
196 $this->callAPISuccess('Contact', 'create', $organizationParams);
197 $result = $this->callAPISuccessGetSingle('Contact', $organizationParams);
198 $this->assertEquals($string, $result['organization_name']);
199 }
200
201 /**
202 * Get international string data for testing against api calls.
203 */
204 public function getInternationalStrings() {
205 $invocations = [];
206 $invocations[] = ['Scarabée'];
207 $invocations[] = ['Iñtërnâtiônàlizætiøn'];
208 $invocations[] = ['これは日本語のテキストです。読めますか'];
209 $invocations[] = ['देखें हिन्दी कैसी नजर आती है। अरे वाह ये तो नजर आती है।'];
210 return $invocations;
211 }
212
213 /**
214 * Test civicrm_contact_create.
215 *
216 * Verify that preferred language can be set.
217 *
218 * @param int $version
219 *
220 * @dataProvider versionThreeAndFour
221 * @throws \CRM_Core_Exception
222 */
223 public function testAddCreateIndividualWithPreferredLanguage($version) {
224 $this->_apiversion = $version;
225 $params = [
226 'first_name' => 'abc1',
227 'contact_type' => 'Individual',
228 'last_name' => 'xyz1',
229 'preferred_language' => 'es_ES',
230 ];
231
232 $contact = $this->callAPISuccess('contact', 'create', $params);
233 $this->getAndCheck($params, $contact['id'], 'Contact');
234 }
235
236 /**
237 * Test civicrm_contact_create with sub-types.
238 *
239 * Verify that sub-types are created successfully and not deleted by subsequent updates.
240 *
241 * @param int $version
242 *
243 * @dataProvider versionThreeAndFour
244 * @throws \CRM_Core_Exception
245 */
246 public function testIndividualSubType($version) {
247 $this->_apiversion = $version;
248 $params = [
249 'first_name' => 'test abc',
250 'contact_type' => 'Individual',
251 'last_name' => 'test xyz',
252 'contact_sub_type' => ['Student', 'Staff'],
253 ];
254 $contact = $this->callAPISuccess('contact', 'create', $params);
255 $cid = $contact['id'];
256
257 $params = [
258 'id' => $cid,
259 'middle_name' => 'foo',
260 ];
261 $this->callAPISuccess('contact', 'create', $params);
262
263 $contact = $this->callAPISuccess('contact', 'get', ['id' => $cid]);
264
265 $this->assertEquals(['Student', 'Staff'], $contact['values'][$cid]['contact_sub_type']);
266
267 $this->callAPISuccess('Contact', 'create', [
268 'id' => $cid,
269 'contact_sub_type' => [],
270 ]);
271
272 $contact = $this->callAPISuccess('contact', 'get', ['id' => $cid]);
273 $this->assertTrue(empty($contact['values'][$cid]['contact_sub_type']));
274 }
275
276 /**
277 * Verify that we can retreive contacts of different sub types
278 *
279 * @param int $version
280 *
281 * @throws \CRM_Core_Exception
282 * @throws \CiviCRM_API3_Exception
283 *
284 * @dataProvider versionThreeAndFour
285 */
286 public function testGetMultipleContactSubTypes($version) {
287 $this->_apiversion = $version;
288
289 // This test presumes that there are no parents or students in the dataset
290
291 // create a student
292 $student = $this->callAPISuccess('contact', 'create', [
293 'email' => 'student@example.com',
294 'contact_type' => 'Individual',
295 'contact_sub_type' => 'Student',
296 ]);
297
298 // create a parent
299 $parent = $this->callAPISuccess('contact', 'create', [
300 'email' => 'parent@example.com',
301 'contact_type' => 'Individual',
302 'contact_sub_type' => 'Parent',
303 ]);
304
305 // create a parent
306 $this->callAPISuccess('contact', 'create', [
307 'email' => 'parent@example.com',
308 'contact_type' => 'Individual',
309 ]);
310
311 // get all students and parents
312 $getParams = ['contact_sub_type' => ['IN' => ['Parent', 'Student']]];
313 $result = civicrm_api3('contact', 'get', $getParams);
314
315 // check that we retrieved the student and the parent
316 $this->assertArrayHasKey($student['id'], $result['values']);
317 $this->assertArrayHasKey($parent['id'], $result['values']);
318 $this->assertEquals(2, $result['count']);
319
320 }
321
322 /**
323 * Verify that attempt to create contact with empty params fails.
324 */
325 public function testCreateEmptyContact() {
326 $this->callAPIFailure('contact', 'create', []);
327 }
328
329 /**
330 * Verify that attempt to create contact with bad contact type fails.
331 */
332 public function testCreateBadTypeContact() {
333 $params = [
334 'email' => 'man1@yahoo.com',
335 'contact_type' => 'Does not Exist',
336 ];
337 $this->callAPIFailure('contact', 'create', $params, "'Does not Exist' is not a valid option for field contact_type");
338 }
339
340 /**
341 * Verify that attempt to create individual contact without required fields fails.
342 */
343 public function testCreateBadRequiredFieldsIndividual() {
344 $params = [
345 'middle_name' => 'This field is not required',
346 'contact_type' => 'Individual',
347 ];
348 $this->callAPIFailure('contact', 'create', $params);
349 }
350
351 /**
352 * Verify that attempt to create household contact without required fields fails.
353 */
354 public function testCreateBadRequiredFieldsHousehold() {
355 $params = [
356 'middle_name' => 'This field is not required',
357 'contact_type' => 'Household',
358 ];
359 $this->callAPIFailure('contact', 'create', $params);
360 }
361
362 /**
363 * Test required field check.
364 *
365 * Verify that attempt to create organization contact without required fields fails.
366 */
367 public function testCreateBadRequiredFieldsOrganization() {
368 $params = [
369 'middle_name' => 'This field is not required',
370 'contact_type' => 'Organization',
371 ];
372
373 $this->callAPIFailure('contact', 'create', $params);
374 }
375
376 /**
377 * Verify that attempt to create individual contact with only an email succeeds.
378 *
379 * @throws \CRM_Core_Exception
380 */
381 public function testCreateEmailIndividual() {
382 $primaryEmail = 'man3@yahoo.com';
383 $notPrimaryEmail = 'man4@yahoo.com';
384 $params = [
385 'email' => $primaryEmail,
386 'contact_type' => 'Individual',
387 'location_type_id' => 1,
388 ];
389
390 $contact1 = $this->callAPISuccess('contact', 'create', $params);
391
392 $this->assertEquals(3, $contact1['id']);
393 $email1 = $this->callAPISuccess('email', 'get', ['contact_id' => $contact1['id']]);
394 $this->assertEquals(1, $email1['count']);
395 $this->assertEquals($primaryEmail, $email1['values'][$email1['id']]['email']);
396
397 $this->callAPISuccess('email', 'create', ['contact_id' => $contact1['id'], 'is_primary' => 0, 'email' => $notPrimaryEmail]);
398
399 // Case 1: Check with criteria primary 'email' => array('IS NOT NULL' => 1)
400 $result = $this->callAPISuccess('contact', 'get', ['email' => ['IS NOT NULL' => 1]]);
401 $this->assertEquals($primaryEmail, $email1['values'][$email1['id']]['email']);
402
403 // Case 2: Check with criteria primary 'email' => array('<>' => '')
404 $result = $this->callAPISuccess('contact', 'get', ['email' => ['<>' => '']]);
405 $this->assertEquals($primaryEmail, $email1['values'][$email1['id']]['email']);
406
407 // Case 3: Check with email_id='primary email id'
408 $result = $this->callAPISuccessGetSingle('contact', ['email_id' => $email1['id']]);
409 $this->assertEquals($contact1['id'], $result['id']);
410
411 // Check no wildcard is appended
412 $this->callAPISuccessGetCount('Contact', ['email' => 'man3@yahoo.co'], 0);
413
414 $this->callAPISuccess('contact', 'delete', $contact1);
415 }
416
417 /**
418 * Test creating individual by name.
419 *
420 * Verify create individual contact with only first and last names succeeds.
421 *
422 * @param int $version
423 *
424 * @dataProvider versionThreeAndFour
425 * @throws \CRM_Core_Exception
426 */
427 public function testCreateNameIndividual($version) {
428 $this->_apiversion = $version;
429 $params = [
430 'first_name' => 'abc1',
431 'contact_type' => 'Individual',
432 'last_name' => 'xyz1',
433 ];
434
435 $this->callAPISuccess('contact', 'create', $params);
436 }
437
438 /**
439 * Test creating individual by display_name.
440 *
441 * Display name & sort name should be set.
442 *
443 * @param int $version
444 *
445 * @dataProvider versionThreeAndFour
446 * @throws \CRM_Core_Exception
447 */
448 public function testCreateDisplayNameIndividual($version) {
449 $this->_apiversion = $version;
450 $params = [
451 'display_name' => 'abc1',
452 'contact_type' => 'Individual',
453 ];
454
455 $contact = $this->callAPISuccess('contact', 'create', $params);
456 $params['sort_name'] = 'abc1';
457 $this->getAndCheck($params, $contact['id'], 'contact');
458 }
459
460 /**
461 * Test that name searches are case insensitive.
462 *
463 * @param int $version
464 *
465 * @dataProvider versionThreeAndFour
466 * @throws \CRM_Core_Exception
467 */
468 public function testGetNameVariantsCaseInsensitive($version) {
469 $this->_apiversion = $version;
470 $this->callAPISuccess('contact', 'create', [
471 'display_name' => 'Abc1',
472 'contact_type' => 'Individual',
473 ]);
474 $this->callAPISuccessGetSingle('Contact', ['display_name' => 'aBc1']);
475 $this->callAPISuccessGetSingle('Contact', ['sort_name' => 'aBc1']);
476 Civi::settings()->set('includeNickNameInName', TRUE);
477 $this->callAPISuccessGetSingle('Contact', ['display_name' => 'aBc1']);
478 $this->callAPISuccessGetSingle('Contact', ['sort_name' => 'aBc1']);
479 Civi::settings()->set('includeNickNameInName', FALSE);
480 }
481
482 /**
483 * Test old keys still work.
484 *
485 * Verify that attempt to create individual contact with
486 * first and last names and old key values works
487 *
488 * @throws \CRM_Core_Exception
489 */
490 public function testCreateNameIndividualOldKeys() {
491 $params = [
492 'individual_prefix' => 'Dr.',
493 'first_name' => 'abc1',
494 'contact_type' => 'Individual',
495 'last_name' => 'xyz1',
496 'individual_suffix' => 'Jr.',
497 ];
498
499 $contact = $this->callAPISuccess('contact', 'create', $params);
500 $result = $this->callAPISuccess('contact', 'getsingle', ['id' => $contact['id']]);
501
502 $this->assertArrayKeyExists('prefix_id', $result);
503 $this->assertArrayKeyExists('suffix_id', $result);
504 $this->assertArrayKeyExists('gender_id', $result);
505 $this->assertEquals(4, $result['prefix_id']);
506 $this->assertEquals(1, $result['suffix_id']);
507 }
508
509 /**
510 * Test preferred keys work.
511 *
512 * Verify that attempt to create individual contact with
513 * first and last names and old key values works
514 */
515 public function testCreateNameIndividualRecommendedKeys2() {
516 $params = [
517 'prefix_id' => 'Dr.',
518 'first_name' => 'abc1',
519 'contact_type' => 'Individual',
520 'last_name' => 'xyz1',
521 'suffix_id' => 'Jr.',
522 'gender_id' => 'Male',
523 ];
524
525 $contact = $this->callAPISuccess('contact', 'create', $params);
526 $result = $this->callAPISuccess('contact', 'getsingle', ['id' => $contact['id']]);
527
528 $this->assertArrayKeyExists('prefix_id', $result);
529 $this->assertArrayKeyExists('suffix_id', $result);
530 $this->assertArrayKeyExists('gender_id', $result);
531 $this->assertEquals(4, $result['prefix_id']);
532 $this->assertEquals(1, $result['suffix_id']);
533 }
534
535 /**
536 * Test household name is sufficient for create.
537 *
538 * Verify that attempt to create household contact with only
539 * household name succeeds
540 *
541 * @param int $version
542 *
543 * @dataProvider versionThreeAndFour
544 * @throws \CRM_Core_Exception
545 */
546 public function testCreateNameHousehold($version) {
547 $this->_apiversion = $version;
548 $params = [
549 'household_name' => 'The abc Household',
550 'contact_type' => 'Household',
551 ];
552 $this->callAPISuccess('contact', 'create', $params);
553 }
554
555 /**
556 * Test organization name is sufficient for create.
557 *
558 * Verify that attempt to create organization contact with only
559 * organization name succeeds.
560 *
561 * @param int $version
562 *
563 * @dataProvider versionThreeAndFour
564 */
565 public function testCreateNameOrganization($version) {
566 $this->_apiversion = $version;
567 $params = [
568 'organization_name' => 'The abc Organization',
569 'contact_type' => 'Organization',
570 ];
571 $this->callAPISuccess('contact', 'create', $params);
572 }
573
574 /**
575 * Verify that attempt to create organization contact without organization name fails.
576 */
577 public function testCreateNoNameOrganization() {
578 $params = [
579 'first_name' => 'The abc Organization',
580 'contact_type' => 'Organization',
581 ];
582 $this->callAPIFailure('contact', 'create', $params);
583 }
584
585 /**
586 * Check that permissions on API key are restricted (CRM-18112).
587 *
588 * @param int $version
589 *
590 * @dataProvider versionThreeAndFour
591 */
592 public function testCreateApiKey($version) {
593 $this->_apiversion = $version;
594 $config = CRM_Core_Config::singleton();
595 $contactId = $this->individualCreate([
596 'first_name' => 'A',
597 'last_name' => 'B',
598 ]);
599
600 // Allow edit -- because permissions aren't being checked
601 $config->userPermissionClass->permissions = [];
602 $result = $this->callAPISuccess('Contact', 'create', [
603 'id' => $contactId,
604 'api_key' => 'original',
605 ]);
606 $this->assertEquals('original', $result['values'][$contactId]['api_key']);
607
608 // Allow edit -- because we have adequate permission
609 $config->userPermissionClass->permissions = ['access CiviCRM', 'edit all contacts', 'edit api keys'];
610 $result = $this->callAPISuccess('Contact', 'create', [
611 'check_permissions' => 1,
612 'id' => $contactId,
613 'api_key' => 'abcd1234',
614 ]);
615 $this->assertEquals('abcd1234', $result['values'][$contactId]['api_key']);
616
617 // Disallow edit -- because we don't have permission
618 $config->userPermissionClass->permissions = ['access CiviCRM', 'edit all contacts'];
619 $result = $this->callAPIFailure('Contact', 'create', [
620 'check_permissions' => 1,
621 'id' => $contactId,
622 'api_key' => 'defg4321',
623 ]);
624 $this->assertRegExp(';Permission denied to modify api key;', $result['error_message']);
625
626 // Return everything -- because permissions are not being checked
627 $config->userPermissionClass->permissions = [];
628 $result = $this->callAPISuccess('Contact', 'create', [
629 'id' => $contactId,
630 'first_name' => 'A2',
631 ]);
632 $this->assertEquals('A2', $result['values'][$contactId]['first_name']);
633 $this->assertEquals('B', $result['values'][$contactId]['last_name']);
634 $this->assertEquals('abcd1234', $result['values'][$contactId]['api_key']);
635
636 // Return everything -- because we have adequate permission
637 $config->userPermissionClass->permissions = ['access CiviCRM', 'edit all contacts', 'edit api keys'];
638 $result = $this->callAPISuccess('Contact', 'create', [
639 'check_permissions' => 1,
640 'id' => $contactId,
641 'first_name' => 'A3',
642 ]);
643 $this->assertEquals('A3', $result['values'][$contactId]['first_name']);
644 $this->assertEquals('B', $result['values'][$contactId]['last_name']);
645 $this->assertEquals('abcd1234', $result['values'][$contactId]['api_key']);
646
647 // Should also be returned via join
648 $joinResult = $this->callAPISuccessGetSingle('Email', [
649 'check_permissions' => 1,
650 'contact_id' => $contactId,
651 'return' => 'contact_id.api_key',
652 ]);
653 $field = $this->_apiversion == 4 ? 'contact.api_key' : 'contact_id.api_key';
654 $this->assertEquals('abcd1234', $joinResult[$field]);
655
656 // Restricted return -- because we don't have permission
657 $config->userPermissionClass->permissions = ['access CiviCRM', 'view all contacts', 'edit all contacts'];
658 $result = $this->callAPISuccess('Contact', 'create', [
659 'check_permissions' => 1,
660 'id' => $contactId,
661 'first_name' => 'A4',
662 ]);
663 $this->assertEquals('A4', $result['values'][$contactId]['first_name']);
664 $this->assertEquals('B', $result['values'][$contactId]['last_name']);
665 $this->assertTrue(empty($result['values'][$contactId]['api_key']));
666
667 // Should also be restricted via join
668 $joinResult = $this->callAPISuccessGetSingle('Email', [
669 'check_permissions' => 1,
670 'contact_id' => $contactId,
671 'return' => ['email', 'contact_id.api_key'],
672 ]);
673 $this->assertTrue(empty($joinResult['contact_id.api_key']));
674 }
675
676 /**
677 * Check with complete array + custom field.
678 *
679 * Note that the test is written on purpose without any
680 * variables specific to participant so it can be replicated into other entities
681 * and / or moved to the automated test suite
682 *
683 * @param int $version
684 *
685 * @dataProvider versionThreeAndFour
686 */
687 public function testCreateWithCustom($version) {
688 $this->_apiversion = $version;
689 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
690
691 $params = $this->_params;
692 $params['custom_' . $ids['custom_field_id']] = "custom string";
693 $description = "This demonstrates setting a custom field through the API.";
694 $result = $this->callAPIAndDocument($this->_entity, 'create', $params, __FUNCTION__, __FILE__, $description);
695
696 $check = $this->callAPISuccess($this->_entity, 'get', [
697 'return.custom_' . $ids['custom_field_id'] => 1,
698 'id' => $result['id'],
699 ]);
700 $this->assertEquals("custom string", $check['values'][$check['id']]['custom_' . $ids['custom_field_id']]);
701
702 $this->customFieldDelete($ids['custom_field_id']);
703 $this->customGroupDelete($ids['custom_group_id']);
704 }
705
706 /**
707 * CRM-12773 - expectation is that civicrm quietly ignores fields without values.
708 */
709 public function testCreateWithNULLCustomCRM12773() {
710 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
711 $params = $this->_params;
712 $params['custom_' . $ids['custom_field_id']] = NULL;
713 $this->callAPISuccess('contact', 'create', $params);
714 $this->customFieldDelete($ids['custom_field_id']);
715 $this->customGroupDelete($ids['custom_group_id']);
716 }
717
718 /**
719 * CRM-14232 test preferred language set to site default if not passed.
720 *
721 * @param int $version
722 *
723 * @dataProvider versionThreeAndFour
724 */
725 public function testCreatePreferredLanguageUnset($version) {
726 $this->_apiversion = $version;
727 $this->callAPISuccess('Contact', 'create', [
728 'first_name' => 'Snoop',
729 'last_name' => 'Dog',
730 'contact_type' => 'Individual',
731 ]);
732 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Dog']);
733 $this->assertEquals('en_US', $result['preferred_language']);
734 }
735
736 /**
737 * CRM-14232 test preferred language returns setting if not passed.
738 *
739 * @param int $version
740 *
741 * @dataProvider versionThreeAndFour
742 */
743 public function testCreatePreferredLanguageSet($version) {
744 $this->_apiversion = $version;
745 $this->callAPISuccess('Setting', 'create', ['contact_default_language' => 'fr_FR']);
746 $this->callAPISuccess('Contact', 'create', [
747 'first_name' => 'Snoop',
748 'last_name' => 'Dog',
749 'contact_type' => 'Individual',
750 ]);
751 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Dog']);
752 $this->assertEquals('fr_FR', $result['preferred_language']);
753 }
754
755 /**
756 * CRM-14232 test preferred language returns setting if not passed where setting is NULL.
757 * TODO: Api4
758 */
759 public function testCreatePreferredLanguageNull() {
760 $this->callAPISuccess('Setting', 'create', ['contact_default_language' => 'null']);
761 $this->callAPISuccess('Contact', 'create', [
762 'first_name' => 'Snoop',
763 'last_name' => 'Dog',
764 'contact_type' => 'Individual',
765 ]);
766 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Dog']);
767 $this->assertEquals(NULL, $result['preferred_language']);
768 }
769
770 /**
771 * CRM-14232 test preferred language returns setting if not passed where setting is NULL.
772 *
773 * @param int $version
774 *
775 * @dataProvider versionThreeAndFour
776 */
777 public function testCreatePreferredLanguagePassed($version) {
778 $this->_apiversion = $version;
779 $this->callAPISuccess('Setting', 'create', ['contact_default_language' => 'null']);
780 $this->callAPISuccess('Contact', 'create', [
781 'first_name' => 'Snoop',
782 'last_name' => 'Dog',
783 'contact_type' => 'Individual',
784 'preferred_language' => 'en_AU',
785 ]);
786 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Dog']);
787 $this->assertEquals('en_AU', $result['preferred_language']);
788 }
789
790 /**
791 * CRM-15792 - create/update datetime field for contact.
792 */
793 public function testCreateContactCustomFldDateTime() {
794 $customGroup = $this->customGroupCreate(['extends' => 'Individual', 'title' => 'datetime_test_group']);
795 $dateTime = CRM_Utils_Date::currentDBDate();
796 //check date custom field is saved along with time when time_format is set
797 $params = [
798 'first_name' => 'abc3',
799 'last_name' => 'xyz3',
800 'contact_type' => 'Individual',
801 'email' => 'man3@yahoo.com',
802 'api.CustomField.create' => [
803 'custom_group_id' => $customGroup['id'],
804 'name' => 'test_datetime',
805 'label' => 'Demo Date',
806 'html_type' => 'Select Date',
807 'data_type' => 'Date',
808 'time_format' => 2,
809 'weight' => 4,
810 'is_required' => 1,
811 'is_searchable' => 0,
812 'is_active' => 1,
813 ],
814 ];
815
816 $result = $this->callAPISuccess('Contact', 'create', $params);
817 $customFldId = $result['values'][$result['id']]['api.CustomField.create']['id'];
818 $this->assertNotNull($result['id']);
819 $this->assertNotNull($customFldId);
820
821 $params = [
822 'id' => $result['id'],
823 "custom_{$customFldId}" => $dateTime,
824 'api.CustomValue.get' => 1,
825 ];
826
827 $result = $this->callAPISuccess('Contact', 'create', $params);
828 $this->assertNotNull($result['id']);
829 $customFldDate = date("YmdHis", strtotime($result['values'][$result['id']]['api.CustomValue.get']['values'][0]['latest']));
830 $this->assertNotNull($customFldDate);
831 $this->assertEquals($dateTime, $customFldDate);
832 $customValueId = $result['values'][$result['id']]['api.CustomValue.get']['values'][0]['id'];
833 $dateTime = date('Ymd');
834 //date custom field should not contain time part when time_format is null
835 $params = [
836 'id' => $result['id'],
837 'api.CustomField.create' => [
838 'id' => $customFldId,
839 'html_type' => 'Select Date',
840 'data_type' => 'Date',
841 'time_format' => '',
842 ],
843 'api.CustomValue.create' => [
844 'id' => $customValueId,
845 'entity_id' => $result['id'],
846 "custom_{$customFldId}" => $dateTime,
847 ],
848 'api.CustomValue.get' => 1,
849 ];
850 $result = $this->callAPISuccess('Contact', 'create', $params);
851 $this->assertNotNull($result['id']);
852 $customFldDate = date("Ymd", strtotime($result['values'][$result['id']]['api.CustomValue.get']['values'][0]['latest']));
853 $customFldTime = date("His", strtotime($result['values'][$result['id']]['api.CustomValue.get']['values'][0]['latest']));
854 $this->assertNotNull($customFldDate);
855 $this->assertEquals($dateTime, $customFldDate);
856 $this->assertEquals(000000, $customFldTime);
857 $this->callAPISuccess('Contact', 'create', $params);
858 }
859
860 /**
861 * Test creating a current employer through API.
862 */
863 public function testContactCreateCurrentEmployer() {
864 // Here we will just do the get for set-up purposes.
865 $count = $this->callAPISuccess('contact', 'getcount', [
866 'organization_name' => 'new employer org',
867 'contact_type' => 'Organization',
868 ]);
869 $this->assertEquals(0, $count);
870 $employerResult = $this->callAPISuccess('contact', 'create', array_merge($this->_params, [
871 'current_employer' => 'new employer org',
872 ]));
873 // do it again as an update to check it doesn't cause an error
874 $employerResult = $this->callAPISuccess('contact', 'create', array_merge($this->_params, [
875 'current_employer' => 'new employer org',
876 'id' => $employerResult['id'],
877 ]));
878 $expectedCount = 1;
879 $this->callAPISuccess('contact', 'getcount', [
880 'organization_name' => 'new employer org',
881 'contact_type' => 'Organization',
882 ], $expectedCount);
883
884 $result = $this->callAPISuccess('contact', 'getsingle', [
885 'id' => $employerResult['id'],
886 ]);
887
888 $this->assertEquals('new employer org', $result['current_employer']);
889
890 }
891
892 /**
893 * Test creating a current employer through API.
894 *
895 * Check it will re-activate a de-activated employer
896 */
897 public function testContactCreateDuplicateCurrentEmployerEnables() {
898 // Set up - create employer relationship.
899 $employerResult = $this->callAPISuccess('contact', 'create', array_merge($this->_params, ['current_employer' => 'new employer org']));
900 $relationship = $this->callAPISuccess('relationship', 'get', [
901 'contact_id_a' => $employerResult['id'],
902 ]);
903
904 //disable & check it is disabled
905 $this->callAPISuccess('relationship', 'create', ['id' => $relationship['id'], 'is_active' => 0]);
906 $this->callAPISuccess('relationship', 'getvalue', [
907 'id' => $relationship['id'],
908 'return' => 'is_active',
909 ], 0);
910
911 // Re-set the current employer - thus enabling the relationship.
912 $this->callAPISuccess('contact', 'create', array_merge($this->_params, [
913 'current_employer' => 'new employer org',
914 'id' => $employerResult['id'],
915 ]));
916 //check is_active is now 1
917 $relationship = $this->callAPISuccess('relationship', 'getsingle', ['return' => 'is_active']);
918 $this->assertEquals(1, $relationship['is_active']);
919 }
920
921 /**
922 * Check deceased contacts are not retrieved.
923 *
924 * Note at time of writing the default is to return default. This should possibly be changed & test added.
925 *
926 * @param int $version
927 *
928 * @dataProvider versionThreeAndFour
929 */
930 public function testGetDeceasedRetrieved($version) {
931 $this->_apiversion = $version;
932 $this->callAPISuccess($this->_entity, 'create', $this->_params);
933 $c2 = $this->callAPISuccess($this->_entity, 'create', [
934 'first_name' => 'bb',
935 'last_name' => 'ccc',
936 'contact_type' => 'Individual',
937 'is_deceased' => 1,
938 ]);
939 $result = $this->callAPISuccess($this->_entity, 'get', ['is_deceased' => 0]);
940 $this->assertFalse(array_key_exists($c2['id'], $result['values']));
941 }
942
943 /**
944 * Test that sort works - old syntax.
945 */
946 public function testGetSort() {
947 $c1 = $this->callAPISuccess($this->_entity, 'create', $this->_params);
948 $c2 = $this->callAPISuccess($this->_entity, 'create', [
949 'first_name' => 'bb',
950 'last_name' => 'ccc',
951 'contact_type' => 'Individual',
952 ]);
953 $result = $this->callAPISuccess($this->_entity, 'get', [
954 'sort' => 'first_name ASC',
955 'return.first_name' => 1,
956 'sequential' => 1,
957 'rowCount' => 1,
958 'contact_type' => 'Individual',
959 ]);
960
961 $this->assertEquals('abc1', $result['values'][0]['first_name']);
962 $result = $this->callAPISuccess($this->_entity, 'get', [
963 'sort' => 'first_name DESC',
964 'return.first_name' => 1,
965 'sequential' => 1,
966 'rowCount' => 1,
967 ]);
968 $this->assertEquals('bb', $result['values'][0]['first_name']);
969
970 $this->callAPISuccess($this->_entity, 'delete', ['id' => $c1['id']]);
971 $this->callAPISuccess($this->_entity, 'delete', ['id' => $c2['id']]);
972 }
973
974 /**
975 * Test the like operator works for Contact.get
976 *
977 * @throws \CRM_Core_Exception
978 */
979 public function testGetEmailLike() {
980 $this->individualCreate();
981 $this->callAPISuccessGetCount('Contact', ['email' => ['LIKE' => 'an%']], 1);
982 $this->callAPISuccessGetCount('Contact', ['email' => ['LIKE' => 'ab%']], 0);
983 }
984
985 /**
986 * Test that we can retrieve contacts using array syntax.
987 *
988 * I.e 'id' => array('IN' => array('3,4')).
989 *
990 * @param int $version
991 *
992 * @dataProvider versionThreeAndFour
993 */
994 public function testGetINIDArray($version) {
995 $this->_apiversion = $version;
996 $c1 = $this->callAPISuccess($this->_entity, 'create', $this->_params);
997 $c2 = $this->callAPISuccess($this->_entity, 'create', [
998 'first_name' => 'bb',
999 'last_name' => 'ccc',
1000 'contact_type' => 'Individual',
1001 ]);
1002 $c3 = $this->callAPISuccess($this->_entity, 'create', [
1003 'first_name' => 'hh',
1004 'last_name' => 'll',
1005 'contact_type' => 'Individual',
1006 ]);
1007 $result = $this->callAPISuccess($this->_entity, 'get', ['id' => ['IN' => [$c1['id'], $c3['id']]]]);
1008 $this->assertEquals(2, $result['count']);
1009 $this->assertEquals([$c1['id'], $c3['id']], array_keys($result['values']));
1010 $this->callAPISuccess($this->_entity, 'delete', ['id' => $c1['id']]);
1011 $this->callAPISuccess($this->_entity, 'delete', ['id' => $c2['id']]);
1012 $this->callAPISuccess($this->_entity, 'delete', ['id' => $c3['id']]);
1013 }
1014
1015 /**
1016 * Test variants on deleted behaviour.
1017 */
1018 public function testGetDeleted() {
1019 $params = $this->_params;
1020 $contact1 = $this->callAPISuccess('contact', 'create', $params);
1021 $params['is_deleted'] = 1;
1022 $params['last_name'] = 'bcd';
1023 $contact2 = $this->callAPISuccess('contact', 'create', $params);
1024 $countActive = $this->callAPISuccess('contact', 'getcount', [
1025 'showAll' => 'active',
1026 'contact_type' => 'Individual',
1027 ]);
1028 $countAll = $this->callAPISuccess('contact', 'getcount', ['showAll' => 'all', 'contact_type' => 'Individual']);
1029 $countTrash = $this->callAPISuccess('contact', 'getcount', ['showAll' => 'trash', 'contact_type' => 'Individual']);
1030 $countDefault = $this->callAPISuccess('contact', 'getcount', ['contact_type' => 'Individual']);
1031 $countDeleted = $this->callAPISuccess('contact', 'getcount', [
1032 'contact_type' => 'Individual',
1033 'contact_is_deleted' => 1,
1034 ]);
1035 $countNotDeleted = $this->callAPISuccess('contact', 'getcount', [
1036 'contact_is_deleted' => 0,
1037 'contact_type' => 'Individual',
1038 ]);
1039 $this->callAPISuccess('contact', 'delete', ['id' => $contact1['id']]);
1040 $this->callAPISuccess('contact', 'delete', ['id' => $contact2['id']]);
1041 $this->assertEquals(1, $countNotDeleted, 'contact_is_deleted => 0 is respected');
1042 $this->assertEquals(1, $countActive);
1043 $this->assertEquals(1, $countTrash);
1044 $this->assertEquals(2, $countAll);
1045 $this->assertEquals(1, $countDeleted);
1046 $this->assertEquals(1, $countDefault, 'Only active by default in line');
1047 }
1048
1049 /**
1050 * Test that sort works - new syntax.
1051 *
1052 * @param int $version
1053 *
1054 * @dataProvider versionThreeAndFour
1055 */
1056 public function testGetSortNewSyntax($version) {
1057 $this->_apiversion = $version;
1058 $c1 = $this->callAPISuccess($this->_entity, 'create', $this->_params);
1059 $c2 = $this->callAPISuccess($this->_entity, 'create', [
1060 'first_name' => 'bb',
1061 'last_name' => 'ccc',
1062 'contact_type' => 'Individual',
1063 ]);
1064 $result = $this->callAPISuccess($this->_entity, 'getvalue', [
1065 'return' => 'first_name',
1066 'contact_type' => 'Individual',
1067 'options' => [
1068 'limit' => 1,
1069 'sort' => 'first_name',
1070 ],
1071 ]);
1072 $this->assertEquals('abc1', $result);
1073
1074 $result = $this->callAPISuccess($this->_entity, 'getvalue', [
1075 'return' => 'first_name',
1076 'contact_type' => 'Individual',
1077 'options' => [
1078 'limit' => 1,
1079 'sort' => 'first_name DESC',
1080 ],
1081 ]);
1082 $this->assertEquals('bb', $result);
1083
1084 $this->callAPISuccess($this->_entity, 'delete', ['id' => $c1['id']]);
1085 $this->callAPISuccess($this->_entity, 'delete', ['id' => $c2['id']]);
1086 }
1087
1088 /**
1089 * Test sort and limit for chained relationship get.
1090 *
1091 * https://issues.civicrm.org/jira/browse/CRM-15983
1092 * @param int $version
1093 *
1094 * @dataProvider versionThreeAndFour
1095 */
1096 public function testSortLimitChainedRelationshipGetCRM15983($version) {
1097 $this->_apiversion = $version;
1098 // Some contact
1099 $create_result_1 = $this->callAPISuccess('contact', 'create', [
1100 'first_name' => 'Jules',
1101 'last_name' => 'Smos',
1102 'contact_type' => 'Individual',
1103 ]);
1104
1105 // Create another contact with two relationships.
1106 $create_params = [
1107 'first_name' => 'Jos',
1108 'last_name' => 'Smos',
1109 'contact_type' => 'Individual',
1110 'api.relationship.create' => [
1111 [
1112 'contact_id_a' => '$value.id',
1113 'contact_id_b' => $create_result_1['id'],
1114 // spouse of:
1115 'relationship_type_id' => 2,
1116 'start_date' => '2005-01-12',
1117 'end_date' => '2006-01-11',
1118 'description' => 'old',
1119 ],
1120 [
1121 'contact_id_a' => '$value.id',
1122 'contact_id_b' => $create_result_1['id'],
1123 // spouse of (was married twice :))
1124 'relationship_type_id' => 2,
1125 'start_date' => '2006-07-01',
1126 'end_date' => '2010-07-01',
1127 'description' => 'new',
1128 ],
1129 ],
1130 ];
1131 $create_result = $this->callAPISuccess('contact', 'create', $create_params);
1132
1133 // Try to retrieve the contact and the most recent relationship.
1134 $get_params = [
1135 'sequential' => 1,
1136 'id' => $create_result['id'],
1137 'api.relationship.get' => [
1138 'contact_id_a' => '$value.id',
1139 'options' => [
1140 'limit' => '1',
1141 'sort' => 'start_date DESC',
1142 ],
1143 ],
1144 ];
1145 $get_result = $this->callAPISuccess('contact', 'getsingle', $get_params);
1146
1147 // Clean up.
1148 $this->callAPISuccess('contact', 'delete', [
1149 'id' => $create_result['id'],
1150 ]);
1151
1152 // Assert.
1153 $this->assertEquals(1, $get_result['api.relationship.get']['count']);
1154 $this->assertEquals('new', $get_result['api.relationship.get']['values'][0]['description']);
1155 }
1156
1157 /**
1158 * Test apostrophe works in get & create.
1159 *
1160 * @param int $version
1161 *
1162 * @dataProvider versionThreeAndFour
1163 */
1164 public function testGetApostropheCRM10857($version) {
1165 $this->_apiversion = $version;
1166 $params = array_merge($this->_params, ['last_name' => "O'Connor"]);
1167 $this->callAPISuccess($this->_entity, 'create', $params);
1168 $result = $this->callAPISuccess($this->_entity, 'getsingle', [
1169 'last_name' => "O'Connor",
1170 'sequential' => 1,
1171 ]);
1172 $this->assertEquals("O'Connor", $result['last_name']);
1173 }
1174
1175 /**
1176 * Test between accepts zero.
1177 *
1178 * In the past it incorrectly required !empty.
1179 *
1180 * @param int $version
1181 *
1182 * @dataProvider versionThreeAndFour
1183 */
1184 public function testGetBetweenZeroWorks($version) {
1185 $this->_apiversion = $version;
1186 $this->callAPISuccess($this->_entity, 'get', [
1187 'contact_id' => ['BETWEEN' => [0, 9]],
1188 ]);
1189 $this->callAPISuccess($this->_entity, 'get', [
1190 'contact_id' => [
1191 'BETWEEN' => [
1192 (0 - 9),
1193 0,
1194 ],
1195 ],
1196 ]);
1197 }
1198
1199 /**
1200 * Test retrieval by addressee id.
1201 * V3 only - the "skip_greeting_processing" param is not currently in v4
1202 */
1203 public function testGetByAddresseeID() {
1204 $individual1ID = $this->individualCreate([
1205 'skip_greeting_processing' => 1,
1206 'addressee_id' => 'null',
1207 'email_greeting_id' => 'null',
1208 'postal_greeting_id' => 'null',
1209 ]);
1210 $individual2ID = $this->individualCreate();
1211
1212 $this->assertEquals($individual1ID,
1213 $this->callAPISuccessGetValue('Contact', ['contact_type' => 'Individual', 'addressee_id' => ['IS NULL' => 1], 'return' => 'id'])
1214 );
1215 $this->assertEquals($individual1ID,
1216 $this->callAPISuccessGetValue('Contact', ['contact_type' => 'Individual', 'email_greeting_id' => ['IS NULL' => 1], 'return' => 'id'])
1217 );
1218 $this->assertEquals($individual1ID,
1219 $this->callAPISuccessGetValue('Contact', ['contact_type' => 'Individual', 'postal_greeting_id' => ['IS NULL' => 1], 'return' => 'id'])
1220 );
1221
1222 $this->assertEquals($individual2ID,
1223 $this->callAPISuccessGetValue('Contact', ['contact_type' => 'Individual', 'addressee_id' => ['NOT NULL' => 1], 'return' => 'id'])
1224 );
1225 }
1226
1227 /**
1228 * Check with complete array + custom field.
1229 *
1230 * Note that the test is written on purpose without any
1231 * variables specific to participant so it can be replicated into other entities
1232 * and / or moved to the automated test suite
1233 */
1234 public function testGetWithCustom() {
1235 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
1236
1237 $params = $this->_params;
1238 $params['custom_' . $ids['custom_field_id']] = "custom string";
1239 $description = "This demonstrates setting a custom field through the API.";
1240 $subfile = "CustomFieldGet";
1241 $result = $this->callAPISuccess($this->_entity, 'create', $params);
1242
1243 $check = $this->callAPIAndDocument($this->_entity, 'get', [
1244 'return.custom_' . $ids['custom_field_id'] => 1,
1245 'id' => $result['id'],
1246 ], __FUNCTION__, __FILE__, $description, $subfile);
1247
1248 $this->assertEquals("custom string", $check['values'][$check['id']]['custom_' . $ids['custom_field_id']]);
1249 $fields = ($this->callAPISuccess('contact', 'getfields', $params));
1250 $this->assertTrue(is_array($fields['values']['custom_' . $ids['custom_field_id']]));
1251 $this->customFieldDelete($ids['custom_field_id']);
1252 $this->customGroupDelete($ids['custom_group_id']);
1253 }
1254
1255 /**
1256 * Tests that using 'return' with a custom field not of type contact does not inappropriately filter.
1257 *
1258 * https://lab.civicrm.org/dev/core/issues/1025
1259 *
1260 * @throws \CRM_Core_Exception
1261 */
1262 public function testGetWithCustomOfActivityType() {
1263 $this->createCustomGroupWithFieldOfType(['extends' => 'Activity']);
1264 $this->createCustomGroupWithFieldOfType(['extends' => 'Contact'], 'text', 'contact_');
1265 $contactID = $this->individualCreate();
1266 $this->callAPISuccessGetSingle('Contact', ['id' => $contactID, 'return' => ['external_identifier', $this->getCustomFieldName('contact_text')]]);
1267 }
1268
1269 /**
1270 * Check with complete array + custom field.
1271 *
1272 * Note that the test is written on purpose without any
1273 * variables specific to participant so it can be replicated into other entities
1274 * and / or moved to the automated test suite
1275 */
1276 public function testGetWithCustomReturnSyntax() {
1277 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
1278
1279 $params = $this->_params;
1280 $params['custom_' . $ids['custom_field_id']] = "custom string";
1281 $description = "This demonstrates setting a custom field through the API.";
1282 $subfile = "CustomFieldGetReturnSyntaxVariation";
1283 $result = $this->callAPISuccess($this->_entity, 'create', $params);
1284 $params = ['return' => 'custom_' . $ids['custom_field_id'], 'id' => $result['id']];
1285 $check = $this->callAPIAndDocument($this->_entity, 'get', $params, __FUNCTION__, __FILE__, $description, $subfile);
1286
1287 $this->assertEquals("custom string", $check['values'][$check['id']]['custom_' . $ids['custom_field_id']]);
1288 $this->customFieldDelete($ids['custom_field_id']);
1289 $this->customGroupDelete($ids['custom_group_id']);
1290 }
1291
1292 /**
1293 * Check that address name, ID is returned if required.
1294 */
1295 public function testGetReturnAddress() {
1296 $contactID = $this->individualCreate();
1297 $result = $this->callAPISuccess('address', 'create', [
1298 'contact_id' => $contactID,
1299 'address_name' => 'My house',
1300 'location_type_id' => 'Home',
1301 'street_address' => '1 my road',
1302 ]);
1303 $addressID = $result['id'];
1304
1305 $result = $this->callAPISuccessGetSingle('contact', [
1306 'return' => 'address_name, street_address, address_id',
1307 'id' => $contactID,
1308 ]);
1309 $this->assertEquals($addressID, $result['address_id']);
1310 $this->assertEquals('1 my road', $result['street_address']);
1311 $this->assertEquals('My house', $result['address_name']);
1312
1313 }
1314
1315 /**
1316 * Test group filter syntaxes.
1317 */
1318 public function testGetGroupIDFromContact() {
1319 $groupId = $this->groupCreate();
1320 $params = [
1321 'email' => 'man2@yahoo.com',
1322 'contact_type' => 'Individual',
1323 'location_type_id' => 1,
1324 'api.group_contact.create' => ['group_id' => $groupId],
1325 ];
1326
1327 $this->callAPISuccess('contact', 'create', $params);
1328 // testing as integer
1329 $params = [
1330 'filter.group_id' => $groupId,
1331 'contact_type' => 'Individual',
1332 ];
1333 $result = $this->callAPISuccess('contact', 'get', $params);
1334 $this->assertEquals(1, $result['count']);
1335 // group 26 doesn't exist, but we can still search contacts in it.
1336 $params = [
1337 'filter.group_id' => 26,
1338 'contact_type' => 'Individual',
1339 ];
1340 $this->callAPISuccess('contact', 'get', $params);
1341 // testing as string
1342 $params = [
1343 'filter.group_id' => "$groupId, 26",
1344 'contact_type' => 'Individual',
1345 ];
1346 $result = $this->callAPISuccess('contact', 'get', $params);
1347 $this->assertEquals(1, $result['count']);
1348 $params = [
1349 'filter.group_id' => "26,27",
1350 'contact_type' => 'Individual',
1351 ];
1352 $this->callAPISuccess('contact', 'get', $params);
1353
1354 // testing as string
1355 $params = [
1356 'filter.group_id' => [$groupId, 26],
1357 'contact_type' => 'Individual',
1358 ];
1359 $result = $this->callAPISuccess('contact', 'get', $params);
1360 $this->assertEquals(1, $result['count']);
1361
1362 //test in conjunction with other criteria
1363 $params = [
1364 'filter.group_id' => [$groupId, 26],
1365 'contact_type' => 'Organization',
1366 ];
1367 $this->callAPISuccess('contact', 'get', $params);
1368 $params = [
1369 'filter.group_id' => [26, 27],
1370 'contact_type' => 'Individual',
1371 ];
1372 $result = $this->callAPISuccess('contact', 'get', $params);
1373 $this->assertEquals(0, $result['count']);
1374 }
1375
1376 /**
1377 * Verify that attempt to create individual contact with two chained websites succeeds.
1378 */
1379 public function testCreateIndividualWithContributionDottedSyntax() {
1380 $description = "This demonstrates the syntax to create 2 chained entities.";
1381 $subFile = "ChainTwoWebsites";
1382 $params = [
1383 'first_name' => 'abc3',
1384 'last_name' => 'xyz3',
1385 'contact_type' => 'Individual',
1386 'email' => 'man3@yahoo.com',
1387 'api.contribution.create' => [
1388 'receive_date' => '2010-01-01',
1389 'total_amount' => 100.00,
1390 'financial_type_id' => $this->_financialTypeId,
1391 'payment_instrument_id' => 1,
1392 'non_deductible_amount' => 10.00,
1393 'fee_amount' => 50.00,
1394 'net_amount' => 90.00,
1395 'trxn_id' => 15345,
1396 'invoice_id' => 67990,
1397 'source' => 'SSF',
1398 'contribution_status_id' => 1,
1399 'skipCleanMoney' => 1,
1400 ],
1401 'api.website.create' => [
1402 'url' => "http://civicrm.org",
1403 ],
1404 'api.website.create.2' => [
1405 'url' => "http://chained.org",
1406 ],
1407 ];
1408
1409 $result = $this->callAPIAndDocument('Contact', 'create', $params, __FUNCTION__, __FILE__, $description, $subFile);
1410
1411 // checking child function result not covered in callAPIAndDocument
1412 $this->assertAPISuccess($result['values'][$result['id']]['api.website.create']);
1413 $this->assertEquals("http://chained.org", $result['values'][$result['id']]['api.website.create.2']['values'][0]['url']);
1414 $this->assertEquals("http://civicrm.org", $result['values'][$result['id']]['api.website.create']['values'][0]['url']);
1415
1416 // delete the contact
1417 $this->callAPISuccess('contact', 'delete', $result);
1418 }
1419
1420 /**
1421 * Verify that attempt to create individual contact with chained contribution and website succeeds.
1422 */
1423 public function testCreateIndividualWithContributionChainedArrays() {
1424 $params = [
1425 'first_name' => 'abc3',
1426 'last_name' => 'xyz3',
1427 'contact_type' => 'Individual',
1428 'email' => 'man3@yahoo.com',
1429 'api.contribution.create' => [
1430 'receive_date' => '2010-01-01',
1431 'total_amount' => 100.00,
1432 'financial_type_id' => $this->_financialTypeId,
1433 'payment_instrument_id' => 1,
1434 'non_deductible_amount' => 10.00,
1435 'fee_amount' => 50.00,
1436 'net_amount' => 90.00,
1437 'trxn_id' => 12345,
1438 'invoice_id' => 67890,
1439 'source' => 'SSF',
1440 'contribution_status_id' => 1,
1441 'skipCleanMoney' => 1,
1442 ],
1443 'api.website.create' => [
1444 [
1445 'url' => "http://civicrm.org",
1446 ],
1447 [
1448 'url' => "http://chained.org",
1449 'website_type_id' => 2,
1450 ],
1451 ],
1452 ];
1453
1454 $description = "Demonstrates creating two websites as an array.";
1455 $subfile = "ChainTwoWebsitesSyntax2";
1456 $result = $this->callAPIAndDocument('Contact', 'create', $params, __FUNCTION__, __FILE__, $description, $subfile);
1457
1458 // the callAndDocument doesn't check the chained call
1459 $this->assertEquals(0, $result['values'][$result['id']]['api.website.create'][0]['is_error']);
1460 $this->assertEquals("http://chained.org", $result['values'][$result['id']]['api.website.create'][1]['values'][0]['url']);
1461 $this->assertEquals("http://civicrm.org", $result['values'][$result['id']]['api.website.create'][0]['values'][0]['url']);
1462
1463 $this->callAPISuccess('contact', 'delete', $result);
1464 }
1465
1466 /**
1467 * Test for direction when chaining relationships.
1468 *
1469 * https://issues.civicrm.org/jira/browse/CRM-16084
1470 * @param int $version
1471 *
1472 * @dataProvider versionThreeAndFour
1473 */
1474 public function testDirectionChainingRelationshipsCRM16084($version) {
1475 $this->_apiversion = $version;
1476 // Some contact, called Jules.
1477 $create_result_1 = $this->callAPISuccess('contact', 'create', [
1478 'first_name' => 'Jules',
1479 'last_name' => 'Smos',
1480 'contact_type' => 'Individual',
1481 ]);
1482
1483 // Another contact: Jos, child of Jules.
1484 $create_params = [
1485 'first_name' => 'Jos',
1486 'last_name' => 'Smos',
1487 'contact_type' => 'Individual',
1488 'api.relationship.create' => [
1489 [
1490 'contact_id_a' => '$value.id',
1491 'contact_id_b' => $create_result_1['id'],
1492 // child of
1493 'relationship_type_id' => 1,
1494 ],
1495 ],
1496 ];
1497 $create_result_2 = $this->callAPISuccess('contact', 'create', $create_params);
1498
1499 // Mia is the child of Jos.
1500 $create_params = [
1501 'first_name' => 'Mia',
1502 'last_name' => 'Smos',
1503 'contact_type' => 'Individual',
1504 'api.relationship.create' => [
1505 [
1506 'contact_id_a' => '$value.id',
1507 'contact_id_b' => $create_result_2['id'],
1508 // child of
1509 'relationship_type_id' => 1,
1510 ],
1511 ],
1512 ];
1513 $create_result_3 = $this->callAPISuccess('contact', 'create', $create_params);
1514
1515 // Get Jos and his children.
1516 $get_params = [
1517 'sequential' => 1,
1518 'id' => $create_result_2['id'],
1519 'api.relationship.get' => [
1520 'contact_id_b' => '$value.id',
1521 'relationship_type_id' => 1,
1522 ],
1523 ];
1524 $get_result = $this->callAPISuccess('contact', 'getsingle', $get_params);
1525
1526 // Clean up first.
1527 $this->callAPISuccess('contact', 'delete', [
1528 'id' => $create_result_1['id'],
1529 ]);
1530 $this->callAPISuccess('contact', 'delete', [
1531 'id' => $create_result_2['id'],
1532 ]);
1533
1534 // Assert.
1535 $this->assertEquals(1, $get_result['api.relationship.get']['count']);
1536 $this->assertEquals($create_result_3['id'], $get_result['api.relationship.get']['values'][0]['contact_id_a']);
1537 }
1538
1539 /**
1540 * Verify that attempt to create individual contact with first, and last names and email succeeds.
1541 */
1542 public function testCreateIndividualWithNameEmail() {
1543 $params = [
1544 'first_name' => 'abc3',
1545 'last_name' => 'xyz3',
1546 'contact_type' => 'Individual',
1547 'email' => 'man3@yahoo.com',
1548 ];
1549
1550 $contact = $this->callAPISuccess('contact', 'create', $params);
1551
1552 $this->callAPISuccess('contact', 'delete', $contact);
1553 }
1554
1555 /**
1556 * Verify that attempt to create individual contact with no data fails.
1557 */
1558 public function testCreateIndividualWithOutNameEmail() {
1559 $params = [
1560 'contact_type' => 'Individual',
1561 ];
1562 $this->callAPIFailure('contact', 'create', $params);
1563 }
1564
1565 /**
1566 * Test create individual contact with first &last names, email and location type succeeds.
1567 */
1568 public function testCreateIndividualWithNameEmailLocationType() {
1569 $params = [
1570 'first_name' => 'abc4',
1571 'last_name' => 'xyz4',
1572 'email' => 'man4@yahoo.com',
1573 'contact_type' => 'Individual',
1574 'location_type_id' => 1,
1575 ];
1576 $result = $this->callAPISuccess('contact', 'create', $params);
1577
1578 $this->callAPISuccess('contact', 'delete', ['id' => $result['id']]);
1579 }
1580
1581 /**
1582 * Verify that when changing employers the old employer relationship becomes inactive.
1583 */
1584 public function testCreateIndividualWithEmployer() {
1585 $employer = $this->organizationCreate();
1586 $employer2 = $this->organizationCreate();
1587
1588 $params = [
1589 'email' => 'man4@yahoo.com',
1590 'contact_type' => 'Individual',
1591 'employer_id' => $employer,
1592 ];
1593
1594 $result = $this->callAPISuccess('contact', 'create', $params);
1595 $relationships = $this->callAPISuccess('relationship', 'get', [
1596 'contact_id_a' => $result['id'],
1597 'sequential' => 1,
1598 ]);
1599
1600 $this->assertEquals($employer, $relationships['values'][0]['contact_id_b']);
1601
1602 // Add more random relationships to make the test more realistic
1603 foreach (['Employee of', 'Volunteer for'] as $relationshipType) {
1604 $relTypeId = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType', $relationshipType, 'id', 'name_a_b');
1605 $this->callAPISuccess('relationship', 'create', [
1606 'contact_id_a' => $result['id'],
1607 'contact_id_b' => $this->organizationCreate(),
1608 'is_active' => 1,
1609 'relationship_type_id' => $relTypeId,
1610 ]);
1611 }
1612
1613 // Add second employer
1614 $params['employer_id'] = $employer2;
1615 $params['id'] = $result['id'];
1616 $result = $this->callAPISuccess('contact', 'create', $params);
1617
1618 $relationships = $this->callAPISuccess('relationship', 'get', [
1619 'contact_id_a' => $result['id'],
1620 'sequential' => 1,
1621 'is_active' => 0,
1622 ]);
1623
1624 $this->assertEquals($employer, $relationships['values'][0]['contact_id_b']);
1625 }
1626
1627 /**
1628 * Verify that attempt to create household contact with details succeeds.
1629 */
1630 public function testCreateHouseholdDetails() {
1631 $params = [
1632 'household_name' => 'abc8\'s House',
1633 'nick_name' => 'x House',
1634 'email' => 'man8@yahoo.com',
1635 'contact_type' => 'Household',
1636 ];
1637
1638 $contact = $this->callAPISuccess('contact', 'create', $params);
1639
1640 $this->callAPISuccess('contact', 'delete', $contact);
1641 }
1642
1643 /**
1644 * Verify that attempt to create household contact with inadequate details fails.
1645 */
1646 public function testCreateHouseholdInadequateDetails() {
1647 $params = [
1648 'nick_name' => 'x House',
1649 'email' => 'man8@yahoo.com',
1650 'contact_type' => 'Household',
1651 ];
1652 $this->callAPIFailure('contact', 'create', $params);
1653 }
1654
1655 /**
1656 * Verify successful update of individual contact.
1657 */
1658 public function testUpdateIndividualWithAll() {
1659 $contactID = $this->individualCreate();
1660
1661 $params = [
1662 'id' => $contactID,
1663 'first_name' => 'abcd',
1664 'contact_type' => 'Individual',
1665 'nick_name' => 'This is nickname first',
1666 'do_not_email' => '1',
1667 'do_not_phone' => '1',
1668 'do_not_mail' => '1',
1669 'do_not_trade' => '1',
1670 'legal_identifier' => 'ABC23853ZZ2235',
1671 'external_identifier' => '1928837465',
1672 'image_URL' => 'http://some.url.com/image.jpg',
1673 'home_url' => 'http://www.example.org',
1674 ];
1675
1676 $this->callAPISuccess('Contact', 'Update', $params);
1677 $getResult = $this->callAPISuccess('Contact', 'Get', $params);
1678 unset($params['contact_id']);
1679 //Todo - neither API v2 or V3 are testing for home_url - not sure if it is being set.
1680 //reducing this test partially back to api v2 level to get it through
1681 unset($params['home_url']);
1682 foreach ($params as $key => $value) {
1683 $this->assertEquals($value, $getResult['values'][$contactID][$key]);
1684 }
1685 }
1686
1687 /**
1688 * Verify successful update of organization contact.
1689 *
1690 * @throws \Exception
1691 */
1692 public function testUpdateOrganizationWithAll() {
1693 $contactID = $this->organizationCreate();
1694
1695 $params = [
1696 'id' => $contactID,
1697 'organization_name' => 'WebAccess India Pvt Ltd',
1698 'legal_name' => 'WebAccess',
1699 'sic_code' => 'ABC12DEF',
1700 'contact_type' => 'Organization',
1701 ];
1702
1703 $this->callAPISuccess('Contact', 'Update', $params);
1704 $this->getAndCheck($params, $contactID, 'Contact');
1705 }
1706
1707 /**
1708 * Test merging 2 organizations.
1709 *
1710 * CRM-20421: This test make sure that inherited memberships are deleted upon merging organization.
1711 */
1712 public function testMergeOrganizations() {
1713 $organizationID1 = $this->organizationCreate([], 0);
1714 $organizationID2 = $this->organizationCreate([], 1);
1715 $contact = $this->callAPISuccess('contact', 'create', array_merge($this->_params, [
1716 'employer_id' => $organizationID1,
1717 ]));
1718 $contact = $contact["values"][$contact["id"]];
1719
1720 $membershipType = $this->createEmployerOfMembership();
1721 $membershipParams = [
1722 'membership_type_id' => $membershipType["id"],
1723 'contact_id' => $organizationID1,
1724 'start_date' => "01/01/2015",
1725 'join_date' => "01/01/2010",
1726 'end_date' => "12/31/2015",
1727 ];
1728 $ownermembershipid = $this->contactMembershipCreate($membershipParams);
1729
1730 $contactmembership = $this->callAPISuccess("membership", "getsingle", [
1731 "contact_id" => $contact["id"],
1732 ]);
1733
1734 $this->assertEquals($ownermembershipid, $contactmembership["owner_membership_id"], "Contact membership must be inherited from Organization");
1735
1736 CRM_Dedupe_Merger::moveAllBelongings($organizationID2, $organizationID1, [
1737 "move_rel_table_memberships" => "0",
1738 "move_rel_table_relationships" => "1",
1739 "main_details" => [
1740 "contact_id" => $organizationID2,
1741 "contact_type" => "Organization",
1742 ],
1743 "other_details" => [
1744 "contact_id" => $organizationID1,
1745 "contact_type" => "Organization",
1746 ],
1747 ]);
1748
1749 $contactmembership = $this->callAPISuccess("membership", "get", [
1750 "contact_id" => $contact["id"],
1751 ]);
1752
1753 $this->assertEquals(0, $contactmembership["count"], "Contact membership must be deleted after merging organization without memberships.");
1754 }
1755
1756 /**
1757 * Test the function that determines if 2 contacts have conflicts.
1758 *
1759 * @throws \CRM_Core_Exception
1760 */
1761 public function testMergeGetConflicts() {
1762 list($contact1, $contact2) = $this->createDeeplyConflictedContacts();
1763 $conflicts = $this->callAPISuccess('Contact', 'get_merge_conflicts', ['to_keep_id' => $contact1, 'to_remove_id' => $contact2])['values'];
1764 $this->assertEquals([
1765 'safe' => [
1766 'conflicts' => [
1767 'contact' => [
1768 'first_name' => [$contact1 => 'Anthony', $contact2 => 'different', 'title' => 'First Name'],
1769 'external_identifier' => [$contact1 => 'unique and special', $contact2 => 'uniquer and specialler', 'title' => 'External Identifier'],
1770 $this->getCustomFieldName('text') => [$contact1 => 'mummy loves me', $contact2 => 'mummy loves me more', 'title' => 'Enter text here'],
1771 ],
1772 'address' => [
1773 [
1774 'location_type_id' => '1',
1775 'title' => 'Address 1 (Home)',
1776 'street_address' => [
1777 $contact1 => 'big house',
1778 $contact2 => 'medium house',
1779 ],
1780 'display' => [
1781 $contact1 => "big house\nsmall city, \n",
1782 $contact2 => "medium house\nsmall city, \n",
1783 ],
1784 ],
1785 [
1786 'location_type_id' => '2',
1787 'street_address' => [
1788 $contact1 => 'big office',
1789 $contact2 => 'medium office',
1790 ],
1791 'title' => 'Address 2 (Work)',
1792 'display' => [
1793 $contact1 => "big office\nsmall city, \n",
1794 $contact2 => "medium office\nsmall city, \n",
1795 ],
1796 ],
1797 ],
1798 'email' => [
1799 [
1800 'location_type_id' => '1',
1801 'email' => [
1802 $contact1 => 'bob@example.com',
1803 $contact2 => 'anthony_anderson@civicrm.org',
1804 ],
1805 'title' => 'Email 1 (Home)',
1806 'display' => [
1807 $contact1 => 'bob@example.com',
1808 $contact2 => 'anthony_anderson@civicrm.org',
1809 ],
1810 ],
1811 ],
1812 ],
1813 'resolved' => [],
1814 ],
1815 ], $conflicts);
1816
1817 $this->callAPISuccess('Job', 'process_batch_merge');
1818 $defaultRuleGroupID = $this->callAPISuccessGetValue('RuleGroup', [
1819 'contact_type' => 'Individual',
1820 'used' => 'Unsupervised',
1821 'return' => 'id',
1822 'options' => ['limit' => 1],
1823 ]);
1824
1825 $duplicates = $this->callAPISuccess('Dedupe', 'getduplicates', ['rule_group_id' => $defaultRuleGroupID]);
1826 $this->assertEquals($conflicts['safe']['conflicts'], $duplicates['values'][0]['safe']['conflicts']);
1827 }
1828
1829 /**
1830 *
1831 * @throws \CRM_Core_Exception
1832 */
1833 public function testGetConflictsAggressiveMode() {
1834 list($contact1, $contact2) = $this->createDeeplyConflictedContacts();
1835 $conflicts = $this->callAPISuccess('Contact', 'get_merge_conflicts', ['to_keep_id' => $contact1, 'to_remove_id' => $contact2, 'mode' => ['safe', 'aggressive']])['values'];
1836 $this->assertEquals([
1837 'contact' => [
1838 'external_identifier' => 'uniquer and specialler',
1839 'first_name' => 'different',
1840 'custom_1' => 'mummy loves me more',
1841 ],
1842 ], $conflicts['aggressive']['resolved']);
1843 }
1844
1845 /**
1846 * Create inherited membership type for employer relationship.
1847 *
1848 * @return int
1849 *
1850 * @throws \CRM_Core_Exception
1851 */
1852 private function createEmployerOfMembership() {
1853 $params = [
1854 'domain_id' => CRM_Core_Config::domainID(),
1855 'name' => 'Organization Membership',
1856 'member_of_contact_id' => 1,
1857 'financial_type_id' => 1,
1858 'minimum_fee' => 10,
1859 'duration_unit' => 'year',
1860 'duration_interval' => 1,
1861 'period_type' => 'rolling',
1862 'relationship_type_id' => 5,
1863 'relationship_direction' => 'b_a',
1864 'visibility' => 'Public',
1865 'is_active' => 1,
1866 ];
1867 $membershipType = $this->callAPISuccess('membership_type', 'create', $params);
1868 return $membershipType['values'][$membershipType['id']];
1869 }
1870
1871 /**
1872 * Verify successful update of household contact.
1873 *
1874 * @param int $version
1875 *
1876 * @dataProvider versionThreeAndFour
1877 */
1878 public function testUpdateHouseholdWithAll($version) {
1879 $this->_apiversion = $version;
1880 $contactID = $this->householdCreate();
1881
1882 $params = [
1883 'id' => $contactID,
1884 'household_name' => 'ABC household',
1885 'nick_name' => 'ABC House',
1886 'contact_type' => 'Household',
1887 ];
1888
1889 $result = $this->callAPISuccess('Contact', 'Update', $params);
1890
1891 $expected = [
1892 'contact_type' => 'Household',
1893 'is_opt_out' => 0,
1894 'sort_name' => 'ABC household',
1895 'display_name' => 'ABC household',
1896 'nick_name' => 'ABC House',
1897 ];
1898 $this->getAndCheck($expected, $result['id'], 'contact');
1899 }
1900
1901 /**
1902 * Test civicrm_update() without contact type.
1903 *
1904 * Deliberately exclude contact_type as it should still cope using civicrm_api.
1905 *
1906 * CRM-7645.
1907 *
1908 * @param int $version
1909 *
1910 * @dataProvider versionThreeAndFour
1911 */
1912 public function testUpdateCreateWithID($version) {
1913 $this->_apiversion = $version;
1914 $contactID = $this->individualCreate();
1915 $this->callAPISuccess('Contact', 'Update', [
1916 'id' => $contactID,
1917 'first_name' => 'abcd',
1918 'last_name' => 'wxyz',
1919 ]);
1920 }
1921
1922 /**
1923 * Test civicrm_contact_delete() with no contact ID.
1924 *
1925 * @param int $version
1926 *
1927 * @dataProvider versionThreeAndFour
1928 */
1929 public function testContactDeleteNoID($version) {
1930 $this->_apiversion = $version;
1931 $params = [
1932 'foo' => 'bar',
1933 ];
1934 $this->callAPIFailure('contact', 'delete', $params);
1935 }
1936
1937 /**
1938 * Test civicrm_contact_delete() with error.
1939 *
1940 * @param int $version
1941 *
1942 * @dataProvider versionThreeAndFour
1943 */
1944 public function testContactDeleteError($version) {
1945 $this->_apiversion = $version;
1946 $params = ['contact_id' => 999];
1947 $this->callAPIFailure('contact', 'delete', $params);
1948 }
1949
1950 /**
1951 * Test civicrm_contact_delete().
1952 *
1953 * @param int $version
1954 *
1955 * @dataProvider versionThreeAndFour
1956 */
1957 public function testContactDelete($version) {
1958 $this->_apiversion = $version;
1959 $contactID = $this->individualCreate();
1960 $params = [
1961 'id' => $contactID,
1962 ];
1963 $this->callAPIAndDocument('contact', 'delete', $params, __FUNCTION__, __FILE__);
1964 }
1965
1966 /**
1967 * Test civicrm_contact_get() return only first name.
1968 *
1969 * @param int $version
1970 *
1971 * @dataProvider versionThreeAndFour
1972 */
1973 public function testContactGetRetFirst($version) {
1974 $this->_apiversion = $version;
1975 $contact = $this->callAPISuccess('contact', 'create', $this->_params);
1976 $params = [
1977 'contact_id' => $contact['id'],
1978 'return_first_name' => TRUE,
1979 'sort' => 'first_name',
1980 ];
1981 $result = $this->callAPISuccess('contact', 'get', $params);
1982 $this->assertEquals(1, $result['count']);
1983 $this->assertEquals($contact['id'], $result['id']);
1984 $this->assertEquals('abc1', $result['values'][$contact['id']]['first_name']);
1985 }
1986
1987 /**
1988 * Test civicrm_contact_get() return only first name & last name.
1989 *
1990 * Use comma separated string return with a space.
1991 *
1992 * @param int $version
1993 *
1994 * @dataProvider versionThreeAndFour
1995 */
1996 public function testContactGetReturnFirstLast($version) {
1997 $this->_apiversion = $version;
1998 $contact = $this->callAPISuccess('contact', 'create', $this->_params);
1999 $params = [
2000 'contact_id' => $contact['id'],
2001 'return' => 'first_name, last_name',
2002 ];
2003 $result = $this->callAPISuccess('contact', 'getsingle', $params);
2004 $this->assertEquals('abc1', $result['first_name']);
2005 $this->assertEquals('xyz1', $result['last_name']);
2006 //check that other defaults not returns
2007 $this->assertArrayNotHasKey('sort_name', $result);
2008 $params = [
2009 'contact_id' => $contact['id'],
2010 'return' => 'first_name,last_name',
2011 ];
2012 $result = $this->callAPISuccess('contact', 'getsingle', $params);
2013 $this->assertEquals('abc1', $result['first_name']);
2014 $this->assertEquals('xyz1', $result['last_name']);
2015 //check that other defaults not returns
2016 $this->assertArrayNotHasKey('sort_name', $result);
2017 }
2018
2019 /**
2020 * Test civicrm_contact_get() return only first name & last name.
2021 *
2022 * Use comma separated string return without a space
2023 *
2024 * @param int $version
2025 *
2026 * @dataProvider versionThreeAndFour
2027 */
2028 public function testContactGetReturnFirstLastNoComma($version) {
2029 $this->_apiversion = $version;
2030 $contact = $this->callAPISuccess('contact', 'create', $this->_params);
2031 $params = [
2032 'contact_id' => $contact['id'],
2033 'return' => 'first_name,last_name',
2034 ];
2035 $result = $this->callAPISuccess('contact', 'getsingle', $params);
2036 $this->assertEquals('abc1', $result['first_name']);
2037 $this->assertEquals('xyz1', $result['last_name']);
2038 //check that other defaults not returns
2039 $this->assertArrayNotHasKey('sort_name', $result);
2040 }
2041
2042 /**
2043 * Test civicrm_contact_get() with default return properties.
2044 */
2045 public function testContactGetRetDefault() {
2046 $contactID = $this->individualCreate();
2047 $params = [
2048 'contact_id' => $contactID,
2049 'sort' => 'first_name',
2050 ];
2051 $result = $this->callAPISuccess('contact', 'get', $params);
2052 $this->assertEquals($contactID, $result['values'][$contactID]['contact_id']);
2053 $this->assertEquals('Anthony', $result['values'][$contactID]['first_name']);
2054 }
2055
2056 /**
2057 * Test civicrm_contact_getquick() with empty name param.
2058 */
2059 public function testContactGetQuick() {
2060 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact', 'email' => 'TestContact@example.com']);
2061
2062 $result = $this->callAPISuccess('contact', 'getquick', ['name' => 'T']);
2063 $this->assertEquals($contactID, $result['values'][0]['id']);
2064 $params = [
2065 'name' => "TestContact@example.com",
2066 'field_name' => 'sort_name',
2067 ];
2068 $result = $this->callAPISuccess('contact', 'getquick', $params);
2069 $this->assertEquals($contactID, $result['values'][0]['id']);
2070 }
2071
2072 /**
2073 * Test civicrm_contact_get) with empty params.
2074 *
2075 * @param int $version
2076 *
2077 * @dataProvider versionThreeAndFour
2078 */
2079 public function testContactGetEmptyParams($version) {
2080 $this->_apiversion = $version;
2081 $this->callAPISuccess('contact', 'get', []);
2082 }
2083
2084 /**
2085 * Test civicrm_contact_get(,true) with no matches.
2086 *
2087 * @param int $version
2088 *
2089 * @dataProvider versionThreeAndFour
2090 */
2091 public function testContactGetOldParamsNoMatches($version) {
2092 $this->_apiversion = $version;
2093 $this->individualCreate();
2094 $result = $this->callAPISuccess('contact', 'get', ['first_name' => 'Fred']);
2095 $this->assertEquals(0, $result['count']);
2096 }
2097
2098 /**
2099 * Test civicrm_contact_get(,true) with one match.
2100 */
2101 public function testContactGetOldParamsOneMatch() {
2102 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2103
2104 $result = $this->callAPISuccess('contact', 'get', ['first_name' => 'Test']);
2105 $this->assertEquals($contactID, $result['values'][$contactID]['contact_id']);
2106 $this->assertEquals($contactID, $result['id']);
2107 }
2108
2109 /**
2110 * Test civicrm_contact_get(,true) with space in sort_name.
2111 */
2112 public function testContactGetSpaceMatches() {
2113 $contactParams_1 = [
2114 'first_name' => 'Sanford',
2115 'last_name' => 'Blackwell',
2116 'sort_name' => 'Blackwell, Sanford',
2117 'contact_type' => 'Individual',
2118 ];
2119 $this->individualCreate($contactParams_1);
2120
2121 $contactParams_2 = [
2122 'household_name' => 'Blackwell family',
2123 'sort_name' => 'Blackwell family',
2124 'contact_type' => 'Household',
2125 ];
2126 $this->individualCreate($contactParams_2);
2127
2128 $result = $this->callAPISuccess('contact', 'get', ['sort_name' => 'Blackwell F']);
2129 $this->assertEquals(1, $result['count']);
2130 }
2131
2132 /**
2133 * Test civicrm_contact_search_count().
2134 */
2135 public function testContactGetEmail() {
2136 $params = [
2137 'email' => 'man2@yahoo.com',
2138 'contact_type' => 'Individual',
2139 'location_type_id' => 1,
2140 ];
2141
2142 $contact = $this->callAPISuccess('contact', 'create', $params);
2143
2144 $params = [
2145 'email' => 'man2@yahoo.com',
2146 ];
2147 $result = $this->callAPIAndDocument('contact', 'get', $params, __FUNCTION__, __FILE__);
2148 $this->assertEquals('man2@yahoo.com', $result['values'][$result['id']]['email']);
2149
2150 $this->callAPISuccess('contact', 'delete', $contact);
2151 }
2152
2153 /**
2154 * Ensure consistent return format for option group fields.
2155 *
2156 * @param int $version
2157 *
2158 * @dataProvider versionThreeAndFour
2159 */
2160 public function testSetPreferredCommunicationNull($version) {
2161 $this->_apiversion = $version;
2162 $contact = $this->callAPISuccess('contact', 'create', array_merge($this->_params, [
2163 'preferred_communication_method' => ['Phone', 'SMS'],
2164 ]));
2165 $preferredCommunicationMethod = $this->callAPISuccessGetValue('Contact', [
2166 'id' => $contact['id'],
2167 'return' => 'preferred_communication_method',
2168 ]);
2169 $this->assertNotEmpty($preferredCommunicationMethod);
2170 $contact = $this->callAPISuccess('contact', 'create', array_merge($this->_params, [
2171 'preferred_communication_method' => 'null',
2172 'id' => $contact['id'],
2173 ]));
2174 $preferredCommunicationMethod = $this->callAPISuccessGetValue('Contact', [
2175 'id' => $contact['id'],
2176 'return' => 'preferred_communication_method',
2177 ]);
2178 $this->assertEmpty($preferredCommunicationMethod);
2179 }
2180
2181 /**
2182 * Ensure consistent return format for option group fields.
2183 *
2184 * @throws \CRM_Core_Exception
2185 */
2186 public function testPseudoFields() {
2187 $params = [
2188 'preferred_communication_method' => ['Phone', 'SMS'],
2189 'preferred_language' => 'en_US',
2190 'gender_id' => 'Female',
2191 'prefix_id' => 'Mrs.',
2192 'suffix_id' => 'II',
2193 'communication_style_id' => 'Formal',
2194 ];
2195
2196 $contact = $this->callAPISuccess('contact', 'create', array_merge($this->_params, $params));
2197
2198 $result = $this->callAPISuccess('contact', 'getsingle', ['id' => $contact['id']]);
2199 $this->assertEquals('Both', $result['preferred_mail_format']);
2200
2201 $this->assertEquals('en_US', $result['preferred_language']);
2202 $this->assertEquals(1, $result['communication_style_id']);
2203 $this->assertEquals(1, $result['gender_id']);
2204 $this->assertEquals('Female', $result['gender']);
2205 $this->assertEquals('Mrs.', $result['individual_prefix']);
2206 $this->assertEquals(1, $result['prefix_id']);
2207 $this->assertEquals('II', $result['individual_suffix']);
2208 $this->assertEquals(CRM_Core_PseudoConstant::getKey("CRM_Contact_BAO_Contact", 'suffix_id', 'II'), $result['suffix_id']);
2209 $this->callAPISuccess('contact', 'delete', $contact);
2210 $this->assertEquals([
2211 CRM_Core_PseudoConstant::getKey("CRM_Contact_BAO_Contact", 'preferred_communication_method', 'Phone'),
2212 CRM_Core_PseudoConstant::getKey("CRM_Contact_BAO_Contact", 'preferred_communication_method', 'SMS'),
2213 ], $result['preferred_communication_method']);
2214 }
2215
2216 /**
2217 * Test birth date parameters.
2218 *
2219 * These include value, array & birth_date_high, birth_date_low
2220 * && deceased.
2221 *
2222 * @throws \CRM_Core_Exception
2223 */
2224 public function testContactGetBirthDate() {
2225 $contact1 = $this->callAPISuccess('contact', 'create', array_merge($this->_params, ['birth_date' => 'first day of next month - 2 years']));
2226 $contact2 = $this->callAPISuccess('contact', 'create', array_merge($this->_params, ['birth_date' => 'first day of next month - 5 years']));
2227 $contact3 = $this->callAPISuccess('contact', 'create', array_merge($this->_params, ['birth_date' => 'first day of next month -20 years']));
2228
2229 $result = $this->callAPISuccess('contact', 'get', []);
2230 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -2 years')), $result['values'][$contact1['id']]['birth_date']);
2231 $result = $this->callAPISuccess('contact', 'get', ['birth_date' => 'first day of next month -5 years']);
2232 $this->assertEquals(1, $result['count']);
2233 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['birth_date']);
2234 $result = $this->callAPISuccess('contact', 'get', ['birth_date_high' => date('Y-m-d', strtotime('-6 years'))]);
2235 $this->assertEquals(1, $result['count']);
2236 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -20 years')), $result['values'][$contact3['id']]['birth_date']);
2237 $result = $this->callAPISuccess('contact', 'get', [
2238 'birth_date_low' => date('Y-m-d', strtotime('-6 years')),
2239 'birth_date_high' => date('Y-m-d', strtotime('- 3 years')),
2240 ]);
2241 $this->assertEquals(1, $result['count']);
2242 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['birth_date']);
2243 $result = $this->callAPISuccess('contact', 'get', [
2244 'birth_date_low' => '-6 years',
2245 'birth_date_high' => '- 3 years',
2246 ]);
2247 $this->assertEquals(1, $result['count']);
2248 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['birth_date']);
2249 }
2250
2251 /**
2252 * Test Address parameters
2253 *
2254 * This include state_province, state_province_name, country
2255 */
2256 public function testContactGetWithAddressFields() {
2257 $individuals = [
2258 [
2259 'first_name' => 'abc1',
2260 'contact_type' => 'Individual',
2261 'last_name' => 'xyz1',
2262 'api.address.create' => [
2263 'country' => 'United States',
2264 'state_province_id' => 'Michigan',
2265 'location_type_id' => 1,
2266 ],
2267 ],
2268 [
2269 'first_name' => 'abc2',
2270 'contact_type' => 'Individual',
2271 'last_name' => 'xyz2',
2272 'api.address.create' => [
2273 'country' => 'United States',
2274 'state_province_id' => 'Alabama',
2275 'location_type_id' => 1,
2276 ],
2277 ],
2278 ];
2279 foreach ($individuals as $params) {
2280 $contact = $this->callAPISuccess('contact', 'create', $params);
2281 }
2282
2283 // Check whether Contact get API return successfully with below Address params.
2284 $fieldsToTest = [
2285 'state_province_name' => 'Michigan',
2286 'state_province' => 'Michigan',
2287 'country' => 'United States',
2288 'state_province_name' => ['IN' => ['Michigan', 'Alabama']],
2289 'state_province' => ['IN' => ['Michigan', 'Alabama']],
2290 ];
2291 foreach ($fieldsToTest as $field => $value) {
2292 $getParams = [
2293 'id' => $contact['id'],
2294 $field => $value,
2295 ];
2296 $result = $this->callAPISuccess('Contact', 'get', $getParams);
2297 $this->assertEquals(1, $result['count']);
2298 }
2299 }
2300
2301 /**
2302 * Test Deceased date parameters.
2303 *
2304 * These include value, array & Deceased_date_high, Deceased date_low
2305 * && deceased.
2306 */
2307 public function testContactGetDeceasedDate() {
2308 $contact1 = $this->callAPISuccess('contact', 'create', array_merge($this->_params, ['deceased_date' => 'first day of next month - 2 years']));
2309 $contact2 = $this->callAPISuccess('contact', 'create', array_merge($this->_params, ['deceased_date' => 'first day of next month - 5 years']));
2310 $contact3 = $this->callAPISuccess('contact', 'create', array_merge($this->_params, ['deceased_date' => 'first day of next month -20 years']));
2311
2312 $result = $this->callAPISuccess('contact', 'get', []);
2313 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -2 years')), $result['values'][$contact1['id']]['deceased_date']);
2314 $result = $this->callAPISuccess('contact', 'get', ['deceased_date' => 'first day of next month -5 years']);
2315 $this->assertEquals(1, $result['count']);
2316 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['deceased_date']);
2317 $result = $this->callAPISuccess('contact', 'get', ['deceased_date_high' => date('Y-m-d', strtotime('-6 years'))]);
2318 $this->assertEquals(1, $result['count']);
2319 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -20 years')), $result['values'][$contact3['id']]['deceased_date']);
2320 $result = $this->callAPISuccess('contact', 'get', [
2321 'deceased_date_low' => '-6 years',
2322 'deceased_date_high' => date('Y-m-d', strtotime('- 3 years')),
2323 ]);
2324 $this->assertEquals(1, $result['count']);
2325 $this->assertEquals(date('Y-m-d', strtotime('first day of next month -5 years')), $result['values'][$contact2['id']]['deceased_date']);
2326 }
2327
2328 /**
2329 * Test for Contact.get id=@user:username.
2330 */
2331 public function testContactGetByUsername() {
2332 // Setup - create contact with a uf-match.
2333 $cid = $this->individualCreate([
2334 'contact_type' => 'Individual',
2335 'first_name' => 'testGetByUsername',
2336 'last_name' => 'testGetByUsername',
2337 ]);
2338
2339 $ufMatchParams = [
2340 'domain_id' => CRM_Core_Config::domainID(),
2341 'uf_id' => 99,
2342 'uf_name' => 'the-email-matching-key-is-not-really-the-username',
2343 'contact_id' => $cid,
2344 ];
2345 $ufMatch = CRM_Core_BAO_UFMatch::create($ufMatchParams);
2346 $this->assertTrue(is_numeric($ufMatch->id));
2347
2348 // setup - mock the calls to CRM_Utils_System_*::getUfId
2349 $mockFunction = $this->mockMethod;
2350 $userSystem = $this->$mockFunction('CRM_Utils_System_UnitTests', ['getUfId']);
2351 $userSystem->expects($this->once())
2352 ->method('getUfId')
2353 ->with($this->equalTo('exampleUser'))
2354 ->will($this->returnValue(99));
2355 CRM_Core_Config::singleton()->userSystem = $userSystem;
2356
2357 // perform a lookup
2358 $result = $this->callAPISuccess('Contact', 'get', [
2359 'id' => '@user:exampleUser',
2360 ]);
2361 $this->assertEquals('testGetByUsername', $result['values'][$cid]['first_name']);
2362
2363 // Check search of contacts with & without uf records
2364 $result = $this->callAPISuccess('Contact', 'get', ['uf_user' => 1]);
2365 $this->assertArrayHasKey($cid, $result['values']);
2366
2367 $result = $this->callAPISuccess('Contact', 'get', ['uf_user' => 0]);
2368 $this->assertArrayNotHasKey($cid, $result['values']);
2369 }
2370
2371 /**
2372 * Test to check return works OK.
2373 */
2374 public function testContactGetReturnValues() {
2375 $extraParams = [
2376 'nick_name' => 'Bob',
2377 'phone' => '456',
2378 'email' => 'e@mail.com',
2379 ];
2380 $contactID = $this->individualCreate($extraParams);
2381 //actually it turns out the above doesn't create a phone
2382 $this->callAPISuccess('phone', 'create', ['contact_id' => $contactID, 'phone' => '456']);
2383 $result = $this->callAPISuccess('contact', 'getsingle', ['id' => $contactID]);
2384 foreach ($extraParams as $key => $value) {
2385 $this->assertEquals($result[$key], $value);
2386 }
2387 //now we check they are still returned with 'return' key
2388 $result = $this->callAPISuccess('contact', 'getsingle', [
2389 'id' => $contactID,
2390 'return' => array_keys($extraParams),
2391 ]);
2392 foreach ($extraParams as $key => $value) {
2393 $this->assertEquals($result[$key], $value);
2394 }
2395 }
2396
2397 /**
2398 * Test creating multiple phones using chaining.
2399 *
2400 * @param int $version
2401 *
2402 * @dataProvider versionThreeAndFour
2403 * @throws \Exception
2404 */
2405 public function testCRM13252MultipleChainedPhones($version) {
2406 $this->_apiversion = $version;
2407 $contactID = $this->householdCreate();
2408 $this->callAPISuccessGetCount('phone', ['contact_id' => $contactID], 0);
2409 $params = [
2410 'contact_id' => $contactID,
2411 'household_name' => 'Household 1',
2412 'contact_type' => 'Household',
2413 'api.phone.create' => [
2414 0 => [
2415 'phone' => '111-111-1111',
2416 'location_type_id' => 1,
2417 'phone_type_id' => 1,
2418 ],
2419 1 => [
2420 'phone' => '222-222-2222',
2421 'location_type_id' => 1,
2422 'phone_type_id' => 2,
2423 ],
2424 ],
2425 ];
2426 $this->callAPISuccess('contact', 'create', $params);
2427 $this->callAPISuccessGetCount('phone', ['contact_id' => $contactID], 2);
2428
2429 }
2430
2431 /**
2432 * Test for Contact.get id=@user:username (with an invalid username).
2433 */
2434 public function testContactGetByUnknownUsername() {
2435 // setup - mock the calls to CRM_Utils_System_*::getUfId
2436 $mockFunction = $this->mockMethod;
2437 $userSystem = $this->$mockFunction('CRM_Utils_System_UnitTests', ['getUfId']);
2438 $userSystem->expects($this->once())
2439 ->method('getUfId')
2440 ->with($this->equalTo('exampleUser'))
2441 ->will($this->returnValue(NULL));
2442 CRM_Core_Config::singleton()->userSystem = $userSystem;
2443
2444 // perform a lookup
2445 $result = $this->callAPIFailure('Contact', 'get', [
2446 'id' => '@user:exampleUser',
2447 ]);
2448 $this->assertRegExp('/cannot be resolved to a contact ID/', $result['error_message']);
2449 }
2450
2451 /**
2452 * Verify attempt to create individual with chained arrays and sequential.
2453 *
2454 * @param int $version
2455 *
2456 * @dataProvider versionThreeAndFour
2457 */
2458 public function testGetIndividualWithChainedArraysAndSequential($version) {
2459 $this->_apiversion = $version;
2460 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
2461 $params['custom_' . $ids['custom_field_id']] = "custom string";
2462
2463 $moreIDs = $this->CustomGroupMultipleCreateWithFields();
2464 $params = [
2465 'sequential' => 1,
2466 'first_name' => 'abc3',
2467 'last_name' => 'xyz3',
2468 'contact_type' => 'Individual',
2469 'email' => 'man3@yahoo.com',
2470 'api.website.create' => [
2471 [
2472 'url' => "http://civicrm.org",
2473 ],
2474 [
2475 'url' => "https://civicrm.org",
2476 ],
2477 ],
2478 ];
2479
2480 $result = $this->callAPISuccess('Contact', 'create', $params);
2481
2482 // delete the contact and custom groups
2483 $this->callAPISuccess('contact', 'delete', ['id' => $result['id']]);
2484 $this->customGroupDelete($ids['custom_group_id']);
2485 $this->customGroupDelete($moreIDs['custom_group_id']);
2486
2487 $this->assertEquals($result['id'], $result['values'][0]['id']);
2488 $this->assertArrayKeyExists('api.website.create', $result['values'][0]);
2489 }
2490
2491 /**
2492 * Verify attempt to create individual with chained arrays.
2493 */
2494 public function testGetIndividualWithChainedArrays() {
2495 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
2496 $params['custom_' . $ids['custom_field_id']] = "custom string";
2497
2498 $moreIDs = $this->CustomGroupMultipleCreateWithFields();
2499 $description = "This demonstrates the usage of chained api functions.\nIn this case no notes or custom fields have been created.";
2500 $subfile = "APIChainedArray";
2501 $params = [
2502 'first_name' => 'abc3',
2503 'last_name' => 'xyz3',
2504 'contact_type' => 'Individual',
2505 'email' => 'man3@yahoo.com',
2506 'api.contribution.create' => [
2507 'receive_date' => '2010-01-01',
2508 'total_amount' => 100.00,
2509 'financial_type_id' => 1,
2510 'payment_instrument_id' => 1,
2511 'non_deductible_amount' => 10.00,
2512 'fee_amount' => 50.00,
2513 'net_amount' => 90.00,
2514 'trxn_id' => 12345,
2515 'invoice_id' => 67890,
2516 'source' => 'SSF',
2517 'contribution_status_id' => 1,
2518 ],
2519 'api.contribution.create.1' => [
2520 'receive_date' => '2011-01-01',
2521 'total_amount' => 120.00,
2522 'financial_type_id' => $this->_financialTypeId = 1,
2523 'payment_instrument_id' => 1,
2524 'non_deductible_amount' => 10.00,
2525 'fee_amount' => 50.00,
2526 'net_amount' => 90.00,
2527 'trxn_id' => 12335,
2528 'invoice_id' => 67830,
2529 'source' => 'SSF',
2530 'contribution_status_id' => 1,
2531 ],
2532 'api.website.create' => [
2533 [
2534 'url' => "http://civicrm.org",
2535 ],
2536 ],
2537 ];
2538
2539 $result = $this->callAPISuccess('Contact', 'create', $params);
2540 $params = [
2541 'id' => $result['id'],
2542 'api.website.get' => [],
2543 'api.Contribution.get' => [
2544 'total_amount' => '120.00',
2545 ],
2546 'api.CustomValue.get' => 1,
2547 'api.Note.get' => 1,
2548 ];
2549 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__, __FILE__, $description, $subfile);
2550 // delete the contact
2551 $this->callAPISuccess('contact', 'delete', $result);
2552 $this->customGroupDelete($ids['custom_group_id']);
2553 $this->customGroupDelete($moreIDs['custom_group_id']);
2554 $this->assertEquals(0, $result['values'][$result['id']]['api.website.get']['is_error']);
2555 $this->assertEquals("http://civicrm.org", $result['values'][$result['id']]['api.website.get']['values'][0]['url']);
2556 }
2557
2558 /**
2559 * Verify attempt to create individual with chained arrays and sequential.
2560 *
2561 * @see https://issues.civicrm.org/jira/browse/CRM-15815
2562 *
2563 * @param int $version
2564 *
2565 * @dataProvider versionThreeAndFour
2566 */
2567 public function testCreateIndividualWithChainedArrayAndSequential($version) {
2568 $this->_apiversion = $version;
2569 $params = [
2570 'sequential' => 1,
2571 'first_name' => 'abc5',
2572 'last_name' => 'xyz5',
2573 'contact_type' => 'Individual',
2574 'email' => 'woman5@yahoo.com',
2575 'api.phone.create' => [
2576 ['phone' => '03-231 07 95'],
2577 ['phone' => '03-232 51 62'],
2578 ],
2579 'api.website.create' => [
2580 'url' => 'http://civicrm.org',
2581 ],
2582 ];
2583 $result = $this->callAPISuccess('Contact', 'create', $params);
2584
2585 // I could try to parse the result to see whether the two phone numbers
2586 // and the website are there, but I am not sure about the correct format.
2587 // So I will just fetch it again before checking.
2588 // See also http://forum.civicrm.org/index.php/topic,35393.0.html
2589 $params = [
2590 'sequential' => 1,
2591 'id' => $result['id'],
2592 'api.website.get' => [],
2593 'api.phone.get' => [],
2594 ];
2595 $result = $this->callAPISuccess('Contact', 'get', $params);
2596
2597 // delete the contact
2598 $this->callAPISuccess('contact', 'delete', $result);
2599
2600 $this->assertEquals(2, $result['values'][0]['api.phone.get']['count']);
2601 $this->assertEquals(1, $result['values'][0]['api.website.get']['count']);
2602 }
2603
2604 /**
2605 * Test retrieving an individual with chained array syntax.
2606 */
2607 public function testGetIndividualWithChainedArraysFormats() {
2608 $description = "This demonstrates the usage of chained api functions.\nIn this case no notes or custom fields have been created.";
2609 $subfile = "APIChainedArrayFormats";
2610 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
2611 $params['custom_' . $ids['custom_field_id']] = "custom string";
2612
2613 $moreIDs = $this->CustomGroupMultipleCreateWithFields();
2614 $params = [
2615 'first_name' => 'abc3',
2616 'last_name' => 'xyz3',
2617 'contact_type' => 'Individual',
2618 'email' => 'man3@yahoo.com',
2619 'api.contribution.create' => [
2620 'receive_date' => '2010-01-01',
2621 'total_amount' => 100.00,
2622 'financial_type_id' => $this->_financialTypeId,
2623 'payment_instrument_id' => 1,
2624 'non_deductible_amount' => 10.00,
2625 'fee_amount' => 50.00,
2626 'net_amount' => 90.00,
2627 'source' => 'SSF',
2628 'contribution_status_id' => 1,
2629 'skipCleanMoney' => 1,
2630 ],
2631 'api.contribution.create.1' => [
2632 'receive_date' => '2011-01-01',
2633 'total_amount' => 120.00,
2634 'financial_type_id' => $this->_financialTypeId,
2635 'payment_instrument_id' => 1,
2636 'non_deductible_amount' => 10.00,
2637 'fee_amount' => 50.00,
2638 'net_amount' => 90.00,
2639 'source' => 'SSF',
2640 'contribution_status_id' => 1,
2641 'skipCleanMoney' => 1,
2642 ],
2643 'api.website.create' => [
2644 [
2645 'url' => "http://civicrm.org",
2646 ],
2647 ],
2648 ];
2649
2650 $result = $this->callAPISuccess('Contact', 'create', $params);
2651 $params = [
2652 'id' => $result['id'],
2653 'api.website.getValue' => ['return' => 'url'],
2654 'api.Contribution.getCount' => [],
2655 'api.CustomValue.get' => 1,
2656 'api.Note.get' => 1,
2657 'api.Membership.getCount' => [],
2658 ];
2659 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__, __FILE__, $description, $subfile);
2660 $this->assertEquals(2, $result['values'][$result['id']]['api.Contribution.getCount']);
2661 $this->assertEquals(0, $result['values'][$result['id']]['api.Note.get']['is_error']);
2662 $this->assertEquals("http://civicrm.org", $result['values'][$result['id']]['api.website.getValue']);
2663
2664 $this->callAPISuccess('contact', 'delete', $result);
2665 $this->customGroupDelete($ids['custom_group_id']);
2666 $this->customGroupDelete($moreIDs['custom_group_id']);
2667 }
2668
2669 /**
2670 * Test complex chaining.
2671 */
2672 public function testGetIndividualWithChainedArraysAndMultipleCustom() {
2673 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
2674 $params['custom_' . $ids['custom_field_id']] = "custom string";
2675 $moreIDs = $this->CustomGroupMultipleCreateWithFields();
2676 $andMoreIDs = $this->CustomGroupMultipleCreateWithFields([
2677 'title' => "another group",
2678 'name' => 'another name',
2679 ]);
2680 $description = "This demonstrates the usage of chained api functions with multiple custom fields.";
2681 $subfile = "APIChainedArrayMultipleCustom";
2682 $params = [
2683 'first_name' => 'abc3',
2684 'last_name' => 'xyz3',
2685 'contact_type' => 'Individual',
2686 'email' => 'man3@yahoo.com',
2687 'api.contribution.create' => [
2688 'receive_date' => '2010-01-01',
2689 'total_amount' => 100.00,
2690 'financial_type_id' => 1,
2691 'payment_instrument_id' => 1,
2692 'non_deductible_amount' => 10.00,
2693 'fee_amount' => 50.00,
2694 'net_amount' => 90.00,
2695 'trxn_id' => 12345,
2696 'invoice_id' => 67890,
2697 'source' => 'SSF',
2698 'contribution_status_id' => 1,
2699 'skipCleanMoney' => 1,
2700 ],
2701 'api.contribution.create.1' => [
2702 'receive_date' => '2011-01-01',
2703 'total_amount' => 120.00,
2704 'financial_type_id' => 1,
2705 'payment_instrument_id' => 1,
2706 'non_deductible_amount' => 10.00,
2707 'fee_amount' => 50.00,
2708 'net_amount' => 90.00,
2709 'trxn_id' => 12335,
2710 'invoice_id' => 67830,
2711 'source' => 'SSF',
2712 'contribution_status_id' => 1,
2713 'skipCleanMoney' => 1,
2714 ],
2715 'api.website.create' => [
2716 [
2717 'url' => "http://civicrm.org",
2718 ],
2719 ],
2720 'custom_' . $ids['custom_field_id'] => "value 1",
2721 'custom_' . $moreIDs['custom_field_id'][0] => "value 2",
2722 'custom_' . $moreIDs['custom_field_id'][1] => "warm beer",
2723 'custom_' . $andMoreIDs['custom_field_id'][1] => "vegemite",
2724 ];
2725
2726 $result = $this->callAPISuccess('Contact', 'create', $params);
2727 $result = $this->callAPISuccess('Contact', 'create', [
2728 'contact_type' => 'Individual',
2729 'id' => $result['id'],
2730 'custom_' .
2731 $moreIDs['custom_field_id'][0] => "value 3",
2732 'custom_' .
2733 $ids['custom_field_id'] => "value 4",
2734 ]);
2735
2736 $params = [
2737 'id' => $result['id'],
2738 'api.website.getValue' => ['return' => 'url'],
2739 'api.Contribution.getCount' => [],
2740 'api.CustomValue.get' => 1,
2741 ];
2742 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__, __FILE__, $description, $subfile);
2743
2744 $this->customGroupDelete($ids['custom_group_id']);
2745 $this->customGroupDelete($moreIDs['custom_group_id']);
2746 $this->customGroupDelete($andMoreIDs['custom_group_id']);
2747 $this->assertEquals(0, $result['values'][$result['id']]['api.CustomValue.get']['is_error']);
2748 $this->assertEquals('http://civicrm.org', $result['values'][$result['id']]['api.website.getValue']);
2749 }
2750
2751 /**
2752 * Test checks usage of $values to pick & choose inputs.
2753 *
2754 * Api3 Only - chaining syntax is too funky for v4 (assuming entityTag "entity_id" field will be filled by magic)
2755 */
2756 public function testChainingValuesCreate() {
2757 $description = "This demonstrates the usage of chained api functions. Specifically it has one 'parent function' &
2758 2 child functions - one receives values from the parent (Contact) and the other child (Tag).";
2759 $subfile = "APIChainedArrayValuesFromSiblingFunction";
2760 $params = [
2761 'display_name' => 'batman',
2762 'contact_type' => 'Individual',
2763 'api.tag.create' => [
2764 'name' => '$value.id',
2765 'description' => '$value.display_name',
2766 'format.only_id' => 1,
2767 ],
2768 'api.entity_tag.create' => ['tag_id' => '$value.api.tag.create'],
2769 ];
2770 $result = $this->callAPIAndDocument('Contact', 'Create', $params, __FUNCTION__, __FILE__, $description, $subfile);
2771 $this->assertEquals(0, $result['values'][$result['id']]['api.entity_tag.create']['is_error']);
2772
2773 $tablesToTruncate = [
2774 'civicrm_contact',
2775 'civicrm_activity',
2776 'civicrm_entity_tag',
2777 'civicrm_tag',
2778 ];
2779 $this->quickCleanup($tablesToTruncate, TRUE);
2780 }
2781
2782 /**
2783 * Test TrueFalse format - I couldn't come up with an easy way to get an error on Get.
2784 *
2785 * @param int $version
2786 *
2787 * @dataProvider versionThreeAndFour
2788 */
2789 public function testContactGetFormatIsSuccessTrue($version) {
2790 $this->_apiversion = $version;
2791 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2792 $description = "This demonstrates use of the 'format.is_success' param.
2793 This param causes only the success or otherwise of the function to be returned as BOOLEAN";
2794 $subfile = "FormatIsSuccess_True";
2795 $params = ['id' => $contactID, 'format.is_success' => 1];
2796 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__, __FILE__, $description, $subfile);
2797 $this->assertEquals(1, $result);
2798 $this->callAPISuccess('Contact', 'Delete', $params);
2799 }
2800
2801 /**
2802 * Test TrueFalse format.
2803 *
2804 * @param int $version
2805 *
2806 * @dataProvider versionThreeAndFour
2807 */
2808 public function testContactCreateFormatIsSuccessFalse($version) {
2809 $this->_apiversion = $version;
2810
2811 $description = "This demonstrates use of the 'format.is_success' param.
2812 This param causes only the success or otherwise of the function to be returned as BOOLEAN";
2813 $subfile = "FormatIsSuccess_Fail";
2814 $params = ['id' => 500, 'format.is_success' => 1];
2815 $result = $this->callAPIAndDocument('Contact', 'Create', $params, __FUNCTION__, __FILE__, $description, $subfile);
2816 $this->assertEquals(0, $result);
2817 }
2818
2819 /**
2820 * Test long display names.
2821 *
2822 * CRM-21258
2823 *
2824 * @param int $version
2825 *
2826 * @dataProvider versionThreeAndFour
2827 */
2828 public function testContactCreateLongDisplayName($version) {
2829 $this->_apiversion = $version;
2830 $result = $this->callAPISuccess('Contact', 'Create', [
2831 'first_name' => str_pad('a', 64, 'a'),
2832 'last_name' => str_pad('a', 64, 'a'),
2833 'contact_type' => 'Individual',
2834 ]);
2835 $this->assertEquals('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', $result['values'][$result['id']]['display_name']);
2836 $this->assertEquals('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', $result['values'][$result['id']]['sort_name']);
2837 }
2838
2839 /**
2840 * Test that we can set the sort name via the api or alter it via a hook.
2841 *
2842 * As of writing this is being fixed for Organization & Household but it makes sense to do for individuals too.
2843 *
2844 * @param int $version
2845 *
2846 * @dataProvider versionThreeAndFour
2847 */
2848 public function testCreateAlterSortName($version) {
2849 $this->_apiversion = $version;
2850 $organizationID = $this->organizationCreate(['organization_name' => 'The Justice League', 'sort_name' => 'Justice League, The']);
2851 $organization = $this->callAPISuccessGetSingle('Contact', ['return' => ['sort_name', 'display_name'], 'id' => $organizationID]);
2852 $this->assertEquals('Justice League, The', $organization['sort_name']);
2853 $this->assertEquals('The Justice League', $organization['display_name']);
2854 $this->hookClass->setHook('civicrm_pre', [$this, 'killTheJusticeLeague']);
2855 $this->organizationCreate(['id' => $organizationID, 'sort_name' => 'Justice League, The']);
2856 $organization = $this->callAPISuccessGetSingle('Contact', ['return' => ['sort_name', 'display_name', 'is_deceased'], 'id' => $organizationID]);
2857 $this->assertEquals('Steppenwolf wuz here', $organization['display_name']);
2858 $this->assertEquals('Steppenwolf wuz here', $organization['sort_name']);
2859 $this->assertEquals(1, $organization['is_deceased']);
2860
2861 $householdID = $this->householdCreate();
2862 $household = $this->callAPISuccessGetSingle('Contact', ['return' => ['sort_name', 'display_name'], 'id' => $householdID]);
2863 $this->assertEquals('Steppenwolf wuz here', $household['display_name']);
2864 $this->assertEquals('Steppenwolf wuz here', $household['sort_name']);
2865 }
2866
2867 /**
2868 * Implements hook_pre().
2869 */
2870 public function killTheJusticeLeague($op, $entity, $id, &$params) {
2871 $params['sort_name'] = 'Steppenwolf wuz here';
2872 $params['display_name'] = 'Steppenwolf wuz here';
2873 $params['is_deceased'] = 1;
2874 }
2875
2876 /**
2877 * Test Single Entity format.
2878 *
2879 * @param int $version
2880 *
2881 * @dataProvider versionThreeAndFour
2882 */
2883 public function testContactGetSingleEntityArray($version) {
2884 $this->_apiversion = $version;
2885 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2886 $description = "This demonstrates use of the 'format.single_entity_array' param.
2887 This param causes the only contact to be returned as an array without the other levels.
2888 It will be ignored if there is not exactly 1 result";
2889 $subfile = "GetSingleContact";
2890 $result = $this->callAPIAndDocument('Contact', 'GetSingle', ['id' => $contactID], __FUNCTION__, __FILE__, $description, $subfile);
2891 $this->assertEquals('Mr. Test Contact II', $result['display_name']);
2892 $this->callAPISuccess('Contact', 'Delete', ['id' => $contactID]);
2893 }
2894
2895 /**
2896 * Test Single Entity format.
2897 *
2898 * @param int $version
2899 *
2900 * @dataProvider versionThreeAndFour
2901 */
2902 public function testContactGetFormatCountOnly($version) {
2903 $this->_apiversion = $version;
2904 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2905 $description = "This demonstrates use of the 'getCount' action.
2906 This param causes the count of the only function to be returned as an integer.";
2907 $params = ['id' => $contactID];
2908 $result = $this->callAPIAndDocument('Contact', 'GetCount', $params, __FUNCTION__, __FILE__, $description,
2909 'GetCountContact');
2910 $this->assertEquals('1', $result);
2911 $this->callAPISuccess('Contact', 'Delete', $params);
2912 }
2913
2914 /**
2915 * Test id only format.
2916 *
2917 * @param int $version
2918 *
2919 * @dataProvider versionThreeAndFour
2920 */
2921 public function testContactGetFormatIDOnly($version) {
2922 $this->_apiversion = $version;
2923 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2924 $description = "This demonstrates use of the 'format.id_only' param.
2925 This param causes the id of the only entity to be returned as an integer.
2926 It will be ignored if there is not exactly 1 result";
2927 $subfile = "FormatOnlyID";
2928 $params = ['id' => $contactID, 'format.only_id' => 1];
2929 $result = $this->callAPIAndDocument('Contact', 'Get', $params, __FUNCTION__, __FILE__, $description, $subfile);
2930 $this->assertEquals($contactID, $result);
2931 $this->callAPISuccess('Contact', 'Delete', $params);
2932 }
2933
2934 /**
2935 * Test id only format.
2936 *
2937 * @param int $version
2938 *
2939 * @dataProvider versionThreeAndFour
2940 */
2941 public function testContactGetFormatSingleValue($version) {
2942 $this->_apiversion = $version;
2943 $contactID = $this->individualCreate(['first_name' => 'Test', 'last_name' => 'Contact']);
2944 $description = "This demonstrates use of the 'format.single_value' param.
2945 This param causes only a single value of the only entity to be returned as an string.
2946 It will be ignored if there is not exactly 1 result";
2947 $subFile = "FormatSingleValue";
2948 $params = ['id' => $contactID, 'return' => 'display_name'];
2949 $result = $this->callAPIAndDocument('Contact', 'getvalue', $params, __FUNCTION__, __FILE__, $description, $subFile);
2950 $this->assertEquals('Mr. Test Contact II', $result);
2951 $this->callAPISuccess('Contact', 'Delete', $params);
2952 }
2953
2954 /**
2955 * Test that permissions are respected when creating contacts.
2956 *
2957 * @param int $version
2958 *
2959 * @dataProvider versionThreeAndFour
2960 */
2961 public function testContactCreationPermissions($version) {
2962 $this->_apiversion = $version;
2963 $params = [
2964 'contact_type' => 'Individual',
2965 'first_name' => 'Foo',
2966 'last_name' => 'Bear',
2967 'check_permissions' => TRUE,
2968 ];
2969 $config = CRM_Core_Config::singleton();
2970 $config->userPermissionClass->permissions = ['access CiviCRM'];
2971 $result = $this->callAPIFailure('contact', 'create', $params);
2972 $this->assertContains('failed', $result['error_message'], 'lacking permissions should not be enough to create a contact');
2973
2974 $config->userPermissionClass->permissions = ['access CiviCRM', 'add contacts', 'import contacts'];
2975 $this->callAPISuccess('contact', 'create', $params);
2976 }
2977
2978 /**
2979 * Test that delete with skip undelete respects permissions.
2980 * TODO: Api4
2981 *
2982 * @throws \CRM_Core_Exception
2983 */
2984 public function testContactDeletePermissions() {
2985 $contactID = $this->individualCreate();
2986 $tag = $this->callAPISuccess('Tag', 'create', ['name' => 'to be deleted']);
2987 $this->callAPISuccess('EntityTag', 'create', ['entity_id' => $contactID, 'tag_id' => $tag['id']]);
2988 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
2989 $this->callAPIFailure('Contact', 'delete', [
2990 'id' => $contactID,
2991 'check_permissions' => 1,
2992 'skip_undelete' => 1,
2993 ]);
2994 $this->callAPISuccess('Contact', 'delete', [
2995 'id' => $contactID,
2996 'check_permissions' => 0,
2997 'skip_undelete' => 1,
2998 ]);
2999 $this->callAPISuccessGetCount('EntityTag', ['entity_id' => $contactID], 0);
3000 }
3001
3002 /**
3003 * Test update with check permissions set.
3004 *
3005 * @param int $version
3006 *
3007 * @dataProvider versionThreeAndFour
3008 */
3009 public function testContactUpdatePermissions($version) {
3010 $this->_apiversion = $version;
3011 $params = [
3012 'contact_type' => 'Individual',
3013 'first_name' => 'Foo',
3014 'last_name' => 'Bear',
3015 'check_permissions' => TRUE,
3016 ];
3017 $result = $this->callAPISuccess('contact', 'create', $params);
3018 $config = CRM_Core_Config::singleton();
3019 $params = [
3020 'id' => $result['id'],
3021 'contact_type' => 'Individual',
3022 'last_name' => 'Bar',
3023 'check_permissions' => TRUE,
3024 ];
3025
3026 $config->userPermissionClass->permissions = ['access CiviCRM'];
3027 $result = $this->callAPIFailure('contact', 'update', $params);
3028 $this->assertEquals('Permission denied to modify contact record', $result['error_message']);
3029
3030 $config->userPermissionClass->permissions = [
3031 'access CiviCRM',
3032 'add contacts',
3033 'view all contacts',
3034 'edit all contacts',
3035 'import contacts',
3036 ];
3037 $this->callAPISuccess('contact', 'update', $params);
3038 }
3039
3040 /**
3041 * Test contact proximity api.
3042 */
3043 public function testContactProximity() {
3044 // first create a contact with a SF location with a specific
3045 // geocode
3046 $contactID = $this->organizationCreate();
3047
3048 // now create the address
3049 $params = [
3050 'street_address' => '123 Main Street',
3051 'city' => 'San Francisco',
3052 'is_primary' => 1,
3053 'country_id' => 1228,
3054 'state_province_id' => 1004,
3055 'geo_code_1' => '37.79',
3056 'geo_code_2' => '-122.40',
3057 'location_type_id' => 1,
3058 'contact_id' => $contactID,
3059 ];
3060
3061 $result = $this->callAPISuccess('address', 'create', $params);
3062 $this->assertEquals(1, $result['count']);
3063
3064 // now do a proximity search with a close enough geocode and hope to match
3065 // that specific contact only!
3066 $proxParams = [
3067 'latitude' => 37.7,
3068 'longitude' => -122.3,
3069 'unit' => 'mile',
3070 'distance' => 10,
3071 ];
3072 $result = $this->callAPISuccess('contact', 'proximity', $proxParams);
3073 $this->assertEquals(1, $result['count']);
3074 }
3075
3076 /**
3077 * Test that Ajax API permission is sufficient to access getquick api.
3078 *
3079 * (note that getquick api is required for autocomplete & has ACL permissions applied)
3080 */
3081 public function testGetquickPermissionCRM13744() {
3082 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviEvent'];
3083 $this->callAPIFailure('contact', 'getquick', ['name' => 'b', 'check_permissions' => TRUE]);
3084 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
3085 $this->callAPISuccess('contact', 'getquick', ['name' => 'b', 'check_permissions' => TRUE]);
3086 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access AJAX API'];
3087 $this->callAPISuccess('contact', 'getquick', ['name' => 'b', 'check_permissions' => TRUE]);
3088 }
3089
3090 /**
3091 * Test that getquick returns contacts with an exact first name match first.
3092 *
3093 * The search string 'b' & 'bob' both return ordered by sort_name if includeOrderByClause
3094 * is true (default) but if it is false then matches are returned in ID order.
3095 *
3096 * @dataProvider getSearchSortOptions
3097 */
3098 public function testGetQuickExactFirst($searchParameters, $settings, $firstContact, $secondContact = NULL) {
3099 $this->getQuickSearchSampleData();
3100 $this->callAPISuccess('Setting', 'create', $settings);
3101 $result = $this->callAPISuccess('contact', 'getquick', $searchParameters);
3102 $this->assertEquals($firstContact, $result['values'][0]['sort_name']);
3103 $this->assertEquals($secondContact, $result['values'][1]['sort_name']);
3104 $this->callAPISuccess('Setting', 'create', ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE]);
3105 }
3106
3107 public function getSearchSortOptions() {
3108 $firstAlphabeticalContactBySortName = 'A Bobby, Bobby';
3109 $secondAlphabeticalContactBySortName = 'Aadvark, Bob';
3110 $secondAlphabeticalContactWithEmailBySortName = 'Bob, Bob';
3111 $firstAlphabeticalContactFirstNameBob = 'Aadvark, Bob';
3112 $secondAlphabeticalContactFirstNameBob = 'Bob, Bob';
3113 $firstByIDContactFirstNameBob = 'Bob, Bob';
3114 $secondByIDContactFirstNameBob = 'K Bobby, Bob';
3115 $firstContactByID = 'Bob, Bob';
3116 $secondContactByID = 'E Bobby, Bobby';
3117 $bobLikeEmail = 'A Bobby, Bobby';
3118
3119 return [
3120 'empty_search_basic' => [
3121 'search_parameters' => ['name' => '%'],
3122 'settings' => ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE],
3123 'first_contact' => $firstAlphabeticalContactBySortName,
3124 'second_contact' => $secondAlphabeticalContactBySortName,
3125 ],
3126 'empty_search_basic_no_wildcard' => [
3127 'search_parameters' => ['name' => '%'],
3128 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3129 'first_contact' => $firstAlphabeticalContactBySortName,
3130 'second_contact' => $secondAlphabeticalContactBySortName,
3131 ],
3132 'single_letter_search_basic' => [
3133 'search_parameters' => ['name' => 'b'],
3134 'settings' => ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE],
3135 'first_contact' => $firstAlphabeticalContactBySortName,
3136 'second_contact' => $secondAlphabeticalContactBySortName,
3137 ],
3138 'bob_search_basic' => [
3139 'search_parameters' => ['name' => 'bob'],
3140 'settings' => ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE],
3141 'first_contact' => $firstAlphabeticalContactBySortName,
3142 'second_contact' => $secondAlphabeticalContactBySortName,
3143 ],
3144 // This test has been disabled as is proving to be problematic to reproduce due to MySQL sorting issues between different versions
3145 // 'bob_search_no_orderby' => array(
3146 // 'search_parameters' => array('name' => 'bob'),
3147 // 'settings' => array('includeWildCardInName' => TRUE, 'includeOrderByClause' => FALSE),
3148 // 'first_contact' => $firstContactByID,
3149 // 'second_contact' => $secondContactByID,
3150 //),
3151 'bob_search_no_wildcard' => [
3152 'search_parameters' => ['name' => 'bob'],
3153 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3154 'second_contact' => $bobLikeEmail,
3155 'first_contact' => $secondAlphabeticalContactFirstNameBob,
3156 ],
3157 // This should be the same as just no wildcard as if we had an exactMatch while searching by
3158 // sort name it would rise to the top CRM-19547
3159 'bob_search_no_wildcard_no_orderby' => [
3160 'search_parameters' => ['name' => 'bob'],
3161 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3162 'second_contact' => $bobLikeEmail,
3163 'first_contact' => $secondAlphabeticalContactFirstNameBob,
3164 ],
3165 'first_name_search_basic' => [
3166 'search_parameters' => ['name' => 'bob', 'field_name' => 'first_name'],
3167 'settings' => ['includeWildCardInName' => TRUE, 'includeOrderByClause' => TRUE],
3168 'first_contact' => $firstAlphabeticalContactFirstNameBob,
3169 'second_contact' => $secondAlphabeticalContactFirstNameBob,
3170 ],
3171 'first_name_search_no_wildcard' => [
3172 'search_parameters' => ['name' => 'bob', 'field_name' => 'first_name'],
3173 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3174 'first_contact' => $firstAlphabeticalContactFirstNameBob,
3175 'second_contact' => $secondAlphabeticalContactFirstNameBob,
3176 ],
3177 // This test has been disabled as is proving to be problematic to reproduce due to MySQL sorting issues between different versions
3178 //'first_name_search_no_orderby' => array(
3179 // 'search_parameters' => array('name' => 'bob', 'field_name' => 'first_name'),
3180 // 'settings' => array('includeWildCardInName' => TRUE, 'includeOrderByClause' => FALSE),
3181 // 'first_contact' => $firstByIDContactFirstNameBob,
3182 // 'second_contact' => $secondByIDContactFirstNameBob,
3183 //),
3184 'email_search_basic' => [
3185 'search_parameters' => ['name' => 'bob', 'field_name' => 'email', 'table_name' => 'eml'],
3186 'settings' => ['includeWildCardInName' => FALSE, 'includeOrderByClause' => TRUE],
3187 'first_contact' => $firstAlphabeticalContactBySortName,
3188 'second_contact' => $secondAlphabeticalContactWithEmailBySortName,
3189 ],
3190 ];
3191 }
3192
3193 /**
3194 * Test that getquick returns contacts with an exact first name match first.
3195 */
3196 public function testGetQuickEmail() {
3197 $this->getQuickSearchSampleData();
3198 $loggedInContactID = $this->createLoggedInUser();
3199 $result = $this->callAPISuccess('contact', 'getquick', [
3200 'name' => 'c',
3201 ]);
3202 $expectedData = [
3203 'A Bobby, Bobby :: bob@bobby.com',
3204 'Bob, Bob :: bob@bob.com',
3205 'C Bobby, Bobby',
3206 'H Bobby, Bobby :: bob@h.com',
3207 'Second Domain',
3208 $this->callAPISuccessGetValue('Contact', ['id' => $loggedInContactID, 'return' => 'last_name']) . ', Logged In :: anthony_anderson@civicrm.org',
3209 ];
3210 $this->assertEquals(6, $result['count']);
3211 foreach ($expectedData as $index => $value) {
3212 $this->assertEquals($value, $result['values'][$index]['data']);
3213 }
3214 $result = $this->callAPISuccess('contact', 'getquick', [
3215 'name' => 'h.',
3216 ]);
3217 $expectedData = [
3218 'H Bobby, Bobby :: bob@h.com',
3219 ];
3220 foreach ($expectedData as $index => $value) {
3221 $this->assertEquals($value, $result['values'][$index]['data']);
3222 }
3223 $this->callAPISuccess('Setting', 'create', ['includeWildCardInName' => FALSE]);
3224 $result = $this->callAPISuccess('contact', 'getquick', [
3225 'name' => 'h.',
3226 ]);
3227 $this->callAPISuccess('Setting', 'create', ['includeWildCardInName' => TRUE]);
3228 $this->assertEquals(0, $result['count']);
3229 }
3230
3231 /**
3232 * Test that getquick returns contacts with an exact first name match first.
3233 */
3234 public function testGetQuickEmailACL() {
3235 $this->getQuickSearchSampleData();
3236 $loggedInContactID = $this->createLoggedInUser();
3237 CRM_Core_Config::singleton()->userPermissionClass->permissions = [];
3238 $result = $this->callAPISuccess('contact', 'getquick', [
3239 'name' => 'c',
3240 ]);
3241 $this->assertEquals(0, $result['count']);
3242
3243 $this->hookClass->setHook('civicrm_aclWhereClause', [$this, 'aclWhereNoBobH']);
3244 CRM_Contact_BAO_Contact_Permission::cache($loggedInContactID, CRM_Core_Permission::VIEW, TRUE);
3245 $result = $this->callAPISuccess('contact', 'getquick', [
3246 'name' => 'c',
3247 ]);
3248
3249 // Without the acl it would be 6 like the previous email getquick test.
3250 $this->assertEquals(5, $result['count']);
3251 $expectedData = [
3252 'A Bobby, Bobby :: bob@bobby.com',
3253 'Bob, Bob :: bob@bob.com',
3254 'C Bobby, Bobby',
3255 'Second Domain',
3256 $this->callAPISuccessGetValue('Contact', ['id' => $loggedInContactID, 'return' => 'last_name']) . ', Logged In :: anthony_anderson@civicrm.org',
3257 ];
3258 foreach ($expectedData as $index => $value) {
3259 $this->assertEquals($value, $result['values'][$index]['data']);
3260 }
3261 }
3262
3263 /**
3264 * Test that getquick returns contacts with an exact first name match first.
3265 */
3266 public function testGetQuickExternalID() {
3267 $this->getQuickSearchSampleData();
3268 $result = $this->callAPISuccess('contact', 'getquick', [
3269 'name' => 'b',
3270 'field_name' => 'external_identifier',
3271 'table_name' => 'cc',
3272 ]);
3273 $this->assertEquals(0, $result['count']);
3274 $result = $this->callAPISuccess('contact', 'getquick', [
3275 'name' => 'abc',
3276 'field_name' => 'external_identifier',
3277 'table_name' => 'cc',
3278 ]);
3279 $this->assertEquals(1, $result['count']);
3280 $this->assertEquals('Bob, Bob', $result['values'][0]['sort_name']);
3281 }
3282
3283 /**
3284 * Test that getquick returns contacts with an exact first name match first.
3285 */
3286 public function testGetQuickID() {
3287 $max = CRM_Core_DAO::singleValueQuery("SELECT max(id) FROM civicrm_contact");
3288 $this->getQuickSearchSampleData();
3289 $result = $this->callAPISuccess('contact', 'getquick', [
3290 'name' => $max + 2,
3291 'field_name' => 'id',
3292 'table_name' => 'cc',
3293 ]);
3294 $this->assertEquals(1, $result['count']);
3295 $this->assertEquals('E Bobby, Bobby', $result['values'][0]['sort_name']);
3296 $result = $this->callAPISuccess('contact', 'getquick', [
3297 'name' => $max + 2,
3298 'field_name' => 'contact_id',
3299 'table_name' => 'cc',
3300 ]);
3301 $this->assertEquals(1, $result['count']);
3302 $this->assertEquals('E Bobby, Bobby', $result['values'][0]['sort_name']);
3303 }
3304
3305 /**
3306 * Test that getquick returns contacts with different cases of phone substring.
3307 */
3308 public function testGetQuickPhone() {
3309 $this->getQuickSearchSampleData();
3310 $criterias = [
3311 [
3312 'criteria' => [
3313 'name' => '87-6',
3314 'field_name' => 'phone_numeric',
3315 ],
3316 'count' => 2,
3317 'sort_names' => [
3318 'I Bobby, Bobby',
3319 'J Bobby, Bobby',
3320 ],
3321 ],
3322 [
3323 'criteria' => [
3324 'name' => '876-1',
3325 'field_name' => 'phone_numeric',
3326 ],
3327 'count' => 1,
3328 'sort_names' => [
3329 'I Bobby, Bobby',
3330 ],
3331 ],
3332 [
3333 'criteria' => [
3334 'name' => '87623',
3335 'field_name' => 'phone_numeric',
3336 ],
3337 'count' => 1,
3338 'sort_names' => [
3339 'J Bobby, Bobby',
3340 ],
3341 ],
3342 [
3343 'criteria' => [
3344 'name' => '8a7abc6',
3345 'field_name' => 'phone_numeric',
3346 ],
3347 'count' => 2,
3348 'sort_names' => [
3349 'I Bobby, Bobby',
3350 'J Bobby, Bobby',
3351 ],
3352 ],
3353 ];
3354
3355 foreach ($criterias as $criteria) {
3356 $result = $this->callAPISuccess('contact', 'getquick', $criteria['criteria']);
3357 $this->assertEquals($result['count'], $criteria['count']);
3358 foreach ($criteria['sort_names'] as $key => $sortName) {
3359 $this->assertEquals($sortName, $result['values'][$key]['sort_name']);
3360 }
3361 }
3362 }
3363
3364 /**
3365 * Test that getquick returns contacts with an exact first name match first.
3366 *
3367 * Depending on the setting the sort name sort might click in next or not - test!
3368 */
3369 public function testGetQuickFirstName() {
3370 $this->getQuickSearchSampleData();
3371 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => TRUE]);
3372 $result = $this->callAPISuccess('contact', 'getquick', [
3373 'name' => 'Bob',
3374 'field_name' => 'first_name',
3375 'table_name' => 'cc',
3376 ]);
3377 $expected = [
3378 'Aadvark, Bob',
3379 'Bob, Bob',
3380 'K Bobby, Bob',
3381 'A Bobby, Bobby',
3382 ];
3383
3384 foreach ($expected as $index => $value) {
3385 $this->assertEquals($value, $result['values'][$index]['sort_name']);
3386 }
3387 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => FALSE]);
3388 $result = $this->callAPISuccess('contact', 'getquick', ['name' => 'bob']);
3389 $this->assertEquals('Bob, Bob', $result['values'][0]['sort_name']);
3390 // This test has been disabled as is proving to be problematic to reproduce due to MySQL sorting issues between different versions
3391 //$this->assertEquals('E Bobby, Bobby', $result['values'][1]['sort_name']);
3392 }
3393
3394 /**
3395 * Test that getquick applies ACLs.
3396 */
3397 public function testGetQuickFirstNameACLs() {
3398 $this->getQuickSearchSampleData();
3399 $userID = $this->createLoggedInUser();
3400 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => TRUE, 'search_autocomplete_count' => 15]);
3401 CRM_Core_Config::singleton()->userPermissionClass->permissions = [];
3402 $result = $this->callAPISuccess('contact', 'getquick', [
3403 'name' => 'Bob',
3404 'field_name' => 'first_name',
3405 'table_name' => 'cc',
3406 ]);
3407 $this->assertEquals(0, $result['count']);
3408
3409 $this->hookClass->setHook('civicrm_aclWhereClause', [$this, 'aclWhereNoBobH']);
3410 CRM_Contact_BAO_Contact_Permission::cache($userID, CRM_Core_Permission::VIEW, TRUE);
3411 $result = $this->callAPISuccess('contact', 'getquick', [
3412 'name' => 'Bob',
3413 'field_name' => 'first_name',
3414 'table_name' => 'cc',
3415 ]);
3416 $this->assertEquals('K Bobby, Bob', $result['values'][2]['sort_name']);
3417 // Without the ACL 9 would be bob@h.com.
3418 $this->assertEquals('I Bobby, Bobby', $result['values'][10]['sort_name']);
3419 }
3420
3421 /**
3422 * Full results returned.
3423 *
3424 * @implements CRM_Utils_Hook::aclWhereClause
3425 *
3426 * @param string $type
3427 * @param array $tables
3428 * @param array $whereTables
3429 * @param int $contactID
3430 * @param string $where
3431 */
3432 public function aclWhereNoBobH($type, &$tables, &$whereTables, &$contactID, &$where) {
3433 $where = " (email <> 'bob@h.com' OR email IS NULL) ";
3434 $whereTables['civicrm_email'] = "LEFT JOIN civicrm_email e ON contact_a.id = e.contact_id";
3435 }
3436
3437 /**
3438 * Test that getquick returns contacts with an exact last name match first.
3439 */
3440 public function testGetQuickLastName() {
3441 $this->getQuickSearchSampleData();
3442 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => TRUE]);
3443 $result = $this->callAPISuccess('contact', 'getquick', [
3444 'name' => 'Bob',
3445 'field_name' => 'last_name',
3446 'table_name' => 'cc',
3447 ]);
3448 $expected = [
3449 'Bob, Bob',
3450 'A Bobby, Bobby',
3451 'B Bobby, Bobby',
3452 ];
3453
3454 foreach ($expected as $index => $value) {
3455 $this->assertEquals($value, $result['values'][$index]['sort_name']);
3456 }
3457 $this->callAPISuccess('Setting', 'create', ['includeOrderByClause' => FALSE]);
3458 $result = $this->callAPISuccess('contact', 'getquick', ['name' => 'bob']);
3459 $this->assertEquals('Bob, Bob :: bob@bob.com', $result['values'][0]['data']);
3460 }
3461
3462 /**
3463 * Test that getquick returns contacts by city.
3464 */
3465 public function testGetQuickCity() {
3466 $this->getQuickSearchSampleData();
3467 $result = $this->callAPISuccess('contact', 'getquick', [
3468 'name' => 'o',
3469 'field_name' => 'city',
3470 'table_name' => 'sts',
3471 ]);
3472 $this->assertEquals('B Bobby, Bobby :: Toronto', $result['values'][0]['data']);
3473 $result = $this->callAPISuccess('contact', 'getquick', [
3474 'name' => 'n',
3475 'field_name' => 'city',
3476 'table_name' => 'sts',
3477 ]);
3478 $this->assertEquals('B Bobby, Bobby :: Toronto', $result['values'][0]['data']);
3479 $this->assertEquals('C Bobby, Bobby :: Whanganui', $result['values'][1]['data']);
3480 }
3481
3482 /**
3483 * Set up some sample data for testing quicksearch.
3484 */
3485 public function getQuickSearchSampleData() {
3486 $contacts = [
3487 ['first_name' => 'Bob', 'last_name' => 'Bob', 'external_identifier' => 'abc', 'email' => 'bob@bob.com'],
3488 ['first_name' => 'Bobby', 'last_name' => 'E Bobby', 'external_identifier' => 'abcd'],
3489 [
3490 'first_name' => 'Bobby',
3491 'last_name' => 'B Bobby',
3492 'external_identifier' => 'bcd',
3493 'api.address.create' => [
3494 'street_address' => 'Sesame Street',
3495 'city' => 'Toronto',
3496 'location_type_id' => 1,
3497 ],
3498 ],
3499 [
3500 'first_name' => 'Bobby',
3501 'last_name' => 'C Bobby',
3502 'external_identifier' => 'bcde',
3503 'api.address.create' => [
3504 'street_address' => 'Te huarahi',
3505 'city' => 'Whanganui',
3506 'location_type_id' => 1,
3507 ],
3508 ],
3509 ['first_name' => 'Bobby', 'last_name' => 'D Bobby', 'external_identifier' => 'efg'],
3510 ['first_name' => 'Bobby', 'last_name' => 'A Bobby', 'external_identifier' => 'hij', 'email' => 'bob@bobby.com'],
3511 ['first_name' => 'Bobby', 'last_name' => 'F Bobby', 'external_identifier' => 'klm'],
3512 ['first_name' => 'Bobby', 'last_name' => 'G Bobby', 'external_identifier' => 'nop'],
3513 ['first_name' => 'Bobby', 'last_name' => 'H Bobby', 'external_identifier' => 'qrs', 'email' => 'bob@h.com'],
3514 [
3515 'first_name' => 'Bobby',
3516 'last_name' => 'I Bobby',
3517 'api.phone.create' => [
3518 'phone' => '876-123',
3519 'phone_ext' => '444',
3520 "phone_type_id" => "Phone",
3521 'location_type_id' => 1,
3522 'is_primary' => 1,
3523 ],
3524 ],
3525 [
3526 'first_name' => 'Bobby',
3527 'last_name' => 'J Bobby',
3528 'api.phone.create' => [
3529 'phone' => '87-6-234',
3530 'phone_ext' => '134',
3531 "phone_type_id" => "Phone",
3532 'location_type_id' => 1,
3533 'is_primary' => 1,
3534 ],
3535 ],
3536 ['first_name' => 'Bob', 'last_name' => 'K Bobby', 'external_identifier' => 'bcdef'],
3537 ['first_name' => 'Bob', 'last_name' => 'Aadvark'],
3538 ];
3539 foreach ($contacts as $type => $contact) {
3540 $contact['contact_type'] = 'Individual';
3541 $this->callAPISuccess('Contact', 'create', $contact);
3542 }
3543 }
3544
3545 /**
3546 * Test get ref api - gets a list of references to an entity.
3547 */
3548 public function testGetReferenceCounts() {
3549 $result = $this->callAPISuccess('Contact', 'create', [
3550 'first_name' => 'Testily',
3551 'last_name' => 'McHaste',
3552 'contact_type' => 'Individual',
3553 'api.Address.replace' => [
3554 'values' => [],
3555 ],
3556 'api.Email.replace' => [
3557 'values' => [
3558 [
3559 'email' => 'spam@dev.null',
3560 'is_primary' => 0,
3561 'location_type_id' => 1,
3562 ],
3563 ],
3564 ],
3565 'api.Phone.replace' => [
3566 'values' => [
3567 [
3568 'phone' => '234-567-0001',
3569 'is_primary' => 1,
3570 'location_type_id' => 1,
3571 ],
3572 [
3573 'phone' => '234-567-0002',
3574 'is_primary' => 0,
3575 'location_type_id' => 1,
3576 ],
3577 ],
3578 ],
3579 ]);
3580
3581 //$dao = new CRM_Contact_BAO_Contact();
3582 //$dao->id = $result['id'];
3583 //$this->assertTrue((bool) $dao->find(TRUE));
3584 //
3585 //$refCounts = $dao->getReferenceCounts();
3586 //$this->assertTrue(is_array($refCounts));
3587 //$refCountsIdx = CRM_Utils_Array::index(array('name'), $refCounts);
3588
3589 $refCounts = $this->callAPISuccess('Contact', 'getrefcount', [
3590 'id' => $result['id'],
3591 ]);
3592 $refCountsIdx = CRM_Utils_Array::index(['name'], $refCounts['values']);
3593
3594 $this->assertEquals(1, $refCountsIdx['sql:civicrm_email:contact_id']['count']);
3595 $this->assertEquals('civicrm_email', $refCountsIdx['sql:civicrm_email:contact_id']['table']);
3596 $this->assertEquals(2, $refCountsIdx['sql:civicrm_phone:contact_id']['count']);
3597 $this->assertEquals('civicrm_phone', $refCountsIdx['sql:civicrm_phone:contact_id']['table']);
3598 $this->assertTrue(!isset($refCountsIdx['sql:civicrm_address:contact_id']));
3599 }
3600
3601 /**
3602 * Test the use of sql operators.
3603 *
3604 * @param int $version
3605 *
3606 * @dataProvider versionThreeAndFour
3607 */
3608 public function testSQLOperatorsOnContactAPI($version) {
3609 $this->_apiversion = $version;
3610 $this->individualCreate();
3611 $this->organizationCreate();
3612 $this->householdCreate();
3613 $contacts = $this->callAPISuccess('contact', 'get', ['legal_name' => ['IS NOT NULL' => TRUE]]);
3614 $this->assertEquals($contacts['count'], CRM_Core_DAO::singleValueQuery('select count(*) FROM civicrm_contact WHERE legal_name IS NOT NULL'));
3615 $contacts = $this->callAPISuccess('contact', 'get', ['legal_name' => ['IS NULL' => TRUE]]);
3616 $this->assertEquals($contacts['count'], CRM_Core_DAO::singleValueQuery('select count(*) FROM civicrm_contact WHERE legal_name IS NULL'));
3617 }
3618
3619 /**
3620 * CRM-14743 - test api respects search operators.
3621 *
3622 * @param int $version
3623 *
3624 * @dataProvider versionThreeAndFour
3625 */
3626 public function testGetModifiedDateByOperators($version) {
3627 $this->_apiversion = $version;
3628 $preExistingContactCount = CRM_Core_DAO::singleValueQuery('select count(*) FROM civicrm_contact');
3629 $contact1 = $this->individualCreate();
3630 $sql = "UPDATE civicrm_contact SET created_date = '2012-01-01', modified_date = '2013-01-01' WHERE id = " . $contact1;
3631 CRM_Core_DAO::executeQuery($sql);
3632 $contact2 = $this->individualCreate();
3633 $sql = "UPDATE civicrm_contact SET created_date = '2012-02-01', modified_date = '2013-02-01' WHERE id = " . $contact2;
3634 CRM_Core_DAO::executeQuery($sql);
3635 $contact3 = $this->householdCreate();
3636 $sql = "UPDATE civicrm_contact SET created_date = '2012-03-01', modified_date = '2013-03-01' WHERE id = " . $contact3;
3637 CRM_Core_DAO::executeQuery($sql);
3638 $contacts = $this->callAPISuccess('contact', 'get', ['modified_date' => ['<' => '2014-01-01']]);
3639 $this->assertEquals($contacts['count'], 3);
3640 $contacts = $this->callAPISuccess('contact', 'get', ['modified_date' => ['>' => '2014-01-01']]);
3641 $this->assertEquals($contacts['count'], $preExistingContactCount);
3642 }
3643
3644 /**
3645 * CRM-14743 - test api respects search operators.
3646 *
3647 * @param int $version
3648 *
3649 * @dataProvider versionThreeAndFour
3650 */
3651 public function testGetCreatedDateByOperators($version) {
3652 $this->_apiversion = $version;
3653 $preExistingContactCount = CRM_Core_DAO::singleValueQuery('select count(*) FROM civicrm_contact');
3654 $contact1 = $this->individualCreate();
3655 $sql = "UPDATE civicrm_contact SET created_date = '2012-01-01' WHERE id = " . $contact1;
3656 CRM_Core_DAO::executeQuery($sql);
3657 $contact2 = $this->individualCreate();
3658 $sql = "UPDATE civicrm_contact SET created_date = '2012-02-01' WHERE id = " . $contact2;
3659 CRM_Core_DAO::executeQuery($sql);
3660 $contact3 = $this->householdCreate();
3661 $sql = "UPDATE civicrm_contact SET created_date = '2012-03-01' WHERE id = " . $contact3;
3662 CRM_Core_DAO::executeQuery($sql);
3663 $contacts = $this->callAPISuccess('contact', 'get', ['created_date' => ['<' => '2014-01-01']]);
3664 $this->assertEquals($contacts['count'], 3);
3665 $contacts = $this->callAPISuccess('contact', 'get', ['created_date' => ['>' => '2014-01-01']]);
3666 $this->assertEquals($contacts['count'], $preExistingContactCount);
3667 }
3668
3669 /**
3670 * CRM-14263 check that API is not affected by search profile related bug.
3671 */
3672 public function testReturnCityProfile() {
3673 $contactID = $this->individualCreate();
3674 CRM_Core_Config::singleton()->defaultSearchProfileID = 1;
3675 $this->callAPISuccess('address', 'create', [
3676 'contact_id' => $contactID,
3677 'city' => 'Cool City',
3678 'location_type_id' => 1,
3679 ]);
3680 $result = $this->callAPISuccess('contact', 'get', ['city' => 'Cool City', 'return' => 'contact_type']);
3681 $this->assertEquals(1, $result['count']);
3682 }
3683
3684 /**
3685 * CRM-15443 - ensure getlist api does not return deleted contacts.
3686 *
3687 * @throws \CRM_Core_Exception
3688 */
3689 public function testGetlistExcludeConditions() {
3690 $name = 'Scarabée';
3691 $contact = $this->individualCreate(['last_name' => $name]);
3692 $this->individualCreate(['last_name' => $name, 'is_deceased' => 1]);
3693 $this->individualCreate(['last_name' => $name, 'is_deleted' => 1]);
3694 // We should get all but the deleted contact.
3695 $result = $this->callAPISuccess('contact', 'getlist', ['input' => $name]);
3696 $this->assertEquals(2, $result['count']);
3697 // Force-exclude the deceased contact.
3698 $result = $this->callAPISuccess('contact', 'getlist', [
3699 'input' => $name,
3700 'params' => ['is_deceased' => 0],
3701 ]);
3702 $this->assertEquals(1, $result['count']);
3703 $this->assertEquals($contact, $result['values'][0]['id']);
3704 }
3705
3706 /**
3707 * Test contact getactions.
3708 */
3709 public function testGetActions() {
3710 $description = "Getting the available actions for an entity.";
3711 $result = $this->callAPIAndDocument($this->_entity, 'getactions', [], __FUNCTION__, __FILE__, $description);
3712 $expected = [
3713 'create',
3714 'delete',
3715 'get',
3716 'getactions',
3717 'getcount',
3718 'getfields',
3719 'getlist',
3720 'getoptions',
3721 'getquick',
3722 'getrefcount',
3723 'getsingle',
3724 'getvalue',
3725 'merge',
3726 'proximity',
3727 'replace',
3728 'setvalue',
3729 'update',
3730 ];
3731 $deprecated = [
3732 'update',
3733 'getquick',
3734 ];
3735 foreach ($expected as $action) {
3736 $this->assertTrue(in_array($action, $result['values']), "Expected action $action");
3737 }
3738 foreach ($deprecated as $action) {
3739 $this->assertArrayKeyExists($action, $result['deprecated']);
3740 }
3741 }
3742
3743 /**
3744 * Test the duplicate check function.
3745 */
3746 public function testDuplicateCheck() {
3747 $harry = [
3748 'first_name' => 'Harry',
3749 'last_name' => 'Potter',
3750 'email' => 'harry@hogwarts.edu',
3751 'contact_type' => 'Individual',
3752 ];
3753 $this->callAPISuccess('Contact', 'create', $harry);
3754 $result = $this->callAPISuccess('Contact', 'duplicatecheck', [
3755 'match' => $harry,
3756 ]);
3757
3758 $this->assertEquals(1, $result['count']);
3759 $result = $this->callAPISuccess('Contact', 'duplicatecheck', [
3760 'match' => [
3761 'first_name' => 'Harry',
3762 'last_name' => 'Potter',
3763 'email' => 'no5@privet.drive',
3764 'contact_type' => 'Individual',
3765 ],
3766 ]);
3767 $this->assertEquals(0, $result['count']);
3768 $this->callAPIFailure('Contact', 'create', array_merge($harry, ['dupe_check' => 1]));
3769 }
3770
3771 /**
3772 * Test the duplicate check function.
3773 */
3774 public function testDuplicateCheckRuleNotReserved() {
3775 $harry = [
3776 'first_name' => 'Harry',
3777 'last_name' => 'Potter',
3778 'email' => 'harry@hogwarts.edu',
3779 'contact_type' => 'Individual',
3780 ];
3781 $defaultRule = $this->callAPISuccess('RuleGroup', 'getsingle', ['used' => 'Unsupervised', 'is_reserved' => 1]);
3782 $this->callAPISuccess('RuleGroup', 'create', ['id' => $defaultRule['id'], 'is_reserved' => 0]);
3783 $this->callAPISuccess('Contact', 'create', $harry);
3784 $result = $this->callAPISuccess('Contact', 'duplicatecheck', [
3785 'match' => $harry,
3786 ]);
3787
3788 $this->assertEquals(1, $result['count']);
3789 $this->callAPISuccess('RuleGroup', 'create', ['id' => $defaultRule['id'], 'is_reserved' => 1]);
3790 }
3791
3792 /**
3793 * Test variants on retrieving contact by type.
3794 */
3795 public function testGetByContactType() {
3796 $individual = $this->callAPISuccess('Contact', 'create', [
3797 'email' => 'individual@test.com',
3798 'contact_type' => 'Individual',
3799 ]);
3800 $household = $this->callAPISuccess('Contact', 'create', [
3801 'household_name' => 'household@test.com',
3802 'contact_type' => 'Household',
3803 ]);
3804 $organization = $this->callAPISuccess('Contact', 'create', [
3805 'organization_name' => 'organization@test.com',
3806 'contact_type' => 'Organization',
3807 ]);
3808 // Test with id - getsingle will throw an exception if not found
3809 $this->callAPISuccess('Contact', 'getsingle', [
3810 'id' => $individual['id'],
3811 'contact_type' => 'Individual',
3812 ]);
3813 $this->callAPISuccess('Contact', 'getsingle', [
3814 'id' => $individual['id'],
3815 'contact_type' => ['IN' => ['Individual']],
3816 'return' => 'id',
3817 ]);
3818 $this->callAPISuccess('Contact', 'getsingle', [
3819 'id' => $organization['id'],
3820 'contact_type' => ['IN' => ['Individual', 'Organization']],
3821 ]);
3822 // Test as array
3823 $result = $this->callAPISuccess('Contact', 'get', [
3824 'contact_type' => ['IN' => ['Individual', 'Organization']],
3825 'options' => ['limit' => 0],
3826 'return' => 'id',
3827 ]);
3828 $this->assertContains($organization['id'], array_keys($result['values']));
3829 $this->assertContains($individual['id'], array_keys($result['values']));
3830 $this->assertNotContains($household['id'], array_keys($result['values']));
3831 // Test as string
3832 $result = $this->callAPISuccess('Contact', 'get', [
3833 'contact_type' => 'Household',
3834 'options' => ['limit' => 0],
3835 'return' => 'id',
3836 ]);
3837 $this->assertNotContains($organization['id'], array_keys($result['values']));
3838 $this->assertNotContains($individual['id'], array_keys($result['values']));
3839 $this->assertContains($household['id'], array_keys($result['values']));
3840 }
3841
3842 /**
3843 * Test merging 2 contacts.
3844 *
3845 * Someone kindly bequethed us the legacy of mixed up use of main_id & other_id
3846 * in the params for contact.merge api.
3847 *
3848 * This test protects that legacy.
3849 */
3850 public function testMergeBizzareOldParams() {
3851 $this->createLoggedInUser();
3852 $otherContact = $this->callAPISuccess('contact', 'create', $this->_params);
3853 $mainContact = $this->callAPISuccess('contact', 'create', $this->_params);
3854 $this->callAPISuccess('contact', 'merge', [
3855 'main_id' => $mainContact['id'],
3856 'other_id' => $otherContact['id'],
3857 ]);
3858 $contacts = $this->callAPISuccess('contact', 'get', $this->_params);
3859 $this->assertEquals($otherContact['id'], $contacts['id']);
3860 }
3861
3862 /**
3863 * Test merging 2 contacts.
3864 */
3865 public function testMerge() {
3866 $this->createLoggedInUser();
3867 $otherContact = $this->callAPISuccess('contact', 'create', $this->_params);
3868 $retainedContact = $this->callAPISuccess('contact', 'create', $this->_params);
3869 $this->callAPISuccess('contact', 'merge', [
3870 'to_keep_id' => $retainedContact['id'],
3871 'to_remove_id' => $otherContact['id'],
3872 'auto_flip' => FALSE,
3873 ]);
3874
3875 $contacts = $this->callAPISuccess('contact', 'get', $this->_params);
3876 $this->assertEquals($retainedContact['id'], $contacts['id']);
3877 $activity = $this->callAPISuccess('Activity', 'getsingle', [
3878 'target_contact_id' => $retainedContact['id'],
3879 'activity_type_id' => 'Contact Merged',
3880 ]);
3881 $this->assertEquals(date('Y-m-d'), date('Y-m-d', strtotime($activity['activity_date_time'])));
3882 $activity2 = $this->callAPISuccess('Activity', 'getsingle', [
3883 'target_contact_id' => $otherContact['id'],
3884 'activity_type_id' => 'Contact Deleted by Merge',
3885 ]);
3886 $this->assertEquals($activity['id'], $activity2['parent_id']);
3887 $this->assertEquals('Normal', civicrm_api3('option_value', 'getvalue', [
3888 'value' => $activity['priority_id'],
3889 'return' => 'label',
3890 'option_group_id' => 'priority',
3891 ]));
3892
3893 }
3894
3895 /**
3896 * Test merging 2 contacts with custom fields.
3897 *
3898 * @throws \API_Exception
3899 * @throws \CRM_Core_Exception
3900 * @throws \Civi\API\Exception\UnauthorizedException
3901 */
3902 public function testMergeCustomFields() {
3903 $contact1 = $this->individualCreate();
3904 // Not sure this is quite right but it does get it into the file table
3905 $file = $this->callAPISuccess('Attachment', 'create', [
3906 'name' => 'header.txt',
3907 'mime_type' => 'text/plain',
3908 'description' => 'My test description',
3909 'content' => 'My test content',
3910 'entity_table' => 'civicrm_contact',
3911 'entity_id' => $contact1,
3912 ]);
3913
3914 $this->createCustomGroupWithFieldsOfAllTypes();
3915 $fileField = $this->getCustomFieldName('file');
3916 $linkField = $this->getCustomFieldName('link');
3917 $dateField = $this->getCustomFieldName('select_date');
3918 $selectField = $this->getCustomFieldName('select_string');
3919 $countryField = $this->getCustomFieldName('country');
3920 $referenceField = $this->getCustomFieldName('contact_reference');
3921
3922 $countriesByName = array_flip(CRM_Core_PseudoConstant::country(FALSE, FALSE));
3923 $customFieldValues = [
3924 // @todo fix the fatal bug on this & uncomment - see dev/core#723
3925 $fileField => $file['id'],
3926 $linkField => 'http://example.org',
3927 $dateField => '2018-01-01 17:10:56',
3928 $selectField => 'G',
3929 $countryField => $countriesByName['New Zealand'],
3930 $referenceField => $this->householdCreate(),
3931 ];
3932 $this->callAPISuccess('Contact', 'create', array_merge([
3933 'id' => $contact1,
3934 ], $customFieldValues));
3935
3936 $contact2 = $this->individualCreate();
3937 $this->callAPISuccess('contact', 'merge', [
3938 'to_keep_id' => $contact2,
3939 'to_remove_id' => $contact1,
3940 'auto_flip' => FALSE,
3941 ]);
3942 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact2, 'return' => array_keys($customFieldValues)]);
3943 $this->assertEquals($contact2, CRM_Core_DAO::singleValueQuery('SELECT entity_id FROM civicrm_entity_file WHERE file_id = ' . $file['id']));
3944 foreach ($customFieldValues as $key => $value) {
3945 $this->assertEquals($value, $contact[$key]);
3946 }
3947 }
3948
3949 /**
3950 * Test retrieving merged contacts.
3951 *
3952 * The goal here is to start with a contact deleted by merged and find out the contact that is the current version of them.
3953 *
3954 * @throws \CRM_Core_Exception
3955 */
3956 public function testMergedGet() {
3957 $this->contactIDs[] = $this->individualCreate();
3958 $this->contactIDs[] = $this->individualCreate();
3959 $this->contactIDs[] = $this->individualCreate();
3960 $this->contactIDs[] = $this->individualCreate();
3961
3962 // First do an 'unnatural merge' - they 'like to merge into the lowest but this will mean that contact 0 merged to contact [3].
3963 // When the batch merge runs.... the new lowest contact is contact[1]. All contacts will merge into that contact,
3964 // including contact[3], resulting in only 3 existing at the end. For each contact the correct answer to 'who did I eventually
3965 // wind up being should be [1]
3966 $this->callAPISuccess('Contact', 'merge', ['to_remove_id' => $this->contactIDs[0], 'to_keep_id' => $this->contactIDs[3]]);
3967
3968 $this->callAPISuccess('Job', 'process_batch_merge', []);
3969 foreach ($this->contactIDs as $contactID) {
3970 if ($contactID === $this->contactIDs[1]) {
3971 continue;
3972 }
3973 $result = $this->callAPIAndDocument('Contact', 'getmergedto', ['sequential' => 1, 'contact_id' => $contactID], __FUNCTION__, __FILE__);
3974 $this->assertEquals(1, $result['count']);
3975 $this->assertEquals($this->contactIDs[1], $result['values'][0]['id']);
3976 }
3977
3978 $result = $this->callAPIAndDocument('Contact', 'getmergedfrom', ['contact_id' => $this->contactIDs[1]], __FUNCTION__, __FILE__)['values'];
3979 $mergedContactIds = array_merge(array_diff($this->contactIDs, [$this->contactIDs[1]]));
3980 $this->assertEquals($mergedContactIds, array_keys($result));
3981 }
3982
3983 /**
3984 * Test merging 2 contacts with delete to trash off.
3985 *
3986 * We are checking that there is no error due to attempting to add an activity for the
3987 * deleted contact.
3988 *
3989 * CRM-18307
3990 */
3991 public function testMergeNoTrash() {
3992 $this->createLoggedInUser();
3993 $this->callAPISuccess('Setting', 'create', ['contact_undelete' => FALSE]);
3994 $otherContact = $this->callAPISuccess('contact', 'create', $this->_params);
3995 $retainedContact = $this->callAPISuccess('contact', 'create', $this->_params);
3996 $this->callAPISuccess('contact', 'merge', [
3997 'to_keep_id' => $retainedContact['id'],
3998 'to_remove_id' => $otherContact['id'],
3999 'auto_flip' => FALSE,
4000 ]);
4001 $this->callAPISuccess('Setting', 'create', ['contact_undelete' => TRUE]);
4002 }
4003
4004 /**
4005 * Ensure format with return=group shows comma-separated group IDs.
4006 *
4007 * CRM-19426
4008 */
4009 public function testContactGetReturnGroup() {
4010 // Set up a contact, asser that they were created.
4011 $contact_params = [
4012 'contact_type' => 'Individual',
4013 'first_name' => 'Test',
4014 'last_name' => 'Groupmember',
4015 'email' => 'test@example.org',
4016 ];
4017 $create_contact = $this->callApiSuccess('Contact', 'create', $contact_params);
4018 $this->assertEquals(0, $create_contact['is_error']);
4019 $this->assertInternalType('int', $create_contact['id']);
4020
4021 $created_contact_id = $create_contact['id'];
4022
4023 // Set up multiple groups, add the contact to the groups.
4024 $test_groups = ['Test group A', 'Test group B'];
4025 foreach ($test_groups as $title) {
4026 // Use this contact as group owner, since we know they exist.
4027 $group_params = [
4028 'title' => $title,
4029 'created_id' => $created_contact_id,
4030 ];
4031 $create_group = $this->callApiSuccess('Group', 'create', $group_params);
4032 $this->assertEquals(0, $create_group['is_error']);
4033 $this->assertInternalType('int', $create_group['id']);
4034
4035 $created_group_ids[] = $create_group['id'];
4036
4037 // Add contact to the new group.
4038 $group_contact_params = [
4039 'contact_id' => $created_contact_id,
4040 'group_id' => $create_group['id'],
4041 ];
4042 $create_group_contact = $this->callApiSuccess('GroupContact', 'create', $group_contact_params);
4043 $this->assertEquals(0, $create_group_contact['is_error']);
4044 $this->assertInternalType('int', $create_group_contact['added']);
4045 }
4046
4047 // Use the Contact,get API to retrieve the contact
4048 $contact_get_params = [
4049 'id' => $created_contact_id,
4050 'return' => 'group',
4051 ];
4052 $contact_get = $this->callApiSuccess('Contact', 'get', $contact_get_params);
4053 $this->assertInternalType('array', $contact_get['values'][$created_contact_id]);
4054 $this->assertInternalType('string', $contact_get['values'][$created_contact_id]['groups']);
4055
4056 // Ensure they are shown as being in each created group.
4057 $contact_group_ids = explode(',', $contact_get['values'][$created_contact_id]['groups']);
4058 foreach ($created_group_ids as $created_group_id) {
4059 $this->assertContains($created_group_id, $contact_group_ids);
4060 }
4061 }
4062
4063 /**
4064 * CRM-20144 Verify that passing title of group works as well as id
4065 * Tests the following formats
4066 * contact.get group='title1'
4067 * contact.get group=id1
4068 */
4069 public function testContactGetWithGroupTitle() {
4070 // Set up a contact, asser that they were created.
4071 $contact_params = [
4072 'contact_type' => 'Individual',
4073 'first_name' => 'Test2',
4074 'last_name' => 'Groupmember',
4075 'email' => 'test@example.org',
4076 ];
4077 $create_contact = $this->callApiSuccess('Contact', 'create', $contact_params);
4078 $created_contact_id = $create_contact['id'];
4079 // Set up multiple groups, add the contact to the groups.
4080 $test_groups = ['Test group C', 'Test group D'];
4081 foreach ($test_groups as $title) {
4082 $group_params = [
4083 'title' => $title,
4084 'created_id' => $created_contact_id,
4085 ];
4086 $create_group = $this->callApiSuccess('Group', 'create', $group_params);
4087 $created_group_id = $create_group['id'];
4088
4089 // Add contact to the new group.
4090 $group_contact_params = [
4091 'contact_id' => $created_contact_id,
4092 'group_id' => $create_group['id'],
4093 ];
4094 $this->callApiSuccess('GroupContact', 'create', $group_contact_params);
4095 $contact_get = $this->callAPISuccess('contact', 'get', ['group' => $title, 'return' => 'group']);
4096 $this->assertEquals(1, $contact_get['count']);
4097 $this->assertEquals($created_contact_id, $contact_get['id']);
4098 $contact_groups = explode(',', $contact_get['values'][$created_contact_id]['groups']);
4099 $this->assertContains((string) $create_group['id'], $contact_groups);
4100 $contact_get2 = $this->callAPISuccess('contact', 'get', ['group' => $created_group_id, 'return' => 'group']);
4101 $this->assertEquals($created_contact_id, $contact_get2['id']);
4102 $contact_groups2 = explode(',', $contact_get2['values'][$created_contact_id]['groups']);
4103 $this->assertContains((string) $create_group['id'], $contact_groups2);
4104 $this->callAPISuccess('group', 'delete', ['id' => $created_group_id]);
4105 }
4106 $this->callAPISuccess('contact', 'delete', ['id' => $created_contact_id, 'skip_undelete' => TRUE]);
4107 }
4108
4109 /**
4110 * CRM-20144 Verify that passing title of group works as well as id
4111 * Tests the following formats
4112 * contact.get group=array('title1', title1)
4113 * contact.get group=array('IN' => array('title1', 'title2)
4114 */
4115 public function testContactGetWithGroupTitleMultipleGroups() {
4116 $description = "Get all from group and display contacts.";
4117 $subFile = "GroupFilterUsingContactAPI";
4118 // Set up a contact, asser that they were created.
4119 $contact_params = [
4120 'contact_type' => 'Individual',
4121 'first_name' => 'Test2',
4122 'last_name' => 'Groupmember',
4123 'email' => 'test@example.org',
4124 ];
4125 $create_contact = $this->callApiSuccess('Contact', 'create', $contact_params);
4126 $created_contact_id = $create_contact['id'];
4127 $createdGroupsTitles = $createdGroupsIds = [];
4128 // Set up multiple groups, add the contact to the groups.
4129 $test_groups = ['Test group C', 'Test group D'];
4130 foreach ($test_groups as $title) {
4131 $group_params = [
4132 'title' => $title,
4133 'created_id' => $created_contact_id,
4134 ];
4135 $create_group = $this->callApiSuccess('Group', 'create', $group_params);
4136 $created_group_id = $create_group['id'];
4137 $createdGroupsIds[] = $create_group['id'];
4138 $createdGroupTitles[] = $title;
4139 // Add contact to the new group.
4140 $group_contact_params = [
4141 'contact_id' => $created_contact_id,
4142 'group_id' => $create_group['id'],
4143 ];
4144 $create_group_contact = $this->callApiSuccess('GroupContact', 'create', $group_contact_params);
4145 }
4146 $contact_get = $this->callAPISuccess('contact', 'get', ['group' => $createdGroupTitles, 'return' => 'group']);
4147 $this->assertEquals(1, $contact_get['count']);
4148 $this->assertEquals($created_contact_id, $contact_get['id']);
4149 $contact_groups = explode(',', $contact_get['values'][$created_contact_id]['groups']);
4150 foreach ($createdGroupsIds as $id) {
4151 $this->assertContains((string) $id, $contact_groups);
4152 }
4153 $contact_get2 = $this->callAPIAndDocument('contact', 'get', ['group' => ['IN' => $createdGroupTitles]], __FUNCTION__, __FILE__, $description, $subFile);
4154 $contact_get2 = $this->callAPISuccess('contact', 'get', ['group' => ['IN' => $createdGroupTitles], 'return' => 'group']);
4155 $this->assertEquals($created_contact_id, $contact_get2['id']);
4156 $contact_groups2 = explode(',', $contact_get2['values'][$created_contact_id]['groups']);
4157 foreach ($createdGroupsIds as $id) {
4158 $this->assertContains((string) $id, $contact_groups2);
4159 }
4160 foreach ($createdGroupsIds as $id) {
4161 $this->callAPISuccess('group', 'delete', ['id' => $id]);
4162 }
4163 $this->callAPISuccess('contact', 'delete', ['id' => $created_contact_id, 'skip_undelete' => TRUE]);
4164 }
4165
4166 /**
4167 * CRM-20144 Verify that passing title of group works as well as id
4168 * Tests the following formats
4169 * contact.get group=array('title1' => 1)
4170 * contact.get group=array('titke1' => 1, 'title2' => 1)
4171 * contact.get group=array('id1' => 1)
4172 * contact.get group=array('id1' => 1, id2 => 1)
4173 */
4174 public function testContactGetWithGroupTitleMultipleGroupsLegacyFormat() {
4175 // Set up a contact, asser that they were created.
4176 $contact_params = [
4177 'contact_type' => 'Individual',
4178 'first_name' => 'Test2',
4179 'last_name' => 'Groupmember',
4180 'email' => 'test@example.org',
4181 ];
4182 $create_contact = $this->callApiSuccess('Contact', 'create', $contact_params);
4183 $created_contact_id = $create_contact['id'];
4184 $createdGroupsTitles = $createdGroupsIds = [];
4185 // Set up multiple groups, add the contact to the groups.
4186 $test_groups = ['Test group C', 'Test group D'];
4187 foreach ($test_groups as $title) {
4188 $group_params = [
4189 'title' => $title,
4190 'created_id' => $created_contact_id,
4191 ];
4192 $create_group = $this->callApiSuccess('Group', 'create', $group_params);
4193 $created_group_id = $create_group['id'];
4194 $createdGroupsIds[] = $create_group['id'];
4195 $createdGroupTitles[] = $title;
4196 // Add contact to the new group.
4197 $group_contact_params = [
4198 'contact_id' => $created_contact_id,
4199 'group_id' => $create_group['id'],
4200 ];
4201 $create_group_contact = $this->callApiSuccess('GroupContact', 'create', $group_contact_params);
4202 }
4203 $contact_get = $this->callAPISuccess('contact', 'get', ['group' => [$createdGroupTitles[0] => 1], 'return' => 'group']);
4204 $this->assertEquals(1, $contact_get['count']);
4205 $this->assertEquals($created_contact_id, $contact_get['id']);
4206 $contact_groups = explode(',', $contact_get['values'][$created_contact_id]['groups']);
4207 foreach ($createdGroupsIds as $id) {
4208 $this->assertContains((string) $id, $contact_groups);
4209 }
4210 $contact_get2 = $this->callAPISuccess('contact', 'get', ['group' => [$createdGroupTitles[0] => 1, $createdGroupTitles[1] => 1], 'return' => 'group']);
4211 $this->assertEquals(1, $contact_get2['count']);
4212 $this->assertEquals($created_contact_id, $contact_get2['id']);
4213 $contact_groups2 = explode(',', $contact_get2['values'][$created_contact_id]['groups']);
4214 foreach ($createdGroupsIds as $id) {
4215 $this->assertContains((string) $id, $contact_groups2);
4216 }
4217 $contact_get3 = $this->callAPISuccess('contact', 'get', ['group' => [$createdGroupsIds[0] => 1], 'return' => 'group']);
4218 $this->assertEquals($created_contact_id, $contact_get3['id']);
4219 $contact_groups3 = explode(',', $contact_get3['values'][$created_contact_id]['groups']);
4220 foreach ($createdGroupsIds as $id) {
4221 $this->assertContains((string) $id, $contact_groups3);
4222 }
4223 $contact_get4 = $this->callAPISuccess('contact', 'get', ['group' => [$createdGroupsIds[0] => 1, $createdGroupsIds[1] => 1], 'return' => 'group']);
4224 $this->assertEquals($created_contact_id, $contact_get4['id']);
4225 $contact_groups4 = explode(',', $contact_get4['values'][$created_contact_id]['groups']);
4226 foreach ($createdGroupsIds as $id) {
4227 $this->assertContains((string) $id, $contact_groups4);
4228 }
4229 foreach ($createdGroupsIds as $id) {
4230 $this->callAPISuccess('group', 'delete', ['id' => $id]);
4231 }
4232 $this->callAPISuccess('contact', 'delete', ['id' => $created_contact_id, 'skip_undelete' => TRUE]);
4233 }
4234
4235 /**
4236 * Test the prox_distance functionality works.
4237 *
4238 * This is primarily testing functionality in the BAO_Query object that 'happens to be'
4239 * accessible via the api.
4240 */
4241 public function testContactGetProximity() {
4242 CRM_Core_Config::singleton()->geocodeMethod = 'CRM_Utils_MockGeocoder';
4243 $this->individualCreate();
4244 $contactID = $this->individualCreate();
4245 $this->callAPISuccess('Address', 'create', [
4246 'contact_id' => $contactID,
4247 'is_primary' => 1,
4248 'city' => 'Whangarei',
4249 'street_address' => 'Dent St',
4250 'geo_code_1' => '-35.8743325',
4251 'geo_code_2' => '174.4567136',
4252 'location_type_id' => 'Home',
4253 ]);
4254 $contact = $this->callAPISuccess('Contact', 'get', [
4255 'prox_distance' => 100,
4256 'prox_geo_code_1' => '-35.72192',
4257 'prox_geo_code_2' => '174.32034',
4258 ]);
4259 $this->assertEquals(1, $contact['count']);
4260 $this->assertEquals($contactID, $contact['id']);
4261 }
4262
4263 public function testLoggedInUserAPISupportToken() {
4264 $description = "Get contact id of the current logged in user";
4265 $subFile = "ContactIDOfLoggedInUserContactAPI";
4266 $cid = $this->createLoggedInUser();
4267 $contact = $this->callAPIAndDocument('contact', 'get', ['id' => 'user_contact_id'], __FUNCTION__, __FILE__, $description, $subFile);
4268 $this->assertEquals($cid, $contact['id']);
4269 }
4270
4271 /**
4272 * @param $groupID
4273 * @param $contact
4274 */
4275 protected function putGroupContactCacheInClearableState($groupID, $contact) {
4276 // We need to force the situation where there is invalid data in the cache and it
4277 // is due to be cleared.
4278 CRM_Core_DAO::executeQuery("
4279 INSERT INTO civicrm_group_contact_cache (group_id, contact_id)
4280 VALUES ({$groupID}, {$contact['id']})
4281 ");
4282 CRM_Core_DAO::executeQuery("UPDATE civicrm_group SET cache_date = '2017-01-01'");
4283 // Reset so it does not skip.
4284 Civi::$statics['CRM_Contact_BAO_GroupContactCache']['is_refresh_init'] = FALSE;
4285 }
4286
4287 /**
4288 * CRM-21041 Test if 'communication style' is set to site default if not passed.
4289 *
4290 * @param int $version
4291 *
4292 * @dataProvider versionThreeAndFour
4293 * @throws \CRM_Core_Exception
4294 */
4295 public function testCreateCommunicationStyleUnset($version) {
4296 $this->_apiversion = $version;
4297 $this->callAPISuccess('Contact', 'create', [
4298 'first_name' => 'John',
4299 'last_name' => 'Doe',
4300 'contact_type' => 'Individual',
4301 ]);
4302 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Doe']);
4303 $this->assertEquals(1, $result['communication_style_id']);
4304 }
4305
4306 /**
4307 * CRM-21041 Test if 'communication style' is set if value is passed.
4308 *
4309 * @throws \CRM_Core_Exception
4310 * @throws \CiviCRM_API3_Exception
4311 */
4312 public function testCreateCommunicationStylePassed() {
4313 $this->callAPISuccess('Contact', 'create', [
4314 'first_name' => 'John',
4315 'last_name' => 'Doe',
4316 'contact_type' => 'Individual',
4317 'communication_style_id' => 'Familiar',
4318 ]);
4319 $result = $this->callAPISuccessGetSingle('Contact', ['last_name' => 'Doe']);
4320 $params = [
4321 'option_group_id' => 'communication_style',
4322 'label' => 'Familiar',
4323 'return' => 'value',
4324 ];
4325 $optionResult = civicrm_api3('OptionValue', 'get', $params);
4326 $communicationStyle = reset($optionResult['values']);
4327 $this->assertEquals($communicationStyle['value'], $result['communication_style_id']);
4328 }
4329
4330 /**
4331 * Test that creating a contact with various contact greetings works.
4332 *
4333 * @param int $version
4334 *
4335 * @dataProvider versionThreeAndFour
4336 * @throws \CRM_Core_Exception
4337 */
4338 public function testContactGreetingsCreate($version) {
4339 $this->_apiversion = $version;
4340 // Api v4 takes a return parameter like postal_greeting_display which matches the field.
4341 // v3 has a customised parameter 'postal_greeting'. The v4 parameter is more correct so
4342 // we will not change it to match v3. The keyString value allows the test to support both.
4343 $keyString = $version === 4 ? '_display' : '';
4344
4345 $contact = $this->callAPISuccess('Contact', 'create', ['first_name' => 'Alan', 'last_name' => 'MouseMouse', 'contact_type' => 'Individual']);
4346 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => 'postal_greeting' . $keyString]);
4347 $this->assertEquals('Dear Alan', $contact['postal_greeting_display']);
4348
4349 $contact = $this->callAPISuccess('Contact', 'create', ['id' => $contact['id'], 'postal_greeting_id' => 2]);
4350 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => 'postal_greeting' . $keyString]);
4351 $this->assertEquals('Dear Alan MouseMouse', $contact['postal_greeting_display']);
4352
4353 $contact = $this->callAPISuccess('Contact', 'create', ['organization_name' => 'Alan\'s Show', 'contact_type' => 'Organization']);
4354 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => "postal_greeting{$keyString}, addressee{$keyString}, email_greeting{$keyString}"]);
4355 $this->assertEquals('', $contact['postal_greeting_display']);
4356 $this->assertEquals('', $contact['email_greeting_display']);
4357 $this->assertEquals('Alan\'s Show', $contact['addressee_display']);
4358 }
4359
4360 /**
4361 * Test that creating a contact with various contact greetings works.
4362 *
4363 * @param int $version
4364 *
4365 * @dataProvider versionThreeAndFour
4366 *
4367 * @throws \CRM_Core_Exception
4368 */
4369 public function testContactGreetingsCreateWithCustomField($version) {
4370 $this->_apiversion = $version;
4371 // Api v4 takes a return parameter like postal_greeting_display which matches the field.
4372 // v3 has a customised parameter 'postal_greeting'. The v4 parameter is more correct so
4373 // we will not change it to match v3. The keyString value allows the test to support both.
4374 $keyString = $version === 4 ? '_display' : '';
4375
4376 $ids = $this->entityCustomGroupWithSingleFieldCreate(__FUNCTION__, __FILE__);
4377 $contact = $this->callAPISuccess('Contact', 'create', ['first_name' => 'Alan', 'contact_type' => 'Individual', 'custom_' . $ids['custom_field_id'] => 'Mice']);
4378
4379 // Change postal greeting to involve a custom field.
4380 $postalOption = $this->callAPISuccessGetSingle('OptionValue', ['option_group_id' => 'postal_greeting', 'filter' => 1, 'is_default' => 1]);
4381 $this->callAPISuccess('OptionValue', 'create', [
4382 'id' => $postalOption['id'],
4383 'name' => 'Dear {contact.first_name} {contact.custom_' . $ids['custom_field_id'] . '}',
4384 'label' => 'Dear {contact.first_name} {contact.custom_' . $ids['custom_field_id'] . '}',
4385 ]);
4386
4387 // Update contact & see if postal greeting now reflects the new string.
4388 $this->callAPISuccess('Contact', 'create', ['id' => $contact['id'], 'last_name' => 'MouseyMousey']);
4389 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => 'postal_greeting' . $keyString]);
4390 $this->assertEquals('Dear Alan Mice', $contact['postal_greeting_display']);
4391
4392 // Set contact to have no postal greeting & check it is correct.
4393 $this->callAPISuccess('Contact', 'create', ['id' => $contact['id'], 'postal_greeting_id' => 'null']);
4394 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contact['id'], 'return' => 'postal_greeting' . $keyString]);
4395 $this->assertEquals('', $contact['postal_greeting_display']);
4396
4397 //Cleanup
4398 $this->callAPISuccess('OptionValue', 'create', ['id' => $postalOption['id'], 'name' => 'Dear {contact.first_name}']);
4399 $this->customFieldDelete($ids['custom_field_id']);
4400 $this->customGroupDelete($ids['custom_group_id']);
4401 }
4402
4403 /**
4404 * Test that smarty variables are parsed if they exist in the greeting template.
4405 *
4406 * In this test we have both a Civi token & a Smarty token and we check both are processed.
4407 *
4408 * @param int $version
4409 *
4410 * @dataProvider versionThreeAndFour
4411 * @throws \CRM_Core_Exception
4412 */
4413 public function testGreetingParseSmarty($version) {
4414 $this->_apiversion = $version;
4415 // Api v4 takes a return parameter like postal_greeting_display which matches the field.
4416 // v3 has a customised parameter 'postal_greeting'. The v4 parameter is more correct so
4417 // we will not change it to match v3. The keyString value allows the test to support both.
4418 $keyString = $version === 4 ? '_display' : '';
4419 $postalOption = $this->callAPISuccessGetSingle('OptionValue', ['option_group_id' => 'postal_greeting', 'filter' => 1, 'is_default' => 1]);
4420 $this->callAPISuccess('OptionValue', 'create', [
4421 'id' => $postalOption['id'],
4422 'name' => "Dear {contact.first_name} {if \'{contact.first_name}\' === \'Tim\'}The Wise{/if}",
4423 'label' => "Dear {contact.first_name} {if '{contact.first_name}' === 'Tim'} The Wise{/if}",
4424 ]);
4425 $contactID = $this->individualCreate();
4426 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contactID, 'return' => 'postal_greeting' . $keyString]);
4427 $this->assertEquals('Dear Anthony', $contact['postal_greeting_display']);
4428
4429 $this->callAPISuccess('Contact', 'create', ['id' => $contactID, 'first_name' => 'Tim']);
4430 $contact = $this->callAPISuccessGetSingle('Contact', ['id' => $contactID, 'return' => 'postal_greeting' . $keyString]);
4431 $this->assertEquals('Dear Tim The Wise', $contact['postal_greeting_display']);
4432 }
4433
4434 /**
4435 * Test getunique api call for Contact entity
4436 */
4437 public function testContactGetUnique() {
4438 $result = $this->callAPIAndDocument($this->_entity, 'getunique', [], __FUNCTION__, __FILE__);
4439 $this->assertEquals(1, $result['count']);
4440 $this->assertEquals(['external_identifier'], $result['values']['UI_external_identifier']);
4441 }
4442
4443 /**
4444 * API test to retrieve contact from group having different group title and name.
4445 */
4446 public function testContactGetFromGroup() {
4447 $groupId = $this->groupCreate([
4448 'name' => 'Test_Group',
4449 'domain_id' => 1,
4450 'title' => 'New Test Group Created',
4451 'description' => 'New Test Group Created',
4452 'is_active' => 1,
4453 'visibility' => 'User and User Admin Only',
4454 ]);
4455 $contact = $this->callAPISuccess('contact', 'create', $this->_params);
4456 $groupContactCreateParams = [
4457 'contact_id' => $contact['id'],
4458 'group_id' => $groupId,
4459 'status' => 'Pending',
4460 ];
4461 $groupContact = $this->callAPISuccess('groupContact', 'create', $groupContactCreateParams);
4462 $groupGetContact = $this->CallAPISuccess('groupContact', 'get', $groupContactCreateParams);
4463 $this->CallAPISuccess('Contact', 'getcount', [
4464 'group' => "Test_Group",
4465 ]);
4466 }
4467
4468 /**
4469 * Test the related contacts filter.
4470 *
4471 * @throws \Exception
4472 */
4473 public function testSmartGroupsForRelatedContacts() {
4474 $rtype1 = $this->callAPISuccess('relationship_type', 'create', [
4475 "name_a_b" => uniqid() . " Child of",
4476 "name_b_a" => uniqid() . " Parent of",
4477 ]);
4478 $rtype2 = $this->callAPISuccess('relationship_type', 'create', [
4479 "name_a_b" => uniqid() . " Household Member of",
4480 "name_b_a" => uniqid() . " Household Member is",
4481 ]);
4482 $h1 = $this->householdCreate();
4483 $c1 = $this->individualCreate(['last_name' => 'Adams']);
4484 $c2 = $this->individualCreate(['last_name' => 'Adams']);
4485 $this->callAPISuccess('relationship', 'create', [
4486 'contact_id_a' => $c1,
4487 'contact_id_b' => $c2,
4488 'is_active' => 1,
4489 // Child of
4490 'relationship_type_id' => $rtype1['id'],
4491 ]);
4492 $this->callAPISuccess('relationship', 'create', [
4493 'contact_id_a' => $c1,
4494 'contact_id_b' => $h1,
4495 'is_active' => 1,
4496 // Household Member of
4497 'relationship_type_id' => $rtype2['id'],
4498 ]);
4499 $this->callAPISuccess('relationship', 'create', [
4500 'contact_id_a' => $c2,
4501 'contact_id_b' => $h1,
4502 'is_active' => 1,
4503 // Household Member of
4504 'relationship_type_id' => $rtype2['id'],
4505 ]);
4506
4507 $ssParams = [
4508 'form_values' => [
4509 // Child of
4510 'display_relationship_type' => $rtype1['id'] . '_a_b',
4511 'sort_name' => 'Adams',
4512 ],
4513 ];
4514 $g1ID = $this->smartGroupCreate($ssParams, ['name' => uniqid(), 'title' => uniqid()]);
4515 $ssParams = [
4516 'form_values' => [
4517 // Household Member of
4518 'display_relationship_type' => $rtype2['id'] . '_a_b',
4519 ],
4520 ];
4521 $g2ID = $this->smartGroupCreate($ssParams, ['name' => uniqid(), 'title' => uniqid()]);
4522 $ssParams = [
4523 'form_values' => [
4524 // Household Member is
4525 'display_relationship_type' => $rtype2['id'] . '_b_a',
4526 ],
4527 ];
4528 // the reverse of g2 which adds another layer for overlap at related contact filter
4529 $g3ID = $this->smartGroupCreate($ssParams, ['name' => uniqid(), 'title' => uniqid()]);
4530 CRM_Contact_BAO_GroupContactCache::loadAll();
4531 $this->callAPISuccessGetCount('contact', ['group' => $g1ID], 1);
4532 $this->callAPISuccessGetCount('contact', ['group' => $g2ID], 2);
4533 $this->callAPISuccessGetCount('contact', ['group' => $g3ID], 1);
4534 }
4535
4536 /**
4537 * Test creating a note from the contact.create API call when only passing the note as a string.
4538 */
4539 public function testCreateNoteinCreate() {
4540 $loggedInContactID = $this->createLoggedInUser();
4541 $this->_params['note'] = "Test note created by API Call as a String";
4542 $contact = $this->callAPISuccess('Contact', 'create', $this->_params);
4543 $note = $this->callAPISuccess('Note', 'get', ['contact_id' => $loggedInContactID]);
4544 $this->assertEquals($note['values'][$note['id']]['note'], "Test note created by API Call as a String");
4545 $note = $this->callAPISuccess('Note', 'get', ['entity_id' => $contact['id']]);
4546 $this->assertEquals($note['values'][$note['id']]['note'], "Test note created by API Call as a String");
4547 $this->callAPISuccess('Contact', 'delete', ['id' => $contact['id'], 'skip_undelete' => TRUE]);
4548 }
4549
4550 /**
4551 * Test Creating a note from the contact.create api call when passing the note params as an array.
4552 */
4553 public function testCreateNoteinCreateArrayFormat() {
4554 $contact1 = $this->callAPISuccess('Contact', 'create', ['first_name' => 'Alan', 'last_name' => 'MouseMouse', 'contact_type' => 'Individual']);
4555 $this->_params['note'] = [['note' => "Test note created by API Call as array", 'contact_id' => $contact1['id']]];
4556 $contact2 = $this->callAPISuccess('Contact', 'create', $this->_params);
4557 $note = $this->callAPISuccess('Note', 'get', ['contact_id' => $contact1['id']]);
4558 $this->assertEquals($note['values'][$note['id']]['note'], "Test note created by API Call as array");
4559 $note = $this->callAPISuccess('Note', 'get', ['entity_id' => $contact2['id']]);
4560 $this->assertEquals($note['values'][$note['id']]['note'], "Test note created by API Call as array");
4561 }
4562
4563 /**
4564 * Verify that passing tag IDs to Contact.get works
4565 *
4566 * Tests the following formats
4567 * - Contact.get tag='id1'
4568 * - Contact.get tag='id1,id2'
4569 * - Contact.get tag='id1, id2'
4570 */
4571 public function testContactGetWithTag() {
4572 $contact = $this->callApiSuccess('Contact', 'create', [
4573 'contact_type' => 'Individual',
4574 'first_name' => 'Test',
4575 'last_name' => 'Tagged',
4576 'email' => 'test@example.org',
4577 ]);
4578 $tags = [];
4579 foreach (['Tag A', 'Tag B'] as $name) {
4580 $tags[] = $this->callApiSuccess('Tag', 'create', [
4581 'name' => $name,
4582 ]);
4583 }
4584
4585 // assign contact to "Tag B"
4586 $this->callApiSuccess('EntityTag', 'create', [
4587 'entity_table' => 'civicrm_contact',
4588 'entity_id' => $contact['id'],
4589 'tag_id' => $tags[1]['id'],
4590 ]);
4591
4592 // test format Contact.get tag='id1'
4593 $contact_get = $this->callAPISuccess('Contact', 'get', [
4594 'tag' => $tags[1]['id'],
4595 'return' => 'tag',
4596 ]);
4597 $this->assertEquals(1, $contact_get['count']);
4598 $this->assertEquals($contact['id'], $contact_get['id']);
4599 $this->assertEquals('Tag B', $contact_get['values'][$contact['id']]['tags']);
4600
4601 // test format Contact.get tag='id1,id2'
4602 $contact_get = $this->callAPISuccess('Contact', 'get', [
4603 'tag' => $tags[0]['id'] . ',' . $tags[1]['id'],
4604 'return' => 'tag',
4605 ]);
4606 $this->assertEquals(1, $contact_get['count']);
4607 $this->assertEquals($contact['id'], $contact_get['id']);
4608 $this->assertEquals('Tag B', $contact_get['values'][$contact['id']]['tags']);
4609
4610 // test format Contact.get tag='id1, id2'
4611 $contact_get = $this->callAPISuccess('Contact', 'get', [
4612 'tag' => $tags[0]['id'] . ', ' . $tags[1]['id'],
4613 'return' => 'tag',
4614 ]);
4615 $this->assertEquals(1, $contact_get['count']);
4616 $this->assertEquals($contact['id'], $contact_get['id']);
4617 $this->assertEquals('Tag B', $contact_get['values'][$contact['id']]['tags']);
4618
4619 foreach ($tags as $tag) {
4620 $this->callAPISuccess('Tag', 'delete', ['id' => $tag['id']]);
4621 }
4622 $this->callAPISuccess('Contact', 'delete', [
4623 'id' => $contact['id'],
4624 'skip_undelete' => TRUE,
4625 ]);
4626 }
4627
4628 /**
4629 * Create pair of contacts with multiple conflicts.
4630 *
4631 * @return array
4632 *
4633 * @throws \CRM_Core_Exception
4634 */
4635 protected function createDeeplyConflictedContacts(): array {
4636 $this->createCustomGroupWithFieldOfType();
4637 $contact1 = $this->individualCreate([
4638 'email' => 'bob@example.com',
4639 'api.address.create' => ['location_type_id' => 'work', 'street_address' => 'big office', 'city' => 'small city'],
4640 'api.address.create.2' => ['location_type_id' => 'home', 'street_address' => 'big house', 'city' => 'small city'],
4641 'external_identifier' => 'unique and special',
4642 $this->getCustomFieldName('text') => 'mummy loves me',
4643 ]);
4644 $contact2 = $this->individualCreate([
4645 'first_name' => 'different',
4646 'api.address.create.1' => ['location_type_id' => 'home', 'street_address' => 'medium house', 'city' => 'small city'],
4647 'api.address.create.2' => ['location_type_id' => 'work', 'street_address' => 'medium office', 'city' => 'small city'],
4648 'external_identifier' => 'uniquer and specialler',
4649 'api.email.create' => ['location_type_id' => 'Other', 'email' => 'bob@example.com'],
4650 $this->getCustomFieldName('text') => 'mummy loves me more',
4651 ]);
4652 return [$contact1, $contact2];
4653 }
4654
4655 }