Clone Any SObject Record With Lightning Component

In this blog, I am going to show how to clone the record using lightning component quick action. The same functionality can be implemented by using even quick actions without having component also. But this component will be used to clone any object single record.

Step 1: Lightning Component 

Here is the complete code for the quick action.

Apex Class

public class CloneSingleRecord {
    @AuraEnabled
    public static String cloneAnySobjet(String recordId){
        Map<String, Schema.SObjectType> schemaMap = Schema.getGlobalDescribe();
        String objectAPIName = '';
        String keyPrefix = recordId.substring(0,3);
        for( Schema.SObjectType obj : schemaMap.Values() ){
            String prefix = obj.getDescribe().getKeyPrefix();
            if(prefix == keyPrefix){
                objectAPIName = obj.getDescribe().getName();
                break;
            }
        }
        Set <String> fieldMap = schemaMap.get(objectAPIName).getDescribe().fields.getMap().keySet();
        String soqlQuery = 'SELECT ' ; 
        for (String s :fieldMap ){
            if(schema.getGlobalDescribe().get(objectAPIName).getDescribe().fields.getMap().get(s).getDescribe().isAccessible()){
                soqlQuery +=  + s+',';
            }
        }
        soqlQuery =  soqlQuery.removeEnd(',');
        soqlQuery += ' FROM ' +objectAPIName +' WHERE ID = \'' + recordId +'\'' ;
        System.debug('soqlQuery'+soqlQuery);
        SObject record = Database.query(soqlQuery);
        SObject clondedParentRecordID= record.clone(false, false, false, false);
        try{
            insert clondedParentRecordID ;
            return clondedParentRecordID.id ;
        }catch(Exception e){
            return '' ;
        }
        
    }
}

Lightning Component

<aura:component implements="force:lightningQuickActionWithoutHeader,force:hasRecordId" controller="CloneSingleRecord">
    <aura:attribute name="errorMsg" type="String" />
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <ui:message aura:id="errorMsg" severity="error" class="slds-hide">
        {!v.errorMsg}
    </ui:message>
    
</aura:component>
({
    doInit : function(component, event, helper) {
        var action = component.get("c.cloneAnySobjet");
        action.setParams({"recordId": component.get("v.recordId")});
        action.setCallback(this, function(response) {
            var state = response.getState();
            if(state === "SUCCESS") {
                var sObjectEvent = $A.get("e.force:navigateToSObject");
                sObjectEvent.setParams({
                    "recordId": response.getReturnValue(),
                    "slideDevName": "detail"
                });
                sObjectEvent.fire();
            }else if (state === "ERROR"){
                var errors = response.getError();
                if(errors) {
                    cmp.set("v.errorMsg", errors[0].message);
                    var errorMsg = cmp.find('errorMsg');
                    $A.util.removeClass(errorMsg, 'slds-hide');
                    var field = cmp.find('field');
                    $A.util.addClass(field, 'slds-hide');
                }
            }
        });
        $A.enqueueAction(action);
    },
})

Step 2: Create a Quick Action

now create a new quick action as shown below

Step 3: Add the Action to the page layout

Now add the quick action to the page layout Salesforce Mobile and Lightning Experience Actions section. After adding the quick action, you can able to see them on the page layout.

Testing 

Now when you click on the Custom clone button it is going to clone the record.

 

Lightning Component Clone with Related Records

In this blog, I am going to explain a simple quick action lightning component that will clone the records and its related data also. In this example, I am controlling which all objects are allowed to as part of the cloning is through the custom metadata data types.

These are the records for metadata 

Apex Class

public class SuperClone {
    private static SuperCloneService service = new SuperCloneService();
    @AuraEnabled
    public static Id doClone(Id parentId) {
        Id clonedId = service.doClone(parentId);
        return clonedId;
    }
}
public class SuperCloneService {
    public Id doClone(String parentId) {
        Set<String> querySobject = new Set<String>();
        for(Super_Clone_Objects__mdt m : [select Id, DeveloperName, Label, API_Name__c 
                                          from Super_Clone_Objects__mdt  ]){ 
                                              querySobject.add(m.API_Name__c) ;  
                                          }
        Map <String, Schema.SObjectType> schemaMap = Schema.getGlobalDescribe();
        String objectAPIName = '';
        String keyPrefix = parentId.substring(0,3);
        for( Schema.SObjectType obj : schemaMap.Values() ){
            String prefix = obj.getDescribe().getKeyPrefix();
            if(prefix == keyPrefix){
                objectAPIName = obj.getDescribe().getName();
                break;
            }
        }
        Set <String> fieldMap = schemaMap.get(objectAPIName).getDescribe().fields.getMap().keySet();
        List<String> finalFields = new List<String>() ;
        finalFields.addAll(fieldMap);
        
        SObjectType objToken = Schema.getGlobalDescribe().get(objectAPIName); 
        DescribeSObjectResult objDef = objToken.getDescribe();
        Map<String,String> so = new Map<String,String>();
        Map<String,String> so1 = new Map<String,String>();
        
        for (Schema.ChildRelationship cr: objDef.getChildRelationships()) 
        {
            if(cr.getField().getDescribe().isAccessible()&& cr.getField().getDescribe().isCreateable()&&cr.getField().getDescribe().isAccessible() && cr.getRelationshipName()!=null){
                if(querySobject.contains(''+cr.getChildSObject())){
                    so.put(''+cr.getChildSObject()  , ''+cr.getRelationshipName());
                    so1.put(''+cr.getRelationshipName()  , ''+cr.getField());
                }
            }
        } 
        
        List<String> subqueries = prepareSubqueries(so, schemaMap);
        String query =
            'SELECT ' + String.join(finalFields, ',')+
            ','+String.join(subqueries, ',') +
            ' FROM ' +objectAPIName +
            ' WHERE Id = \''+parentId+'\'';
        
        List<Sobject> parentObj = Database.query(query);
        Sobject parentRecordId = parentObj[0];
        
        Sobject clonedRecord = parentRecordId.clone();
        insert clonedRecord;
        List<sObject> childObjects =cloneChildren(parentRecordId, clonedRecord, so  ,so1);
        insert childObjects;
        System.debug('clonedRecord'+clonedRecord.Id);
        return clonedRecord.Id ;
        
    }
    
    private List<sObject> cloneChildren(
        Sobject parent,
        Sobject child,
        Map<String , String> childRelatedListObjects,
        Map<String , String> childRelatedListObjects1
    ){
        
        List<sObject> childObjects = new List<SObject>();
        for (String childObjectDefinition : childRelatedListObjects.values()) {
            List<sObject> parentRecords = parent.getSObjects(childObjectDefinition);
            System.debug('parentRecords'+parentRecords);
            if (parentRecords != null) {
                List<sObject> records = parentRecords.deepClone();
                System.debug('records'+records);
                for (sObject record : records) {
                    record.put(childRelatedListObjects1.get(childObjectDefinition), child.Id);
                }
                childObjects.addAll(records);
            }
        }
        return childObjects;
    }
    
    private List<String> prepareSubqueries(
        Map<String , String> childrelatedListObjects,
        Map <String, Schema.SObjectType> schemaMap
    ){
        List<String> subqueries = new List<String>();
        for(String childObject : childrelatedListObjects.keySet()){
            List<String> childFields = new List<String>();
            Map <String, Schema.SObjectField> fieldMap = schemaMap.get(childObject).getDescribe().fields.getMap();
            System.debug('fieldMap'+fieldMap);
            for(Schema.SObjectField sof : fieldMap.values()){
                DescribeFieldResult dfr = sof.getDescribe();
                if(dfr.isCreateable()){
                    childFields.add(dfr.getName());
                }
            }
            if(!childFields.isEmpty()){
                String query = '(SELECT ' + String.join(childFields, ',') + ' FROM ' + childrelatedListObjects.get(childObject) + ')';
                subqueries.add(query);
            }
            
        }
        return subqueries;
    }
    
}

Lightning Component 

 

<aura:component implements="force:lightningQuickAction,force:hasRecordId"
                controller="SuperClone">
    <aura:attribute name="errorMsg" type="String" />
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <ui:message aura:id="errorMsg" severity="error" class="slds-hide">
        {!v.errorMsg}
    </ui:message>
</aura:component>

controller.js

({
    doInit: function(cmp, event, helper) {
        var action = cmp.get("c.doClone");
        action.setParams(
            {
                parentId : cmp.get("v.recordId")
            }
        );
        action.setCallback(this, function(response) {
            var state = response.getState();
            console.log('state sucess'+state);
            
            if (cmp.isValid()) {
                if (state === "SUCCESS") {
                    console.log('state sucess'+state);
                    
                    var clonedId = response.getReturnValue();
                    var navEvt = $A.get("e.force:navigateToSObject");
                    navEvt.setParams({
                        "recordId": clonedId
                    });
                    navEvt.fire();
                } else if(state == "ERROR"){
                    console.log('state'+state);
                    var errors = response.getError();
                    if(errors) {
                        console.log(errors[0].message);
                        var hideQuickAcion = $A.get("e.force:closeQuickAction");
                        hideQuickAcion.fire();
                    }
                }
            }
            helper.hideElement(cmp, 'modalSpinner');
        });
        
        $A.enqueueAction(action);
    },
    
})

helper.js

({
    hideElement: function (cmp, elementId) {
        var elm = cmp.find(elementId);
        $A.util.addClass(elm, 'slds-hide');
    },
    showElement: function (cmp, elementId) {
                component.set("v.Spinner", true); 

        var elm = cmp.find(elementId);
        $A.util.removeClass(elm, 'slds-hide');
    },
    
    showToast : function(type, title, message) {
        var toastEvent = $A.get("e.force:showToast");
        if(toastEvent) {
            toastEvent.setParams({
                "title": title,
                "message": message,
                "type": type
            });
            toastEvent.fire();
        }
    },
    
})

 

.THIS .spinnerWrapper{
    width: 24px;
    height: 24px;
}

.THIS .spinnerDiv{
    margin-right: 12px;
    margin-left: 12px;
}

.THIS .cardBody{
    padding-left: 1%;
    padding-right: 1%;
}

Creating Quick Action 

You will be able to create quick action with the above components shown below.Go to the object manager and create a quick action as shown below.

After saving the action, add this action to the page layout. this component we can reuse it on any object.

Once the user clicks on the clone button its will clone the parent and all child records data along with relationship as shown below.

 

 

Lightning App Background Utility Items

In this blog, I am going to show how to use the background utility items run without a visible entry in the utility bar. background utility items provide an unobtrusive way to add functionality to your Lightning apps without cluttering the user experience. In standard navigation and console navigation apps, you can create and add background utility items to invisibly execute code. Implement the lightning:backgroundUtilityItem interface to use a custom component as a background utility item. In this example, I am logging the details into the custom object whenever the user landing into the application for audit purpose. here is the simple custom object that will be used to store the audit history

Apex Class

public class AuditMonitoringService {
    @AuraEnabled 
    public static void insertAudit(String recordId){
        Audit_History__c aHistory = new Audit_History__c() ; 
        aHistory.Last_View_Time__c =System.now();
        aHistory.View_By__c = UserInfo.getUserName() ;
        aHistory.Application_Name__c='Lightning Service';
        insert aHistory ;
    }
}

Lightning Component 

<aura:component implements="lightning:backgroundUtilityItem,force:hasRecordId" controller="AuditMonitoringService">
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
</aura:component>
({
    doInit : function(component, event, helper) {
        var action = component.get("c.insertAudit");
        action.setParams({"recordId":component.get("v.recordId")});
        action.setCallback(this, function(response) {
            var state = response.getState();
            // Display toast message to indicate load status
            var toastEvent = $A.get("e.force:showToast");
            if (state === 'SUCCESS'){
                toastEvent.setParams({
                    "title": "Success!",
                    "message": " Audit Log is created ."
                });
            }
            else {
                toastEvent.setParams({
                    "title": "Error!",
                    "message": " Something has gone wrong."
                });
            }
            toastEvent.fire();
        });
        $A.enqueueAction(action);
    }
})

Add Component to  Utility Bar 

  1. Navigate to Setup > App Manager > New Lightning App
  2. Edit the Lightning  App > Under the App Settings >Utility Items > Add Utility item as shown below. Save it 

Testing 

After adding the background utility item to the app whenever the user landed on the application it will log into the custom object and shows the notification message as shown below.

 

 

Multiple Leads Conversion with Lightning Component

Simple code to convert multiple Leads at a time.

Apex Class 

public class LeadConversionAuraController {
    //1. The @AuraEnabled annotation enables client- and server-side access to an Apex controller method. Providing this annotation makes your methods available to your Lightning components.
    @AuraEnabled
    public static list<leadConversionWrapper> getLeadWithCheckbox(){
        //2. Performing query on lead.
        list<Lead> leadObj =[SELECT id,Name,Email,phone,company,status FROM Lead WHERE IsConverted=false];
        list<leadConversionWrapper> leadWrapper= new list<leadConversionWrapper>();
        for(Lead i:leadObj){
            //3. Adding lead in wrapper class.
            leadWrapper.add(new leadConversionWrapper(i)) ; 
        }
        //4. Returning wrapper class.
        return leadWrapper;
    }
    //5. The @AuraEnabled annotation enables client- and server-side access to an Apex controller method. Providing this annotation makes your methods available to your Lightning components.
    @AuraEnabled
    //6. This method used to convert lead.
    public static void convertSelectedLead(List<Lead> selectedLead){
        for(lead leadObj: selectedLead){
            Database.LeadConvert lc = new Database.LeadConvert();
            lc.setLeadId(leadObj.id);
            lc.setopportunityname(leadObj.Company);
            LeadStatus convertStatus = [SELECT Id, MasterLabel FROM LeadStatus WHERE IsConverted=true LIMIT 1];
            lc.setConvertedStatus(convertStatus.MasterLabel);
            //7. Lead conversion.
            Database.LeadConvertResult lcr = Database.convertLead(lc);
        }     
    }
    // 8. Wrapper class to hold lead with checkbox.
    Public class leadConversionWrapper{
        @AuraEnabled
        Public Lead leadObj;
        @AuraEnabled
        Public Boolean isBoolean;
        public leadConversionWrapper(Lead leadObj){
            this.leadObj = leadObj;
            this.isBoolean = isBoolean;
        }
    }
    
}

 

Lightning Component 

<aura:component controller="LeadConversionAuraController" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >
    <!-- 2.handler to call method "doInit" of jsCOntroller on loading of component. -->
    <aura:handler name="init" value="{!this}" action="{!c.leadwithcheckbox}"/>
    <!-- 1.attribute to store return value and show on component. -->
    <aura:attribute name="leadWrapper" type="LeadConversionAuraController.LeadConversionAuraController[]" />
    <br/><div class="slds-grid">
    <div class="slds-col slds-size--6-of-6">
        <center><ui:button class="detilbuton" label="Convert Lead" press="{!c.convertLead}"/></center>
    </div>
    </div><br/>
    <div class="slds-p-horizontal--small slds-size--1-of-1 slds-medium-size--1-of-1 slds-large-size--1-of-1">
        <div class="table-responsive">
            <table id="tableI" class="slds-table slds-table--bordered table" >
                <!-- Table header -->
                <thead>
                    <tr class="slds-text-title--caps header">
                        <td width="100"> Select </td>
                        <td> Name </td>
                        <td> Company </td>
                        <td> Email  </td>
                        <td> Phone </td>
                        <td> Status</td>
                    </tr>
                </thead>
                <!-- Table body -->
                <tbody>
                    <aura:iteration items="{!v.leadWrapper}" var="item">
                        <tr>
                            <td>
                                <div class="checkbox">
                                    <ui:inputCheckbox aura:id="checkbox" value="{!item.isBoolean}"/>
                                </div>
                            </td>
                            <td>{!item.leadObj.Name}</td>
                            <td>{!item.leadObj.Company}</td>
                            <td>{!item.leadObj.Email}</td>
                            <td>{!item.leadObj.Phone}</td>
                            <td>{!item.leadObj.Status}</td>
                        </tr>
                    </aura:iteration>
                </tbody>
            </table>
        </div>
    </div>
</aura:component>
({
    // 1. This is called on loding of component.
    leadwithcheckbox : function(component, event, helper) {
        //2. Calling method "getLeadWithCheckbox" of apex class "LeadConversionAuraController".
        var action =component.get("c.getLeadWithCheckbox");
        action.setCallback(this, function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                //3.Setting return value of method "getLeadWithCheckbox" of apex class "LeadConversionAuraController" to attribute "leadWrapper".
                component.set("v.leadWrapper", response.getReturnValue());
            }
        });
        //4. $A.enqueueAction adds the server-side action to the queue.
        $A.enqueueAction(action);
    },
    // 5. This is called by Conver Lead button.
    convertLead : function(component, event, helper){
        // 6. Getting wrapper list.
        var leadWrapper = component.get("v.leadWrapper");
        //7. Calling method "convertSelectedLead" of apex class "LeadConversionAuraController".
        var action = component.get("c.convertSelectedLead");
        // 8. Array decleration.
        var selectedLead = new Array();
        for(var i=0; i < leadWrapper.length; i++){
            if(leadWrapper[i].isBoolean){
                // 9. Hold selected lead to convert.
                selectedLead.push(leadWrapper[i].leadObj);
            }
        }
        action.setParams({
            "selectedLead":selectedLead
        })
        if(selectedLead.length > 0){
            action.setCallback(this, function(response){
                // 10. Checking status of response.
                var state = response.getState();
                if (state === "SUCCESS") {
                    alert('Lead isconverted sucessfully.');
                    location.reload();
                }
                else{
                    alert('Error occurred while converting lead.');
                }
            });
        }else{
            alert('Please select any Lead');
        }
        $A.enqueueAction(action);
    }
    
})

 

MultipleLeadConversionApp

<aura:application extends="force:slds">
    <!-- Calling component "MultipleLeadConversion" -->
    <c:MultipleLeadConversion/>
</aura:application>