How To: Filter Records in Related Lists With Super Clone Pro
December 16, 2017How To: Clone a record and delete the original record in Salesforce
April 25, 2018How To: Clone a Hierarchy of Records Asynchronously with Super Clone Pro
Cloning large and complex hierarchies of records can run into platform limits. Salesforce’s governor limits for CPU time limit, heap size, and view state can prevent successfully cloning a hierarchy. Factors that impact this include a large numbers of records, lots of data in the records, or a significant amount of custom Apex/Workflow/Formula logic. One solution is to run the Super Clone Pro process asynchronously. This provides higher limits to process the cloning logic successfully.
Submitting the cloning logic asynchronously requires additional custom coding. There is no warranty on the code below because each Salesforce environment and object being cloned is different, but this will hopefully provide a starting point that will help with your cloning.
- This example uses custom objects called Solution and Solution Items. A custom Visualforce page will prompt the user for a new name, start date, and end date. This information will be passed to a queueable Apex job that runs the Super Clone Pro api.
- The Visualforce page uses an actionpoller to check the job status. The Status will updates on the page job is processing and complete.
- After completion, the records have been inserted, and will be available for the user to navigate to.
Super Clone Pro Configuration: solutionClone
* Notice the field actions set to “Set by URL Paramter”. The text in the Value column corresponds to the keys of the parameter map that is passed into the Super Clone Pro API. The API will apply the values from the parameter map to the fields in the Solution object.
Apex Class Queueable Job: SolutionCloneQueueable
public class SolutionCloneQueueable implements Queueable{ // instance variable for parameters public final map<String, String> paramMap; // initialize public SolutionCloneQueueable(map<String, String> input) { paramMap = input; } // run clone logic public void execute(QueueableContext context) { set<Id> ObjIdSet = new set<Id>(); map<Id, list<sObject>> ParentObjById; String recId = paramMap.get('recid'); ObjIdSet.add(recId); Savepoint sp = Database.setSavepoint(); try { system.debug('paramMap: ' + paramMap); // clone the record hierarchy ParentObjById = lcrm_scp.ScpApi.clone('solutionConfig', ObjIdSet, 1, paramMap); // retrieve new parent record sObject newSObj = ParentObjById.get(recId)[0]; list<Solution_Item__c> siList = new list<Solution_Item__c>(); // perform post clone logic on Solution Items for(Solution_Item__c si : [SELECT Id, Name, Solution__c FROM Solution_Item__c WHERE Solution__c = :newSObj.Id]) { si.Name = 'Add Logic for new name'; siList.add(si); } if (!siList.isEmpty()) { update siList; } } catch (exception e) { Database.rollback(sp); system.debug('error: ' + e.getMessage()); // do other error handling logic } } }
Apex Visualforce Controller: SolutionCloneController
public class SolutionCloneController { public Boolean isSubmitted {get; set;} public Boolean isCompleted {get; set;} public Boolean isPolling {get; set;} public Boolean isRecIdFound {get; set;} public String jobId {get; set;} public Solution__c sol {get; set;} public String recId; // initialize and retrieve the parent solution record public SolutionCloneController() { isSubmitted = false; isCompleted = false; isRecIdFound = false; recId = ApexPages.currentPage().getParameters().get('Id'); try { sol = [SELECT Id, Name, Start_Date__c, End_Date__c FROM Solution__c WHERE Id = :recId LIMIT 1]; isRecIdFound = true; } catch (exception e) { ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'Solution record was not found. (' + recId + ')')); } } // submit the queueable apex job public void doSubmit() { // do custom parameter validation before submitting // setup parameter map map<String, String> paramMap = new map<String, String>(); paramMap.put('recid', sol.Id); paramMap.put('name', sol.Name); paramMap.put('start', sol.Start_Date__c.format()); paramMap.put('end', sol.End_Date__c.format()); // submit the async job jobID = System.enqueueJob(new SolutionCloneQueueable(paramMap)); isSubmitted = true; } // return to the origional record public PageReference doCancel() { return new PageReference('/'+recId); } // return the status of the queueable job public String getJobStatus() { AsyncApexJob jobInfo = [SELECT Status,NumberOfErrors FROM AsyncApexJob WHERE Id=:jobID]; if(!'Queued'.equalsIgnoreCase(jobInfo.Status) && !'Processing'.equalsIgnoreCase(jobInfo.Status)) { isCompleted = true; } return jobInfo.Status; } }
Visualforce Page: SolutionClone
<apex:page controller="SolutionCloneController"> <apex:form id="theForm"> <apex:pageMessages id="errormsg" /> <apex:pageBlock mode="maindetail"> <apex:pageBlockButtons id="buttons" location="top"> <apex:outputPanel rendered="{!!isSubmitted}" > <apex:commandButton action="{!doSubmit}" value="Submit" status="submitButtonStatus" rerender="theForm" rendered="{!isRecIdFound}" /> <apex:commandButton action="{!DoCancel}" value="Cancel" immediate="true" /> </apex:outputPanel> </apex:pageBlockButtons> <apex:pageBlockSection columns="1" rendered="{!isRecIdFound && !isSubmitted}"> <apex:inputField value="{!sol.Name}" /> <apex:inputField value="{!sol.Start_Date__c}" /> <apex:inputField value="{!sol.End_Date__c}" /> </apex:pageBlockSection> </apex:pageBlock> <apex:outputPanel id="poll" rendered="{!isRecIdFound}"> <apex:actionPoller rendered="{!!isCompleted}" reRender="completedjobs" interval="5"/> <apex:outputPanel rendered="{!isSubmitted}" id="completedjobs"> <b>Status</b>: {!JobStatus} <br /> <b>Job Id</b>: {!jobId} <apex:outputPanel rendered="{!isCompleted}"> <apex:actionFunction name="turnOffPoll" reRender="poll" /> </apex:outputPanel> </apex:outputPanel> </apex:outputPanel> </apex:form> </apex:page>
Custom Button on Solution page layout
/apex/SolutionClone?id={!Solution__c.Id}