From 41cf58d3ed398a63e7ab07648301237894b65cc6 Mon Sep 17 00:00:00 2001 From: Alice Frumin Date: Thu, 31 Jan 2019 17:19:40 -0500 Subject: [PATCH 1/9] dev/core#530 CiviCase: making roles available in either direction --- CRM/Activity/Page/AJAX.php | 4 +- CRM/Case/BAO/Case.php | 183 ++++++++++++++---- CRM/Case/BAO/Query.php | 7 +- CRM/Case/Form/AddToCaseAsRole.php | 14 +- CRM/Case/Form/CaseView.php | 2 +- CRM/Case/Info.php | 2 +- CRM/Case/ManagedEntities.php | 7 +- CRM/Case/XMLProcessor.php | 15 +- CRM/Case/XMLProcessor/Process.php | 19 +- CRM/Report/Form/Case/Summary.php | 8 +- ang/crmCaseType.ang.php | 6 - ang/crmCaseType.js | 57 +++--- tests/karma/unit/crmCaseTypeSpec.js | 28 ++- .../phpunit/CRM/Case/BAO/CaseTypeForkTest.php | 2 +- 14 files changed, 242 insertions(+), 112 deletions(-) diff --git a/CRM/Activity/Page/AJAX.php b/CRM/Activity/Page/AJAX.php index 1da789c2734a..6c78fb59c5b1 100644 --- a/CRM/Activity/Page/AJAX.php +++ b/CRM/Activity/Page/AJAX.php @@ -161,7 +161,7 @@ public static function getCaseRoles() { foreach ($caseRelationships as $key => $value) { // This role has been filled - unset($caseRoles[$value['relation_type']]); + unset($caseRoles[$value['relation_type'] . '_' . $value['relationship_direction']]); // mark original case relationships record to use on setting edit links below $caseRelationships[$key]['source'] = 'caseRel'; } @@ -209,7 +209,7 @@ public static function getCaseRoles() { foreach ($caseRelationships as $key => &$row) { $typeLabel = $row['relation']; // Add "
(Case Manager)" to label - if (!empty($row['relation_type']) && $row['relation_type'] == $managerRoleId) { + if (!empty($row['relation_type']) && !empty($row['relationship_direction']) && $row['relation_type'] . '_' . $row['relationship_direction'] == $managerRoleId) { $row['relation'] .= '
' . '(' . ts('Case Manager') . ')'; } // view user links diff --git a/CRM/Case/BAO/Case.php b/CRM/Case/BAO/Case.php index aaa37f5a595e..f7d40e7e1900 100644 --- a/CRM/Case/BAO/Case.php +++ b/CRM/Case/BAO/Case.php @@ -434,7 +434,7 @@ public static function getCaseActivityQuery($type = 'upcoming', $userID, $condit 'civicrm_case.status_id as case_status_id', 't_act.status_id as status_id', 'civicrm_case.start_date as case_start_date', - 'case_relation_type.label_b_a as case_role', + "GROUP_CONCAT(DISTINCT IF(case_relationship.contact_id_b = $userID, case_relation_type.label_a_b, case_relation_type.label_b_a) SEPARATOR ', ') as case_role", 't_act.activity_date_time as activity_date_time', 't_act.id as activity_id', ); @@ -475,8 +475,8 @@ public static function getCaseActivityQuery($type = 'upcoming', $userID, $condit ON civicrm_phone.contact_id = civicrm_contact.id AND civicrm_phone.is_primary = 1 LEFT JOIN civicrm_relationship case_relationship - ON case_relationship.contact_id_a = civicrm_case_contact.contact_id - AND case_relationship.contact_id_b = {$userID} + ON ((case_relationship.contact_id_a = civicrm_case_contact.contact_id AND case_relationship.contact_id_b = {$userID}) + OR (case_relationship.contact_id_b = civicrm_case_contact.contact_id AND case_relationship.contact_id_a = {$userID})) AND case_relationship.is_active AND case_relationship.case_id = civicrm_case.id LEFT JOIN civicrm_relationship_type case_relation_type @@ -535,10 +535,11 @@ public static function getCases($allCases = TRUE, $params = array(), $context = $whereClauses = array('civicrm_case.is_deleted = 0 AND civicrm_contact.is_deleted <> 1'); if (!$allCases) { - $whereClauses[] .= " case_relationship.contact_id_b = {$userID} AND case_relationship.is_active "; + $whereClauses[] = "(case_relationship.contact_id_b = {$userID} OR case_relationship.contact_id_a = {$userID})"; + $whereClauses[] = 'case_relationship.is_active'; } if (empty($params['status_id']) && ($type == 'upcoming' || $type == 'any')) { - $whereClauses[] = " civicrm_case.status_id != " . CRM_Core_PseudoConstant::getKey('CRM_Case_BAO_Case', 'case_status_id', 'Closed'); + $whereClauses[] = "civicrm_case.status_id != " . CRM_Core_PseudoConstant::getKey('CRM_Case_BAO_Case', 'case_status_id', 'Closed'); } foreach (array('case_type_id', 'status_id') as $column) { @@ -703,26 +704,28 @@ public static function getCasesSummary($allCases = TRUE) { // build rows with actual data $rows = array(); - $myGroupByClause = $mySelectClause = $myCaseFromClause = $myCaseWhereClause = ''; + $myGroupByClause = $mySelectClause = $myCaseFromClause = $myCaseWhereClauseA = $myCaseWhereClauseB = ''; if ($allCases) { $userID = 'null'; $all = 1; $case_owner = 1; - $myGroupByClause = ' GROUP BY civicrm_case.id'; + $myGroupByClauseB = ' GROUP BY civicrm_case.id'; } else { $all = 0; $case_owner = 2; - $myCaseWhereClause = " AND case_relationship.contact_id_b = {$userID} AND case_relationship.is_active "; - $myGroupByClause = " GROUP BY CONCAT(case_relationship.case_id,'-',case_relationship.contact_id_b)"; + $myCaseWhereClauseA = " AND case_relationship.contact_id_a = {$userID} AND case_relationship.is_active "; + $myGroupByClauseA = " GROUP BY CONCAT(civicrm_case.id,'-',case_relationship.contact_id_a)"; + $myCaseWhereClauseB = " AND case_relationship.contact_id_b = {$userID} AND case_relationship.is_active "; + $myGroupByClauseB = " GROUP BY CONCAT(civicrm_case.id,'-',case_relationship.contact_id_b)"; } - $myGroupByClause .= ", case_status.label, status_id, case_type_id"; - + $myGroupByClauseB .= ", case_status.label, status_id, case_type_id, civicrm_case.id"; + $myGroupByClauseA = $myGroupByClauseB; // FIXME: This query could be a lot more efficient if it used COUNT() instead of returning all rows and then counting them with php $query = " SELECT case_status.label AS case_status, status_id, civicrm_case_type.title AS case_type, - case_type_id, case_relationship.contact_id_b + case_type_id, case_relationship.contact_id_b as case_contact FROM civicrm_case INNER JOIN civicrm_case_contact cc on cc.case_id = civicrm_case.id LEFT JOIN civicrm_case_type ON civicrm_case.case_type_id = civicrm_case_type.id @@ -732,7 +735,20 @@ public static function getCasesSummary($allCases = TRUE) { LEFT JOIN civicrm_relationship case_relationship ON ( case_relationship.case_id = civicrm_case.id AND case_relationship.contact_id_b = {$userID} AND case_relationship.is_active ) WHERE is_deleted = 0 AND cc.contact_id IN (SELECT id FROM civicrm_contact WHERE is_deleted <> 1) -{$myCaseWhereClause} {$myGroupByClause}"; +{$myCaseWhereClauseB} {$myGroupByClauseB} +UNION +SELECT case_status.label AS case_status, status_id, civicrm_case_type.title AS case_type, + case_type_id, case_relationship.contact_id_a as case_contact + FROM civicrm_case + INNER JOIN civicrm_case_contact cc on cc.case_id = civicrm_case.id + LEFT JOIN civicrm_case_type ON civicrm_case.case_type_id = civicrm_case_type.id + LEFT JOIN civicrm_option_group option_group_case_status ON ( option_group_case_status.name = 'case_status' ) + LEFT JOIN civicrm_option_value case_status ON ( civicrm_case.status_id = case_status.value + AND option_group_case_status.id = case_status.option_group_id ) + LEFT JOIN civicrm_relationship case_relationship ON ( case_relationship.case_id = civicrm_case.id + AND case_relationship.contact_id_a = {$userID}) + WHERE is_deleted = 0 AND cc.contact_id IN (SELECT id FROM civicrm_contact WHERE is_deleted <> 1) +{$myCaseWhereClauseA} {$myGroupByClauseA}"; $res = CRM_Core_DAO::executeQuery($query); while ($res->fetch()) { @@ -1204,29 +1220,53 @@ public static function getRelatedContacts($caseID, $includeDetails = TRUE) { $caseInfo = civicrm_api3('Case', 'getsingle', array( 'id' => $caseID, // Most efficient way of retrieving definition is to also include case type id and name so the api doesn't have to look it up separately - 'return' => array('case_type_id', 'case_type_id.name', 'case_type_id.definition'), + 'return' => array('case_type_id', 'case_type_id.name', 'case_type_id.definition', 'contact_id'), )); if (!empty($caseInfo['case_type_id.definition']['caseRoles'])) { $caseRoles = CRM_Utils_Array::rekey($caseInfo['case_type_id.definition']['caseRoles'], 'name'); } } - $values = array(); - $query = ' - SELECT cc.display_name as name, cc.sort_name as sort_name, cc.id, cr.relationship_type_id, crt.label_b_a as role, crt.name_b_a, ce.email, cp.phone - FROM civicrm_relationship cr - LEFT JOIN civicrm_relationship_type crt - ON crt.id = cr.relationship_type_id - LEFT JOIN civicrm_contact cc - ON cc.id = cr.contact_id_b - LEFT JOIN civicrm_email ce - ON ce.contact_id = cc.id - AND ce.is_primary= 1 - LEFT JOIN civicrm_phone cp - ON cp.contact_id = cc.id - AND cp.is_primary= 1 - WHERE cr.case_id = %1 AND cr.is_active AND cc.is_deleted <> 1'; - $params = array(1 => array($caseID, 'Integer')); + $values = array(); + $query = << 1 + LEFT JOIN civicrm_email ce + ON ce.contact_id = cc.id + AND ce.is_primary= 1 + LEFT JOIN civicrm_phone cp + ON cp.contact_id = cc.id + AND cp.is_primary= 1 + WHERE cr.case_id = %1 + AND cr.is_active + AND cc.id NOT IN (%2) + UNION + SELECT cc.display_name as name, cc.sort_name as sort_name, cc.id, cr.relationship_type_id, crt.label_a_b as role, crt.name_a_b as role_name, ce.email, cp.phone + FROM civicrm_relationship cr + JOIN civicrm_relationship_type crt + ON crt.id = cr.relationship_type_id + JOIN civicrm_contact cc + ON cc.id = cr.contact_id_b + AND cc.is_deleted <> 1 + LEFT JOIN civicrm_email ce + ON ce.contact_id = cc.id + AND ce.is_primary= 1 + LEFT JOIN civicrm_phone cp + ON cp.contact_id = cc.id + AND cp.is_primary= 1 + WHERE cr.case_id = %1 + AND cr.is_active + AND cc.id NOT IN (%2) +HERESQL; + $params = array( + 1 => array($caseID, 'Integer'), + 2 => array(implode(',', $caseInfo['client_id']), 'String'), + ); $dao = CRM_Core_DAO::executeQuery($query, $params); while ($dao->fetch()) { @@ -1244,7 +1284,7 @@ public static function getRelatedContacts($caseID, $includeDetails = TRUE) { 'phone' => $dao->phone, ); // Add more info about the role (creator, manager) - $role = CRM_Utils_Array::value($dao->name_b_a, $caseRoles); + $role = CRM_Utils_Array::value($dao->role_name, $caseRoles); if ($role) { unset($role['name']); $details += $role; @@ -1848,16 +1888,27 @@ public static function getCaseManagerContact($caseType, $caseId) { $managerRoleId = $xmlProcessor->getCaseManagerRoleId($caseType); if (!empty($managerRoleId)) { - $managerRoleQuery = " -SELECT civicrm_contact.id as casemanager_id, - civicrm_contact.sort_name as casemanager - FROM civicrm_contact - LEFT JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = civicrm_contact.id AND civicrm_relationship.relationship_type_id = %1) AND civicrm_relationship.is_active - LEFT JOIN civicrm_case ON civicrm_case.id = civicrm_relationship.case_id - WHERE civicrm_case.id = %2 AND is_active = 1"; + if (substr($managerRoleId, -4) == '_a_b') { + $managerRoleQuery = " + SELECT civicrm_contact.id as casemanager_id, + civicrm_contact.sort_name as casemanager + FROM civicrm_contact + LEFT JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = civicrm_contact.id AND civicrm_relationship.relationship_type_id = %1) AND civicrm_relationship.is_active + LEFT JOIN civicrm_case ON civicrm_case.id = civicrm_relationship.case_id + WHERE civicrm_case.id = %2 AND is_active = 1"; + } + if (substr($managerRoleId, -4) == '_b_a') { + $managerRoleQuery = " + SELECT civicrm_contact.id as casemanager_id, + civicrm_contact.sort_name as casemanager + FROM civicrm_contact + LEFT JOIN civicrm_relationship ON (civicrm_relationship.contact_id_a = civicrm_contact.id AND civicrm_relationship.relationship_type_id = %1) AND civicrm_relationship.is_active + LEFT JOIN civicrm_case ON civicrm_case.id = civicrm_relationship.case_id + WHERE civicrm_case.id = %2 AND is_active = 1"; + } $managerRoleParams = array( - 1 => array($managerRoleId, 'Integer'), + 1 => array(substr($managerRoleId, 0, -4), 'Integer'), 2 => array($caseId, 'Integer'), ); @@ -3214,4 +3265,58 @@ public static function getEntityRefFilters() { return $filters; } + /** + * Fetch Case Role direction from Case Type + */ + public static function getCaseRoleDirection($caseId, $roleTypeId = NULL) { + try { + $case = civicrm_api3('Case', 'getsingle', array('id' => $caseId)); + } + catch (CiviCRM_API3_Exception $e) { + // Lack of permissions will throw an exception + return 0; + } + if (!empty($case['case_type_id'])) { + try { + $caseType = civicrm_api3('CaseType', 'getsingle', array('id' => $case['case_type_id'], 'return' => array('definition'))); + } + catch (CiviCRM_API3_Exception $e) { + // Lack of permissions will throw an exception + return 'no case type found'; + } + if (!empty($caseType['definition']['caseRoles'])) { + $caseRoles = array(); + foreach ($caseType['definition']['caseRoles'] as $key => $roleDetails) { + // Check if its an a_b label + try { + $relType = civicrm_api3('RelationshipType', 'getsingle', array('label_a_b' => $roleDetails['name'])); + } + catch (CiviCRM_API3_Exception $e) { + } + if (!empty($relType['id'])) { + $roleDetails['id'] = $relType['id']; + $roleDetails['direction'] = 'a_b'; + } + // Check if its a b_a label + try { + $relTypeBa = civicrm_api3('RelationshipType', 'getsingle', array('label_b_a' => $roleDetails['name'])); + } + catch (CiviCRM_API3_Exception $e) { + } + if (!empty($relTypeBa['id'])) { + if (!empty($roleDetails['direction'])) { + $roleDetails['direction'] = 'bidrectional'; + } + else { + $roleDetails['id'] = $relTypeBa['id']; + $roleDetails['direction'] = 'b_a'; + } + } + $caseRoles[$roleDetails['id']] = $roleDetails; + } + } + return $caseRoles; + } + } + } diff --git a/CRM/Case/BAO/Query.php b/CRM/Case/BAO/Query.php index e5bf54125eb7..321118bf96e7 100644 --- a/CRM/Case/BAO/Query.php +++ b/CRM/Case/BAO/Query.php @@ -110,7 +110,7 @@ public static function select(&$query) { } if (!empty($query->_returnProperties['case_role'])) { - $query->_select['case_role'] = "case_relation_type.label_b_a as case_role"; + $query->_select['case_role'] = "IF(case_relationship.contact_id_b = contact_a.id, case_relation_type.label_b_a, case_relation_type.label_a_b) as case_role"; $query->_element['case_role'] = 1; $query->_tables['case_relationship'] = $query->_whereTables['case_relationship'] = 1; $query->_tables['case_relation_type'] = $query->_whereTables['case_relation_type'] = 1; @@ -296,7 +296,7 @@ public static function whereClauseSingle(&$values, &$query) { if ($value == 2) { $session = CRM_Core_Session::singleton(); $userID = $session->get('userID'); - $query->_where[$grouping][] = ' ( ' . CRM_Contact_BAO_Query::buildClause("case_relationship.contact_id_b", $op, $userID, 'Int') . ' AND ' . CRM_Contact_BAO_Query::buildClause("case_relationship.is_active", '<>', 0, 'Int') . ' ) '; + $query->_where[$grouping][] = ' (( ' . CRM_Contact_BAO_Query::buildClause("case_relationship.contact_id_b", $op, $userID, 'Int') . ' AND ' . CRM_Contact_BAO_Query::buildClause("case_relationship.is_active", '<>', 0, 'Int') . ' ) OR ( ' . CRM_Contact_BAO_Query::buildClause("case_relationship.contact_id_a", $op, $userID, 'Int') . ' AND ' . CRM_Contact_BAO_Query::buildClause("case_relationship.is_active", '<>', 0, 'Int') . ' ))'; $query->_qill[$grouping][] = ts('Case %1 My Cases', [1 => $op]); $query->_tables['case_relationship'] = $query->_whereTables['case_relationship'] = 1; } @@ -434,7 +434,6 @@ public static function whereClauseSingle(&$values, &$query) { // adding where clause for case_role case 'case_role': - $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_relation_type.name_b_a", $op, $value, 'String'); $query->_qill[$grouping][] = ts("Role in Case %1 '%2'", [1 => $op, 2 => $value]); $query->_tables['case_relation_type'] = $query->_whereTables['case_relationship_type'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; @@ -549,7 +548,7 @@ public static function from($name, $mode, $side) { case 'case_relationship': $session = CRM_Core_Session::singleton(); $userID = $session->get('userID'); - $from .= " $side JOIN civicrm_relationship case_relationship ON ( case_relationship.contact_id_a = civicrm_case_contact.contact_id AND case_relationship.contact_id_b = {$userID} AND case_relationship.case_id = civicrm_case.id )"; + $from .= " $side JOIN civicrm_relationship case_relationship ON ( case_relationship.contact_id_a = civicrm_case_contact.contact_id AND case_relationship.contact_id_b = {$userID} AND case_relationship.case_id = civicrm_case.id OR case_relationship.contact_id_b = civicrm_case_contact.contact_id AND case_relationship.contact_id_a = {$userID} AND case_relationship.case_id = civicrm_case.id )"; break; case 'case_relation_type': diff --git a/CRM/Case/Form/AddToCaseAsRole.php b/CRM/Case/Form/AddToCaseAsRole.php index 20be94a95ad2..f2809047e3c9 100644 --- a/CRM/Case/Form/AddToCaseAsRole.php +++ b/CRM/Case/Form/AddToCaseAsRole.php @@ -68,15 +68,23 @@ public function postProcess() { $contacts = $this->_contactIds; $clients = CRM_Case_BAO_Case::getCaseClients($caseId); + $caseRole = CRM_Case_BAO_Case::getCaseRoleDirection($caseId, $roleTypeId); $params = [ - 'contact_id_a' => $clients[0], - 'contact_id_b' => $contacts, 'case_id' => $caseId, 'relationship_type_id' => $roleTypeId, ]; - CRM_Contact_BAO_Relationship::createMultiple($params, 'a'); + if ($caseRole[$roleTypeId]['direction'] == 'b_a') { + $params['contact_id_b'] = $clients[0]; + $params['contact_id_a'] = $contacts; + CRM_Contact_BAO_Relationship::createMultiple($params, 'b'); + } + elseif ($caseRole[$roleTypeId]['direction'] == 'a_b' || $caseRole[$roleTypeId]['direction'] = 'bidirectional') { + $params['contact_id_a'] = $clients[0]; + $params['contact_id_b'] = $contacts; + CRM_Contact_BAO_Relationship::createMultiple($params, 'a'); + } $url = CRM_Utils_System::url( 'civicrm/contact/view/case', diff --git a/CRM/Case/Form/CaseView.php b/CRM/Case/Form/CaseView.php index fdf587ca58df..2f9eb82a0e8c 100644 --- a/CRM/Case/Form/CaseView.php +++ b/CRM/Case/Form/CaseView.php @@ -289,7 +289,7 @@ public function buildQuickForm() { foreach ($caseRelationships as $key => & $value) { if (!empty($managerRoleId)) { - if ($managerRoleId == $value['relation_type']) { + if (substr($managerRoleId, 0, -4) == $value['relation_type'] && substr($managerRoleId, -3) == $value['relationship_direction']) { $value['relation'] = $managerLabel; } } diff --git a/CRM/Case/Info.php b/CRM/Case/Info.php index 413385c55ddf..f7f0e0cd9522 100644 --- a/CRM/Case/Info.php +++ b/CRM/Case/Info.php @@ -145,7 +145,7 @@ public function getReferenceCounts($dao) { elseif ($dao instanceof CRM_Contact_DAO_RelationshipType) { /** @var $dao CRM_Contact_DAO_RelationshipType */ $count = CRM_Case_XMLRepository::singleton() - ->getRelationshipReferenceCount($dao->{CRM_Case_XMLProcessor::REL_TYPE_CNAME}); + ->getRelationshipReferenceCount($dao->label_a_b); if ($count > 0) { $result[] = [ 'name' => 'casetypexml:relationships', diff --git a/CRM/Case/ManagedEntities.php b/CRM/Case/ManagedEntities.php index 461b51f5dafc..d3d33a027866 100644 --- a/CRM/Case/ManagedEntities.php +++ b/CRM/Case/ManagedEntities.php @@ -112,7 +112,12 @@ public static function createManagedRelationshipTypes(CRM_Case_XMLRepository $xm if (!isset(Civi::$statics[__CLASS__]['reltypes'])) { $relationshipInfo = CRM_Core_PseudoConstant::relationshipType('label', TRUE, NULL); - Civi::$statics[__CLASS__]['reltypes'] = CRM_Utils_Array::collect(CRM_Case_XMLProcessor::REL_TYPE_CNAME, $relationshipInfo); + foreach ($relationshipInfo as $id => $relTypeDetails) { + Civi::$statics[__CLASS__]['reltypes']["{$id}_a_b"] = $relTypeDetails['label_a_b']; + if ($relTypeDetails['label_a_b'] != $relTypeDetails['label_b_a']) { + Civi::$statics[__CLASS__]['reltypes']["{$id}_b_a"] = $relTypeDetails['label_b_a']; + } + } } $validRelTypes = Civi::$statics[__CLASS__]['reltypes']; diff --git a/CRM/Case/XMLProcessor.php b/CRM/Case/XMLProcessor.php index 863ea68ff5b5..f118d4c203be 100644 --- a/CRM/Case/XMLProcessor.php +++ b/CRM/Case/XMLProcessor.php @@ -52,16 +52,6 @@ class CRM_Case_XMLProcessor { */ public static $relationshipTypes = NULL; - /** - * Relationship-types have four name fields (name_a_b, name_b_a, label_a_b, - * label_b_a), but CiviCase XML refers to reltypes by a single name. - * REL_TYPE_CNAME identifies the canonical name field as used by CiviCase XML. - * - * This appears to be "label_b_a", but IMHO "name_b_a" would be more - * sensible. - */ - const REL_TYPE_CNAME = 'label_b_a'; - /** * @param $caseType * @@ -119,7 +109,10 @@ public function &allRelationshipTypes() { self::$relationshipTypes = []; foreach ($relationshipInfo as $id => $info) { - self::$relationshipTypes[$id] = $info[CRM_Case_XMLProcessor::REL_TYPE_CNAME]; + self::$relationshipTypes[$id . '_b_a'] = $info['label_b_a']; + if ($info['label_b_a'] !== $info['label_a_b']) { + self::$relationshipTypes[$id . '_a_b'] = $info['label_a_b']; + } } } diff --git a/CRM/Case/XMLProcessor/Process.php b/CRM/Case/XMLProcessor/Process.php index b83e11cce663..03501e993eff 100644 --- a/CRM/Case/XMLProcessor/Process.php +++ b/CRM/Case/XMLProcessor/Process.php @@ -214,10 +214,10 @@ public function &caseRoles($caseRolesXML, $isCaseManager = FALSE) { */ public function createRelationships($relationshipTypeName, &$params) { $relationshipTypes = &$this->allRelationshipTypes(); - // get the relationship id - $relationshipTypeID = array_search($relationshipTypeName, $relationshipTypes); + // get the relationship + $relationshipType = array_search($relationshipTypeName, $relationshipTypes); - if ($relationshipTypeID === FALSE) { + if ($relationshipType === FALSE) { $docLink = CRM_Utils_System::docURL2("user/case-management/set-up"); CRM_Core_Error::fatal(ts('Relationship type %1, found in case configuration file, is not present in the database %2', [1 => $relationshipTypeName, 2 => $docLink] @@ -232,15 +232,22 @@ public function createRelationships($relationshipTypeName, &$params) { foreach ($client as $key => $clientId) { $relationshipParams = [ - 'relationship_type_id' => $relationshipTypeID, - 'contact_id_a' => $clientId, - 'contact_id_b' => $params['creatorID'], + 'relationship_type_id' => substr($relationshipType, 0, -4), 'is_active' => 1, 'case_id' => $params['caseID'], 'start_date' => date("Ymd"), 'end_date' => CRM_Utils_Array::value('relationship_end_date', $params), ]; + if (substr($relationshipType, -4) == '_b_a') { + $relationshipParams['contact_id_b'] = $clientId; + $relationshipParams['contact_id_a'] = $params['creatorID']; + } + if (substr($relationshipType, -4) == '_a_b') { + $relationshipParams['contact_id_a'] = $clientId; + $relationshipParams['contact_id_b'] = $params['creatorID']; + } + if (!$this->createRelationship($relationshipParams)) { CRM_Core_Error::fatal(); return FALSE; diff --git a/CRM/Report/Form/Case/Summary.php b/CRM/Report/Form/Case/Summary.php index 1db23629ce08..286fe3b2332b 100644 --- a/CRM/Report/Form/Case/Summary.php +++ b/CRM/Report/Form/Case/Summary.php @@ -229,6 +229,9 @@ public function select() { if ($fieldName == 'duration') { $select[] = "IF({$table['fields']['end_date']['dbAlias']} Is Null, '', DATEDIFF({$table['fields']['end_date']['dbAlias']}, {$table['fields']['start_date']['dbAlias']})) as {$tableName}_{$fieldName}"; } + elseif ($tableName == 'civicrm_relationship_type') { + $select[] = " IF(contact_civireport.id = relationship_civireport.contact_id_a, relationship_type_civireport.label_b_a, relationship_type_civireport.label_a_b) as civicrm_relationship_type_label_b_a"; + } else { $select[] = "{$field['dbAlias']} as {$tableName}_{$fieldName}"; } @@ -290,7 +293,7 @@ public function from() { if ($this->_relField) { $this->_from = " FROM civicrm_contact $c -inner join civicrm_relationship $cr on {$c}.id = ${cr}.contact_id_b +inner join civicrm_relationship $cr on {$c}.id = ${cr}.contact_id_b OR {$c}.id = ${cr}.contact_id_a inner join civicrm_case $cc on ${cc}.id = ${cr}.case_id inner join civicrm_relationship_type $crt on ${crt}.id=${cr}.relationship_type_id inner join civicrm_case_contact $ccc on ${ccc}.case_id = ${cc}.id @@ -308,6 +311,9 @@ public function from() { public function where() { $clauses = []; + if (!empty($this->_params['fields']['label_b_a']) && $this->_params['fields']['label_b_a'] == 1) { + $clauses[] = 'contact_civireport.sort_name != c2_civireport.sort_name'; + } $this->_having = ''; foreach ($this->_columns as $tableName => $table) { if (array_key_exists('filters', $table)) { diff --git a/ang/crmCaseType.ang.php b/ang/crmCaseType.ang.php index 21596810d419..c71e9c6678b5 100644 --- a/ang/crmCaseType.ang.php +++ b/ang/crmCaseType.ang.php @@ -5,12 +5,6 @@ // ODDITY: This only loads if CiviCase is active. -CRM_Core_Resources::singleton()->addSetting([ - 'crmCaseType' => [ - 'REL_TYPE_CNAME' => CRM_Case_XMLProcessor::REL_TYPE_CNAME, - ], -]); - return [ 'ext' => 'civicrm', 'js' => ['ang/crmCaseType.js'], diff --git a/ang/crmCaseType.js b/ang/crmCaseType.js index ee2344ece81a..41f28286a81b 100644 --- a/ang/crmCaseType.js +++ b/ang/crmCaseType.js @@ -74,11 +74,22 @@ limit: 0 } }]; - reqs.relTypes = ['RelationshipType', 'get', { + reqs.relTypes = ['Relationship', 'getoptions', { sequential: 1, + field: 'relationship_type_id', + context: 'create', + is_active: 1, + options: { + limit: 0 + } + }]; + reqs.relTypesForm = ['Relationship', 'getoptions', { + sequential: 1, + field: 'relationship_type_id', + context: 'create', + isForm: 1, is_active: 1, options: { - sort: CRM.crmCaseType.REL_TYPE_CNAME, limit: 0 } }]; @@ -238,11 +249,9 @@ }); crmCaseType.controller('CaseTypeCtrl', function($scope, crmApi, apiCalls, crmUiHelp) { - var REL_TYPE_CNAME, defaultAssigneeDefaultValue, ts; + var defaultAssigneeDefaultValue, ts; (function init () { - // CRM_Case_XMLProcessor::REL_TYPE_CNAME - REL_TYPE_CNAME = CRM.crmCaseType.REL_TYPE_CNAME; ts = $scope.ts = CRM.ts(null); $scope.hs = crmUiHelp({file: 'CRM/Case/CaseType'}); @@ -264,36 +273,16 @@ $scope.activityTypeOptions = _.map(apiCalls.actTypes.values, formatActivityTypeOption); $scope.defaultAssigneeTypes = apiCalls.defaultAssigneeTypes.values; $scope.relationshipTypeOptions = _.map(apiCalls.relTypes.values, function(type) { - return {id: type[REL_TYPE_CNAME], text: type.label_b_a}; + return {id: type.key, text: type.value}; + }); + $scope.defaultRelationshipTypeOptions = _.map(apiCalls.relTypesForm.values, function(type) { + return {value: type.key, label: type.value}; }); - $scope.defaultRelationshipTypeOptions = getDefaultRelationshipTypeOptions(); // stores the default assignee values indexed by their option name: $scope.defaultAssigneeTypeValues = _.chain($scope.defaultAssigneeTypes) .indexBy('name').mapValues('value').value(); } - /// Returns the default relationship type options. If the relationship is - /// bidirectional (Ex: Spouse of) it adds a single option otherwise it adds - /// two options representing the relationship type directions - /// (Ex: Employee of, Employer is) - function getDefaultRelationshipTypeOptions() { - return _.transform(apiCalls.relTypes.values, function(result, relType) { - var isBidirectionalRelationship = relType.label_a_b === relType.label_b_a; - - result.push({ - label: relType.label_b_a, - value: relType.id + '_b_a' - }); - - if (!isBidirectionalRelationship) { - result.push({ - label: relType.label_a_b, - value: relType.id + '_a_b' - }); - } - }, []); - } - /// initializes the case type object function initCaseType() { var isNewCaseType = !apiCalls.caseType; @@ -434,11 +423,15 @@ if (_.where($scope.relationshipTypeOptions, {id: roleName}).length) { roles.push({name: roleName}); } else { - CRM.loadForm(CRM.url('civicrm/admin/reltype', {action: 'add', reset: 1, label_a_b: roleName, label_b_a: roleName})) + CRM.loadForm(CRM.url('civicrm/admin/reltype', {action: 'add', reset: 1, label_a_b: roleName})) .on('crmFormSuccess', function(e, data) { var newType = _.values(data.relationshipType)[0]; - roles.push({name: newType[REL_TYPE_CNAME]}); - $scope.relationshipTypeOptions.push({id: newType[REL_TYPE_CNAME], text: newType.label_b_a}); + roles.push({name: newType.label_a_b}); + // Assume that the case role should be A-B but add both directions as options. + $scope.relationshipTypeOptions.push({id: newType.label_a_b, text: newType.label_a_b}); + if (newType.label_a_b != newType.label_b_a) { + $scope.relationshipTypeOptions.push({id: newType.label_b_a, text: newType.label_b_a}); + } $scope.$digest(); }); } diff --git a/tests/karma/unit/crmCaseTypeSpec.js b/tests/karma/unit/crmCaseTypeSpec.js index 3369579c19bd..4b0a221346be 100644 --- a/tests/karma/unit/crmCaseTypeSpec.js +++ b/tests/karma/unit/crmCaseTypeSpec.js @@ -18,10 +18,6 @@ describe('crmCaseType', function() { CRM.resourceUrls = { 'civicrm': '' }; - // CRM_Case_XMLProcessor::REL_TYPE_CNAME - CRM.crmCaseType = { - 'REL_TYPE_CNAME': 'label_b_a' - }; module('crmCaseType'); module('crmJsonComparator'); inject(function(crmJsonComparator) { @@ -203,6 +199,30 @@ describe('crmCaseType', function() { } ] }, + relTypesForm: { + values: [ + { + "key": "14_b_a", + "value": "Benefits Specialist" + }, + { + "key": "14_a_b", + "value": "Benefits Specialist is" + }, + { + "key": "9_b_a", + "value": "Case Coordinator" + }, + { + "key": "9_a_b", + "value": "Case Coordinator is" + }, + { + "key": "2_b_a", + "value": "Spouse of" + } + ] + }, caseType: { "id": "1", "name": "housing_support", diff --git a/tests/phpunit/CRM/Case/BAO/CaseTypeForkTest.php b/tests/phpunit/CRM/Case/BAO/CaseTypeForkTest.php index caa23c93d3b4..09ca91dbe66c 100644 --- a/tests/phpunit/CRM/Case/BAO/CaseTypeForkTest.php +++ b/tests/phpunit/CRM/Case/BAO/CaseTypeForkTest.php @@ -39,7 +39,7 @@ public function testManagerContact() { ]); //Check if manager is correctly retrieved from xml processor. $xmlProcessor = new CRM_Case_XMLProcessor_Process(); - $this->assertEquals($relTypeID, $xmlProcessor->getCaseManagerRoleId('ForkableCaseType')); + $this->assertEquals($relTypeID . '_b_a', $xmlProcessor->getCaseManagerRoleId('ForkableCaseType')); } /** From a1afe798a9fafd7f552b97c7e99d5e8894a2dbdb Mon Sep 17 00:00:00 2001 From: Alice Frumin Date: Tue, 9 Apr 2019 14:56:18 -0400 Subject: [PATCH 2/9] dev/core#530 CiviCase: fix count on the case dashboard --- CRM/Case/BAO/Case.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CRM/Case/BAO/Case.php b/CRM/Case/BAO/Case.php index f7d40e7e1900..dabd71f63f62 100644 --- a/CRM/Case/BAO/Case.php +++ b/CRM/Case/BAO/Case.php @@ -724,7 +724,7 @@ public static function getCasesSummary($allCases = TRUE) { $myGroupByClauseA = $myGroupByClauseB; // FIXME: This query could be a lot more efficient if it used COUNT() instead of returning all rows and then counting them with php $query = " -SELECT case_status.label AS case_status, status_id, civicrm_case_type.title AS case_type, +SELECT civicrm_case.id, case_status.label AS case_status, status_id, civicrm_case_type.title AS case_type, case_type_id, case_relationship.contact_id_b as case_contact FROM civicrm_case INNER JOIN civicrm_case_contact cc on cc.case_id = civicrm_case.id @@ -737,7 +737,7 @@ public static function getCasesSummary($allCases = TRUE) { WHERE is_deleted = 0 AND cc.contact_id IN (SELECT id FROM civicrm_contact WHERE is_deleted <> 1) {$myCaseWhereClauseB} {$myGroupByClauseB} UNION -SELECT case_status.label AS case_status, status_id, civicrm_case_type.title AS case_type, +SELECT civicrm_case.id, case_status.label AS case_status, status_id, civicrm_case_type.title AS case_type, case_type_id, case_relationship.contact_id_a as case_contact FROM civicrm_case INNER JOIN civicrm_case_contact cc on cc.case_id = civicrm_case.id From 5655a8d4acd2af4d6452148c781a55a5836df5ed Mon Sep 17 00:00:00 2001 From: Alice Frumin Date: Tue, 9 Apr 2019 15:34:54 -0400 Subject: [PATCH 3/9] dev/core#530 CiviCase: fix Manage Case print report duplicate case roles rows --- CRM/Case/XMLProcessor/Report.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CRM/Case/XMLProcessor/Report.php b/CRM/Case/XMLProcessor/Report.php index 98534dc95ec8..2915874f79e5 100644 --- a/CRM/Case/XMLProcessor/Report.php +++ b/CRM/Case/XMLProcessor/Report.php @@ -824,8 +824,8 @@ public static function printCaseReport() { $xmlProcessor = new CRM_Case_XMLProcessor_Process(); $caseRoles = $xmlProcessor->get($caseType, 'CaseRoles'); foreach ($caseRelationships as $key => & $value) { - if (!empty($caseRoles[$value['relation_type']])) { - unset($caseRoles[$value['relation_type']]); + if (!empty($caseRoles[$value['relation_type'] . '_' . $value['relationship_direction']])) { + unset($caseRoles[$value['relation_type'] . '_' . $value['relationship_direction']]); } if ($isRedact) { if (!array_key_exists($value['name'], $report->_redactionStringRules)) { From d0a94888435a0bdc1f451cd3d74c25748fe189a3 Mon Sep 17 00:00:00 2001 From: Alice Frumin Date: Wed, 1 May 2019 15:47:55 -0400 Subject: [PATCH 4/9] dev/core#530 CiviCase: translate case role direction coming from xml --- CRM/Case/BAO/Case.php | 4 ++-- CRM/Case/Info.php | 10 +++++++-- CRM/Case/XMLProcessor.php | 35 ++++++++++++++++--------------- CRM/Case/XMLProcessor/Process.php | 14 ++++++++++--- CRM/Case/XMLRepository.php | 2 ++ 5 files changed, 41 insertions(+), 24 deletions(-) diff --git a/CRM/Case/BAO/Case.php b/CRM/Case/BAO/Case.php index dabd71f63f62..f25f917749ee 100644 --- a/CRM/Case/BAO/Case.php +++ b/CRM/Case/BAO/Case.php @@ -3295,7 +3295,7 @@ public static function getCaseRoleDirection($caseId, $roleTypeId = NULL) { } if (!empty($relType['id'])) { $roleDetails['id'] = $relType['id']; - $roleDetails['direction'] = 'a_b'; + $roleDetails['direction'] = 'b_a'; } // Check if its a b_a label try { @@ -3309,7 +3309,7 @@ public static function getCaseRoleDirection($caseId, $roleTypeId = NULL) { } else { $roleDetails['id'] = $relTypeBa['id']; - $roleDetails['direction'] = 'b_a'; + $roleDetails['direction'] = 'a_b'; } } $caseRoles[$roleDetails['id']] = $roleDetails; diff --git a/CRM/Case/Info.php b/CRM/Case/Info.php index f7f0e0cd9522..ce5421fd1b0d 100644 --- a/CRM/Case/Info.php +++ b/CRM/Case/Info.php @@ -144,8 +144,14 @@ public function getReferenceCounts($dao) { } elseif ($dao instanceof CRM_Contact_DAO_RelationshipType) { /** @var $dao CRM_Contact_DAO_RelationshipType */ - $count = CRM_Case_XMLRepository::singleton() - ->getRelationshipReferenceCount($dao->label_a_b); + + // Need to look both directions, but no need to translate case role + // direction from XML perspective to client-based perspective + $xmlRepo = CRM_Case_XMLRepository::singleton(); + $count = $xmlRepo->getRelationshipReferenceCount($dao->label_a_b); + if ($dao->label_a_b != $dao->label_b_a) { + $count += $xmlRepo->getRelationshipReferenceCount($dao->label_b_a); + } if ($count > 0) { $result[] = [ 'name' => 'casetypexml:relationships', diff --git a/CRM/Case/XMLProcessor.php b/CRM/Case/XMLProcessor.php index f118d4c203be..245e9ace1c26 100644 --- a/CRM/Case/XMLProcessor.php +++ b/CRM/Case/XMLProcessor.php @@ -42,16 +42,6 @@ class CRM_Case_XMLProcessor { */ public static $activityTypes = NULL; - /** - * FIXME: This does *NOT* belong in a static property, but we're too late in - * the 4.5-cycle to do the necessary cleanup. - * - * Format is array(int $id => string $relTypeCname). - * - * @var array|null - */ - public static $relationshipTypes = NULL; - /** * @param $caseType * @@ -101,22 +91,33 @@ public function &allActivityTypes($indexName = TRUE, $all = FALSE) { } /** + * Get all relationship type labels + * + * TODO: These should probably be names, but under legacy behavior this has + * been labels. + * + * @param bool $fromXML + * Is this to be used for lookup of values from XML? + * Relationships are recorded in XML from the perspective of the non-client + * while relationships in the UI and everywhere else are from the + * perspective of the client. Since the XML can't be expected to be + * switched, the direction needs to be translated. * @return array */ - public function &allRelationshipTypes() { - if (self::$relationshipTypes === NULL) { + public function &allRelationshipTypes($fromXML = FALSE) { + if (!isset(Civi::$statics[__CLASS__]['reltypes'][$fromXML])) { $relationshipInfo = CRM_Core_PseudoConstant::relationshipType('label', TRUE); - self::$relationshipTypes = []; + Civi::$statics[__CLASS__]['reltypes'][$fromXML] = []; foreach ($relationshipInfo as $id => $info) { - self::$relationshipTypes[$id . '_b_a'] = $info['label_b_a']; + Civi::$statics[__CLASS__]['reltypes'][$fromXML][$id . '_b_a'] = ($fromXML) ? $info['label_a_b'] : $info['label_b_a']; if ($info['label_b_a'] !== $info['label_a_b']) { - self::$relationshipTypes[$id . '_a_b'] = $info['label_a_b']; + Civi::$statics[__CLASS__]['reltypes'][$fromXML][$id . '_a_b'] = ($fromXML) ? $info['label_b_a'] : $info['label_a_b']; } } } - return self::$relationshipTypes; + return Civi::$statics[__CLASS__]['reltypes'][$fromXML]; } /** @@ -124,7 +125,7 @@ public function &allRelationshipTypes() { */ public static function flushStaticCaches() { self::$activityTypes = NULL; - self::$relationshipTypes = NULL; + unset(Civi::$statics[__CLASS__]['reltypes']); } } diff --git a/CRM/Case/XMLProcessor/Process.php b/CRM/Case/XMLProcessor/Process.php index 03501e993eff..4cfb5c8d7e1a 100644 --- a/CRM/Case/XMLProcessor/Process.php +++ b/CRM/Case/XMLProcessor/Process.php @@ -181,7 +181,11 @@ public function processActivitySet($activitySetXML, &$params) { * @return array|mixed */ public function &caseRoles($caseRolesXML, $isCaseManager = FALSE) { - $relationshipTypes = &$this->allRelationshipTypes(); + // Look up relationship types according to the XML convention (described + // from perspective of non-client) but return the labels according to the UI + // convention (described from perspective of client) + $relationshipTypes = &$this->allRelationshipTypes(TRUE); + $relationshipTypesToReturn = &$this->allRelationshipTypes(FALSE); $result = []; foreach ($caseRolesXML as $caseRoleXML) { @@ -195,7 +199,7 @@ public function &caseRoles($caseRolesXML, $isCaseManager = FALSE) { } if (!$isCaseManager) { - $result[$relationshipTypeID] = $relationshipTypeName; + $result[$relationshipTypeID] = $relationshipTypesToReturn[$relationshipTypeID]; } elseif ($relationshipTypeXML->manager == 1) { return $relationshipTypeID; @@ -213,7 +217,9 @@ public function &caseRoles($caseRolesXML, $isCaseManager = FALSE) { * @throws Exception */ public function createRelationships($relationshipTypeName, &$params) { - $relationshipTypes = &$this->allRelationshipTypes(); + // The relationshipTypeName is coming from XML, so the argument should be + // `TRUE` + $relationshipTypes = &$this->allRelationshipTypes(TRUE); // get the relationship $relationshipType = array_search($relationshipTypeName, $relationshipTypes); @@ -350,6 +356,8 @@ public function getDeclaredActivityTypes($caseTypeXML) { } /** + * Relationships are straight from XML, described from perspective of non-client + * * @param SimpleXMLElement $caseTypeXML * * @return array symbolic relationship-type names diff --git a/CRM/Case/XMLRepository.php b/CRM/Case/XMLRepository.php index 54011e77e3b9..243c74c5cc73 100644 --- a/CRM/Case/XMLRepository.php +++ b/CRM/Case/XMLRepository.php @@ -258,6 +258,8 @@ public function getAllDeclaredActivityTypes() { } /** + * Relationships are straight from XML, described from perspective of non-client + * * @return array symbolic-names of relationship-types */ public function getAllDeclaredRelationshipTypes() { From d0878639b0cca008795721ba6541c7e23b0c5925 Mon Sep 17 00:00:00 2001 From: Alice Frumin Date: Fri, 3 May 2019 11:31:54 -0400 Subject: [PATCH 5/9] dev/core#530 CiviCase: don't assume that clients or case role contacts are individuals --- CRM/Case/ManagedEntities.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CRM/Case/ManagedEntities.php b/CRM/Case/ManagedEntities.php index d3d33a027866..15bc97354f7e 100644 --- a/CRM/Case/ManagedEntities.php +++ b/CRM/Case/ManagedEntities.php @@ -123,6 +123,12 @@ public static function createManagedRelationshipTypes(CRM_Case_XMLRepository $xm $relTypes = $xmlRepo->getAllDeclaredRelationshipTypes(); foreach ($relTypes as $relType) { + // Making assumption that client is the A side of the relationship. + // Relationship label coming from XML, meaning from perspective of + // non-client. + + // These assumptions only apply if a case type is introduced without the + // relationship types already existing. $managed = [ 'module' => 'civicrm', 'name' => "civicase:rel:$relType", @@ -136,8 +142,8 @@ public static function createManagedRelationshipTypes(CRM_Case_XMLRepository $xm 'label_a_b' => "$relType is", 'label_b_a' => $relType, 'description' => $relType, - 'contact_type_a' => 'Individual', - 'contact_type_b' => 'Individual', + 'contact_type_a' => NULL, + 'contact_type_b' => NULL, 'contact_sub_type_a' => NULL, 'contact_sub_type_b' => NULL, ], From bb8b702c1c7b88ef789d7eabad2e7a9425a08def Mon Sep 17 00:00:00 2001 From: Alice Frumin Date: Fri, 3 May 2019 12:50:05 -0400 Subject: [PATCH 6/9] dev/core#530 CiviCase: save new case roles in correct direction for xml and ui --- ang/crmCaseType.js | 92 +++++++++++++++++++++++++-------- ang/crmCaseType/rolesTable.html | 3 +- 2 files changed, 72 insertions(+), 23 deletions(-) diff --git a/ang/crmCaseType.js b/ang/crmCaseType.js index 41f28286a81b..899ad35abf7e 100644 --- a/ang/crmCaseType.js +++ b/ang/crmCaseType.js @@ -74,22 +74,11 @@ limit: 0 } }]; - reqs.relTypes = ['Relationship', 'getoptions', { + reqs.relTypes = ['RelationshipType', 'get', { sequential: 1, - field: 'relationship_type_id', - context: 'create', - is_active: 1, - options: { - limit: 0 - } - }]; - reqs.relTypesForm = ['Relationship', 'getoptions', { - sequential: 1, - field: 'relationship_type_id', - context: 'create', - isForm: 1, is_active: 1, options: { + sort: 'label_a_b', limit: 0 } }]; @@ -272,17 +261,58 @@ $scope.activityTypes = _.indexBy(apiCalls.actTypes.values, 'name'); $scope.activityTypeOptions = _.map(apiCalls.actTypes.values, formatActivityTypeOption); $scope.defaultAssigneeTypes = apiCalls.defaultAssigneeTypes.values; - $scope.relationshipTypeOptions = _.map(apiCalls.relTypes.values, function(type) { - return {id: type.key, text: type.value}; - }); - $scope.defaultRelationshipTypeOptions = _.map(apiCalls.relTypesForm.values, function(type) { - return {value: type.key, label: type.value}; - }); + $scope.relationshipTypeOptions = getRelationshipTypeOptions(false); + $scope.defaultRelationshipTypeOptions = getRelationshipTypeOptions(true); // stores the default assignee values indexed by their option name: $scope.defaultAssigneeTypeValues = _.chain($scope.defaultAssigneeTypes) .indexBy('name').mapValues('value').value(); } + // Returns the relationship type options. If the relationship is + // bidirectional (Ex: Spouse of) it adds a single option otherwise it adds + // two options representing the relationship type directions (Ex: Employee + // of, Employer of). + // + // The default relationship field needs values that are IDs with direction, + // while the role field needs values that are names (with implicit + // direction). + // + // At any rate, the labels should follow the convention in the UI of + // describing case roles from the perspective of the client, while the + // values must follow the convention in the XML of describing case roles + // from the perspective of the non-client. + function getRelationshipTypeOptions($isDefault) { + return _.transform(apiCalls.relTypes.values, function(result, relType) { + var isBidirectionalRelationship = relType.label_a_b === relType.label_b_a; + if ($isDefault) { + result.push({ + label: relType.label_b_a, + value: relType.id + '_a_b' + }); + + if (!isBidirectionalRelationship) { + result.push({ + label: relType.label_a_b, + value: relType.id + '_b_a' + }); + } + } + else { + result.push({ + text: relType.label_b_a, + id: relType.name_a_b + }); + + if (!isBidirectionalRelationship) { + result.push({ + text: relType.label_a_b, + id: relType.name_b_a + }); + } + } + }, []); + } + /// initializes the case type object function initCaseType() { var isNewCaseType = !apiCalls.caseType; @@ -316,6 +346,15 @@ } }); }); + + // go lookup and add client-perspective labels for $scope.caseType.definition.caseRoles + _.each($scope.caseType.definition.caseRoles, function (set) { + _.each($scope.relationshipTypeOptions, function (relTypes) { + if (relTypes.text == set.name) { + set.displaylabel = relTypes.id; + } + }); + }); } /// initializes the selected statuses @@ -420,13 +459,15 @@ $scope.addRole = function(roles, roleName) { var names = _.pluck($scope.caseType.definition.caseRoles, 'name'); if (!_.contains(names, roleName)) { - if (_.where($scope.relationshipTypeOptions, {id: roleName}).length) { - roles.push({name: roleName}); + var matchingRoles = _.filter($scope.relationshipTypeOptions, {id: roleName}); + if (matchingRoles.length) { + var matchingRole = matchingRoles.shift(); + roles.push({name: roleName, displaylabel: matchingRole.text}); } else { CRM.loadForm(CRM.url('civicrm/admin/reltype', {action: 'add', reset: 1, label_a_b: roleName})) .on('crmFormSuccess', function(e, data) { var newType = _.values(data.relationshipType)[0]; - roles.push({name: newType.label_a_b}); + roles.push({name: newType.label_a_b, displaylabel: newType.label_b_a}); // Assume that the case role should be A-B but add both directions as options. $scope.relationshipTypeOptions.push({id: newType.label_a_b, text: newType.label_a_b}); if (newType.label_a_b != newType.label_b_a) { @@ -527,6 +568,13 @@ $scope.caseType.definition.activityAsgmtGrps = $scope.caseType.definition.activityAsgmtGrps.toString().split(","); } + function dropDisplaylabel (v) { + delete v.displaylabel; + } + + // strip out labels from $scope.caseType.definition.caseRoles + _.map($scope.caseType.definition.caseRoles, dropDisplaylabel); + var result = crmApi('CaseType', 'create', $scope.caseType, true); result.then(function(data) { if (data.is_error === 0 || data.is_error == '0') { diff --git a/ang/crmCaseType/rolesTable.html b/ang/crmCaseType/rolesTable.html index cc64a60a8eb6..e7edee076e6e 100644 --- a/ang/crmCaseType/rolesTable.html +++ b/ang/crmCaseType/rolesTable.html @@ -13,7 +13,8 @@ - {{relType.name}} + + {{relType.displaylabel}} From b79ff943000a9c506c9511da1ae61723911b1a3b Mon Sep 17 00:00:00 2001 From: Alice Frumin Date: Mon, 6 May 2019 12:21:21 -0400 Subject: [PATCH 7/9] dev/core#530 CiviCase: fixing tests to match new convention --- tests/karma/unit/crmCaseTypeSpec.js | 4 ++-- tests/phpunit/CRM/Case/BAO/CaseTypeForkTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/karma/unit/crmCaseTypeSpec.js b/tests/karma/unit/crmCaseTypeSpec.js index 4b0a221346be..2cfb624e2a12 100644 --- a/tests/karma/unit/crmCaseTypeSpec.js +++ b/tests/karma/unit/crmCaseTypeSpec.js @@ -352,13 +352,13 @@ describe('crmCaseType', function() { result.push({ label: relType.label_b_a, - value: relType.id + '_b_a' + value: relType.id + '_a_b' }); if (!isBidirectionalRelationship) { result.push({ label: relType.label_a_b, - value: relType.id + '_a_b' + value: relType.id + '_b_a' }); } }, []); diff --git a/tests/phpunit/CRM/Case/BAO/CaseTypeForkTest.php b/tests/phpunit/CRM/Case/BAO/CaseTypeForkTest.php index 09ca91dbe66c..4fce36f37aa1 100644 --- a/tests/phpunit/CRM/Case/BAO/CaseTypeForkTest.php +++ b/tests/phpunit/CRM/Case/BAO/CaseTypeForkTest.php @@ -39,7 +39,7 @@ public function testManagerContact() { ]); //Check if manager is correctly retrieved from xml processor. $xmlProcessor = new CRM_Case_XMLProcessor_Process(); - $this->assertEquals($relTypeID . '_b_a', $xmlProcessor->getCaseManagerRoleId('ForkableCaseType')); + $this->assertEquals($relTypeID . '_a_b', $xmlProcessor->getCaseManagerRoleId('ForkableCaseType')); } /** From cd2c9605ce23be865f91f8a9193ec5572bb5458d Mon Sep 17 00:00:00 2001 From: Alice Frumin Date: Wed, 8 May 2019 10:42:20 -0400 Subject: [PATCH 8/9] dev/core#530 CiviCase: using label instead of name to be consistent and commenting why --- ang/crmCaseType.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ang/crmCaseType.js b/ang/crmCaseType.js index 899ad35abf7e..4dc077a37b77 100644 --- a/ang/crmCaseType.js +++ b/ang/crmCaseType.js @@ -297,16 +297,18 @@ }); } } + // TODO The ids below really should use names not labels see + // https://lab.civicrm.org/dev/core/issues/774 else { result.push({ text: relType.label_b_a, - id: relType.name_a_b + id: relType.label_a_b }); if (!isBidirectionalRelationship) { result.push({ text: relType.label_a_b, - id: relType.name_b_a + id: relType.label_b_a }); } } @@ -455,6 +457,10 @@ activity.default_assignee_contact = null; }; + // TODO roleName passed to addRole is a misnomer, its passed as the + // label HOWEVER it should be saved to xml as the name see + // https://lab.civicrm.org/dev/core/issues/774 + /// Add a new role $scope.addRole = function(roles, roleName) { var names = _.pluck($scope.caseType.definition.caseRoles, 'name'); From f34c103499479213f09b854e2e5fdf804014b561 Mon Sep 17 00:00:00 2001 From: Alice Frumin Date: Thu, 9 May 2019 12:33:56 -0400 Subject: [PATCH 9/9] dev/core#530 CiviCase: when creating a new reltype assume client is A --- ang/crmCaseType.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ang/crmCaseType.js b/ang/crmCaseType.js index 4dc077a37b77..a1ff51ae5749 100644 --- a/ang/crmCaseType.js +++ b/ang/crmCaseType.js @@ -473,7 +473,7 @@ CRM.loadForm(CRM.url('civicrm/admin/reltype', {action: 'add', reset: 1, label_a_b: roleName})) .on('crmFormSuccess', function(e, data) { var newType = _.values(data.relationshipType)[0]; - roles.push({name: newType.label_a_b, displaylabel: newType.label_b_a}); + roles.push({name: newType.label_b_a, displaylabel: newType.label_a_b}); // Assume that the case role should be A-B but add both directions as options. $scope.relationshipTypeOptions.push({id: newType.label_a_b, text: newType.label_a_b}); if (newType.label_a_b != newType.label_b_a) {