Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

I have a requirement to deep clone (Parent -> Child -> Grand Child) records by clicking a button at the Parent Level. I believe Salesforce supports only Clone at the Parent Level, it does not have Deep Clone Out Of the Box. Has anyone ever built it ?

share|improve this question
add comment

4 Answers

Apex: In terms of building a solution in Apex code, there is some partial support for this you can leverage. The SObject.clone method and also JSON support can help build solutions to achieve this in code, but there is no platform deep clone.

SObjectDataLoader.cls: I've recently leveraged the JSON Apex support to implement a native SObject import/export that has the same requirement. Take a look at SObjectDataLoader. Which will perform a deep serialize (and thus deserialize) of child records and related records. By combing the two operations like so you can implement a generic Apex deep clone by passing only Id's of the master records.

Set<ID> clonedMasterIds =
     SObjectDataLoader.deserialize( SObjectDataLoader.serialize(masterIdsToDeepClone) );

You can read more about this here, cloning is one of many use cases that could be applied. The serialize and deserialize methods are configurable to control what gets cloned and/or resolved during deserialization.

enter image description here

AppExchange: There is also a number of AppExchange solutions if you wanted something already pre-built.

share|improve this answer
add comment

You could leverage the power of External Ids to join up the objects and use the sObject Clone Method. External Ids let you dynamically match the records, without having to explicitly set the ids of the parent records being created in the clone.

Let's take the example of Account -> Opportunity -> Opportunity Product

List<Account> accts = new List<Account>{};
List<Opportunity> opptys = new List<Opportunity>{};
List<OpportunityLineItem> olis = new List<OpportunityLineItem>{};

Account acc = [Select Id, Name .... from Account];

Account accNew = acc.clone(false, true); //See sObject Clone Method
accNew.ExternalId__c = acc.Name + Datetime.now(); // set unique external Id
accts.add(accNew);

for(Opportunity opp : [Select Id, Name, ... (Select Id, ... from OpportunityLineItems) from Opportunity where AccountId = :acc.Id]){
Opportunity newOpp = opp.clone(false, true);
newOpp.ExternalId__c = opp.Name + Datetime.now(); //set unique external id

newOpp.Account = new Account(ExternalId__c = accNew.ExternalId__c); //set parent ref

opptys.add(newOpp);

for(OpportunityLineItem oli : opp.OpportunityLineItems){

OpportunityLineItem oliNew = oli.clone(false, true);
oliNew.Opportunity = new Opportunity(ExternalId__c = newOpp.ExternalId__c); //set parent

olis.add(oliNew);
}
}
}
}

insert accts;
upsert opptys; //upsert matches to accounts using external id
upsert olis; //upsert matches to opportunities using external id
share|improve this answer
add comment

Well I guess to accomplish the same just have to maintain the order in which DML is issued. Since with every DML calls the Id field of sobject is automatically populated. You can then leverage the power of collection(which stores the reference of object rather than value) to map the child and grand child records.

Extending the *Account -> Opportunity -> Opportunity Product further.

The Util Class

class AccountDeepCloneUtil 
{
    public static void cloneAccount(Account acc) 
    {
        Map<Opportunity,List<OpportunityLineItems>> OpportunityLineItemsMapping = new Map<Opportunity,List<OpportunityLineItems>>();
        List<Opportunity> newOpportunityList = new List<Opportunity>();
        List<Opportunity> oppList = [Select Id, Name, ... (Select Id, ... from OpportunityLineItems) from Opportunity where AccountId = :acc.Id];
        Account newAcc = acc.clone(false, true); //do a deep clone
        //insert the account record
        insert newAcc;
        for(Opportunity opp : oppList){
            Opportunity newOpp = opp.clone(false, true); //do a deep clone
            newOpp.Account = newAcc.Id;
            newOpportunityList.add(newOpp);
            OpportunityLineItemsMapping.put(newOpp,opp.OpportunityLineItems.deepClone(false,false,false));
        }
        //insert opportunity
        insert newOpportunityList;

        for(Opportunity opp :OpportunityLineItemsMapping.keySet()){
            for(OpportunityLineItemoppLineItem : OpportunityLineItemsMapping.get(opp)){
                OpportunityLineItem.OpportunityId = opp.Id;
            }
        }

        insert OpportunityLineItemsMapping.values();
    }
}

How to use the class?

AccountDeepCloneUtil.cloneAccount([Select Id, Name .... from Account WHERE Id=<your record Id>]);

PS: The code written just using a notepad. Hence may not actually compile.Please feel free to make any changes.

share|improve this answer
add comment

Here is an app that does that. For now it clones Parent and Child, which is what most of the orgs need.

Clone any object with any related list in just 2 clicks! See this app for more info: https://appexchange.salesforce.com/listingDetail?listingId=a0N3000000B39tWEAR

share|improve this answer
    
Looks interesting but you should provide some kind of disclaimer (whether you're related to the company behind this paid app or not, looks quite expensive). Also this doesn't answer the question 100% as the OP needs to clone grandchildren records too... –  eyescream Jun 1 '13 at 23:45
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.