INTRODUCTION
Have you ever wondered what a subject is in Dynamics CRM? Did you feel anytime that subject is mysterious element in CRM? Or have you struggled anytime trying to decode or dig deeper into the subject and failing? If you have encountered any of the symptoms above or if you have faced troubles with subjects in Dynamics CRM, then you are exactly at the right place. This article will open the mystery behind subject and will provide you a 360-degree view of subject hierarchy.
ADD, EDIT & DELETE SUBJECTS
To add a new subject, edit the label of existing subject or delete any of the subject, you need to first browse to subject entity (Settings -> Service Management -> Subject). You can simply play around using 3 options present on left side of your screen as well as expanding or contracting subjects to make any modification to existing subject hierarchy. This post is intended for matured CRM users who perform complex operations with subject entity.
ARCHITECTURE OF SUBJECT ENTITY
If you investigate default solution, you can find the subject entity. After expanding it and selecting fields, there are 3 fields which are useful for us:
- Feature Mask: If this field is set to 1, respective subject is visible in user interface. If it is 0, then you are masking or hiding the subject.
- Title: This field is single line text field which captures the subject title (the name of subject).
- Parent Subject: It is a lookup field (lookup to another subject record) which will point to the parent subject’s record.
If you look at the subject hierarchy, every new subject you add from the front end, a record of subject entity will be created in the back-end with feature mask set to 1 (visible in UI), title field is set to subject’s name and parent subject lookup is set to the parent subject record that. Unfortunately, you cannot see a subject record from UI or search for a subject record through advance find. The data in subject entity is completely hidden from UI and can only be viewed from CRM SDK messages.
DATA MIGRATION & DATA INTEGRATION OF SUBJECTS
One practical challenge we faced is we have a whole lot of subjects in our Development environment. The next question for us is how should we migrate all the subjects to stage, production by preserving the GUID? It is impractical if you manually re-create the whole subjects in production. Moreover, the manually created subjects will get a new GUID thus there is some data inconsistency in terms of GUID between development & production CRM.
To export subject data from development CRM along with GUID, we use a native Microsoft tool called CMT (configuration migration tool). This tool is generally used to move configuration data (teams, goals etc) by preserving GUID between different environments. For detailed knowledge on how to use CMT, please follow below link:
http://survivingcrm.com/2014/06/configuration-migration-tool-working-smarter-metadata-entities/
After you have exported subject entities data into a .txt file, this is how it looks like with all critical columns:
You have 5 columns in the exported file:
- Featuremask: If you want to hide a subject, simply set this to 0 or else set it to 1 to display subject in the front-end UI.
- Parent Subject ID: GUID of parent subject record
- Subject ID: GUID of subject record
- Parent subject: Title of parent subject record
- Title: Title of subject record
These columns are sufficient enough to perform subject data import into stage & production CRM environments by preserving GUID’s. Now using the native CRM out of box data import feature, you can carefully map the fields and import the subject data thereby preserving GUID’s.
If you have a limited set of subjects data, you can always create a console application and user service.Retrieve() to retrieve related subject record and edit data in it.
PRECAUTIONS WHILE DELETING SUBJECT
Be careful when you want to delete a subject inside a subject hierarchy. If you have complex 3-4 level hierarchy and want to get rid of a parent subject, always remember to start deleting all child subjects and then only delete the parent subject. I faced issue where I deleted parent subject and I couldn’t be able to delete any of the child subject records because every child subject record has a lookup to parent subject record. Since parent subject record no longer exists, the child subject records which have a lookup to parent record became meaningless. The only way to overcome this problem is to set featuremask bit to 0 for those child subject records.
EXTRACT THE SUBJECT HIERARCHY INTO CUSTOM FIELDS ON THE FORM
Recently, we implemented a business requirement as part of customer service module implementation where we have both 3 & 4 level subject hierarchy and whenever a CS representative selects a subject, all the 4 subjects in the hierarchy must be captured in 4 different custom fields on CS form and the whole subject hierarchy separated by : is captured in a single line of text field to display the whole selected subject tree in a single glance.
We have written java script to trigger it on change of subject field. We are making WebAPI calls inside the java script to retrieve a subject record and XRM scripts to populate the fields on CS form.
The following is the javascript we have written:
function getSubject() {
debugger;
var lookupObject = Xrm.Page.getAttribute("subjectid").getValue();
var fullname = "";
if (lookupObject != null && lookupObject != undefined) {
var childName = lookupObject[0].name;
var columnSet = "_parentsubject_value";
var result = webApi_Retrieve("subjects", lookupObject[0].id, columnSet, false);
if (result != null && result != undefined) {
var parentName = result["_parentsubject_value@OData.Community.Display.V1.FormattedValue"];
var parentId = result["_parentsubject_value"];
if (parentName == null || parentName == undefined) {
alert('You selected incorrect subject Hierarchy');
return;
}
var presult = webApi_Retrieve("subjects", parentId, columnSet, false);
if (presult != null && presult != undefined) {
var mainParent = presult["_parentsubject_value@OData.Community.Display.V1.FormattedValue"];
var topParentId = presult["_parentsubject_value"];
var tpresult = webApi_Retrieve("subjects", topParentId, columnSet, false);
var topParent = tpresult["_parentsubject_value@OData.Community.Display.V1.FormattedValue"];
if (topParent != null && topParent != undefined) {
fullname = topParent + " : " + mainParent + " : " + parentName + " : " + childName;
Xrm.Page.getAttribute("new_subjecthierarchy").setValue(fullname);
Xrm.Page.getAttribute("new _tier1").setValue(topParent);
Xrm.Page.getAttribute("new _tier2").setValue(mainParent);
Xrm.Page.getAttribute("new _tier3").setValue(parentName);
Xrm.Page.getAttribute("new _tier4").setValue(childName);
}
else {
fullname = mainParent + " : " + parentName + " : " + childName;
Xrm.Page.getAttribute("new _subjecthierarchy").setValue(fullname);
Xrm.Page.getAttribute("new _tier1").setValue(mainParent);
Xrm.Page.getAttribute("new _tier2").setValue(parentName);
Xrm.Page.getAttribute("new _tier3").setValue(childName);
Xrm.Page.getAttribute("new _tier4").setValue("");
}
WebApi_Retrieve is a custom function which accepts subject record GUID and makes Web API calls to CRM server and it returns result object which can be decoded to retrieve the subject title and parent subject GUID.
function webApi_Retrieve(entityName, recordId, columnSet, async) {
var results;
var req = new XMLHttpRequest();
var url = window.parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/" + entityName + "(" + recordId.replace('{', '').replace('}', '') + ")?$select=" + columnSet;
req.open("GET", url, async);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");
req.send();
if (req.status == 200) {
results = JSON.parse(req.response);
}
else {
alert(req.statusText);
return null;
}
return results;
}
}
}
}
}