Useful Dynamics CRM Form Scripts #3
Update a Contact Record with Parent Account Data, Using Web Services from JavaScript
| There are lots of ways of creating CRM contact records. For example, you can navigate first to an account form, click Contacts in the details section, then click the New Contact button. If you do that, you’ll notice that fields like Streets 1, 2 and 3, City, State/Province, Zip/Postal Code and a few others are auto-filled with information from the parent account record. | If you need to learn how to customize Dynamics CRM, topics like the one in this article are covered in my one-day live online training class, Customizing Dynamics CRM and the xRM Platform |
This is referred to as mapping, and it’s a handy, customizable feature that can be used in lots of different places. You can use it to “clone” records, for example like the technique I described here.
But mapping has its limitations, the most obvious of which is that it only works if you create the child record from the context of the parent. Suppose you create and save a contact record without a parent account, and then assign it to a parent account after the fact — ex post facto, as it were. I remember being surprised the first time I saw this, thinking hmmm…if I create the child record from the parent it fills in those mapped fields, so why shouldn’t it fill in the mapped fields if I assign the child to the parent later on? Then my instructor clarified it for me: because that’s not how it works!
Aha, I thought, good point.
But you can customize CRM to make this happen, by writing some form script on the onChange event of the Parent Customer on the contact form. Here’s a stripped-down version of the code, uncommented for brevity’s sake. (By the way, I just noticed this and haven’t figured out yet what the problem is, but when I copy this code as is to the clipboard and then paste it in the onChange event, it does not work. I think the WordPress editor is putting some extra characters in there…but this is a hard one to track down…even when I’m using VS to debug!
– anyway, when I figure it out I’ll post a fix, but for now, you may need to do some debugging of your own if you copy it as-is)
//debugger;
var AccountLookUp = crmForm.all.parentcustomerid.DataValue;
if (AccountLookUp != null && AccountLookUp != "undefined") {
var AccountID = AccountLookUp[0].id;
var Accountxml = "" +
"<?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\">" +
GenerateAuthenticationHeader() +
" <soap:Body>" +
" <RetrieveMultiple xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" +
" <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query\" xsi:type=\"q1:QueryExpression\">" +
" <q1:EntityName>account</q1:EntityName>" +
" <q1:ColumnSet xsi:type=\"q1:AllColumns\" />" +
" <q1:Distinct>false</q1:Distinct>" +
" <q1:Criteria>" +
" <q1:FilterOperator>And</q1:FilterOperator>" +
" <q1:Conditions>" +
" <q1:Condition>" +
" <q1:AttributeName>accountid</q1:AttributeName>" +
" <q1:Operator>Like</q1:Operator>" +
" <q1:Values>" +
" <q1:Value xsi:type=\"xsd:string\">" + AccountID + "</q1:Value>" +
" </q1:Values>" +
" </q1:Condition>" +
" </q1:Conditions>" +
" </q1:Criteria>" +
" </query>" +
" </RetrieveMultiple>" +
" </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/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", Accountxml.length);
xmlHttpRequest.send(Accountxml);
var resultAccountXml = xmlHttpRequest.responseXML;
var entityNodes = resultAccountXml.selectNodes("//RetrieveMultipleResult/BusinessEntities/BusinessEntity");
for (var i = 0; i < entityNodes.length; i++) {
var entityNode = entityNodes[i];
var TelephoneNode = entityNode.selectSingleNode("q1:telephone1");
var Telephone = (TelephoneNode == null) ? null : TelephoneNode.text;
var Line1Node = entityNode.selectSingleNode("q1:address1_line1");
var Line1 = (Line1Node == null) ? null : Line1Node.text;
var CityNode = entityNode.selectSingleNode("q1:address1_city");
var City = (CityNode == null) ? null : CityNode.text;
var StateNode = entityNode.selectSingleNode("q1:address1_stateorprovince");
var State = (StateNode == null) ? null : StateNode.text;
var ZipNode = entityNode.selectSingleNode("q1:address1_postalcode");
var Zip = (ZipNode == null) ? null : ZipNode.text;
}
if (crmForm.all.telephone1.DataValue == null)
{crmForm.all.telephone1.DataValue = Telephone;}
if(crmForm.all.address1_line1.DataValue == null)
{crmForm.all.address1_line1.DataValue = Line1; }
if (crmForm.all.address1_city.DataValue == null)
{crmForm.all.address1_city.DataValue = City; }
if (crmForm.all.address1_stateorprovince.DataValue == null)
{crmForm.all.address1_stateorprovince.DataValue = State;}
if(crmForm.all.address1_postalcode.DataValue == null)
{crmForm.all.address1_postalcode.DataValue = Zip;}
}
Like I said, you can copy this to the clipboard, paste it into the onChange event of the Parent Customer field (schema name parentcustomerid), make sure the onChange event is enabled, and it should work unchanged, regardless of which version of CRM you’re running. If you examine the JavaScript, you’ll see a line containing “RetrieveMultiple”, which is actually one of the six commonly used methods of the CrmService web service, which is used for…well, things like this.
This code works great…but it’s pretty gnarly the first time you see it. It’s explained quite well in the Dynamics CRM 4.0 SDK, for example here.
How Does the Code Work?
One of the reasons I like this “calling web services from JavaScript” example is that it illustrates a good point: that sometimes you just can’t tell what the heck’s going on with your code unless you debug it with a tool like Visual Studio. Without Visual Studio, you can try to figure out how the code works (or worse yet…try to get it to work if it doesn’t!) by using the JavaScript alert function. When you’re starting out with form scripting this usually works fine: it’s easy, it runs in the browser, and for relatively simple code it can get the job done.
In the script above, you might place the following line immediately after the variable Accountxml is constructed, and before the xmlHttpRequest. Make it look like this:
"</soap:Envelope>" +"";
alert (Accountxml);
var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
If you add the alert function that way, preview the contact form and use the Parent Customer lookup field to select an account record, you’ll see something like this:
Now if you already know what’s going on this might be fine…but if like me you’re a little challenged when it comes to using code to construct complicated XML packages for SOAP messages you’ll probably need a little more insight!
By the way, the reason this code is complex is summarized nicely in the Dynamics CRM SDK, from which I quote here:
By creating your own Web service to contain the code used to access CRM, you can avoid the more challenging process of JScript string manipulation and identifying the correct structure for your SOAP envelope contents.
In Microsoft Dynamics CRM Online you cannot create a Web service application on the server so you must use XMLHttp requests to the Microsoft Dynamics CRM Web services. This can also be used to enable access to Microsoft Dynamics CRM Web services while the user is working offline with the Microsoft Dynamics CRM for Outlook.
That means the code I’m explaining here is a specific example of a very general class of code: since in CRM Online you cannot create a Web service application on the server, the only way you can access the CRM web services is to construct SOAP messages like the example does, and send them off to the server with XMLHttp requests. So while this code is a little difficult to understand at first, it’s also more general: it will work with CRM Online, on-premise or IFD deployments. So: more general, but more complex. Always with these tradeoffs!
So anyway, let’s see a better way to debug code like this, using Visual Studio. Assuming you’ve got it installed, you can get some insight into how this code works by following these steps:
- In Internet Explorer, select Internet Options from the Tools menu, and click the Advanced Tab,
- De-select the checkbox for Disable script debugging in the Browsing section. (that is, you want to enable script debugging!)
- Then, go to the very beginning of the script listing above, and add the command ”debugger;”, so it looks like this:
debugger;
var AccountLookUp = crmForm.all.parentcustomerid.DataValue;
if (AccountLookUp != null && AccountLookUp != "undefined") {
var AccountID = AccountLookUp[0].id;
var Accountxml = "" +
"<?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\">" +
GenerateAuthenticationHeader() + .....
Then you can preview the contact form. Click the lookup button on the Parent Custom field, and select an account. You’ll see a dialog like this one, in which you can select a New instance of Visual Studio and click Yes.
Then a new instance of VS opens, and you’ll be in debugging mode on the line of code you can see highlighted:
If you press F10, the form script will run one line at a time. You can examine the value of variables and so forth in the Locals section, and keep pressing F10 to walk through the code. Notice in the following figure, I’ve advanced to just after the Accountxml variable got constructed (where I put the Alert function in the first version). What’s cool about this is I can use the XML Visualizer to really figure out how that XML is being cosntructed:
Here’s what it looks like. NOW I can see what’s going on, how the SOAP Envelope contains both the Header and the Body, and how the Body contains the Query that’s going to be sent to the server with the RetrieveMultiple method, and all that good stuff.
What did I learn from this example? I better summarize it so I don’t forget:
- I think that’s a good example of when you’d need to use Visual Studio to figure out Dynamics CRM form scripts.
- Plus, it’s a good example of how to use CRM web services with an approach that will work for CRM Online.
- And it’s also a good way of updating information on a child record from its parent, if the child record didn’t get those fields populated from relationship mapping.



Nate Said,
June 11, 2010 @ 8:47 am
Fantastic Post! You managed to explain Web Services *and* debugging in one post!
One thing to add, perhaps in your next post, is that if you start using Web Services, you’ll want to re-use your code otherwise you’ll go crazy. I use Daniel Cai’s setup: http://danielcai.blogspot.com/2010/05/mscrm4-web-service-toolkit-for.html
But there are several others that you can use..
Richard Knudson Said,
June 11, 2010 @ 4:10 pm
Thanks Nate — I’m glad you liked it. I know what you mean about the “going crazy” bit. The first time I saw code like this, using Notepad as my editor, it was kind of like that. I’ll check out Daniel’s setup — haven’t used it before as I only recently started using these web services. Thanks for the tip!
Richard
Michael Cross Said,
July 13, 2010 @ 5:33 am
Is there a specific reason why you used RetrieveMultiple rather than Retrieve? In this situation, you’re only selecting one record. Also, I like how you handle the situation if there is no data, since the SDK doesn’t show this. The nulls were driving me crazy when I first started using this technique and the error message kept saying something about an object required.
Michael Cross Said,
July 13, 2010 @ 5:39 am
One thing I’m stuck with is how to get both the GUID and the name for a lookup attribute from the parent to set the value of a lookup attribute on the child. In my situation, we have created custom entities for the State/Province and Country to make them table-driven. I now need to get both the GUID (which I can do) and the name (which I haven’t been able to figure out how to do in one query) in order to set the value of the State/Province and Country lookups on the child. The only way I’ve been able to get this to work is to construct another Retrieve message using the GUID from the first query to query the State/Province and Country entities.
Manjeet Singh Said,
June 10, 2011 @ 4:31 am
Hi,
Can any one please share xml schema/format for Create Contact and account.
Regards,
Manjeet Singh