APIv4 - Add CiviCase and CaseContact entities
authorColeman Watts <coleman@civicrm.org>
Thu, 25 Mar 2021 14:48:33 +0000 (10:48 -0400)
committerColeman Watts <coleman@civicrm.org>
Fri, 2 Apr 2021 18:49:51 +0000 (14:49 -0400)
12 files changed:
CRM/Case/BAO/Case.php
Civi/Api4/Action/CiviCase/CiviCaseSaveTrait.php [new file with mode: 0644]
Civi/Api4/Action/CiviCase/Create.php [new file with mode: 0644]
Civi/Api4/Action/CiviCase/Save.php [new file with mode: 0644]
Civi/Api4/Action/CiviCase/Update.php [new file with mode: 0644]
Civi/Api4/Action/GetActions.php
Civi/Api4/CaseContact.php [new file with mode: 0644]
Civi/Api4/CiviCase.php [new file with mode: 0644]
Civi/Api4/Generic/Traits/DAOActionTrait.php
Civi/Api4/Service/Spec/Provider/CaseCreationSpecProvider.php [new file with mode: 0644]
tests/phpunit/api/v4/DataSets/ConformanceTest.json
tests/phpunit/api/v4/Entity/ConformanceTest.php

index 2b919e2ebc7989c010480cf883a2e8046cfae57a..a7d10036a9060487471589080e575c8b064f0854 100644 (file)
@@ -229,6 +229,14 @@ WHERE civicrm_case.id = %1";
     return FALSE;
   }
 
+  /**
+   * @param $id
+   * @return bool
+   */
+  public static function del($id) {
+    return self::deleteCase($id);
+  }
+
   /**
    * Enable disable case related relationships.
    *
diff --git a/Civi/Api4/Action/CiviCase/CiviCaseSaveTrait.php b/Civi/Api4/Action/CiviCase/CiviCaseSaveTrait.php
new file mode 100644 (file)
index 0000000..8f83c11
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+
+namespace Civi\Api4\Action\CiviCase;
+
+/**
+ * @inheritDoc
+ */
+trait CiviCaseSaveTrait {
+
+  /**
+   * @param array $cases
+   * @return array
+   */
+  protected function writeObjects($cases) {
+    $cases = array_values($cases);
+    $result = parent::writeObjects($cases);
+
+    // If the case doesn't have an id, it's new & needs to be opened.
+    foreach ($cases as $idx => $case) {
+      if (empty($case['id'])) {
+        $this->openCase($case, $result[$idx]['id']);
+      }
+    }
+    return $result;
+  }
+
+  /**
+   * @param $case
+   * @param $id
+   * @throws \CRM_Core_Exception
+   */
+  private function openCase($case, $id) {
+    // Add case contacts (clients)
+    foreach ((array) $case['contact_id'] as $cid) {
+      $contactParams = ['case_id' => $id, 'contact_id' => $cid];
+      \CRM_Case_BAO_CaseContact::create($contactParams);
+    }
+
+    $caseType = \CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $case['case_type_id'], 'name');
+
+    // Pass "Open Case" params to XML processor
+    $xmlProcessor = new \CRM_Case_XMLProcessor_Process();
+    $params = [
+      'clientID' => $case['contact_id'] ?? NULL,
+      'creatorID' => $case['creator_id'] ?? NULL,
+      'standardTimeline' => 1,
+      'activityTypeName' => 'Open Case',
+      'caseID' => $id,
+      'subject' => $case['subject'] ?? NULL,
+      'location' => $case['location'] ?? NULL,
+      'activity_date_time' => $case['start_date'] ?? NULL,
+      'duration' => $case['duration'] ?? NULL,
+      'medium_id' => $case['medium_id'] ?? NULL,
+      'details' => $case['details'] ?? NULL,
+      'custom' => [],
+      'relationship_end_date' => $case['end_date'] ?? NULL,
+    ];
+
+    // Do it! :-D
+    $xmlProcessor->run($caseType, $params);
+  }
+
+}
diff --git a/Civi/Api4/Action/CiviCase/Create.php b/Civi/Api4/Action/CiviCase/Create.php
new file mode 100644 (file)
index 0000000..9ebd5de
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+
+namespace Civi\Api4\Action\CiviCase;
+
+/**
+ * @inheritDoc
+ */
+class Create extends \Civi\Api4\Generic\DAOCreateAction {
+  use CiviCaseSaveTrait;
+
+}
diff --git a/Civi/Api4/Action/CiviCase/Save.php b/Civi/Api4/Action/CiviCase/Save.php
new file mode 100644 (file)
index 0000000..2b01873
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Action\CiviCase;
+
+/**
+ * @inheritDoc
+ */
+class Save extends \Civi\Api4\Generic\DAOSaveAction {
+  use CiviCaseSaveTrait;
+
+}
diff --git a/Civi/Api4/Action/CiviCase/Update.php b/Civi/Api4/Action/CiviCase/Update.php
new file mode 100644 (file)
index 0000000..67bd771
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+
+namespace Civi\Api4\Action\CiviCase;
+
+/**
+ * @inheritDoc
+ */
+class Update extends \Civi\Api4\Generic\DAOUpdateAction {
+  use CiviCaseSaveTrait;
+
+}
index 7065c6c2aebc5654ce3cb1955aa5605be11eff27..e0dfbb597b3f7483f3f5539b89271fa0317eb7af 100644 (file)
@@ -64,7 +64,7 @@ class GetActions extends BasicGetAction {
       foreach (glob("$dir/*.php") as $file) {
         $actionName = basename($file, '.php');
         $actionClass = new \ReflectionClass($nameSpace . '\\' . $actionName);
-        if ($actionClass->isInstantiable() && $actionClass->isSubclassOf('\Civi\\Api4\Generic\AbstractAction')) {
+        if ($actionClass->isInstantiable() && $actionClass->isSubclassOf('\Civi\Api4\Generic\AbstractAction')) {
           $this->loadAction(lcfirst($actionName));
         }
       }
diff --git a/Civi/Api4/CaseContact.php b/Civi/Api4/CaseContact.php
new file mode 100644 (file)
index 0000000..4d8ae66
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+
+namespace Civi\Api4;
+
+/**
+ * CaseContact BridgeEntity.
+ *
+ * This connects a client to a case.
+ *
+ * @see \Civi\Api4\Case
+ * @package Civi\Api4
+ */
+class CaseContact extends Generic\DAOEntity {
+  use Generic\Traits\EntityBridge;
+
+  protected static function getEntityTitle($plural = FALSE) {
+    return $plural ? ts('Case Clients') : ts('Case Client');
+  }
+
+}
diff --git a/Civi/Api4/CiviCase.php b/Civi/Api4/CiviCase.php
new file mode 100644 (file)
index 0000000..e1fb635
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+
+namespace Civi\Api4;
+
+/**
+ * Case entity.
+ *
+ * Note that the class for this entity is named "CiviCase" because "Case" is a keyword reserved by php.
+ *
+ * @see https://docs.civicrm.org/user/en/latest/case-management/what-is-civicase/
+ * @package Civi\Api4
+ */
+class CiviCase extends Generic\DAOEntity {
+
+  /**
+   * Explicitly declare entity name because it doesn't match the name of this class
+   * (due to the php reserved keyword issue)
+   *
+   * @return string
+   */
+  protected static function getEntityName() {
+    return 'Case';
+  }
+
+  /**
+   * @param bool $checkPermissions
+   * @return Action\CiviCase\Create
+   */
+  public static function create($checkPermissions = TRUE) {
+    return (new Action\CiviCase\Create('Case', __FUNCTION__))
+      ->setCheckPermissions($checkPermissions);
+  }
+
+  /**
+   * @param bool $checkPermissions
+   * @return Action\CiviCase\Save
+   */
+  public static function save($checkPermissions = TRUE) {
+    return (new Action\CiviCase\Save('Case', __FUNCTION__))
+      ->setCheckPermissions($checkPermissions);
+  }
+
+  /**
+   * @param bool $checkPermissions
+   * @return Action\CiviCase\Update
+   */
+  public static function update($checkPermissions = TRUE) {
+    return (new Action\CiviCase\Update('Case', __FUNCTION__))
+      ->setCheckPermissions($checkPermissions);
+  }
+
+}
index b7b4c2d3ac09d86ecf15458bec7ae578abc8226d..dea475d2fb510a6f3fa40aba20c07156f03e1e0a 100644 (file)
@@ -15,6 +15,7 @@ namespace Civi\Api4\Generic\Traits;
 use Civi\Api4\CustomField;
 use Civi\Api4\Service\Schema\Joinable\CustomGroupJoinable;
 use Civi\Api4\Utils\FormattingUtil;
+use Civi\Api4\Utils\CoreUtil;
 
 /**
  * @method string getLanguage()
@@ -35,8 +36,7 @@ trait DAOActionTrait {
    * @return \CRM_Core_DAO|string
    */
   protected function getBaoName() {
-    require_once 'api/v3/utils.php';
-    return \_civicrm_api3_get_BAO($this->getEntityName());
+    return CoreUtil::getBAOFromApiName($this->getEntityName());
   }
 
   /**
@@ -158,8 +158,6 @@ trait DAOActionTrait {
    * @param array $params
    * @param int $entityId
    *
-   * @return mixed
-   *
    * @throws \API_Exception
    * @throws \CRM_Core_Exception
    */
diff --git a/Civi/Api4/Service/Spec/Provider/CaseCreationSpecProvider.php b/Civi/Api4/Service/Spec/Provider/CaseCreationSpecProvider.php
new file mode 100644 (file)
index 0000000..ce0dcf7
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\FieldSpec;
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class CaseCreationSpecProvider implements Generic\SpecProviderInterface {
+
+  /**
+   * @inheritDoc
+   */
+  public function modifySpec(RequestSpec $spec) {
+    $creator = new FieldSpec('creator_id', $spec->getEntity(), 'Integer');
+    $creator->setTitle(ts('Case Creator'));
+    $creator->setDescription('Contact who created the case.');
+    $creator->setFkEntity('Contact');
+    $creator->setInputType('EntityRef');
+    $spec->addFieldSpec($creator);
+
+    $contact = new FieldSpec('contact_id', $spec->getEntity(), 'Array');
+    $contact->setTitle(ts('Case Contact(s)'));
+    $contact->setLabel(ts('Case Client(s)'));
+    $contact->setDescription('Contact(s) who are case clients.');
+    $contact->setFkEntity('Contact');
+    $contact->setInputType('EntityRef');
+    $contact->setRequired(TRUE);
+    $spec->addFieldSpec($contact);
+
+    $location = new FieldSpec('location', $spec->getEntity(), 'String');
+    $location->setTitle(ts('Activity Location'));
+    $location->setDescription('Open Case activity location.');
+    $spec->addFieldSpec($location);
+
+    $medium_id = new FieldSpec('medium_id', $spec->getEntity(), 'Integer');
+    $medium_id->setTitle(ts('Activity Medium'));
+    $medium_id->setDescription('Open Case activity medium.');
+    $spec->addFieldSpec($medium_id);
+
+    $duration = new FieldSpec('duration', $spec->getEntity(), 'Integer');
+    $duration->setTitle(ts('Activity Duration'));
+    $duration->setInputType('Number');
+    $duration->setDescription('Open Case activity duration (minutes).');
+    $spec->addFieldSpec($duration);
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function applies($entity, $action) {
+    return $entity === 'Case' && $action === 'create';
+  }
+
+}
index d12286873301bf145483090c671cf1de3c509814..f34be53340b0fcb80b4d61c487af482496b81d67 100644 (file)
@@ -7,6 +7,14 @@
       "@ref": "test_contact_1"
     }
   ],
+  "Case": [
+    {
+      "case_type_id": 1,
+      "status_id": 1,
+      "contact_id": "@ref test_contact_1.id",
+      "creator_id": "@ref test_contact_1.id"
+    }
+  ],
   "CustomGroup": [
     {
       "name": "MyFavoriteThings",
index e9ec92d78eb51bb2afaf5ed5f22028174adef3b4..db0767550d9f36342626f81ad64f77626c8c5f31 100644 (file)
@@ -71,13 +71,9 @@ class ConformanceTest extends UnitTestCase {
    */
   public function getEntitiesHitech() {
     // Ensure all components are enabled so their entities show up
-    \CRM_Core_BAO_ConfigSetting::enableComponent('CiviEvent');
-    \CRM_Core_BAO_ConfigSetting::enableComponent('CiviGrant');
-    \CRM_Core_BAO_ConfigSetting::enableComponent('CiviCase');
-    \CRM_Core_BAO_ConfigSetting::enableComponent('CiviContribute');
-    \CRM_Core_BAO_ConfigSetting::enableComponent('CiviCampaign');
-    \CRM_Core_BAO_ConfigSetting::enableComponent('CiviPledge');
-    \CRM_Core_BAO_ConfigSetting::enableComponent('CiviReport');
+    foreach (array_keys(\CRM_Core_Component::getComponents()) as $component) {
+      \CRM_Core_BAO_ConfigSetting::enableComponent($component);
+    }
     return $this->toDataProviderArray(Entity::get(FALSE)->execute()->column('name'));
   }
 
@@ -93,11 +89,13 @@ class ConformanceTest extends UnitTestCase {
   public function getEntitiesLotech() {
     $manual['add'] = [];
     $manual['remove'] = ['CustomValue'];
+    $manual['transform'] = ['CiviCase' => 'Case'];
 
     $scanned = [];
     $srcDir = dirname(__DIR__, 5);
     foreach ((array) glob("$srcDir/Civi/Api4/*.php") as $name) {
-      $scanned[] = preg_replace('/\.php/', '', basename($name));
+      $fileName = basename($name, '.php');
+      $scanned[] = $manual['transform'][$fileName] ?? $fileName;
     }
 
     $names = array_diff(