Dynamics CRM: Automating record names

In every application that makes use of a relational database in a multi-tier architecture, beyond the unique identifiers for records within a table, it is common practice to allow records to be named in a way that would help users easily retrieve the relevant records, be it by search or browsing. In Microsoft Dynamics CRM is no different. Accounts are named by the company name while opportunities are named by their topic, and so on.

Since the idea of naming records is for the identification and retrieval of records, it makes sense to me that users can name Accounts and Contacts to their liking. However, this isn’t the case for Goal records in my organisation.

When creating Goals, users can type anything they want in the name field, which could range from sensible names like “James W Revenue Goal for Q4/2011” to cryptic names like “JW Rev Q4/11”, or anything but professional, like “Show me the money”. Sure we could come up with a naming convention that users should follow. The problem, evidently, is making sure that users will follow it.

The objective of this post is to propose a step-by-step solution for the automatic naming of Goal records following a naming convention through the use of one Javascript.

Defining our naming convention

The first step is to come up with a naming convention we would like to use. First I would look into my entity (in this case, Goals), and see which fields are relevant for identifying the record. I came up with:

  • Goal owner: To whom the goal applies to. I certainly want this field to be included in the Goal Name, so users can easily identify which Goals apply to whom.
  • Goal metric: Identifies what is being measured with the goal. Since users and teams can have multiple goals, such as one for measuring revenue targets, another for number of service cases and so on, I would like to easily identify which goal is for a given metric.
  • Time period: Finally, since users and teams can have multiple goals of the same metric for different periods, I would like to easily identify which goal for a given metric is on what period.

I considered adding the manager name or the parent goal name into the mix, but I reckon these three variables would be enough. Once we decide which fields should generate the record names, the next time is to decide how they will be combined. I came up with the following taxonomy:

  • For goals set in fiscal periods: <name of goal owner><goal metric> (<fiscal year>/<quarter>)
  • For goals set in custom periods: <name of goal owner><goal metric> (<from date><to date>)

Following this example, if we create a goal for the user John Doe with based on the metric Revenue in the Q2 (second quarter) of 2010, the goal would be automatically named as: John Doe - Revenue (2010/Q2).

If we create a goal for the user Mary Smith based on the metric Revenue from 01-June-2010 to 01-June-2011, the goal would be automatically named as: Mary Smith - Revenue (2010-06-01 - 2011-06-01).

The next step is to create the script we will use to enforce this naming convention.

Customise the Goal entity

We will now move on to Dynamics CRM and start customising our Goal entity. We will make the Name field read-only, add a javascript for the logic, and bind the javascript on form and control events.

Adding our javascript

First we must add the following javascript as a web resource in Dynamics CRM.You can find the step-by-step instructions on how to add a web resource below the code.

SetGoalName = function () {
  var CRM_FORM_TYPE = Xrm.Page.ui.getFormType();
  if (CRM_FORM_TYPE == 4 || CRM_FORM_TYPE == 3) { return; }

    var attr_title = Xrm.Page.getAttribute('title');
    attr_title.setSubmitMode('always');

    var name_goalownerid = '<goal owner>';
    var attr_goalownerid = new Array();
    attr_goalownerid = Xrm.Page.getAttribute('goalownerid').getValue();
    if (attr_goalownerid != null &amp;&amp; attr_goalownerid.length > 0) {
        var name_goalownerid = attr_goalownerid[0].name;
    }

    var name_metricid = '<goal metric>';
    var attr_metricid = new Array();
    attr_metricid = Xrm.Page.getAttribute('metricid').getValue();
    if (attr_metricid != null &amp;&amp; attr_metricid.length > 0) {
        var name_metricid = attr_metricid[0].name;
    }

    // When option Fiscal Period is selected
    if (Xrm.Page.getAttribute('isfiscalperiodgoal').getValue() == true) {
        var txt_fiscalperiod = '<quarter>';
        var txt_fiscalyear = '<fiscal year>';

        if (Xrm.Page.getAttribute('fiscalperiod').getSelectedOption() != null) {
            var txt_fiscalperiod = Xrm.Page.getAttribute('fiscalperiod').getSelectedOption().text;
        }

        if (Xrm.Page.getAttribute('fiscalyear').getSelectedOption() != null) {
            var txt_fiscalyear = Xrm.Page.getAttribute('fiscalyear').getSelectedOption().text;
        }

        Xrm.Page.getAttribute('title').setValue(name_goalownerid + ' - ' + name_metricid + ' (' + txt_fiscalyear + '/' + txt_fiscalperiod + ')');
    }

    // When option Custom Period is selected
    else {
        var date_goalstartdate = '<from>';
        var date_goalenddate = '<to>';
        var val_goalstartdate = Xrm.Page.getAttribute('goalstartdate').getValue();

        var y_goalstartdate = val_goalstartdate.getYear().toString();
        var m_goalstartdate = (val_goalstartdate.getMonth() + 1).toString();
        var d_goalstartdate = val_goalstartdate.getDate().toString();

        if (m_goalstartdate.length == 1) {
            m_goalstartdate = '0' + m_goalstartdate;
        }

        if (d_goalstartdate.length == 1) {
            d_goalstartdate = '0' + d_goalstartdate;
        }

        var date_goalstartdate = y_goalstartdate + '-' + m_goalstartdate + '-' + d_goalstartdate;
        var val_goalenddate = Xrm.Page.getAttribute('goalenddate').getValue();
        var y_goalenddate = val_goalenddate.getYear().toString();
        var m_goalenddate = (val_goalenddate.getMonth() + 1).toString();
        var d_goalenddate = val_goalenddate.getDate().toString();

        if (m_goalenddate.length == 1) {
            m_goalenddate = '0' + m_goalenddate;
        }

        if (d_goalenddate.length == 1) {
            d_goalenddate = '0' + d_goalenddate;
        }

        var date_goalenddate = y_goalenddate + '-' + m_goalenddate + '-' + d_goalenddate;

        Xrm.Page.getAttribute('title').setValue(name_goalownerid + ' - ' + name_metricid + ' (' + date_goalstartdate + ' - ' + date_goalenddate + ')');
    }

}
  1. Log-in into Dynamics CRM with an account that has system customisation privileges.
  2. In the navigation pane, click on the Settings module.
  3. In the navigation pane, under System, click on Customizations.
  4. In the workplace, click on Customize the System to open the customisation window.
  5. In the customisation window menu bar, click on New and select Web Resource to open the New Web Resource window.
  6. The New Web Resource window will load. Create a web resource with the following settings:
    • Name: new_AutoGoalNameSet
    • Display Name: Auto Goal Name Set
    • Description: This script will automatically set the name of Goal records based on key fields through concatenation
    • Type: Script (JScript)
  7. Click on the Text Editor button and paste the code provided above in the Edit Content window, then click on OKto close the text editor window.
  8. Back on the new web resource window, click on Save, followed by Publish, then close the window.

Setting the Name field as read-only

Next we must set the Name field in the Goal form as read-only in order to avoid users typing a value (since the whole purpose of this exercise is to automate the name of Goal records). Here is how to do it:

  1. Back at the customization window, in the navigation pane click on the arrow next to Entities to expand the entities list.
  2. Under Entities, look for the item Goal and click on the arrow next to it to expand its items.
  3. Under Goal, click on Forms.
  4. The list of forms for the Goal entity will load. Double-click on the form named Information (Form Type: Main, State: Managed).
  5. In the form designer for Goal’s Information form, double-click on the Name field under the General tab.
  6. The properties window for the Name field will load. Tick the option box Field is read-only, then click on OK.

Biding our javascript onto form and control events

The next step is to bind the javascript onto Goal form events and controls. The idea is that as users change the value of the related fields, the Name field will be automatically populated. So we want to add the javascript onto the following events:

  • Form – onSave
  • Goal Metric – onChange
  • Goal Owner – onChange
  • Goal Period Type – onChange
  • Fiscal Period – onChange
  • Fiscal Year – onChange
  • From – onChange
  • To – onChange

Here is how to do it:

  1. Back at the form designer for Goal’s Information form, on Form Properties.
  2. On the form properties window, click on the arrow next to Form Libraries to expand it.
  3. Under form libraries, click on the Add button.
  4. A look-up window for web resources will load. Select the web resource we created on step 6 of Add a javascript (new_AutoGoalNameSet) then click on OK.
  5. Back at the form properties window, click on the arrow next to Event Handlers to expand it.
  6. Under event handlers, make sure that the selected Control is Form, and that the selected Event is OnSave, then click on the Add button.
  7. The handler properties window will load. Make sure that the selected Library is new_AutoGoalNameSet.
  8. On the Function field, provide the name of our function: SetGoalName.
  9. Make sure the Enabled option is selected and click on OK to close the handler properties window and go back to the form properties. window, then click OK on the form properties window to close it as well.
  10. Back at the form designer, double-click the Goal Metric field.
  11. The properties window for the Goal Metric field will load. Click on the Events tab.
  12. Under the events tab, make sure that the selected Event is OnChange then click on the Add button
  13. The handler properties window will load. Make sure that the selected Library is new_AutoGoalNameSet.
  14. On the Function field, provide the name of our function: SetGoalName.
  15. Make sure the Enabled option is selected and click on OK to close the handler properties window and go back to the field properties. window, then click OK on the field properties window to close it as well.
  16. Repeat steps 11 to 15 for the following other fields: Goal Owner, Goal Period Type, Fiscal Period, Fiscal Year, From and To
  17. Back at the form designer, click on Save followed by Publish. After the customisation is published, you can close the from designer and customisation window.

Testing our customisation

We can now test our customisation. When creating goals, users would not be able to manually type the goal’s name. As users populate the fields Goal Metric and Goal Owner and sets the fiscal period, the name of the goal will change dynamically. Note also how the formatting for the period changes depending if the fiscal period is either a custom period or based on a set fiscal period.

A note on required fields: It is worth mentioning that since all the fields we used to set the goal record name with this script are required fields, this makes our code much easier since users must populate those fields in order to save the record. If we used some fields that weren’t required, we would have to add additional coding for handling fields left blank.

Wrapping up

I hope you can find this script useful. It has a lot of examples on how to gather text and data from lookup fields, text, date fields and boolean checks. I’d like to thank the gurus at the MSDN forum who helped me get to the end result of this script: Janu_m and pogo69.


1 Comment

  • Great post!

    I'm trying to implement this for the name creation of an order thats being submitted. The naming convention that I've selected is to set the name of the order to the company name and the date/time it was created.

    After trying to implement the code I get an error saying that my function is not an object function.

    Here is the code…

    SetOrderName = function () {
    var CRM_FORM_TYPE = Xrm.Page.ui.getFormType();
    if (CRM_FORM_TYPE == 4 || CRM_FORM_TYPE == 3) { return; }

    var attr_title = Xrm.Page.getAttribute("name");
    attr_title.setSubmitMode("always");

    var name_customerid = "<customer>";
    var attr_customerid = new Array();
    attr_customerid = Xrm.Page.getAttribute("customerid").getValue();
    if (attr_customerid != null && attr_customerid.length > 0) {
    var name_customerid = attr_customerid[0].customerid;
    }

    var name_createdon = "<created on>";
    var attr_createdon = new Array();
    attr_createdon = Xrm.Page.getAttribute("created on").getValue();
    if (attr_createdon != null && attr_createdon.length > 0) {
    var name_createdon = attr_createdon[0].name;
    }

    Xrm.Page.getAttribute("title").setValue(name_customerid + " – " + name_createdon);

    }

    I didn't use the if statements because I don't need it with my naming convention. I implemented the code on the customerid and createdon fields and for the form on save.

    I'm new to javascript, so I'm sure it is something that I'm missing in my script.

    Any thoughts?