compare nonexistent array keys to 0 better
[civicrm-core.git] / tests / phpunit / CRM / Case / BAO / CaseTest.php
1 <?php
2
3 /**
4 * Class CRM_Case_BAO_CaseTest
5 * @group headless
6 */
7 class CRM_Case_BAO_CaseTest extends CiviUnitTestCase {
8
9 public function setUp() {
10 parent::setUp();
11
12 $this->tablesToTruncate = [
13 'civicrm_activity',
14 'civicrm_contact',
15 'civicrm_custom_group',
16 'civicrm_custom_field',
17 'civicrm_case',
18 'civicrm_case_contact',
19 'civicrm_case_activity',
20 'civicrm_case_type',
21 'civicrm_activity_contact',
22 'civicrm_managed',
23 'civicrm_relationship',
24 'civicrm_relationship_type',
25 ];
26
27 $this->quickCleanup($this->tablesToTruncate);
28
29 $this->loadAllFixtures();
30
31 CRM_Core_BAO_ConfigSetting::enableComponent('CiviCase');
32 }
33
34 /**
35 * Make sure that the latest case activity works accurately.
36 */
37 public function testCaseActivity() {
38 $userID = $this->createLoggedInUser();
39
40 $addTimeline = civicrm_api3('Case', 'addtimeline', [
41 'case_id' => 1,
42 'timeline' => "standard_timeline",
43 ]);
44
45 $query = CRM_Case_BAO_Case::getCaseActivityQuery('recent', $userID, ' civicrm_case.id IN( 1 )');
46 $res = CRM_Core_DAO::executeQuery($query);
47 $openCaseType = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Open Case');
48 while ($res->fetch()) {
49 $message = 'Failed asserting that the case activity query has a activity_type_id property:';
50 $this->assertObjectHasAttribute('activity_type_id', $res, $message . PHP_EOL . print_r($res, TRUE));
51 $message = 'Failed asserting that the latest activity from Case ID 1 was "Open Case":';
52 $this->assertEquals($openCaseType, $res->activity_type_id, $message . PHP_EOL . print_r($res, TRUE));
53 }
54 }
55
56 protected function tearDown() {
57 parent::tearDown();
58 $this->quickCleanup($this->tablesToTruncate, TRUE);
59 }
60
61 public function testAddCaseToContact() {
62 $params = [
63 'case_id' => 1,
64 'contact_id' => 17,
65 ];
66 CRM_Case_BAO_CaseContact::create($params);
67
68 $recent = CRM_Utils_Recent::get();
69 $this->assertEquals('Test Contact - Housing Support', $recent[0]['title']);
70 }
71
72 /**
73 * Create case role relationship between given contacts for provided case ID.
74 *
75 * @param $contactIdA
76 * @param $contactIdB
77 * @param $caseId
78 * @param bool $isActive
79 */
80 private function createCaseRoleRelationship($contactIdA, $contactIdB, $caseId, $isActive = TRUE) {
81 $relationshipType = $this->relationshipTypeCreate([
82 'contact_type_b' => 'Individual',
83 ]);
84
85 $this->callAPISuccess('Relationship', 'create', [
86 'contact_id_a' => $contactIdA,
87 'contact_id_b' => $contactIdB,
88 'relationship_type_id' => $relationshipType,
89 'case_id' => $caseId,
90 'is_active' => $isActive,
91 ]);
92 }
93
94 /**
95 * Asserts number of cases for given logged in user.
96 *
97 * @param $loggedInUser
98 * @param $caseId
99 * @param $caseCount
100 */
101 private function assertCasesOfUser($loggedInUser, $caseId, $caseCount) {
102 $summary = CRM_Case_BAO_Case::getCasesSummary(FALSE);
103 $upcomingCases = CRM_Case_BAO_Case::getCases(FALSE, [], 'dashboard', TRUE);
104 $caseRoles = CRM_Case_BAO_Case::getCaseRoles($loggedInUser, $caseId);
105
106 $this->assertEquals($caseCount, $upcomingCases, 'Upcoming case count must be ' . $caseCount);
107 if ($caseCount === 0) {
108 // If there really are 0 cases then there won't be any subelements for
109 // status and count, so we get a false error if we use the assertEquals
110 // check since it tries to get a subelement on type int. In this case
111 // the summary rows are just the case type pseudoconstant list.
112 $this->assertSame(array_flip(CRM_Case_PseudoConstant::caseType()), $summary['rows']);
113 }
114 else {
115 $this->assertEquals($caseCount, $summary['rows']['Housing Support']['Ongoing']['count'], 'Housing Support Ongoing case summary must be ' . $caseCount);
116 }
117 $this->assertEquals($caseCount, count($caseRoles), 'Total case roles for logged in users must be ' . $caseCount);
118 }
119
120 /**
121 * core/issue-1623: My Case dashlet doesn't sort by name but contact_id instead
122 *
123 * @throws \CRM_Core_Exception
124 */
125 public function testSortByCaseContact() {
126 // delete any cases if present
127 $this->callAPISuccess('Case', 'get', ['api.Case.delete' => ['id' => '$value.id']]);
128
129 // create three contacts with different name, later used in respective cases
130 $contacts = [
131 $this->individualCreate(['first_name' => 'Antonia', 'last_name' => 'D`souza']),
132 $this->individualCreate(['first_name' => 'Darric', 'last_name' => 'Roy']),
133 $this->individualCreate(['first_name' => 'Adam', 'last_name' => 'Pitt']),
134 ];
135 $loggedInUser = $this->createLoggedInUser();
136 $relationshipType = $this->relationshipTypeCreate([
137 'contact_type_b' => 'Individual',
138 ]);
139
140 // create cases for each contact
141 $cases = [];
142 foreach ($contacts as $contactID) {
143 $cases[] = $caseID = $this->createCase($contactID)->id;
144 $this->callAPISuccess('Relationship', 'create', [
145 'contact_id_a' => $contactID,
146 'contact_id_b' => $loggedInUser,
147 'relationship_type_id' => $relationshipType,
148 'case_id' => $caseID,
149 'is_active' => TRUE,
150 ]);
151 }
152
153 // USECASE A: fetch all cases using the AJAX fn without any sorting criteria, and match the result
154 global $_GET;
155 $_GET = [
156 'start' => 0,
157 'length' => 10,
158 'type' => 'any',
159 'all' => 1,
160 'is_unittest' => 1,
161 ];
162
163 $cases = [];
164 try {
165 CRM_Case_Page_AJAX::getCases();
166 }
167 catch (CRM_Core_Exception_PrematureExitException $e) {
168 $cases = $e->errorData['data'];
169 }
170
171 // list of expected sorted names in order the respective cases were created
172 $unsortedExpectedContactNames = [
173 'D`souza, Antonia',
174 'Roy, Darric',
175 'Pitt, Adam',
176 ];
177 $unsortedActualContactNames = CRM_Utils_Array::collect('sort_name', $cases);
178 foreach ($unsortedExpectedContactNames as $key => $name) {
179 // Something has changed recently that has exposed one of the problems with queries that are not full-groupby-compliant. Temporarily commenting this out until figure out what to do since this exact query doesn't seem to come up anywhere on common screens.
180 //$this->assertContains($name, $unsortedActualContactNames[$key]);
181 }
182
183 // USECASE B: fetch all cases using the AJAX fn based any 'Contact' sorting criteria, and match the result against expected sequence of names
184 $_GET = [
185 'start' => 0,
186 'length' => 10,
187 'type' => 'any',
188 'all' => 1,
189 'is_unittest' => 1,
190 'columns' => [
191 1 => [
192 'data' => 'sort_name',
193 'name' => NULL,
194 'searchable' => TRUE,
195 'orderable' => TRUE,
196 'search' => [
197 'value' => NULL,
198 'regex' => FALSE,
199 ],
200 ],
201 ],
202 'order' => [
203 [
204 'column' => 1,
205 'dir' => 'asc',
206 ],
207 ],
208 ];
209
210 $cases = [];
211 try {
212 CRM_Case_Page_AJAX::getCases();
213 }
214 catch (CRM_Core_Exception_PrematureExitException $e) {
215 $cases = $e->errorData['data'];
216 }
217
218 // list of expected sorted names in ASC order
219 $sortedExpectedContactNames = [
220 'D`souza, Antonia',
221 'Pitt, Adam',
222 'Roy, Darric',
223 ];
224 $sortedActualContactNames = CRM_Utils_Array::collect('sort_name', $cases);
225 foreach ($sortedExpectedContactNames as $key => $name) {
226 $this->assertContains($name, $sortedActualContactNames[$key]);
227 }
228 }
229
230 /**
231 * Test that Case count is exactly one for logged in user for user's active role.
232 *
233 * @throws \CRM_Core_Exception
234 */
235 public function testActiveCaseRole() {
236 $individual = $this->individualCreate();
237 $caseObj = $this->createCase($individual);
238 $caseId = $caseObj->id;
239 $loggedInUser = $this->createLoggedInUser();
240 $this->createCaseRoleRelationship($individual, $loggedInUser, $caseId);
241 $this->assertCasesOfUser($loggedInUser, $caseId, 1);
242 }
243
244 /**
245 * Test that case count is zero for logged in user for user's inactive role.
246 */
247 public function testInactiveCaseRole() {
248 $individual = $this->individualCreate();
249 $caseObj = $this->createCase($individual);
250 $caseId = $caseObj->id;
251 $loggedInUser = $this->createLoggedInUser();
252 $this->createCaseRoleRelationship($individual, $loggedInUser, $caseId, FALSE);
253 $this->assertCasesOfUser($loggedInUser, $caseId, 0);
254 }
255
256 public function testGetCaseType() {
257 $caseTypeLabel = CRM_Case_BAO_Case::getCaseType(1);
258 $this->assertEquals('Housing Support', $caseTypeLabel);
259 }
260
261 public function testRetrieveCaseIdsByContactId() {
262 $caseIds = CRM_Case_BAO_Case::retrieveCaseIdsByContactId(3, FALSE, 'housing_support');
263 $this->assertEquals([1], $caseIds);
264 }
265
266 /**
267 * Test that all custom files are migrated to new case when case is assigned to new client.
268 */
269 public function testCaseReassignForCustomFiles() {
270 $individual = $this->individualCreate();
271 $customGroup = $this->customGroupCreate(array(
272 'extends' => 'Case',
273 ));
274 $customGroup = $customGroup['values'][$customGroup['id']];
275
276 $customFileFieldA = $this->customFieldCreate(array(
277 'custom_group_id' => $customGroup['id'],
278 'html_type' => 'File',
279 'is_active' => 1,
280 'default_value' => 'null',
281 'label' => 'Custom File A',
282 'data_type' => 'File',
283 ));
284
285 $customFileFieldB = $this->customFieldCreate(array(
286 'custom_group_id' => $customGroup['id'],
287 'html_type' => 'File',
288 'is_active' => 1,
289 'default_value' => 'null',
290 'label' => 'Custom File B',
291 'data_type' => 'File',
292 ));
293
294 // Create two files to attach to the new case
295 $filepath = Civi::paths()->getPath('[civicrm.files]/custom');
296
297 CRM_Utils_File::createFakeFile($filepath, 'Bananas do not bend themselves without a little help.', 'i_bend_bananas.txt');
298 $fileA = $this->callAPISuccess('File', 'create', ['uri' => "$filepath/i_bend_bananas.txt"]);
299
300 CRM_Utils_File::createFakeFile($filepath, 'Wombats will bite your ankles if you run from them.', 'wombats_bite_your_ankles.txt');
301 $fileB = $this->callAPISuccess('File', 'create', ['uri' => "$filepath/wombats_bite_your_ankles.txt"]);
302
303 $caseObj = $this->createCase($individual);
304
305 $this->callAPISuccess('Case', 'create', array(
306 'id' => $caseObj->id,
307 'custom_' . $customFileFieldA['id'] => $fileA['id'],
308 'custom_' . $customFileFieldB['id'] => $fileB['id'],
309 ));
310
311 $reassignIndividual = $this->individualCreate();
312 $this->createLoggedInUser();
313 $newCase = CRM_Case_BAO_Case::mergeCases($reassignIndividual, $caseObj->id, $individual, NULL, TRUE);
314
315 $entityFiles = new CRM_Core_DAO_EntityFile();
316 $entityFiles->entity_id = $newCase[0];
317 $entityFiles->entity_table = $customGroup['table_name'];
318 $entityFiles->find();
319
320 $totalEntityFiles = 0;
321 while ($entityFiles->fetch()) {
322 $totalEntityFiles++;
323 }
324
325 $this->assertEquals(2, $totalEntityFiles, 'Two files should be attached with new case.');
326 }
327
328 /**
329 * FIXME: need to create an activity to run this test
330 * function testGetCases() {
331 * $cases = CRM_Case_BAO_Case::getCases(TRUE, 3);
332 * $this->assertEquals('Housing Support', $cases[1]['case_type']);
333 * $this->assertEquals(1, $cases[1]['case_type_id']);
334 * }
335 */
336 public function testGetCasesSummary() {
337 $cases = CRM_Case_BAO_Case::getCasesSummary();
338 $this->assertEquals(1, $cases['rows']['Housing Support']['Ongoing']['count']);
339 }
340
341 /**
342 * Test that getRelatedCases() returns the other case when you create a
343 * Link Cases activity on one of the cases.
344 */
345 public function testGetRelatedCases() {
346 $loggedInUser = $this->createLoggedInUser();
347 // create some cases
348 $client_id_1 = $this->individualCreate([], 0);
349 $caseObj_1 = $this->createCase($client_id_1, $loggedInUser);
350 $case_id_1 = $caseObj_1->id;
351 $client_id_2 = $this->individualCreate([], 1);
352 $caseObj_2 = $this->createCase($client_id_2, $loggedInUser);
353 $case_id_2 = $caseObj_2->id;
354
355 // Create link case activity. We could go thru the whole form processes
356 // but we really just want to test the BAO function so just need the
357 // activity to exist.
358 $result = $this->callAPISuccess('activity', 'create', [
359 'activity_type_id' => 'Link Cases',
360 'subject' => 'Test Link Cases',
361 'status_id' => 'Completed',
362 'source_contact_id' => $loggedInUser,
363 'target_contact_id' => $client_id_1,
364 'case_id' => $case_id_1,
365 ]);
366
367 // Put it in the format needed for endPostProcess
368 $activity = new StdClass();
369 $activity->id = $result['id'];
370 $params = [
371 'link_to_case_id' => $case_id_2,
372 ];
373 CRM_Case_Form_Activity_LinkCases::endPostProcess(NULL, $params, $activity);
374
375 // Get related cases for case 1
376 $cases = CRM_Case_BAO_Case::getRelatedCases($case_id_1);
377 // It should have case 2
378 $this->assertEquals($case_id_2, $cases[$case_id_2]['case_id']);
379
380 // Ditto but reverse the cases
381 $cases = CRM_Case_BAO_Case::getRelatedCases($case_id_2);
382 $this->assertEquals($case_id_1, $cases[$case_id_1]['case_id']);
383 }
384
385 /**
386 * Test various things after a case is closed.
387 *
388 * This annotation is not ideal, but without it there is some kind of
389 * messup that happens to quickform that persists between tests, e.g.
390 * it can't add maxfilesize validation rules.
391 * @runInSeparateProcess
392 * @preserveGlobalState disabled
393 */
394 public function testCaseClosure() {
395 $loggedInUser = $this->createLoggedInUser();
396 $client_id = $this->individualCreate();
397 $caseObj = $this->createCase($client_id, $loggedInUser);
398 $case_id = $caseObj->id;
399
400 // Get the case status option value for "Resolved" (name="Closed").
401 $closed_status = $this->callAPISuccess('OptionValue', 'getValue', [
402 'return' => 'value',
403 'option_group_id' => 'case_status',
404 'name' => 'Closed',
405 ]);
406 $this->assertNotEmpty($closed_status);
407
408 // Get the activity status option value for "Completed"
409 $completed_status = $this->callAPISuccess('OptionValue', 'getValue', [
410 'return' => 'value',
411 'option_group_id' => 'activity_status',
412 'name' => 'Completed',
413 ]);
414 $this->assertNotEmpty($completed_status);
415
416 // Get the value for the activity type id we need to create
417 $atype = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Change Case Status');
418
419 // Now it gets weird. There doesn't seem to be a good way to test this, so we simulate a form and the various bits that go with it.
420
421 // HTTP vars needed because that's how the form determines stuff
422 $oldMETHOD = empty($_SERVER['REQUEST_METHOD']) ? NULL : $_SERVER['REQUEST_METHOD'];
423 $oldGET = empty($_GET) ? [] : $_GET;
424 $oldREQUEST = empty($_REQUEST) ? [] : $_REQUEST;
425 $_SERVER['REQUEST_METHOD'] = 'GET';
426 $_GET['caseid'] = $case_id;
427 $_REQUEST['caseid'] = $case_id;
428 $_GET['cid'] = $client_id;
429 $_REQUEST['cid'] = $client_id;
430 $_GET['action'] = 'add';
431 $_REQUEST['action'] = 'add';
432 $_GET['reset'] = 1;
433 $_REQUEST['reset'] = 1;
434 $_GET['atype'] = $atype;
435 $_REQUEST['atype'] = $atype;
436
437 $form = new CRM_Case_Form_Activity();
438 $form->controller = new CRM_Core_Controller_Simple('CRM_Case_Form_Activity', 'Case Activity');
439 $form->_activityTypeId = $atype;
440 $form->_activityTypeName = 'Change Case Status';
441 $form->_activityTypeFile = 'ChangeCaseStatus';
442
443 $form->preProcess();
444 $form->buildQuickForm();
445 $form->setDefaultValues();
446
447 // Now submit the form. Store the date used so we can check it later.
448
449 $t = time();
450 $now_date = date('Y-m-d H:i:s', $t);
451 $now_date_date_only = date('Y-m-d', $t);
452 $actParams = [
453 'is_unittest' => TRUE,
454 'case_status_id' => $closed_status,
455 'activity_date_time' => $now_date,
456 'target_contact_id' => $client_id,
457 'source_contact_id' => $loggedInUser,
458 // yeah this is extra weird, but without it you get the wrong subject
459 'subject' => 'null',
460 ];
461
462 $form->postProcess($actParams);
463
464 // Ok now let's check some things
465
466 $result = $this->callAPISuccess('Case', 'get', [
467 'sequential' => 1,
468 'id' => $case_id,
469 ]);
470 $caseData = array_shift($result['values']);
471
472 $this->assertEquals($caseData['end_date'], $now_date_date_only);
473 $this->assertEquals($caseData['status_id'], $closed_status);
474
475 // now get the latest activity and check some things for it
476
477 $actId = max($caseData['activities']);
478 $this->assertNotEmpty($actId);
479
480 $result = $this->callAPISuccess('Activity', 'get', [
481 'sequential' => 1,
482 'id' => $actId,
483 ]);
484 $activity = array_shift($result['values']);
485
486 $this->assertEquals($activity['subject'], 'Case status changed from Ongoing to Resolved');
487 $this->assertEquals($activity['activity_date_time'], $now_date);
488 $this->assertEquals($activity['status_id'], $completed_status);
489
490 // Now replace old globals
491 if (is_null($oldMETHOD)) {
492 unset($_SERVER['REQUEST_METHOD']);
493 }
494 else {
495 $_SERVER['REQUEST_METHOD'] = $oldMETHOD;
496 }
497 $_GET = $oldGET;
498 $_REQUEST = $oldREQUEST;
499 }
500
501 /**
502 * Test getGlobalContacts
503 */
504 public function testGetGlobalContacts() {
505 //Add contact to case resource.
506 $caseResourceContactID = $this->individualCreate();
507 $this->callAPISuccess('GroupContact', 'create', [
508 'group_id' => "Case_Resources",
509 'contact_id' => $caseResourceContactID,
510 ]);
511
512 //No contact should be returned.
513 CRM_Core_Config::singleton()->userPermissionClass->permissions = [];
514 $groupInfo = [];
515 $groupContacts = CRM_Case_BAO_Case::getGlobalContacts($groupInfo);
516 $this->assertEquals(0, count($groupContacts));
517
518 //Verify if contact is returned correctly.
519 CRM_Core_Config::singleton()->userPermissionClass->permissions = [
520 'access CiviCRM',
521 'view all contacts',
522 ];
523 $groupInfo = [];
524 $groupContacts = CRM_Case_BAO_Case::getGlobalContacts($groupInfo);
525 $this->assertEquals(1, count($groupContacts));
526 $this->assertEquals($caseResourceContactID, key($groupContacts));
527 }
528
529 /**
530 * Test max_instances
531 */
532 public function testMaxInstances() {
533 $loggedInUser = $this->createLoggedInUser();
534 $client_id = $this->individualCreate();
535 $caseObj = $this->createCase($client_id, $loggedInUser);
536 $case_id = $caseObj->id;
537
538 // Sanity check to make sure we'll be testing what we think we're testing.
539 $this->assertEquals($caseObj->case_type_id, 1);
540
541 // Get the case type
542 $result = $this->callAPISuccess('CaseType', 'get', [
543 'sequential' => 1,
544 'id' => 1,
545 ]);
546 $caseType = array_shift($result['values']);
547 $activityTypeName = $caseType['definition']['activityTypes'][1]['name'];
548 // Sanity check to make sure we'll be testing what we think we're testing.
549 $this->assertEquals($activityTypeName, "Medical evaluation");
550
551 // Look up the activity type label - we need it later
552 $result = $this->callAPISuccess('OptionValue', 'get', [
553 'sequential' => 1,
554 'option_group_id' => 'activity_type',
555 'name' => $activityTypeName,
556 ]);
557 $optionValue = array_shift($result['values']);
558 $activityTypeLabel = $optionValue['label'];
559 $this->assertNotEmpty($activityTypeLabel);
560
561 // Locate the existing activity independently so we can check it
562 $result = $this->callAPISuccess('Activity', 'get', [
563 'sequential' => 1,
564 // this sometimes confuses me - pass in the name for the id
565 'activity_type_id' => $activityTypeName,
566 ]);
567 // There should be only one in the database at this point so this should be the id.
568 $activity_id = $result['id'];
569 $this->assertNotEmpty($activity_id);
570 $this->assertGreaterThan(0, $activity_id);
571 $activityArr = array_shift($result['values']);
572
573 // At the moment everything should be happy, although there's nothing to test because if max_instances has no value then nothing gets called, which is correct since it means unlimited. But we don't have a way to test that right now. For fun we could test max_instances=0 but that isn't the same as "not set". 0 would actually mean 0 are allowed, which is pointless, since then why would you even add the activity type to the config.
574
575 // Update max instances for the activity type
576 // We're not really checking that the tested code has retrieved the new case type definition, just that given some numbers as input it returns the right thing as output, so these lines are mostly symbolic at the moment.
577 $caseType['definition']['activityTypes'][1]['max_instances'] = 1;
578 $this->callAPISuccess('CaseType', 'create', $caseType);
579
580 // Now we should get a link back
581 $editUrl = CRM_Case_Form_Activity::checkMaxInstances(
582 $case_id,
583 $activityArr['activity_type_id'],
584 // max instances
585 1,
586 $loggedInUser,
587 $client_id,
588 // existing activity count
589 1
590 );
591 $this->assertNotNull($editUrl);
592
593 $expectedUrl = CRM_Utils_System::url(
594 'civicrm/case/activity',
595 "reset=1&cid={$client_id}&caseid={$case_id}&action=update&id={$activity_id}"
596 );
597 $this->assertEquals($editUrl, $expectedUrl);
598
599 // And also a bounce message is expected
600 $bounceMessage = CRM_Case_Form_Activity::getMaxInstancesBounceMessage(
601 $editUrl,
602 $activityTypeLabel,
603 // max instances,
604 1,
605 // existing activity count
606 1
607 );
608 $this->assertNotEmpty($bounceMessage);
609
610 // Now check with max_instances = 2
611 $caseType['definition']['activityTypes'][1]['max_instances'] = 2;
612 $this->callAPISuccess('CaseType', 'create', $caseType);
613
614 // So it should now be back to being happy
615 $editUrl = CRM_Case_Form_Activity::checkMaxInstances(
616 $case_id,
617 $activityArr['activity_type_id'],
618 // max instances
619 2,
620 $loggedInUser,
621 $client_id,
622 // existing activity count
623 1
624 );
625 $this->assertNull($editUrl);
626 $bounceMessage = CRM_Case_Form_Activity::getMaxInstancesBounceMessage(
627 $editUrl,
628 $activityTypeLabel,
629 // max instances,
630 2,
631 // existing activity count
632 1
633 );
634 $this->assertEmpty($bounceMessage);
635
636 // Add new activity check again
637 $newActivity = [
638 'case_id' => $case_id,
639 'activity_type_id' => $activityArr['activity_type_id'],
640 'status_id' => $activityArr['status_id'],
641 'subject' => "A different subject",
642 'activity_date_time' => date('Y-m-d H:i:s'),
643 'source_contact_id' => $loggedInUser,
644 'target_id' => $client_id,
645 ];
646 $this->callAPISuccess('Activity', 'create', $newActivity);
647
648 $editUrl = CRM_Case_Form_Activity::checkMaxInstances(
649 $case_id,
650 $activityArr['activity_type_id'],
651 // max instances
652 2,
653 $loggedInUser,
654 $client_id,
655 // existing activity count
656 2
657 );
658 // There should be no url here.
659 $this->assertNull($editUrl);
660
661 // But there should be a warning message still.
662 $bounceMessage = CRM_Case_Form_Activity::getMaxInstancesBounceMessage(
663 $editUrl,
664 $activityTypeLabel,
665 // max instances,
666 2,
667 // existing activity count
668 2
669 );
670 $this->assertNotEmpty($bounceMessage);
671 }
672
673 /**
674 * Test changing the label for the case manager role and then creating
675 * a case.
676 * At the time this test was written this test would fail, demonstrating
677 * one problem with name vs label.
678 */
679 public function testCreateCaseWithChangedManagerLabel() {
680 // We could just assume the relationship that gets created has
681 // relationship_type_id = 1, but let's create a case, see what the
682 // id is, then do our actual test.
683 $loggedInUser = $this->createLoggedInUser();
684 $client_id = $this->individualCreate();
685 $caseObj = $this->createCase($client_id, $loggedInUser);
686 $case_id = $caseObj->id;
687
688 // Going to assume the stock case type has what it currently has at the
689 // time of writing, which is the autocreated case manager relationship for
690 // the logged in user.
691 $getParams = [
692 'contact_id_b' => $loggedInUser,
693 'case_id' => $case_id,
694 ];
695 $result = $this->callAPISuccess('Relationship', 'get', $getParams);
696 // as noted above assume this is the only one
697 $relationship_type_id = $result['values'][$result['id']]['relationship_type_id'];
698
699 // Save the old labels first so we can put back at end of test.
700 $oldParams = [
701 'id' => $relationship_type_id,
702 ];
703 $oldValues = $this->callAPISuccess('RelationshipType', 'get', $oldParams);
704 // Now change the label of the relationship type.
705 $changeParams = [
706 'id' => $relationship_type_id,
707 'label_a_b' => 'Best ' . $oldValues['values'][$relationship_type_id]['label_a_b'],
708 'label_b_a' => 'Best ' . $oldValues['values'][$relationship_type_id]['label_b_a'],
709 ];
710 $this->callAPISuccess('RelationshipType', 'create', $changeParams);
711
712 // Now try creating another case.
713 $caseObj2 = $this->createCase($client_id, $loggedInUser);
714 $case_id2 = $caseObj2->id;
715
716 $checkParams = [
717 'contact_id_b' => $loggedInUser,
718 'case_id' => $case_id2,
719 ];
720 $result = $this->callAPISuccess('Relationship', 'get', $checkParams);
721 // Main thing is the above createCase call doesn't fail, but let's check
722 // the relationship type id is what we expect too while we're here.
723 // See note above about assuming this is the only relationship autocreated.
724 $this->assertEquals($relationship_type_id, $result['values'][$result['id']]['relationship_type_id']);
725
726 // Now put relationship type back to the way it was.
727 $changeParams = [
728 'id' => $relationship_type_id,
729 'label_a_b' => $oldValues['values'][$relationship_type_id]['label_a_b'],
730 'label_b_a' => $oldValues['values'][$relationship_type_id]['label_b_a'],
731 ];
732 $this->callAPISuccess('RelationshipType', 'create', $changeParams);
733 }
734
735 /**
736 * Test change case status with linked cases choosing the option to
737 * update the linked cases.
738 */
739 public function testChangeCaseStatusLinkedCases() {
740 $loggedInUser = $this->createLoggedInUser();
741 $clientId1 = $this->individualCreate();
742 $clientId2 = $this->individualCreate();
743 $case1 = $this->createCase($clientId1, $loggedInUser);
744 $case2 = $this->createCase($clientId2, $loggedInUser);
745 $linkActivity = $this->callAPISuccess('Activity', 'create', [
746 'case_id' => $case1->id,
747 'source_contact_id' => $loggedInUser,
748 'target_contact' => $clientId1,
749 'activity_type_id' => 'Link Cases',
750 'subject' => 'Test Link Cases',
751 'status_id' => 'Completed',
752 ]);
753
754 // Put it in the format needed for endPostProcess
755 $activity = new StdClass();
756 $activity->id = $linkActivity['id'];
757 $params = ['link_to_case_id' => $case2->id];
758 CRM_Case_Form_Activity_LinkCases::endPostProcess(NULL, $params, $activity);
759
760 // Get the option_value.value for case status Closed
761 $closedStatusResult = $this->callAPISuccess('OptionValue', 'get', [
762 'option_group_id' => 'case_status',
763 'name' => 'Closed',
764 'return' => ['value'],
765 ]);
766 $closedStatus = $closedStatusResult['values'][$closedStatusResult['id']]['value'];
767
768 // Go thru the motions to change case status
769 $form = new CRM_Case_Form_Activity_ChangeCaseStatus();
770 $form->_caseId = [$case1->id];
771 $form->_oldCaseStatus = [$case1->status_id];
772 $params = [
773 'id' => $case1->id,
774 'case_status_id' => $closedStatus,
775 'updateLinkedCases' => '1',
776 ];
777
778 CRM_Case_Form_Activity_ChangeCaseStatus::beginPostProcess($form, $params);
779 // Check that the second case is now also in the form member.
780 $this->assertEquals([$case1->id, $case2->id], $form->_caseId);
781
782 // We need to pass in an actual activity later
783 $result = $this->callAPISuccess('Activity', 'create', [
784 'case_id' => $case1->id,
785 'source_contact_id' => $loggedInUser,
786 'target_contact' => $clientId1,
787 'activity_type_id' => 'Change Case Status',
788 'subject' => 'Status changed',
789 'status_id' => 'Completed',
790 ]);
791 $changeStatusActivity = new CRM_Activity_DAO_Activity();
792 $changeStatusActivity->id = $result['id'];
793 $changeStatusActivity->find(TRUE);
794
795 $params = [
796 'case_id' => $case1->id,
797 'target_contact_id' => [$clientId1],
798 'case_status_id' => $closedStatus,
799 'activity_date_time' => $changeStatusActivity->activity_date_time,
800 ];
801
802 CRM_Case_Form_Activity_ChangeCaseStatus::endPostProcess($form, $params, $changeStatusActivity);
803
804 // @todo Check other case got closed.
805 /*
806 * We can't do this here because it doesn't happen until the parent
807 * activity does its thing.
808 $linkedCase = $this->callAPISuccess('Case', 'get', ['id' => $case2->id]);
809 $this->assertEquals($closedStatus, $linkedCase['values'][$linkedCase['id']]['status_id']);
810 $this->assertEquals(date('Y-m-d', strtotime($changeStatusActivity->activity_date_time)), $linkedCase['values'][$linkedCase['id']]['end_date']);
811 */
812 }
813
814 }