Showing posts with label C# for CRM. Show all posts
Showing posts with label C# for CRM. Show all posts

Wednesday, 15 February 2017

Error: "Unable to Login to Dynamics CRMOrganizationWebProxyClient is nullOrganizationWebProxyClient is nullOrganizationServiceProxy is nullOrganizationServiceProxy is null" during connecting to CRM Online using Xrm.Tooling.Connector

It’s been long time I have not written any post.
So, now I just want to share my experience when I have error once trying to connect to the CRM Online using Xrm.Tooling.Connector

Here is there error:

"Unable to Login to Dynamics CRMOrganizationWebProxyClient is nullOrganizationWebProxyClient is nullOrganizationServiceProxy is nullOrganizationServiceProxy is null"

Here is my connection string:
image

It does seem okay since I am following the example given by SDK:
image

But I receive that error as it makes me cannot connect to the CRM.

Then, how to solve it?

It is easy, just fix the URL and ensure that’s you have correct Username, password and also AuthType parameter

So, the mistake here is just the URL, change this to your unique organization, not the friendly/display name
image

*I know it can be misleading because the example is using the display name as same as you use to type your URL when you are trying to access through your browser.

*Change to:
image

To find the Unique name, please to the Customizations –> Developer Resource and find the unique name of your organization
image

Hope this helps!
Thank you

Tuesday, 6 September 2016

Utilize Custom Action to Help Filtering The Lookup View in CRM Form

Overview

Sometimes in our project, we have requirement to filter lookup based on some conditions and it can be achieved using addCustomView or addCustomFilter function.

And sometimes it is just not too easy to do it in Javascript or the complex fetch xml, so in my blog I just want to share another method to get the filtered result same as you wanted, that is using Javascript + Custom Action!

Detail

Following my previous post: http://missdynamicscrm.blogspot.sg/2014/08/crm-2013-using-addcustomfilter-to-get-filtered-lookup-field-based-on-linked-entity.html

So, considering you have this filter:
image

To get the result as per expected, you can use Custom Action.

Steps:

1. You need to create a custom action that give you output, either STRING or ENTITYCOLLECTION

2. Inside the custom action code, if you want to return string, you can use comma delimited concept, or using | as delimiter, or you can just return the Final XML Filter already.

If you use EntityCollection, you might need to parse it again.

3. Then create a javascript that can call the Custom Action, you can use this method for easy way:
http://www.magnetismsolutions.com/blog/paulnieuwelaar/2015/08/12/Call-Action-in-CRM-2015-Easily-from-JavaScript-Library

4. Then you get the result as parameter, you can just set it to the filter = “the Result” (if you use Final XML Filter as the Output) or you parse the GUID if you use the comma or | delimited concept, or if you use EntityCollection then you need to parse it back.

5. See the result

Basically, you just need to get this result:

image

Either you just easily using String as output or other method it is up to u.

But the point here is we can use Custom Action for solving complex filtering and remember that we can use impersonation also to get the data you want if it is related to the other entities as well, imagine if we also need to have multiple entities involved then it might be easier if we use Custom Action as we just replace the DLL if there is any other changes using Plugin Registration Tool.

Hope this helps!

Thanks,
Aileen

Tuesday, 31 May 2016

Modify CRM View Query or Filter Criteria on Demand Dynamically using Plugin Retrieve

Hi guys,

Just want to share how we can modify the View Filter Criteria/Condition dynamically.

Introduction

Recently I have requirement that I need to create a View in CRM that there is no way to construct it from Advanced Find and make it dynamic.

For example,
I need to have query that:
1. The Date parameter is must be changed accodingly based on the today date and
2. Another parameter must get the value from a Configuration Entity
Ok, for number 1, we can force the users to always go to the View and change the date accordingly, but how about number 2?

So, my use case:

Show me the list of Customers that due date is in the next 21 days?
Meanwhile, 21 here is must be dynamically obtained from another Configuration entity.

So, this is nearly impossible, because even we use Advanced Find there is no way to use variable as parameter in the filter criteria that we need to query from another entity that is NOT related at all!
As we know that Advanced Find Query, only has limited operator and also it cannot Query from another entity, only limited to the related entities.

Use Case

So, to go to the code, I need to tell the scenario first, and to make it simpler, I just use this use Case:
I want to get all Active Customers that birthday is this Month.

This month is May for example.
So, I have a custom field = Birthday Month

Which I have auto-populated this field before with the Date Birth component (in another plugin).
So, now I need to always update the View to always Query to the:

Status = Active and Birthday Month = 5

And for the Next Month (June) should be:
Status = Active and Birthday Month = 6

Which last Month for April
Status = Active and Birthday Month = 4

Those number 5, 6, and 4 are supposed to be generated dynamically.

And I can’t use the This-Month operator because I dont have May 2016 Data, since I save the DOB, not updating every customer BOD every year plus 1 year.

You can also add another requirement, such as must be a Member Customer with Annual Income more than X, which X is you taken from Configuration entity.

What you need is just a new fetch XML or Query Expression that you can just convert use this request!


QueryExpressionToFetchXmlRequest req = new QueryExpressionToFetchXmlRequest();
req.Query = qenew;
QueryExpressionToFetchXmlResponse resp = (QueryExpressionToFetchXmlResponse)service.Execute(req);

But, for this use case, my point is jut to point out to you how to modify the Query just as per you wish (sorry as per Users’ whish Smile)

The Current View

image

So, for the post here, I just need to replace the Query from the Existing View (in your case, you might need to create a new View)

image

Then I got the savedqueryid from this view

The Code

public void Execute(IServiceProvider serviceProvider)

{
            #region must 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.OutputParameters.Contains("BusinessEntity"))
            {
                var retrievedResult = (Entity)context.OutputParameters["BusinessEntity"];
                Entity entity = retrievedResult;
                string fetch = string.Empty;


                //the below GUID, you can do advance query to make this as non-hardcoded one, you can find by name as well, for example
                //just for my use case here, I post here as final GUID of the 'Active Customer' view GUID
                //in ACTUAL case, do not use harcoded one because it will change in different environment unless for Out of The Box Entities View
                if ((Guid)entity["savedqueryid"] == new Guid("00000000-0000-0000-00AA-000010001004")) 
                {
                    QueryExpression qenew = new QueryExpression("contact");


                    ConditionExpression activeCon = new ConditionExpression()
                    {
                        AttributeName = "statecode",
                        Operator = ConditionOperator.Equal,
                        Values = { 1 }
                    };


                    int currentMonth = DateTime.Now.Month; //this is the variable that dynamically you need to insert in


                    ConditionExpression birthdayCon = new ConditionExpression()
                    {
                        AttributeName = "ags_birthdaymonth",
                        Operator = ConditionOperator.Equal,
                        Values = { currentMonth }
                    };


                    qenew.Criteria.Conditions.Add(activeCon);
                    qenew.Criteria.Conditions.Add(birthdayCon);


                    //for easy fetch XML I use the Query Expression then convert to FetchXML
                    //but you can always use the directly generated fetchXML and also can get from Advanced Find as well
                    QueryExpressionToFetchXmlRequest req = new QueryExpressionToFetchXmlRequest();
                    req.Query = qenew;
                    QueryExpressionToFetchXmlResponse resp = (QueryExpressionToFetchXmlResponse)service.Execute(req);


                    //work with newly formed fetch string
                    string myfetch = resp.FetchXml;


                    //change the existing
                    entity["name"] = "Active Customer Birthday this Month"; 
                    entity["fetchxml"] = myfetch;
                }
            }
}



Code Explanation

*For the GUID of savedqueryid section, I got the ID and I implement in my code

image

*In your case, you need to do query, do not hardcode, please see my comment in the code as well.
For the Query Expresison, this is the place for you to change the logic as per your current requirement


image

Then for the Fetch XML

image

As the title mentioned, Then you need to register as POST event for RETRIEVE message, entity = savedquery.
Refresh the Advanced Find or View to see the result.

Result

As we can see that now, the Active Customer View (The View that we just now changed the fetchXML dynamically), now has this Query

image


You can also try to tweak your code, by just add additional conditional over the existing view by retrieving the fetch xml

string baseFetchXML = enSavedQuery["fetchxml"].ToString();

Then use this request to convert to Query Expression easily

FetchXmlToQueryExpressionResponse

Example of what I Did:



Remember, you can also add another Parameters.

So use this method for Querying:
1. Query that different from the static query from the View
2. Query that requires dynamic Date/Datetime variables
3. Query that needs condition obtained from another non-related entity
4. Query with variable from another Entity, let’s say Configuration Entity
5. Another Impossible Query using standard static Advanced Find
6. Also can do query that involving Current User, Security Role, or Team Query
7. Query requires Operator like “Does-Not-Equal Today” then you can utilize the “ON-OR-BEFORE” or “ON-ON-AFTER”
8. Query for “NOT IN”, then this one you can utilize the “DOES NOT EQUAL” function

But remember, you still need to use  FetchXML in the End.

Hope this helps!
Thanks

Thursday, 31 March 2016

Intercept Column/Field Value in CRM View using RetrieveMultiple Plugin C#

In the previous post, I explained about how to modify the lookup field display using Plugin in CRM.

Now, I just want to show you how to modify also other column and I give a use case here is to display the column as Calculated Field.

Well, in CRM 2015 and above Microsoft introduced 2 new types of field: Calculated and Roll-up field, but what if your organization still using earlier version and you already created a field without using that type (do you care to re-create again?) or you want user not to always refresh it.

So, here I want to give you use case in simple scenario, that is to display “Age” field dynamically based on Birthdate of Contact versus current date.

Use Case

Now you have a custom field: Age, as Integer or Whole Number, but all blank.

You have choice: Always update this using batch job that will run everyday or you show as a report dynamically.

So, this is the combination of those choices, you do not need to create report, but you just need to intercept the Plugin to show the data that you want.

So here is before you apply the plugin:

*Age is blank
image

The Code

And here is the code:

 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

            //this is to modify the Age column

            if (context.OutputParameters.Contains("BusinessEntityCollection"))
            {
                var retrievedResult = (EntityCollection)context.OutputParameters["BusinessEntityCollection"];
                foreach (Entity entity in retrievedResult.Entities)
                {                 
                    if (entity.Contains("birthdate"))
                    {
                        var birthday = entity.GetAttributeValue<DateTime>("birthdate");
                        int age = CalculateAgeYear(birthday);
                        //modify the Age field
                    entity.Attributes["pa_age"] = age;
                    }
                    
                }
            }
        }

You can modify any field so long you put the correct type of object as the value based on the each field type

Result

And here is the result

image

Yes, you can display this in the view.

What if you want to display in the form?
You need to tweak the plugin and use the “Retrieve” message, instead.

Or you can use this as well
http://stackoverflow.com/questions/15048688/dynamics-crm-2011-plugin-retrieve-and-retrieve-multiple

*Thanks Pedro, my friend.

Which Retrieve can also be used for form and also RetrieveMultiple also.

Well, this is not only for one field, is can be for many fields..Including lookup that I explained in my other previous post.

And the good thing also you can display in any view so long the field is exist.

But the disadvantage is you cannot use this as keyword to search or filter, because it is actually not stored in the database.

Example:

Active Contacts View

image

So by using this capability, you can extend this for example to display the combination of 1 to N records!

For example: Total Number of related cases, total dollar of Revenue (from Opportunity), total child Contacts, total number of days of open cases, any aging fields, any rollup fiel as well, and so on..

And you do not even need plugin or workflow to always update this field everytine got any changes in the source for calculated field or any new or removed Associated records changes as well, or creating batch job or recreating your field to be using CRM 2015 rollup and calculated fields..Amazing right..

Hope this helps!
Thanks

Thursday, 28 January 2016

ModifyAccess Message Plugin CRM C#

Introduction

In my previous post, explained about Share and Unshare Access which will trigger GrantAccess and also RevokeAccess. But those message, as explained before will only be triggered if you Share the record to User or Team that has no previous added access at all or also Unshare the record completely removing the access from the previous User or Team.

How about if you want to have your own logic when you change the Access only to the specific users that already have the access, either you remove (not completely) and also add from the existing access so long you Update or Modify it, for End users, they just know about Share, but CRM has its own message request triggered.

The Code

And here is the code..

*You can continue from my previous post code

else if (context.MessageName == "ModifyAccess")
            {
                // Obtain the target entity from the input parameter.
                EntityReference EntityRef = (EntityReference)context.InputParameters["Target"];
                //after get this EntityRef then will be easy to continue the logic

                //Obtain the principal access object from the input parameter
                Microsoft.Crm.Sdk.Messages.PrincipalAccess PrincipalAccess = (Microsoft.Crm.Sdk.Messages.PrincipalAccess)context.InputParameters["PrincipalAccess"];

                //Then got the User or Team and also Access Control that being Granted

                //***to Get User/Team that being Shared With
                var userOrTeam = PrincipalAccess.Principal;
                var userOrTeamId = userOrTeam.Id;
                var userOrTeamName = userOrTeam.Name; //this entityReference will not return name, only ID
                var userOrTeamLogicalName = userOrTeam.LogicalName;

                //use the logical Name to know whether this is User or Team!
                if (userOrTeamLogicalName == "team")
                {
                    //what you are going to do if shared to Team?
                }
                if (userOrTeamLogicalName == "systemuser")
                {
                    //what you are going to do if shared to Team?
                }

                Trace(userOrTeamId.ToString());
                Trace(userOrTeamLogicalName);

                //***to Get the Principal Access
                var AccessMask = PrincipalAccess.AccessMask;
                Trace(AccessMask.ToString());

                throw new InvalidPluginExecutionException("ModifyAccess triggered"); //please remove later

                //your logic continue here after already have all of them
            }

And here is the complete code

void Post_Message_GrantAccess(IServiceProvider serviceProvider, IPluginExecutionContext context)
        {
            if (context.MessageName == "GrantAccess")
            {
                //this GrantAccess is for sharing
                //for Unshare, messagename = "RevokeAccess" and will do the same passing parameters

                // Obtain the target entity from the input parameter.
                EntityReference EntityRef = (EntityReference)context.InputParameters["Target"];
                //after get this EntityRef then will be easy to continue the logic

                //Obtain the principal access object from the input parameter
                Microsoft.Crm.Sdk.Messages.PrincipalAccess PrincipalAccess = (Microsoft.Crm.Sdk.Messages.PrincipalAccess)context.InputParameters["PrincipalAccess"];

                //Then got the User or Team and also Access Control that being Granted

                //***to Get User/Team that being Shared With
                var userOrTeam = PrincipalAccess.Principal;
                var userOrTeamId = userOrTeam.Id;
                var userOrTeamName = userOrTeam.Name; //entityReference only return ID, not Name so will be empty string here
                var userOrTeamLogicalName = userOrTeam.LogicalName;

                //use the logical Name to know whether this is User or Team!
                if (userOrTeamLogicalName == "team")
                {
                    //what you are going to do if shared to Team?
                }
                if (userOrTeamLogicalName == "systemuser")
                {
                    //what you are going to do if shared to Team?
                }

                Trace(userOrTeamId.ToString());
                Trace(userOrTeamLogicalName);

                //***to Get the Principal Access
                var AccessMask = PrincipalAccess.AccessMask;
                Trace(AccessMask.ToString());

                //throw new InvalidPluginExecutionException("Shared"); //please remove later

                //your logic continue here after already have all of them

            }
                
            else if (context.MessageName == "RevokeAccess")
            {
                // Obtain the target entity from the input parameter.
                EntityReference EntityRef = (EntityReference)context.InputParameters["Target"];
                //after get this EntityRef then will be easy to continue the logic

                //Unshare does not have PrincipalAccess because it removes all, only can get the revokee
                //Obtain the principal access object from the input parameter
                var Revokee = (EntityReference)context.InputParameters["Revokee"];
                var RevokeeId = Revokee.Id;
                var RevokeeLogicalName = Revokee.LogicalName; //this one Team or User

                Trace(RevokeeId.ToString());
                Trace(RevokeeLogicalName);

                throw new InvalidPluginExecutionException("Unshared"); //please remove later
            }

            else if (context.MessageName == "ModifyAccess")
            {
                // Obtain the target entity from the input parameter.
                EntityReference EntityRef = (EntityReference)context.InputParameters["Target"];
                //after get this EntityRef then will be easy to continue the logic

                //Obtain the principal access object from the input parameter
                Microsoft.Crm.Sdk.Messages.PrincipalAccess PrincipalAccess = (Microsoft.Crm.Sdk.Messages.PrincipalAccess)context.InputParameters["PrincipalAccess"];

                //Then got the User or Team and also Access Control that being Granted

                //***to Get User/Team that being Shared With
                var userOrTeam = PrincipalAccess.Principal;
                var userOrTeamId = userOrTeam.Id;
                var userOrTeamName = userOrTeam.Name;
                var userOrTeamLogicalName = userOrTeam.LogicalName;

                //use the logical Name to know whether this is User or Team!
                if (userOrTeamLogicalName == "team")
                {
                    //what you are going to do if shared to Team?
                }
                if (userOrTeamLogicalName == "systemuser")
                {
                    //what you are going to do if shared to Team?
                }

                Trace(userOrTeamId.ToString());
                Trace(userOrTeamLogicalName);

                //***to Get the Principal Access
                var AccessMask = PrincipalAccess.AccessMask;
                Trace(AccessMask.ToString());

                throw new InvalidPluginExecutionException("ModifyAccess triggered"); //please remove later

                //your logic continue here after already have all of them
            }
        }

Register the Plugin

And how to register the Plugin?
Easy…

Just add more event so-called ModifyAccess



So for the complete solution, you will have 3 steps registered in the Plugin
1. GrantAccess
2. RevokeAccess
3. ModifyAccess

Result

And here is the result..
I just want to modify the Access of this User from Read & Write to Read & Append & Share



Then here is the Trace result



*Remember to always remove the “throw new InvalidPluginExecutionException” because it will stop your process.

And I hope this can help you!
This is the requirement in my project that currently I am doing it so basically is helping me to document it!

Monday, 25 January 2016

Plugin Triggered When Share/Unshare CRM C#

Introduction

Hi everyone, I just want to share how Share/Unshare in CRM will trigger a plugin or just in case you need to do some logic after users has done the Share/Unshare records in CRM.

Especially if you want to Share the child records once you share the parent record, yes you can do the Cascading behaviour, but you might be aware that this parental and configurable cascading behaviour can only applied to 1 relationship, so imagine you have many entities, which this problem is the one i am facing right now.

The Problem

In the Plugin Registration Tool, there is no Share or Un-Share Messages and you see MSDN also does not have!



So meaning CRM does not allow the injection of the Share or Un-Share logic?

No..CRM does allow, just the Message Name is..

GrantAccess and RevokeAccess

Meanwhile you also can get the Parameters from that Request

The Code

This is the Code!

        //pass the context here from your common Execute function
        void Post_Message_GrantOrRevokeAccess(IServiceProvider serviceProvider, IPluginExecutionContext context)
        {
            if (context.MessageName == "GrantAccess")
            {
                //this GrantAccess is for sharing
                //for Unshare, messagename = "RevokeAccess" and will do the same passing parameters

                // Obtain the target entity from the input parameter.
                EntityReference EntityRef = (EntityReference)context.InputParameters["Target"];
                //after get this EntityRef then will be easy to continue the logic

                //Obtain the principal access object from the input parameter
                Microsoft.Crm.Sdk.Messages.PrincipalAccess PrincipalAccess = (Microsoft.Crm.Sdk.Messages.PrincipalAccess)context.InputParameters["PrincipalAccess"];

                //Then got the User or Team and also Access Control that being Granted

                //***to Get User/Team that being Shared With
                var userOrTeam = PrincipalAccess.Principal;
                var userOrTeamId = userOrTeam.Id;
                var userOrTeamName = userOrTeam.Name;
//this userOrTeam.Name will be blank since entityReference only will give you ID
                var userOrTeamLogicalName = userOrTeam.LogicalName;

                //use the logical Name to know whether this is User or Team!
                if (userOrTeamLogicalName == "team")
                {
                    //what you are going to do if shared to Team?
                }
                if (userOrTeamLogicalName == "systemuser")
                {
                    //what you are going to do if shared to Team?
                }

                Trace(userOrTeamId.ToString());
                Trace(userOrTeamLogicalName);

                //***to Get the Principal Access
                var AccessMask = PrincipalAccess.AccessMask;
                Trace(AccessMask.ToString());

                throw new InvalidPluginExecutionException("to trigger the trace only"); //please remove later

                //your logic continue here after already have all of them

            }
            else if (context.MessageName == "RevokeAccess")
            {
                 // Obtain the target entity from the input parameter.
                EntityReference EntityRef = (EntityReference)context.InputParameters["Target"];
                //after get this EntityRef then will be easy to continue the logic

                //Unshare does not have PrincipalAccess because it removes all, only can get the revokee
                //Obtain the principal access object from the input parameter
                var Revokee = (EntityReference)context.InputParameters["Revokee"];
                var RevokeeId = Revokee.Id;
                var RevokeeLogicalName = Revokee.LogicalName; //this one Team or User

                Trace(RevokeeId.ToString());
                Trace(RevokeeLogicalName);

                throw new InvalidPluginExecutionException("Unshared"); //please remove later
            }
        }

I just give you the concept that you can just continue from it..

Register the Plugin

Yup this is the last step, just using your favorite Plugin Registration Tool will do.



Result from the Trace

And here is the result that you can see, you can get the user or Team you have shared With and also What is the Access Mask

I try to share to a CRM User: Read and Write




So you can get the full Parameter in your plugin

Here is the Trace result



*As you can see you can get the System User ID as well Team ID if you share to Team and also the privilege, ReadAccess and also WriteAccess

How about UnShare?

Same as well!

I completely remove the CRMUser from all his previous shared access (Read & Write)



And this is the Trace result




*But, you need to Remember that Share here meaning performing Share to the user or Team that previously did not have any Access, it will trigger the GRANTACCESS event.
And also Unshare means Completely remove the Access, then it will trigger the REVOKEACCESS event.

If you want to only modify the Access (ex: from Read and Write to Read only), then you need to register your plugin to another message, so call MODIFYACCESS which i will tell you about this in the my next post..(I hope soon).

*Remember to remove the Trace, I use my own function to do trace in order let you know the Result and also you might not need it

And I hope this is helpful for you guys!
Thank you.

Wednesday, 13 January 2016

Aggregate Fetch XML Calculation C# CRM

Hi Just want to share the Code to Calculate the Aggregate in CRM, especially for those who are in the CRM version, older than 2015 so that there will be no calculated and roll-up field so far.
Using this helper code you just need to pass the Parent Id, and it will do the rest of Calculation and of course you just need to supply the Operator (like SUM, Count, AVG, etc)

public string CalculateAggregateFetchXML(string strEntityName, string strAggregateAttributeName,
                                                       AggregateOperator aggregateOperator, string strFilterXML) {
            string strFetchXML = string.Empty;
            string strAggregateAlias = "nec_alias";

            strFetchXML = string.Format(@" 
                            <fetch distinct='false' mapping='logical' aggregate='true'> 
                                <entity name='{0}'> 
                                    <attribute name='{1}' alias='{2}' aggregate='{3}' />
                                    {4}
                                </entity> 
                            </fetch>", strEntityName, strAggregateAttributeName, strAggregateAlias, aggregateOperator.ToString(), strFilterXML);

            EntityCollection aggregateResult = CrmService.RetrieveMultiple(new FetchExpression(strFetchXML));
            decimal totalValue = 0;

            foreach (var c in aggregateResult.Entities) {
                decimal aggregate2 = 0;
                if (c.Attributes.Contains(strAggregateAlias)) {
                    AliasedValue alias = ((AliasedValue)c[strAggregateAlias]);

                    if (alias.Value is Money) {
                        aggregate2 = ((Money)((AliasedValue)c[strAggregateAlias]).Value).Value;
                    } else if (alias.Value is Int32 || alias.Value is int) {
                        aggregate2 = ((int)((AliasedValue)c[strAggregateAlias]).Value);
                    } else if (alias.Value is Decimal || alias.Value is decimal) {
                        aggregate2 = ((decimal)((AliasedValue)c[strAggregateAlias]).Value);
                    }

                    totalValue = aggregate2;
                }
            }
            return totalValue.ToString();
        }

And the AggregateOperator Enum

public enum AggregateOperator {
            [Description("sum")]
            sum = 1,
            //
            [Description("avg")]
            avg = 2,
            //
            [Description("min")]
            min = 3,
            //
            [Description("max")]
            max = 4,
            //
            [Description("count(*)")]
            count = 5,
            //
            [Description("countcolumn")]
            countcolumn = 5,
        }

This usage is very easy

strTotalAttByRace = CrmContext.CalculateAggregateFetchXML(“entityname” “primaryidfieldname”,
                                                  NEC.ESBU.Helpers.CrmHelper.AggregateOperator.count,strFilterXML);

//strFilterXML You can skip this or use this in case you need more condition

It will result you a string or you can change to Integer, that is the total aggregate result

*Example for Aggregate Result with fetch xml

Is to return total Attendance by Race (that stored in the Contact entity)

Attendance is many to 1 relationship with Contact

strFilterXML = string.Format(@"<filter type='and'> 
                                                <condition attribute='{0}' operator='eq' uitype='{1}' value='{2}' />
                                                <condition attribute='pa_sessionid' operator='eq' uitype='pa_session' value='{3}' />
                                                <condition attribute='pa_type' operator='eq' value='{4}' />
                                            </filter>
                                            <link-entity name='contact' from='contactid' to='pa_participantid' alias='af'>
                                                <filter type='and'>
                                                    <condition attribute='pa_race' operator='eq' value='{5}' />
                                                </filter>
                                            </link-entity>",
                            strAttributeProductIdName, strEntityProductIdName, guidProductId.ToString(), guidSessionId.ToString(),
                            (int)productType, (int)raceType);

This will be very useful if you want to calculate child record, either SUM, COUNT, or AVG for instance, then just pass the parent ID or add more fetch XML to add more filter

*Work for Money, Whole Number, and Decimal data type

Hope this helps!
Thanks

Wednesday, 7 October 2015

CRM Error: There is no active transaction. This error is usually caused by custom plug-ins that ignore errors from service calls and continue processing.

Hi guys,

Just a quick one.

Just now I receive this error from my triggered custom plugin:

"There is no active transaction. This error is usually caused by custom plug-ins that ignore errors from service calls and continue processing."

And I believe that this often happen to the development.

Root Cause

Here is the root cause:
1. I have a custom plugin in the onCreate event.

2. Inside my custom plugin, I have Assign function

3. I try to put ‘try and catch’ just to avoid the error

4. Then it hits another error which is: “There is no active transaction. This error is usually caused by custom plug-ins that ignore errors from service calls and continue processing.”

5. So, I put a logger

6. And I have this error, instead:

image

Which this error is actually CRM System Error:
SecLib::CrmCheckPrivilege failed. Returned hr = -2147220943 on UserId: 01dc8c30-eb68-e511-80f2-00155dad0019 and PrivilegeType: Read
So, my conclusion:

Conclusion

1. My Team/User that I want to assign to does not have Any Security Role that having this Read privilege for the entity record object i want to assign to.

2. I try to skip the CRM Error by putting the try and catch just before the ‘Assign’ request

3. But, it fails to proceed, instead, CRM still insists to block this creation..

4. Because it is also right, you cannot do it anyway, CRM system plugin would still block and there is no way you skip the process that needs another rule to apply.

5. Eventhough i put this try catch and try to cancel the Assignment, I still receive the error:

image

What to Check

So, if you find this error, please check:

- Whether you have custom plugin/custom workflow active triggered
- Whether you put skipping the error that will have impact to the CRM process, it is not possible
- Actually you better to log the error
- Because you won’t know what it is
- This is not your logic wrong in your custom plugin
- This just you need to fix why CRM cannot proceed?
- Is that because your user/team does not have privilege or you missed some parameters required
- This error might happen like for Assignment, Lead Qualification, Quote creation, etc

Hope this helps.

Friday, 21 August 2015

Get Fiscal Year in CRM C#

Sometimes we need to Fiscal Year in CRM and want to use the Out of the box one

Here is the query using C#, I give 2 ways, using LINQ and Late Bound;

//Using LINQ

private void RetrieveOrganizationFiscalYear()
{

            _service = new XrmContext();
            XrmContext xrmContext = new XrmContext();
            var OrganizationSettings = from os in xrmContext.OrganizationSet
                                       select new Organization

                                       {
                                           FiscalCalendarStart = os.FiscalCalendarStart,
                                       };

            DateTime dtStartFiscalYear = OrganizationSettings.First().FiscalCalendarStart.Value; 
            //this is the date of start fiscal year
 }

//Using Late Bound
 private void RetrieveOrganizationFiscalYearLateBound(IOrganizationService _service)
 {
            //Retrieve organization
            Entity enOrganization = new Entity("organization");
            EntityCollection ecOrganizations = new EntityCollection();
            DateTime dtStartFiscal = new DateTime();
            QueryExpression qx = new QueryExpression();
            qx.EntityName = "organization";
            qx.ColumnSet.AllColumns = true;
            ecOrganizations = _service.RetrieveMultiple(qx);

            if (ecOrganizations.Entities.Count > 0)
            {
                enOrganization = ecOrganizations.Entities[0];
                dtStartFiscal = enOrganization.GetAttributeValue<DateTime>("fiscalcalendarstart");
                //this is the date of start fiscal year
            }
 }

You can see this post as well:
https://community.dynamics.com/crm/f/117/t/160719

Hope this helps!

Thanks.

Sunday, 5 July 2015

Utilizing CRM Custom Action for Transaction Rollback Purposes

Introduction

In CRM 2013, we have a new feature, so-called Custom Action.

Well, I have posted about this feature long ago.


That time I was rarely using that because got problem in deploying the Custom Action which I believed has been fixed and now my colleague’s opening again my mind to re-consider using this feature.

Custom Action Features

Do you know that beside its great features such as:

1.  For replacing Configuration entity
2. Or even to validate something that need server side code which you are no longer need plugin again since its capacity to be called without triggering it by creating or changing field value like what plugin does,
3. Or to call this using javascript,
4. Plus even new amusing feature in CRM 2015 Update 1 to call it through Workflow,
5. Again, you can add more action as well as what workflow has been doing for you the whole time.

What I want to emphasize and spot in this post is its capability to do rollback.

Example or Scenario

Imagine, in daily transaction, in CRM, you have Header and Detail, Parent and Child, and Main and Line Item, Transaction and Transaction Detail.

Then also for major impact transactions, such as Bank Account, posting to Finance, Order and Order Detail, you need to secure that all of records must be done properly and 100% success rate, otherwise fail it! You cannot deliver an Order to customer if not all the details were created successfully and also very common situation you need to flag a record that you have updated in other entity, you need to make sure both are success, you cannot just in halfway, success in one transaction while fail to another, you need BOTH of them SUCCESS and EITHER one is FAIL, ALL must be stated FAIL and roll it back as the original one. This table is very important because it is Financial record, a –finance-related means A Money related, so both must be in sync, one fail, both must fail.

Well, CRM also gives good example in term of reciprocal and must done transaction, that is fund transferring:

“The classic example is transferring funds between two bank accounts. If you withdraw funds from one account you must deposit them in the other. If either fails, both must fail.”

The Research and The Implementation

I think what you all are waiting for is the real example in term of coding or implementation

Now, let’s say I have two entities, Entity A and B.

If Entity A is fail created and I need to make sure B also fail and vice versa.

I have 5 Entity A records and 5 Entity B records.
1 of them is failed then I must fail them.

*All of the example is within the CRM DB Context, not other system (external) involved that can be rolled-back, okay.

Standard CRM Transaction CRUD Request

There is no rollback for this standard CRUD Request because it’s processing one by one record.

Execute Multiple Request CRM

Well, now let’s move little bit to more advance, what will happen if we use the Execute Multiple Request, will it be rolled-back?

The answer is no..It will stop you to the next action if you use the skip or ignore error parameter to No, but in fact, it does not roll back the previous made transaction.

Custom Action

This is what I want to emphasize.
Assuming, I have this Action:

image

*For creating an action you can refer to my blog posts or other good example through the internet and good explanation from Power Object:

Then, inside the Custom Action code, I have the Create Record like I did in the Code-1 but I put in the CRM Action by parsing the EntityCollection as input

image

*Inside the collapsed region:

image

This one if you enable, means “no error” so that it won’t enable roll-ed back because assuming this action never throw an error, so you might need to re-think when you implement this.

For roll-back purposes, do not catch error for each single transaction.

You can do that in lump sum then just throw it all!

Okay, now, so what happened to my record after I trigger this action through executing it programmatically?

Well, it throw me error and there is no record at all!

*First entity
image

*Second Entity

image

*Well, as we can see here is, one fail, make the others also fail.

Other Research

Now, to make it lively, I increase the number of record to 800.
Then I make the failure in 789th row.
image
See what happened when I keep refreshing… then in fact I failed in almost the last row.
*First Refresh
image
*as you can see we have 130 records here
Then..
keep refreshing
*Second Refresh…
image

208 records here..

*Third time..

image

667 records here..

And until my code was stopped..

image

Yeah, it does stop in the record no 789.

And now..

I try to refresh again the advanced find..

And guess what…

All records….It’s gone..

image

So, it has the concept, once one is failed, then the other must failed as well!

What if I disable the Roll-Back

To ensure my curiosity and my doubt about the rollback function (well even for such example I still have doubt little bit but annoying) then I try to disable the rollback checkbox to ensure this usage.
image
And here is the result when I turn it off.
image

*As you can see here is the record is created anyway, no rollback same as what CRM Normal CRUD or Execute Multiple did before.

This does stop the transaction, but not rollback the previous committed transaction, so it proves the concept of rollback that Custom Action has.

So, hopefully this article is not so long for you to read and you can get advantage by reading it.

I intend to document it because in case I forgot it will become our e-memory and to encourage anyone to keep using this feature for your important couple of transactions that needs rollback each other.

Thanks and have a nice day!

Sunday, 28 June 2015

Convert CRM Plugin Message to CRM Alert or Notification When Save Programmatically JavaScript

Introduction

In my previous post, I have mentioned about how to get the Plugin Context Message and pass it to CRM Client Script.

Now, I just want to continue that post and make some advance message on it (while I shown before by parsing and send it to form notification before)

Assuming, in your Plugin, you list down and combine all of your message together and you put it in the List<string> or even a StringBuilder as usual then you use AppendLine Method

For example:


sbValidationMessage.AppendLine("Hey you!");
sbValidationMessage.AppendLine("Hey you2!");

With sbValidationMessage is initialized instance name of StringBuilder()

Okay, now I want to show you in the real message, you are expecting that you will see two lines as well.

But, your expectations is not going to be real..

Why? Because Plugin in CRM is not supporting the HTML Message, so it does not recognize newline or break, it will make as whitespace only, well you can change it unsupported by converting the dialog message to innerHTML method but again, it is unsupported!

So, we will have this:

image

In fact, you are expecting to have two lines instead of messy message mixed in one statement.

So, what you can do is you can get and parse the message from the ErrorCallback!

Solution

Here we go..

I see this article and I found that we can have two next function, either success or error

https://msdn.microsoft.com/en-us/library/dn481607(v=crm.6).aspx
Xrm.Page.data.save().then(successCallback, errorCallback)
So, i check and start my investigation I get this clue:

image

Then, now I go and fix my code:

The Code

//this the function that my ribbon or other event that need to save programmatically
function publish() {
    if (confirm("Confirm this Class, so that it can be appearing in the Class creation for course reference?")) {
        Xrm.Page.getAttribute("new_ispublished").setValue(true);
        Xrm.Page.data.save().then(successCallback, errorCallback);
        //Xrm.Page.data.refresh();
    }
}

function successCallback() {
    //Needed to set form dirty to false explicitly as it is not done by platform
    Xrm.Page.data.setFormDirty(false);
    var Id = Xrm.Page.data.entity.getId();
    Xrm.Utility.openEntityForm("my_entity", Id);
}

function errorCallback(saveErrorResponse) {
    if (saveErrorResponse != null) {
        if (saveErrorResponse.debugMessage != null) {
            alert(saveErrorResponse.debugMessage);
        }
    }
}

Result

image

As you can see, now we have lines with break line.

or you might break it into Form Notifications

image

Code for Form Notification

//this the function that my ribbon or other event that need to save programmatically
function publish() {
    if (confirm("Confirm this Class, so that it can be appearing in the Class creation for course reference?")) {
        Xrm.Page.getAttribute("new_ispublished").setValue(true);
        Xrm.Page.data.save().then(successCallback, errorCallback);
        //Xrm.Page.data.refresh();
    }
}

function successCallback() {
    //Needed to set form dirty to false explicitly as it is not done by platform
    Xrm.Page.data.setFormDirty(false);
    var Id = Xrm.Page.data.entity.getId();
    Xrm.Utility.openEntityForm("my_entity", Id);
}

function errorCallback(saveErrorResponse) {
    if (saveErrorResponse != null) {
        if (saveErrorResponse.debugMessage != null) {
            alert(saveErrorResponse.debugMessage);
            var strSplitted = splitStringToArray(saveErrorResponse.debugMessage, "\r\n");
            for (var i = 0; i < strSplitted.length; i++) {
                var notifId = "Notif_" + i + 1;
                Xrm.Page.ui.setFormNotification(strSplitted[i], 'ERROR', notifId);
                //remember you might need to clear the notification also when first hit the ribbon, otherwise it will keep adding.
            }
        }
    }
}

function splitStringToArray(str, separator) {
    return str.split(separator);
}

*But you need to note that the plugin message will be still appearing since this is the OOB Save Method, if you need to avoid it, you might need to use OData Merge to Update programmatically and pass the callback function async.

https://msdn.microsoft.com/en-us/library/gg328090.aspx

https://msdn.microsoft.com/en-us/library/gg334427.aspx

Well, it can be useful for next client script action but you cannot avoid the plugin message still appearing as debug message in supported way.
I use odata Merge rather than Save if want to manipulate without caring about this Plugin Message and it works.

Hope this helps!

Thanks.

Saturday, 16 May 2015

There was an error while trying to deserialize parameter http://schemas.microsoft.com/xrm/2011/Contracts/Services:query. The InnerException message was 'Error in line x position y. Element 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:anyType' contains data from a type that maps to the name 'System.Collections.Generic:List`1'

When you do the ConditionOperator.In in your Query during perfoming RetrieveMultiple, you might encounter this error.

“The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://schemas.microsoft.com/xrm/2011/Contracts/Services:query. The InnerException message was 'Error in line x position y. Element 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:anyType' contains data from a type that maps to the name 'System.Collections.Generic:List`1'. The deserializer has no knowledge of any type that maps to this name. Consider changing the implementation of the ResolveName method on your DataContractResolver to return a non-null value for name 'List`1' and namespace 'System.Collections.Generic'.'.  Please see InnerException for more details.”

Well, here is the way how I solve this.

Root Cause

Actually, the Root Cause is because I was passing the List<object> rather than an Array, which resulted to the error.

Solution

I just change my code

From:

List<Guid> guidMyIds = new List<Guid>();
//I call the function to return list of Guid's
guidMyIds = GetGuidList();
qx.Criteria.AddCondition("guid", ConditionOperator.In, guidMyIds);

Then I change to
List<Guid> guidMyIds = new List<Guid>();
//I call the function to return list of Guid's
guidMyIds = GetGuidList();
qx.Criteria.AddCondition("guid", ConditionOperator.In, guidMyIds.ToArray());

I just change the list to array and now it works.

guidMyIds.ToArray()

Hope this helps!

Thanks!