+ Start a Discussion
jprosserjprosser 

Clone question

Hi Folks,
I would like to make a copy of an opportunity and all its line-items when someone clicks on a custom button (via an S-Control calling a web-service using Apex)

I figure I will pass the Opportunity ID as a parameter in the webservice call, but now I'm having a problem with the cloning part.

If I initialize an Opportunity object "opp" with an SOQL select statement, and then call "opp.clone(true), am I only cloning the object that I initialized? I want to make a copy of the entire thing.  I'm missing a fundamental concept I think.

here's the Apex routine.

WebService static String duplicateOpp(String arg) {
      String test = '005E0000003Relo';
      Opportunity opp = [ select Id,Name from opportunity where id=:test];
     
      Opportunity newOpp = opp.clone( true);
     
      update newOpp;
     
       
      return 'Copied ' + newOpp.Name + ' of type ' + newOpp.Type;
    }
Best Answer chosen by Admin (Salesforce Developers) 
sfdcfoxsfdcfox
You're using an extra query and DML call... this is Not Good™. Instead, just clone and then insert.
 
Code:
Opportunity opp=[select Id,Name from opportunity where id='XXXX000003Relo'];
Opportunity newOpp = opp.clone(false, true);
newOpp.Name=opp.Name + ' CLONE';
insert newOpp;

 
When you are cloning a record, it's an in-memory reference; clearing the ID field lets you insert it as a new opportunity.

All Answers

soofsoof
Hi,
 
I think when you call the clone method, you're just creating another reference to the same object, and NOT creating a new object/ record in Salesforce database.  That's what I can gather from the documentation http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_methods_object_apex.htm
 
Let me know if you think that this is not what the documentation means.
 
Thanks.
micwamicwa
The link in the last post is not the method you are using. If you use the clone method of the sObject (what you did) and the parameter is true then it will create a deep copy. You will have a new opportunity that is not linked with the original one. If you use false as parameter you will only have a shallow copy that is a reference to the original opp.


clone Boolean opt_preserve_id, Boolean opt_IsDeepClone<sObject> (of same type)Creates a copy of the sObject record.

The optional opt_preserve_id argument determines whether the ID of the original object is preserved or cleared in the duplicate.

The optional opt_IsDeepClone argument determines whether the method creates a full copy of the sObject, or just a reference:
  • If set to true, the method creates a full copy of the sObject. All fields on the sObject are duplicated in memory, including relationship fields. Consequently, if you make changes to a field on the cloned sObject, the original sObject is not affected.
  • If set to false, the method creates a reference to the original sObject. Consequently, if you make changes to a field on the cloned sObject, the original sObject is also affected.

jprosserjprosser
Thanks!  But I still can't make a copy of an object.

If I execute this:
Code:
      String test = 'SOMEID';
      Opportunity opp = [ select Id,Name from opportunity where id=:test];     
      Opportunity newOpp = opp.clone(true, true);
      newOpp.Name=opp.Name + ' CLONE';   
      update newOpp;

I don't get a copy, presumably because I've told clone to preserve the ID.

If I call clone like this:
   Opportunity newOpp = opp.clone(false, true);
then the Id is cleared and the update throws an exception.

How do I generate a new Id for the clone? Maybe this is all I need to do?

Thanks,
-Joe



micwamicwa
You can't generate a new id, you will get the id like this:

Code:
 String test = 'SOMEID';
      Opportunity opp = [ select Id,Name from opportunity where id=:test];     
      Opportunity newOpp = opp.clone(false, true);
     Id cloneId = newOpp.Id;

 The exception you get must be because of something else.

jprosserjprosser
If I paste this block of code in the "Execute Apex" window of the system log,

Code:
Opportunity opp = [ select Id,Name from opportunity where id='XXXX000003Relo'];

Opportunity newOpp = opp.clone(false, true);
Id newid = newOpp.Id;
newOpp.Name=opp.Name + ' CLONE';

update newOpp;

 


I get the following:

Code:
20080806190706.191:AnonymousBlock: line 3, column 7: DeclareVar: SOBJECT:Opportunity opp 
20080806190706.191:AnonymousBlock: line 3, column 25: SOQL query with 1 row finished in 10 ms
20080806190706.191:AnonymousBlock: line 3, column 7: initial value: Opportunity:{Name=New Cust , Id='XXXX000003Relo'}
20080806190706.191:AnonymousBlock: line 6, column 7: DeclareVar: SOBJECT:Opportunity newOpp
20080806190706.191:AnonymousBlock: line 6, column 7: initial value: Opportunity:{Name=New Cust }
20080806190706.191:AnonymousBlock: line 7, column 1: DeclareVar: Id newid
20080806190706.191:AnonymousBlock: line 7, column 1: initial value: null
20080806190706.191:AnonymousBlock: line 8, column 7: SOBJECT:Opportunity newOpp.Name <= Add
20080806190706.191:AnonymousBlock: line 10, column 7: Update: SOBJECT:Opportunity
20080806190706.191:AnonymousBlock: line 10, column 7: DML Operation executed in 12 ms System.DmlException: Update failed. First exception on row 0; first error: MISSING_ARGUMENT, Id not specified in an update call AnonymousBlock: line 10, column 7

 it looks like the id of the clone is null, and this is what is causing the update to fail.
-Joe

micwamicwa
Ok, after the exception I might know what the mistake is. You need to insert the opportunity after cloning. Try that code:
Code:
Opportunity opp = [ select Id,Name from opportunity where id='XXXX000003Relo'];
      
Opportunity newOpp = opp.clone(false, true);
insert newOpp;

Opportunity newOppInserted = [ select Id,Name from opportunity where id=:newOpp.Id];
newOppInserted.Name=opp.Name + ' CLONE';
 
update newOppInserted;

 

sfdcfoxsfdcfox
You're using an extra query and DML call... this is Not Good™. Instead, just clone and then insert.
 
Code:
Opportunity opp=[select Id,Name from opportunity where id='XXXX000003Relo'];
Opportunity newOpp = opp.clone(false, true);
newOpp.Name=opp.Name + ' CLONE';
insert newOpp;

 
When you are cloning a record, it's an in-memory reference; clearing the ID field lets you insert it as a new opportunity.
This was selected as the best answer
jprosserjprosser
Thank you very much!
o2bo2b

I tried this, not all fields are getting copied. Any idea????

micwamicwa

You must select all the fields in the SOQL statement you want to clone.

Christopher_Alun_LewisChristopher_Alun_Lewis

Micwa is quite right, all of the fields have to be present in the object in your Apex code, either through using a SOQL query or populating the details manually.

 

Since coming accross this cloning details issue, I have created a generic cloning method that can be used to create a copy of any object, populating all of the fields values in the process without having to retrieve the details through SOQL before hand.

 

This method can be found in my blog post: How to dynamically clone Salesforce objects with all fields populated.

 

Hope it helps anyone still looking at this issue.