Sunday, October 11, 2009

CRM 4.0 cloning using entity mapping




In the past I wrote a few posts about cloning an entity using client side scripting. Most of the posts implemented complex script especially to non developers. I wanted to show you a simple cloning technique that leverages CRM built in features and can also work for CRM online.

The benefits of using this technique are:
1. Gain full control over which attributes are cloned
2. Ability to change which attributes are cloned without adding / changing your code.
3. Usage of a very simple script that does not need to be changed when reused.
4. Ability to easily track the parent record from which the cloned entity originated.

Following is the list of built in CRM features I am going to utilize in the post:
1. Creation of CRM form toolbar button
2. Creation of entity relationship.
3. Creation of mapping between related entities.
4. Adding simple script to the cloned entity onload handler.

So how can we actually make the above features work to our advantage. The main feature that lies in the heart of this technique is the ability to create a self referencing relationship between the an entity to itself and use the mapping wizard to tell CRM which attributes we what to pass from the parent record. Once we have that all that remains is to understand how CRM uses the mapping on the client side much like when you create a child contact record from within an account from.

If you take a close look at the URL that is used by CRM when you create a child related record you’ll notice that the URL uses a specific format that tells CRM what is the parent record id and type. Once you learn how to replicate this behavior you’re half way implementing this solution.
The following script which you’ll eventually need to paste in the entity onload event handler shows how to construct the child record or in our case the cloned record url:


Clone = function()
{
var cloneUrl = location.pathname + "?";
cloneUrl += "_CreateFromType=" + crmForm.ObjectTypeCode +
cloneUrl += "&_CreateFromId=" + crmForm.ObjectId +
cloneUrl += "&etc=" + crmForm.ObjectTypeCode + "#";

var cloneFeatures = 'toolbars=0,status=1,width=' + document.body.offsetWidth + "height=" + document.body.offsetHeight;

window.open(cloneUrl,'',cloneFeatures);
}


Once we have the script in place we need to add a toolbar button that will fire the actual cloning process.
Following is a sample clone button xml which you should add to your isv.config


<Entity name="gi_test">
<ToolBar ValidForCreate="0" ValidForUpdate="1">
<Button Icon="/_imgs/ico_18_debug.gif" JavaScript="Clone();">
<Titles>
<Title LCID="1033" Text="Clone" />
</Titles>
<ToolTips>
<ToolTip LCID="1033" Text="Clone" />
</ToolTips>
</Button>
<ToolBarSpacer />
</ToolBar>
</Entity>


Next you’ll need to create a self referencing relationship. To do that open the entity customization and select 1:N relationship. Then select create new relationship. Inside the relationship form choose the same entity on both sides of the relationship. You should select not to display the left navigation link. The bellow image encapsulates the process of creating the self referencing relationship.



Once the relationship is saved you’ll see a mapping link on the relationship form. Select the mapping link to open the mapping wizard.
Add as much mapping as required. Before you publish the entity consider the final step.



Finally, if you wish to track the originating record you only need to add the entity lookup (parent test in this case) that was created as a result of the self referencing relationship. You can also set the lookup field as read only so users can’t change it manually.



That’s it! publish your entity and you’re done.

32 comments:

Vijaya said...

Adi,
I want to implent this clone functionality for a custum entity. Does it works?

Adi Katz said...

This works for customizable and custom entities. The example in the post uses a custom entity called test.

Lucky said...

Hi Adi,
I am very much impressed with this Clone method. I tried it to clone a Quote record. Steps I followed:
1.I created a Quote selfreference relationship and mapped attributes
2. Created a custom Clone Button using ISV config.
3.I added your javascript code on Quote onload event and published the customisations.

I am getting Javascript error on Quote Onload event.
Can you please tell me how to solve this error and attain the functionality.

Adi Katz said...

The process you described sounds right. What error are you getting?

Darren Mercieca said...

Hi Adi, Great Post. I am now attempting to promt the user to enter how may times he would like to clone it, instead of only once. Do you think this is possible? Or do you have any suggestions? Thanks

Lucky said...

Hi Adi,
When I click on Clone button, the page is redirecting CRM error message Window with message 'An error has occurred. try again ..'
Can you please help me.

Adi Katz said...

Darren, you can prompt the user for the number of duplicates and run the onload code x times but I it’s not usable. I suggest letting the user creating a clone out of an existing clone i.e. original  clone1 (new)  clone1(saved)  clone2(new)  clone2(saved)  clone3…

Adi Katz said...

Lucky,

Different entities might require different URLs when creating a child (quote) in a parent (quote) context. I have not tested all , this is just an example. You should create a quote self referencing relationship and create it manually in CRM, then open the child quote and observe its URL. If it’s different from the one the onload code produces you’ll need to make the necessary adjustments in your code.

Anonymous said...

TY for all your extremely useful blog posts!

there seems to be a mistake in the Clone method. ti should read:

Clone = function() {
var cloneUrl = location.pathname + "?";
cloneUrl += "_CreateFromType=" + crmForm.ObjectTypeCode;
cloneUrl += "&_CreateFromId=" + crmForm.ObjectId;
cloneUrl += "&etc=" + crmForm.ObjectTypeCode + "#";

var cloneFeatures = 'toolbars=0,status=1,width=' + document.body.offsetWidth + "height=" + document.body.offsetHeight;

window.open(cloneUrl, '', cloneFeatures);
}

Anonymous said...

Dear Adi,

To start with I placed your ISV code in my ISV config file and imported for the quote entity. I can see the "Clone" button in my VPC but cannot find the button in my test server.
The only difference betwen my VPC and test server is rollup 7 which is not in my vpc.
Kindly advice pls....!

Regards
Prince

Adi Katz said...

By Default the ISV buttons are not shown in the application. You need to change your system settings and refresh the form.

Anonymous said...

Thanx a lot Adi for your generous support, I forgot to enable the ISV config in application mode.

Now I've
1.I created a Quote selfreference relationship and mapped attributes
2. I added your javascript code on Quote onload event as it is and published the customisations

When i click onto the clone button nothing seems to happen. As per the above comments as u said I have to make necessary adjustments in the code which I'm not sure where I've to tweak?
Do I've to add the url of my quote entity in ur code? If yes, the url for
Quote-> http://crm/Contoso/sfa/quotes/edit.aspx?id={863084E1-CDD1-DE11-AE58-0003FFAFD723}#

Quote Detail -> http://crm/Contoso/sfa/quotedetail/edit.aspx?_CreateFromType=1084&_CreateFromId=%7b863084E1-CDD1-DE11-AE58-0003FFAFD723%7d

I dunno wat to add and wat not to? One more thing i noticed is that I've a price list.After adding the code when I click on price list lookup button in quote all my price list has disappeared? I find ur code among all others the most convienient to implement. hence pls advice how to proceed....

Regards
Prince

Anonymous said...

The above code in the comments section for onload event seems to be working.
Using the code above from the comments when I click the clone button another windows is poping up but the page displays " An error has occured...Pls contact microsoft....". Dunno where is the issue??

Matthew said...

Thanks for this Adi, I am also getting "An error has occured...Pls contact microsoft...." when I click on the clone button.

I suspect the URL needs to change in the Onload event depending on whether the Entity is custom or not.
Will continue playing with this.

Paul said...

Adi - Great way to clone records, thanks.

Would it be a quick addition to fire this on Create? I would like a "Save & Clone" button (similar to the "Save & New" button).

Thanks for all your community support!

Adi Katz said...

Try adding crmForm.Save(); after the window.open call.

Max said...

Hi,

Personally, I had to do this kind of action in the past. However, instead of doing it in JScript, I created a new manual Workflow.

My question is what are the advantages to use a JScript instead of a workflow to clone an entity?

The workflow is configured that way ([xyz] is the entity that you want to clone):
1) Create a new workflow
1.1) Workflow name: Clone [xyz]
1.2) Entity: [xyz]
1.3) Click "OK"
2) Configure the workflow
2.1) Check "On Demand"
2.2) Uncheck everything in the "Start When" section
2.3) Change the scope to "Organization"
2.4) Click "Add Step"
2.4.1) Select "Create Record"
2.4.2) Select [xyz]
2.4.3) Click "Set Properties"
2.4.4) Use the form assistant to put dynamic values
2.4.4.1) Briefly, this is your mapping
2.4.5) Click "Save and close"
2.5) Rename the "Type a step description here." to "Create a copy of the record selected.”
2.6) Click on "Publish"

You can also add some pre-conditions and post-condition to it, but I guess you can understand the possibilities…

If you want to use this new workflow:
1) Go to your [xyz] entity (or use an advanced query)
2) Select all entities that you want to clone
2.1) Click “Run Workflow…”
2.2) Select “Clone [xyz]”
2.3) Click “OK”
2.4) Click “OK”

The “Clone [xyz]” workflow will be executed to all entities selected.

Chris Rogers said...

This is a really nice customization, Adi. Most of the folks citing errors here probably haven't been able to work through the errors in the Clone function definition. Anonymous, on 2009 Oct 24, got close but still missed the ones in the cloneFeatures assignment. Here's my corrected version:


Clone = function()
{
var cloneUrl = location.pathname + "?"
+ "_CreateFromType=" + crmForm.ObjectTypeCode
+ "&_CreateFromId=" + crmForm.ObjectId
+ "&etc=" + crmForm.ObjectTypeCode + "#";

var cloneFeatures = 'toolbars=0,status=1,width=' + document.body.offsetWidth + ',height=' + document.body.offsetHeight;

window.open(cloneUrl,'',cloneFeatures);
}

Adi Katz said...

Thanks Chris! I Appreciate the input.

Carl said...

Hi Adi,

Great sollution. I'd like to make this work to duplucate orders. And as you know: an order can be linked to one or several products. The total ammount of these products will then appear in the order.

Is it possible to not only copy the fields, but also the relationships to the products. So that the same total ammount would appear on the order?

Thanks,

Ashish said...

Hi Can it is possible at the time of cloning opportunity i can also clone the product existing in that opportunity..

If u did this previously then pls guide me ..

mthulisi said...

Hi,
Great Solution.I tried to clone a quoute. This work fine, but i also need to clone the products in the quote so that the two quotes have exactly the same products and totals. Its the same thing with the orders and invoice. How do i do this?Please help?

Laurie said...

This code worked really well on a custom entity. But, I cannot get this to work on a customizable entity. Can this be used on customizable entities? I get a generic CRM error on the window open statement.

Anonymous said...

Hi,
This work fine, but i also need to clone the products in the quote so that the two quotes have exactly the same products and totals. Its the same thing with the orders and invoice. How do i do this?

Thanks,

Anonymous said...

Hi Adi

I have a situation where need to clone parent-child entities with 1:N relation (think Customer with multiple addresses where customer and address are different entities). Need to clone customer but also clone the related addresses. Any help is appreciated.

yeshi said...

Hi Adi
I am very very impressed with your posting very helpful. I am a new MS CRM user, I am suppose to customize a very populated form. with lots of field to collect data. no matter how I arrange the fields the form looks very ugly. I tried the collapsing section or hide/unhide samples you already posted. they did not resolve the issue.
I would like to have a check box (if available in CRM) and based on the check box I want the fields to appear or not appear.Please help if you have any suggestion or any link , that would be very great
Thank you in advance

Shashi said...

Adi, really wonderful customization. Easiest way to clone a record i have seen yet. I have followed the steps posted by you on the load event of form and it's working fine from the record. But when I add the following JavaScript on the menu bar button of an entity it's giving error. It's not uploading the ISV.config file. I have modified the code to work from menu bar of an entity.

"if (crmGrid.InnerGrid.SelectedRecords.length == 1){var typeCode = crmGrid.InnerGrid.SelectedRecords[0][1]; var id = crmGrid.InnerGrid.SelectedRecords[0][0];
var cloneUrl= '/AnnetTechnologies/userdefined/edit.aspx?';
cloneUrl += '_CreateFromType=' + typeCode +'&_CreateFromId=' + id + '&etc=' + typeCode + '#';
var cloneFeatures = 'toolbars=0,status=1,width=600,height= 400';
window.open(cloneUrl,'',cloneFeatures);
}"

Can you tell me how can i clone the record from menu bar button as from the record toolbar button.

Thx,
Shashi

Shashi said...
This comment has been removed by the author.
Shashi said...
This comment has been removed by the author.
Shashi said...

Guys, the error was in the line #3.
cloneUrl += '_CreateFromType=' + typeCode +'&_CreateFromId=' + id + '&etc=' + typeCode + '#';

The preceeding & ampersand sign in the parameter &_CreateFromId= and &etc= should replace with amp&;_CreateFromId= and amp&;_etc=

Sorry for delaying the post.

Thx,
Shashi

Rajeev said...

I have used the similar code. It is working absolutely fine.

But recently when I added some attributes to the form I found that when the new copy of the form is opened then all the attributes get added to it,
but values of new attributes are not appearing. could please help me in this regard.

finisterre said...

Any chance of getting this working with hosted CRM 2011?

Can it clone opportunities that are both open and closed?