Nov 17, 2013

Plug-in – Check context in Early bound & Late bound approaches

I previously provided a nice descriptive article on developing plug-ins using Dynamics CRM toolkit. I tried the same approach for CRM 2013 online in both early bound and late bound approaches which worked fine. New SDK\Tools\DeveloperToolkit provides the latest toolkit you can install. Then you get the project templates for CRM 2013 in Visual Studio.


Here I am coding to create a task when opportunity is updated.

Early Bound Sample code;

protected void ExecutePostOpportunityUpdate(LocalPluginContext localContext)
{
if (localContext == null)
{
    throw new ArgumentNullException("localContext");
}

IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;

if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
    Opportunity _Opportunity = ((Entity)context.InputParameters["Target"]).ToEntity<Opportunity>();
    try
    {
        // Create Task
        Task _task = new Task();
        _task.Subject = "opportunity changed";
        _task.RegardingObjectId = new EntityReference(Opportunity.EntityLogicalName, _Opportunity.Id);
        _task.Description = "Please look into this opportunity changes";
        service.Create(_task);
    }
    catch (FaultException ex)
    {
        throw new InvalidPluginExecutionException("Plug-in error : ", ex);
    }

    finally
    {
        _Opportunity = null;
    }
}
}

Late Bound Sample code;

protected void ExecutePostOpportunityUpdate(LocalPluginContext localContext)
 {
  if (localContext == null)
  {
      throw new ArgumentNullException("localContext");
  }

  IPluginExecutionContext context = localContext.PluginExecutionContext;
  IOrganizationService service = localContext.OrganizationService;

  if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
  {
      Entity entity = (Entity)context.InputParameters["Target"];
     try
      {
          // Create Task
          Entity task = new Entity("task");
          task["subject"] = "opportunity changed (Late)";
          task["regardingobjectid"] = new EntityReference("opportunity", (Guid)entity.Attributes["opportunityid"]);
          task["description"] = "Please look into this opportunity changes";
          service.Create(task);
      }
      catch (FaultException ex)
      {
          throw new InvalidPluginExecutionException("Plug-in error : ", ex);
      }
      finally
      {
          entity = null;
      }
  }
}

Late Bound – Early Bound argument could be as meaningless as Cannon-Nikon argument in photography!

Whatever you do, if you can do it wiser, it matters. Anyway, I am happy to illustrate one simple mistake one could do if not understood clearly, when coding with early bound approach.

When coding plug-ins one of the crucial things is to identify if an attribute exists in the context. Typically, in update message, we need to know if a field is changed by inspecting the context. In late bound, we do it this way and it works fine;

Entity entity = (Entity)context.InputParameters["Target"];
if (entity.Contains("currentsituation"))
{
    //Code
}

In early bound, we usually cast the context in to our entity type.


After casting we see all the fields in intelligence, yet it doesn’t say they are available. They are available only if they exist in the context.

This can be misleading. In other words below is NOT the way to do something if currentsituation field is changed.

Opportunity _Opportunity = ((Entity)context.InputParameters["Target"]).ToEntity<Opportunity>();
if (_Opportunity.CurrentSituation != null)
{
    // Code
}

So, checking the context has to be done in the late bound manner (as shown, using Contains keyword). Keep in mind, when we say field exist in the context, that means “its presence”. Still its value can be NULL or NOT NULL, which has to be checked as next step, depending on your requirement.

Nov 10, 2013

Dynamics CRM toolkit for CRM online

This is an amazing step by Microsoft to leverage the development effort especially in online department. From here you can download the needful stuff.

Also check this article for one of the clear explanations on how to get started;

http://mscrmshop.blogspot.com.au/2012/01/step-by-step-plugin-tutorial-using.html

Workflow that waits till a date in the record

This is something cool. If I elaborate this a bit; this will allow you to trigger something on an important day like contract end date or etc. This can actually replace the need of a windows service that checks something periodically.

Of course I wrote something similar sometimes back, but it is practically bad. It could make new instance again and again till the exact date reaches.

Now it is just one line waiting statement that does the magic.

Typical example I tried is to put a flag for insurance records when they reach expiry date.
 
In CRM 2011, it will be like this;
 
 
In CRM 2011 online it will be like this;

 
In CRM 2013 online
 
 
If you check the record after creation you will see the waiting statement in the workflow tab.
 
 
Since expiry date can be changed later on, it is also advisable to trigger on Update as well as on Create. If a user changes the expiry date later on, you will see an extra waiting record.
 
 
Anyway, as I tested, it worked for the correct expiry date. 


Sep 27, 2013

Issue of Select into Temp table with NULL fields

Sometimes when we create Temporary tables we might need to create some null fields. Idea is to update them later.

Consider this script which seems correct.

SELECT 'JOHN' as Name, NULL as Company into #temp

Now we will try to populate the null field with some data, probably with char data.

UPDATE #temp
SET Company = 'ABC and Company'

We are getting this error.


If you check the error message carefully, you might notice it’s a converting error. Actual problem is null field is considered to be an int, unless it’s properly defined with a type.

So correct statement to create the temp table is like this;

SELECT 'JOHN' as Name, Cast(null as varchar(50)) as Company into #temp

Sep 16, 2013

Convert Guid to a String in SQL

Probably it’s just a simple thing, but I had to play a little bit to figure out this... so I thought of posting…

If you want to convert a Guid to a String in your query this is how you do it.

Id =CONVERT(VARCHAR(36),opportunityid)

Practical usage comes in SSRS reports; where you can’t play around with Guids within the report surface, in fact you need to get it as a string. I faced this when I needed to have a link to real record from the report.

Aug 21, 2013

Retrieving OptionSet Label using Value and Wise versa

Just though of posting a code snippet. This c# code could be helpful for someone. Code written as a static methods.

Pass the Value and retrive the Label.

public static string GetOptionsetLabel(IOrganizationService _service, String _entityName, String _attribute, int _optionVal)
    {
        string _optionName = String.Empty;

        RetrieveAttributeRequest attributeRequest = new RetrieveAttributeRequest
        {
            EntityLogicalName = _entityName,
            LogicalName = _attribute,
            RetrieveAsIfPublished = true
        };

        RetrieveAttributeResponse attributeResponse = (RetrieveAttributeResponse)service.Execute(attributeRequest);
        AttributeMetadata attrMetadata = (AttributeMetadata)attributeResponse.AttributeMetadata;
        PicklistAttributeMetadata picklistMetadata = (PicklistAttributeMetadata)attrMetadata;
        
        foreach (OptionMetadata optionMeta in picklistMetadata.OptionSet.Options)
        {
            if (optionMeta.Value == _optionVal)
            {
                _optionName = optionMeta.Label.UserLocalizedLabel.Label;
                break;
            }
        }
        return _optionName;
    }

Now pass the Label and retriev the Value;

public static int GetOptionSetVal(IOrganizationService _service, String _entityName, String _attribute, String _optionLabel)
    {
        int _optionVal = -1;

        RetrieveAttributeRequest attributeRequest = new RetrieveAttributeRequest
        {
            EntityLogicalName = _entityName,
            LogicalName = _attribute,
            RetrieveAsIfPublished = true
        };

        RetrieveAttributeResponse attributeResponse = (RetrieveAttributeResponse)service.Execute(attributeRequest);
        AttributeMetadata attrMetadata = (AttributeMetadata)attributeResponse.AttributeMetadata;
        PicklistAttributeMetadata picklistMetadata = (PicklistAttributeMetadata)attrMetadata;
        
        foreach (OptionMetadata optionMeta in picklistMetadata.OptionSet.Options)
        {
            if (optionMeta.Label.UserLocalizedLabel.Label == _optionLabel)
            {
                _optionVal = optionMeta.Value.Value;
                break;
            }
        }
        return _optionVal;
    }

Cheers.

Aug 14, 2013

Jul 30, 2013

Setting regarding object in JavaScript

When programmatically creating a task/activity, I found it is little tricky the way we need to assign regarding object. For the benefit of others, though of sharing the code snippet. I am using Ascentium CrmService for my client side operations.

var beEnt = new BusinessEntity("task");
beEnt.attributes["regardingobjectid"] = "F5E42F4A-8539-E011-8602-005056A61424"; //GUID of record
beEnt.attributes["regardingobjectid"].name = "ABC and Company"; //name of the record
beEnt.attributes["regardingobjecttypecode"] = "account"; //type of record
beEnt.attributes["subject"] ="sample subject";
beEnt.attributes["description"] = "Sample body";
oService.Create(beEnt);

Jul 7, 2013

Coloring the Subgrid rows

As we discussed previously, we can use colours for branding purposes and improve the useability. Most clients seem happy to have different colours for different rows within the Subgrid depending on their status or etc.

For example: In my cases I have an Institute entity where sub institutes are in a Subgrid. I am going to give a colour for profit making institutes. (Highlighting expensive quote products within a Quote would make more sense for you). Outcome would be something like this;


How we going to achieve this? This consists of three steps.

1) Wait till Subgid is loaded

Biggest challenge of playing with Subgids is they load in an Asynchronous manner. In that case normal onload would not fire. So we attach a method for loading with a Delay. This is the code in loading;

setTimeout("attachGridAction();", 500);

Now we will implement the relevant method.  This got a trick. In catch statement delays and calls the method again. This allows you to wait and load the code again to see if Subgrid is loaded. Your real code will run only after that is accomplished.

function attachGridAction() 
{ 
  try 
  {
    var _grid = document.getElementById("Sub_Org_List");
    if ((_grid) && (_grid.readyState == 'complete'))
    {
        // Code goes here
    }
  }
  catch (err) 
  {
       setTimeout("attachGridAction();", 500);
  }
}

2) Determine the criteria

Now you need to determine the criteria of the records to be coloured. Actually you need to read the relevant Guids. Read the relevant Fetch XML as below. If you uncomment the second line, you will see the fetch xml statement of the Subgrid. Now you are free to retrieve those records and determine the Guids according to your criteria.

var _fetch = document.getElementById("effectiveFetchXml");
//alert(_fetch.value);

3) Colouring the row

Now you have the Guids ready. Now perform this code within a loop for your Guids; (I am just using one single record to make it clearer)

var _checkbox =   document.getElementById('checkBox_{4ACC296A-0CE5-E211-A9C3-00155D467A0E}');
var _checkboxParentParent = _checkbox.parentNode.parentNode;
_checkboxParentParent.bgColor = 'DarkKhaki'; 

Limitation;
As you may guess, this doesn’t work for next pages if paging is performed for Subgrid. What you can do is increase the items in the Subgrid allowing to have a scrollbar and ignore the paging option.

Jul 2, 2013

Change colour of Fonts, Sections and Tabs

Though Microsoft Dynamics CRM 2011 forms are nicely presented, there could be instances that you may need to change the colours. It can be for branding purposes or to improve useability aspects. Most of them could be done through loading Java Scripts. We will check some of basics here.

In these codes colour can be determined by HTML code or standard name.
Click this for a basic list of such names and relevent HTML codes.

1) Fonts and backgrounds of the Fields

//Colouring the background of the label
document.getElementById('new_name_c').style.backgroundColor="CornflowerBlue";

//Colouring the field value
document.getElementById('new_name').style.color="#8B0000";

//Colouring the label
document.getElementById('new_contactperson_c').style.color="DarkOrange";

//Colouring the background of the field value
document.getElementById('new_contactperson').style.backgroundColor = 'Gold'


2) Tabs

document.getElementById("tab1").style.backgroundColor = '#F0E68C';
 


3) Sections

document.getElementById('{883d8330-156d-e4e5-130b-934752157dd3}').style.backgroundColor = 'DeepSkyBlue';


We will look into much complex colouring options in another post.

Jun 27, 2013

Plugin for opportunity Win / Lose

It can be a common requirement to execute plug-ins when opportunities are closed as Win or Lose. In most cases you need to identify two cases separately. Ironically, I didn’t find many resourceful articles about it. In fact, I thought of sharing my experience.

These are the relevant statecode and statuscode combination for opportunity.


First one could think of having a State Change plug-in for this. But I learned it’s not successful.

Whether it is Win or Lose, opportunity will be closed. So it creates a record in OpportunityClose entity. Then I though, we could do a create plug-in for OpportunityClose entity. Now the problem is you are not able to catch whether it’s a Win or Lose. If your requirement is just to do something when opportunity is closed, this works.

Then only I decided to do two different plugin for Win message and Lose message which triggers the plugin in the correct action.

So correct plugin registration steps would be seen as below;



Then I checked the plug-in context which made me shocked again. It doesn’t have opportunity record but it does have an OpportunityClose.


OpportunityClose is of course an unfamiliar entity for me. For you too obviously! Then only I realised it should be called a “Black Sheep”. You know why? Could you guess the primary Key of this entity? Your obvious answer should be opportunitycloseid which is completely wrong! Check below picture. It is Activityid!

Anyway, strangeness of primary key was explained only for your knowledge. The good side is this entity contains opportunityid which is the gateway for all the attributes of our current record. So for both Win and Lose plugins I started coding as below, by passing the OpportunityClose instead of Opportunity, knowing that it contains opportunityid.

context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
service = factory.CreateOrganizationService(context.UserId);

if (context.InputParameters.Contains("OpportunityClose"))
{
   oppCloseEnt = (Entity)context.InputParameters["OpportunityClose"];
   .........
   .........
}

In summery I am passing opportunityclose, read opportunityid in it, retrieve opportunity fields I need using the service. In a way, it’s like asking something about your home from your neighbour! Anyway, it worked for me!

Jun 25, 2013

Issues of using checkbox in CRM 2011

In a previous post I explained difference of using onChange and onClick in CRM 4.0

Now it has come the time to talk the same thing against CRM 2011. As same as 4.0, even in CRM 2011 we got only the onChange in native manner. As we all know the problem is you need to click somewhere else after clicking the checkbox to execute the event.  So we need to implement onClick to accomplish this. It is bit similar to  4.0, but there is a complication.

For some crazy reason, standard Xrm model that works for 2011 doesn’t work here and have to use the 4.0 style code. Other issue is execution take cyclic pattern and our code gets executed twice! So we need to change the focus as soon as we finish the code. Please check the working code (this should go in onload event);

MyCheckbox = function()
{
    crmForm.all.new_sample.onclick = function()
    {
       //Code
       //Change focus to another field  
    }
}

Issues are not finish yet! Practically, we might need to do something according to the value of the relevant check box. Biggest confusion comes here. Values given are completely opposite. When you check the checkbox you might get false instead of true and vice versa. Why this happens?

I am trying to understand it this way. (please correct me If I am wrong) This is not an error. We are executing the code on “click”. This doesn’t mean we have changed the value in the time we execute the code. Of course what we see is the "changed" situation through the form. Actually code happens for previous value of the check box. I think this is the reason Microsoft doesn’t provide this event in their native framework.

If we understand this, we are good to proceed with our work without any trouble. Only thing is do the opposite when playing around with the value of the checkbox upon onClick.

var _checkbox = Xrm.Page.getAttribute("new_sample").getValue();
if (_checkbox == true)
{ 
  //code for false 
}
else
{
  //code for true 
}

This is bit confusing.. but manageable.

Merging DLLs to register plug-ins in database

When we code plug-ins usually, we do them according to programming principles.  One strong principle we all adhere is separating codes to different layers: mainly for calling part, Business Logic Layer, Data Access Layer and etc. So we can copy all DLLS to relevant path (Server\Bin\Assembly) when deploying the plugin and call the accessing part through plug-in registration tool. In this case we need to register the plug-in in Disk mode.

How about if we need to register the plug-in in Database mode?


Here we can only point one DLL. So are we going to get rid of our systematic code that produces few DLLs? No. You can merge the DLLs to one and register.

Suppose I got below three DLLs and XXXPlugin.dll is the calling DLL.

XXXPlugin.dll

XXXDataLayer.dll

XXXBizLayer.dll

Below is the code to bundle the DLLs to one called “PluginCaller.dll” (you can use any name).

REM ilmerge  /targetplatform:v4,C:\Windows\Microsoft.NET\Framework\v4.0.30319   XXXPlugin.dll    /keyfile:xxxKey.snk  /out:PluginCaller.dll 


ilmerge  /targetplatform:v4,C:\Windows\Microsoft.NET\Framework\v4.0.30319    PluginCaller.dll XXXBizLayer.dll XXXDataLayer.dll /keyfile:xxxKey.snk   /out:PluginCaller.dll  

Now you are good to go.

Note

Now this tempt you to use a development CRM organization too in the same server as Live, since you can register any number of plug-in versions of same DLLs.

Still I don’t think it’s an advisable thing. While infrastructure specialists can find more reasons for this, my simple question is will your client going to allow you to install development tools in the same server? Otherwise how you going to debug the development CRM? How you going to restart IIS in the process of development?

By the meantime, this will be a good solution for CRM online systems too.

(I am thanking my colleague Kin Ng for helping me with this)

Jun 17, 2013

Assigning a User or Team

Just though of posting an important code snippet on how to assign a user to a record. Please find a sample code.

using Microsoft.Crm.Sdk.Messages;

AssignRequest assignAcc = new AssignRequest
{
    Assignee = new EntityReference("systemuser", new Guid("5BA4B404-EF87-4F11-8492-D437823E18AB")),
    Target = new EntityReference("opportunity", new Guid("F01F4B23-A996-4519-8395-C7428DDEA8DB"))
};
service.Execute(assignAcc);

Here Target entity is the entity type of the record we are going to assign a new user.  In my case, its opportunity. Here I am assigning a user but we can use same code for assigning a team. In that case Assignee will have to change accordingly. (team instead of systemuser)

Jun 4, 2013

Calculate total from related entity values

This is a very common scenario. For example Quote entity contains fields to have total of all the relevant quote product values. Yet, we don’t need to bother about it since CRM is taking care of those totals. Suppose we need to do such calculation in our custom scenario it will not be handled by CRM.

Just like Quote and Quote product (or Opportunity and Opportunity product), suppose we got entity that has related records as child records or line items. Each child record could have a value for surcharge and primary entity should have the total surcharge. How you accomplish this? In other terms, when child values get changed total should get changed.


Now we will consider the events that child values could change. They are Create, Update and Delete. Obviously we need to write plug-ins for those events to adjust the total. Below would be our algorithm;

Total (Surcharge) = Sum of All Line Item (Surcharge)

Now consider different events;

Post Create – Execute above algorithm

Post Update – Execute above algorithm

What about the delete?

This is the tricky part. Since there is nothing called post-delete, obviously we need to run this in pre-delete. Still we don’t get expected value by executing the same algorithm in pre delete. Why? This uses pre values. In this case total will not be affected by the calculation. Total will not get deducted the value of deleted child record. How we solve this?

You will only have to update the surcharge value of the deleting record to zero in pre delete plug-in. When you do this, post-update plug-in will execute and total will be set except for the record which will be deleted soon. So this is the correct out come!

Apr 29, 2013

How Queues work

This is a very good video on functionality of Queues of CRM 2011 works. This is simply yet clearly explained… though of sharing..



Apr 2, 2013

Creating notes programmatically

Adding notes for any data record is a powerful feature in Microsoft Dynamics CRM 2011. In this way, user can merge external information as files in different formats such as text, pdf, image or etc.


All these files are kept in CRM in Note entity. Schema name for this entity is annotation and it is not highly customizable. In fact, there will not be a need of doing it. However we might need to create and copy annotations. You can even retrieve data.

If you check the DB, you will realise how those notes are kept in the table fields.


For me, most exciting field is documentbody, that keeps the content as encoded data. Objectid is the other remarkable field, which is actually the lookup for related record. Now we will see a couple of code snippets;

This is how we can create an annotation using a text file. In my sample I am doing it for a specific record in quote. Record is mentioned by Guid.

string filePath = @"C:\Sumedha\log\Sumedha.txt";
byte[] fileContent = File.ReadAllBytes(filePath);
string encodedData = System.Convert.ToBase64String(fileContent);

Entity _annotation = new Entity("annotation");
_annotation.Attributes["objectid"] = new EntityReference("quote", new Guid("41613500-9F87-E211-905C-00155D467A0E"));
_annotation.Attributes["objecttypecode"] = "quote";
_annotation.Attributes["subject"] = "Demo";
_annotation.Attributes["documentbody"] = encodedData;
_annotation.Attributes["mimetype"] = @"text/plain";
_annotation.Attributes["notetext"] = "My Sample attachment";
_annotation.Attributes["filename"] = "MySample.txt";
service.Create(_annotation);

Now we will see how same thing is achieved using a pdf file. You will realise that encoding method and mimetype are different.

FileStream _stream = File.OpenRead(@"C:\Sumedha\log\TestPDF.pdf");
byte[] _bData = new byte[_stream.Length];
_stream.Read(_bData, 0, _bData.Length);
_stream.Close();
string encodedData = System.Convert.ToBase64String(_bData);

Entity _annotation = new Entity("annotation");
_annotation.Attributes["objectid"] = new EntityReference("quote", new Guid("41613500-9F87-E211-905C-00155D467A0E"));
_annotation.Attributes["objecttypecode"] = "quote";
_annotation.Attributes["subject"] = "Demo";
_annotation.Attributes["documentbody"] = encodedData;
_annotation.Attributes["mimetype"] = @"application/pdf";
_annotation.Attributes["notetext"] = "My Sample attachment";
_annotation.Attributes["filename"] = "MySample.pdf";
service.Create(_annotation);

Also you can do the same without a file, if you are interested in using just a string as the input. For that you can do the data encoding as below and do the rest as usual.

string _str ="Sample - file doesnt get created in server";
byte[] _bstr = Encoding.ASCII.GetBytes(_str);
string encodedData = System.Convert.ToBase64String(_bstr);

By the way, I found a nice blog article that provides a lot of code snippets in this regards. Please refer it here;
http://lakshmanindian.wordpress.com/2012/11/01/attachments-in-microsoft-dynamics-crm-2011/

Mar 26, 2013

Debugging and trouble shooting of plug-ins

Post on trouble shooting using tracer.

I was thinking of summarising few facts that would be helpful in solving issues of plug-ins. In my list of suggestions, I am also giving links to other blogs that describes the steps descriptively.

1.Test whether plug-in is firing

First thing first; as a practice I always check whether my plug-in is firing before implementing lengthy logics. For that I always develop a very simple plug-in first and test whether it fires. My plug-in is just throwing an exception. For example, check below line;

throw new InvalidPluginExecutionException("Quote Pre update...fired!");

If I see the exception, I am ready to enhance the code.

2. Debug

Debugging is the next popular way of finding whether code works as expected. This consists of few basic steps to get the debugger start for your operation.

a. Make sure you copy your .pdb files along with .dll files to the specific location
    (i.e. Program Files\Microsoft Dynamics CRM\Server\bin\assembly)
b. Attach w3wp.exe to your process in VS. (attach CRMAsyncService.exe for asynchronous operations or workflows)
c. Do the relevant operation for the plug-in that jumps the debugger to your break point.

3. Remote debugging

In order to perform normal debugging you need to have the developer environment in the CRM server itself. If your problem occurs explicitly in production server you may need to do remote debugging which could be a much complex process.

Please read this post that explains how to do it;
http://weblogs.asp.net/pabloperalta/archive/2010/12/01/how-to-remote-debug-dynamics-crm-plugins-and-workflow-assemblies.aspx

4. Write into a log file

In practical world, you are not allowed to change configurations in a production server and that can stop you from remote debugging either. In that case, you can go back to basics. Writing required information to a log file. It’s a matter of adding few lines to your plug-in code. Check below suggested code;

using System.IO;

StreamWriter sw;
sw = File.CreateText(@"C:\Devspace\log\Calcs.txt"); 
try
{
 ........... 
 sw.WriteLine("Value 1 : " + value1);
 sw.WriteLine("Value 2 : " + value2);
 ...........
 ...........
}
catch (Exception e)
{
 sw.WriteLine("Exception: " + e.Message);
 throw e;
}
finally
{
 sw.Close();
}

5. Profiling (Specially for online plugins)

Trouble shooting of CRM online plug-in could be another hurdle. 

Below post explains how to accomplish it through profiling, using latest plug-in registration tool;
http://guruprasadcrm.blogspot.com.au/2011/11/how-to-debug-crm-2011-online-plugin.html

Mar 20, 2013

Sales process – Part 4 – Modifying calculation of quote

In previous posts we discussed how quote pricing is done in the native way and modifying them in item level (i.e. Quote Product) in Microsoft Dynamics CRM 2011. As the final stage, we will look in to the ways of modifying the grand total (i.e. Quote) if we happen to do so.

This requirement could occur mainly as below;

a) add a surcharge, administration cost or insurance
b) give discount comes within a loyalty program or discount determined by total
c) calculation of commissions/ loyalty points according to total

Consider the pricing pane of the quote form.


Three fields I put within the green squares are editable fields. Check them with their schema names and types.

Name
Schema Name
Type
Quote Discount %
discountpercentage
Decimal
Quote Discount
discountamount
Currency
Freight Amount
freightamount
Currency

These are the field that can’t be edited. For ease of explaining, I am dividing them to two different categories.

None editable – Category A -Fields derived from the quote products

Name
Schema Name
Type
Detail amount
totallineitemamount
Currency
Total Tax
totaltax
Currency

None editable – Category B – Calculated fields

Name
Schema Name
Type
Pre-Freight Amount
totalamountlessfreight
Currency
Total Amount
totalamount
Currency

Considerations for plug-in development

Obviously we need to develop our own plug-ins to enhance/modify the current calculations. Now consider my research result on plugin behaviour in this regard. When “creating” the quote, calculations are not applicable since quote product addition comes in a later stage. In fact, we are talking about update plug-in.

Also it is important to understand user actions that update plug-in would fire.  (Will call this action type for ease of referencing)

i) Opening the Quote
ii) Saving the Quote
iii) Pressing Recalculate button
iv) Adding/ Modifying/ Deleting  a quote product

Please check below table. I use word “available” to say it is available in plug-in context. When I say “retrieve” it means retrieve the current value from database using the web service.

Update
Pre stage
Post stage
Editable fields
(of first table)
Available if changed, otherwise retrievable. Can be modified. *
Available if changed, otherwise retrievable. Can be modified.*
None editable (Both Category A and B)
Available only for Action type (iv).** Can be retrieved. Cannot modify.
Available only for Action type (iv). ** Can be retrieved. Cannot modify.
* If you modify the same entity in Update event, make sure you avoid infinite loops.
** Plug-in run by action of addition/modification/deletion of a quote product.


Revealed plug-in behaviour seems bit complex. None editable fields become available in plug-in context when update is executed by a change of a quote product.

Other exciting observation is, when I register the plug-in for pre-stage it get executed more than once depending on users actions (refer i, ii, iii, iv) . This is bit confusing and unexplained. However, this complexity keeps us away from writing codes for pre update stage.  I am happy if someone has figured out this and can share.

Good news is we don’t need to understand all these to do our modifications.  Most important behaviour is whenever we change the editable fields, calculations get updated accordingly, regardless of when you change.

As a summery we will stick in to some rules which will give us enough space for our work. Consider below facts;

Some points to think

1) Register your plug-in in post Update.

2) You can retrieve none editable category (A) fields and use for your logic. If you see carefully, you will realise these (sum of line item values and sum of taxes) are the important reference fields to implement any logic together with editable fields.  Anyway, never try to modify category (A) fields.

3) Never try to reference or modify category (B) fields. If you want to reference them determine the values by calculating. (Reference 2nd part of this article)

4) Out of three editable fields, quote discount and freight amount gives you the most needed space to enhance the functionality as you wish. One field is being added to the total while one is being subtracted. So whatever the adjustment you need to do can be done through these two fields. Think a bit.

5) Whatever the modifications you do, you have to be creative enough to use the native total fields and native functionalities. Our changes should be carefully pushed into the bult-in mechanism through given spaces.

1st part of this article: Sales process – Part 1 – Product Catalog
2nd part of this article: Sales process – Part 2 – Quoting
3rd part of this article: Sales process – Part 3 – Modifying calculation of quote product

Mar 14, 2013

Sales process – Part 3 – Modifying calculation of quote product

A couple of previous posts explained the standard way of working with quotations and its level of flexibility. In rare cases, it could need you to modify the standard calculations. In this article, we will analyse options and constrains of doing such tasks in Quote product. Most popular requirement that would come your way could be implementing automated tax (or GST) system. Natively TAX is just an editable field in quote product.

Consider the pricing pane of the quote product form.


Three fields I put within the green squares are editable fields. These are the editable fields with their schema names and types.

Name
Schema Name
Type
Quantity
quantity
Decimal
Manual Discount
manualdiscountamount
Currency
Tax
tax
Currency

These are the field that can’t be edited with their schema names and types.

Name
Schema Name
Type
Price per unit
priceperunit
Currency
Volume Discount
volumediscountamount
Currency
Amount
baseamount
Currency
Extended Amount
extendedamount
Currency

Considerations for plug-in development

Obviously we need to develop our own plug-ins to enhance/modify the current calculations. Now consider my research result on plugin behaviour in this regard.

 
Create
Update
Pre stage
Post stage
Pre stage
Post stage
Editable fields
(of first table)
Available, can modify
Available, can modify
Modified field available and others can be retrieved, can modify *
Modified field available and others can be retrieved, can modify*
Other field
(of second table)
Not available, cannot modify
Not available, cannot modify
Can be retrieved, cannot modify.
Can be retrieved, cannot modify.
* If you modify the same entity in Update event make sure you avoid infinite loops.

Essence of this result is we can only play around with Quantity, Manual Discount and Tax. Good news is any time you change those fields; calculations are done accordingly to newly changed values of those fields. In other terms “Extended amount” get the correct value regardless of where you change those fields.

It implies CRM does its own calculations after the plug-in engine has completed its performances. In fact, you should not/ cannot amend other fields such as Price per Unit, Volume Discount, Amount and Extended Amount.

Some points to think

1) If you want to introduce an auto tax calculation method, it’s possible. Better have the logic for both create and modify event plug-ins.

2) If you consider the fact that we have two fields (namely Tax and Manual discount) to play around; tax is being added and Manual discount is being subtracted from total automatically. So whatever the additions and subtractions you need can be pushed to the current calculation mechanism if you are creative enough. Think a bit.

3) There is a chance your logic needs fields which are unavailable in plug-in context such as Price per Unit, Volume Discount and Amount. In that case, you are still able to retrieve first two fields from Price List and Discount List entities respectively and Calculate Amount. Read my 1st and 2nd parts of this article for better understanding.

4) One would also think of give up the native total fields and introduce new custom fields, WHICH IS NOT RECOMMONDED AT ALL. This means you are rewriting your own calculation logic for entire quoting including quote product and quote. That will also push you to introduce your own recalculation button in Quote header. Also you are again facing problems when quote values are transferred to Order entity in a later stage. Personally, I don’t see any reason to go in to this mess.

1st part of this article: Sales process – Part 1 – Product Catalog
2nd part of this article: Sales process – Part 2 – Quoting
4th part of this article: Sales process – Part 4 – Modifying calculation of quote