2009-12-21

Upgrade of javascript function to hide "Add Existing ..." button

I improved the source code for one of my previous post — Remove 'Add Existing...' button from associated view entities form.

function HideAssociatedViewButtons(areaPrefix, loadAreaId, buttonTitles,addAreaParams){
 var navElement = document.getElementById(areaPrefix+loadAreaId); 
   if (navElement != null)    {
        navElement.onclick = function LoadAreaOverride(){
            if(addAreaParams.length>0)
    loadArea('area'+loadAreaId, addAreaParams[0]);
   else
    loadArea('area'+loadAreaId);
            var iframeid='area' + loadAreaId + 'Frame';
   HideViewButtons(document.getElementById(iframeid), buttonTitles);
        }
    }
}
 
function HideViewButtons(Iframe, buttonTitles) { 
    if (Iframe != null ) {
  Iframe.onreadystatechange = function HideTitledButtons() { 
            if (Iframe.readyState == 'complete') { 
                var iFrame = frames[window.event.srcElement.id]; 
                var liElements = iFrame.document.getElementsByTagName('li');
 
    for (var j = 0; j < buttonTitles.length; j++) { 
                    for (var i = 0; i < liElements.length; i++) { 
                        if (liElements[i].getAttribute('title') == buttonTitles[j]) { 
                            liElements[i].style.display = 'none'; 
                            break; 
                        }
                    } 
                } 
            } 
        } 
    }
} 

2009-12-03

On form load set some selected value for related entity pick-list

I had to set the value for related entity pick-list — filter of date for Activities of some account, like it was default Selected value.

SelectArea('Activities', 'scheduledend');
SelectArea('ActivityHistory', 'actualend');

function SelectArea(loadAreaId, selectId)
{
var navElement = document.getElementById('nav'+loadAreaId); 
if (navElement != null) 
{
 navElement.onclick = function LoadAreaOverride() {
  loadArea('area'+loadAreaId);
  var iframeid = 'area'+loadAreaId + 'Frame';
  var Iframe=document.getElementById(iframeid);
  if (Iframe != null ) {
   Iframe.onreadystatechange = function SetAllasDefault() { 
    if (Iframe.readyState == 'complete') { 
     var iFrame = frames[window.event.srcElement.id]; 
     var oSelect = iFrame.document.getElementById(selectId);
     var oOption = oSelect.options[oSelect.options.length-1];
     oOption.selected = true;
     oSelect.FireOnChange();
    }
   }
  }
 
  
 }
}
}
Put at form OnLoad() event;

2009-11-13

Passing parameters between MS CRM plugins

I was needed to pass a variable from one plugin to another. If you have a two plugin at pre and post stage registered for on entity, then you can use a SharedVariables context property. But, if every plugins is a different assembly, and they are registered for a different entities... So, when the first plugin is firing, the second, which is starting after that, needed to know some information for redirecting it logic to another way.

I had created some common assembly with helper methods and called it "Helper". Then I had created a static class with static variable and had put the value into it, at the first plugin execution runtime. In the second plugin I just get the value from the static common variable.

And nothing about read\write DB operations.
Helper assembly.
namespace Helper
{
    public static class Keeper
    {
        private static bool _UpdateOppAfterHistory = true;
        public static bool UpdateOppAfterHistory 
        {
            get { return _UpdateOppAfterHistory; }
            set { _UpdateOppAfterHistory = value; }
        }
        
    }
    public class OpportunityHelper
    { ... }
}

First firing plugin code
Keeper.UpdateOppAfterHistory = false;

OpportunityHelper.CreateOpportunityHistory(Opp,crmService);
                
// removing the flag to the back
Keeper.UpdateOppAfterHistory = true;

Second firing plugin code
// common shared variable analysis
bool UpdateOppAfterHistory = Keeper.UpdateOppAfterHistory;
if (UpdateOppAfterHistory)
{ ... }

2009-11-10

How to hide an items of drop-down list?

"We should not be able to choose some steps of Opportunity History, if the opportunity has no quotes or sales orders." - that was they said.

And that was I wrote.

var oService = null;
var oppid = null;

function FormOnLoad()
{
 oService = new Ascentium_CrmService(null, null);
 
 var oParentCrmForm = window.opener.parent.document.all.crmForm;
 if(oParentCrmForm)
 { 
  oppid = oParentCrmForm.ObjectId;
 }
 
 HideComboboxItems();
}


window.HideComboboxItems = function()
{
var newstep = document.getElementById("new_step");
if(newstep)
{
 // checking opportunity for quotes
 var sFetchXml ='<fetch mapping="logical" aggregate="true" version="1.0"><entity name="quote"><attribute name="quoteid" aggregate="count" alias="count" />'+
  '<filter><condition attribute="opportunityid" operator="eq" value="'+oppid+'" /></filter></entity></fetch>';
 
 var aoFetchResult = oService.Fetch(sFetchXml); 
 var quoteCount = 0;

    if(aoFetchResult.length > 0)
    {
       quoteCount = aoFetchResult[0].attributes["count"].value;
    }
 
 // if no qoutes, then hiding items 4,5,6,7 of dropdown list
 if(quoteCount == 0 )
 {
  var allDropDownElements = newstep.childNodes;
  var lastnodevalue = allDropDownElements.length;
  for(var i=lastnodevalue; i >= 4; i--)
  {
    newstep.options.remove(i);
  }
 }
 else
 {
  // if there is some qoutes, then checking for salesorders
  var sFetchXml ='&lr;fetch mapping="logical" aggregate="true" version="1.0"><entity name="salesorder"><attribute name="salesorderid" aggregate="count" alias="count" />'+
   '<filter><condition attribute="opportunityid" operator="eq" value="'+oppid+'" /></filter></entity></fetch>';
  
  var aoFetchResult = oService.Fetch(sFetchXml); 
  var SOCount = 0;

  if(aoFetchResult.length > 0)
  {
     SOCount = aoFetchResult[0].attributes["count"].value;
  }
  
  // if no salesorders, then hiding items 6,7
  if(SOCount == 0)
  {
   var allDropDownElements = newstep.childNodes;
   var lastnodevalue = allDropDownElements.length;
   for(var i=lastnodevalue; i >=6; i--)
   {
     newstep.options.remove(i);
   }
  }
 }
 
}
}

2009-11-09

Salesorder Fulfill state

I was trying to catch the Fulfill state of sales order and my try was successful, with registering plugin to Update message at Child pipeline. But there was a one little problem - you can't get the salesorderdetail thru service. I don't know why, but the simple code like in "Example 1" was falling down with Generic SQL error x80044150.

//  Example 1
QueryByAttribute qba = new QueryByAttribute();
qba.EntityName = EntityName.salesorderdetail.ToString();
qba.ColumnSet = new AllColumns();
qba.Attributes = new string[] { "salesorderid" };
qba.Values = new object[] { soid };
BusinessEntityCollection bec = crmService.RetrieveMultiple(qba);


Then I was switch on the Fulfill Sdk Message and everything start working fine!
1. open up the SdkMessage view, filter by Name and find the SdkMessageId
2. open up the SdkMessageFilter view, filter by SdkMessageId you got from step 1.
3. change IsCustomProcessingStepAllowed to 'True'

update dbo.SdkMessageFilter
set IsCustomProcessingStepAllowed=1
where SdkMessageId=
(select top 1 SdkMessageId from dbo.SdkMessage where name ='Fulfill')

I have a question, why the Fulfill message has been unplugged from customization?

2009-11-02

Parse XML date in CRM date with Javascript

A JavaScript function to parse an XML (ISO-8601) date string (e.g., "2008-01-18") that returns a JavaScript Date object.

// parsing
function parseDate(xmlDate)
{
      if (!/^[0-9]{4}\-[0-9]{2}\-[0-9]{2}/.test(xmlDate)) {
           throw new RangeError("xmlDate must be in ISO-8601 format YYYY-MM-DD.");
      }
      return new Date(xmlDate.substring(0,4), xmlDate.substring(5,7)-1, xmlDate.substring(8,10));
}

// and using
if(retrievedOpp.attributes["estimatedclosedate"] != null && retrievedOpp.attributes["estimatedclosedate"] != undefined){
     crmForm.all.new_estimatedclosedate.DataValue = parseDate(retrievedOpp.attributes["estimatedclosedate"].value);}

Source http://dev.ektron.com/blogs.aspx?id=14140

2009-10-29

How to determine on which pipeline a plugin is executing - Parent or Childe

The InvocationSource property is an integer value that you can use to determine whether the current plug-in is running in a child pipeline.

MessageInvocationSource Values
FieldValueDescription
Child 1 Specifies a child pipeline
Parent 0 Specifies a parent pipeline

2009-10-20

MS CRM Fetch and Retrieve from javascript

This code snippet was writing exactly like recommended in MSCRM SDK 4.09.
I was put it in OnLoad() event of my account form, and in the lookup field(at my example - new_postalcodeid) at OnChange() event I was calling my function.

There is two function that are doing the same work but with different methods.

var authenticationHeader = GenerateAuthenticationHeader();
crmForm.all.address1_city.ForceSubmit = true;

window.GetRegionByIndex_Retrieve = function()  
{  
 
if (crmForm.all.new_postalcodeid.DataValue == null)
{
  crmForm.all.address1_city.Disabled = false;
 crmForm.all.address1_city.DataValue = "";
 return;
}

var lookupItem = new Array;

lookupItem = crmForm.all.new_postalcodeid.DataValue;
if(lookupItem[0] != null)
{
 // Prepare the SOAP message.
 var xml = "<?xml version='1.0' encoding='utf-8'?>"+ 
 "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'"+
 " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"+
 " xmlns:xsd='http://www.w3.org/2001/XMLSchema'>"+ 
 authenticationHeader+ 
 "<soap:Body>"+ 
 "<Retrieve xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"+ 
 "<entityName>new_postalcode</entityName>"+ 
 "<id>"+lookupItem[0].id+"</id>"+ 
 "<columnSet xmlns:q1='http://schemas.microsoft.com/crm/2006/Query' xsi:type='q1:ColumnSet'>"+ 
 "<q1:Attributes>"+ 
 "<q1:Attribute>new_regcityarea</q1:Attribute>"+ 
 "</q1:Attributes>"+ 
 "</columnSet>"+ 
 "</Retrieve>"+ 
 "</soap:Body>"+ 
 "</soap:Envelope>";
 // Prepare the xmlHttpObject and send the request.
 var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
 xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
 xHReq.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Retrieve");
 xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
 xHReq.setRequestHeader("Content-Length", xml.length);
 xHReq.send(xml);
 // Capture the result.
 var resultXml = xHReq.responseXML;

 // Check for errors.
 var errorCount = resultXml.selectNodes('//error').length;
 if (errorCount != 0)
 {
 var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
 alert(msg);
 }
 // Display the retrieved value.
 else
 {
 crmForm.all.address1_city.DataValue = resultXml.selectSingleNode("//q1:new_regcityarea").nodeTypedValue;
 crmForm.all.address1_city.Disabled = true;
 }
}
}



window.GetRegionByIndex_Fetch = function (){
if (crmForm.all.new_postalcodeid.DataValue == null)
{
  crmForm.all.address1_city.Disabled = false;
 crmForm.all.address1_city.DataValue = "";
 return;
}

var lookupItem = new Array;

lookupItem = crmForm.all.new_postalcodeid.DataValue;
if(lookupItem[0] != null)
{
 var xml = "<?xml version='1.0' encoding='utf-8'?>" +
 "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'" +
 " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'" +
 " xmlns:xsd='http://www.w3.org/2001/XMLSchema'>" +
 authenticationHeader +
 "<soap:Body>" +
 "<Fetch xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>" +
 "<fetchXml>" +
 "&lt;fetch mapping='logical'&gt;" +
 "&lt;entity name='new_postalcode'&gt;" +
 "&lt;attribute name='new_postalcodeid'/&gt;"+
 "&lt;attribute name='new_regcityarea'/&gt;"+
 "&lt;filter type='and'&gt;"+
 "&lt;condition attribute='new_postalcodeid' operator='eq' value='"+lookupItem[0].id+"'/&gt;"+
 "&lt;/filter&gt;&lt;/entity&gt;&lt;/fetch&gt;" +
 "</fetchXml>" +
 "</Fetch>" +
 " </soap:Body>" +
 "</soap:Envelope>";

 var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
 xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
 xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Fetch");
 xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
 xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
 xmlHttpRequest.send(xml);
 
 var resultXml = xmlHttpRequest.responseXML;
 // Check for errors.
 var errorCount = resultXml.selectNodes('//error').length;
 if (errorCount != 0)
 {
  var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
  alert(msg);
 }
 // Process and display the results.
 else
 {

  // Capture the result and UnEncode it.
  var resultSet = new String();
  resultSet = resultXml.text;
  resultSet.replace('&lt;','<');
  resultSet.replace('&gt;','>');

  var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
  xmlDoc.async = false;
  xmlDoc.loadXML(resultSet);
  var results = xmlDoc.getElementsByTagName('result');
  
  for(i=0;i<results.length;i++)
  {
   crmForm.all.address1_city.DataValue = results[i].selectSingleNode('//new_regcityarea').nodeTypedValue;
  }
   crmForm.all.address1_city.Disabled = true;
 }
}
}

2009-10-13

How to retrieve from many-to-many entity.

If you need to retrieve something from many-to-many relationship entity, and there is no such entity in CRM, then you should use the FetchXml, instead of Query.
// Base bodel of FetchXml for get relationship entity
string fetchXML =
"<fetch distinct='false' mapping='logical'>" +
"<entity name='" + relationshipName + "'><filter type='and'>" +
"<condition attribute='" + relatedEntityName + "id' operator='eq' value='" + relatedId + "' />" +
"<condition attribute='" + typeEntityName + "id' operator='eq' value='" + profileId + "' />" +
"</filter></entity>" + "</fetch>";

For example, next code snippet is checking a count of relations between the Account entity and New_Industry entity, wich relationship is many-to-many. To achieve this, I need to check the relationship entity - New_industry_account, wich is contains all of relation of our two entities.
ICrmService crmService = context.CreateCrmService(true);

// check for relation
#region FetchXML
string fetchXML =
"<fetch distinct='false' mapping='logical'>" +
"<entity name='new_industry_account'><filter type='and'>" +
"<condition attribute='accountid' operator='eq' value='" + accountid.ToString() + "' />" +
"</filter></entity></fetch>";
#endregion

string fetchresult = crmService.Fetch(fetchXML);
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(fetchresult);
XmlNodeList xnodlist = xmldoc.SelectNodes("resultset/result");
if (xnodlist.Count > 0) { return; }

Update a number of custom attributes of DynamicEntity with TargetUpdateDynamic.

This code I was used in plugins for updating the custom attributes using Microsoft.Crm.SDK.

using System;
using System.Collections.Generic;
using System.Collections;

// Microsoft Dynamics CRM namespaces
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;

...

// Extract the DynamicEntity from the request.
entity = (DynamicEntity)retrieved.BusinessEntity;

// declare a property array
ArrayList arrProps = new ArrayList();

// create some property
CrmBooleanProperty new_withindustry = new CrmBooleanProperty();
new_withindustry.Name="new_withindustry";
new_withindustry.Value= new CrmBoolean();
new_withindustry.Value.Value=true;
arrProps.Add(new_withindustry);

// create another property
CrmMoneyProperty new_industrydeal = new CrmMoneyProperty();
new_industrydeal.Name = "new_industrydeal";
new_industrydeal.Value = new CrmMoney();
new_industrydeal.Value.Value = 12345.0m;
arrProps.Add(new_industrydeal);

//   Update the properties array on the DynamicEntity.
entity.Properties.AddRange((Property[])arrProps.ToArray(typeof(Property)));

// Create the update target.
TargetUpdateDynamic updateDynamic = new TargetUpdateDynamic();

// Set the properties of the target.
updateDynamic.Entity = entity;

//   Create the update request object.
UpdateRequest update = new UpdateRequest();

//   Set request properties.
update.Target = updateDynamic;

//   Execute the request.
UpdateResponse updated = (UpdateResponse)crmService.Execute(update);

Dynamic hyperlink in Reporting Services 2008

I had a custom entity - project and was needed to open a project card from the report.
So I had done exactly as saying in http://msdn.microsoft.com/en-us/library/ms157159.aspx and in the expression field of hyperlink I was typed next

="http://SERVERNAME/ORGANIZATIONNAME/userdefined/edit.aspx?id=%7b" & Fields!projectid.Value.ToString() & "%7d&etc=10005"

SERVERNAME and ORGANIZATIONNAME must be replaced with your crm values.

2009-10-12

Registering plugins for event on many-to-many entities

I found that from the box MS CRM 4.0 are not supported the events on many-to-many relationship entities(bridge entities). But Aaron Elder has hack it.

All you need is just register it at AssociateEntities or DiassociateEntities event with empty Primary and Secondary Entity fields.

-- ============================================================================
-- Enable Associate and Disassociate Plug-in Events Script v1.0
-- ----------------------------------------------------------------------------
-- (c) 2009 Aaron Elder
-- ============================================================================
-- DISCLAIMER:
-- This script is provided "AS IS" with no warranties, and confers no rights.
-- ============================================================================
-- While this is obviously "unsupported", I think the fact that these events
-- are not available is a bug and hopefully it will be fixed in a rollup.
-- ============================================================================

USE AscentiumCrmDev_MSCRM
GO

-- Find the deployments SDK Filter ID for the
-- Associate and Disassociate Entity SDK Messages
DECLARE @DisassociateEntitiesFilterId uniqueidentifier
DECLARE @AssociateEntitiesFilterId uniqueidentifier
SET @DisassociateEntitiesFilterId = (SELECT SdkMessageId FROM SdkMessageBase WHERE [Name] = 'DisassociateEntities')
SET @AssociateEntitiesFilterId = (SELECT SdkMessageId FROM SdkMessageBase WHERE [Name] = 'AssociateEntities')

-- Enable the Associate and Disassociate Filters to be valid for custom processing
-- Custom Processing means "you register plug-ins against it"
-- Note: We only do this for the "generic" (OTC == 0) case, just to be safer
UPDATE SdkMessageFilterBase SET IsCustomProcessingStepAllowed = 1
       WHERE SdkMessageId = @DisassociateEntitiesFilterId AND PrimaryObjectTypeCode = 0


UPDATE SdkMessageFilterBase SET IsCustomProcessingStepAllowed = 1
       WHERE SdkMessageId = @AssociateEntitiesFilterId AND PrimaryObjectTypeCode = 0
(C)
http://consulting.ascentium.com/blog/crm/Post533.aspx

2009-10-07

Window redirect and close

This was the window that was opened from CRM form menu (ISV.Config.xml). The window has received some parameters and then redirected to another window, which is preparing and opening the MS Word document, but the pop-up window was still open - this is rude. So, I've closed it.

// call this code on body onload event
function redirect()
{
    // needed URL of pop-up window or modify current location URL
    window.opener.location = window.location.href.replace(/Default2/, "Default");
    window.top.close();
}

Close window from server-side

// closethe page if successful update
RegisterStartupScript("load", "<" + "script type=\"text/javascript\">\n" +
"self.close();\n" +
"<" + "/script>");

Dim strscript As String = "<"+"script language=javascript>window.top.close();<"+"/script>"
If (Not ClientScript.IsStartupScriptRegistered("clientScript")) Then
ClientScript.RegisterStartupScript(Page.GetType(), "clientScript", strscript)
End If

System.Text.StringBuilder sb = System.Text.StringBuilder ();

sb.Append("");

if(!IsClientScriptRegistered("myScript",sb.ToString())
   this.RegisterClientScriptBlock("myScript",sb.ToString());

All of code was found in Internet

2009-10-05

Mask the phone fields

I was need some mask on the "mobilephone" field in contact card. So I was combine some google search result with my experience and got next code:
//base maskphone function 
window.applyMask = function()
{
    // var of fields wich must be masked
    var ele = document.getElementById("mobilephone");

var FormatPhoneHandler=function()
    {
      var PH = this.value;
      var re = /\D/g;
      // +7 - this is country code of Russia, so this is decision only for one country
      PH = PH.replace("+7","");
      PH = PH.replace(re,"");
     
      if(PH.length <1)
      {
          this.value = "";
      }
      else if(PH.length <= 3)
      {
            this.value = "+7 (" + PH;
      }
      else if(PH.length > 3 && PH.length <= 6)
      {
            this.value = "+7 (" + PH.substring(0,3) + ") " + PH.substring(3,6);
      }
      else if(PH.length > 6)
      {
            this.value = "+7 (" + PH.substring(0,3) + ") " + PH.substring(3,6) + "-" + PH.substring(6,10);
      }
    }

    ele.onkeyup = FormatPhoneHandler;
}

// turn it on
applyMask();

Place this code in onLoad event in the entity wich you need and don't forget change names of the field for the right one.

2009-08-20

Embeded notification

We were needed some kind of notify message for user without any pop-up or an alerts.


function CreateRequestObject() {
if (window.XMLHttpRequest) {
try {
return new XMLHttpRequest();
} catch (e) { }
} else if (window.ActiveXObject) {
try {
return new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
try {
return new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) { }
}
}
return null;
}

function RetrieveEntity(sEntityName, GUID, sAttributeNames) {
var attributes = "";
if (sAttributeNames.length == 0)
return null;
for (var i = 0; i < sAttributeNames.length; i++) {
attributes += "" + sAttributeNames[i] + "";
}
var soapBody = "" +
"" +
"" + sEntityName + "" +
"" + GUID + "" +
"" +
"" + attributes + "" +
"
" +
"
" +
"
";
var soapXml = " "xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' " +
"xmlns:xsd='http://www.w3.org/2001/XMLSchema' " +
"xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>";
soapXml += GenerateAuthenticationHeader();
soapXml += soapBody;
soapXml += "
";

xmlhttp = CreateRequestObject();
xmlhttp.open("POST", "/mscrmservices/2007/crmservice.asmx", false);
xmlhttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlhttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Retrieve");
xmlhttp.setRequestHeader("Content-Length", soapXml.length);
xmlhttp.send(soapXml);

// Capture the result.
var resultXml = xmlhttp.responseXML;
// Check for errors.
var errorCount = resultXml.selectNodes('//error').length;
if (errorCount != 0) {
var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
alert(msg);
return null;
}
// Display the retrieved value.
else {
var retEntityValues = new Array();
var retrieveNodes = resultXml.selectNodes("soap:Envelope/soap:Body/RetrieveResponse/RetrieveResult/q1:*");
for (var i = 0; i < retrieveNodes.length; i++) {
var nodeName = retrieveNodes[0].baseName;
var nodeValue = retrieveNodes[0].nodeTypedValue;
retEntityValues[nodeName] = nodeValue;
}
return retEntityValues
}
}

function AddNotification(noticationId, text) {
if (crmForm.all.Notifications.all[noticationId] != null)
crmForm.all.Notifications.all[noticationId + '_text'].innerHTML = text;
else {
crmForm.all.Notifications.innerHTML += '
' + text + '
';
crmForm.all.Notifications.style.display = 'block';
}
}

function RemoveNotification(noticationId) {
if (crmForm.all.Notifications.all[noticationId] != null) {
crmForm.all.Notifications.all[noticationId].removeNode(true);
if (crmForm.all.Notifications.childNodes.length == 0)
crmForm.all.Notifications.style.display = 'none';
}
}

var customer = crmForm.all.customerid;
if (customer.DataValue != null && customer.DataValue[0].typename == 'account') {
var cs = new Array();
cs[0] = 'new_attention';
var acc = RetrieveEntity('account', customer.DataValue[0].id, cs);
if (acc != null && acc['new_attention'] == 1) {
AddNotification('NotificationAccount', 'Клиент требует внимания! Для более подробной информации перейдите в карточку клиента.');
crmForm.all.customerid_d.getElementsByTagName('SPAN')[0].style.color = 'Red';
}
else {
RemoveNotification('NotificationAccount');
}
}
else {
RemoveNotification('NotificationAccount');
}

2009-08-19

Get contact status in custom extended marketing list


declare @contactid uniqueidentifier
declare @campaignactivityid uniqueidentifier
-- some Guids
set @contactid='48CF39EE-AA8C-DE11-A526-000C2989FA53'
set @campaignactivityid='72CE8156-AD8C-DE11-A526-000C2989FA53'

select exL.New_ExtMarketingListsElementId, exL.New_contactStatus
from dbo.New_ExtMarketingListsElementExtensionBase exL
JOIN dbo.ContactBase cnt ON exL.new_contact=cnt.ContactId
JOIN dbo.ListBase lst ON exL.new_list=lst.ListId
JOIN dbo.CampaignActivityItemBase cai ON lst.ListId=cai.ItemId
where cnt.ContactId=@contactid
and cai.CampaignActivityId=@campaignactivityid

How to get Marketing Lists by Campaign Activity Id


declare @campaignactivityid uniqueidentifier
-- an example GUID
set @campaignactivityid='72CE8156-AD8C-DE11-A526-000C2989FA53'

-- связь между маркетинговым списком и кампанией осуществляется через промежуточную таблицу CampaignActivityItemBase
select *
from dbo.CampaignActivityItemBase cai
JOIN dbo.ListBase lb ON lb.ListId=cai.ItemId
where cai.CampaignActivityId=@campaignactivityid

2009-08-13

Remove 'Add Existing...' button from associated view entities form

I was found this code at Dave Hawes blog — http://blog.davehawes.com/post/2008/04/23/MSCRM-4-Remove-Add-Existing-xxxxx-button.aspx
This code should be added to onLoad event for the form of the entity where you want to hide buttons for the associated views of other entities.


HideAssociatedViewButtons('new_account_new_accountclassificate', ['Добавить к этой записи существующий объект Классификация организации']);

function HideAssociatedViewButtons(loadAreaId, buttonTitles){
var navElement = document.getElementById('nav_' + loadAreaId);
if (navElement != null) {
navElement.onclick = function LoadAreaOverride() {
loadArea(loadAreaId);
HideViewButtons(document.getElementById(loadAreaId + 'Frame'), buttonTitles);
}
}
}

function HideViewButtons(Iframe, buttonTitles) {
if (Iframe != null ) {
Iframe.onreadystatechange = function HideTitledButtons() {
if (Iframe.readyState == 'complete') {
var iFrame = frames[window.event.srcElement.id];
var liElements = iFrame.document.getElementsByTagName('li');

for (var j = 0; j < buttonTitles.length; j++) {
for (var i = 0; i < liElements.length; i++) {
if (liElements[i].getAttribute('title') == buttonTitles[j]) {
liElements[i].style.display = 'none';
break;
}
}
}
}
}
}
}

Publishing it here for memory.

2009-08-12

How to get a PhoneCallId


SELECT
ActivityPartyBase.PartyId AS ContactId,
ActivityPartyBase.ActivityId AS PhoneCallId
FROM

CampaignActivityBase
INNER JOIN ActivityPointerBase

ON (ActivityPointerBase.RegardingObjectId = CampaignActivityBase.ActivityId)AND (ActivityPointerBase.DeletionStateCode = 0)

INNER JOIN PhoneCall

ON ((PhoneCall.ActivityId = ActivityPointerBase.ActivityId) AND (ActivityPointerBase.StateCode = 0) AND (PhoneCall.DeletionStateCode = 0))

INNER JOIN ActivityPartyBase

ON ((PhoneCall.ActivityId = ActivityPartyBase.ActivityId) AND (PartyObjectTypeCode = 2))

INNER JOIN ContactBase

ON (ContactBase.ContactId = ActivityPartyBase.PartyId) AND (ContactBase.DeletionStateCode = 0)


WHERE
(CampaignActivityBase.ActivityId = @CampaignActivityId)

2009-07-20

How to write into the eventlog custom journal with impersonalization in code


private const string LOG_NAME = "MSCRM.Common";

///
/// Write message in log
///

public static void WriteLog(string message, Exception exception)
{
System.Security.Principal.WindowsImpersonationContext ctx = null;
ctx = System.Security.Principal.WindowsIdentity.Impersonate(IntPtr.Zero);
try
{
string logSource = "MSCRM.Common.Web.Controls.Grid";

if (EventLog.SourceExists(logSource))
{
if (!EventLog.LogNameFromSourceName(logSource, ".").Equals(LOG_NAME))
{
EventLog.DeleteEventSource(logSource, ".");
EventLog.CreateEventSource(logSource, LOG_NAME);
}
}
else
{
EventLog.CreateEventSource(logSource, LOG_NAME);
}
EventLog el = new EventLog();
el.Log = LOG_NAME;
el.Source = logSource;
el.WriteEntry(String.Format("{0}\nDetails:\n{1}", message, GetLogInfo(exception)), EventLogEntryType.Error);
}
catch (Exception ex)
{
throw ex;
}
finally
{
ctx.Undo();
}
}

Get selected item in CRM grid

The following snippet will allow you to retrieve an array containing the Id values of the selected records in the grid:

// get array of selected records
var a = document.all['crmGrid'].InnerGrid.SelectedRecords;
var selectedItems = new Array(a.length);
for (var i=0; i < a.length; i++)
{
selectedItems[i] = a[i][0];
}
alert(selectedItems);

To get all of the records in the grid (ie. “All Records on Current Page”):

// array of all records on current page
var iTotal = document.all['crmGrid'].InnerGrid.NumberOfRecords;
var o = document.all['crmGrid'].InnerGrid;
var allItems = new Array;
var ii = 0;
for (var i=0; i < iTotal; i++)
{
allItems[ii] = o.rows[i].oid;
ii++;
}
alert(allItems);

The source: http://blog.customereffective.com/blog/2009/02/javascript-snippets-for-working-with-grids-in-crm.html

2009-07-16

How to programmatically launch an application under a specific user


ProcessStartInfo psi = new ProcessStartInfo("cmd", @"/C c:\SomeApp.exe");
psi.UserName = "UserName";
psi.Domain = "DomaiName";
psi.Password = ReadPassword("password");
psi.UseShellExecute = false;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
using (Process p = Process.Start(psi))
{
// if you need to wait the end of the process
p.WaitForExit();
}

public System.Security.SecureString ReadPassword(string password)
{
System.Security.SecureString secPass = new System.Security.SecureString();
for (int i = 0; i < password.Length; i++)
secPass.AppendChar(password[i]);
return secPass;
}

2009-07-10

MSCRM, SQL and Localization

If you need to look at localized name of some attribute thru the SQL then check out next code


select l.*
from MetadataSchema.LocalizedLabel l
join MetadataSchema.Attribute a on l.objectid=a.AttributeId
join MetadataSchema.Entity e on a.EntityId=e.EntityId
where a.name= 'address1_city' and e.name='account'

2009-06-26

Campaign Activity status = Pending

If campaignactivity status is "pending" all the time, when you are distributing the activities, then check out the Microsoft CRM Asynchronous Processing Service was started on the server.

Convert HTML to plain text and remove tags


// convert HTML to plain text
string Description = System.Web.HttpUtility.HtmlDecode(HTML_encoded_string);

Description = StripTags(Description);

...

// remove tags from HTML-parsed string
private string StripTags(string HTML)
{
System.Text.RegularExpressions.Regex objRegEx = new System.Text.RegularExpressions.Regex("<[^>]*>");

return objRegEx.Replace(HTML, "");
}

2009-06-09

TargetUpdate VS Update method

If you have a question what is better for use: UpdateRequest with TargetUpdate or Update method, then first of all you must to look at MS CRM SDK "CrmService Common Methods" article.

There was said:
"The CrmService Web service provides a set of methods used to perform the most common operations on system and custom entities. These operations can also be performed by using the Execute method and the corresponding message. However, the methods will perform faster so they should be used whenever possible."

So service.Update is a winner.

2009-06-04

CrmDateTime conversion to DateTime


Convert.ToDateTime(crmdatetime.Value)
DateTime.Parse(crmdatetime.Value)


For more information on the CrmDateTime, look in the SDK:
http://msdn2.microsoft.com/en-us/library/aa613542.aspx

Open any entity for edit

The universal URL to open any entity for edit is

http://server name/company name/CRMReports/viewer/drillopen.aspx?ID=objectguid&OTC=object type code

Object type code you can find at Microsoft Dynamics CRM SDK Entity Type Codes article.

Also like that:
http://server name/company name/CRMReports/viewer/drillopen.aspx?ID=objectguid&LogicalName=entity name (contact, account, e.t.c.)

2009-05-29

Error: "Unable to connect to the remote server"

To resolve this error you can edit registry at MS CRM Server - look through the article (PartnerSource) at Microsoft Dynamics: KB913515

Also here is another way, to use the unsafe connection sharing.
Article at blogs.msdn.com: "Best Practices for Better Performance from Your Custom Code"

2009-05-12

MS CRM database fragmentation

If your CRM is very slow, then check the indexes fragmentation.
And if received table was not empty then you must rebuild the indexes.


USE AdventureBase
GO
SELECT a.index_id, name, avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats (DB_ID(N'AdventureBase'), NULL,NULL, NULL, NULL) AS a
JOIN sys.indexes AS b ON a.object_id = b.object_id AND a.index_id = b.index_id
where avg_fragmentation_in_percent>30
GO

2009-05-08

Replace non-digit symbols with RegEx

If you need to restrict input in nvarchar field(only the digit symbols for example), you may write some code to OnSave() event.
This code is replace all of non-digit symbols in new_telephonenumber field to nothing, i.e. "".


//before: new_telephonenumber = "555-555$#5"
//after: new_telephonenumber = "5555555"

var re = /\D/g;
if(document.crmForm.all.new_telephonenumber.DataValue != null){
crmForm.all.new_telephonenumber.DataValue = crmForm.all.new_telephonenumber.DataValue.replace(re,"");
}

2009-05-06

The Microsoft Dynamics CRM Developer Toolkit

Microsoft Dynamics CRM 4.0 toolkit for Visual Studio 2008. Download link

2009-05-05

Test before save

I don't want to write a server code if I can do the same at the client side.
Here is the test for INN field, that must contain only the numbers.

var re = new RegExp("[^0-9]");
if(crmForm.all.new_inn.DataValue != null){
if(re.test(crmForm.all.new_inn.DataValue == true){
alert("ИНН должен содержать только цифры");
event.returnValue = false;
return false;
}
}

2009-05-04

Error: 0x80042f09 SecLib::CheckPrivilege failed. Returned hr = -2147209463

If you get an exception like next one:

0x80042f09
SecLib::CheckPrivilege failed. Returned hr = -2147209463, User: 529cc8c0-23b6-dd11-a373-001cc458db68


You should check the user roles, because the error code 0x80042f09 means "The user has not been granted any licenses or the user has not been assigned any roles."
The user can have the role that can have no rights to the entity or to the entities witch have the owner-member relationship with base entity.
For example:
We have two entities organization -> opportunity and one role "salesperson" with rights on the organization and no rights to the opportunity. If you set the user with just "salesperson" role to the Owner of some organization, you will get the error 0x80042f09.

Same is fair to all errors "SecLib::CrmCheckPrivilege failed."

2009-04-26

DateTime in MSCRM

How to format string to write in CrmDateTime attribute:


string startdate; // - can be a parameter in method

opportunity oop = new opportunity();

opp.new_startdate = new CrmDateTime();

if(startdate!="")
opp.new_startdate.Value = (Convert.ToDateTime(startdate)).ToString("yyyy-MM-ddTHH:mm:ss");
else
opp.new_startdate.Value = DateTime.Today.ToString("yyyy-MM-ddTHH:mm:ss");

2009-04-10

Null Values - based on MS CRM SDK

From Microsoft CRM SDK :

When you update an entity instance there is a mechanism you can use to differentiate the absence of a value from a null value. To set an attribute value to null you must set both IsNull and IsNullSpecified to true. For attributes of type String, you must assign the value to String.Empty. This same mechanism is used when you create a query to find a null value.

Another option is to use the type helper code available in Microsoft.Crm.Sdk assembly and in the CRMHelpers classes.

Example
The following code example shows you how to create null instances of the various Microsoft Dynamics CRM attribute types.

CrmBoolean boolean = new CrmBoolean();
boolean.IsNull = true;
boolean.IsNullSpecified = true;

CrmDecimal dec = new CrmDecimal();
dec.IsNull = true;
dec.IsNullSpecified = true;

CrmFloat f = new CrmFloat();
f.IsNull = true;
f.IsNullSpecified = true;

CrmMoney money = new CrmMoney();
money.IsNull = true;
money.IsNullSpecified = true;

CrmNumber number = new CrmNumber();
number.IsNull = true;
number.IsNullSpecified = true;

Lookup lookup = new Lookup();
lookup.IsNull = true;
lookup.IsNullSpecified = true;

PickList list = new PickList();
list.IsNull = true;
list.IsNullSpecified = true;

Status status = new Status();
status.IsNull = true;
status.IsNullSpecified = true;

Owner owner = new Owner();
owner.IsNull = true;
owner.IsNullSpecified = true;


Example
The following code example shows you how to create null instances of the various Microsoft Dynamics CRM attribute types using the type helper classes.

CrmBoolean boolean = CrmBoolean.Null;

CrmDecimal dec = CrmDecimal.Null;

CrmFloat flt = CrmFloat.Null;

CrmMoney money = CrmMoney.Null;

CrmNumber number = CrmNumber.Null;

Lookup lookup = Lookup.Null;

Picklist list = Picklist.Null;

Status status = Status.Null;

Owner owner = Owner.Null;


My Example
Moving the data from one field to another and cleaning the first field after that.

opportunity opp = new opportunity();
opp.opportunityid = new Key();
opp.opportunityid.Value = findedopp.opportunityid.Value;

opp.new_profittreshold = new CrmMoney();
opp.new_profittreshold.Value = (decimal)findedopp.new_investmenttreshold.Value;

opp.new_investmenttreshold = new CrmFloat();
opp.new_investmenttreshold.IsNull = true;
opp.new_investmenttreshold.IsNullSpecified = true;

service.Update(opp);

2009-04-08

JavaScript свойства объекта

Посмотреть свойства объекта - можно использовать конструкцию

for(key in obj){alert(key)}

2009-01-21

How to close window without prompt. JavaScript.

For most of browsers work code like this:


window.opener = top;
window.close();


But for IE7 will work this code:

<script language=javascript>
function CloseWindow()
{
window.open('','_self','');
window.close();
}
</script>

<input type=button name="closebutton" value="Close" class="Button" onclick="CloseWindow()"/>

Передача нескольких параметров JavaSript в XML

In ISV.Config I was needed a <Button> with JavaScript on it for open modal dialog with 2 or more parameters. In the dialog I'll calculate some sums for displayed opportunities.

So this is example:

<Button JavaScript="var url='http://localhost/CalcTotals.aspx';var paramStr = 'viewid=' + crmGrid.GetParameter('viewid'); if (crmGrid.GetParameter('filterDisplay') != null) paramStr += '&amp;' + 'filter=' + crmGrid.GetParameter('filterDisplay');if (crmGrid.GetParameter('quickfind') != null) paramStr += '&amp;' + 'quickfind=' + crmGrid.GetParameter('quickfind'); url= url + '?' + paramStr; showModalDialog(url , '','status:no;dialogWidth:610px;dialogHeight:210px;dialogHide:true;help:no;scroll:no');" AccessKey="в" WinMode="1" PassParams="1">;

The character "&" must be replaced with "&amp;".

An XML comment cannot contain '--', and '-' cannot be the last character.

The most possible reason of this error is in your web.config or in your custom XML file.
There is great likelihood that is in AppSettings section.