May 14, 2012

Filtered lookup in CRM 2011: a dynamic lookup view

Filtered lookup in CRM 2011 is quite different than CRM 4.0. It contains more code, but result could be fruitful. I learn this as a different approach. This actually is imposing of a custom view which is temporary and dynamic. I like the idea. “addCustomView” is the method that enables us to do this. We will see how I do this.

Assume I have a custom entity called office. My office entity contains two main lookups. One is for account and other one is for contact. Obviously, I need to have a filter for contact lookup which will show only the contacts of selected account (if account lookup field is not null). In this approach, we are just creating a view and make it default when loading the lookup window.

Below is the method I use for this. This code shows you how it gives more control over the new view... we decide its name, criteria and also layouts that contains the widths of each column to show. Guid is any new Guid.

function ContactLookupFilter(accountid, accountname)
{     
var _viewId = "{10CACCF3-AC63-46FE-920B-DFEF53BCDE33}";
var _entityName = "contact";
var _viewDisplayName = "Contacts of Account : " + accountname;

var _fetchXml = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>" +
                "<entity name='contact'>" +
                "<attribute name='fullname' />" +
                " <attribute name='contactid' />" +
                " <attribute name='parentcustomerid' />" +
                " <attribute name='address1_city' />" +
                " <attribute name='address1_telephone1' />" +
                " <attribute name='telephone1' />" +
                " <attribute name='emailaddress1' />" +
                "<filter type='and'>" +
                "<condition attribute='parentcustomerid' operator='eq' value='"+accountid+"'/>" +
                "</filter>" +
                "<order attribute='fullname' descending='false' />" +
                "</entity>" +
                "</fetch>";

var _layoutXml = "<grid name='resultset' object='1' jump='name' select='1' icon='1' preview='1'>" +
                 "<row name='result' id='contactid'>" +
                    "<cell name='fullname' width='250' />" +
                    "<cell name='parentcustomerid' width='150' />" +
                    "<cell name='address1_city' width='150' />" +
                    "<cell name='address1_telephone1' width='100' />" +
                    "<cell name='telephone1' width='100' />" +
                    "<cell name='emailaddress1' width='150' />" +
                  "</row>" +
                  "</grid>";

var lookupControl = Xrm.Page.ui.controls.get('office_contactid');
lookupControl.addCustomView(_viewId, _entityName, _viewDisplayName, _fetchXml, _layoutXml, true);
}

Here is the code to call this from the loading event.

function office_OnLoad()
{
 var attrs =  Xrm.Page.data.entity.attributes;
 if (attrs.get("office_accountid").getValue() != null)
 {
 var _aid  =attrs.get("office_accountid").getValue()[0].id; 
 var _aname  =attrs.get("office_accountid").getValue()[0].name; 
 ContactLookupFilter(_aid, _aname);
 }
}

Are we done now? Some explained this is it, but I propose you to call this method from the OnChange event of the account lookup as well. Let me explain why? If we call this method only in loading, this custom view will load for the contacts of account which is currently assigned to the form. If user changes the account (and not save yet) prior to filling the contact lookup, our view is still showing the contacts of the account which was present when loading. But now we got different account on the form... Hope you understand the scenario...

Now check how its look likes... Can you see how our new view have got the custom name we gave. Very descriptive; since we have selected “SO and Company” as the account, we can see that as a part of the view name.


See how this dynamic view is listed along with other views that give the flexibility of selection.

May 10, 2012

Custom button: Must show both icon and title!

CRM form has a toolbar that contains most needed built in buttons such as SAVE, CLOSE and etc. It also allows you to create your own action buttons too. This is pretty straight forward. Usually, we are allowed to have icon and title for those buttons along with tooltip.

If we add number of custom buttons, there could be a space problem since width of the toolbar is limited. CRM is doing something smart to show only icon when there is an issue of space. You will sometimes notice that you see both icon and title only when a form is maximized.


Even if we add more custom buttons through ISV, we can hide them according to the current status of the form when loading. We discussed it here. However, this doesn’t release any space.  In fact, hiding some of the custom buttons through JavaScript doesn’t solve the space issue. This results in showing only the icons for the custom buttons while a lot of free space is visible.

If we need to show both icon and title necessarily, there is no simple recommended way. However I tried one out-of-box method that worked for me. This is not a Microsoft supported way.

It contains below three steps.

1) Create images that contains icon and text both

Even you are not a graphic designer; there is an easy way to do this. Just add the custom button (with title) to another test entity and get a screen print which will give you the chance of creating a nice image that got the CRM look and feel. Then load this to usual CRM image folder. (Program Files\Microsoft Dynamics CRM\CRMWeb\_imgs)


2) Change ISV entry

Now you need to do two changes to ISV entry as below.

<!--Original -->
<Button JavaScript="ReadySale();" Icon="/_imgs/ico_18_sales.gif" PassParams="1">
  <Titles>
     <Title LCID="1033" Text="Ready for Sale" />
  </Titles>
  <ToolTips>
     <ToolTip LCID="1033" Text="Ready for Sale" />
  </ToolTips>
</Button>

<!--Changed entry -->
<Button JavaScript="ReadySale();" Icon="/_imgs/ico_18_sales_EXT.gif" PassParams="1">
   <ToolTips>
        <ToolTip LCID="1033" Text="Ready for Sale" />
   </ToolTips>
</Button>

You need to replace the image name with your new one and remove the title tag. Obviously, if you keep the title tag, you will see the title twice if form toolbar stretches with enough space.

3) Ignore style of the button on loading

Now we should understand that CRM styles are applied to each and every component of the toolbar. So usually it doesn’t allow you to keep long text along with the icon. So you need to remove relevant style from the tag in onload event.

var _list = document.getElementsByTagName('LI');
var m = 0;
while (m < _list.length)
{
  if (_list[m].getAttribute('title') == 'Ready for Sale') 
  {
   var str = _list[m].outerHTML;
   var str1 = str.replace("ms-crm-Menu-ButtonFirst", "");
   _list[m].outerHTML = unescape(str1);
  }
  m = m + 1;
}

Mission accomplished!

Handle Save, Save & close and unload of CRM form

This is a tricky situation one can come across in CRM 4.0 development. Leaving the form can happen in three ways. Those are SAVE, SAVE AND CLOSE, UNLOAD
We can write JavaScript for all three occasions.

UNLOAD

If you put below code in onLoad event, you can execute a script on unload.

// Put this in onload
window.onbeforeunload = function()
{
    alert("unload event")      
}

SAVE & SAVE AND CLOSE

Saving is straight forward in CRM form, but you can distinguish two ways of saving by event mode. Try below script in save event.

var _mode = event.Mode;
alert("Save event .. with mode : " +_mode);

Different modes could be easily distinguished as blow.



SAVE - 1
SAVE AND CLOSE – 2

Now my real focus is to do something when we leave a form with any of above three methods. Best example would be Quote entity can have total fields which depend on quotedetail entities under it (quotedetai is a child of quote). If we need them to be accurate at any given moment... we might need to do the calculations (unless we do it in plug-ins) whenever any of above events occur.

Way above event occurs are quite messy.
Please check what I found. Different combination of events trigger depends on circumstance.

SAVE (without change to form data) - Save event
SAVE (with change to form data) - save event, unload event
SAVE AND CLOSE (without change to form data) - save event, unload event

SAVE AND CLOSE (with change to form data) - save event, unload event

So, if we place the calculation event in all the three events, nothing will go wrong but in some occasions, it will run twice. Still I don’t know how to solve this simply. What we could do is maintaining a hidden check box (Boolean field) to say whether form needs recalculation. We should be smart enough to update this field even when child records are changed, if they influence the calculations.

Then we can add our own calculation methods to all three events, but calculation should always check the Boolean field before execution. In this way, we can avoid happening of calculation twice for no reason.

I am glad to hear if someone got better approach for this.