Microsoft Office SharePoint Web Part for Microsoft Dynamics CRM

Copied from Joris Kalz blog:


We just released the localized version of the list web part for Microsoft Dynamics CRM to support Microsoft Office Sharepoint Server (MOSS 2007). The great thing about is that you can use Sharepoint to create a dashboard to display essential Microsoft Dynamics CRM data for your users. E.g. this can be used to create a central portal or dashboard for your sales team with all relevant information and reports coming from Microsoft CRM and Sharepoint.

The List Web Part is now available in the following languages: English, Brazilian, Danish, Dutch, French, German, Italian, Spanish.

Download: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=bc9b3526-decf-4057-a530-91840c0d5401



Clone a record

There are some interesting things in the May 2006 Demo VPC that you might not have seen before. One of them is cloning a record. There's an addon written and supplied with the VPC that does take of cloning a contact. I've used it in some other projects and will be using it in my current project again. Ofcourse you can modify the code to match any entity.

I'll add the code here so that you dont have to download the complete VPC. There will be added a piece of code to the isv.config file and an additional file will need to be placed on the server.

Here is the piece that needs to be added in to the isv.config file:


<configuration version="3.0.0000.0">
<Root />
<Entities>
<Entity name="contact">
<ToolBar ValidForCreate="0" ValidForUpdate="1">
<Button Title="Clone Contact" ToolTip="Create a Copy of This Contact" Icon="/CloneContact/contactclone.gif" Url="/CloneContact/CloneContact.htm" WinMode="1" WinParams="dialogHeight:100px;dialogWidth:300px;"/>
</ToolBar>
</Entity>
</Entities>
</configuration>


This is the code that needs to be stored in the file as indicated in the isv.config file:

<html>
<title>Clone Contact</title>

<style>

BODY, TD
{
font-family: arial;
font-size: 12px;
}

TD.body
{
border-bottom: solid 1px #cccccc;
text-align: center;
}

</style>

<script language="javascript">

// Set global variable for the cloned contact window
var oClonedContact;

function window.onload()
{
// Open a new contact form
oClonedContact = window.open('/sfa/conts/edit.aspx','','menubar=0, status=1, width=1000, height=600');

// Set a timeout to wait for the new contact form to load
setTimeout('checkPageState()',100);
}

// Checks if the new contact form has completed loading
// When it completes, CloneContact will be called
// If it's not loaded, it will set a timeout and check again.
function checkPageState()
{
if (oClonedContact.document.readyState == 'complete')
{
CloneContact();
return;
}

setTimeout('checkPageState()',100);
}

function CloneContact()
{
// Get a pointer to the parent window
var oParent = window.dialogArguments;
var oSource = oParent.document.crmForm;

// With the target crmForm
with(oClonedContact.document.crmForm)
{
// Name fields
salutation.DataValue = oSource.salutation.DataValue;
firstname.DataValue = oSource.firstname.DataValue;
nickname.DataValue = oSource.nickname.DataValue;
lastname.DataValue = oSource.lastname.DataValue;
jobtitle.DataValue = oSource.jobtitle.DataValue;
parentcustomerid.DataValue = oSource.parentcustomerid.DataValue;

telephone1.DataValue = oSource.telephone1.DataValue;
telephone2.DataValue = oSource.telephone2.DataValue;
mobilephone.DataValue = oSource.mobilephone.DataValue;
fax.DataValue = oSource.fax.DataValue;
pager.DataValue = oSource.pager.DataValue;
emailaddress1.DataValue = oSource.emailaddress1.DataValue;

// Address fields
//address1_name.DataValue = oSource.address1_name.DataValue;
address1_line1.DataValue = oSource.address1_line1.DataValue;
address1_line2.DataValue = oSource.address1_line2.DataValue;
address1_line3.DataValue = oSource.address1_line3.DataValue;
address1_city.DataValue = oSource.address1_city.DataValue;
address1_stateorprovince.DataValue = oSource.address1_stateorprovince.DataValue;

address1_postalcode.DataValue = oSource.address1_postalcode.DataValue;
address1_country.DataValue = oSource.address1_country.DataValue;
//address1_telephone1.DataValue = oSource.address1_telephone1.DataValue;
//address1_addresstypecode.DataValue = oSource.address1_addresstypecode.DataValue;
//address1_shippingmethodcode.DataValue = oSource.address1_shippingmethodcode.DataValue;
//address1_freighttermscode.DataValue = oSource.address1_freighttermscode.DataValue;

// Professional Information fields
//department.DataValue = oSource.department.DataValue;
accountrolecode.DataValue = oSource.accountrolecode.DataValue;
managername.DataValue = oSource.managername.DataValue;
managerphone.DataValue = oSource.managerphone.DataValue;

assistantname.DataValue = oSource.assistantname.DataValue;
assistantphone.DataValue = oSource.assistantphone.DataValue;

// Personal Information fields
gendercode.DataValue = oSource.gendercode.DataValue;
familystatuscode.DataValue = oSource.familystatuscode.DataValue;
spousesname.DataValue = oSource.spousesname.DataValue;

all.birthdate.DataValue = oSource.all.birthdate.DataValue;
all.anniversary.DataValue = oSource.all.anniversary.DataValue;

// Description
//description.DataValue = oSource.description.DataValue;

// Internal Information fields
ownerid.DataValue = oSource.ownerid.DataValue;
originatingleadid.DataValue = oSource.originatingleadid.DataValue;

// Billing Information fields
//creditlimit.DataValue = oSource.creditlimit.DataValue;
//all.creditonhold.DataValue = oSource.all.creditonhold.DataValue;
//paymenttermscode.DataValue = oSource.paymenttermscode.DataValue;
//defaultpricelevelid.DataValue = oSource.defaultpricelevelid.DataValue;

// Contact Methods fields
preferredcontactmethodcode.DataValue = oSource.preferredcontactmethodcode.DataValue;
all.donotemail.DataValue = oSource.all.donotemail.DataValue;
all.donotbulkemail.DataValue = oSource.all.donotbulkemail.DataValue;
all.donotphone.DataValue = oSource.all.donotphone.DataValue;
all.donotfax.DataValue = oSource.all.donotfax.DataValue;
all.donotpostalmail.DataValue = oSource.all.donotpostalmail.DataValue;

// Marketing Information fields
all.donotsendmm.DataValue = oSource.all.donotsendmm.DataValue;
all.lastusedincampaign.DataValue = oSource.all.lastusedincampaign.DataValue;

// Service Preferences fields
preferredappointmenttimecode.DataValue = oSource.preferredappointmenttimecode.DataValue;
preferredappointmentdaycode.DataValue = oSource.preferredappointmentdaycode.DataValue;
preferredserviceid.DataValue = oSource.preferredserviceid.DataValue;
preferredequipmentid.DataValue = oSource.preferredequipmentid.DataValue;
preferredsystemuserid.DataValue = oSource.preferredsystemuserid.DataValue;
}

// Finally, close the dialog
window.close();
}

</script>

<body>

<table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0" align>
<tr valign="middle">
<td class="body" align="center">
<div style="font-size= 10pt; font-family= Tahoma;">Cloning Contact...</div>
</td>
</tr>
</table>

</body>

</html>


It's just a free addon from Microsoft, so use it if you can :)



Error when creating a new appointment

Robert Feenstra, working for Atlanticasset, has informed me about an issue that he had when he created a new appointment. He did not only tell me his issue, he also was able to give the solution! To be able to help more people, here is the error and the solution.

When trying to create a new appointment, upon save get the error:

The record that you are requesting is currently unavailable. Either the record was not found or you do not have sufficient security permissions to view it.


Upon clicking "Ok", I get the error:
General failure in scheduling engine


with a "Ignore and Save" buttonwhich will save the appointment, but still marks the appointment as having a General failure in scheduling engine from then on.

This is caused by an orphan record in the database. To find out which one it is, run the following script against the MSCRM database.

select r.ResourceId, r.OrganizationId, r.BusinessUnitId, r.Name, u.FullName from Resource as r left outer join SystemUserBase as u on r.ResourceId = u.SystemUserId where r.ObjectTypeCode = 8


If the 4th and 5th columns do not match, this CRM user record has been deleted from the SystemUserBase table, then we need to remove the orphan record from the Resoursebase table. To do so, please run the following script.

delete from ResourceBase where ResourceId in (select r.ResourceId from Resource as r left outer join SystemUserBase as u on r.ResourceId = u.SystemUserId where r.ObjectTypeCode = 8 and u.FullName is null)


That should solve the issue. Ofcourse you should always make a backup before trying these kind of actions!

Thanks Robert for sharing!



Interesting issues with the CRMDateTime continuum.

This is a guest post by a friend and colleague of mine: Leon Krancher. He has been digging into an issue regarding CRMDateTime and integration. Make sure you read this before you start your own integration!

Well I guess I’d better start off with a little introduction. My name is Leon Krancher and I’m a colleague of Ronald at Avanade. As we both work with CRM we occasionally find time to get together, have a beer and share experiences. Recently I told him about a problem I encountered while trying to create a service that would synchronize my customers Data warehouse with CRM. Ronald thought it was interesting and asked me to write about it for his blog. So here we go.

Let’s start out with a more detailed description of what had to be created.

The customer’s data warehouse contained information about its customers. This information is updated frequently by the customer’s backoffice systems as well as a number connections to third party systems. Whenever the data changed a database trigger would update a datatime field inside the database to indicate the exact moment the customers data was changed.
Because the data warehouse data was also used in CRM I was tasked with creating an application that would update the data stored in CRM whenever the data warehouse was updated. Unfortunately I only had a very limited timeslot in which this application would have to run so I had to find a way to update only those few records that had changed. To solve this problem I decided to do the following:

Ø First I would add an attribute to the entity in CRM that would be used to store the DateTime value used to store the date and time on which the data was originally created in the data warehouse.

Ø Second I wrote a cross-database join that would join the data warehouse on the CRM database, compare both datetime values and return those rows for which the entity in CRM was outdated.

Ø The last step was to use the CRM webservice to update the CRM entities returned by the previous query[1].

Unfortunately whenever I tried to run my tool it would always update all records even though the data warehouse had not changed.
After some frantic debugging[2] I finally figured out what the true problem was.

When storing my data in CRM I used the following code taken from the CRM SDK.


providedContact.new_nawsyncdate.Value = syncDateAccordingToDatabase.ToUniversalTime().ToString("u");

This converts the syncDateAccordingToDatabase DateTime object to a string in the universal time format so it can be stored in CRM. Unfortunately the universal time format omits any milliseconds.

So whenever SQL Server would compare the dateTime stored in the data warehouse to the dateTime stored in CRM the data warehouse DateTime that did contain any milliseconds would be seen as ‘larger’, hence the continuous cycle of updates.

After figuring this out the problem was easily solved by adjusting the database query so it would always remove any milliseconds before making the comparison.

I hope this provides you guys with both a funny story and some help if you ever run into a similar problem.

-Leon

[1] I couldn’t update the CRM database directly because that would prevent the execution of a callout

[2] In my defense, the matters were complicated because the data warehouse and the CRM databases were set to use different time zones making the problem appear like a conversion problem



Dynamic picklists and other samples

Somebody asked me for some advice on how to create dynamic picklists. I have seen this question in the newsgroups several times as well. Now you might expect that I do have a coding sample for you, but I did not create one. And still I do have one which you can download here. Now... If I didn't create it, who did?

Microsoft did create this for you. And made it available. And even better. You probably do have it already! If you have downloaded the SDK, then you have this coding sample already. If not, then download the SDK here. In this SDK is not only the CHM file explaining how to work with the CRM API, but also a lot more information in separate files. I'd suggest you to take a look in the folder "sdk samples". In this folder you will find multiple folders including "fullsample". This fullsample folder does give you the following examples:

- activity events
- callout sample (both vb and c#)
- callout sample 2 (both vb and c#)
- databinding
- duplicate detection
- dynamic picklist
- import export publish (both vb and c#)
- metadata diagram (both vb and c#)
- workflow plugin (both vb and c#)

Next to the fullsample, also take a look at the other folders in the sdk samples directory. This might save you a lot of work!!!



Gezocht: Stukadoor

This post is intentionally in Dutch.

Zoals de titel al aangeeft: ik zoek een stukadoor. Al meer dan een jaar kijk ik uit naar de oplevering van m'n huisje en het is bijna zo ver. Op 22 mei krijg ik de sleutel en kan ik beginnen met de binnenkant. Als eerste zullen de muren van spachtelputz voorzien moeten worden. De persoon die dat voor mij zou gaan doen, die heeft alleen juist dán twee weken vakantie...

Nu is het dé tijd om mij te helpen :) Kent iemand van mijn lezers een stukadoor die voor een vriendelijk prijsje mij wil helpen? Stuur ajb een mailtje naar: ronaldl at avanade dot com.

Dank je!