Introduction & Example
In my previous post, I was talking about how to validate the mandatory fields when you click a ribbon utilizing the “Xrm.Page.data.save().then(successCallback, errorCallback);” method.
Now, what if we need one more advance validation such as from related entities or external service, we need server side code in plugin, not only because it is needed but also because it is easier for some .NET Programmer rather than writing in javascript and also we might need to use to validate it from anywhere.
Following my previous post, now, there is requirement to check whether the campaign has Campaign Activity defined or not before you make this campaign as launched.
Solution & Result
So, now I create a plugin to validateHere is my Plugin Code:
using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Query; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Xrm.CRM2015.Plugin.Campaign { public class Campaign : IPlugin { public void Execute(IServiceProvider serviceProvider) { #region must to have IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); // Create service with context of current user IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId); //create tracing service ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); #endregion if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity) { //create entity context Entity entity = (Entity)context.InputParameters["Target"]; if (entity.LogicalName != "campaign") return; else { //if status reason is changed if (entity.Contains("statuscode")) { if (((OptionSetValue)entity["statuscode"]).Value == 2) //Launched { //put MY Logic here if (CheckIsHavingCampaignActivity(service, entity) == false) { throw new InvalidPluginExecutionException("Please add at least one Campaign Activity"); } } } } } } private bool CheckIsHavingCampaignActivity(IOrganizationService service, Entity entity) { //get the Campaign Activity from this Campaign QueryByAttribute querybyattribute = new QueryByAttribute("campaignactivity"); querybyattribute.ColumnSet = new ColumnSet(true); // Attribute to query. querybyattribute.Attributes.AddRange("regardingobjectid"); // Value of queried attribute to return. querybyattribute.Values.AddRange(entity.Id); // Query passed to service proxy. EntityCollection retrieved = service.RetrieveMultiple(querybyattribute); if (retrieved.Entities.Count == 0) { return false; } else { return true; } } } }
As we know that plugin message is very flat, simple, and straight-forward, the user can be confused with only one message in the centre of the screen, different from Javascript validation that you can add alert or you can add notification for CRM 2013 and above. This is one of the disadvantage over its plugin capability, you might need some additional art to reach user expectation in validation.
Now, I want to make sure that this message also passed to the Form Scripting and then I want to add more notification to the users telling them that to launch this Campaign, they need at least one Campaign Activity.
So, I would like to catch the exception from plugin to determine my next action.
From my previous post, we can add the errorCallback method and attach some function on it and I put debugger:
Yay,, horreey, we can grab the SaveErrorResponse message…
Now, I add some scripts:
function launch() { Xrm.Page.data.save().then(successCallback, errorCallback); } function successCallback() { //set status to Launched alert("Your campaign has been published"); //then refresh if needed var Id = Xrm.Page.data.entity.getId(); Xrm.Utility.openEntityForm("campaign", Id); } function errorCallback(saveErrorResponse) { if (saveErrorResponse.message.indexOf("Activity") != -1) { Xrm.Page.ui.setFormNotification("Hi, don't forget to add the Campaign Activity!!", 'ERROR'); } //remember you can also clean up the notification }And this is the result:
Not only that, using this message you can do more than that.
Now, I want to put more spices on it..
That is to make the user more aware by set notification in field level
Then, after I add Campaign Activity, I can launch it successfully
The Code
function launch() { //do not forget to always clear it Xrm.Page.getControl("totalcampaignactivityactualcost").clearNotification(); Xrm.Page.data.save().then(successCallback, errorCallback); } function successCallback() { //set status to Launched alert("Your campaign has been published"); //then refresh if needed var Id = Xrm.Page.data.entity.getId(); Xrm.Utility.openEntityForm("campaign", Id); } function errorCallback(saveErrorResponse) { if (saveErrorResponse != null) { if (saveErrorResponse.message != 'undefined' && saveErrorResponse.message != null) { if (saveErrorResponse.message.indexOf("Activity") != -1) { Xrm.Page.ui.setFormNotification("Hi, don't forget to add the Campaign Activity!!", 'ERROR'); //remember you can also clean up the notification Xrm.Page.getControl("totalcampaignactivityactualcost").setNotification("Please put more activity"); Xrm.Page.getControl("totalcampaignactivityactualcost").setFocus(); } } } }
Conclusion
*In CRM 2013 and above, I meet new favourite idol feature to love, that is Custom Action.
But, for CRM 2011 this feature is not available and since it is a new feature, I realize many developers still do not want to use it and plugin is more friendly for us since it has been accompanying us for years and we already knew its concept pretty well.
This method can be useful for some circumstances:
1. When you want validate something from Ribbon action or any other javascript that can trigger plugin through onsave event (in this example)
*You can also use oData action that can call plugin and return it.
2. When you want to check duplicate not using duplicate detection, then you can show the user which field is really causing the duplicate trouble by giving it special setFocus treatment
3. Do other action that need server side code
Now, you are be able to pass and obtain the plugin context message that you can use to manipulate the CRM Form UI.
But, remember, plugin will stop the saving if got exception, if you need process to keep going, there is no other choice than using Custom Action, Javascript, or you can put some field yes/no to pass the plugin.
Hope this helps!
Thanks.
Hi Aileen, I've managed to catch the plugin exeception and alert the user via a messagebox, but is there a way to suppress the standard "Business Process Error" dialog from CRM, because this is also this showing after my alert.
ReplyDeleteYou can have a peek at link to read an article dedicated to tracking programs.
ReplyDeleteNice blog and absolutely outstanding. You can do something much better but i still say this perfect.Keep trying for the best. Hire JavaScript developer
ReplyDeleteThank you.
ReplyDeleteHi, we have plugin on send of email. We are throwing InvaligPluginException based on some conditions to stop the Email being sent. But user should not be shown the Business process dialogue. The code seems to do exact same thing but for our situation, this is not working. Can you please give some idea on how to get saveErrorResponse and stop the Dialogue being displayed
ReplyDelete