Hi, I'm moving my blog to http://crmdude.wordpress.com/, please update your bookmarks.
Shafraz
Sunday, 13 September 2009
Moving Blog!
Posted by
Shafraz Nasser
at
1:06 pm
0
comments
Thursday, 20 August 2009
Querying a PartyList field
CRM doesn't allow a PartyList field to be queried using a QueryExpression (it doesn't allow it using Advanced Find either). A colleague had tried to query the Campaign Response records to ascertain whether a Contact had responded to a Campaign before before performing some other process(es).
The trick was to query the Activity Pointer entity (where the regardingobjectid is the Campaign) and then perform a link to the Activity Party entity (where the party is the Contact). The following function shows this:
private static bool AlreadyResponded(string strCampaignId, string strCustomerId)
{
ConditionExpression condition = new ConditionExpression();
condition.AttributeName = "regardingobjectid";
condition.Operator = ConditionOperator.Equal;
condition.Values = new object[] { strCampaignId };
FilterExpression filter = new FilterExpression();
filter.Conditions = new ConditionExpression[] { condition };
filter.FilterOperator = LogicalOperator.And;
# region link to the Activity Party entity
LinkEntity link = new LinkEntity();
link.LinkFromEntityName = EntityName.activitypointer.ToString();
link.LinkToEntityName = EntityName.activityparty.ToString();
link.LinkFromAttributeName = "activityid";
link.LinkToAttributeName = "activityid";
ConditionExpression linkCondition = new ConditionExpression();
linkCondition.AttributeName = "partyid";
linkCondition.Operator = ConditionOperator.Equal;
linkCondition.Values = new object[] { strCustomerId };
FilterExpression linkFilter = new FilterExpression();
linkFilter.Conditions = new ConditionExpression[] { linkCondition };
linkFilter.FilterOperator = LogicalOperator.And;
link.LinkCriteria = linkFilter;
# endregion
QueryExpression query = new QueryExpression();
query.ColumnSet = new AllColumns();
query.Criteria = filter;
query.EntityName = EntityName.activitypointer.ToString();
query.LinkEntities = new LinkEntity[] { link };
BusinessEntityCollection collection = crm.RetrieveMultiple(query);
if (collection != null && collection.BusinessEntities.Length > 0)
return true;
return false;
}
Posted by
Shafraz Nasser
at
9:40 am
0
comments
Monday, 15 June 2009
Consuming the MS CRM Web Service for a Secondary Organisation
If you've got multiple CRM organisations in a MS CRM instance, you would have noticed that connecting to the default Web Service gives you only the customisations of the default organisation. In the past, I've been downloading the WSDL files in the Customisation section for each of my secondary organisations and then creating a Web Service reference to the downloaded file- especially annoying when you make changes to your customisation and have to keep the file updated!
Well, I was about to do the same this morning.. until I discovered realised that I could simply consume the download URL of the WSDL file itself:
http://SERVERNAME/MSCrmServices/2007/CrmServiceWsdl.aspx?uniquename=ORGANISATION
Why didn't I discover this before?
Posted by
Shafraz Nasser
at
9:35 am
0
comments
Labels: customisation, tips, tricks, webservice
Friday, 3 April 2009
Setting a lookup value to null
To set a lookup field to null you'd do the following in your c# code:
vehicle.new_accountid = new Lookup();
vehicle.new_accountid.IsNull = true;
vehicle.new_accountid.IsNullSpecified = true;
To do the same using JavaScript, you'd do the following:
crmForm.all.new_accountid.DataValue = null;
Posted by
Shafraz Nasser
at
8:35 am
0
comments
Labels: code, Javascript
Monday, 2 March 2009
Debugging Custom Workflows
Debugging a custom Workflow that you've written (and registered to your Microsoft CRM instance) can be done by attaching to the Microsoft CRM Asynchronous Service process (as opposed to the w3p.exe process when debugging plug-ins).
Posted by
Shafraz Nasser
at
2:21 pm
0
comments
Thursday, 12 February 2009
Change default Public View using Plug-In
One of the things I get asked a lot about is having different Views on Entities for different users. This is possible using a Plug-In. I've attached a sample showing how to change defaults (and hide views) depending on the user's security role. The plug-in has to be registered for the RetreieveMultiple message on the savedquery entity to be be executed in the post stage (I have also included an export from the registration tool).
In this example I'm setting two different default views depending on whether the user is a Sales Person (My Active Contacts) or a Sales Manager (Active Contacts) and in addition, hiding a view if the user is a Sales Person.
Posted by
Shafraz Nasser
at
7:36 pm
0
comments
Labels: code, plugin, savedquery
Wednesday, 11 February 2009
Create a picklist attribute programmatically in CRM
I wrote a little console application that uses the CRM Metadata Service to create a Picklist attribute on the contact entity called country and adds all of the options to it. With a little modification, you can make it create any other picklist attributes..
I got a copy of the list of countries (ISO 3166) from here
I've attached the solution to my SkyDrive account, just remember to update the app.config file (fairly straightforward) and also add a reference to the CRM Metadata Web Service and name it MetadataSdk.
Happy coding!
Posted by
Shafraz Nasser
at
2:57 pm
0
comments
Wednesday, 4 February 2009
Performing a Javascript Web Service call to the Metadata Service
About a week ago a colleague of mine told me that he'd been able to use my JS script to retrieve fields from a related record upon the change of a lookup value- which worked exactly as he wanted. However, one of the fields he retrieved was a Picklist value- which was obviously a numeric value and pretty pointless since the field he was displaying the value in was nvarchar.
He had three options;
1) Hard code the text values for each option value in his code (yeah, right!),
2) Create a picklist with the same values (text and numeric) as the source picklist and ensure that his documentation clearly outlines that the two picklists will forever have to kept in sync (umm, no thanks),
3) Perform a web service call to the Metadata service and retrieve the options for the source picklist (!).
So I decided to put together the JavaScript code to achieve (3). In the example below, I'm retrieving the accountcategorycode field from the Account entity.
var request = "" +
"<?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>" +
" <Execute xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" +
" <Request xsi:type=\"RetrieveAttributeRequest\">" +
" <MetadataId>00000000-0000-0000-0000-000000000000</MetadataId>" +
" <EntityLogicalName>account</EntityLogicalName>" +
" <LogicalName>accountcategorycode</LogicalName>" +
" <RetrieveAsIfPublished>false</RetrieveAsIfPublished>" +
" </Request>" +
" </Execute>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";
var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
xmlHttpRequest.Open("POST", "/mscrmservices/2007/MetadataService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Execute");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", request.length);
xmlHttpRequest.send(request);
var result = xmlHttpRequest.responseXML;
alert(result.xml);
Hope that helps somebody out there!
Posted by
Shafraz Nasser
at
8:04 pm
2
comments
Labels: code, Javascript, metadata, webservice
Thursday, 29 January 2009
Deleting records in MSCRM 4.0
As many of you would be aware, when deleting records from MSCRM, the record itself isn't removed from the SQL database straight away- instead the DeleteStateCode of the record in question is updated to '2'. There are a number of ways to forcing a delete of the row immediately (this may be necessary if you're testing a heavy data import script for example).
One way is to write a SQL script to empty the database tables representing your entities, so to remove all Contacts, you'd run the following script (this is assuming there are no relationships with any other entity- eg, no Contact has a Parent Customer):
delete from ContactExtensionBase
delete from ContactBase
Another way is to either delete your records from the application (thus setting DeleteStateCode to 2) or updating records in the database manually using SQL to set DeleteStateCode to 2 and then running the script Bill Owens has put up on his blog: http://billoncrmtech.blogspot.com/2008/05/delete-forcing-microsoft-crm-40-to.html. His script makes the CRM deletion job run immediately (after you restart the CRM Asynchronous service).
Finally, there is also the ScaleGroup Job Editor which is a nifty little tool that you run on the CRM server itself. The tool allows you to set how often you want the Deletion job to run (among other CRM jobs). More info and download from here: http://code.msdn.microsoft.com/ScaleGroupJobEditor/Release/ProjectReleases.aspx?ReleaseId=676
I'm sure people have discovered other (and better) ways to bulk delete records from CRM.. please feel free to share them with me!
Posted by
Shafraz Nasser
at
8:35 am
0
comments
Tuesday, 13 January 2009
Performing a JavaScript web service call
//Prepare variables for a account to retrieve.
var accountid = crmForm.all.customerid.DataValue[0].id
var authenticationHeader = GenerateAuthenticationHeader();
//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>account</entityName>"+
"<id>"+accountid+"</id>"+
"<columnSet xmlns:q1='http://schemas.microsoft.com/crm/2006/Query' xsi:type='q1:ColumnSet'>"+
"<q1:Attributes>"+
"<q1:Attribute>creditonhold</q1:Attribute>"+
"<q1:Attribute>name</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;
Posted by
Shafraz Nasser
at
10:21 am
0
comments
Labels: code, Javascript
Monday, 22 December 2008
An early look at CRM “5”
The Microsoft CRM Team Blog's latest post has some information on the next version of CRM: http://blogs.msdn.com/crm/archive/2008/12/18/an-early-look-at-crm-5.aspx
Posted by
Shafraz Nasser
at
4:16 pm
0
comments
Labels: upcoming release
Thursday, 6 September 2007
Quick Campaign Bug
A client of mine found a problem with Quick Campaign's in his CRM environment. He was playing with the QC functionality and created a new QC, and then deleted the associated merge Letter/Email without sending it/distributing it. He then could not delete the QC record and got an error message saying that "This record is currently being used by Microsoft CRM and cannot be deleted."
I had to remove the records from the database itself. The QC entity does not have it's own table in the database, instead it is recognised within the system a Bulk Operation Activity. I had to remove it from the following tables:
- ActivityPartyBase,
- BulkOperationBase,
- ActivityPointerBase.
Posted by
Shafraz Nasser
at
4:10 pm
0
comments
Labels: bug, quick campaign
Tuesday, 14 August 2007
Microsoft CRM 4.0 (Titan)
I attended a Microsoft CRM 4.0 session at Microsoft NZ's offices here in Auckland early last week. The session was presented by Ed Martinez who is one of the Development Managers of the application.
Some of the new features in 4.0 are:
- multi organisation,
- multi currency,
- multi language,
- multi entity grid,
- improved workflow engine (WWF),
- reporting wizard,
- report scheduling wizard,
- web client mail merge,
- duplicate detection.
Unfortunately I am pressed for time so I can't go into further detail on each of the above..
Posted by
Shafraz Nasser
at
4:32 pm
0
comments
Labels: upcoming release
"Send Direct Email" does not work
A client called me a few days ago and said that he couldn't 'Send Direct Emails'. After he'd select the template to send, it would simply do nothing, the History wouldn't show the send email nor would the recipient recieve any emails.
Naturally, the first place I looked was the Microsoft CRM Bulk Email service. I found that it was set to logon as the Network Service account. I changed this to Local System account (this is after I had tested to ensure that emailing was working from all other avenues in Microsoft CRM) and voila it started working.
Posted by
Shafraz Nasser
at
4:25 pm
2
comments
Tuesday, 29 May 2007
Changing User Name in AD?
If you are changing the username of a CRM user in Active Directory, you'll notice that the username field on the User record in CRM will still show the old username. That's because the change in AD doesn't propogate to the CRM database.
You'll find that even though the username field is incorrect in CRM, the user will still be able to log in as normal- that's because authentication is handled by AD and not CRM. However, exported dynamic worksheets and pivot-tables will no longer work. They will also recieve errors when trying to view most of the standard CRM reports.
To fix the problem, simply update the DomainName field in the systemuser table in your OrganisationName
UPDATE systemuser SET DomainName = 'MYDOMAIN\andrew' WHERE DomainName = 'MYDOMAIN\andr3w'
Posted by
Shafraz Nasser
at
10:01 am
0
comments
Labels: Active Directory, systemuser
Tuesday, 15 May 2007
Quick Create Form Layout
Unfortunately, the Quick Create form's aren't customisable in Microsoft CRM. Which is pretty annoying considering that the Business Required and Business Recommended fields are simply picked up and rendered as they read off the screen (left to right for every row). It's even more bizarre considering the tab ordering starts at the first row in the left-most column in every section and moves down the column, until reaching the end of the column before going to the second column!
If one were to have an Account record where the following fields (including the Account Name field) were Business Required:
Then the Quick Create form for the Account renders as follows:
You can change the order of the Business Required and Business Recommend fields so that they render in a logical way on the Quick Create form.
Which would render the Account Quick Create form as follows
crmForm.all.address1_line1.tabIndex = 1110;
crmForm.all.address1_line2.tabIndex = 1120;
crmForm.all.address1_line3.tabIndex = 1130;
crmForm.all.address1_city.tabIndex = 1140;
crmForm.all.address1_country.tabIndex = 1150;
I've simply taken the current tabIndex values being assigned to the 5 fields above and reassigned them to ensure that I don't distrupt anything else on the Account form.
Posted by
Shafraz Nasser
at
3:01 pm
0
comments
Labels: customisation, quick create, tips, tricks
Bug on Quick Create form
Not sure how many people have experienced this as yet, nor have I tried it on any of our customer's sites yet. We're running an English version with only Update Rollup 1 installed with no JavaScript on the form either.
When you have the Quick Create form open, and if the form has atleast 1 Business Required field and if you hit 'Enter' prior to filling the Business Required field, you recieve a warning message informing you that all required fields haven't been filled in and then the 'OK' field becomes disabled and remains disabled even after you've filled in all fields on the form...
Posted by
Shafraz Nasser
at
11:11 am
0
comments
Labels: bug, quick create