Quantcast
Channel: Sam's Activities
Viewing all 3363 articles
Browse latest View live

Scripting on Business Process Flow in CRM 2015- Part I

$
0
0

With the introduction of MS CRM 2015, came many new features. The one we are going to discuss here is advancement of Business Process Flow and especially the Scripting part, which came as boon.

Before CRM 2015, the Business Process Flow exist, but no API availability made it impossible for the developers to interact with the BPF via programming.

CRM 2015 gave it a new look, while creating a Business Process Flow, we have the option of creating Branched stages, which in simple terms is we can conditionally decide which stage to be shown after the current stage. The branching decisions can be made on the basis of the value of any step used for the current stage.

Previously we were not allowed to visit an entity more than once before, now we can visit an entity more than once.

Above are the few highlights of features added for BPF, let`s delve into the Programmability enhancement.

The most important part is, How to hook to the Stage change or Stage Select events of BPF? This is indeed the simplest part.

Example:

If you want to hook to the Stage change event, you can do this by writing the below code. And, you have the freedom to place this code either onLoad or onSave of the form or else on change of any field.

//Register a function on change of the stage

Xrm.Page.data.process.addOnStageChange(stageChange);

 

stageChange – It is the function to be called when the stage is changed.

 

Similarly, you can hook onto the Stage select event.

//Register a function on select of a stage

Xrm.Page.data.process.addOnStageSelected(stageSelected);

 

stageSelected – It is the function to be called when the stage is selected.

 

We have the options to unhook the events as well.

//Unbind a function on change of the stage

Xrm.Page.data.process.removeOnStageChange(stageChange);

//Unbind a function on select of a stage

Xrm.Page.data.process.removeOnStageSelected(stageSelected);

By hooking onto either stage change or stage select events, we have plethora of options to use.

Like we mentioned above, that we can Branch the entities conditionally. We can leverage this functionality by showing different fields for an entity depending on the value selected for the step on the previous stage.

Let us explain you few of the available methods in this part of the Blog, and the rest in this.

  • Collapse or Expand the Business Process Flow:

We have the option to collapse the Business Process Flow by default, on load of the form.

Snippet:

Xrm.Page.ui.process.setDisplayState(string)

string: “expanded”  or “collapsed” are the two variables that can be passed.

 

  • Show/Hide the Business Process Flow:

We can show or hide the business flow. It can be useful when we don`t need few of the Security Roles to see the BPF.

Snippet:

Xrm.Page.ui.process.setVisible(bool)

bool: “true” to show and “false” to hide.

 

  • Active Process:

We can get the Current Active Process and we can set the Current Active Process. This is useful if you want different security profiles to see different processes.

  • getActiveProcess

This will give you the object of the current Active Process. Object will have,

Process Name, Process Id(GUID), Render State(Visible/Hidden) & Collection of Stage Objects.

Snippet:

var procObj = Xrm.Page.data.process.getActiveProcess();

  • setActiveProcess

This will allow you to set the Active Process.

Snippet:

Xrm.Page.data.process.setActiveProcess(procGUID, callbackFunction);

procGUID: Id of the process to be set.

callbackFunction: Function that will be called in order we have any necessary actions to be performed on setting the process.

 

  • Active Stage:

We can get the Current Active Stage and we can also set the Current Active Stage. This is useful if you want a user to move back to previous stage, when he selects “x” value for a step on current Active stage.

  • getActiveStage

This  will give you the object of current Active Stage. Object will have, Stage Name, Stage ID(GUID), Base Entity, Stage Status(active/inactive) & Collection of Step objects.

            Snippet:

var actStg = Xrm.Page.data.process.getActiveStage();

  • setActiveStage

This will allow you to set an Active Stage.

Note: Only completed stage for the current entity can be set using this method.

            Snippet:

Xrm.Page.data.process.setActiveStage(stgGUID, callbackFunction);

stgGUID: ID of the stage to be set.

callbackFunction: If in case any actions are to be performed after setting the Active Stage.


Dynamics CRM 2015 Update 1 – Features that will change the way you work with Dynamics CRM

$
0
0

The next release of CRM 2015 had been announced a few months back. It is going to be an “Online only” release which means only users of CRM Online will receive this update. Feature updates to CRM made in this release update will not be made available to On-Premise until the end of the year.

This release is going to take the version from the current 7.0 to 7.1.x. This would be called the “CRM Online 2015 Update 1”

  1. The first big change that you notice and the most talked about change is the Navigation Change.

nav_bar

These would now be “Pull-down” menus instead of the earlier “scroll” menus.

  1. Color your tiles: These look nice and colorful. Yes colors, you now choose the color of the tiles for your entities. The color can be specified for the entity through the entity customizations area

entity_color

  1. Style your CRM: So you have been either doing this or wanting to do this for a while now. Some did get this done in earlier versions in unsupported way. Well, now you have a supported way to do this.

 In this version product team has provided you with greater control over how you would like your CRM to “look”

 You can create “Themes”. In the theme you can set the logo, Navbar color, how your hyperlinks should color

theme

 

The logo would appear at the top left of the nav bar as shown below.

 

logo

  1. Pin Recent Records/Views: In the previous version we had the ability to quickly views the recently accessed records, but it did not provide a way to pin certain records or views or permanent quick access.

 

The little timer control at the top is the “Recently Accessed Records”.

 

mru

This not only lists the views but also the records. Most importantly it lists the views/records/ dashboards from across the application. This means you could be in Sales and from this button quick access Cases without having to switch to Service area.

 

  1. Edit-Ready forms: In the previous version when you opened any form, it was not immediately available to you for editing or performing any operation on the form. You had to wait until the form completely loads and you get control of the form to type in.

 Now, the moment the form loads, it is available to you to type in. So you no longer have to wait for it to load completely. This is a huge performance improvement and it is definitely going to save a lot of time for a lot of people.

 

Well, there are many more improvements to look forward in the next release. There will be many more blogs to follow in the coming days targeted towards sharing the new improvements in the new release.

Listener services "not running"

$
0
0

Hi,

I have gone through various blogs and setup the connector for CRM/MDM but the listener and publisher services show up as "not running" in the integration options in MDM.

1. CRM and MDM are both in same online tenant.

2. Azure is not on the same tenant. it is a different subscription all together.

here is a screenshot of how it looks

Any pointers to resolve this?

Thanks!

Sam

Add Custom View in Lookup and Disable all the other Views

$
0
0

We had a requirement in which we were supposed to use a addCustomView method in order to filter the Lookup. We came across a situation in which we were able to set the custom view(The view with filtered records) as a default view to restrict the users from choosing the other records, but if a user wants, he/she could choose the different view from the Lookup window dialog and select the other records too, which the users were not supposed to choose.

Let us first introduce you to the situation, on the Quote form we have a Lookup field called Project Template. And, on the Quote Product form we have another Lookup field called Project Template Itemswhich should be filtered as per the value chosen for the Project Template.

We`ll start from the point where we have the ID of the chosen Project Template(to get the ID, you need to write a script on the Load of the Quote Product form).

Initially, what we started off was adding our custom view, but it still allowed the user to switch between different views, as a result users were able to choose records from other views.

We`ll explain you both the situations, the Initial Situation(which was adding the custom view) and theIdeal Situation(which was adding the custom view and restricting users from selecting other view) .

First and foremost, for the Lookup that`ll have filtered values, you need to change the below properties. These are the necessary steps.

Step 1:

From the form editor, go to the properties of the Lookup field.

Step 2:

1

In the Additional Properties section,

Default View – Select a View.

View Selector – Choose “Show Selected Views“.

System Views  – Only Select the view which was selected in the Default view.

Initial Situation:

In this situation, we were doing everything right but, we missed something that stopped us from getting the ideal output.

Code

function setCustomView(projectTemplateId) {

var functionName = “setCustomView”;

try {

//Check whether the field exist or not

        if (isValid(Xrm.Page.getControl(“new_projecttemplateitemid”))) {

// add the randomly generated GUID for the view id
var viewId = ‘{00000000-0000-0000-0000-000000000001}’;

 

//add the entity name

var entityName = “new_projecttemplateitem”;

 

// add the view display name

var viewDisplayName = “Project Template Item Lookup View”;

 

// fetch xml for filtered Project Template Item records

var fetchXml = “<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’false’> ” +

“<entity name=’new_projecttemplateitem’>” +

“<attribute name=’new_name’ />” +

“<filter type=’and’>” +

“<condition attribute=’new_projecttemplate’ operator=’eq’ value=’” + projectTemplateId + “‘ />” +

“</filter>” +

“</entity>” +

“</fetch>”;

 

//Grid Layout for filtered records

var layoutXml = “<grid name=’resultset’ object=’1′ jump=’productid’ select=’1′ icon=’1′ preview=’1′>” +

“<row name=’result’ id=’new_projecttemplateitemid’>” +

“<cell name=’new_name’ width=’150′ />” +

“<cell name=’createdon’ width=’150′ />” +

“</row>” +

“</grid>”;

 

// add the custom view for the lookup field

Xrm.Page.getControl(“new_projecttemplateitemid”).addCustomView(viewId, entityName, viewDisplayName, fetchXml, layoutXml, true);

}

} catch (e) {

throwError(e, functionName);

}

}

 

In the above code everything was right but,

on selecting the Custom view, we were getting the below results:

2

And, on selecting the System View, we were getting the below results.

3

Both the views were giving us different results, which we never wanted.

 

So, in the search of an ideal situation we found the below solution.

Ideal Situation:

Replace the code in the box with the below code.

Code

// add the default View GUID for the view id

var viewId = Xrm.Page.getControl(“new_projecttemplateitemid”).getDefaultView();

This single piece of line works like a charm.

Now, we got the below results on selecting both the views.

Custom View:

4

System View:

5

 

And, this is how we got the desirable result with a small tweak in the code.

Plug-in Tracing in Dynamics CRM 2015 Update 1

$
0
0

Before CRM 2015 SP1 update, an alternative option to debug the Plug-in or Workflow for developers was to use tracing messages which would help to trace the program using custom messages.

The developer had to manually throw an exception in order to trace the program. And then when the plug in/workflow is triggered a dialog is shown up to “Download the Log File” which contains the tracing information.

So, the user/Developer had to read the tracing from the downloaded text file, which was a tedious job itself.

So now in CRM 2015 SP1 update, we have one more option in which we have a new Entity called “Plug-in Trace Logs” which itself creates record for any exception occurred or thrown by the developer.

But in order to enable this feature we need to make following settings in CRM.

  • Go to Settings >> Administration

1

 

  • Under Administration  >> System Settings  >> Customization Tab2

 

So as we can see here there is an option “Enable logging to plug-in trace log”. By default it is Off, we need to select the option “Exception” in order to enable the creation of Plug-in trace log records in case any exception or error occurs.

There’s one more option in addition to this as mentioned in SDK and i.e. “All”. In the preview Version we don’t have it.

It’s mentioned that if we select “All” then the tracing information would be written to Trace Log record upon complete code execution or any error occurred throw custom code.

Once we have enabled the feature, we can view the Trace Log Records under:

Settings >> Plug In Trace Log Entity:

3

 

Records are created in CRM as below, which are read only just like System Job records to find out the error occurred.

4

 

Now let’s see what this record actually contains and why it simplifies the earlier process of debugging Plug-In and workflow using ITracingService interface.

5

 

You can see the record contains attributes like Message Name, Primary Entity, Mode etc which helps to get the information related to Plug-in.

As shown below it contains two fields Message Block and Exception Details which contains the actual data the user / developer is keen to understand.

6

Message Block: Contains the actual Tracing information which we specify in our Plug-in /Custom Workflow to Trace method. For e.g. tracing.Trace(“Workflow Started”); where tracing is an object of ITracingService.

Exception Details: Contains the Exception related information which has occurred in Plug-in or Custom Workflow.

Also we can view the Trace Log records using Advanced Find. Hence we can even filter out records based on the Query specified in Advance find. Below is an example of that:

We are selecting only those records which are created due to Plug-ins.

 

In addition to this there’s one more advantage of the Trace Log Entity.

Before, this feature was introduced the developers used to create a Custom Entity for storing Log Records so that they could trace the tracing information or exception details. But it was quite tricky in case of Transactional Plug-ins, as we know if any part of code fails in it, the entire changes would roll back and the custom entity to store the tracing information is not  created. Although now, with this System Trace Log Entity the Trace records will be created even if  the code fails.

Let’s take an example to understand it. We have registered a Plug-in on “Qualify Lead” on the Post Operation.

Let’s qualify the lead and suppose an exception occurs during execution as below:

8

We can “Download Log File” and read the “Tracing Information” which we used to do earlier. Or now we have an option to read the related Trace Log Record which is created even if the changes are Roll Back in the system  i.e. Lead  is not qualified.

The related trace log record created in the system is as below:

9

Thus, as we can see now the trace log record is created anyhow in the system even if the operations in the Plug-in are roll backed.

Now, who can view these records by default in CRM?

By default, the System Administrators and System Customizers can view these records but the access to view can be granted to other Users too.

NOTE: Trace logs will only be written to the trace log entities if the plug-in developer uses tracing within the plug-in.

If we have enabled this feature in our System, then Trace Log records will henceforth be created in CRM for the entities having Plug-in or Custom Workflow (which implements ITracingService) registered on it. Due to this more number of Trace log records could be created in CRM, so in order to manage this we can create a Bulk Deletion Job which we can schedule such that the trace log records would be deleted over fixed interval of time.

More ways to search a record in Dynamics CRM 2015 Update 1 – Alternate Keys

$
0
0

Prior to Microsoft Dynamic CRM 2015 Update 1 the only way to identify the records in CRM was the record guid. Now with the introduction of the Alternate Keys in Dynamics CRM 2015 Update 1 we can use alternate keys to uniquely identify a record in CRM in place of the primary key (GUID).

Features of Alternate Key:

  1. Provides a way to uniquely identify record.
  2. Alternate keys can be Composite keys i.e. more than one attributes can be selected to define the alternate key, thus providing a way to define a key as a combination of multiple attributes.
  3. Every Alternate Key you define creates an index behind the scene.
  4. Once you define alternate keys for an entity it acts as inbuilt duplicate detection rule i.e. it will throw an error as can be seen below if you will try to create a record with the same alternate key value which is already set for some another record.

                       

 

Create Alternate Key

Let us go through the steps to create an Alternate Key.

Alternate Keys are associated with an Entity and therefore you need to create them as a part of entity customizations.

Navigate to Settings -> Customization -> Entity to find a new "Keys" tab added along with Forms and Fields tabs.


On the "Keys" tab click the “New" button to add new keys for the entity.

When you click on "New" you get a list of "Fields" for the entities from which you can choose which Fields to set as Alternate keys. Following screen shot shows a list of fields for Account entity from which we can select to create as Alternate Key.


       

Only attributes of following types can be added as Alternate Keys -

1. Decimal Number

2. Whole Number

3. Single Line of Text

Note : Per entity you can define a maximum of 5 Alternate Keys only.

Alternate Keys Implementation through Code

Prior to Alternate keys, if we had to say add a new opportunity for a customer. We would like to first search for the customer and get the Guid so that we could assign that as the customer when creating an order. This meant we had to send out an explicit search request prior to the actual operation of creating an order.

With Alternate Keys such explicit calls are no longer required. Platform improvements made with the implementation of Alternate Keys actually take care of this internally.

Let's check the code to see how we can use this

1. Lets Create a new Contact and set the parentcustomerid using keys.

  

    KeyAttributeCollection keys = new KeyAttributeCollection();
    keys.Add("accountnumber", "ASH0011");
    
    Entity contactEntity = new Entity("contact");
    contactEntity["lastname"] = "Test last name 1";
    contactEntity["parentcustomerid"] = new EntityReference("account", keys); 
    contactEntity["fax"] = "1234579856";

    CreateRequest req = new CreateRequest
       {
         Target = contactEntity
       };

   _service.Execute(req);

This will automatically search for the "Account" from the keys and set it as "parentcustomerid" on the Contact created.

If the keys that are specified in the code do not exist then it will throw an error "A record with the specified key values does not exist in account entity".

2. Upsert an Account using Alternate key

  

    KeyAttributeCollection acckeys = new KeyAttributeCollection();
    acckeys.Add("emailaddress1", "someone3@example.com");
    acckeys.Add("telephone1", "555-0152");

    Entity accountEntity = new Entity("account", acckeys);
    accountEntity["name"] = "Test Account name 1";
    accountEntity["fax"] = "12345745678";

    UpsertRequest upreq = new UpsertRequest();
    upreq.Target = accountEntity;

    UpsertResponse resp = (UpsertResponse)_service.Execute(upreq);

 

When we execute the above code if an Account already exists with the Alternate Keys specified in the code then it will update the Account which has the matching Alternate keys and “RecordCreated” attribute is returned as "False" in Upsert response else it will create a new Account record and set the specified Alternate keys as the corresponding attributes values on Account so that we need not provide them separately and the “RecordCreated “ is returned as "true". In above sample code you can find that we have created two Alternate keys (composite keys) i.e. “emailaddress1” and “telephone1” so if the values specified for both the keys i.e. “emailaddress1” and “telephone1” matches with another record than in this case it will find that matching record and update that existing record. If any one of the keys value matches than it will create a new record.

Create or Update through a single request – Upsert in Dynamics CRM 2015 Update 1

$
0
0

Background:

Traditionally, insert and update have always been separate requests in Dynamics CRM. The logic to identify whether a record has to be created or updated was always something the developers had to handle themselves. Once you have identified the action to perform, call the appropriate message.

The API enhancements introduced in Dynamics CRM 2015 Update 1 (Spring Update CRM Online), we have now been provided with a new Request/Message “UPSERT”. Note this message is currently not available for On-Premise systems and it requires the CRM Online organizations to be updated to 7.1.x.

 

How does UPSERT work?

For UPSERT to work, we need to use the concept of Alternate Keys that is also a new concept introduced in the Spring Update. To learn more about Alternate Keys you can check the article here. In the AlternateKeys, you provide the fields/value pairs to look for a matching record. You cannot use any combination of field/value pairs. The field/Value pair provided should be a part of an Altenate Key already defined for the entity.

Here is how we form the request

// Define the Alternate key which uniquely identifies the record
KeyAttributeCollection acckeys = new KeyAttributeCollection();
acckeys.Add(“accountnumber”, “ASH001″);

// Create Account entity object by specifying the entity logical name and Alternate key
Entity accountEntity = new Entity(“account”,acckeys);

// Specify fields to update
accountEntity["name"] = “Account 001″;
accountEntity["telephone1"] = “23457567”;

// Create Upsert request
UpsertRequest upreq = new UpsertRequest();
upreq.Target = accountEntity;

// Execute Upsert request
UpsertResponse response = (UpsertResponse)_service.Execute(upreq);

 

Working: 

You first define the Alternate Key that is to be used as the match criteria. Make sure you pass this Key as a part of instantiating the entity object.

Next you go ahead and create an entity object just like you did previously.

When this request is executed, the API looks for the match based on the Keys provided. If a match is found, it internally send an Update request. The Update Plugins/Workflows if any registered would be executed.

If no matching record is found, it will internally call the Create request. The Keys are passed on as a part of the entity fields and the record created would also have the values automatically set for these fields. So in the above example, we have not explicity added AccountNumber to the Entity attribute collection. But since it has been provided as part of Alternate Keys collection, the newly created record would have the accountnumber set to “ASH001″. In this case, the Create plugins/workflows would be executed.

 

How to identify if it actually Inserted or Updated the record?

The UpsertResponse, provides you with the details on this. In the “UpsertResponse” we get the “RecordCreated” attribute set to “True” if the Create request was called and a new record was created. It is set to “False” if the record existed and was updated. 

One thing which you can notice in the above Upsert operation is that there was no need to provide the Id field (GUID which uniquely identifies any record in Microsoft Dynamic CRM) i.e. “accountid” in case of Account entity for identifying the existing matching record and then perform Create or Update operation. We achieved this using the alternate key which uniquely identified if the record existed and thereby eliminated the overhead of retrieving existing record to check if the record already exists.

 

Conclusion: 

Upsert helps reduce the roundtrips required for explicit search requests that was otherwise used. This could be widely used in scenarios where data needs to be integrated with external systems and ID/GUID is very rarely the match criteria. Make sure to define the Keys before you use them as part of this request.

API enhancement – Service.Update Message in CRM 2015 Update 1

$
0
0

Introduction:

For updating an existing record in Dynamics CRM, we have always had the Service.Update message. Pass in the entity object and it would update the corresponding record back in CRM. This message however only allowed you to update regular fields. CRM has always had some special fields like Status, Owner etc that could not be updated using the Update Message. These had special messages in the SDK to update the values.

To update Owner, use Assign request

To update Status, use the SetState request.

In this current release, the API has been update, to remove the need to execute special messages for Status and Owner and instead we can now update these by setting these fields and passing them as a part of the Update request to CRM.

Code Sample differences:

Updating the rating, the Owner and the status of the contact record in Dynamics CRM

Entity contact = new Entity(); 

//specify the entity logical name
contact.LogicalName = “contact”; 

//specify the contact that need to be updated
contact.Id = new Guid(“A925C42D-CFE4-E411-80E8-C4346BAD5414″); 

//Set the fax field
contact["fax"] = “412”; 

//Call the update message
service.Update(contact);

//Call the Assign to change the owner
AssignRequest assign = new AssignRequest
{
Assignee = new EntityReference(SystemUser.EntityLogicalName, _otherUserId),

Target = new EntityReference(Contact.EntityLogicalName,new Guid(“A925C42D-CFE4-E411-80E8-C4346BAD5414″))
};

// Execute the Request
_service.Execute(assign);

//Now change the status

// Create the Request Object
SetStateRequest state = new SetStateRequest();

EntityReference contactReference = new EntityReference()
{
LogicalName = Contact.EntityLogicalName,

Id = new Guid(“A925C42D-CFE4-E411-80E8-C4346BAD5414″)
};

state.EntityMoniker = contactReference

// Set the Request Object’s Properties
state.State = new OptionSetValue(1);
state.Status = new OptionSetValue(2);
 

// Execute the Request
_service.Execute(state);


 As you can see, it took 3 separate request to get these 3 fields updated.

With the new API you can simply perform all of these three with a single Update request

Entity contact = new Entity(); 

//specify the entity logical name
contact.LogicalName = “contact”; 

//specify the contact that need to be updated
contact.Id = new Guid(“A925C42D-CFE4-E411-80E8-C4346BAD5414″); 

//set the fax
contact["fax"] = “412”;

//change the owner of the contact
contact["ownerid"] = new EntityReference(“systemuser”, new Guid(“FB8C44BF-B0E9-E411-80ED-C4346BAD5414″)); 

//change the status to inactive
contact["statecode"] = new OptionSetValue(1); 

//change the state to inactive
contact["statuscode"] = new OptionSetValue(2); 

//update the contact
service.Update(contact);

 Impact on Plugins:

The SetState and Assign request have always called the Update request also internally. This means, if you had a plugin registered on SetState, Assign and Update message of the “Contact” entity. The sample code above would have executed the SetState and Update, both plugins for just the SetState request. Similarly the Assign would have executed the plugin registered on both Assign as well as Update message. So it was the Update message that was executed irrespective of which fields or through which message a record was updated in Dynamics CRM.

The new API Update message, will not internally execute the SetState or Assign request if you were to execute the above sample code to update the owner and the status. So any previous plugins registered on the SetState or the Assign request will be have to re-registered on the Update message, should you plan to start using the new Update message to update the owner and status as well.

When you change the owner or the state of the record from CRM UI, it still executes the SetState and Assign Plugin, so it does not impact there.

The new API would not execute the SetState or Assign message plugin, BUT, it will call an UPDATE request for each of these messages i.e the sample code above would result in the UPDATE plugin being executed thrice.

1st time for the updating Fax – This time, the target would receive only the Fax field (non-owner/non-status fields)

2nd time for the Owner – only the owner field is available in Target

3rd time for the state – This time only status fields are passed in Target.

The following sample plugin code, would help you understand how to identify for which field the plugin has been triggered.

// Check if plugin context contains the Target

if (context.InputParameters.ContainsKey(“Target”))
{
Entity targetContactEntity = (Entity)context.InputParameters["Target"];

string desc = “”;

if (targetContactEntity!= null)
{
// When fired for owner change
if (targetContactEntity.Attributes.ContainsKey(“ownerid”))
{
desc = “Owner changed”;
}

// When fired for status change

if (targetContactEntity.Attributes.ContainsKey(“statecode”))
{
desc = “Status Changed”;
}

// Create Task activity

Entity task = new Entity(“task”);

task["subject"] = “Task from plugin : ” + desc;

_service.Create(task);
}
} 
  1. Once for all non -owner / non-status fields i.e. “fax” – When fired for rest of these fields the target entity attribute does not contain “owner” and “statecode”. Hence the “Task” which we are creating in this plug-in is created with subject – “Task from plug-in”.
  2. Once for owner field change – When fired for owner field the target entity attribute will only contain updated owner field. So here we can find out if the plug-in has been fired for owner field change by checking for if the target entity attribute contains “ownerid” as done in above plug-in code. Hence the “Task” is created with subject – “Task from plug-in : Owner changed”.
  3. Once for Status field change – When fired for status field the target entity attribute will only contain updated status field. So here we can find out if the plug-in has been fired for status field change by checking for if the target entity attribute contains “statecode” as done in above plug-in code. Hence the “Task” is created with subject – “Task from plug-in : Status changed”.

Impact on Workflow:

  1. Workflows registered for the Assign message by users continue to be triggered by updates to owner fields.
  2. Workflows containing the Change Status step continue to be triggered by updates to state/status fields.

For example:

If you have a workflow registered to execute when a record is assigned, updating the owner of the record using “Update” operation would trigger the workflow. Similarly for the Status changes workflow, they are triggered as well when the state is changed using the new Update message.

Conclusion:

  • Allows to change the Status, State, Owner, Business Unit and Manager using a single Update operation.
  • Reduces the code complexity.
  • Additional service calls to the CRM are reduced thereby improving performance.

Change Tracking Feature of CRM 2015 Update 1

$
0
0

As we have integration of Dynamics CRM with other external systems, in that case we need to keep track of changes that were done after last synchronization of data and we integrate only those changes in external system. To achieve this previously we have to add some kind of custom logic like add one date field in CRM to track the date of last synchronization. Now, in CRM 2015 update 1 we have a built-in functionality to achieve this, it is called Change Tracking.

Change Tracking is used in Dynamics CRM to keep the data synchronized in a better way by detecting what data has changed since the data was last synchronized.

To retrieve the changes for an entity, we need to enable the Change Tracking functionality for that entity.

This feature can be enabled by using the customization user interface (UI) or programmatically by setting the ChangeTrackingEnabled property to True.

ChangeTracking2

To achieve this functionality CRM 2015 has introduced a new request called RetrieveEntityChangesRequest.

When you execute this request for the very first time, then it returns all records for the entity and that data can be used to sync into any other external system. The message also returns a DataToken that we can use in the RetrieveEntityChangesRequest, so that when we execute this request the next time then it returns data for those changes that occurred since last execution of request.

If you want to get the changed data(created/updated/deleted) of Account since last execution of request then RetrieveEntityChangesRequest message is used to retrieve the data. After executing this request it will return the BusinessEntityChangesCollection of new/updated and deleted records. The new/updated records can be retrieved from NewOrUpdatedItem and deleted item can be retrieved from RemovedOrDeletedItem.

Following is the sample code for RetrieveEntityChangesRequest:

RetrieveEntityChangesRequest changeTracking = new RetrieveEntityChangesRequest();
//set the properties
changeTracking.Columns = new ColumnSet(true);
changeTracking.DataVersion = “471550!05/15/2015 10:08:13″;
changeTracking.EntityName = “account”;

changeTracking.PageInfo = new PagingInfo() { Count = 5000, PageNumber = 1, ReturnTotalRecordCount = false };

RetrieveEntityChangesResponse res = (RetrieveEntityChangesResponse)_service.Execute(changeTracking);

//get the token
string dataToken = res.EntityChanges.DataToken;
BusinessEntityChangesCollection busEntChanColl = res.EntityChanges.Changes;

for (int bizColl = 0; busEntChanColl.Count > bizColl; bizColl++)
{
if (busEntChanColl[bizColl].Type.ToString().ToLowerInvariant() == “neworupdated”)
{
NewOrUpdatedItem createUp =(NewOrUpdatedItem) busEntChanColl[bizColl];
Entity changedEnt = createUp.NewOrUpdatedEntity;
}
else if(busEntChanColl[bizColl].Type.ToString().ToLowerInvariant() == “removeordeleted”)
{
RemovedOrDeletedItem removeOrDelItem = (RemovedOrDeletedItem)busEntChanColl[bizColl];
EntityReference delItem = removeOrDelItem.RemovedItem;
}
}

If you doesn`t provide the DataVersion parameter for request then it will always return all records.

So to retrieve only records those are Created/Updated/Delete after the last execution of request then you have to provide the DataVersion parameter, DataToken returned by previous execution of request is what we have to pass as DataVersion new value for the next request.

For example: res.EntityChanges.DataToken in the above code snippet gives us the DataToken.

If you have the windows service/ windows application for integration of CRM with another system then you can store the DataToken value in app.config. If you used it in a plug-in or a workflow then it is better to create custom entity to store value of DataToken.

How Security Role Privileges are Inherited from Team to User

$
0
0

If there are multiple users, business units and teams in any CRM system, then it is not necessary that every user has maximum rights/ privileges/ roles. There are situations where a minimum permission user should be able to create an entity record for which individually it does not have rights but his/ her team has those rights. So we think that should work and user should be able to create records, say suppose quote record. But there is more to this concept which we have explained in this blog.

Considerations and terminologies used in following use case as an example:

Team A – Team who has permission and its team member user does not have any permission

User A – One of the team member that comes under Team A which does not have any permission

Parent BU – this is a parent business unit and Team A comes under this BU

Child BU – this is child of BU Parent and User A comes under this BU

User role – no level of read, write or create privilege on quote entity and this security role is assigned to User A

Team role – USER level read, write and create privilege on quote entity and this security role is assigned to Team A

In this case, the quote record cannot be created with User A as the owner but he/ she can create quote record after setting Team A as the owner, here created by/ modified by will be User A.

If Team role is given parent child BU level permissions, then too it does not work because User A falls in Parent BU whereas Team A falls in its Child BU.

So when organization level read, write and create privilege on quote are given to team role, then user A can create and be owner of quote record. Note here User A still does *not* have any level of privilege on quote individually as per his/ her personal security role.

Now consider the setup where only change is been made is that Business Unit of Team A is changed to Parent BU from Child BU, so both Team A and User A are under same Business Unit i.e. Parent BU.

In this case, if you provide Business Unit level read, write and create privilege of quote to Team role, then also User A can create quote record under his/ her own login and owner of that quote record will also be User A.

Hence, if you have a role which provides create, read write privilege at a User level for some entity, and this role is only assigned to a Team, then any user in that Team can create records of this type, but only if they make the Team as the Owner before they save the record. If they try to save the record with themselves as the owner, the security model does not allow this.

Here one might think, I am the member of this Team, so team’s role should apply to me and make me act like a team and allow me to create these records, but the role has user level permission by which it means it is applicable only for “you”, i.e. the Team itself, not you, yourself.”

Conclusion:

While setting up roles for team with the perspective of getting them inherited to its team members, the level of privilege on respective action is important depending on situation if that should be allowed to same business unit team members or any of the team members, but in any case user level privileges will not work in team’s role.

Clone Records in Dynamics CRM

$
0
0

Very often we are troubled with writing huge codes and end up spending our valuable time for cloning an entity record. Just imagine the time needed if you are asked to create a cloned record with more than 100 attributes. Don’t you think it’s a time consuming effort and how great would it be if all this could happen just with a two line code?

The below sample code will guide you on how to create a cloned record using Clone() function of Microsoft.Xrm.Client Namespace:

//Add references

using Microsoft.Crm.Sdk;

using Microsoft.Xrm.Sdk;

using OrganizationXrm;

using Microsoft.Crm.Sdk.Messages;

using Microsoft.Crm.Sdk.Samples;

using Microsoft.Xrm.Sdk.Client;

//Used for Clone Function

using Microsoft.Xrm.Client;

 

//Declare Variables

Entity parentaccount= new Entity(“account”);

Guid parentaccountid = Guid.Empty;

Entity childaccount;

Guid childaccountid = Guid.Empty;

 

try

{

//Set attributes of the Parent Account

parentaccount.Attributes["name"] = “Parent Account”;

parentaccount.Attributes["telephone1"] = “Telephone1″;

//create the parent account

parentaccountid = _service.Create(parentaccount);

//Clone the Account Record using Clone function;

//Clone function takes a bool parameter which relates the Related Entities of the parent

//record to the cloned records, if set to true.

//The bool parameter passed to Clone method is set to true by default.

childaccount = parentaccount.Clone(true);

//Remove all the attributes of type primaryid as all the cloned records will have their own primaryid

childaccount.Attributes.Remove(childaccount.LogicalName + “id”);

//Remove the telephone1 attribute from the cloned record to differentiate between the parent and cloned record

childaccount.Attributes.Remove(“telephone1″);

//create the cloned record

childaccountid = _service.Create(childaccount);

}

catch (SaveChangesException ex)

{

throw ex;

}

Notes:

  • It is limited to an On-Premise environment only.
  • Add reference Microsoft.Xrm.Client.
  • Works with commonly used OOB and Custom entities.
  • Removing primaryid attribute from the attribute collection of cloned instance is mandatory.

Cloning a record using JavaScript

Cloning a record is not only limited to a Custom Workflow or a Plug-in, the same functionality can be very well achieved in JavaScript with the below workaround.

Below example explains how to Clone Line Items of one Quote onto another Quote.

1. Retrieve Data:

The first step to start off with is to retrieve the records that we need to clone.  In this example since we are Cloning the Line Items, so we`ll retrieve all the Line Items of the Quote from which we need to Clone. All the retrieved Line Items will be stored in a variable called “retrievedProducts”. This variable will be later on used to “Create” the Line Items for other Quote.

//Retrieve the Values

XrmServiceToolkit.Rest.RetrieveMultiple

(“QuoteDetailSet”,

//Here you can have some selected fields or all the fields

options,

function successCallback(results) {

//get the retrieved result in a variable

retrievedProducts = results;

},

function errorCallback(error) {

showError(error, functionName);

},

function onComplete() { },

false);

2. Delete Extra Data

The next step is to remove the extra data. The extra data involves all the ID attributes that can create Duplicity issue. And, the “__metadata” attribute, this attribute contains extra data that is not needed for us to Clone the record and hence we`ll remove it. To remove the unwanted data from the “retrievedProducts” Object, we`ll use thedeletefunctionality.

In this example the “deleteExtraData” function is called at the time of creating the records.

//This function deletes the extra data from the Object

function deleteExtraData(retrievedObject) {

//Remove all the extra fields

delete retrievedObject.__metadata;

delete retrievedObject.QuoteId;

delete retrievedObject.QuoteDetailId;

 

//Check for all the __metadata fields and remove it

delete retrievedObject.__metadata;

//Loop through Objects in Object

for (var key in retrievedObject) {

if (retrievedObject.hasOwnProperty(key)) {

//Check if the value is null if it is null then do nothing

if (retrievedObject[key] == null) {

 

} else {

//Check the type is the type is object, remove metadata

if (typeof (retrievedObject[key]) == “object”) {

//deletes the metadata from the object

delete retrievedObject[key].__metadata;

}

}

}

}

}

3. Create the Record

The final step is the creation of the record. In this step we`ll loop through the retrieved values which in this example is stored in the variable “retrievedProducts ” and create the Line Items one by one for the desired Quote. Since we have removed the Quote Id we need to associate a Quote Id.And, in this step we`ll pass the “retrievedProducts ” Object to the     “deleteExtraData” function. And, the formatted value would be passed for the creation.

for (var j in retrievedProducts) {

//Get the formatted value. formattedQuoteProduct is an Object.

formattedQuoteProduct[j] = deleteExtraData(retrievedProducts[j]);

//Since we removed the Quote Id we need to associate it with another Quote Id as the Quote Line Items needs to have a Quote Id.newQuoteId can be the Quote Id of the Quote on which you need to clone the Line Items

formattedQuoteProduct[j].QuoteId = {

Id: newQuoteId,

LogicalName: “quote”

};

//Create the products

XrmServiceToolkit.Rest.Create(formattedQuoteProduct[j],

“QuoteDetailSet”,

function successCallback() { },

function errorCallback(error) {

showError(error, functionName);

}, false);

 

}

Creating a cloned record using fetch expression:

We have introduced how to clone record using  .Clone()function in On-Premise environment and also by using  JavaScript and now  we would like to introduce another method of cloning records which can be used on any environment and allows us to define the attributes to be replicated on the cloned record.

Working:

At first, we will fetch the entity for which we have to clone the records and in the attributes we will add only those attributes which we need to copy to the cloned records.

Here, we have taken the “Account” entity of the CRM to create cloned records:

string sourceqry = @”<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’false’>” +

“<entity name=’account’>”+

“<attribute name=’name’/>”+

“<attribute name=’primarycontactid’/>”+

“<attribute name=’telephone1′/>”+

“<attribute name=’accountid’/>”+

“<order attribute=’name’ descending=’false’/>”+

“<filter type=’and’>”+

“<condition attribute=’name’ operator=’eq’ value=’XXXXXX’/>” +

“</filter>”+

“</entity>”+

“</fetch>”;

 

//Declare an entity collection to hold the entities retreived by the query

EntityCollection AccountsCollection = service.RetrieveMultiple(newFetchExpression(sourceqry));

 

if (AccountsCollection.Entities.Count > 0)

{

for (int count = 0; count < AccountsCollection.Entities.Count; count++)

{

Entity account = AccountsCollection.Entities[count];

 

//Set the EntityState to null, so that a new cloned record can be created

account.EntityState = null;

 

//remove the PrimaryId of the record

account.Attributes.Remove(account.LogicalName + “id”);

 

//Set any field of the cloned record to differentiate between the parent and the cloned record

account.Attributes["name"] = “Cloned record created at: ” + System.DateTime.Now;

 

//set a unique id to the cloned record

account.Id = Guid.NewGuid();

 

//Create the new cloned record

service.Create(account);

}

}

}

catch (SaveChangesException ex)

{

throw ex;

}

catch (FaultException<OrganizationServiceFault> ex)

{

throw ex;

}

catch (Exception ex)

{

throw ex;

}

In the above code snippet we have cloned the “Account” entity records where name of the “Account” is “XXXXX” and we have just added only few attributes in the fetch expression which we need to get copied to the new cloned records.

Note:

  • Set the “EntityState” to”null”.
  • Remove the “PrimaryId” of the record.
  • Any (OOB + Custom) entity can be cloned.

How to apply script on Header fields and BPF fields

$
0
0

CRM 2013 now allows java script coding on header fields as well as BPF fields. Sometimes we come across with the requirement where we need to write script on the fields existing in the business process flow and not on the form. Same with the fields on header of any form.

When a form displays a business process flow control in the header, additional controls gets added for each attribute that is been displayed in that BPF. These controls hold unique names like:header_process_<attribute name>. So using this control we can write script around these fields.

The Controls displayed in the form header are also accessible and hold unique name like:header_<attribute name>.

We can perform all those operations that we can perform on the attribute added on the form. Below we have mentioned some of the sample scripts around such fields.

  • Show/Hide field

We can also show/hide the fields from the BPF same like we do for the other form attributes. Below we have hidden the field based on the value selected on the form.

img1

We can simply use below line of code to show/hide the field from the BPF.

Xrm.Page.getControl(“header_process_parentcontactid”).setVisible(false);

You can use below line of code to show/hide the field from the Header.

Xrm.Page.getControl(“header_leadsourcecode”).setVisible(false);

  • Make the field Read only

The field will be read only after applying script on the BPF controls.

img2

Below is a line of code where we have disabled the control from the BPF.

//Check if the control exist on the form

if (Xrm.Page.getControl(“header_process_parentcontactid”) != null) {

//Set the control disabled

Xrm.Page.getControl(“header_process_parentcontactid”).setDisabled(true);

}

The same script you can use to apply to the header fields.

//Check if the control exist on the form

if (Xrm.Page.getControl(“header_leadsourcecode”) != null) {

//Set the control disabled

Xrm.Page.getControl(“header_leadsourcecode”).setDisabled(true);

}

  • Filter Lookup

We can also filter on lookup based on another. Below we have written script to filter Existing Contact lookup based on Existing Account selected.

We have selected Fourth Coffee Account in the Existing Account as you can see in below screen shot:

img3

 

And the Existing Contact lookup will now show only Account associated Contacts in this lookup, where as previously it was showing All the contacts exist in CRM regardless of any condition.

 

 

img4

 

Below is a code snippet which will help you to filter a lookup:

function filterLookup() {

//Check if the control exist on the form

if (Xrm.Page.getControl(“header_process_parentcontactid”) != null) {

// add the event handler for PreSearch Event

Xrm.Page.getControl(“header_process_parentcontactid”).addPreSearch(addFilter);

}

}

function addFilter() {

var accountId = null;

var accountLookup;

var fetchQuery;

try {

//Check if control exist on form

if (Xrm.Page.getControl(“header_process_parentaccountid”) != null && Xrm.Page.getControl(“header_process_parentaccountid”).getAttribute().getValue() != null) {

//Get Account lookup value

accountLookup = Xrm.Page.getControl(“header_process_parentaccountid”).getAttribute().getValue();

//Get the account id

accountId = accountLookup[0].id;

}

//Build fetch

if (accountId != null || accountId != undefined) {

fetchQuery = “<filter type=’and’>” +

“<condition attribute=’statecode’ operator=’eq’ value=’0′ />” +

“<condition attribute=’parentcustomerid’ operator=’eq’ value=’” + accountId + “‘ />” +

“</filter>”;

//add custom filter

Xrm.Page.getControl(“header_process_parentcontactid”).addCustomFilter(fetchQuery);

}

} catch (e) {

Xrm.Utility.alertDialog(“addFilter Error: ” + (e.description || e.message));

}

}

  • Get/Set Value

We need to use below method to get/set the value on the BPF fields.

Xrm.Page.getControl(“header_process_descriptions”).getAttribute().getValue();

Xrm.Page.getControl(“header_process_description”).getAttribute().setValue(“Hello World”);

If you want to write a script on the header fields then you can use below line of code for this.

Xrm.Page.getControl(“header_leadsourcecode”).getAttribute().setValue(parseInt(value));

Key Points:

  • We can only perform actions like set/get value, show/hide fields, add custom filter etc. but we cannot add event handlers on the BPF fields. For this we need to add the same field on the form and then add event handler on that field.
  • As we cannot apply OOB filter lookups on the BPF fields, achieving through scripts is an advantage.

Thank You for Visiting Our Booth

$
0
0

Microsoft Convergence 2015 was a huge success once again for Inogic. Thank You, from Inogic Team for visiting our booth (No. 1751) and talking to us about our solutions and services. And a special word of Thanks for the Microsoft Team for the amazing arrangements and a great partnership.

Now that you’ve gotten to know us, it’s time to take our business forward.

Main Highlight at Convergence was our solution Maplytics™.  We use MAPS day in and out for our personal use so why not use maps for our business. Our Mapalytics enables you to take advantage of Dynamics CRM by gathering new insights through visual representation of data that can help you gain a thorough understanding of your market and in pursuing new market opportunities. It gives you insights to monitor your business from every aspect, be it the sales people or your prospects. As displayed during the convergence Mapalytics is not just for your Sales, since it supports custom entities you can use it for all purposes say to find Feedback from customers in a specific area or inquiries within a distance . Its easily customizable by us so you can manage your business in a much more effective way.

Apart from this we also showcased our QuickBooks to Dynamics CRM integrator solution – InoLink, that allows you to have a 360 degree view of your customer, contact details, order and payment history by simultaneously updating the information in both the applications. This helps in eliminating the need for double entry by auto transferring the data into other system thereby managing your data efficiently and reducing duplicity.

Every year we have something special at Convergence and this year we had 2 new add-ons introduced this time at Convergence. Click2Export is an add-on that helps you to export any Dynamics CRM report in the system with single click and attach it to an email or note or just download it without much efforts. It is an easy to install/ configure with user friendly user interface. In one click UI, you can export any report and decide the actions that should take place after exporting it. The next one was User Adoption Tracker which enables managers to monitor the actions of team members with respect to using Dynamics CRM for their day to day operation and not with the intent of monitoring what exact data was entered by the user.

Convergence also gave us an opportunity to educate our audience and showcase our services in order to prepare organizations for the road ahead. We had various interactive sessions, presentations, discussion, etc. that revolved around our offerings such as CRM customizations, integrations, implementations, portal development, troubleshooting and much more.

So do not hesitate to contact us with questions, to request a bid, or to talk with us about anything else we can do to help you regarding Dynamics CRM implementation.

Attending Convergence 2015 has been a great experience and we hope you have enjoyed the event as much as we have. We are looking forward to take this journey ahead and await to meet next year.

How to pass Object or collection of Objects to a web resource in CRM 2011

$
0
0

In Dynamics CRM if we want to pass data to a web resource, then we use Xrm.Utility.openWebResource function and pass as second parameter. But in this case we can’t pass Object or Array as parameter.

So to solve this problem one can use JSON. Using JSON we can convert Object or Collection of Object (Array) in string format. This converted data can be used to pass data to web resource. Let us take an example of passing collection of object to web resource. Here I have used collection of array that contains Id, Description and Name of Record. 

               var arrayObject = new Array();

              arrayObject[0] = new Object();

              arrayObject[0].name = "Sample Record 1";

              arrayObject[0].id = “1.";

              arrayObject[0].decription = "Test Description ";

Then after defining collection of object we will pass that object to JSON.stringify. This function will return collection in JSON String format. Then this JSON string is encoded to get parameter that is going to passed to the web resource.

                       var customParameters = encodeURIComponent(JSON.stringify(arrayObject));

This encoded JSON string can be passed to web resource using below code: 

                       Xrm.Utility.openWebResource("new_/Records.html", customParameters);

Here “Records.html” is used for displaying this collection of object on HTML page. In web resource “Records.html”, passed parameter can be collected as follows:

                         var queryParam = Xrm.Page.context.getQueryStringParameters();

The collected parameter is in JSON string format so we have to convert array using JSON.parse to convert it into collection of object.

                         if (queryParam.Data) {

                                   arrayObject = JSON.parse(queryParam.Data);

                          }

In this way we can pass collection of object as parameter to web resource. Similarly we can do for object as well. 

We have used above code for passing some data to HTML web resource Records.html. This web resource takes collection of objects. And then show in table format as you can see in below screen shot.

But when we pass parameter to web resource it is passed as query string. Query string has limitation based on browsers used. So when we pass parameter to web resource it trims/neglects extra characters from the query string. So it will cause problem while converting JSON string object into Object. And this problem of query string limitation can be solved using window.opener. 

The opener property returns a reference to the window that created the window. When opening a window with window.open(), you can use this property from the destination window to return details of the source (parent) window. By using window.opener we can access global parameters defined in parent web resource. In this way we can access Objects or collection without any limitations.

Let us take same example of passing collection of object to web resource. We will take collection of object defined as follows. 

               var _arrayObject = new Array();

              arrayObject[0] = new Object();

              arrayObject[0].name = "Record 1";

              arrayObject[0].id = "1.";

              arrayObject[0].decription = "Test Description";

              arrayObject[0] = new Object();

              arrayObject[0].name = "Record 2";

              arrayObject[0].id = "2.";

              arrayObject[0].decription = "Test Description";

Then we open web resource i.e. Records.html using Xrm.Utility.openWebResource.

               Xrm.Utility.openWebResource("new_/Records.html");

In web resource, passed data can be collected as follows:

                if (window.opener._arrayObject != null) {

                     var  _arrayObject = window.opener._arrayObject

            }

By using above we can pass large amount of data to HTML web resource.

I have used the windows.opener to collect the data from _arrayObject global object. This collection is converted into HTML table format. I have used this code for displaying selected records into grid.

-------------------------------------------------

Posted by: Inogic

For more information/discussions (documents, sample code snippets, detailed work flow or diagrams)

Please be free to visit the following links or email us:

Web: http://www.inogic.com

Blog: http://inogic.blogspot.com

Email: news@inogic.com

--------------------------------------------------

 

Scripting on Business Process Flow in CRM 2015- Part I

$
0
0

With the introduction of MS CRM 2015, came many new features. The one we are going to discuss here is advancement of Business Process Flow and especially the Scripting part, which came as boon.

Before CRM 2015, the Business Process Flow exist, but no API availability made it impossible for the developers to interact with the BPF via programming.

CRM 2015 gave it a new look, while creating a Business Process Flow, we have the option of creating Branched stages, which in simple terms is we can conditionally decide which stage to be shown after the current stage. The branching decisions can be made on the basis of the value of any step used for the current stage.

Previously we were not allowed to visit an entity more than once before, now we can visit an entity more than once.

Above are the few highlights of features added for BPF, let`s delve into the Programmability enhancement.

The most important part is, How to hook to the Stage change or Stage Select events of BPF? This is indeed the simplest part.

Example:

If you want to hook to the Stage change event, you can do this by writing the below code. And, you have the freedom to place this code either onLoad or onSave of the form or else on change of any field.

//Register a function on change of the stage

Xrm.Page.data.process.addOnStageChange(stageChange);

 

stageChange – It is the function to be called when the stage is changed.

 

Similarly, you can hook onto the Stage select event.

//Register a function on select of a stage

Xrm.Page.data.process.addOnStageSelected(stageSelected);

 

stageSelected – It is the function to be called when the stage is selected.

 

We have the options to unhook the events as well.

//Unbind a function on change of the stage

Xrm.Page.data.process.removeOnStageChange(stageChange);

//Unbind a function on select of a stage

Xrm.Page.data.process.removeOnStageSelected(stageSelected);

By hooking onto either stage change or stage select events, we have plethora of options to use.

Like we mentioned above, that we can Branch the entities conditionally. We can leverage this functionality by showing different fields for an entity depending on the value selected for the step on the previous stage.

Let us explain you few of the available methods in this part of the Blog, and the rest in this.

  • Collapse or Expand the Business Process Flow:

We have the option to collapse the Business Process Flow by default, on load of the form.

Snippet:

Xrm.Page.ui.process.setDisplayState(string)

string: “expanded”  or “collapsed” are the two variables that can be passed.

 

  • Show/Hide the Business Process Flow:

We can show or hide the business flow. It can be useful when we don`t need few of the Security Roles to see the BPF.

Snippet:

Xrm.Page.ui.process.setVisible(bool)

bool: “true” to show and “false” to hide.

 

  • Active Process:

We can get the Current Active Process and we can set the Current Active Process. This is useful if you want different security profiles to see different processes.

  • getActiveProcess

This will give you the object of the current Active Process. Object will have,

Process Name, Process Id(GUID), Render State(Visible/Hidden) & Collection of Stage Objects.

Snippet:

var procObj = Xrm.Page.data.process.getActiveProcess();

  • setActiveProcess

This will allow you to set the Active Process.

Snippet:

Xrm.Page.data.process.setActiveProcess(procGUID, callbackFunction);

procGUID: Id of the process to be set.

callbackFunction: Function that will be called in order we have any necessary actions to be performed on setting the process.

 

  • Active Stage:

We can get the Current Active Stage and we can also set the Current Active Stage. This is useful if you want a user to move back to previous stage, when he selects “x” value for a step on current Active stage.

  • getActiveStage

This  will give you the object of current Active Stage. Object will have, Stage Name, Stage ID(GUID), Base Entity, Stage Status(active/inactive) & Collection of Step objects.

            Snippet:

var actStg = Xrm.Page.data.process.getActiveStage();

  • setActiveStage

This will allow you to set an Active Stage.

Note: Only completed stage for the current entity can be set using this method.

            Snippet:

Xrm.Page.data.process.setActiveStage(stgGUID, callbackFunction);

stgGUID: ID of the stage to be set.

callbackFunction: If in case any actions are to be performed after setting the Active Stage.


Dynamics CRM 2015 Update 1 – Features that will change the way you work with Dynamics CRM

$
0
0

The next release of CRM 2015 had been announced a few months back. It is going to be an “Online only” release which means only users of CRM Online will receive this update. Feature updates to CRM made in this release update will not be made available to On-Premise until the end of the year.

This release is going to take the version from the current 7.0 to 7.1.x. This would be called the “CRM Online 2015 Update 1”

  1. The first big change that you notice and the most talked about change is the Navigation Change.

nav_bar

These would now be “Pull-down” menus instead of the earlier “scroll” menus.

  1. Color your tiles: These look nice and colorful. Yes colors, you now choose the color of the tiles for your entities. The color can be specified for the entity through the entity customizations area

entity_color

  1. Style your CRM: So you have been either doing this or wanting to do this for a while now. Some did get this done in earlier versions in unsupported way. Well, now you have a supported way to do this.

 In this version product team has provided you with greater control over how you would like your CRM to “look”

 You can create “Themes”. In the theme you can set the logo, Navbar color, how your hyperlinks should color

theme

 

The logo would appear at the top left of the nav bar as shown below.

 

logo

  1. Pin Recent Records/Views: In the previous version we had the ability to quickly views the recently accessed records, but it did not provide a way to pin certain records or views or permanent quick access.

 

The little timer control at the top is the “Recently Accessed Records”.

 

mru

This not only lists the views but also the records. Most importantly it lists the views/records/ dashboards from across the application. This means you could be in Sales and from this button quick access Cases without having to switch to Service area.

 

  1. Edit-Ready forms: In the previous version when you opened any form, it was not immediately available to you for editing or performing any operation on the form. You had to wait until the form completely loads and you get control of the form to type in.

 Now, the moment the form loads, it is available to you to type in. So you no longer have to wait for it to load completely. This is a huge performance improvement and it is definitely going to save a lot of time for a lot of people.

 

Well, there are many more improvements to look forward in the next release. There will be many more blogs to follow in the coming days targeted towards sharing the new improvements in the new release.

Listener services "not running"

$
0
0

Hi,

I have gone through various blogs and setup the connector for CRM/MDM but the listener and publisher services show up as "not running" in the integration options in MDM.

1. CRM and MDM are both in same online tenant.

2. Azure is not on the same tenant. it is a different subscription all together.

here is a screenshot of how it looks

Any pointers to resolve this?

Thanks!

Sam

Add Custom View in Lookup and Disable all the other Views

$
0
0

We had a requirement in which we were supposed to use a addCustomView method in order to filter the Lookup. We came across a situation in which we were able to set the custom view(The view with filtered records) as a default view to restrict the users from choosing the other records, but if a user wants, he/she could choose the different view from the Lookup window dialog and select the other records too, which the users were not supposed to choose.

Let us first introduce you to the situation, on the Quote form we have a Lookup field called Project Template. And, on the Quote Product form we have another Lookup field called Project Template Itemswhich should be filtered as per the value chosen for the Project Template.

We`ll start from the point where we have the ID of the chosen Project Template(to get the ID, you need to write a script on the Load of the Quote Product form).

Initially, what we started off was adding our custom view, but it still allowed the user to switch between different views, as a result users were able to choose records from other views.

We`ll explain you both the situations, the Initial Situation(which was adding the custom view) and theIdeal Situation(which was adding the custom view and restricting users from selecting other view) .

First and foremost, for the Lookup that`ll have filtered values, you need to change the below properties. These are the necessary steps.

Step 1:

From the form editor, go to the properties of the Lookup field.

Step 2:

1

In the Additional Properties section,

Default View – Select a View.

View Selector – Choose “Show Selected Views“.

System Views  – Only Select the view which was selected in the Default view.

Initial Situation:

In this situation, we were doing everything right but, we missed something that stopped us from getting the ideal output.

Code

function setCustomView(projectTemplateId) {

var functionName = “setCustomView”;

try {

//Check whether the field exist or not

        if (isValid(Xrm.Page.getControl(“new_projecttemplateitemid”))) {

// add the randomly generated GUID for the view id
var viewId = ‘{00000000-0000-0000-0000-000000000001}’;

 

//add the entity name

var entityName = “new_projecttemplateitem”;

 

// add the view display name

var viewDisplayName = “Project Template Item Lookup View”;

 

// fetch xml for filtered Project Template Item records

var fetchXml = “<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’false’> ” +

“<entity name=’new_projecttemplateitem’>” +

“<attribute name=’new_name’ />” +

“<filter type=’and’>” +

“<condition attribute=’new_projecttemplate’ operator=’eq’ value=’” + projectTemplateId + “‘ />” +

“</filter>” +

“</entity>” +

“</fetch>”;

 

//Grid Layout for filtered records

var layoutXml = “<grid name=’resultset’ object=’1′ jump=’productid’ select=’1′ icon=’1′ preview=’1′>” +

“<row name=’result’ id=’new_projecttemplateitemid’>” +

“<cell name=’new_name’ width=’150′ />” +

“<cell name=’createdon’ width=’150′ />” +

“</row>” +

“</grid>”;

 

// add the custom view for the lookup field

Xrm.Page.getControl(“new_projecttemplateitemid”).addCustomView(viewId, entityName, viewDisplayName, fetchXml, layoutXml, true);

}

} catch (e) {

throwError(e, functionName);

}

}

 

In the above code everything was right but,

on selecting the Custom view, we were getting the below results:

2

And, on selecting the System View, we were getting the below results.

3

Both the views were giving us different results, which we never wanted.

 

So, in the search of an ideal situation we found the below solution.

Ideal Situation:

Replace the code in the box with the below code.

Code

// add the default View GUID for the view id

var viewId = Xrm.Page.getControl(“new_projecttemplateitemid”).getDefaultView();

This single piece of line works like a charm.

Now, we got the below results on selecting both the views.

Custom View:

4

System View:

5

 

And, this is how we got the desirable result with a small tweak in the code.

Plug-in Tracing in Dynamics CRM 2015 Update 1

$
0
0

Before CRM 2015 SP1 update, an alternative option to debug the Plug-in or Workflow for developers was to use tracing messages which would help to trace the program using custom messages.

The developer had to manually throw an exception in order to trace the program. And then when the plug in/workflow is triggered a dialog is shown up to “Download the Log File” which contains the tracing information.

So, the user/Developer had to read the tracing from the downloaded text file, which was a tedious job itself.

So now in CRM 2015 SP1 update, we have one more option in which we have a new Entity called “Plug-in Trace Logs” which itself creates record for any exception occurred or thrown by the developer.

But in order to enable this feature we need to make following settings in CRM.

  • Go to Settings >> Administration

1

 

  • Under Administration  >> System Settings  >> Customization Tab2

 

So as we can see here there is an option “Enable logging to plug-in trace log”. By default it is Off, we need to select the option “Exception” in order to enable the creation of Plug-in trace log records in case any exception or error occurs.

There’s one more option in addition to this as mentioned in SDK and i.e. “All”. In the preview Version we don’t have it.

It’s mentioned that if we select “All” then the tracing information would be written to Trace Log record upon complete code execution or any error occurred throw custom code.

Once we have enabled the feature, we can view the Trace Log Records under:

Settings >> Plug In Trace Log Entity:

3

 

Records are created in CRM as below, which are read only just like System Job records to find out the error occurred.

4

 

Now let’s see what this record actually contains and why it simplifies the earlier process of debugging Plug-In and workflow using ITracingService interface.

5

 

You can see the record contains attributes like Message Name, Primary Entity, Mode etc which helps to get the information related to Plug-in.

As shown below it contains two fields Message Block and Exception Details which contains the actual data the user / developer is keen to understand.

6

Message Block: Contains the actual Tracing information which we specify in our Plug-in /Custom Workflow to Trace method. For e.g. tracing.Trace(“Workflow Started”); where tracing is an object of ITracingService.

Exception Details: Contains the Exception related information which has occurred in Plug-in or Custom Workflow.

Also we can view the Trace Log records using Advanced Find. Hence we can even filter out records based on the Query specified in Advance find. Below is an example of that:

We are selecting only those records which are created due to Plug-ins.

 

In addition to this there’s one more advantage of the Trace Log Entity.

Before, this feature was introduced the developers used to create a Custom Entity for storing Log Records so that they could trace the tracing information or exception details. But it was quite tricky in case of Transactional Plug-ins, as we know if any part of code fails in it, the entire changes would roll back and the custom entity to store the tracing information is not  created. Although now, with this System Trace Log Entity the Trace records will be created even if  the code fails.

Let’s take an example to understand it. We have registered a Plug-in on “Qualify Lead” on the Post Operation.

Let’s qualify the lead and suppose an exception occurs during execution as below:

8

We can “Download Log File” and read the “Tracing Information” which we used to do earlier. Or now we have an option to read the related Trace Log Record which is created even if the changes are Roll Back in the system  i.e. Lead  is not qualified.

The related trace log record created in the system is as below:

9

Thus, as we can see now the trace log record is created anyhow in the system even if the operations in the Plug-in are roll backed.

Now, who can view these records by default in CRM?

By default, the System Administrators and System Customizers can view these records but the access to view can be granted to other Users too.

NOTE: Trace logs will only be written to the trace log entities if the plug-in developer uses tracing within the plug-in.

If we have enabled this feature in our System, then Trace Log records will henceforth be created in CRM for the entities having Plug-in or Custom Workflow (which implements ITracingService) registered on it. Due to this more number of Trace log records could be created in CRM, so in order to manage this we can create a Bulk Deletion Job which we can schedule such that the trace log records would be deleted over fixed interval of time.

More ways to search a record in Dynamics CRM 2015 Update 1 – Alternate Keys

$
0
0

Prior to Microsoft Dynamic CRM 2015 Update 1 the only way to identify the records in CRM was the record guid. Now with the introduction of the Alternate Keys in Dynamics CRM 2015 Update 1 we can use alternate keys to uniquely identify a record in CRM in place of the primary key (GUID).

Features of Alternate Key:

  1. Provides a way to uniquely identify record.
  2. Alternate keys can be Composite keys i.e. more than one attributes can be selected to define the alternate key, thus providing a way to define a key as a combination of multiple attributes.
  3. Every Alternate Key you define creates an index behind the scene.
  4. Once you define alternate keys for an entity it acts as inbuilt duplicate detection rule i.e. it will throw an error as can be seen below if you will try to create a record with the same alternate key value which is already set for some another record.

                       

 

Create Alternate Key

Let us go through the steps to create an Alternate Key.

Alternate Keys are associated with an Entity and therefore you need to create them as a part of entity customizations.

Navigate to Settings -> Customization -> Entity to find a new "Keys" tab added along with Forms and Fields tabs.


On the "Keys" tab click the “New" button to add new keys for the entity.

When you click on "New" you get a list of "Fields" for the entities from which you can choose which Fields to set as Alternate keys. Following screen shot shows a list of fields for Account entity from which we can select to create as Alternate Key.


       

Only attributes of following types can be added as Alternate Keys -

1. Decimal Number

2. Whole Number

3. Single Line of Text

Note : Per entity you can define a maximum of 5 Alternate Keys only.

Alternate Keys Implementation through Code

Prior to Alternate keys, if we had to say add a new opportunity for a customer. We would like to first search for the customer and get the Guid so that we could assign that as the customer when creating an order. This meant we had to send out an explicit search request prior to the actual operation of creating an order.

With Alternate Keys such explicit calls are no longer required. Platform improvements made with the implementation of Alternate Keys actually take care of this internally.

Let's check the code to see how we can use this

1. Lets Create a new Contact and set the parentcustomerid using keys.

  

    KeyAttributeCollection keys = new KeyAttributeCollection();
    keys.Add("accountnumber", "ASH0011");
    
    Entity contactEntity = new Entity("contact");
    contactEntity["lastname"] = "Test last name 1";
    contactEntity["parentcustomerid"] = new EntityReference("account", keys); 
    contactEntity["fax"] = "1234579856";

    CreateRequest req = new CreateRequest
       {
         Target = contactEntity
       };

   _service.Execute(req);

This will automatically search for the "Account" from the keys and set it as "parentcustomerid" on the Contact created.

If the keys that are specified in the code do not exist then it will throw an error "A record with the specified key values does not exist in account entity".

2. Upsert an Account using Alternate key

  

    KeyAttributeCollection acckeys = new KeyAttributeCollection();
    acckeys.Add("emailaddress1", "someone3@example.com");
    acckeys.Add("telephone1", "555-0152");

    Entity accountEntity = new Entity("account", acckeys);
    accountEntity["name"] = "Test Account name 1";
    accountEntity["fax"] = "12345745678";

    UpsertRequest upreq = new UpsertRequest();
    upreq.Target = accountEntity;

    UpsertResponse resp = (UpsertResponse)_service.Execute(upreq);

 

When we execute the above code if an Account already exists with the Alternate Keys specified in the code then it will update the Account which has the matching Alternate keys and “RecordCreated” attribute is returned as "False" in Upsert response else it will create a new Account record and set the specified Alternate keys as the corresponding attributes values on Account so that we need not provide them separately and the “RecordCreated “ is returned as "true". In above sample code you can find that we have created two Alternate keys (composite keys) i.e. “emailaddress1” and “telephone1” so if the values specified for both the keys i.e. “emailaddress1” and “telephone1” matches with another record than in this case it will find that matching record and update that existing record. If any one of the keys value matches than it will create a new record.

Viewing all 3363 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>