{"id":207,"date":"2018-07-13T18:02:28","date_gmt":"2018-07-13T17:02:28","guid":{"rendered":"http:\/\/mayeur.com\/wordpressfr\/?p=207"},"modified":"2018-07-13T18:17:33","modified_gmt":"2018-07-13T17:17:33","slug":"calculate-your-opportunity-total-price-with-dynamics-365-an-introduction-to-plugin-programming","status":"publish","type":"post","link":"https:\/\/mayeur.com\/wordpressfr\/calculate-your-opportunity-total-price-with-dynamics-365-an-introduction-to-plugin-programming\/","title":{"rendered":"Calculate your opportunity total price with Dynamics 365 : an introduction to PlugIn programming"},"content":{"rendered":"<p>In this post, I will explain how to customize the calculation of the total price of an opportunity using a PlugIn.<br \/>\n<strong>Be cautious : <\/strong>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 <a href=\"http:\/\/mayeur.com\/wordpressfr\/dynamics-365-product-prices\/\">here<\/a>.<br \/>\nThe 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.<\/p>\n<p>What I needed to create my example :<\/p>\n<ul>\n<li>A Dynamics 365 sandbox environment<\/li>\n<li>Visual Studio 2017<\/li>\n<li>The PluginRegistration tool : get the latest version if you have Dynamics V9. I use&nbsp;the PowerShell script described here : <a href=\"https:\/\/docs.microsoft.com\/en-ca\/dynamics365\/customer-engagement\/developer\/download-tools-nuget\" target=\"_blank\" rel=\"noopener\">https:\/\/docs.microsoft.com\/en-ca\/dynamics365\/customer-engagement\/developer\/download-tools-nuget<\/a><\/li>\n<\/ul>\n<p>As a first step you should create your Visual Studio project, a simple c# class library template for .NET Framework.&nbsp; At the time of writing, the framework should be no greater than 4.5.2 in my Dynamics 365 V9 online.<\/p>\n<p><a href=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-211 aligncenter\" src=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_1.png\" alt=\"\" width=\"1413\" height=\"981\" srcset=\"https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_1.png 1413w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_1-300x208.png 300w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_1-768x533.png 768w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_1-1024x711.png 1024w\" sizes=\"auto, (max-width: 1413px) 100vw, 1413px\" \/><\/a><\/p>\n<p>Next, add the Nuget package used for Dynamics 365 interaction. In the solution explorer, right-click on the project and select \u00ab\u00a0Manage NuGet Packages&#8230;\u00a0\u00bb :<\/p>\n<p><a href=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-212 aligncenter\" src=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_2.png\" alt=\"\" width=\"411\" height=\"408\" srcset=\"https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_2.png 909w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_2-150x150.png 150w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_2-300x298.png 300w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_2-768x763.png 768w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_2-100x100.png 100w\" sizes=\"auto, (max-width: 411px) 100vw, 411px\" \/><\/a>Click on the \u00ab\u00a0Browse\u00a0\u00bb tab, look up \u00ab\u00a0Microsoft.CrmSdk.CoreAssemblies\u00a0\u00bb and click the \u00ab\u00a0Install\u00a0\u00bb button.<\/p>\n<p>In the solution explorer, remove the \u00ab\u00a0Class1\u00a0\u00bb class, and create a new one named \u00ab\u00a0OpportunityPricesPlugin\u00a0\u00bb<\/p>\n<p>Replace your code with the following<\/p>\n<pre class=\"brush: csharp; collapse: true; light: false; title: ; toolbar: true; notranslate\" title=\"\">\r\nusing Microsoft.Xrm.Sdk;\r\nusing Microsoft.Xrm.Sdk.Query;\r\nusing System;\r\nusing System.ServiceModel;\r\n\r\nnamespace OpportunityPrices\r\n{\r\n  public class OpportuinityPricesPlugin : IPlugin\r\n  {\r\n    public void Execute(IServiceProvider serviceProvider)\r\n    {\r\n      \/\/Extract the tracing service for use in debugging sandboxed plug-ins.\r\n      ITracingService tracingService =\r\n        (ITracingService)serviceProvider.GetService(typeof(ITracingService));\r\n      tracingService.Trace(&quot;Will execute OpportuinityPricesPlugin&quot;);\r\n\r\n      \/\/ Obtain the execution context from the service provider.\r\n      IPluginExecutionContext context = (IPluginExecutionContext)\r\n        serviceProvider.GetService(typeof(IPluginExecutionContext));\r\n\r\n      if (!context.InputParameters.Contains(&quot;Target&quot;) &amp;amp;amp;amp;&amp;amp;amp;amp; !(context.InputParameters&#x5B;&quot;Target&quot;] is Entity))\r\n      {\r\n        throw new InvalidPluginExecutionException(&quot;OpportuinityPricesPlugin : Plugin is not correctly registered&quot;);\r\n      }\r\n\r\n      \/\/ Obtain the target entity from the input parameters.\r\n      Entity opportunity = (Entity)context.InputParameters&#x5B;&quot;Target&quot;];\r\n\r\n      \/\/ Verify that the target entity represents an entity type you are expecting. \r\n      if (opportunity.LogicalName != &quot;opportunity&quot;)\r\n      {\r\n        \/\/ The PlugIn is registered on a wrong step.\r\n        return;\r\n      }\r\n\r\n      \/\/ Get the Dynamics 365 service that enables us to communicate with the model\r\n      IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));\r\n      IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);\r\n\r\n      try\r\n      {\r\n        \/\/ Ckeck the statecode, to be coherent with Dynamics rules\r\n        Entity e = service.Retrieve(opportunity.LogicalName, opportunity.Id, new ColumnSet(&quot;statecode&quot;));\r\n        OptionSetValue statecode = (OptionSetValue)e&#x5B;&quot;statecode&quot;];\r\n        if (statecode.Value == 0)\r\n        {\r\n          \/\/ Look up the product line items : an example of a query within a plugin\r\n          QueryExpression query = new QueryExpression(&quot;opportunityproduct&quot;);\r\n          query.ColumnSet.AddColumns(&quot;quantity&quot;, &quot;priceperunit&quot;);\r\n          query.Criteria.AddCondition(&quot;opportunityid&quot;, ConditionOperator.Equal, opportunity.Id);\r\n          EntityCollection ec = service.RetrieveMultiple(query);\r\n          tracingService.Trace(&quot;OpportuinityPricesPlugin: I queried the product line items&quot;);\r\n\r\n          decimal total = 0;\r\n          decimal discount = 0;\r\n          decimal tax = 0;\r\n\r\n          \/\/ Calculate the total amount for each line\r\n          for (int i = 0; i &amp;amp;amp;lt; ec.Entities.Count; i++) { total = total + ((decimal)ec.Entities&#x5B;i]&#x5B;&quot;quantity&quot;] * ((Money)ec.Entities&#x5B;i]&#x5B;&quot;priceperunit&quot;]).Value); (ec.Entities&#x5B;i])&#x5B;&quot;extendedamount&quot;] = new Money(((decimal)ec.Entities&#x5B;i]&#x5B;&quot;quantity&quot;] * ((Money)ec.Entities&#x5B;i]&#x5B;&quot;priceperunit&quot;]).Value)); service.Update(ec.Entities&#x5B;i]); } tracingService.Trace(&quot;OpportuinityPricesPlugin: Calculated the amount of each line&quot;); \/\/ Update the opportunity fields directely because we will register the step in PreOperation opportunity&#x5B;&quot;totallineitemamount&quot;] = new Money(total); \/\/ Calculate discount based on the total amount if (total &amp;amp;amp;gt; (decimal)800.00 &amp;amp;amp;amp;&amp;amp;amp;amp; total &amp;amp;amp;lt; (decimal)1500.00) { discount = total * (decimal)0.05; } else if (total &amp;amp;amp;gt;= (decimal)1500.00)\r\n          {\r\n            discount = total * (decimal)0.10;\r\n          }\r\n\r\n          total = total - discount;\r\n          opportunity&#x5B;&quot;discountamount&quot;] = new Money(discount);\r\n          opportunity&#x5B;&quot;totalamountlessfreight&quot;] = new Money(total);\r\n          tracingService.Trace(&quot;OpportuinityPricesPlugin: updated opp with discount&quot;);\r\n        }\r\n        return;\r\n      }\r\n\r\n      catch (FaultException&amp;amp;amp;lt;OrganizationServiceFault&amp;amp;amp;gt; ex)\r\n      {\r\n        throw new InvalidPluginExecutionException(&quot;An error occurred in OpportuinityPricesPlugin.&quot;, ex);\r\n      }\r\n\r\n      catch (Exception ex)\r\n      {\r\n        tracingService.Trace(&quot;OpportuinityPricesPlugin: {0}&quot;, ex.ToString());\r\n        throw;\r\n      }\r\n    }\r\n\r\n  }\r\n}\r\n<\/pre>\n<p>You should strong sign your assembly.&nbsp; Go in the project properties page, Signing tab, click \u00ab\u00a0Sign the Assembly\u00a0\u00bb, select \u00ab\u00a0New&#8230;\u00a0\u00bb in the \u00ab\u00a0Choose a strong name key file\u00a0\u00bb combo.&nbsp;<\/p>\n<p>Build the project, then launch the \u00ab\u00a0Plugin Registration Tool\u00a0\u00bb (PluginRegistration.exe). Start by creating a new connection by clicking the \u00ab\u00a0+ CREATE NEW CONNECTION\u00a0\u00bb button in the toolbar :<br \/>\n<a href=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_3.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-220 aligncenter\" src=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_3.png\" alt=\"\" width=\"757\" height=\"754\" srcset=\"https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_3.png 757w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_3-150x150.png 150w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_3-300x300.png 300w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_3-100x100.png 100w\" sizes=\"auto, (max-width: 757px) 100vw, 757px\" \/><\/a>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).<\/p>\n<p>Once connected, register your assembly created with Visual Stiduo as in the images.<\/p>\n<p><a href=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_4.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-221 aligncenter\" src=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_4.png\" alt=\"\" width=\"603\" height=\"502\" srcset=\"https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_4.png 603w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_4-300x250.png 300w\" sizes=\"auto, (max-width: 603px) 100vw, 603px\" \/><\/a><\/p>\n<p><a href=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_5.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-223 aligncenter\" src=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_5.png\" alt=\"\" width=\"900\" height=\"1215\" srcset=\"https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_5.png 900w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_5-222x300.png 222w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_5-768x1037.png 768w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_5-759x1024.png 759w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/a><\/p>\n<p>Expand you assembly plugin and add a step :<\/p>\n<p><a href=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_6.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-224 aligncenter\" src=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_6.png\" alt=\"\" width=\"651\" height=\"499\" srcset=\"https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_6.png 651w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_6-300x230.png 300w\" sizes=\"auto, (max-width: 651px) 100vw, 651px\" \/><\/a>Then, choose the \u00ab\u00a0Message\u00a0\u00bb: \u00ab\u00a0Update\u00a0\u00bb, \u00ab\u00a0Primary Entity\u00a0\u00bb: \u00ab\u00a0opportunity\u00a0\u00bb, and \u00ab\u00a0Event Pipeline Stage of Execution\u00a0\u00bb: \u00ab\u00a0PreOperation\u00a0\u00bb.<\/p>\n<p><a href=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_7.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-225 aligncenter\" src=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_7.png\" alt=\"\" width=\"1500\" height=\"945\" srcset=\"https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_7.png 1500w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_7-300x189.png 300w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_7-768x484.png 768w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_7-1024x645.png 1024w\" sizes=\"auto, (max-width: 1500px) 100vw, 1500px\" \/><\/a><\/p>\n<p>As a final step in Dynamics 365 web application : disable standard system pricing calculation in the \u00ab\u00a0Settings\/Administration\/System Settings\u00a0\u00bb, in \u00ab\u00a0Sales\u00a0\u00bb tab :<\/p>\n<p><a href=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_8.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-full wp-image-226\" src=\"http:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_8.png\" alt=\"\" width=\"1002\" height=\"81\" srcset=\"https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_8.png 1002w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_8-300x24.png 300w, https:\/\/mayeur.com\/wordpressfr\/wp-content\/uploads\/2018\/07\/opportunity_plugin_8-768x62.png 768w\" sizes=\"auto, (max-width: 1002px) 100vw, 1002px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Now, you just have to test in dynamics, by updating an existing opportunity.<\/p>\n<p>&nbsp;<\/p>\n<p>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.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":242,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[12],"tags":[14,13],"class_list":["post-207","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dynamics365","tag-crm","tag-dynamics365"],"_links":{"self":[{"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/posts\/207","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/comments?post=207"}],"version-history":[{"count":20,"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/posts\/207\/revisions"}],"predecessor-version":[{"id":237,"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/posts\/207\/revisions\/237"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/media\/242"}],"wp:attachment":[{"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/media?parent=207"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/categories?post=207"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/tags?post=207"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}