Calculate your opportunity total price with Dynamics 365 : an introduction to PlugIn programming

In this post, I will explain how to customize the calculation of the total price of an opportunity using a PlugIn.
Be cautious : creating a plugin is never harmless. You should ALWAYS try to use the standard configuration functionnalities, and there are many in Dynamics 365 as I explained here.
The goal of this post is to be a follow-up of my previous post on Dynamics product prices, as well as an introduction to writing a plugin for Dynamics 365.

What I needed to create my example :

As a first step you should create your Visual Studio project, a simple c# class library template for .NET Framework.  At the time of writing, the framework should be no greater than 4.5.2 in my Dynamics 365 V9 online.

Next, add the Nuget package used for Dynamics 365 interaction. In the solution explorer, right-click on the project and select « Manage NuGet Packages… » :

Click on the « Browse » tab, look up « Microsoft.CrmSdk.CoreAssemblies » and click the « Install » button.

In the solution explorer, remove the « Class1 » class, and create a new one named « OpportunityPricesPlugin »

Replace your code with the following

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.ServiceModel;

namespace OpportunityPrices
  public class OpportuinityPricesPlugin : IPlugin
    public void Execute(IServiceProvider serviceProvider)
      //Extract the tracing service for use in debugging sandboxed plug-ins.
      ITracingService tracingService =
      tracingService.Trace("Will execute OpportuinityPricesPlugin");

      // Obtain the execution context from the service provider.
      IPluginExecutionContext context = (IPluginExecutionContext)

      if (!context.InputParameters.Contains("Target") && !(context.InputParameters["Target"] is Entity))
        throw new InvalidPluginExecutionException("OpportuinityPricesPlugin : Plugin is not correctly registered");

      // Obtain the target entity from the input parameters.
      Entity opportunity = (Entity)context.InputParameters["Target"];

      // Verify that the target entity represents an entity type you are expecting. 
      if (opportunity.LogicalName != "opportunity")
        // The PlugIn is registered on a wrong step.

      // Get the Dynamics 365 service that enables us to communicate with the model
      IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
      IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

        // Ckeck the statecode, to be coherent with Dynamics rules
        Entity e = service.Retrieve(opportunity.LogicalName, opportunity.Id, new ColumnSet("statecode"));
        OptionSetValue statecode = (OptionSetValue)e["statecode"];
        if (statecode.Value == 0)
          // Look up the product line items : an example of a query within a plugin
          QueryExpression query = new QueryExpression("opportunityproduct");
          query.ColumnSet.AddColumns("quantity", "priceperunit");
          query.Criteria.AddCondition("opportunityid", ConditionOperator.Equal, opportunity.Id);
          EntityCollection ec = service.RetrieveMultiple(query);
          tracingService.Trace("OpportuinityPricesPlugin: I queried the product line items");

          decimal total = 0;
          decimal discount = 0;
          decimal tax = 0;

          // Calculate the total amount for each line
          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]); } tracingService.Trace("OpportuinityPricesPlugin: Calculated the amount of each line"); // Update the opportunity fields directely because we will register the step in PreOperation opportunity["totallineitemamount"] = new Money(total); // Calculate discount based on the total amount if (total > (decimal)800.00 && total < (decimal)1500.00) { discount = total * (decimal)0.05; } else if (total >= (decimal)1500.00)
            discount = total * (decimal)0.10;

          total = total - discount;
          opportunity["discountamount"] = new Money(discount);
          opportunity["totalamountlessfreight"] = new Money(total);
          tracingService.Trace("OpportuinityPricesPlugin: updated opp with discount");

      catch (FaultException<OrganizationServiceFault> ex)
        throw new InvalidPluginExecutionException("An error occurred in OpportuinityPricesPlugin.", ex);

      catch (Exception ex)
        tracingService.Trace("OpportuinityPricesPlugin: {0}", ex.ToString());


You should strong sign your assembly.  Go in the project properties page, Signing tab, click « Sign the Assembly », select « New… » in the « Choose a strong name key file » combo. 

Build the project, then launch the « Plugin Registration Tool » (PluginRegistration.exe). Start by creating a new connection by clicking the « + CREATE NEW CONNECTION » button in the toolbar :
Attention : I had a problem while connecting the first time, because my Plugin Registration Tool was at the wrong version : there is no exception message, but the tool keeps asking for login (a TLS 1.2 issue).

Once connected, register your assembly created with Visual Stiduo as in the images.

Expand you assembly plugin and add a step :

Then, choose the « Message »: « Update », « Primary Entity »: « opportunity », and « Event Pipeline Stage of Execution »: « PreOperation ».

As a final step in Dynamics 365 web application : disable standard system pricing calculation in the « Settings/Administration/System Settings », in « Sales » tab :


Now, you just have to test in dynamics, by updating an existing opportunity.


This was just an intro to plugins programming, but I hope it will help you understand the main quick steps. You have also the capability to debug plugins from within Visual Studio, which is a great feature, bu beyond the scope of this post.

SSIS Integration with Dynamics 365 and KingswaySoft toolkit

Data Integration is one of the most common task in a CRM project : be it for project initialization, integration with an ERP, with one of the many third party tools used in the company… A task I commonly needed to do in my freelance activities since many years.

We can see data integration as two categories :

  • real-time integration : usually using techniques like web-services, or specific APIs
  • batch integration : allows for mass data import/update. ETL tools are specifically used for this purpose. 

I will briefly introduce the use of SSIS with Dynamics 365 in this post.

SSIS is the Microsoft ETL, and has quite powerfull features. It is a logical choice for customers who use Microsoft softwares.

The favorite way to do this is usually to use the KingswaySoft third party toolkit. Information can be found here. The product can be used free for development purposes, but needs a licence for real use on a server. It is actively maintained and widely used.

My example scenario is the following : uploading 1000 contacts from a Excel file to Dynamics 365.

My Excel input file

I previously installed the KingswaySoft toolkit, which added added some sources and destinations to SSIS development tool.

I start by creating a new SSIS project :

I create a new « Control Flow », add an Excel source and choose all columns to read :

Then, I simply add a « Dynamics CRM Destination » to the control flow :

As you can see above, KingswaySoft toolkit added several sources and destinations for Dynamics CRM, NAV and AX.

We link the two components, double-click on the « Dynamics CRM Destination », and select « <New…> » in the « CRM Connection Manager » :

You need some informations from Dynamics to create the connexion. In your Web Dynamics application, go to Settings/Customization/Developer Resources :



Look a the Discovery Service. What we need here is « » (in my example).

Now, go back to SSIS, on fill the Dynamics connexion screen :

  • Authentication Type : choose Online Federation
  • CRM Discovery Server : choose the server you found above in Dynamics
  • Fill the username and password for Dynamics 365
  • Select the « Organization » that is suitable : in my example, I take a sandbox Dynamics environment

You can click « Test Connection » just to be sure all is OK.

Now, we define the mapping of data. In our simple example, we just insert new data with no transformation :

As you can see, many options are available, regarding functional capabilities (upsert, deduplication, workflows, …) and performance (batch size, multithreading).

In the « Columns » tab, we define the real mapping. We do not care with coutry code, nor gender transcoding in the scope of this post, but this feature is quite easy to implement in SSIS, whether by a script component or a dedicated existing component.


That’s it!

Now executing the SSIS program. It works successfully… taking nearly 8 minutes… Quite a long time for 1000 contacts and a really simple flow of data. But many optimisation options exist, I work on a workstation with a poor Internet network quality today, and on a Dynamics sandbox with some restriction on the performance side. Performance tuning of SSIS Integraton could be a great post for the future!

Dynamics 365 : Product Prices

Determining the price calculation rules of a product in an opportunity or transactional entity (quote, order or invoice) may be quite complex, due to the multiple combinations available. This complexity gives the freedom to comply to various business needs.

I created the following schema to summurize these options, and will explain them in more details. Most of the options relate to the « Product Line », some are global to the whole entity.

Use system pricing calculation

First of all, we need to check if system pricing calculation rules are applied (which is the cas by default), in system settings (in the Sales tab) :

If it is not the case, a plug-in should be created and registered (will be explained in a future post).

Find « Price List »

Assuming we use System pricing calculation, you need to choose an applicable Price List for your whole entity. Take care that a given Price List relates to unique currency : it must be the same than that of your entity. Moreover, you will need to have « Price List Item » for any product added in the lines.
Default value : if there exists a « Sales Territory » assigned to you, and that Sales Territory is attached to one (and only one) Price List, that Price List will be selected by default, unless you turn that functionnality off in system settings :

Find « Price List Item » and associated « Pricing Method »

Once you have choosen a « Price List », you need to add products to your transaction entity. The price that will be applied is retrieved from the « Price List », if a releavant « Price List Item » exists : the same Product with the same Unit as selected in the transactional entity.
The Price List Item defines the « Pricing Method » :

  • Currency Amount : a fixed amount
    Percent of List : the « List Price » is defined on the Product : Percentage * List Price
  • Percent markup – current cost : the « Current Cost » is defined on the Product. Percent Markup is Current Cost * ( 100 + Percentage) / 100
  • Percent margin – current cost : the « Current Cost » is defined on the Product. Percent Margin is Current Cost + (Current Cost * Percentage) / (100 – Percentage))
  • Percent markup – standard cost : the « Standard Cost » is defined on the Product. Percent Markup is Standard Cost * ( 100 + Percentage) / 100
  • Percent margin – standard cost : the « Standard Cost » is defined on the Product. Percent Margin is Standard Cost + (Standard Cost * Percentage) / (100 – Percentage))

Attention : by default, the fields « List Price », « Standard Cost » and « Current Cost » may not be displayed on the Product form. It is easy to customize and add these.
What are the meaning of these fields? You can assign the meaning you want, but usually :

    • List Price : the official sales Price from an internal « Sales List »
    • Current Cost : the current (temporary) price of a Product – maybe the Price payed to a provider
    • Standard Cost : the typical price of a Product
    • « Percent Margin » is greater than « Percent Markup ».

Percent Margin means : the Percentage of the calculated price that I want as a margin
Percent Markup means : the Percentage of the initial Price that I want to add.
There are some more options in the « Price List Item » that will fine-tune the calculated price of a Product from a Price List :

      • Rounding Policy : None, Up, Down, Nearest
      • Rounding Option (unavailable for the « None » Rounding Policy) :
        • « Ends In » : forces the Price to end with for example with « .99 », so that a calculated price of 50.14 may be automatically rounded to 49.99
        • « Multiple of » : forces to Price to be a multiple of a certain amount – for example, a calculated price of 50.14 may be automatically rounded to 50.10 if the « Rounding Amount » is « 0.10 »
      • Rounding Amount : the amount used by « Rounding Option »

For example, with a cost of 50 and a Percentage of 10 :

  • The Percent Margin is 50 + (50 * 10) / (100 – 10) = 55,56 => my Margin is 5,56 which is 10% of 55,56
  • The Percent Markup is 50 * (100 + 10) / 100 = 55,0 => my Markup is 5 which is 10% of 50

Apply Discount List

Configuration : You can create « Discount Lists ». A Discount List is basicaly an entity specifying the discounts applied depending in quantity (whether an amount, or a percentage) on any product. Then, you attach this Discount List to a « Price List Item » in your Product Catalog configuration.
The discount will be automatically applied on the line.

Apply Manual Discount

You can manually add a absolute discount on the Product Line Item.

Apply footer costs
At the footer of your transactional entity, you may manually add some adjustments :

  • A discount as a percentage
  • A discount as an absolute amount
  • A freight Amount that will be added to the total price

Any feedback and sharing is welcome. Have fun!


2018, un nouveau tournant

Ce début d’année 2018 marque pour moi un tournant professionnel… disons plutôt une inflexion dans le prolongement de plus de vingt ans de consulting CRM.
J’ai fait le choix logique de m’orienter vers le CRM de Microsoft, Dynamics 365.
Logique, car je pratique l’éco-système Microsoft depuis maintenant de nombreuses années – Selligent fonctionne sur .NET depuis le début des années 2000, j’ai acquis une bonne spécialisation sur SSIS (l’outil d’intégration de données Microsoft) et SQL Server de manière générale, réalisé diverses applications/services en C#/.NET, j’ai même créé il y a un an et demi une petite structure partenaire Microsoft dédiée au développement d’applications mobiles en Xamarin.
Logique également car plus de vingt ans de pratique CRM, des dizaines et des dizaines de projets d’intégration m’ont donné les réflexes indispensables à une mise en oeuvre intelligente des outils de la relation client, que ce soit à destination d’équipes commerciales, service clients ou marketing.
Logique enfin, car j’admire et soutient le virage stratégique radical d’ouverture qui a été pris par Microsoft depuis notamment l’arrivée de Satya Nadella aux commandes.

Pour autant, je poursuis mes prestations autour de Selligent CRM (l’outil CDM) qui reste aujourd’hui une solution très souple, adaptables aux besoins des utilisateurs notamment en version on-premises, offrant un coût total d’acquisition très intéressant par rapport aux géants du cloud. Mon expertise avéré sur Selligent me permet d’apporter une vraie plus-value aux clients.

Bilan 2013 et Perspectives 2014

Une année 2013 marquée pour moi par les points suivants :

  • Importante activité de R&D sur FastBiz : notamment, le début d’année a été consacré à la refonte technique du bureau virtuel GWT, pour passer sur les dernières versions, mettre en place un framework MVP (MVP4G), utilisation des UIBinder, …etc, ainsi qu’un accroissement substantiel des fonctionnalités proposées par l’outil
  • Une mise en place OpenCRX chez un client, pour leur CRM interne (une cinquantaine d’utilisateurs)
  • Nombreux projets CTI (Couplage Téléphonie Informatique), en lien avec le CRM Selligent : CCIP, Téléshopping, Pomona, PMU, … Je suis maintenant positionné comme référent CTI pour l’éditeur Selligent. Ma réalisation d’un plug-in en mode Web-Service me permet d’implémenter rapidement ce type de solution. J’ai pu intégrer ce type de solution également sur FastBiz, ce qui nous permet de proposer des montées de fiche (notamment) dans notre CRM
  • Toujours de l’expertise Selligent sur divers sujet, en m’appuyant sur mes 15 ans d’expérience sur le produit

Perspectives 2014 :

  • Certainement une importante activité R&D sur FastBiz, liée au décollage commercial de l’activité
  • Le développement de mes interventions d’expertise sur des sujets techniques sur lesquels j’ai acquis une forte compétence ces dernières années : GWT et GXT, optimisations et configurations Postgresql, optimisations et tuning d’applications Java, expertise sur la suite SQL Server (SSIS, SSRS, SSAS)
  • Poursuite de mon activité Selligent