Sunday, July 15, 2012

Capture and Handle event when one entity record is attached through a Many to Many relationship - MS CRM 4

If you have already dug up too much about trying to capture the event when an entity record in MS CRM is attached ~ linked to a record in many to many relationship.


For example, if you have two entities : Supplier and Company in MS CRM where a supplier can do business with many companies and at the same time a Company can order stuff from many suppliers. What if you want to send some notification whenever a new supplier or an existing supplier is linked to a company; similarly you may want to send a notification when a Supplier signs a deal with 2-3 companies and gets linked to those.


Some experts from MS CRM fraternity have suggested some ways to over ride this limitation of MS CRM that it does not expose any event for the above mentioned scenario, below is one of the examples:  
http://ayazahmad.wordpress.com/2009/07/23/event-on-many-to-many-relationship-nn-in-crm-4/

But making changes to the default plugin model of MS CRM to add Associate or Disassociate event is not so easy and simple while going from another approach : Javascript can save your day and you can capture events when records are linked or unlinked into a many to many relationship.
-----------------------------------------------------------------------------------
Here's how to carry this out: action begins :

1. As you can see, first step in javascript approach is to get the id of the IFRAME element that shows the many to many relationship associated view which in this case is Accounts. As you can see below, you can get it by doing a F12, and clicking on the border of the iframe: areanew_new_supplier_accountFrame



2. Then you need to bubble the event when a record is linked in m-m rel. which is the Add Button. For that you need the id of the button, do an F12 to bring the developer toolbar, click on and pick the mouse pointer under HTML tab in the bar and click on the Add Existing Account button, if will show you the html tags behind and and you can see the id here : _MBtoplocAssocObjlnewnewsupplieraccount1 as shown in the blue line in the image.





Also you will need the id of the remove button, you may have this button on the main toolbar of the associated view as shown, or you may find it under More Actions menu with sub-menu label "Remove". Find the id of that button or sub menu using the same F12 method, it has a  very long string as id as can be seen below.




 Now go to the Form load script of the entity form, on form load, and add a function for capturing the readystatechange event because we want to bubble ~ over ride the on click event of the add/remove link buttons  so that we can push our own logic.

Things to notice in the following code :
1. Overriding Add existing click button is straight forward, attach an onclick event

2. Since Remove is a sub option under the More Actions menu, I fould it could not be overridden the same above way, so I had tweaked it's Action property, and appended one statement after the default Action, this extra statement is the function call.

function InitializeIFrameEvents(Iframe) {
  Iframe.onreadystatechange = function IframeButtonEvent() {
    if (Iframe.readyState == 'complete') { 
      var iFrame = frames[window.event.srcElement.id];

          if(Iframe.contentWindow.document.getElementById(' _MBtoplocAssocObjlnewnewsupplieraccount1 ')!=null) 
{ Iframe.contentWindow.document.getElementById(' _MBtoplocAssocObjlnewnewsupplieraccount1 ').attachEvent( "onclick" ,onLinkAdd); }    
         if(Iframe.contentWindow.document.getElementById(' _MIdoActionExcrmGrid....very long id of the remove sub option from more actions menu ')!=null) 
       {         
        removeButton=Iframe.contentWindow.document.getElementById('_MIdoActionExcrmGrid....very long id of the remove sub option from more actions menu');
        removeButton.action=" doActionEx('crmGrid', '10047', top.crmFormSubmit.crmFormSubmitId.value, 'disassociate', top.crmFormSubmit.crmFormSubmitObjectType.value, 'tab=areanew_new_supplier_account&associationName=new_new_supplier_account&roleOrd=2');   window.parent.crmForm.  onLinkRemoved();";
       }

    }
  }
}





InitializeIFrameEvents(document.getElementById(' areanew_new_supplier_accountFrame '));


// Here is the javascript handler for Add Existing button 


function onLinkAdd()  

   alert(' link added'); 
   //  you can write your own code here , maintain a Counter field on form that keeps the number of records linked in this m2m relationship, this can help in situation when you want to send some email on each record linked
}


// And this function is for the remove sub option click


function onLinkRemoved()  

   alert(' link removed'); 
  // you can reset the counter that will maintain the number of linked records
}

And there you go, you can now do whatever you want on the add and remove events, going more advanced, you will want to apply your own filter as to allow only certain records example Accounts with City London to be linked while restrict the other accounts. As you know default MS CRM lookup do not allow you this functionality, to do this, you will have to tweak the method that runs when you link records, and add your condition logic over there, I will explain that example in my next post.

Wednesday, July 11, 2012

Creating Email through Plugin / SDK in CRM 4 - line breaks don't work


You may have noticed when you create email using a sample code like below in a plugin or for that matter any other SDK based assembley for MS CRM. Your line breaks : \n or \r or System.Environment.NewLine will not work.


          email Email = new email();   
                     Email.to = new activityparty[] { toParty };
                     Email.from = new activityparty[] { fromParty };
                     Email.regardingobjectid = regardingObject;
                     Email.subject = "Hello!!";
                     Email.description = "Dear, line 1" +         
                     System.Environment.NewLine+  
                      "my message for your - line 2";
                     Email.directioncode = new CrmBoolean();
                     Email.directioncode.Value = true;
                     
                     Guid _emailId = _service.Create(Email);
                     
                     SendEmailRequest sendEmailRequest = new SendEmailRequest();
                     sendEmailRequest.EmailId = _emailId;
                     sendEmailRequest.TrackingToken = "";
                     sendEmailRequest.IssueSend = true;


I went into the business entity object that service will return when you query existing Emails in MSCRM, the .description field will have value like HTML rich text code :

.description = " < FONT size=2 face= \"Tahoma, Verdana, Arial \ " >your text < br / >"


So the key is to add "< br / >" as line feed instead of the common dot net and javascript carriage returns and you will be able to format your emails as you want from inside SDK plugins and more.

Friday, June 15, 2012

Cancel Workflow job programmatically - CRM v.4

How many times you had to created a workflow on some date like Due date which keeps changing as per client's request. While this may sound simple in business, on CRM you have a workflow that may be waiting till this date and send a reminder exactly a week before.

So you need to keep calling this workflow every time the date is updated, to make the matters more complicated, all the previous jobs that had initiated due to past modification will keep running and chasing that date which is never going to occur. What you do now, manually cancel those to save system resources and stop the never ending system jobs.

There is a way to do it through SDK, in my case I call this function as a custom workflow activity in the first step of the workflow which Cancels any previous jobs of this workflow.

  #region Cancel My WFs

            QueryExpression qe = new QueryExpression();
            qe.EntityName = EntityName.asyncoperation.ToString();
            ColumnSet cs = new ColumnSet();
            cs.Attributes = new string[] { "name", "regardingobjectid", "createdon","statuscode","statecode" };
           
            FilterExpression fe = new FilterExpression();
            fe.FilterOperator = LogicalOperator.And;

            ConditionExpression ce1 = new ConditionExpression();
            ce1.Operator = ConditionOperator.Equal;
            ce1.AttributeName = "regardingobjectid"; // Your record id
            ce1.Values = new object[] { new Guid("EE87FB7E-6C1F-E111-AF92-0050569F0AE2") };

            ConditionExpression ce2 = new ConditionExpression();
            ce2.Operator = ConditionOperator.Equal;
            ce2.AttributeName = "name"; // GUID of the Waiting Workflow: WF_InDeal_NextEndofTermNotificationDate_Update
            ce2.Values = new object[] { "Your_WF_Name" };

            ConditionExpression ce3 = new ConditionExpression();
            ce3.Operator = ConditionOperator.NotEqual;
            ce3.AttributeName = "statuscode"; // GUID of the Waiting Workflow: WF_InDeal_NextEndofTermNotificationDate_Update
            Status CancelledStatus = new Status(); CancelledStatus.Value = 32;
            ce3.Values = new object[] { CancelledStatus };

            fe.Conditions = new ConditionExpression[] { ce1,ce2};
            fe.FilterOperator = LogicalOperator.And;
            //fe.AddCondition(ce1);
            //fe.AddCondition(ce2);

            qe.ColumnSet = cs;
            qe.Criteria = fe;

            req = new RetrieveMultipleRequest();
            req.ReturnDynamicEntities = true;
            req.Query = qe;
            RetrieveMultipleResponse resp = null;
            resp = (RetrieveMultipleResponse)serv.Execute(req);
            if (resp.BusinessEntityCollection.BusinessEntities.Length  > 0)
            {
                BusinessEntityCollection bec = resp.BusinessEntityCollection;
                asyncoperation asyncOperation = null;
                DynamicEntity dynamicEntity = null;
                TargetUpdateAsyncOperation targetUpdateAsyncOperation = null;
               
                string Status = "";
                for (int i = 0; i < bec.BusinessEntities.Length-1 ; i++)
                {
                    Status = ((UpdateProductBasePrice.crmService.StatusProperty)(((UpdateProductBasePrice.crmService.DynamicEntity)(bec.BusinessEntities[i])).Properties[4])).Value.name;
                    if (!Status.Equals("Canceled"))
                    {
                        asyncOperation = new asyncoperation();
                        dynamicEntity = (DynamicEntity)bec.BusinessEntities[i];
                        asyncOperation.asyncoperationid = new Key();
                        asyncOperation.asyncoperationid.Value = new Guid(((UpdateProductBasePrice.crmService.KeyProperty)(dynamicEntity.Properties[5])).Value.Value.ToString());
                        asyncOperation.statuscode = new Status();
                        asyncOperation.statuscode.Value = 32;
                        asyncOperation.statecode = new AsyncOperationStateInfo();
                        asyncOperation.statecode.Value = AsyncOperationState.Completed;

                        targetUpdateAsyncOperation = new TargetUpdateAsyncOperation();
                        targetUpdateAsyncOperation.AsyncOperation = asyncOperation;

                        UpdateRequest updateAsync = new UpdateRequest();
                        updateAsync.Target = targetUpdateAsyncOperation;

                        try
                        {
                            serv.Execute(updateAsync);
                        }
                        catch (System.Web.Services.Protocols.SoapException soapEx)
                        {
                            continue;
                        }
                    }
                }

            }
            return;
            #endregion 

Thursday, April 19, 2012

MS CRM from Chrome - 2

In continuation with the last post, you can access Microsoft CRM, for that matter IE from Chrome using this link, it's much better and will work with Mozilla and Safari as well I guess

http://app.cloudinternetexplorer.com/


Friday, April 13, 2012

MS CRM from Chrome

Open MS CRM in Chrome using this chrome app 


With this tiny app you can open and use MS CRM in google Chrome, all you need to do is install this app   https://chrome.google.com/webstore/detail/miedgcmlgpmdagojnnbemlkgidepfjfi


Then in new Chrome tab click on the blue IE icon on right side whichi will open site in IE mode from Chrome and then


 write your CRM url, enjoy CRM with Chrome, like Browney and Chocolate ! I have also observed though any new 


window like a record or customisations views will eventually pop up IE windows but you can still browse the main 


page of CRM in Chrome 


As you can see below, I have applied some fancy theme to my Chrome to make it more believable that its actually MS CRM in google chrome browser :



Monday, July 18, 2011

Microsoft Dynamics CRM 2011 – Role Based Forms

Microsoft Dynamics CRM 2011 – Role Based Forms (Courtesy : http://www.powerobjects.com/blog/2010/12/07/microsoft-dynamics-crm-2011-role-based-forms/)

Another exciting new feature in CRM 2011, is the ability to have different forms for different security roles. For Example: You may want the Account form show different fields, for different security roles to maximize the important information that is displayed for each role.
Each Entity has two forms created by default: A “Main” Form and a “Mobile” Form. (Mobile Express)
By Default, the “Main” form is set so that all security rolls use this form whenever they access the Entity. However, with a few minor changes, additional Forms can be created to Rearrange/Add or Remove Information to maximize productivity for different users in CRM.
Let’s take a look at the Account Form.... Read More