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