Tag Archives: button

Duplicate Record Button using Jscript in Microsoft CRM 2011

As detailed here, Rollup Pack 8 for Microsoft CRM 2011 gave us a couple of shiny new jscript toys:

Xrm.Utility.openEntityForm(name,id,parameters)

and

Xrm.Utility.openWebResource(webResourceName,webResourceData,width, height)

In this post I will show you how to add a “Duplicate Record” button to a CRM form, using the new openEntityForm function.

First, lets add the button.  Download Erik Pool’s ribbon editor from CodePlex.  If you are still editing ribbon xml manually and not using this tool then you are mad.   Run the tool, connect to your CRM organisation and open the ribbon you wish to edit.  I am going to add my button to the Case form, adding to the last Group of buttons on the right hand side.  I click in that Button Group and then click New Button and name my button:

image

My button appears small by default so I change the Template Alias to Large.  I need an icon for my button and the one I see on the “Copy Selected” button looks good so I can click on that button and copy and paste the image properties from that button over to my new button:

image

I tidy the Label and Tooltip and then click on the Action tab and enter the name of my jscript library and function, which we will create next:

image

Finally, I save the change and then check my button appears in CRM:

image

I want to demonstrate copying a range of data types so I add a few custom fields to my Case form:

image

Ok, now we can write the jscript function.  There are 3 steps to this:

1. Collect field values from the source record

2. Define the values you want to populate into the destination record

3. Pop the form, passing across the values to be populated

I’ve pasted just the first part below.  Most of this is just me using CRM’s getValue() functon, but you will see I do some null value checks on the lookup fields (to avoid errors) and I also need to extract multiple values from each lookup field.  You will also note for the date field I call another function so that the value returned by the getValue() call gets converted into the MM/DD/YYYY format that the next step requires:

function DuplicateCase() {
    //get values from the Form
    var CaseId = Xrm.Page.data.entity.getId();
    var CaseTitle = Xrm.Page.data.entity.attributes.get("title").getValue();
    if (Xrm.Page.data.entity.attributes.get("customerid").getValue() != null) {
        var CustomerId = Xrm.Page.data.entity.attributes.get("customerid").getValue()[0].id;
        var CustomerName = Xrm.Page.data.entity.attributes.get("customerid").getValue()[0].name;
        var CustomerType = Xrm.Page.data.entity.attributes.get("customerid").getValue()[0].entityType;
    }
    if (Xrm.Page.data.entity.attributes.get("subjectid").getValue() != null) {
        var SubjectId = Xrm.Page.data.entity.attributes.get("subjectid").getValue()[0].id;
        var SubjectName = Xrm.Page.data.entity.attributes.get("subjectid").getValue()[0].name;
    }
    var CaseOriginCode = Xrm.Page.data.entity.attributes.get("caseorigincode").getValue();
    var CaseTypeCode = Xrm.Page.data.entity.attributes.get("casetypecode").getValue();
    var CaseDate = FormatDate("new_dateofincident");  // wants "MM/DD/YYYY" (this might be environment specific though)
    var CaseUrgent = Xrm.Page.data.entity.attributes.get("new_urgent").getValue();
    var CaseClaimAmount = Xrm.Page.data.entity.attributes.get("new_claimamount").getValue();
    if (Xrm.Page.data.entity.attributes.get("transactioncurrencyid").getValue() != null) {
        var CurrencyId = Xrm.Page.data.entity.attributes.get("transactioncurrencyid").getValue()[0].id;
        var CurrencyName = Xrm.Page.data.entity.attributes.get("transactioncurrencyid").getValue()[0].name;
    }
    if (Xrm.Page.data.entity.attributes.get("ownerid").getValue() != null) {
        var OwnerId = Xrm.Page.data.entity.attributes.get("ownerid").getValue()[0].id;
        var OwnerName = Xrm.Page.data.entity.attributes.get("ownerid").getValue()[0].name;
        var OwnerType = Xrm.Page.data.entity.attributes.get("ownerid").getValue()[0].entityType;
    }
    var CaseDescription = Xrm.Page.data.entity.attributes.get("description").getValue();

Here’s the second section of the function, here I am basically placing each of the values I extracted during step 1 into a parameter object (testing for and excluding null values as I go):

 

    //define default values for new Incident record
    var parameters = {};
    if (CaseTitle != null) {
        parameters["title"] = CaseTitle + " - COPY";
    }
    if (CustomerId != null && CustomerName != null) {
        parameters["customerid"] = CustomerId;
        parameters["customeridname"] = CustomerName;
        parameters["customeridtype"] = CustomerType;
    }
    if (SubjectId != null && SubjectName != null) {
        parameters["subjectid"] = SubjectId;
        parameters["subjectidname"] = SubjectName;
    }
    if (CaseOriginCode != null) {
        parameters["caseorigincode"] = CaseOriginCode;
    }
    if (CaseTypeCode != null) {
        parameters["casetypecode"] = CaseTypeCode;
    }
    if (CaseDate != null) {
        parameters["new_dateofincident"] = CaseDate;
    }
    if (CaseUrgent != null) {
        parameters["new_urgent"] = CaseUrgent;
    }
    if (CaseClaimAmount != null) {
        parameters["new_claimamount"] = CaseClaimAmount;
    }
    if (CurrencyId != null && CurrencyName != null) {
        parameters["transactioncurrencyid"] = CurrencyId;
        parameters["transactioncurrencyidname"] = CurrencyName;
    }
    if (OwnerId != null && OwnerName != null) {
        parameters["ownerid"] = OwnerId;
        parameters["owneridname"] = OwnerName;
        parameters["owneridtype"] = OwnerType;
    }
    if (CaseDescription != null) {
        parameters["description"] = CaseDescription;
    }
    if (CaseId != null && CaseTitle != null) {
        parameters["new_parentcase"] = CaseId;
        parameters["new_parentcasename"] = CaseTitle;
    }

And the last bit is simply:

    //pop incident form with default values
    Xrm.Utility.openEntityForm("incident", null, parameters);
}

Here’s how it looks in action.  Here’s my source record:

image

And here’s what pops up when I click the button:

image

To use this yourself in your unique scenarios you will obviously need to edit the getValue() and parameters lines to match your fields.  I’ve covered off the main data types and added a little bit of robustness to help guide you on this.   The openEntityForm utility certainly helps out here.

Here’s the jscript function in full and the missing FormatDate function:

function DuplicateCase() {
    //get values from the Form
    var CaseId = Xrm.Page.data.entity.getId();
    var CaseTitle = Xrm.Page.data.entity.attributes.get("title").getValue();
    if (Xrm.Page.data.entity.attributes.get("customerid").getValue() != null) {
        var CustomerId = Xrm.Page.data.entity.attributes.get("customerid").getValue()[0].id;
        var CustomerName = Xrm.Page.data.entity.attributes.get("customerid").getValue()[0].name;
        var CustomerType = Xrm.Page.data.entity.attributes.get("customerid").getValue()[0].entityType;
    }
    if (Xrm.Page.data.entity.attributes.get("subjectid").getValue() != null) {
        var SubjectId = Xrm.Page.data.entity.attributes.get("subjectid").getValue()[0].id;
        var SubjectName = Xrm.Page.data.entity.attributes.get("subjectid").getValue()[0].name;
    }
    var CaseOriginCode = Xrm.Page.data.entity.attributes.get("caseorigincode").getValue();
    var CaseTypeCode = Xrm.Page.data.entity.attributes.get("casetypecode").getValue();
    var CaseDate = FormatDate("new_dateofincident");  // wants "MM/DD/YYYY" (this might be environment specific though)
    var CaseUrgent = Xrm.Page.data.entity.attributes.get("new_urgent").getValue();
    var CaseClaimAmount = Xrm.Page.data.entity.attributes.get("new_claimamount").getValue();
    if (Xrm.Page.data.entity.attributes.get("transactioncurrencyid").getValue() != null) {
        var CurrencyId = Xrm.Page.data.entity.attributes.get("transactioncurrencyid").getValue()[0].id;
        var CurrencyName = Xrm.Page.data.entity.attributes.get("transactioncurrencyid").getValue()[0].name;
    }
    if (Xrm.Page.data.entity.attributes.get("ownerid").getValue() != null) {
        var OwnerId = Xrm.Page.data.entity.attributes.get("ownerid").getValue()[0].id;
        var OwnerName = Xrm.Page.data.entity.attributes.get("ownerid").getValue()[0].name;
        var OwnerType = Xrm.Page.data.entity.attributes.get("ownerid").getValue()[0].entityType;
    }
    var CaseDescription = Xrm.Page.data.entity.attributes.get("description").getValue();

    //define default values for new Incident record
    var parameters = {};
    if (CaseTitle != null) {
        parameters["title"] = CaseTitle + " - COPY";
    }
    if (CustomerId != null && CustomerName != null) {
        parameters["customerid"] = CustomerId;
        parameters["customeridname"] = CustomerName;
        parameters["customeridtype"] = CustomerType;
    }
    if (SubjectId != null && SubjectName != null) {
        parameters["subjectid"] = SubjectId;
        parameters["subjectidname"] = SubjectName;
    }
    if (CaseOriginCode != null) {
        parameters["caseorigincode"] = CaseOriginCode;
    }
    if (CaseTypeCode != null) {
        parameters["casetypecode"] = CaseTypeCode;
    }
    if (CaseDate != null) {
        parameters["new_dateofincident"] = CaseDate;
    }
    if (CaseUrgent != null) {
        parameters["new_urgent"] = CaseUrgent;
    }
    if (CaseClaimAmount != null) {
        parameters["new_claimamount"] = CaseClaimAmount;
    }
    if (CurrencyId != null && CurrencyName != null) {
        parameters["transactioncurrencyid"] = CurrencyId;
        parameters["transactioncurrencyidname"] = CurrencyName;
    }
    if (OwnerId != null && OwnerName != null) {
        parameters["ownerid"] = OwnerId;
        parameters["owneridname"] = OwnerName;
        parameters["owneridtype"] = OwnerType;
    }
    if (CaseDescription != null) {
        parameters["description"] = CaseDescription;
    }
    if (CaseId != null && CaseTitle != null) {
        parameters["new_parentcase"] = CaseId;
        parameters["new_parentcasename"] = CaseTitle;
    }

    //pop incident form with default values
    Xrm.Utility.openEntityForm("incident", null, parameters);
}

// This function takes the fieldname of a date field as input and returns the value of that field in MM/DD/YYYY format
// Note: the day, month and year variables are numbers
function FormatDate(fieldname) {
    var d = Xrm.Page.data.entity.attributes.get(fieldname).getValue();  
    if (d != null) {
        var curr_date = d.getDate();
        var curr_month = d.getMonth();
        curr_month++;  // getMonth() considers Jan month 0, need to add 1
        var curr_year = d.getFullYear();
        return curr_month + "/" + curr_date + "/" + curr_year;
    }
    else return null;
}
Advertisements

Launching a Dialog from a Ribbon Button in CRM 2011

Here’s some sample code that demonstrates how to execute a Dialog from a Ribbon button in CRM 2011.  The solution sample is provided in managed and unmanaged formats:

Managed Solution (updated: 25 Feb 2011)

Unmanaged Solution (updated: 25 Feb 2011)

note: this has only been tested successfully in CRM Online 

I have built out a Dialog based solution for a call centre, further extending the solution I documented in one of my earlier blogs.  The scenario is as follows:

A call centre agent takes both sales and service inbound calls.  They are tasked with:

  • searching for the caller in CRM and adding them as a new Contact if not found
  • determining whether the call is sales or service related
  • logging the phone call in CRM
  • recording a new opportunity or case
  • informing the caller they will be called back within 24 hours

I wanted a Dialog based solution that would guide the call centre agent through the process with the minimal number of clicks.  I didn’t want the user to have to create the initial CRM record required for the Dialog, I wanted the user to simply click a button.   My solution design makes use of the REST End Point to retrieve the GUID of the Dialog that should be launched and also to create the initial Phone Call record.   Dialog’s are URL addressable so launching the dialog is not difficult once you have the context record and the Dialog GUID.  In the Dialog itself I make use of Dialog Variables and Database Queries to query CRM to see if the Caller exists and to present a list of possible matches back to the user.   Pretty powerful stuff.

 

The end result looks like this…

The user has an incoming call, they click the Incoming Call button. 

 

image

The Button executes a jscript function that does a window.open, launching an HTML page I’ve loaded into CRM as a web resource.

 

An HTML page pops indicating a Phone Call is being created and the Call Script is being loaded:

image

This HTML page is used to reference some jscript libraries I need to extract the CRM context and for the execution of the REST call.  The HTML page provides the progress indicators to the user and then executes a jscript function that does the heavy lifting.

The jscript function:

  • queries the REST end point for Processes to retrieve the GUID of the “Inbound Call Script” Dialog Process.
  • uses the REST end point for Phone Calls to create a Phone Call record.
  • formulates a URL using the CRM context, Dialog GUID and the Phone Call GUID
  • does a window.open to launch that URL

 

And then the Dialog appears:

image

 

The Call Centre Agent greets the caller, notes there reason for calling and collects their name (as prompted by the Dialog script):

image

 

The script then prompts the agent to find out whether the caller has an existing account:

image

 

If the caller indicates they are an existing customer a Contact search page is presented:

image

 

A search is executed against CRM using the search strings entered (doing a “like” search).  And the search results are returned to the agent for review:

image

 

The user can go back and modify their search, or they can select one of the search results, or they can indicate the caller was not found in the database:

image

 

Finally, the agent selects the nature of the enquiry so that an Opportunity or Case record can be created in CRM:

image

 

The final page of the Dialog provides the call wrap up script:

image

 

Behind the scenes a Phone Call record has been created and linked to either an existing Contact or to a new Contact.  A Case or an Opportunity has been created and linked to the Contact.  And the Phone Call has been marked as completed.

 

I was pretty impressed by the extensibility offered by the CRM platform here.  The REST end point performed a little slowly for me but I haven’t tried to performance tune anything here.  The Dialog functionality largely bent the way I needed it to.  The area it was weakest was the display of the Query Results, where a pick list is not really suitable.  

 

To see this in action:

– Download the solution, install, publish and refresh your browser

(Known Issue: Check the Dialog is Active.  The Dialog definition contains references to the Australian currency which prevent it from publishing in instances missing that currency.  Edit those mappings as required)

– Navigate to Workplace –> Contacts

– The “Incoming Call” button should appear on the Ribbon (it appears on the “Main” tab for the core data entities, i.e. on the Account and Contact ribbons)

– Click the button

– Navigate the dialog

– At conclusion you should see:

   – A new completed Phone Call record

   – Attached to either a new Case or a new Opportunity

   – And both records related to either an existing Contact or a new Contact

Adding a button to the CRM toolbar to launch a CRM view

I have found that frequently there will be one key entity in CRM that users spend most of their time on.  Sometimes its Cases, other times its Opportunities.  No matter which entity it is you can improve the user experience by providing faster access to that entity by adding a toolbar button via CRM’s ISV config settings.  Here’s how…

Firstly, this is how it looks.   I’ve added a “My Opportunities” button to the CRM toolbar.  When clicked, a new Internet Explorer window is launched displaying the Opportunity entity, and specifically, displaying the My Open Opportunities view.  Not only have I given my users a 1-click navigation solution but I have also given them a way of launching this key view in a separate window where it can remain open and easily accessible whilst they jump around between other entities back in the main CRM window.    This works in the Outlook client and the web client, but the gains are probably felt more when using the Outlook client as you no longer ‘lose your place’ as you flick back to your Inbox or over to your Calendar.

Here’s what the ISV config entry looks like:

This sits under the <ToolBar> node.

The Button Icon attribute defines the relative path to the button’s image file.   You can create you own image files and point to those but I find you can normally find something useful in the _imgs folder on the CRM server.

The URL attribute is where we specify the URL of the view.   Easiest way to determine the URL of a view is:

– Navigate to the view in CRM

– From the More Actions menu, select Copy Shortcut Of Current View…

– Paste this into Notepad

Quick and easy, but makes CRM just that little bit more enjoyable to work with.