Wednesday, February 9, 2011

Load an associated view into an IFrame, hide it from LHS navigation menu

/**
* Load an associated view into an IFrame, hide it from LHS navigation menu,
* and remove "Add Existing" button in the associated view.
* @author Daniel Cai, http://danielcai.blogspot.com/
*
* Parameters:
* @param iframe: The IFrame's object, e.g. crmForm.all.IFrame_Employer_Address
* @param navItemId: LHS navigator's HTML element ID of the associated view.
It usually starts with "nav".
* @param relName: The relationship name, this parameter is only required
* when you want to remove "Add Existing" button.
* @param roleOrd: Role ordinary of the relationship, this parameter is only
* required for N:N relationship.
*/
function
loadAssociatedViewInIFrame(iframe, navItemId, relName, roleOrd) {var clickActionPattern = /loadArea\(['"]{1}([A-Za-z0-9_]+)['"]{1}\).*/;

url +=
url +=
url +=
url += (!roleOrd) ?

};



var getFrameSrc = function(areaId) {var url = "areas.aspx?oId=" + encodeURI(crmForm.ObjectId);"&oType=" + crmForm.ObjectTypeCode;"&security=" + crmFormSubmit.crmFormSubmitSecurity.value;"&tabSet=" + areaId;"" : "&roleOrd=" + roleOrd;return url;var removeAddExistingButton = function(frameDoc) {if (!frameDoc || !!roleOrd) { return; }var grid = frameDoc.all['crmGrid'];// Locate the "Add Existing" button using its magic id.


btn.parentNode.removeChild(btn);
}
};



removeAddExistingButton(frameDoc);
var btnId = '_MBtoplocAssocOneToMany' + grid.GetParameter('otc') + relName.replace(/_/g, "");var btn = frameDoc.getElementById(btnId);if (btn) {var onReadyStateChange = function() {if (iframe.readyState === 'complete') {var frameDoc = iframe.contentWindow.document;// Remove the padding space around the iframeframeDoc.body.scroll =
frameDoc.body.childNodes[0].rows[0].cells[0].style.padding =
}
};
(






}
navItem.style.display =

iframe.src = getFrameSrc(areaId);
iframe.allowTransparency =
"no";"0px";function init() {if (!crmForm.ObjectId) { return; }var navItem = document.getElementById(navItemId);if (!navItem) { return; }var clickAction = navItem.getAttributeNode('onclick').nodeValue;if (!clickAction || !clickActionPattern.test(clickAction)) {return;'none';var areaId = clickAction.replace(clickActionPattern, '$1');true; // Get rid of the white area around the IFrameiframe.attachEvent(
})();
};
'onreadystatechange', onReadyStateChange);

Monday, February 7, 2011

New Dynamics CRM 2011 Books from Amazon.Com

Product Details
Working with Microsoft Dynamics CRM 2011
- Paperback (Apr. 15, 2011) by Mike Snyder, Jim Steger, and Kristie Reid
Buy new: $49.99 $30.54
Available for Pre-order
Eligible for FREE Super Saver Shipping.

Email Record Links from a Dynamics CRM Workflow

Link from Richard Knudson's Dynamics CRM Trick Bag

It’s easy enough to email another CRM user a link to a record. If you open up a form for almost any record type in Dynamics CRM, you can pull down the Actions menu and select the Send Shortcut command to do this. Dynamics CRM will open up your mail client, and insert a rather complicated looking link which the recipient will be able to click to navigate directly to the record (assuming they have read permissions!)
For example, here’s what it looks like from an opportunity record:

Here’s what it looks like for an account record:

While your specific links will be different than mine (mine are for records contained in my CRM Online database, and the nasty-looking 25-character GUID uniquely identifies CRM records so it better be different!), the general structure will always be the same. I’ll come back to that point a little later, since it will solve a problem for us.

Problem: a Workflow-generated email cannot send a record link

So…while you can use the menu command I just mentioned to manually email a link to a record, you cannot email a link to a record from within a workflow. This might sound somewhat obscure, but it actually comes up a lot, and when you run into it, it just seems like something you should be able to do! Fortunately, there’s a relatively easy fix you can implement, with just a pinch of customization and a smidgeon of script. I’ll show you the solution next, but first, let me illustrate the problem in a little more detail.
Suppose I want a workflow to automatically send an email alert any time something important changes about an opportunity record. Here are a few scenarios to illustrate:
  • If the pipeline stage of an opportunity record changes, send an email alerting the sales manager.
  • If the current date is with three days of an open opportunity’s estimated close date, send a reminder email to the opportunity owner.
  • If an opportunity is closed as “Lost”, send an email alert to the owner’s manager.
You can come up with as many scenarios as there are organizations in the world, but in all of them, it would be nice to include a simple clickable link in the body of the email, so the recipient of the alert can navigate directly to the record in question.
As you know if you’ve read my book on Dynamics CRM workflows, there are many things you can do with Dynamics CRM workflows…but unfortunately, this is not one of them! I’ll illustrate with a simple alert email, sent by a workflow that runs automatically any time specified values of the opportunity entity change.
The following screen shot shows the Set Properties form for the workflow’s Send e-mail action. For demo purposes, the email simply goes to me, and I’ve used Dynamic Values to populate the body of the email with presumably interesting information about the current opportunity.
There is an eponymously titled field you can insert for any entity in Dynamics CRM, in this case, using the “{field name{entity name}}” characteristic of the workflow design environment, it’s the {Opportunity{Opportunity}} you can see in the figure. The problem with that is that it’s only a clickable link to the record if it’s on the Regarding field…and the Regarding field only appears if you happen to be viewing the email activity within Dynamics CRM.

Here’s what an email sent by this workflow looks like in Dynamics CRM (e.g., as a History item associated with the opportunity record):

But if, like most people as you prefer consuming your email in Outlook or a non-CRM email client, you never see that Regarding field. For example, Outlook:

Solution: A Pinch of Customization and a Smidgeon of Script (and URL Addressable Forms)

If you’ve read this far, I’m grateful, and you may have noticed the “Link to record” field that’s appeared in a couple of screen shots. That’s the solution, and it uses a technique called URL Addressable Forms, which simply means that every form in Dynamics CRM can be accessed via a unique URL, consisting of an entity-specific prefix combined with a suffix unique to a specific record. You can use the Send Shortcut command I started out by discussing to see what the prefix is for some common record types:
Common Record Types
Record TypeEdit Form Prefix
Accounthttps://imginc.crm.dynamics.com/sfa/accts/edit.aspx
Contacthttps://imginc.crm.dynamics.com/sfa/conts/edit.aspx
Opportunity https://imginc.crm.dynamics.com/sfa/opps/edit.aspx
Casehttps://imginc.crm.dynamics.com/cs/cases/edit.aspx
Marketing Campaignshttps://imginc.crm.dynamics.com/ma/camps/edit.aspx

You can use a custom attribute for any entity you want to include a clickable link for by following these steps:
  1. Customize the entity in question (e.g., Opportunity) by adding a new attribute. I added a custom attribute to the Opportunity entity, called it “Record Link” (it has a corresponding schema name), and gave it the following properties:


    The most important is to make it a Type of “nvarchar”, since that’s the only type that has the clickable link format of “URL”. Make sure it’s long enough. I made mine 200, but you can always come back and increase the maximum length (unlike the type and format values, which can’t be changed once the custom attribute is created.)

  1. Once the attribute is created, the question is how to put the value into it. This really is just a smidgeon of script code – one line of Jscript you can put in the Opportunity form’s OnSave event:

    crmForm.all.img_recordlink.DataValue = ‘https://imginc.crm.dynamics.com/sfa/opps/edit.aspx?id=’+crmForm.ObjectId;
That’s pretty much it. If you implement this you’ll notice that since I wrote this code for the form’s save event, the new “Record Link” field won’t contain anything until a record’s form has been saved at least once after you’ve saved and published theses customizations. Too bad there’s not a built-in Record Link attribute for every Dynamics CRM entity. I guess I should add that to my top X list of new features that should be included in Dynamic CRM 5!
I wrote a book on Dynamics CRM workflows, by the way, and it contains tons of examples like this one and other useful workflows. You can purchase the book on Lulu.com or on Amazon, and if you do, you can also get a (free) subscription to the online version of the book, where you can download the workflows themselves, customizations, and related content.
Here’s a link you can visit to find out more about my book and purchase it

Thursday, January 27, 2011

Error when Changing/Assigning Owner of CRM Entity

FOLLOWING THE DIRECTIONS BELOW AND REPLACING THE USER DEFINED FUNCTION YOU ARE COMPLETING AN UNSUPPORTED MODIFICATION.

This was a very difficult problem to find... I had two deployments (one development) that would not allow any user to change ownership on any entity.  After using the CRM Trace tool and spending a lot of time finding the error message I was able to find this article that provided the proper code to replace a the user defined function fn_CollectForCascadeAssign with the code, provided below and it worked.

REASON:
According the the Neil Benson, CRM Addict and Jim Glass MSFT Microsoft on the Microsoft Dynamics Forums CRM the issue was introduced by the regression error in Update Rollup 7. 

SUPPORTED FIX:
The Supported Method is the reinstall Update Rollup 8 or 9 after creating or importing a new organization.  Update Rollup 10 Fixes the issue.

SYMPTOMS
Messages in Trace File:
MessageProcessor fail to process message 'Assign' for 'account' - or any other entity

CRM Trace File Message: Crm Exception: Message: , ErrorCode: -2147204784, InnerException: System.Data.SqlClient.SqlException: Invalid column name 'q'.
Invalid column name 's'.
Invalid column name 'y'.

UNSUPPORTED FIX:
Replaced User Defined Function with the code below
USE [database_name]
GO
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go

ALTER function [dbo].[fn_CollectForCascadeAssign]
(
  @root_id uniqueidentifier,
  @root_otc int,
  @isoffline int,
  @old_owner uniqueidentifier
)
returns @t table
(    
   o uniqueidentifier,
   t int,
   p int default 0,
   u uniqueidentifier,
   q uniqueidentifier,
   s int,
   y bit default 0
)
as
begin
insert into @t values(@root_id,@root_otc,0,@old_owner,N'00000000-0000-0000-0000-000000000000',0,0)
if(exists(select * from @t where t=9100))begin insert into @t(o,t,p,u,q,s,y) select o.ReportId,9100,0,o.OwningUser,c.o,c.t,1 from Report o,@t c where o.ParentReportId=c.o and c.t=9100 and o.DeletionStateCode=0
while(@@rowcount <> 0)if(exists(select * from @t where t=9100))insert into @t(o,t,p,u,q,s,y) select o.ReportId,9100,0,o.OwningUser,c.o,c.t,1 from Report o,@t c where o.ParentReportId=c.o and c.t=9100 and o.DeletionStateCode=0 and o.ReportId not in(select o from @t where o=o.ReportId and t=9100) end
if(exists(select * from @t where t=4400))begin insert into @t(o,t,p,u,q,s,y) select o.ActivityId,4402,0,o.OwningUser,c.o,c.t,1 from CampaignActivity o,@t c where o.RegardingObjectId=c.o and c.t=4400 and o.DeletionStateCode=0 end
if(exists(select * from @t where t in(4400,4406)))begin insert into @t(o,t,p,u,q,s,y) select o.ActivityId,4401,0,o.OwningUser,c.o,c.t,1 from CampaignResponse o,@t c where o.RegardingObjectId=c.o and c.t in(4400,4406) and o.DeletionStateCode=0 end
if(exists(select * from @t where t=1))begin insert into @t(o,t,p,u,q,s,y) select o.AccountId,1,0,o.OwningUser,c.o,c.t,1 from Account o,@t c where o.ParentAccountId=c.o and c.t=1 and o.DeletionStateCode=0
while(@@rowcount <> 0)if(exists(select * from @t where t=1))insert into @t(o,t,p,u,q,s,y) select o.AccountId,1,0,o.OwningUser,c.o,c.t,1 from Account o,@t c where o.ParentAccountId=c.o and c.t=1 and o.DeletionStateCode=0 and o.AccountId not in(select o from @t where o=o.AccountId and t=1) end
if(exists(select * from @t where t=1))begin insert into @t(o,t,p,u,q,s,y) select o.ContactId,2,0,o.OwningUser,c.o,c.t,1 from Contact o,@t c where o.AccountId=c.o and c.t=1 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=2))begin insert into @t(o,t,p,u,q,s,y) select o.ContactId,2,0,o.OwningUser,c.o,c.t,1 from Contact o,@t c where o.ParentContactId=c.o and c.t=2 and o.DeletionStateCode=0
while(@@rowcount <> 0)if(exists(select * from @t where t=2))insert into @t(o,t,p,u,q,s,y) select o.ContactId,2,0,o.OwningUser,c.o,c.t,1 from Contact o,@t c where o.ParentContactId=c.o and c.t=2 and o.DeletionStateCode=0 and o.ContactId not in(select o from @t where o=o.ContactId and t=2) end
if(exists(select * from @t where t=1))begin insert into @t(o,t,p,u,q,s,y) select o.InvoiceId,1090,0,o.OwningUser,c.o,c.t,1 from Invoice o,@t c where o.AccountId=c.o and c.t=1 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=2))begin insert into @t(o,t,p,u,q,s,y) select o.InvoiceId,1090,0,o.OwningUser,c.o,c.t,1 from Invoice o,@t c where o.ContactId=c.o and c.t=2 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=2))begin insert into @t(o,t,p,u,q,s,y) select o.SalesOrderId,1088,0,o.OwningUser,c.o,c.t,1 from SalesOrder o,@t c where o.ContactId=c.o and c.t=2 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=1))begin insert into @t(o,t,p,u,q,s,y) select o.SalesOrderId,1088,0,o.OwningUser,c.o,c.t,1 from SalesOrder o,@t c where o.AccountId=c.o and c.t=1 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=1088))begin insert into @t(o,t,p,u,q,s,y) select o.ActivityId,4209,0,o.OwningUser,c.o,c.t,1 from OrderClose o,@t c where o.SalesOrderId=c.o and c.t=1088 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=1))begin insert into @t(o,t,p,u,q,s,y) select o.QuoteId,1084,0,o.OwningUser,c.o,c.t,1 from Quote o,@t c where o.AccountId=c.o and c.t=1 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=2))begin insert into @t(o,t,p,u,q,s,y) select o.QuoteId,1084,0,o.OwningUser,c.o,c.t,1 from Quote o,@t c where o.ContactId=c.o and c.t=2 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=1084))begin insert into @t(o,t,p,u,q,s,y) select o.ActivityId,4211,0,o.OwningUser,c.o,c.t,1 from QuoteClose o,@t c where o.QuoteId=c.o and c.t=1084 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=2))begin insert into @t(o,t,p,u,q,s,y) select o.ContractId,1010,0,o.OwningUser,c.o,c.t,1 from Contract o,@t c where o.ContactId=c.o and c.t=2 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=1))begin insert into @t(o,t,p,u,q,s,y) select o.ContractId,1010,0,o.OwningUser,c.o,c.t,1 from Contract o,@t c where o.AccountId=c.o and c.t=1 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=1))begin insert into @t(o,t,p,u,q,s,y) select o.IncidentId,112,0,o.OwningUser,c.o,c.t,1 from Incident o,@t c where o.AccountId=c.o and c.t=1 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=2))begin insert into @t(o,t,p,u,q,s,y) select o.IncidentId,112,0,o.OwningUser,c.o,c.t,1 from Incident o,@t c where o.ContactId=c.o and c.t=2 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=112))begin insert into @t(o,t,p,u,q,s,y) select o.ActivityId,4206,0,o.OwningUser,c.o,c.t,1 from IncidentResolution o,@t c where o.IncidentId=c.o and c.t=112 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=1))begin insert into @t(o,t,p,u,q,s,y) select o.LeadId,4,0,o.OwningUser,c.o,c.t,1 from Lead o,@t c where o.AccountId=c.o and c.t=1 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=2))begin insert into @t(o,t,p,u,q,s,y) select o.LeadId,4,0,o.OwningUser,c.o,c.t,1 from Lead o,@t c where o.ContactId=c.o and c.t=2 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=2))begin insert into @t(o,t,p,u,q,s,y) select o.OpportunityId,3,0,o.OwningUser,c.o,c.t,1 from Opportunity o,@t c where o.ContactId=c.o and c.t=2 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=1))begin insert into @t(o,t,p,u,q,s,y) select o.OpportunityId,3,0,o.OwningUser,c.o,c.t,1 from Opportunity o,@t c where o.AccountId=c.o and c.t=1 and o.DeletionStateCode=0 end
if(exists(select * from @t where t=3))begin insert into @t(o,t,p,u,q,s,y) select o.CustomerOpportunityRoleId,4503,0,o.OwningUser,c.o,c.t,1 from CustomerOpportunityRole o,@t c where o.OpportunityId=c.o and c.t=3 and o.DeletionStateCode=0 end
if(exists(select * from @t where t in(1088,2,3,1010,1090,4,1,112,1084)))begin insert into @t(o,t,p,u,q,s,y) select o.ActivityId,4214,0,o.OwningUser,c.o,c.t,1 from ServiceAppointment o,@t c where o.RegardingObjectId=c.o and c.t in(1088,2,3,1010,1090,4,1,112,1084) and o.DeletionStateCode=0 end
if(exists(select * from @t where t in(1084,112,1,4400,4,1090,1010,3,2,1088)))begin insert into @t(o,t,p,u,q,s,y) select o.ActivityId,4212,0,o.OwningUser,c.o,c.t,1 from Task o,@t c where o.RegardingObjectId=c.o and c.t in(1084,112,1,4400,4,1090,1010,3,2,1088) and o.DeletionStateCode=0 end
if(exists(select * from @t where t in(1088,2,3,4406,1010,1090,4402,4,1,1084,112)))begin insert into @t(o,t,p,u,q,s,y) select o.ActivityId,4210,0,o.OwningUser,c.o,c.t,1 from PhoneCall o,@t c where o.RegardingObjectId=c.o and c.t in(1088,2,3,4406,1010,1090,4402,4,1,1084,112) and o.DeletionStateCode=0 end
if(exists(select * from @t where t=3))begin insert into @t(o,t,p,u,q,s,y) select o.ActivityId,4208,0,o.OwningUser,c.o,c.t,1 from OpportunityClose o,@t c where o.OpportunityId=c.o and c.t=3 and o.DeletionStateCode=0 end
if(exists(select * from @t where t in(112,1084,1,4402,4,1010,4406,1090,3,1088,2)))begin insert into @t(o,t,p,u,q,s,y) select o.ActivityId,4207,0,o.OwningUser,c.o,c.t,1 from Letter o,@t c where o.RegardingObjectId=c.o and c.t in(112,1084,1,4402,4,1010,4406,1090,3,1088,2) and o.DeletionStateCode=0 end
if(exists(select * from @t where t in(1088,2,3,4406,1090,1010,1,112,1084,4,4402)))begin insert into @t(o,t,p,u,q,s,y) select o.ActivityId,4204,0,o.OwningUser,c.o,c.t,1 from Fax o,@t c where o.RegardingObjectId=c.o and c.t in(1088,2,3,4406,1090,1010,1,112,1084,4,4402) and o.DeletionStateCode=0 end
if(exists(select * from @t where t in(4402,4,1084,112,1,4700,1090,4406,1010,3,1088,2)))begin insert into @t(o,t,p,u,q,s,y) select o.ActivityId,4202,0,o.OwningUser,c.o,c.t,1 from Email o,@t c where o.RegardingObjectId=c.o and c.t in(4402,4,1084,112,1,4700,1090,4406,1010,3,1088,2) and o.DeletionStateCode=0 end
if(exists(select * from @t where t in(2,1088,3,4406,1010,1090,112,1,1084,4402,4)))begin insert into @t(o,t,p,u,q,s,y) select o.ActivityId,4201,0,o.OwningUser,c.o,c.t,1 from Appointment o,@t c where o.RegardingObjectId=c.o and c.t in(2,1088,3,4406,1010,1090,112,1,1084,4402,4) and o.DeletionStateCode=0 end
if(exists(select * from @t where t in(4400,4414,4211,4212,4703,4401,4207,4209,4201,1088,4208,2,1090,4204,4202,4206,1010,4300,3,1,1084,112,4402,4214,4,4210)))begin insert into @t(o,t,p,u,q,s,y) select o.AnnotationId,5,0,o.OwningUser,c.o,c.t,1 from Annotation o,@t c where o.ObjectId=c.o and c.t in(4400,4414,4211,4212,4703,4401,4207,4209,4201,1088,4208,2,1090,4204,4202,4206,1010,4300,3,1,1084,112,4402,4214,4,4210) and o.DeletionStateCode=0 end
return
end

/

Tuesday, January 11, 2011

Hide a form field

Now that each field offers an OnChange event, we want to use it. At least I do. Based on the selection or values in another field, I'm disabling fields all over the screen. But why disabling and not hide them?! It's not documented and therefore not supported, but it sometimes is needed. The code to hide a field is:

crmForm.all.name_c.style.visibility = 'hidden';
crmForm.all.name_d.style.visibility = 'hidden';

Monday, January 10, 2011

Microsoft Online Services Team Blog - Link...

Technical discussions on the Microsoft Online Services and the various aspects of each of these services.

Hosted Blackberry Service for Microsoft Online (BPOS)

Microsoft Online Services is now a BlackBerry® certified partner and BlackBerry Alliance Elite Member. For the Business Productivity Online Standard Suite, licenses and support are now a part of the Microsoft Online Services Hosted BlackBerry service, which customers buy directly from Microsoft. Previously, customers had to purchase and provide their own licenses and support agreement, purchased through a third party, in order to use the Hosted BlackBerry service.

The Hosted BlackBerry service from Microsoft Online Services offers the most commonly requested BlackBerry capabilities, including wireless access to e-mail, calendar, tasks and contacts with global address list (GAL) integration, and device management such as device wipe and password reset. Details about the Hosted Blackberry Service can be found in the Microsoft Online Services  Mobility Solutions Description, which can be downloaded from the Microsoft Download Center. The document includes a full solution description and service pricing.

To learn more or to request a Hosted BlackBerry service agreement, contact your Microsoft representative or a Microsoft Online Services partner. If you do not have a Microsoft partner or representative, you may request the Hosted BlackBerry service agreement through Microsoft Online Services Customer Service.

For additional details please about the Microsoft Online Services mobility offerings, visit www.microsoft.com/online/mobility.