Thursday, April 20, 2017

Using Database.upsert with external ID field

External Id plays very important role if you want to update records without knowing the record Ids or want to relate the child record with parent record without knowing the parent record Id.

As a best practice, you should always make External Id unique. If you are performing upsert with External Id, then following situations will occur:

  1. If no record is found in table with provided External Id, then it will create record in table.
  2. If 1 record is found in table with provided External Id, then it will update record in table.
  3. If more than 1 records is found in table with provided External Id, then system will throw an error.

I am going to cover 2 different aspect of using external Id in apex.

  • Updating a record with External Id

Create a External Id field on Account as Account_Unique_Number__c and mark it as External Id and unique while creating it.Now we will create a new record using upsert.

Execute below command in developer console

List<Account> acclist=new list<Account>();
acc.name='Demo test1';
acc.Account_Unique_Number__c='00001';
acclist.add(acc);
Schema.SObjectField ftoken = Account.Fields.Account_Unique_Number__c;
Database.UpsertResult[] srList = Database.upsert(acclist,ftoken,false);
for (Database.UpsertResult sr : srList) {
    if (sr.isSuccess()) {
        // Operation was successful
    }
    else {
        // Operation failed, so get all errors                
        for(Database.Error err : sr.getErrors()) {
            System.debug('error has occurred.' + err.getStatusCode() + ': ' + err.getMessage());                    
            System.debug('fields that affected this error: ' + err.getFields());
            
        }
    }
}

As there is no record in Account with Account_Unique_Number__c as 00001, system will create a new record.

Now again we will run same script in developer console and will specify some more field values:

List<Account> acclist=new list<Account>();
Account acc=new Account();
acc.name='Demo test1';
acc.Account_Unique_Number__c='00001';
acc.type='Other';
acc.Industry='Banking';
acclist.add(acc);
Schema.SObjectField ftoken = Account.Fields.Account_Unique_Number__c;
Database.UpsertResult[] srList = Database.upsert(acclist,ftoken,false);
for (Database.UpsertResult sr : srList) {
    if (sr.isSuccess()) {
        // Operation was successful
    }
    else {
        // Operation failed, so get all errors                
        for(Database.Error err : sr.getErrors()) {
            System.debug('error has occurred.' + err.getStatusCode() + ': ' + err.getMessage());                    
            System.debug('fields that affected this error: ' + err.getFields());
            
        }
    }

Now you will see that system will update the record as it was able to find a Account record with Account_Unique_Number__c as 00001

  • Relating a child record with parent record by using parent record Id

In order to understand this, we will create contact record and will relate to account using Account_Unique_Number__c. Execute below code in developer console:

List<Contact> conlist=new list<Contact>();
Contact con=new Contact();
con.lastname='Kumar';
con.Firstname='Kumar';
con.email='sunil02kumar@gmail.com';
Account acc=new Account(Account_Unique_Number__c='00001');
con.Account=acc;
conlist.add(con);
Database.UpsertResult[] srList = Database.upsert(conlist,false);
for (Database.UpsertResult sr : srList) {
    if (sr.isSuccess()) {
        // Operation was successful
    }
    else {
        // Operation failed, so get all errors                
        for(Database.Error err : sr.getErrors()) {
            System.debug('error has occurred.' + err.getStatusCode() + ': ' + err.getMessage());                    
            System.debug('fields that affected this error: ' + err.getFields());
        }
    }
}

This will create a new contact for Account which have Account_Unique_Number__c as 00001.

In above code snippet, you can see that in order to relate contact with account, we are not specifying the account 15 or 18 digit record id. We are just specifying the external Id of account and system will maintain the relationship.

If you refer custom object as parent object then refer it with __r. For example in above scenario, if i have to relate contact with custom object say Parent_Obj__c, then I will use below code:

Parent_Obj__c  obj = new Parent_Obj__c(Unique_Number__c='00001');
con.Parent_Obj__r = obj;

Why it is recommended to mark External Id as unique?

Imagine you are creating a contact and specified External Id of parent. Suppose there are 2 records in account table with same value, then system will not able to identify with whom it needs to relate the contact and will throw error saying more than 1 match found.

Same is applicable when you update the record with External Id.



More Blogs>>: 
DYNAMIC APEX IN SALESFORCE
SOQL INJECTION IN SOQL
CUSTOM METADATA AND CUSTOM SETTINGS IMPLEMENTATION TRICKS
SMART TABLE USING ANGULARJS IN VISUALFORCE PAGE
REST API TUTORIAL FOR SALESFORCE
VISUALFORCE COMPONENT FOR RECORD STATUS BAR
FETCHING FILE FROM EXTERNAL/PUBLIC URL AND STORING IT IN SALESFORCE

Wednesday, April 12, 2017

Lightning Data Services : Way to perform operation on records without using server-side Apex class

Today I am going to walk through a new feature introduced by Salesforce - Lightning Data Services.
This is not yet generally available. It is available as developer preview.

What is lightning data services?


Lightning data services allows you to view, create, update and delete a record without using server-side Apex controllers. You can compare this with Standard Controller on VF page which allows you to read, create, update or delete records by providing in built functions like Save, Edit,Delete etc.

Other benefit of using Lightning data services is that all records are cached and shared across all components. This improves the performance because record is loaded only once.
If any component modifies the record, then other component using this records get notified and refresh automatically.

In order to use Lightning data services, you need to use force:recordPreview tag and need to specify recordId while performing any operation.

Now we will cover how to use Lightning data services for different operations.

  • Load Record (View Record)
In order to Load record information on lightning component, you need to pass recordId. It is similar to pass id parameter in URL in VF page while using standard controller.

<force:recordData aura:id="recordLoader"
  recordId="xxxxxxxxxxxxx"
  layoutType="FULL"
  targetRecord="{!v.record}"
    targetFields="{!v.recordInfo}"
  targetError="{!v.recordError}"
  />

Before Summer'17 below was syntax which is now depricated.

<force:recordPreview aura:id="recordLoader"
 recordId="xxxxxxxxxxxxx"
 layoutType="FULL"
 targetRecord="{!v.record}"
 targetError="{!v.recordError}"
 />



recordId: It is 15 or 18 digit recordId.
targetRecord:  This contains complete info about record.
targetFields : A simplified view of the fields in targetRecord, to reference record fields in component markup.
targetError : This specify any error if lightning data services is not successfull in geting record details based on recordId provided.


  • Edit Record
For editing records, you need use same  force:recordPreview tag and you can use additional attribute mode to specify you are editing record.

<force:recordData aura:id="recordHandler"
 recordId="xxxxxxxxxxxx"
 layoutType="FULL"
 targetRecord="{!v.record}"
     targetFields="{!v.recordInfo}"
 targetError="{!v.recordError}"
 mode="EDIT"
 />

Once you get access to record returned by Lightning Data Services in target record, then you can refer it in component to edit its field values. In order to Save the changes performed on record returned by Lightning data services, you can call javascript function to update records in database. In javascript function, you do not need to call apex class methods to update record but use functions provided by Lightning data services framework to perform this operation.

Below is javascript function code :

({
    SaveRecord: function(component, event, helper) {
        component.find("recordHandler").saveRecord($A.getCallback(function(saveResult) {
            if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
//record got updated in salesforce
                // Reload the view so that all components are refreshed after update
                $A.get("e.force:refreshView").fire();
            }
            else {
var errMsg = 'Unknown problem, state: ' + saveResult.state + ', error: ' +         JSON.stringify(saveResult.error);
                console.log(errMsg);
alert(errMsg);
            }
        }));
    },
})

Remember recordHandler is aura:id of  force:recordData tag


  • Create Record
For creating a new record, you need to use same force:recordPreview tag but no need to specify the recordId attribute.

<force:recordData aura:id="accountRecordCreator"
        layoutType="FULL"
        targetRecord="{!v.newAccount}"
       targetFields="{!v.newAccountInfo}"
        targetError="{!v.newAccountError}"
        />

In VF page, if you do not pass id parameter in URL and using standard controller, Save action creates new record in salesforce. Same way in Lightning data services, you do not need recordId for creating record. 
You need to create a template of record so that it can be used to create new record. It is similar to initializing the sobject to avoid null pointer exception in apex controllers. 

So in doInit function, first create template. Below is an example to create template for account:

doInit: function(component, event, helper) {
// Prepare a new record from template
component.find("accountRecordCreator").getNewRecord(
"Account", // sObject type (entity API name)
null,           // record type (null if no recordtype exist
false,         // skip cache?
$A.getCallback(function() {
var rec = component.get("v.newAccount");  //targetRecord attribute
var error = component.get("v.newAccountError");
if(error || (rec === null)) {
console.log("Error initializing record template: " + error);
}
else {
console.log("Record template initialized: " + rec.sobjectType);
}
})
);
},

In above code "newAccount" and "newAccountError" are component attribute which store information related to Lightning data services.

Now you have created a template and can use newAccount component attribute to specify field values on component. Once user specifies the field values, you can call javascript function on button click to create record. Below is sample code:

createContact: function(component, event, helper) {
component.find("accountRecordCreator").saveRecord(function(saveResult) {
if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
//record got created in salesforce
//show toast on UI with message
// Reload the view so that all components are refreshed after update
$A.get("e.force:refreshView").fire();
}
else {
var errMsg = 'Unknown problem, state: ' + saveResult.state + ', error: ' + JSON.stringify(saveResult.error);
console.log(errMsg);
alert(errMsg);
}
});
},

If you want to modify any field value in javascript, then set the value in component attribute which hold object information. In our case attribute is "newAccount". So you can set value for "Type" field in Account (as shown below )then call saveRecord method.

component.set("v.newAccount.Type", 'Direct');


  • Delete Record 
For delete also, you need to use force:recordPreview tag and need to specify the recordId and in fields specify Id.

<force:recordData aura:id="recordDeleteHandler"
      recordId="{!v.recordId}"
      fields="Id"
      targetError="{!v.recordError}"
      />

In order to delete record on button click, call below mentioned controller function:

DeleteRecord: function(component, event, helper) {
        component.find("recordDeleteHandler").deleteRecord($A.getCallback(function(saveResult) {
            if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
//record got deleted from salesforce
                // Reload the view so that all components are refreshed after update
                $A.get("e.force:refreshView").fire();
            }
            else {
 var errMsg = 'Unknown problem, state: ' + saveResult.state + ', error: ' + JSON.stringify(saveResult.error);
                console.log(errMsg);
alert(errMsg);
            }
        }));
    }



I have created a component which will will display list of Account by using Apex controller and then I am creating, editing, viewing and deleting records using Lightning Data Services. For all DMl operation on this component, I am not using server-side apex controller and performing these operations using code snippet shown above.

I have created different components as mentioned below:
  • AccountListView : This will display 10 Accounts and will use controller method to get list of accounts.
  • AccountView : To view account details. We will just pass account recordId to this component.
  • AccountEdit  : This will be used to edit or create new record. If we pass recordId to component, then it will update record. If we pass null in recordId then it will create new record.
After saving below code in your org, create App Page from Lightning App Builder section and add "AccountListView" component to it and activate it. After this open this app page and test the functionality.

If your org have recordtypes defined for Account object, then while creating new Account, you will get recordType selection page. No validation have been applied on fields as this is created for demo purpose only.

You can download complete code from below link:
Sample Code utilizing Lightning Data Services

Below is complete Code:


Looking forward for everyone's comments and feedback!!!!!

Changes to Lightning Data Services (LDS) in Summer'17 Release


More Blogs>>: 
DYNAMICALLY CREATING AND DESTROYING LIGHTNING COMPONENTS    
RAISING AND HANDLING CUSTOM EVENTS IN sALESFORCE lIGHTNING    
WHY TO USE DESIGN RESOURCE AND HOW TO ADD DYNAMIC OPTION TO DATASOURCE    
PASSING INNER WRAPPER CLASS TO LIGHTNING COMPONENT    
LIGHTNING COMPONENT FOR RECORDTYPE SELECTION FOR ANY SOBJECT    

Thursday, April 6, 2017

Login Flows to Display Important Messages to User After Login

Sometimes it is required to display some message to SFDC users when some maintenance or migration activity is getting performed in production. For example, during some major project release which may take many hours or many days for metadata and data migration, you want to display message to SFDC user saying "Maintenance is in progress and you may experience intermittent issues".

You might have seen these kind of intermediate screen informing about Salesforce maintenance window after logging into salesforce.

Login Flows allow administrators to display intermediate screen (Flow) once user is authenticated and after that user will be redirected to home page of salesforce.

You can create a flow using flow designer and associate it with Login flow. As you already know that flow can be used to collect some information from users or to display some message to end users. Using flow you can specify set of screen to capture data from user or navigate the user's to set of instructions.

Important points regarding Login Flows:
  • The login flow screens are embedded within the standard Salesforce login page for uniform user experience.
  • You need to associate Login flows to different profiles. So all user related to that profile will see flow screen after authentication.
  • Login Flows can be applied to SFDC Orgs, communities and portals. 
  • Login flow comes into picture whenever user login through UI either using username and password, delegated authentication, SAML single sign-on, and social sign-on through a third-party authentication provider

Now we will create a sample flow to display maintenance message to end users during some major project release. Below are steps which we need to follow:


Create a flow

  • Navigate to Set Up--> Create-->Flow. Click on New Flow button. This will open Flow designer screen. Drag Screen Palette from left side and name it as "Maintenance Window Message".

  • Now Click on "Add a Field" tab and double click on display text.

  • Now click on "Field Settings" tab and click on display text. Give unique name as "MessageToDisplay". Specify the message which you want to display to end User and then click Ok.

  • Mark screen as start element for you flow by clicking on green down arrow as shown below.

  • Now Save flow by clicking on Save button and name flow as "Maintenance Window Flow".

  • Now Close the Flow and Activate it. Click on Activate link. Remember only active flow will be available for login Flows.

Assign Login Flow to Profile

  • Navigate to Set Up-->Security Controls-->Login Flows. Click on New Button. Specify name as "Maintenance Window Message" and select license and profile. Profile list depends on license selected by user.

  • In this way you can create multiple entries for different profiles.

Now you login into Salesforce to test this. Once you specify your username and password, you will see below message screen.



Points to remember:
  • You can just delete the login Flows records after maintenance window so that users will not see this message after login.
  • You can not create more than 1 login flow record for a given profile. Suppose you want to assign different flow to same profile in login flow then you will get below error:



Hope this will help!!!

For more details on Login flow, please refer below URL:
Login Flows


Other Blogs>>: 
DYNAMIC APEX IN SALESFORCE
SOQL INJECTION IN SOQL
CUSTOM METADATA AND CUSTOM SETTINGS IMPLEMENTATION TRICKS
REST API TUTORIAL FOR SALESFORCE
VISUALFORCE COMPONENT FOR RECORD STATUS BAR
FETCHING FILE FROM EXTERNAL/PUBLIC URL AND STORING IT IN SALESFORCE

Monday, April 3, 2017

Lightning Component for RecordType Selection Screen for any Sobject

I have created a lightning component which can be used to display recordtype selection screen to user. You can just specify the object API name as an attribute and it will display the active recordType for that object.

Below mention code implement the concept of passing inner wrapper class to lightning which was mentioned in my previous blog  Passing Inner Wrapper Class to Lighntning Component.



Below is sample code for this.

Click to Download Complete Code


More Blogs>>: 
DYNAMICALLY CREATING AND DESTROYING LIGHTNING COMPONENTS    
RAISING AND HANDLING CUSTOM EVENTS IN sALESFORCE lIGHTNING    
WHY TO USE DESIGN RESOURCE AND HOW TO ADD DYNAMIC OPTION TO DATASOURCE    
PASSING INNER WRAPPER CLASS TO LIGHTNING COMPONENT
Hope this will help !!!!