Back
Featured image of post Implementing Custom Calculations for Sales Entities (Dynamics CRM/Dynamics 365 for Enterprise)

Implementing Custom Calculations for Sales Entities (Dynamics CRM/Dynamics 365 for Enterprise)

Organisations that deploy Dynamics CRM/Dynamics 365 for Enterprise (CRM/D365E) can immediately take advantage of a number of inbuilt functionality, processes and data models that can be re-purposed with minimal effort. Whilst this approach can often lead to more streamlined deployment of your CRM/D365E solution, individuals customising the system should take care not to make the system fit around a business too much; rather, the opposite must be achieved where ever possible and careful analysis should be carried out in the outset to ensure that this balance is maintained. Sacrificing sensible business processes to accommodate for the quirks of a particular business system is a major pitfall that should be avoided as part of any major IT system deployment.

A good example of this can be found in the pricing calculation engine within CRM/D365E, which is utilised by the following entities within the system:

  • Opportunity
  • Opportunity Product
  • Quote
  • Quote Product
  • Order
  • Order Product
  • Invoice
  • Invoice Product

Rather than having to implement your own logic to generate prices for these entities, businesses can choose to utilise the pricing engine to automatically generate the net total for your Products, calculate appropriate Discounts and then figure out the final total at the end.

For those who are dissatisfied with how CRM performs this calculation, you will be pleased to hear that you have the option to override the default pricing engine and specify your own via a C# plugin. More information, and a very handy code example, can be found on our good friend MSDN:

The pricing engine in Microsoft Dynamics 365 supports a standard set of pricing and discounting methods, which might be limiting to your business depending on your specific requirements for applying taxation, discounts, and other pricing rules for your products. If you want to define custom pricing for your products in opportunities, quotes, orders and invoices, you can use the CalculatePrice message.

To use the custom pricing for your opportunities, quotes, orders, and invoices:

  1. Set the value of the Organization.OOBPriceCalculationEnabled attribute to 0 (false). You can also use the Sales tab in the system settings area in Microsoft Dynamics 365 or Microsoft Dynamics 365 for Outlook to disable system pricing. More information:  Configure product catalog information
  2. Create a plug-in that contains your custom pricing code for calculating the price for your opportunity, quote, order, or invoice.
  3. Register the plug-in on the CalculatePrice message.

Source: https://msdn.microsoft.com/en-us/library/dn817885.aspx

I think the most key thing as part of the above is not to overlook the simplest step - namely, modifying the setting within CRM/D365E that lets you specify your custom pricing engine in the first place. If this is not set, then you may spend many hours trying to figure out why your beautifully developed plugin is not working! It can be found very straightforwardly in Administration area of the application, on the System Settings page:

Whilst the code example provided by Microsoft gives you a good flavour of what you can potentially achieve with your own custom logic, I thought I would share two further examples that I recently was involved in developing, which may also prove useful when putting together your own custom pricing engine.

Calculating Custom Fields/Attributes

Arguably one of the biggest benefits of implementing your own custom pricing engine is being able to incorporate additional fields as part of the calculation. A recent real life example best demonstrates this. I was implementing a quoting solution for a business within Dynamics CRM 2015. The organisation was, fortunately, able to utilise much of the out of the box functionality within CRM as part of their existing processes. The only caveat was that they wanted the ability to add a Margin value at the Order level, in a similar vein to the Discount fields currently on the Quote entity - a Discount value and a Discount percentage value. The organisation wanted the option to do both, either or neither i.e. have the ability to specify a Margin value AND an additional percentage on top of that.

After configuring the appropriate fields within CRM to store both a currency value for the Margin and a decimal value for the Margin (%), we then proceeded to write some custom code that would achieve this aim. A snippet of this can be found below, which takes an existing total value of all Products on a Quote and then applies the correct calculation. It is worth explaining that the system returns NULL values if there is no data in the field when using the GetAttributeValue method (a fact I was already well aware of), which is why we have to specifically set the variables with a default value of 0 and perform the NULL check:

decimal margin = 0;
decimal marginPercent = 0;

if (quote.GetAttributeValue<Money>("new_mycustommarginamountfield") != null)
  margin = quote.GetAttributeValue<Money>("new_mycustommarginamountfield").Value;

if (quote.GetAttributeValue<Money>("new_mycustommarginpercentagefield"))
  marginPercent = quote.GetAttributeValue<decimal>("new_mycustommarginpercentagefield");

//Calculate margin amount based on the total amount

total = total + margin;
quote["totalamountlessfreight"] = new Money(total);
service.Update(quote);

//Calculate margin percentage based on the total amount

decimal marginPercentVal = marginPercent / 100 * total;
total = total + marginPercentVal;
quote["totalamountlessfreight"] = new Money(total);
service.Update(quote);

Calculating Sales Tax

CRM/D365E makes the assumption that tax will always be calculated on the Product Detail level. That’s why the Quote Product, Opportunity Product, Order Product and Invoice Product entities have a Tax field, demonstrated below on the Quote Product form:

There are a few problems with this, however:

  • You cannot set a default Tax for each Product in the system. What this means is that you have to drill down to every Product details entity and populate the Tax manually. Whilst you could look at a Business Rule, Workflow or some custom code to get around this issue, this seems like a rather complicated solution to something that you would expect to be easy to configure.
  • My experience indicates that most companies in the UK calculates tax on the gross amount of an order, and not at an individual Product level. Attempting to try and change a common practice to fit around a business system is a good example of what I spoke about in the introduction to this blog post.
  • Generally, most organisations will work with a flat rate of Tax for all Products (unless they are dealing with other countries). With this in mind, it seems a little crazy having to set this on an individual basis.

By using our own custom calculation logic, we can get around the above and implement a solution that best meets our need. For example, here is a code snippet that will take the total value of all Products on the Order entity and then calculate the VAT tax amount at 20%, saving the tax-only amount and the Net Total back to the system:

decimal vat = 0.20m;
decimal total = 0;
decimal tax - 0;

for (int i = 0; i < ec.Entities.Count; i++)
{
  total = total + ((decimal)ec.Entities[i]["quantity"] * ((Money)ec.Entities[i]["priceperunit"]).Value);
  (ec.Entities[i])["extendedamount"] = new Money(((decimal)ec.Entities[i]["quantity"] * ((Money)ec.Entities[i]["priceperunit"]).Value));
  service.Update(ec.Entities[i]);
}

//Calculate total from tax

tax = total * vat;
total = total + tax;
order["totaltax"] = new Money(tax);
order["totalamount"] = new Money(total);                
service.Update(quote);

It would be disingenuous of me not to point out that the above solution has its own faults - the biggest one being that your code would require manually updating if the tax rate ever changes in the future. You can perhaps get around this issue by instead storing the current tax rate within a CRM entity, that can be updated in line with any future changes. Your plugin could then query this entity/attribute each time the plugin is executed.

Conclusions or Wot I Think

Whilst the ability to override a key feature within CRM/D365E is incredibly welcome and a great example of how you can leverage the system without compromising on your existing business processes, it is arguable that this process is hardly straightforward. A passing knowledge of C# is mandatory to even begin to start implementing your own custom pricing engine, as well as good awareness of how CRM/D365E plugins work. This is all stuff that an administrator of the application may struggle to grasp, thereby requiring dedicated resource or knowledge within the business to implement the desired solution. It would be nice to perhaps see, as part of a future version of D365E, the ability to specify a custom pricing engine within the application itself - similar to how Business Rules were introduced and reduced the need for form-level JScript functions to achieve common tasks. Nevertheless, it has been good to discover that the CalculatePricing message is exposed within the application and that the application has the flexibility for end users to modify (and perhaps improve upon 🙂) one of its key features.

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy