Tuesday, 31 March 2015

Forcing the Save on Read-only Fields

If the value of a filed changes then will always force the save. This get over the issue where read only fields don’t save .

var oldSetValue = Mscrm.AttributeWrapper.prototype.setValue;
Mscrm.AttributeWrapper.prototype.setValue = function(value) {
//   if you are changing the value of a field it should be set to always regardless of access
    if(value != this.getValue())
  {
  this.setSubmitMode("always");
  }
  // Call the function as it would have been called normally:
  oldSetValue.call(this, value);
  // Run stuff after, here.
}

I included it on the form in a separate library called optevia_fieldextentions on the form
You call the SetValue method as normal and it will save regardless if its readonly or not

Xrm.Page.getAttribute('optevia_year2fundingadjustment').setValue(0.00);

Friday, 30 January 2015

CRM 2015 Create a custom Action to Validate Data

The following explains how to take advantage of the Action Feature in 2013 , to validate an entity.
Once the action is in place it can be called from anywhere (Portal , Plugin , Client side JavaScript , Batch job, etc ) via the CRM service. The example below will explain how to validate the contact Record using a custom action.



1. In your project solution in CRM add a new process of type action . The name can be ValidateContact and The entity value can be Contact..  In the process arguments , It needs an Entity type (with contact as the rntity value) as input called "Entity" and  EntityCollection as an Output called "ValidationResults". No steps are required as the executing code will be in the Plugin .


2. Create a new entity called new_validationResult with attributes  Type(option set Error,warning), Feild (string), ValidationMessage(ntext). Refresh the early bound library
3.Publish All Customization's
4. Create a new Plugin with the following Code
 public void Execute(IServiceProvider serviceProvider)
        {
               base.Intialise(serviceProvider);
            EntityCollection collection = new EntityCollection();
            collection.EntityName = new_validationresult.EntityLogicalName;
            collection.MinActiveRowVersion = "-1";
           //To get access to the image of the Quote record
              Contact contact = new Contact();
              if (base.Context.InputParameters.Keys.Contains("Entity") &&                                                                    base.Context.InputParameters["Entity"] != null)
            {
                Entity entityRef = base.Context.InputParameters["Entity"] as Entity;
                contact.Intialise(entityRef);
            }
            if (contact.FirstName == null || contact.FirstName == string.Empty)
            {
                new_validationresult result = new new_validationresult();
                result.new_ValidationMessage = "Please Fill in First Name";
                result.new_Field = "FirstName";
                result.new_Type = (int)new_validationresultnew_Type.Error;
                collection.Entities.Add(result.ToEntity());
            }
            base.Context.OutputParameters["ValidationResults"] = collection;
        }

5. In the Plugin registration tool Register the Plugin . Create a new Step with  the now available "new_ValidateContact" Message. It can Be registered against the Post Execution.
Note for Online you need to restart Plugin Registration Tool in order for the message to be picked up.



6. The new_validateContact action can now be called from anywhere A c# early bound example below. You need to include the /generateActions parameter in the CRMsvcUtil command 

Early Bound Example 
  Contact contact = new Contact();
                contact.LastName = "Cunningham";
                contact.Create();
                new_ValidateContactRequest request = new new_ValidateContactRequest();
                request.Entity = contact.ToEntity();
                EntityReference reference = new EntityReference();
                reference.LogicalName = "contact";
                reference.Id = contact.Id; /// this can be set to a "dummy" contact if you want to validate the                         contact before create
                request.Target = reference;
                new_ValidateContactResponse response = CRMConnection.Service.Execute(request) as                             new_ValidateContactResponse;
                string messages = string.Empty;
                foreach (Entity result in response.ValidationResults.Entities)
                {
                    new_validationresult o = new new_validationresult();
                    o.Intialise(result);
                    messages += o.new_ValidationMessage;
                }
                Console.WriteLine("Errors found:" + messages);
           

 late bound example 
   Entity contact = new Entity("contact");
                contact.Attributes.Add("lastname", "Cunningham Late");
                contact.Id= CRMConnection.Service.Create(contact);
                OrganizationRequest request = new OrganizationRequest();
                request.Parameters.Add("Entity",contact);
                request.RequestName = "new_ValidateContact";
                EntityReference reference = new EntityReference();
                reference.LogicalName = "contact";
                reference.Id = contact.Id; /// this can be set to a "dummy" contact if you want to validate the                         contact before create
                request.Parameters.Add("Target", reference);
                OrganizationResponse response = CRMConnection.Service.Execute(request) as                                         OrganizationResponse;
                string messages = string.Empty;
                EntityCollection collection = (EntityCollection)response.Results["ValidationResults"];
                foreach (Entity result in collection.Entities)
                {
                    messages += result["new_validationmessage"].ToString();
                }
                Console.WriteLine("Errors found:" + messages);
           
 A JavaScript example 

function ExecuteValidationActionCreateProject()
{
var target1 = Sdk.EntityReference("contact",Xrm.Page.data.entity.getId().replace('{', '').replace('}', '') ,null);
var entity1 = null;
   var request =  Sdk.new_ValidateContactRequest(entity1,target1);
    Sdk.Async.execute(request, successCallBack, errorCallBack, passThruObj );
}
function successCallBack(data)
{
var entityCollection = data.getValidationResults();
var validationResults = entityCollection.getEntities();
var message = "" ;
for ( i = 0 ; i < validationResults.getCount(); i++)
{
var entity = validationResults.getByIndex(i);
var attributes = entity.getAttributes().getAttributes();
for ( r = 0 ; r < attributes.getCount(); r++)
{
var attribute = attributes.getByIndex(r);
if (attribute.getName() == "new_validationmessage")
{
message= message.concat(attribute.getValue());
}
}
}
if(message != "")
{
alert("Errors:"+message);
}
}
function errorCallBack(data)
{
alert("Error");
}
function passThruObj(data)
{
}

Note: In this example "Contact " was used as the target entity(see step one). If you choose contact you have to pass an existing contact guid as the target when using this custom action. If you don't the action request will fail. Using "none" will allow you to bypass this check. This would get over the problem where for example you wanted to validate a contact  before creating that contact.(contact would have no guid so you have no guid to pass to the target)

Wednesday, 28 January 2015

Consolidation of Dll references Using ILMerge For CRM 2015 Online

In CRM 2015 online you can only register a self contained plugin with no references to any other DLLS. The following outlines how to to consolidate mutable plugins together so it is fit to be registered online.
The Scenario Example used is a DLL called ContactPlugin creates a letter on the update of a Contact.
It has a reference another DLL called VS.RSACPC.CRM.DataModel which is an early bound CRUD operation plugin which allows any component to manipulate data in CRM.


1. Download and instal ILmerge form the following link ILMerge. The Compent will automattically be installed at the following path:  C:\Program Files (x86)\Microsoft\ILMerge\
2. Create the ContactPlugin , add a reference to the VS.RSACPC.CRM.DataModel and implement the code to create a letter on update. The code below passes the service connection to the CRUD using the base.Intialise method BusinessRuleBase class and then creates a letter in CRM:
using Microsoft.Xrm.Sdk;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VS.RSACPC.CRM.DataModel;
namespace ContactPlugin
{
public class ContactUpdate : BusinessRuleBase, IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
base.Intialise(serviceProvider);
Letter letter = new Letter();
letter.Subject = "Test Letter"+ DateTime.Now;
Entity entity = base.Context.PostEntityImages["PostImage"];
letter.RegardingObjectId = new EntityReference("contact", entity.Id);
letter.Create();
}
}
}

3. Set up the project so all DLLs copied to the to a one location (S drive)/ On successful build there will be two Dlls.
ContactPlugin.dll
VS.RSACPC.CRM.DataModel.dll

4. In CMD Use the following command to Merge the two dlls together with the desired name. It includes a reference to the .net framework and a keyfile which is required for all CRM plugins  . Also required for the cloud is s:\Microsoft.Xrm.Client.dll and Microsoft.Xrm.Sdk.Deployment.dll if you require them as they are not available in the cloud
"C:\Program Files (x86)\Microsoft\ILMerge\ILMerge.exe"  /keyfile:s:\Vulcan.snk /target:library  /targetplatform:v4,"C:\Windows\Microsoft.NET\Framework\v4.0.30319" /out:"s:\SelfContainedPlugin.dll" "s:\NewContactPlugin.dll" "s:\VS.RSACPC.CRM.DataModel.dll" "s:\Microsoft.Xrm.Client.dll"  "s:\Microsoft.Xrm.Sdk.Deployment.dll"

Note:Its worth putting this into a bat file so it can run over and over

5. Once you have the DLL you can register it with plugin registration tool


6. Register the Step and the required Images
7. Test the plugin