Thursday, February 16, 2012

Custom Lookup with multiple choices in CRM 2011

If you create custom lookup in CRM, this lookup is oriented only for one entity.
I want to add possibility to select from some entities in one lookup.

I have custom lookup "originatingleadid". This lookup is oriented on Lead entity. I want to select from views of three different entities: Lead, Contact, User.

For this I've done some customization in opportunity entity, I've created three hidden text fields:
new_type - to save lookup type
new_guid - to save lookup guid
new_name - to save lookup name.
That is all customization.

I've developed three javascript function:

1. function to handle OnChange event on lookup.


function onchange()
{
var originatingLeadValue = Xrm.Page.getAttribute("originatingleadid").getValue();
if (IsNull(originatingLeadValue) || originatingLeadValue[0].type == "4")
{
// Lookup is null or there is a Lead, need to clear hidden fields. To clear one of them will be enough, we will be check this field during onload.
Xrm.Page.getAttribute("new_guid").setValue(null);
}
else
{
// Lookup contains Contact or User, so need to store lookup properties to our hidden fields.
Xrm.Page.getAttribute("new_type").setValue(originatingLeadValue[0].type);
Xrm.Page.getAttribute("new_guid").setValue(originatingLeadValue[0].id);
Xrm.Page.getAttribute("new_name").setValue(originatingLeadValue[0].name);
}
}

2. function to handle OnSave event on Opportuniry Form.

function onsave()
{
var originatingLeadValue = Xrm.Page.getAttribute("originatingleadid").getValue();
if (!IsNull(originatingLeadValue) && originatingLeadValue[0].type != '4')
{
Xrm.Page.getAttribute("originatingleadid").setValue(null);
}
}
 3. function to handle OnLoad event on Opportunity Form.

function onload()
{
lookuptypeIcons = '/_imgs/ico_16_2.gif:/_imgs/ico_16_4.gif:/_imgs/ico_16_8.gif';
lookuptypenames = 'contact:2:Contact,lead:4:Lead,systemuser:8:User';
lookuptypes = '2,4,8';
var savedId = Xrm.Page.getAttribute("new_guid").getValue();
var savedType = Xrm.Page.getAttribute("new_type").getValue();
var savedName = Xrm.Page.getAttribute("new_name").getValue();
var savedEntityName = savedType == "8" ? "systemuser" : "contact";
document.getElementById("originatingleadid").setAttribute("lookuptypes", lookuptypes);
document.getElementById("originatingleadid").setAttribute("lookuptypenames", lookuptypenames);
document.getElementById("originatingleadid").setAttribute("lookuptypeIcons", lookuptypeIcons);
document.getElementById("originatingleadid").setAttribute("defaulttype", "4"); // default type - Lead entity
var originatingLeadValue = Xrm.Page.getAttribute("originatingleadid").getValue();
if (IsNull(originatingLeadValue) && !IsNull(savedId))
{
var value = new Array();
value[0] = new Object();
value[0].displayClass = "ms-crm-Lookup-Item";
value[0].keyValues = new Object();
value[0].values = new Object();
value[0].onclick = "openlui()";
value[0].id = savedId;
value[0].entityType = savedEntityName;
value[0].typename = savedEntityName;
value[0].name = savedName;
value[0].type = savedType;
Xrm.Page.getAttribute("originatingleadid").setValue(value);
}
}
So, now you can select from  three different entity in one lookup :-)
Custom Lookup view


Monday, November 14, 2011

Status Duration


if you're statistics of your opportunities that counting status duration can be very useful for you.
Status Duration - this is how many days opportunity is the current status.


Add new field on opportunity form "new_statusduration" to set Status Duration value.

function getDuration() {
 var thetime = new Date();
 var oneday = 1000 * 60 * 60 * 24;
 var currentMonth = thetime.getMonth() + 1;
 var currentYear = thetime.getFullYear();
 var currentDay = thetime.getDate();
 var currentdate = currentMonth + "/" + currentDay + "/" + currentYear;
 var newdate = new Date(currentdate);



 if (Xrm.Page.getAttribute("opportunityratingcode").getValue() == null) {
  Xrm.Page.getAttribute("new_statusduration").setValue(null);
 }
 else {
  var statusduration = "";
  var xml = "";
  xml += '';
  xml += '';
  xml += '';
  xml += '';
  xml += '';
  xml += '';
  xml += '';
  xml += ''
  xml += '';
  xml += '';
  xml += '';



  var doc = Fetch(xml);
  var nodes = doc.selectNodes("//resultset/result/createdon");
  if (nodes[0] != null) {
   var statusnode = nodes[0].getAttribute("date");
   var statusdate = new Date(statusnode);
   var duration = Math.round((newdate - statusdate) / oneday);
   if (duration < 1) {
    statusduration = duration + " d";
   }
   else {
    statusduration = Math.floor(duration / 30) + " m " + (duration % 30) + " d";
   }
   Xrm.Page.getAttribute("new_statusduration").setSubmitMode("always");
   Xrm.Page.getAttribute("new_statusduration").setValue(statusduration);
  }
 }
}
To use this script you need "Fetch" function
function Fetch(xml) {
 var Xml = "";
 Xml += GenerateAuthenticationHeader();
 Xml += "";
 Xml += "";
 Xml += "";
 Xml += CrmEncodeDecode.CrmXmlEncode(xml); // Microsoft _HtmlEncode function
 Xml += "";
 Xml += "";
 Xml += "";
 Xml += "";
 var XmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); // Microsot CreateXmlHttp function
 XmlHttp.open("POST", "/mscrmservices/2007/crmservice.asmx", false); //Sync Request
 XmlHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
 XmlHttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Fetch");
 XmlHttp.send(Xml);
 var XmlDoc = new ActiveXObject("Msxml2.DOMDocument");
 XmlDoc.async = false;
 XmlDoc.resolveExternals = false;
 XmlDoc.loadXML(XmlHttp.responseXML.text);
 return XmlDoc;
}
Result:
Don't forget enable auditing for Opportunity entity!

Tuesday, November 8, 2011

Don't allow complete appointment unless "Status" is empty

You can don't allow close appointment as completed or cancelled unless user don't specify necessary field. 
Use following script:
function closeAppointment()
{
 var CRM_FORM_SAVE_MODE_SAVEASCOMPLETED = 58;
 var CRM_FORM_SAVE_MODE_CLOSE = 5;
 if ((event.Mode == CRM_FORM_SAVE_MODE_SAVEASCOMPLETED) ||
    (event.Mode == CRM_FORM_SAVE_MODE_CLOSE))
 {
  var qualifiedstatus = Xrm.Page.getAttribute("new_qualifiedstatus");
  if (!IsNull(qualifiedstatus))
  {
   var option = qualifiedstatus.getSelectedOption();
   if (IsNull(option) || IsNull(option.value) || IsNull(option.text))
   {
    alert("Please provide a value for the Qualified Status!");
    document.all.new_qualifiedstatus.focus();
    event.returnValue = false;
    return false;
   }
  }
 }
}

Hide control "Est. Revenue" on Opportunity form

If you want to hide "Est. Revenue" field for all except special CRM role and owner of this opportunity you can use following javascript code:
function GetCurrentUserRoles()
{
 var xml = "" +
"" +
"" +
GenerateAuthenticationHeader() +
" " +
" " +
" " +
" role" +
" " +
" " +
" name" +
" " +
" " +
" false" +
" " +
" " +
" roleid" +
" role" +
" systemuserroles" +
" roleid" +
" Inner" +
" " +
" " +
" systemuserid" +
" systemuserroles" +
" systemuser" +
" systemuserid" +
" Inner" +
" " +
" And" +
" " +
" " +
" systemuserid" +
" EqualUserId" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
"" +
"";
 var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
 xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
 xmlHttpRequest.setRequestHeader("SOAPAction", " http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
 xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
 xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
 xmlHttpRequest.send(xml);
 var resultXml = xmlHttpRequest.responseXML;
 return (resultXml);
}


function hideRevenue()
{
 UserID = Xrm.Page.context.getUserId();
 OwnerID = Xrm.Page.getAttribute("ownerid").getValue()[0].id;
 if (UserHasRole("Boss") || UserID == OwnerID)
 {
  Xrm.Page.ui.controls.get("estimatedvalue").setVisible(true);
 }
 else
 {
  Xrm.Page.ui.controls.get("estimatedvalue").setVisible(false);
 }
}

Set disabled opportunity "Close as Won" button unless probability is not 100 or status is not "A"

To prevent accidental closing of opportunity I present to you method how can set disabled opportunity "Close as Won" button unless probability is not 100% or status is not "A". 

First of all you should export solution with your "Opportunity" entity customization.
Unzipp you solution and open "customizations" file with any XML editor.
Find in this file <RibbonDiffXml> tag and replace its content with the following xml: 

<RibbonDiffXml>
         <CustomActions>
          <CustomAction Id="ABC.Mscrm.HomepageGrid.opportunity.MainTab.Actions.Controls" Location="Mscrm.HomepageGrid.opportunity.MainTab.Actions.Controls._children">
            <CommandUIDefinition>
              <Button Id="Mscrm.HomepageGrid.opportunity.MarkAsWon" Command="Mscrm.HomepageGrid.opportunity.MarkAsWon" Sequence="5" Alt="$Resources:Ribbon.Form.opportunity.MainTab.Actions.MarkAsWon" LabelText="$Resources:Ribbon.Form.opportunity.MainTab.Actions.MarkAsWon" Image16by16="/_imgs/SFA/MarkAsWon_16.png" Image32by32="/_imgs/SFA/MarkAsWon_32.png" TemplateAlias="o1" ToolTipTitle="$Resources:Mscrm_HomepageGrid_opportunity_MainTab_Actions_MarkAsWon_ToolTipTitle" ToolTipDescription="$Resources:Mscrm_HomepageGrid_opportunity_MainTab_Actions_MarkAsWon_ToolTipDescription" />
            </CommandUIDefinition>
          </CustomAction>
          <CustomAction Id="ABC.Mscrm.Form.opportunity.MainTab.Actions.Controls" Location="Mscrm.Form.opportunity.MainTab.Actions.Controls._children">
            <CommandUIDefinition>
              <Button Id="Mscrm.Form.opportunity.MarkAsWon" Command="Mscrm.Form.opportunity.MarkAsWon" Sequence="3" Alt="$Resources:Ribbon.Form.opportunity.MainTab.Actions.MarkAsWon" LabelText="$Resources:Ribbon.Form.opportunity.MainTab.Actions.MarkAsWon" Image16by16="/_imgs/SFA/MarkAsWon_16.png" Image32by32="/_imgs/SFA/MarkAsWon_32.png" TemplateAlias="o1" ToolTipTitle="$Resources:Mscrm_Form_opportunity_MainTab_Actions_MarkAsWon_ToolTipTitle" ToolTipDescription="$Resources:Mscrm_Form_opportunity_MainTab_Actions_MarkAsWon_ToolTipDescription" />
            </CommandUIDefinition>
          </CustomAction>
        </CustomActions>
        <Templates>
          <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
        </Templates>
        <CommandDefinitions>
          <CommandDefinition Id="Mscrm.Form.opportunity.MarkAsWon">
            <EnableRules>
              <EnableRule Id="ABC.Mscrm.CheckImportCustomizationRule" />
            </EnableRules>
            <DisplayRules />
            <Actions />
          </CommandDefinition>
          <CommandDefinition Id="Mscrm.HomepageGrid.opportunity.MarkAsWon">
            <EnableRules>
              <EnableRule Id="ABC.Mscrm.CheckImportCustomizationRule" />
            </EnableRules>
            <DisplayRules />
            <Actions />
          </CommandDefinition>
        </CommandDefinitions>
     <RuleDefinitions>
  <TabDisplayRules />
  <DisplayRules />
    <EnableRules>
    <EnableRule Id="ABC.Mscrm.CheckImportCustomizationRule">
    <OrRule>
  <Or>
     <ValueRule
Field="closeprobability"
Value="100"
Default="0" />
</Or>
  <Or>
     <ValueRule
Field="opportunityratingcode"
Value="1"
Default="0" />
</Or>
</OrRule>
    </EnableRule>
    </EnableRules>
  </RuleDefinitions>
        <LocLabels />
 </RibbonDiffXml> 
Result: