Make the activity search filter on manage case less unwieldy
authordemeritcowboy <demeritcowboy@hotmail.com>
Mon, 4 Jul 2022 00:58:54 +0000 (20:58 -0400)
committerdemeritcowboy <demeritcowboy@hotmail.com>
Tue, 5 Jul 2022 15:49:27 +0000 (11:49 -0400)
CRM/Case/Form/CaseView.php
tests/phpunit/CRM/Case/Form/CaseViewTest.php [new file with mode: 0644]

index 2123f2f7ee281deeb68865dc63dad436f005c6ef..75b35d48c8e1a436de6029d14cd2706d1c916f99 100644 (file)
@@ -294,7 +294,8 @@ class CRM_Case_Form_CaseView extends CRM_Core_Form {
     $this->buildMergeCaseForm();
 
     //call activity form
-    self::activityForm($this, $aTypes);
+    // @todo seems a little odd to call "self" but pass $this in a form function? The only other place this is called from is one place in civihr.
+    self::activityForm($this);
 
     //get case related relationships (Case Role)
     $caseRelationships = CRM_Case_BAO_Case::getCaseRoles($this->_contactID, $this->_caseID, NULL, FALSE);
@@ -468,10 +469,8 @@ class CRM_Case_Form_CaseView extends CRM_Core_Form {
   /**
    * Build the activity selector/datatable
    * @param CRM_Core_Form $form
-   * @param array $aTypes
-   *   To include acivities related to current case id $form->_caseID.
    */
-  public static function activityForm($form, $aTypes = []) {
+  public static function activityForm($form) {
     $caseRelationships = CRM_Case_BAO_Case::getCaseRoles($form->_contactID, $form->_caseID);
     //build reporter select
     $reporters = ["" => ts(' - any reporter - ')];
@@ -480,16 +479,22 @@ class CRM_Case_Form_CaseView extends CRM_Core_Form {
     }
     $form->add('select', 'reporter_id', ts('Reporter/Role'), $reporters, FALSE, ['id' => 'reporter_id_' . $form->_caseID]);
 
-    // take all case activity types for search filter, CRM-7187
+    // List all the activity types that have been used on this case
     $aTypesFilter = [];
-    $allCaseActTypes = CRM_Case_PseudoConstant::caseActivityType();
-    foreach ($allCaseActTypes as $typeDetails) {
-      if (!in_array($typeDetails['name'], ['Open Case'])) {
-        $aTypesFilter[$typeDetails['id']] = $typeDetails['label'] ?? NULL;
-      }
+    $activity_types_on_case = \Civi\Api4\CaseActivity::get()
+      ->addWhere('case_id', '=', $form->_caseID)
+      // we want to include deleted too since the filter can search for deleted
+      ->addWhere('activity_id.is_deleted', 'IN', [0, 1])
+      // technically correct, but this might end up excluding some deleted ones depending on how they got deleted
+      // ->addWhere('activity_id.is_current_revision', '=', 1)
+      ->addSelect('activity_id.activity_type_id', 'activity_id.activity_type_id:label')
+      ->addGroupBy('activity_id.activity_type_id')
+      // this creates strange SQL - if it is too slow could sort in php instead
+      ->addOrderBy('activity_id.activity_type_id:label', 'ASC')
+      ->execute();
+    foreach ($activity_types_on_case as $typeDetails) {
+      $aTypesFilter[$typeDetails['activity_id.activity_type_id']] = $typeDetails['activity_id.activity_type_id:label'];
     }
-    $aTypesFilter = $aTypesFilter + $aTypes;
-    asort($aTypesFilter);
     $form->add('select', 'activity_type_filter_id', ts('Activity Type'), ['' => ts('- select activity type -')] + $aTypesFilter, FALSE, ['id' => 'activity_type_filter_id_' . $form->_caseID]);
 
     $activityStatus = CRM_Core_PseudoConstant::activityStatus();
diff --git a/tests/phpunit/CRM/Case/Form/CaseViewTest.php b/tests/phpunit/CRM/Case/Form/CaseViewTest.php
new file mode 100644 (file)
index 0000000..dff1949
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+/**
+ * Class CRM_Case_Form_CaseViewTest
+ * @group headless
+ */
+class CRM_Case_Form_CaseViewTest extends CiviCaseTestCase {
+
+  /**
+   * Test that the search filter dropdown includes the desired activity types.
+   */
+  public function testSearchFilterDropdown() {
+    $client_id = $this->individualCreate([], 0, TRUE);
+    $caseObj = $this->createCase($client_id, $this->_loggedInUser);
+
+    $form = $this->getFormObject('CRM_Case_Form_CaseView');
+    $form->set('cid', $client_id);
+    $form->set('id', $caseObj->id);
+    $form->buildForm();
+    $options = $form->getElement('activity_type_filter_id')->_options;
+    // We don't care about the first one, just check it's what we expect
+    $this->assertEquals('- select activity type -', $options[0]['text']);
+    unset($options[0]);
+    $mappedOptions = array_map(function($v) {
+      return [$v['attr']['value'] => $v['text']];
+    }, $options);
+    $this->assertEquals([
+      [14 => 'Follow up'],
+      [60 => 'Income and benefits stabilization'],
+      [58 => 'Long-term housing plan'],
+      [55 => 'Medical evaluation'],
+      [56 => 'Mental health evaluation'],
+      [13 => 'Open Case'],
+      [57 => 'Secure temporary housing'],
+    ], array_values($mappedOptions));
+
+    // Now add some activities where the type might not even be in the config.
+    $this->callAPISuccess('Activity', 'create', [
+      'case_id' => $caseObj->id,
+      'subject' => 'aaaa',
+      'activity_type_id' => 'Inbound Email',
+    ]);
+    $this->callAPISuccess('Activity', 'create', [
+      'case_id' => $caseObj->id,
+      'subject' => 'bbbb',
+      'activity_type_id' => 'Email',
+    ]);
+    $this->callAPISuccess('Activity', 'create', [
+      'case_id' => $caseObj->id,
+      'subject' => 'cccc',
+      'activity_type_id' => 'Meeting',
+    ]);
+
+    // And let's delete one since we still want it to be available as a filter
+    $this->callAPISuccess('Activity', 'create', [
+      'case_id' => $caseObj->id,
+      'subject' => 'dddd',
+      'activity_type_id' => 'Phone Call',
+      'is_deleted' => 1,
+    ]);
+
+    $form = $this->getFormObject('CRM_Case_Form_CaseView');
+    $form->set('cid', $client_id);
+    $form->set('id', $caseObj->id);
+    $form->buildForm();
+    $options = $form->getElement('activity_type_filter_id')->_options;
+    unset($options[0]);
+    $mappedOptions = array_map(function($v) {
+      return [$v['attr']['value'] => $v['text']];
+    }, $options);
+    $this->assertEquals([
+      [3 => 'Email'],
+      [14 => 'Follow up'],
+      [12 => 'Inbound Email'],
+      [60 => 'Income and benefits stabilization'],
+      [58 => 'Long-term housing plan'],
+      [55 => 'Medical evaluation'],
+      [1 => 'Meeting'],
+      [56 => 'Mental health evaluation'],
+      [13 => 'Open Case'],
+      [2 => 'Phone Call'],
+      [57 => 'Secure temporary housing'],
+    ], array_values($mappedOptions));
+  }
+
+}