Menu Close

Calculating a manual or percentage discount in the sales process with Microsoft Dynamics CRM

Originally posted in March 2015 on RSM Technology Blog

In the sales process, the ability to find and meet the prospect’s price point is important to winning the deal. Often a client will want to receive a discount on a specific product as a flat rate adjustment or as a percentage of the price of the product. As a member of the sales team, the ability to quickly update the opportunity and get a new quote out the door is important and the last thing you want is your CRM system slowing you down or limiting your ability.

Microsoft Dynamics CRM has a number of ways to allow a sales person to discount an opportunity or quote which include: volume discounting, manual discounting at the product and opportunity level. One feature that Dynamics CRM is currently missing is the ability to discount as a percentage at the product level. With a little work this requirement can easily be accomplished in any of the Dynamics CRM versions.

Because new feature have been released with each progressive version of Dynamics CRM, let us cover the options for each of the versions. The two methods for accomplishing the requirement of product line discounting is either JavaScript or Business Rules. Business Rules were first released with Dynamics CRM 2013 but they were not enabled for Opportunity or Quote Products until Dynamics CRM 2015. JavaScript is support by all of the current versions of Dynamics CRM and there’s an existing blog, Microsoft Dynamics CRM 2011: JavaScript to calculate a percentage discount on the Quote Product, on the subject that includes how to use JavaScript to get around this limitation.

Before we get into the code, we will review specific requirements this code was created to meet. The first requirement is that we want the sales team to be able dictate if the discount will be a flat discount or a percentage discount. The second requirement is that other field will be calculated based on what we entered. For example, if we enter a flat discount of $100 on a $500 product, the percentage field will automatically populate with 20.00% so we can quickly check and make sure we are not giving too great a discount to the client. The inverse process would work as well so if we enter a 20.00% discount on a $500 product, the flat discount will populate with $100. The last requirement is for a value to be appended to line item with either “20% – DISCOUNT APPLIED” or “$100 – DISCOUNT APPLIED” to highlight to the prospect they are getting a special rate.

We will be looking at the JavaScript method as it can be used with any version of Dynamics CRM starting with Dynamics CRM 2011. First, we will need to create a few fields which will call JavaScript.

The first field we will create is a field called “Type of Discount” to allow the user to select if discount is flat rate or percentage. Be sure to use a Two Option data type and rename ‘No’ to ‘Flat Rate’ and ‘Yes’ to ‘Percentage’.

We also need to create the Percentage Discount field as a Decimal Number as mentioned in blog above, and be sure to set the minimum to ‘0.00’ and maximum to ‘100.00’.

The last field that we will need to create is a simple text box named ‘Append’ that will contain the discount applied text.

After these fields have been created and placed on the form, create a web resource with the below JavaScript.

function calcDiscount(){
 //call function OnSave
 var price;
 var qty;
 var perc;
 var type = Xrm.Page.getAttribute("new_typeofdiscount").getValue();
  if (type == true){
   try {
    //get value of various buckets
    var priceField = Xrm.Page.getAttribute("priceperunit");
    if ((priceField.getValue() != null) && (priceField.getValue() != "undefined")) {
     price = priceField.getValue();
    }
    else {
     price = 0;
    }
    var qtyField = Xrm.Page.getAttribute("quantity");
    if ((qtyField.getValue() != null) && (qtyField.getValue() != "undefined")) {
     qty = qtyField.getValue();
    }
    else {
     qty = 0;
    }
    var percField = Xrm.Page.getAttribute("new_percentagediscount");
    if ((percField.getValue() != null) && (percField.getValue() != "undefined")) {
     perc = percField.getValue();
    }
    else {
     perc = 0;
    }
    var disc = (price * qty)*(perc / 100);
    Xrm.Page.getAttribute("manualdiscountamount").setValue(disc);
    Xrm.Page.data.entity.attributes.get("manualdiscountamount").setSubmitMode("always");
    //because field is set to read only, we need to set this to insure save.
    if (perc != 0){
     Xrm.Page.getAttribute("new_append").setValue(perc + '% - DISCOUNT APPLIED');
    } else {
    Xrm.Page.getAttribute("new_append").setValue();
    } 
   }
   catch (err) {
    alert('Error with javascript on updating weighted revenue. ' + err);
   }
   return 0;
  } else {
   try { 
    var priceField = Xrm.Page.getAttribute("priceperunit");
    if ((priceField.getValue() != null) && (priceField.getValue() != "undefined")) {
     price = priceField.getValue();
    }
    else {
     price = 0;
    }
    var qtyField = Xrm.Page.getAttribute("quantity");
    if ((qtyField.getValue() != null) && (qtyField.getValue() != "undefined")) {
     qty = qtyField.getValue();
    }
    else {
     qty = 0;
    }
    var discField =  Xrm.Page.getAttribute("manualdiscountamount");
    if ((discField.getValue() != null) && (discField.getValue() != "undefined")) {
     disc = discField.getValue();
     var disctext = disc.toFixed(2)
    }
    else {
     disc = 0;
    }
    var perc = (disc / (price * qty)) * 100;
    Xrm.Page.getAttribute("new_percentagediscount").setValue(perc);
    Xrm.Page.data.entity.attributes.get("new_percentagediscount").setSubmitMode("always");          
    //because field is set to read only, we need to set this to insure save.
    if (disc != 0){
     Xrm.Page.getAttribute("new_append").setValue('$' + disctext + ' - DISCOUNT APPLIED');
    } else {
    Xrm.Page.getAttribute("new_append").setValue();
    }
   }
   catch (err) {
    alert('Error with javascript on updating weighted revenue. ' + err);
   }
   return;
  }
 }

function TypeOfDiscount(){
 //call function OnLoad of form and OnChange of new_typeofdiscount
 var type = Xrm.Page.getAttribute("new_typeofdiscount").getValue();
 var percfield = Xrm.Page.ui.controls.get("new_percentagediscount");
 var manualfield = Xrm.Page.ui.controls.get("manualdiscountamount");
  if (type == true){
   percfield.setDisabled(false);
   manualfield.setDisabled(true);
  } else {
   percfield.setDisabled(true);
   manualfield.setDisabled(false);
  }
 }

There are two functions in the code, the first function, TypeOfDiscount is designed to lock the field that will be auto calculated and the second function, calcDiscount, is doing the actual calculation of the fields. Place the TypeOfDiscount function on the OnLoad event of the form and the OnChange event of the Type of Discount field so the field will lock when not in use. Place the calcDiscount function on the OnSave event of the form as shown below. The form is now ready for testing.

As with all fields created in Dynamics CRM, it’s important to create a field mapping to related entities if the record progresses through the sales process. In the case of the Opportunity Product, we want to make sure we are mapping over the three fields that we created: Type of Discount, Percentage Discount, and Append. Before you can map the fields, you will need to create the fields on the destination entity with the same field types. Another great thing about using the JavaScript method is that you can reuse the code on Quote or Order Products as well. After the fields have been created, the best tool that I have found to map between the Opportunity Product entity and Quote and Order Product entities is the CRM 2011 Entity Maps tool.

With the above code implemented and tested in your CRM system, it will help streamline the sales process for your sales team. One additional feature that can be made to discounting product line items is having a notification when a discount is above a specified threshold. Having notifications can help sales managers gain better insight into what the sales team is doing but let us save that topic for another time.