From 3ac22820f7f10bbf30e23bb9306289d94d9903ff Mon Sep 17 00:00:00 2001
From: Muhammad Shahrukh <>
Date: Tue, 30 Jul 2024 12:48:28 +0500
Subject: [PATCH] FOSFAB-320: Add functionality to link cases while creating a
case
---
CRM/Civicase/Hook/Post/LinkCase.php | 145 ++++++++++++++++++
.../new-link-cases-case-action.service.js | 40 +++++
...se-details-linked-cases-tab.directive.html | 24 ++-
...case-details-linked-cases-tab.directive.js | 19 ++-
civicase.php | 1 +
5 files changed, 223 insertions(+), 6 deletions(-)
create mode 100644 CRM/Civicase/Hook/Post/LinkCase.php
create mode 100644 ang/civicase/case/actions/services/new-link-cases-case-action.service.js
diff --git a/CRM/Civicase/Hook/Post/LinkCase.php b/CRM/Civicase/Hook/Post/LinkCase.php
new file mode 100644
index 000000000..f604e9a6a
--- /dev/null
+++ b/CRM/Civicase/Hook/Post/LinkCase.php
@@ -0,0 +1,145 @@
+shouldRun($op, $objectName)) {
+ return;
+ }
+
+ $linkToCaseId = (int) CRM_Utils_Request::retrieve('linkToCaseId', 'Positive');
+ $linkedToCaseDetails = $this->getLinkedToCaseDetails($linkToCaseId);
+ $caseDetails = $this->getCaseDetails($objectId);
+
+ $params = [
+ 'case_id' => $linkToCaseId,
+ 'link_to_case_id' => $objectId,
+ 'activity_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Link Cases'),
+ 'medium_id' => CRM_Core_OptionGroup::values('encounter_medium', FALSE, FALSE, FALSE, 'AND is_default = 1'),
+ 'activity_date_time' => date('YmdHis'),
+ 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'),
+ 'subject' => $this->getActivitySubject($caseDetails, $linkedToCaseDetails),
+ 'source_contact_id' => \CRM_Core_Session::getLoggedInContactID(),
+ 'target_contact_id' => $linkedToCaseContacts[0]['contact_id'] ?? NULL,
+ ];
+
+ $activity = CRM_Activity_BAO_Activity::create($params);
+
+ $caseParams = [
+ 'case_id' => $objectId,
+ 'activity_id' => $activity->id,
+ ];
+ CRM_Case_BAO_Case::processCaseActivity($caseParams);
+
+ $caseParams = [
+ 'case_id' => $linkToCaseId,
+ 'activity_id' => $activity->id,
+ ];
+ CRM_Case_BAO_Case::processCaseActivity($caseParams);
+ }
+
+ /**
+ * Determines if the hook should run or not.
+ *
+ * @param string $op
+ * The operation being performed.
+ * @param string $objectName
+ * Object name.
+ *
+ * @return bool
+ * returns a boolean to determine if hook will run or not.
+ */
+ private function shouldRun(string $op, string $objectName): bool {
+ return $objectName == 'Case' && $op === 'create'
+ && (int) CRM_Utils_Request::retrieve('linkToCaseId', 'Positive') > 0;
+ }
+
+ /**
+ * Get case details required to create the activity subject.
+ *
+ * @param int $id
+ * The case id.
+ *
+ * @return array
+ * case details.
+ */
+ private function getLinkedToCaseDetails(int $id): array {
+ $case = CiviCase::get(FALSE)
+ ->addSelect('contact.display_name', 'case_type_id.title')
+ ->addJoin('CaseContact AS case_contact', 'INNER', ['id', '=', 'case_contact.case_id'])
+ ->addJoin('Contact AS contact', 'INNER', ['contact.id', '=', 'case_contact.contact_id'])
+ ->addWhere('id', '=', $id)
+ ->execute()
+ ->first();
+
+ return [
+ 'id' => $id,
+ 'caseType' => $case['case_type_id.title'] ?? '',
+ 'contact' => $case['contact.display_name'] ?? '',
+ ];
+ }
+
+ /**
+ * Get case details required to create the activity subject.
+ *
+ * @param int $id
+ * The case id.
+ *
+ * @return array
+ * case details.
+ */
+ private function getCaseDetails(int $id): array {
+ $caseClient = Contact::get(FALSE)
+ ->addSelect('display_name')
+ ->addWhere('id', '=', $_POST['client_id'] ?? 0)
+ ->execute()
+ ->first();
+
+ return [
+ 'id' => $id,
+ 'caseType' => CRM_Case_BAO_Case::getCaseType($id),
+ 'contact' => $caseClient['display_name'] ?? '',
+ ];
+ }
+
+ /**
+ * Create activity subject.
+ *
+ * @param array $caseDetails
+ * The case details.
+ * @param array $linkedToCaseDetails
+ * The linked to case details.
+ *
+ * @return string
+ * Activity subject.
+ */
+ private function getActivitySubject(array $caseDetails, array $linkedToCaseDetails): string {
+ return ts('Create link between %1 - %2 (CaseID: %3) and %4 - %5 (CaseID: %6)', [
+ 1 => $caseDetails['contact'],
+ 2 => $caseDetails['caseType'],
+ 3 => $caseDetails['id'],
+ 4 => $linkedToCaseDetails['contact'],
+ 5 => $linkedToCaseDetails['caseType'],
+ 6 => $linkedToCaseDetails['id'],
+ ]);
+ }
+
+}
diff --git a/ang/civicase/case/actions/services/new-link-cases-case-action.service.js b/ang/civicase/case/actions/services/new-link-cases-case-action.service.js
new file mode 100644
index 000000000..72c5cc656
--- /dev/null
+++ b/ang/civicase/case/actions/services/new-link-cases-case-action.service.js
@@ -0,0 +1,40 @@
+(function (angular, $, _) {
+ var module = angular.module('civicase');
+
+ module.service('NewLinkCasesCaseAction', NewLinkCasesCaseAction);
+
+ /**
+ * Create and Link Case Action service
+ *
+ * @param {object} $q $q service
+ * @param {object} ActivityType ActivityType
+ * @param {string} currentCaseCategory current case category
+ */
+ function NewLinkCasesCaseAction ($q, ActivityType, currentCaseCategory) {
+ /**
+ * Click event handler for the Action
+ *
+ * @param {object} cases cases
+ *
+ * @returns {Promise} promise which resolves to the path for the popup
+ */
+ this.doAction = function (cases) {
+ var currentCase = cases[0];
+ var activityTypes = ActivityType.getAll(true);
+
+ var link = {
+ path: 'civicrm/case/add',
+ query: {
+ action: 'add',
+ reset: 1,
+ atype: _.findKey(activityTypes, { name: 'Open Case' }),
+ linkToCaseId: currentCase.id,
+ context: 'standalone',
+ case_type_category: currentCaseCategory
+ }
+ };
+
+ return $q.resolve(link);
+ };
+ }
+})(angular, CRM.$, CRM._);
diff --git a/ang/civicase/case/details/linked-cases-tab/directives/case-details-linked-cases-tab.directive.html b/ang/civicase/case/details/linked-cases-tab/directives/case-details-linked-cases-tab.directive.html
index f885a39ea..e130d32a2 100644
--- a/ang/civicase/case/details/linked-cases-tab/directives/case-details-linked-cases-tab.directive.html
+++ b/ang/civicase/case/details/linked-cases-tab/directives/case-details-linked-cases-tab.directive.html
@@ -7,6 +7,13 @@
{{ ts('Link Cases') }}
+
+
+ {{ ts('Create new linked Case') }}
+
{{ ts('Click the button below to create a new link for this case') }}
-
- {{ ts('Link Cases') }}
-
+
diff --git a/ang/civicase/case/details/linked-cases-tab/directives/case-details-linked-cases-tab.directive.js b/ang/civicase/case/details/linked-cases-tab/directives/case-details-linked-cases-tab.directive.js
index d63962be6..dcd95ce07 100644
--- a/ang/civicase/case/details/linked-cases-tab/directives/case-details-linked-cases-tab.directive.js
+++ b/ang/civicase/case/details/linked-cases-tab/directives/case-details-linked-cases-tab.directive.js
@@ -17,12 +17,14 @@
*
* @param {object} $scope the scope object.
* @param {object} LinkCasesCaseAction the link case action service.
+ * @param {object} NewLinkCasesCaseAction the new link case action service.
* @param {Function} civicaseCrmUrl crm url service.
* @param {Function} civicaseCrmLoadForm service to load civicrm forms
*/
function civicaseCaseDetailsLinkedCasesTabController ($scope,
- LinkCasesCaseAction, civicaseCrmUrl, civicaseCrmLoadForm) {
+ LinkCasesCaseAction, NewLinkCasesCaseAction, civicaseCrmUrl, civicaseCrmLoadForm) {
$scope.linkCase = linkCase;
+ $scope.newLinkCase = newLinkCase;
/**
* Opens a modal that allows the user to link the case stored in the scope with
@@ -39,5 +41,20 @@
});
});
}
+
+ /**
+ * Opens a modal that allows the user to open a new case and link it at the same time.
+ *
+ * The case details are refreshed after linking the cases.
+ */
+ function newLinkCase () {
+ NewLinkCasesCaseAction.doAction([$scope.item])
+ .then(function (openCaseForm) {
+ civicaseCrmLoadForm(civicaseCrmUrl(openCaseForm.path, openCaseForm.query))
+ .on('crmFormSuccess crmPopupFormSuccess', function () {
+ $scope.refresh();
+ });
+ });
+ }
}
})(angular);
diff --git a/civicase.php b/civicase.php
index f0b5f904d..5e6d9467a 100644
--- a/civicase.php
+++ b/civicase.php
@@ -279,6 +279,7 @@ function civicase_civicrm_post($op, $objectName, $objectId, &$objectRef) {
new CRM_Civicase_Hook_Post_PopulateCaseCategoryForCaseType(),
new CRM_Civicase_Hook_Post_CaseCategoryCustomGroupSaver(),
new CRM_Civicase_Hook_Post_UpdateCaseTypeListForCaseCategoryCustomGroup(),
+ new CRM_Civicase_Hook_Post_LinkCase(),
];
foreach ($hooks as $hook) {