In a lot of projects we do for our customers with Microsoft Dynamics CRM 3.0 I find it very usefull that, though the relationships are not existing, it is still possible to access the field mappings areas to specify mappings between Opportunity Product, Quote Product, Salesorder Product and Invoice Product. This Microsoft Support Article tells how to do this.
(Custom) mappings between those entities are wanted when custom fields are added to them. When converting an opportunity to a quote, or quote to salesorder or salesorder to invoice, you want the underlying details to be copied completely, including your own custom fields.
But, it is not that easy to do. You have to open pretty complex webpages (i.e. webservice descriptions and webservice result xml) which most (functional) consultants haven't seen before and than copy and paste guid's (who want's to deal with guids?) into the final field mappings area url to do the job. Above all, this has to be done physically on the CRM server itself (at least the first steps).
So, I decided to include the (links to the) mapping pages into our standard product settings module. Pretty easy to do. Nothing else than hyperlinks to the following url (as documented in the Microsoft Support Article):
http://crmserver:5555/tools/systemcustomization/relationships/mappings/mappinglist.aspx?mappingid={mapid}
where
crmserver:5555 needs to be replaced by the correct server:port address. And
{mapid} must contain the id (guid) of the specific field mapping area.
But: how to dynamically obtain the correct mapid? (Microsoft Support says in their article mentioned that those id's differ for each environment!).
The
Microsoft Support Article describes how to obtain the mapid's:
- open a url displaying the description for webservice "EntityMap"
- click a hyperlink to display the webmethod "RetrieveEntityMaps"
- as test parameter, fill in the specific source entityname and click "Invoke"
- find in the displayed result xml the correct mapid and copy this
- (now you can paste the mapid into the final url (see above) to bring up the desired mappings area)
Step 1 to 4 can be easily automated in a C# method which returns the mapid (e.g. a webmethod which can be called from Javascript, or inside an .aspx code-behind page). Here's the code:
string getMapId(string entityFrom, string entityTo)
{
try
{
//create httprequest to call the "RetrieveEntityMaps" webmethod
string urlFromRegistry = (string)Registry.LocalMachine.CreateSubKey("SOFTWARE\\Microsoft\\MSCRM").GetValue("ServerURL");
string uri = Path.Combine(urlFromRegistry, "entitymap.asmx/RetrieveEntityMaps");
string data = "entityName=" + entityFrom;
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
req.Method = WebRequestMethods.Http.Post;
req.UseDefaultCredentials = true;
req.ContentLength = data.Length;
req.ContentType = "application/x-www-form-urlencoded";
//and add the parameter
StreamWriter wr = new StreamWriter(req.GetRequestStream());
wr.Write(data);
wr.Close();
//get the response
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
//load it into a XmlDocument
XmlDocument doc = new XmlDocument();
doc.Load(resp.GetResponseStream());
resp.Close();
//Select the mapid using a Xpath expression
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("ns", "http://schemas.microsoft.com/crm/2006/WebServices");
string xPath = "//ns:entitymap[ns:targetentityname = '" + entityTo + "']/ns:entitymapid";
XmlNode node = doc.SelectSingleNode(xPath, nsmgr);
//Return the InnerText of the found node
if (node != null) return node.InnerText;
return "";
}
catch (Exception e)
{
//probably an authorization error...
return "";
}
}
Note that this code uses undocumented CRM features (at least not documented in the SDK). This makes it unsupported (but I bet it will continue working in 4.0, though I haven't tested it yet).