Dynamics CRM 2011: Diving into the sales pipeline – Part 2

In my previous post of this series, we discussed some fundamental concepts of the sales pipeline and how Dynamics CRM handles the sales process. While considering the requirements of ACME (a fictitious company), we defined the stages of their sales pipeline, and started to outline the functional specification of the opportunity entity, including the elaboration of an opportunity probability matrix.

In this post we will discuss how to apply what have been defined so far by covering the customisation of the Opportunity entity form, including some scripting.

Important Update: This article series has been written before the release of Dynamics CRM 2011 Update Rollup 12 (also known as Polaris). You might continue to read the articles in this series, but it is strongly recommended that you read this addendum before performing any customisations suggested in the series.

So let’s start by customising the Opportunity form.

Opportunity form: Adding and editing fields

  1. Add the Process Code field (salesstagecode) above the Rating field (opportunityratingcode).
  2. Edit the Process Code field properties. Under Display, change the field label to Pipeline Phase.
  3. Now under Details, click on Edit to add the Pipeline Phase stages as options into the option set field, remembering the caveats concerning naming and numbering the pipeline phases we mentioned in the previous post. As per the previous post, we should add the following options:
    1 - Prospect
    2 - Proposal
    3 - Negotiation

    Make sure to save and close the properties dialog for the Process Code field.
  4. Back at the Opportunity form under Forecast Information, make the Probability field (closeprobability) read-only.
  5. Add the Pipeline Phase field (stepname) just under the Probability field, which is the field used by the Sales Pipeline Funnel Chart. Change the properties of the field and make sure that under Visibility, the field is not selected as visible by default.

Our Goal: What we are going to achieve here is that using the Process Code field labeled (i.e.: “disguised”) as the Pipeline Phase field, users will be able to manually select the Pipeline Phase in which the opportunity is at. The label of the option selected in this field will then be copied into the real Pipeline Phase field, which we just added as hidden in the form. Furthermore, we made the Probability field read-only because its value will be automatically calculated based on the combination of values from the Pipeline Phase and Rating fields, following our probability matrix we defined in the previous post.

Adding our JScript: Creating the web resource

In order to achieve the goal mentioned above, we must add some code that will work with the customisation we did so far.

  1. Create a new JScript web resource and name it new_SalesPipeline.
  2. Add the following code into the web resource:
SalesPipelineProbCalc = function() {
  var CRM_FORM_TYPE = Xrm.Page.ui.getFormType();
  if (CRM_FORM_TYPE == 4 || CRM_FORM_TYPE == 3) { return; }

 // Get value of Pipeline Phase set by user
 if (Xrm.Page.getAttribute("salesstagecode").getSelectedOption() != null)
 {var val_salesstagecode = Xrm.Page.getAttribute("salesstagecode").getSelectedOption().value;}
 else {var val_salesstagecode = null;}

 // Get text of Pipeline Phase set by user (this text should be the same for all languages)
 if (Xrm.Page.getAttribute("salesstagecode").getSelectedOption() != null)
 {var txt_salesstagecode = Xrm.Page.getAttribute("salesstagecode").getSelectedOption().text;}
 else {var txt_salesstagecode = null;}

  // Set the text of Pipeline Phase field

 // Set the submition mode of closeprobability (which is set as read-only) in order to commit the value when saving the record
 var closeprobabilityAttr = Xrm.Page.getAttribute("closeprobability");

 // Get value of the Rating selected by user
 if (Xrm.Page.getAttribute("opportunityratingcode").getSelectedOption() != null)
 {var val_rating = Xrm.Page.getAttribute("opportunityratingcode").getSelectedOption().value;}
 else {var val_rating = null;}

 // Calculation of Close Probability Rating as per Phase X Rate matrix
 if (val_salesstagecode == 1 && val_rating == 3)
 { Xrm.Page.getAttribute("closeprobability").setValue(10); }
 else if (val_salesstagecode == 1 && val_rating == 2)
 { Xrm.Page.getAttribute("closeprobability").setValue(25); }
 else if (val_salesstagecode == 1 && val_rating == 1)
 { Xrm.Page.getAttribute("closeprobability").setValue(50); }
 else if (val_salesstagecode == 2 && val_rating == 3)
 { Xrm.Page.getAttribute("closeprobability").setValue(20); }
 else if (val_salesstagecode == 2 && val_rating == 2)
 { Xrm.Page.getAttribute("closeprobability").setValue(50); }
 else if (val_salesstagecode == 2 && val_rating == 1)
 { Xrm.Page.getAttribute("closeprobability").setValue(75); }
 else if (val_salesstagecode == 3 && val_rating == 3)
 { Xrm.Page.getAttribute("closeprobability").setValue(40); }
 else if (val_salesstagecode == 3 && val_rating == 2)
 { Xrm.Page.getAttribute("closeprobability").setValue(75); }
 else if (val_salesstagecode == 3 && val_rating == 1)
 { Xrm.Page.getAttribute("closeprobability").setValue(95); }
 else { Xrm.Page.getAttribute("closeprobability").setValue(0); }

  • Save your changes and make sure to publish the customisation.

A warning on pipeline phases: At the moment, users can move both forward and backwards in the sales pipeline stages without any limitations. For instance, a user could jump from step 1 to 3, or move backwards from step 2 to 1. There are two issues at hand here. First, I personally don’t think an opportunity should move backwards — it should either move forward or be cancelled/lost. Second, the default Sales Pipeline report is based on steps within a workflow that we have yet to create (which we will cover in my next post), and workflows created within Dynamics CRM doesn’t allow us to jump backward on steps.
A workaround would be to add a JScript that prevent users from moving backward on the pipeline phases. See the “bonus JScript” section below for further information.

Opportunity form: Binding JScript into form and fields

Now that we added our JScript as a web resource, we must bind it into the Opportunity form and into field events to ensure the calculation will take place when the user changes the correspondent values within the form. So let’s go back to customise the Opportunity form.

  1. Click the Form Properties button located in the Ribbon. In the Form Properties dialog under the Events tab, click on Add under the Forms Libraries section and select the new_SalesPipeline web resource we created previously. Then close the Form Properties.
  2. Double click on the Process Code field (which we labeled Pipeline Phase) in order to bring its properties. In the Field Properties dialog under the Events tab, click on Add under the Event Handlers section.
  3. On the Handler Properties dialog, where it says Library, select new_SalesPipeline. Where it says Function, type in SalesPipelineProbCalc, which is the name of the function we created in the new_SalesPipeline web resource. Then click on OK, then OK again in the previous dialog to confirm our customisation.
  4. Repeat the two previous steps (2, and 3) for the Rating field.
  5. Save the form and publish the customisation.

What we just did: We have now added the JScript library we created (i.e.: the JScript web resource) into the Opportunity form and bound the SalesPipelineProbCalc into the onChange event for the Process Code (“disguised” as Pipeline Phase) and Rating fields. So when the user changes the value of either one of these fields, the value within the Probability field will change accordingly as per the Probability Matrix we defined previously.
onLoad Form: We could have add this function to the form’s onLoad event. This means that as soon as the form loads, the script will be executed. The script contains a conditional at the top that checks if the form isn’t read-only (e.g.: if viewing a closed opportunity) in order to execute the calculation.

Testing our solution

When can now go ahead and test our solution by creating an opportunity. We can manually specify the pipeline phase in which the opportunity is at, and this will reflect in the Pipeline Funnel chart. We can also give our opportunity the rating of hot, warm or cold, and along with the pipeline phase selected, the system will automatically calculate the probability for closing the opportunity.

Bonus JScript: Filtering option set values

In order to prevent users from jumping backward or skipping phases from the sales pipeline, we can filter the options available within the Pipeline Phase option set based on the current value with a simple JScript:

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

 // Get value of Pipeline Phase set by user
 if (Xrm.Page.getAttribute("salesstagecode").getSelectedOption() != null)
 {var val_salesstagecode = Xrm.Page.getAttribute("salesstagecode").getSelectedOption().value;}
 else {var val_salesstagecode = null;}

 if (val_salesstagecode == 1)
 { Xrm.Page.getControl("salesstagecode").removeOption(3); }

 else if (val_salesstagecode == 2)
 { Xrm.Page.getControl("salesstagecode").removeOption(1); }

 else if (val_salesstagecode == 3)


Make sure you add this JScript on the onLoad event of the form as well as in the onChange event of the Process Code field (salesstagecode) field (labeled as Pipeline Phase in our customised form).

Coming up next: Creating a workflow

In my next post we will be discussing how to create workflows to support our customisation. This is important if we want to use the default Sales Pipeline report that comes with Dynamics CRM.


  • This is a great post. thanks!!

  • When is step three?

    • Hi,

      In a week or two. Thanks for stopping by =)

  • Hey think i found an error in bonus jscript. The var val_salesstagecode was undefined. Let me back up. I added the bonus script to the new_SalesPipeline lib. I added the functions to the onload/onchange events. When I open an opp form I got an var undefined error. I copied // Get value of Pipeline Phase set by user code into the Optfilter code and it seemed to work. Have I made a mistake?

    • You're absolutely right! You did the right thing.

      I used to have the bonus script together with the SalesPipelineProbCalc() function and I split it up for this article. I have updated the script accordingly.

      Thanks for letting me know!


  • Have you ever looked at the Quote-Order-invoice process? Specifically, have you ever created a custom quote template?

    • Not sure I understand what you mean by "quote template". Do you mean a mail merge, or some sort of method to build a quote based on a predefined set of products? If you mean the latter, then I would suggest creating a dialog process for it.

  • Hi, Thank you so much for this insightful post. I have followed all the steps, but am unable to get the % Probably to change according to the matrix mentioned in your previous post. The pipeline stages in "both" pipeline fields seem to be working and updating the relevant charts. Is there something I could have missed?

    • Hi,

      Perhaps it is the script is not bound to the field onChange events? Did you published the customisations as well?

  • Have ever created a link between a web form (e.g. contact us form) and Dynamics Lead form? I'd like to automatically create a CRM lead anytime someone fills out our contact us or newsletter request web forms.

    • Hi,

      This is possible and yes; I did it before. If your Dynamics CRM 2011 implementation is on-premises (i.e.: not hosted by Microsoft) then you need to develop the solution. Shouldn\’t be hard if you\’re a developer. I suggest you start here: http://msdn.microsoft.com/en-us/dynamics/crm/defa

      If you are going for an on-demand implementation (i.e.: hosted by microsoft at crm.dynamics.com) then you could do this with the built-in functionality provided. Check this YouTube video for more information: http://www.youtube.com/watch?v=K_qhhZdNgcg

      Hope it helps!


  • Hi,

    Great tutorial – it is by far the easiest to read and follow of anything I've seen. I wish you had more!

    I have two questions:

    1 – Is there supposed to be an error if a user saves it in a phase 2 ie proposal stage, and then tries to save it by putting it back to prospecting? It sounded like in your post and post 3 that crm will not let this happen – but in ours it is. Does that have anything to do with ours being on premise not hosted?

    2 – For the second jscript, is the function name supposed to be the same as the first? At first I used SalesPipelineOptFilter, but it told me it was null or not a function. So I made it the SalesPipelineProbCalc and I have no errors. I don't know if that's right since this is the first time I've ever even used webresource or jscript.

    Thanks in advance.


    Thanks –


    • Hi Liz,

      About (1): You should get any errors form errors (i.e.: JScript errors) when moving an opportunity back from phase 2 to phase 1. However as explained, this would bring issues with the workflow described in post 3. So this is why I proposed the "bonus script" in this post.

      About (2): You shouldn't get any errors about null or not a function. What you need to do is to add another JScript web resource, copy and paste the bonus JScript in it and add it to the form so you can use it as well. You can add as many functions from different JScript web resources in an event such as form onLoad and control onChange.

  • Hi Pedro,
    I too am having the same problem as Gabriel above – I cannot seem to get the %Probability to change from 0.

    I've gone ahead as per your suggestion and bound the script to the OnChange event – still no joy.

    Any ideas?

    • Hi there,

      Do you get any errors when trying this script, or nothing happens?

      Make sure that the script is bound to the onChange event of both the rating field and the "Pipeline Phase" labeled field (which is really the Process Code field).

      Also make sure that you publish the customisations.

      Here is a hint for debuging purporses of your script. If you believe a part of your script isn't being parsed for whatever reason, you can add a line that says:

      alert("Hello World");

      This line will show a dialog box that says "Hello World" in Dynamics CRM. So the idea is to add this line somewhere you believe the script isn't being parsed. You can change the "Hello World' text for something more meaningful, including the value of variables. But when using variables you should add them inside the brackets without any quotation marks. For example:


      Hope this helps.

      • I am having the same problems. I did all of your suggestions and put an alert statement before the probability assignment section. Where do the alert values show up? On the form or elsewhere? Should the salesstagecode and rating values be changed in the script to the literal values, i.e "Quoted", "Hot", etc or does the 1 , 2, 3 refer to their sequence in the Option Set list? Since I am not seeing the alert values does this indicate that the script is not being executed. I have both of them bound to the Form OnLoad and to the variables On Change.
        Any help?

  • Great post ! Helped me a bunch !!!

  • Hi

    I think a step was missing to select the correct Library on the handler properties when you enter the function name. However Im still getting the following error. Field: salesstagecode Event: onchange Error: The value of the property 'SalesPipelineProbCalc' is null or undefined, not a Function object.

    Can anybody suggest what I have missed.


  • Greeat Post, You don't have a fully working MS CRM before you have implemented this code.

  • Hi Pedro,

    Your code and example doesn't take into account that an opportunity can be 'null', meaning it hasn't been classified.

    When you open an opportunity frothe first time you have to assign it a Pipeline Phase. Since the form doesn't have a 'null' or 'nothing state' it looks like that 'Prospect' has been selected, and everthing is ok. Since the code is trigger on an 'onchange' event, you have to really change it to proposal and then back to Prospect to get all the fields to update correctly. Any thoughts on firming up the code to make sure the optoin list has a null value? My sales people says this is a bug(!) 😉


    • Hi Per,

      I made this series in an attempt to be as vanilla as possible, close to the Dynamics CRM sales process as defined by Microsoft (see the first post of the series). In this case, the 'null' phase would be a lead record.

  • This is definitely the best post I've seen on this subject. What has taken me literally 4 hours in research and trial/ error, your post solved for me in 10 minutes. Thanks so much!!!

    • I'm glad you found it useful! 🙂