Advanced territory management – Part 2 – Alphabetically Ordering Territories

3 minute read time.

This is the second part of a two part tutorial. You do not need to follow on part 1 for part two to make sense.
Part 2 is considered more advanced and should be treated as such as you need to be familiar with CRM's JavaScript classes and functions. 

For Part 1, see this link - https://www.sagecity.com/sage-global-solutions/sage-crm/b/sage-crm-hints-tips-and-tricks/posts/advanced-territory-management-part-1-re-ordering-territories

So now that we have the correct structure in place, lets see if we can do something else to our territory list to make it easier to use.For this exercise our territory structure will look like the following, I will be using the standard territory structure.

As you can see this list is not alphabetical, not only are the second levels not alphabetical, but neither are the third levels.

So what can we do to solve this problem.

Well we can try the tip in part 1 of the post, or we can just use some JavaScript to manipulate it a bit.

For the JavaScript code, I did not include all of it in the post, as you can download the entire code from this post or my git repository -> https://github.com/conrad-roux/SageCRMTerritoryOrdering.

I’ll just be covering some points in the code

crm.ready(function() {
var elementId = "select[id$='_secterr']";
//or to test in the main management list use
//#Terr_ParentID option

var options = $(elementId+' option');
    var arr = options.map(function(index, o) {
				return {
            _id: (index+1),
            parent: 0,
            text: $(o).text(),
            value: o.value,
            level: findBlanks($(o).html(),o.value),
            selected: $(o).is(':selected')
        };
    }).get();

First thing I do is use the crm.ready funtion to check the page load
Secondly I use a jQuery selector function to get the field and then create an array from it, this will get all the options and create and array
However, I am adding some properties to the array to create an object array so that I have more to work with, the import part here is that I need the parent id of each element, and to get that I used the spacing between the names, and called it level. This will help later when I need to find the parent.

That code all exists in the ready function, and will only execute when the page is done loading.

My second snippet is the code for the sorting function. This will handle the sort of the array after the extra properties have been added and the parent of each has been found

function sorting(workingArr)
{
var hashArr = {};

for (var i=0; i<workingArr.length; i++) {
  if (hashArr[workingArr[i].parent] == undefined) hashArr[workingArr[i].parent] = [];
  hashArr[workingArr[i].parent].push(workingArr[i]);
}

var result = hierarhySort(hashArr, 0, []);
return result;

function hierarchySortFunc(a,b ) {
  return a.text.trim() > b.text.trim() ? 1 : a.text.trim() < b.text.trim() ? -1 : 0;
}

function hierarhySort(hashArr, key, result) {
  if (hashArr[key] == undefined) return;
  var workingArr = hashArr[key].sort(hierarchySortFunc);
  for (var i=0; i<workingArr.length; i++) {
    result.push(workingArr[i]);
    hierarhySort(hashArr, workingArr[i]._id, result);
  }
  return result;
}
}

In this function there is 2 important things to note. 
The hash array that gets created based on the first array and the function hierarhySort is recursive into itself.
hashArr is an object, that contains multiple arrays in itself, each array is an array of the elements of the same parent. These arrays then get passed to check against each other and corrected if need be. In the end they all get added together to form 1 big array and passed back to the main function crm.ready

The crm.ready function then performs the last portion of the code

options.each(function(i, o) {
        //console.log(i);
        o.value = sortedArr[i].value;
        $(o).text(sortedArr[i].text);
        if(sortedArr[i].selected)$(o).prop("selected", true);
    });

Which is pushing the new values back to the jQuery selector that contains the object, and then pushes that to the front end.

Now your Territory list will look like this:

Feel free to fork the work off github. I also included some jsfiddle testing code for you to use or download the script from this post. The code file can be placed in the js folder in the CRM directory to make it work. I would suggest renaming the file to prepend zzz so that the file loads later in execution. Also the file extension should be changed to just be .js

As a last note, this code has not been tested on all scenarios, as well as that this is not the most efficient way of doing it and I am sure someone will find something that can be done better.