New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PCHR-3625: Workflow creation, prompt user without manager #351
PCHR-3625: Workflow creation, prompt user without manager #351
Conversation
375e26b
to
7f8e6aa
Compare
@jamienovick can you please have a look at the result? I didn't have a mock, but I'm supposing it was more or less something like this. |
cid: vm.assignment.client_id | ||
}); | ||
|
||
CRM.loadForm(formUrl) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a service in L&A Reqangular for that, we may want to have the same service in T&A?
cc @AkA84
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could use the loadForm
from reqangular, but there are some issues:
- I also need
CRM.url
which is not in reqangular. - The reqangular one also suffers from the digest issue, so it needs to be augmented.
In short, there's no clear benefit on using the reqangular wrapper, the CRM variable can be accessed without issues from tests and all of its services can be used directly without having to wrap them and add more tests on top of them.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think leave it as is.
/** | ||
* Returns object pairings of all the activities and their parent activity type. | ||
* | ||
* @return {Array} Each elements contains an object that holds an activity and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each element
} | ||
}); | ||
|
||
// removes empty values: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need in this comment, compact
is pretty self explanatory.
* Returns all activites that are missing a default assignee related to the | ||
* target contact. | ||
* | ||
* @param {Array} activitiesAndTypes - An array of activities and their types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please align params and return.
* Finds the default assignee for the given activity type and assigns them | ||
* to their corresponding activity. | ||
* | ||
* @param {Array} activitiesAndTypes - An array of activities and their types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please align params and return.
} | ||
|
||
/** | ||
* It populates the `relationshipMissingWarnings` object based on activites |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Populates the... (no need in "it")
When creating a new relationship for the target contact we use I have tested the |
adc40b5
to
56b875d
Compare
2a51b9d
to
f604c76
Compare
…elationship to case client
…e modal assignment component
f604c76
to
2296405
Compare
Changes to the PR starting from commit PCHR-3625: Fix typo: OverviewThese commits add the following changes to the PR:
BeforeAfterTechnical detailsRelationship types are now loaded on initialization of the modal component for two reasons:
var relationshipTypesCache = {};
(function init () {
vm.loading.component = true;
initRelationshipTypesCache()
.then(initDefaultAssigneeOptionsIndex)
.then(initWatchers)
.finally(function () {
vm.loading.component = false;
});
}());
function initRelationshipTypesCache () {
return RelationshipType.all({ options: { limit: 0 } })
.then(function (relationshipTypes) {
relationshipTypesCache = _.indexBy(relationshipTypes.list, 'id');
});
} An important function introduced in these commits is function getRelationshipTypeDetails (defaultAssigneeRelationship) {
var relationshipTypeRules = defaultAssigneeRelationship.split('_');
var relationshipTypeId = relationshipTypeRules[0];
var relationshipTypeDirection = relationshipTypeRules[1];
var relationshipType = relationshipTypesCache[relationshipTypeId];
var isRelationshipTypeBidirectional = relationshipType.label_a_b === relationshipType.label_b_a;
var relationshipLabel = isRelationshipTypeBidirectional || relationshipTypeDirection === 'a'
? relationshipType.label_a_b
: relationshipType.label_b_a;
return {
id: relationshipType.id,
isBidirectional: isRelationshipTypeBidirectional,
sourceContactFieldName: 'contact_id_' + relationshipTypeRules[1],
targetContactFieldName: 'contact_id_' + relationshipTypeRules[2],
label: relationshipLabel
};
} To determine if a relationship type is bidirectional we need to compare their Displaying the relationship type label has now been modified so it works like this: function setRelationshipMissingWarnings (activitiesAndTypes) {
var activitiesWithMissingRelationships;
// skip if no contact has been selected:
if (!vm.assignment.client_id) {
return;
}
activitiesWithMissingRelationships = getActivitiesWithoutDefaultRelationship(activitiesAndTypes);
vm.relationshipMissingWarnings = _.chain(activitiesWithMissingRelationships)
.map(function (activityAndType) {
var relationshipTypeDetails = getRelationshipTypeDetails(
activityAndType.type.default_assignee_relationship);
return {
activity_type_id: activityAndType.activity.activity_type_id,
relationshipLabel: relationshipTypeDetails.label
};
})
.indexBy('activity_type_id')
.mapValues('relationshipLabel')
.value();
} This will return an object similar to: {
"456": "HR Manager is",
"689": "Spouse of"
} Where 456 and 689 are the ids of the activity type, which are used on the view to match if a specific activity is missing their default assignee by relationship. (this last part has not changed from the original PR). To query the default assignee using the relationship model we now need to know if the relationship type is bidirectional in order to change the filters. Here are some scenarios and their expected filters (simplified): // when the value is 123_a_b:
{
relationship_type_id: 123,
contact_id_a: targetContact.id
}
// when the value is 123_b_a:
{
relationship_type_id: 123,
contact_id_b: targetContact.id
}
// when the relationship type is bidirectional and the value is 123_a_b:
{
relationship_type_id: 123,
contact_id_a: targetContact.id,
contact_id_b: targetContact.id,
options: { or: [['contact_id_a', 'contact_id_b']] }
}
// this last one means "where the target contact is in either side of the relationship". The actual code looks like this: function getDefaultAssigneeForActivityTypeByRelationship (activityType) {
// ...
relationshipTypeDetails = getRelationshipTypeDetails(activityType.default_assignee_relationship);
filters = getDefaultAssigneeFiltersForRelationshipType(relationshipTypeDetails);
return Relationship.allValid(filters, null, null, canCacheRelationshipRequests)
.then(function (result) {
canCacheRelationshipRequests = true;
return result.list.map(function (relationship) {
if (relationshipTypeDetails.isBidirectional) {
// depending on the position of the target contact we return the opposite contact id:
return relationship.contact_id_a === vm.assignment.client_id
? relationship.contact_id_b
: relationship.contact_id_a;
} else {
return relationship[relationshipTypeDetails.targetContactFieldName];
}
});
});
}
function getDefaultAssigneeFiltersForRelationshipType (relationshipTypeDetails) {
var filters, sourceContactFieldName;
if (relationshipTypeDetails.isBidirectional) {
return {
relationship_type_id: relationshipTypeDetails.id,
contact_id_a: vm.assignment.client_id,
contact_id_b: vm.assignment.client_id,
options: {
or: [['contact_id_a', 'contact_id_b']],
limit: 1
}
};
} else {
sourceContactFieldName = relationshipTypeDetails.sourceContactFieldName;
filters = {
relationship_type_id: relationshipTypeDetails.id,
options: { limit: 1 }
};
// depending on the direction we can filter by either `contact_id_a` or `contact_id_b`:
filters[sourceContactFieldName] = vm.assignment.client_id;
return filters;
}
} ✅Manual Tests - done |
scope.assignment.client_id = _.uniqueId(); | ||
scope.assignment.case_type_id = selectedAssignment.id; | ||
scope.activity.activitySet = selectedAssignment.definition.activitySets[0]; | ||
} | ||
|
||
/** | ||
* Initializes the cache for assignment and document types. | ||
* Initializes the cache for assignment, tasks, and document types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please document why we are slicing arrays in half, this may be a bit difficult to understand without an explanation.
Overview
This PR displays a warning message when creating workflows and the target contact is missing a relationship that was to be selected by default. It also allows the user to create this relationship from within the workflow creation modal.
Before
The target contact is missing a relationship "HR Manager is" for the "Revoke Access to Database" activity. The assignee is shown as blank, but no warning is displayed.
After
Technical Details
The
initDefaultAssigneesForActivities
method was refactored so the sequence changed to first get the complete list of activities and parent activity types, then load and assign the default assignees, and finally check if any of the default assignees by relationships were not found and display warnings:getActivitiesAndTypes
returns the whole list of activities and their related activity type:setDefaultAssignees
gets the default assignees for the activity type and stores them in the activity (if found):setRelationshipMissingWarnings
checks if there are missing relationships and setups therelationshipMissingWarnings
object:Finally, the user can create the relationship by pressing a button that triggers the
createRelationshipForTargetContact
method: