September 30, 2020

Articles not showing under Topics in Salesforce Community

Go to Article Management and make sure the Article is assigned to the Topics.



September 29, 2020

How to find the number of months between two Dates using formula in Salesforce?

Sample Formula:
 
IF(
YEAR( End_Date__c ) = YEAR( Start_Date__c ) && MONTH( End_Date__c ) = MONTH( Start_Date__c ),
0,
IF ( YEAR( End_Date__c ) = YEAR( Start_Date__c ),
MONTH( End_Date__c ) - MONTH( Start_Date__c ) - 1,
( ( YEAR( End_Date__c ) - YEAR( TODAY() ) ) * 12 ) - ( 13 - MONTH( End_Date__c ) ) + ( 12 - MONTH( Start_Date__c ) )
)
)
 
Output:
 


September 28, 2020

FIELD_INTEGRITY_EXCEPTION, You can’t change the completion date on a case milestone that’s already exited an entitlement process.: Completion Date: [CompletionDate]

Make sure the CaseMilestone is updated before the case is marked as closed.

If the Case is already in Closed status and if you have any processes to update the Milestone Completion Date, you will run into this error.

You can’t change the Completion Date on a Milestone that’s already exited an Entitlement Process.

For example, lets say I have an Entitlement Process built so that Cases exit that Entitlement Process when the Case Escalated check box is set to false and a Milestone is based on Escalated check box equals to true. If I have a Closed Case related to that Entitlement Process and that Case has exited the Entitlement Process(Escalated check box is set to false) and if I want to change the Completed Date on a Milestone on that Case, I can't because the Case is not in the Entitlement Process. If I set the Escalated check box to true on the Case, then it would no longer meet the exit condition for the Entitlement Process. The Case goes back into that Entitlement Process, and it's Milestones can be updated.

If the Exit Criteria is based on Status equal to Closed. Then, the Case Date/Time Closed and Entitlement Process End Time would be updated to the new Close Date. That in turn would affect calculations of the age of that case. (There may be additional considerations too).

SOQL to update CompletionDate:
SELECT Id, CompletionDate FROM CaseMilestone WHERE CaseId = '<Case Id>'

How to use CSS in Static Resource with MIME Type application/zip in LWC in Salesforce?

sample.css:

.textCSS {
    color: red;
}

.slds-card__header {
    color: green;
    font-weight: bold;
    text-align: center;
}

.slds-th__action {
    color: blue;
    justify-content: center !important;
}

.buttonCSS {
    background: black;
    color: white;
    font-weight: bold;
    font-size: 14px;
}

Static Resource:


Apex Class:

public with sharing class LightningCardController {
    
    @AuraEnabled( cacheable = true )  
    public static List < Case > fetchRecs( String strAccId ) {  
        
        List < Case > listRecs = new List < Case >();
        String strSOQL = 'SELECT Id, Subject, Status, Origin FROM Case';

        if ( String.isNotBlank( strAccId ) )
            strSOQL  += ' WHERE AccountId = \'' + strAccId + '\'';
        
        strSOQL += ' LIMIT 3';
        system.debug( 'SOQL is ' + strSOQL );
        listRecs = Database.query( strSOQL );
        return listRecs;
          
    }

}


HTML:

<template>
    <lightning-card  title="My Cases">
        <p class="slds-p-horizontal_small">
            <lightning-datatable key-field="Id"  
                                 data={listRecs}  
                                 columns={columns}  
                                 hide-checkbox-column="true"
                                 class="textCSS">  
            </lightning-datatable>  
        </p>
        <p slot="footer" class="slds-align_absolute-center">            
            <button onclick={navigateToCaseHome} class="buttonCSS">View All Cases</button>          
        </p>
    </lightning-card>
    <template if:true = {error}>  
        {error}>              
    </template>
</template>


JavaScript:

import { api, LightningElement, wire } from 'lwc';
import fetchRecs from '@salesforce/apex/LightningCardController.fetchRecs';
import { NavigationMixin } from 'lightning/navigation';
import { loadStyle } from 'lightning/platformResourceLoader';
import LightningCardCSS from '@salesforce/resourceUrl/LightningCard';

const columns = [   
    { label: 'Subject', fieldName: 'Subject' },
    { label: 'Status', fieldName: 'Status' },
    { label: 'Case Origin', fieldName: 'Origin' }
];

export default class LightningCard extends NavigationMixin( LightningElement ) {

    @api AccountId;
    @api staticResName;
    columns = columns;
    listRecs;
    error;

    renderedCallback() {
        
        Promise.all([
            loadStyle( this, LightningCardCSS + '/' + this.staticResName )
            ]).then(() => {
                console.log( 'Files loaded' );
            })
            .catch(error => {
                console.log( error.body.message );
        });

    }

    @wire(fetchRecs, { strAccId: '$AccountId' })  
    wiredRecs( { error, data } ) {

        if ( data ) {

            console.log( 'Records are ' + JSON.stringify( data ) );
            this.listRecs = data;

        } else if ( error ) {

            this.listRecs = null;
            this.error = error;

        }
        
    }

    createNew() {

        this[NavigationMixin.Navigate]({            
            type: 'standard__objectPage',
            attributes: {
                objectApiName: 'Case',
                actionName: 'new'                
            }
        });

    }

    navigateToCaseHome() {
        
        this[NavigationMixin.Navigate]({
            type: 'standard__objectPage',
            attributes: {
                objectApiName: 'Case',
                actionName: 'home'
            }
        });

    }

}


meta.xml:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>49.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightningCommunity__Page</target>
        <target>lightningCommunity__Default</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightningCommunity__Default">
            <property name="AccountId" type="String"/>
            <property name="staticResName" type="String"/>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>


Community Configuration:


Output:


September 27, 2020

How to use CSS in Static Resource in LWC in Salesforce?

LightningCard.css:
.textCSS {
    color: red;
}

.slds-card__header {
    color: green;
    font-weight: bold;
}

.slds-th__action {
    color: blue;
    justify-content: center !important;
}

.buttonCSS {
    background: black;
    color: white;
    font-weight: bold;
    font-size: 14px;
}
 
Static Resource:
 

 HTML:

<template>
    <lightning-card  title="Hello">
        <button slot="actions" onclick={createNew} class="buttonCSS">New</button>
        <p class="slds-p-horizontal_small">
            <lightning-datatable key-field="Id"  
                                 data={listRecs}  
                                 columns={columns}  
                                 hide-checkbox-column="true"
                                 class="textCSS">  
            </lightning-datatable>  
        </p>
        <p slot="footer" class="slds-align_absolute-center">            
            <button onclick={navigateToCaseHome} class="buttonCSS">View All Cases</button>          
        </p>
    </lightning-card>
    <template if:true = {error}>  
        {error}>              
    </template>
</template>

JavaScript:

import { api, LightningElement, wire } from 'lwc';
import fetchRecs from '@salesforce/apex/LightningCardController.fetchRecs';
import { NavigationMixin } from 'lightning/navigation';
import { loadStyle } from 'lightning/platformResourceLoader';
import LightningCardCSS from '@salesforce/resourceUrl/LightningCard';

const columns = [   
    { label: 'Subject', fieldName: 'Subject' },
    { label: 'Status', fieldName: 'Status' },
    { label: 'Case Origin', fieldName: 'Origin' }
];

export default class LightningCard extends NavigationMixin( LightningElement ) {

    @api AccountId;
    columns = columns;
    listRecs;
    error;

    renderedCallback() {
        
        Promise.all([
            loadStyle( this, LightningCardCSS )
            ]).then(() => {
                console.log( 'Files loaded' );
            })
            .catch(error => {
                console.log( error.body.message );
        });

    }

    @wire(fetchRecs, { strAccId: '$AccountId' })  
    wiredRecs( { error, data } ) {

        if ( data ) {

            console.log( 'Records are ' + JSON.stringify( data ) );
            this.listRecs = data;

        } else if ( error ) {

            this.listRecs = null;
            this.error = error;

        }
        
    }

    createNew() {

        this[NavigationMixin.Navigate]({            
            type: 'standard__objectPage',
            attributes: {
                objectApiName: 'Case',
                actionName: 'new'                
            }
        });

    }

    navigateToCaseHome() {
        
        this[NavigationMixin.Navigate]({
            type: 'standard__objectPage',
            attributes: {
                objectApiName: 'Case',
                actionName: 'home'
            }
        });

    }

}

meta.xml:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>49.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightningCommunity__Page</target>
        <target>lightningCommunity__Default</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightningCommunity__Default">
            <property name="AccountId" type="String"/>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

Apex Class:

public with sharing class LightningCardController {
    
    @AuraEnabled( cacheable = true )  
    public static List < Case > fetchRecs( String strAccId ) {  
        
        List < Case > listRecs = new List < Case >();
        String strSOQL = 'SELECT Id, Subject, Status, Origin FROM Case';

        if ( String.isNotBlank( strAccId ) )
            strSOQL  += ' WHERE AccountId = \'' + strAccId + '\'';
        
        strSOQL += ' LIMIT 3';
        system.debug( 'SOQL is ' + strSOQL );
        listRecs = Database.query( strSOQL );
        return listRecs;
          
    }

}

Output:

 

September 26, 2020

Sorting in lightning-datatable in LWC Salesforce

Sample Code:

LWC HTML:


<template>
     
    <lightning-card title = "Search Accounts" icon-name = "custom:custom63">
 
        <div class = "slds-m-around_medium">
 
            <lightning-input type = "search" onchange = {handleKeyChange} class = "slds-m-bottom_small" label = "Search" >
            </lightning-input>
 
            <template if:true = {accounts}>
                 
                <div style="height: 300px;">
 
                    <lightning-datatable key-field="Id"
                                         data={accounts}
                                         columns={columns}
                                         hide-checkbox-column="true"
                                         show-row-number-column="true"
                                         onrowaction={handleRowAction}
                                         default-sort-direction={defaultSortDirection}
                                         sorted-direction={sortDirection}
                                         sorted-by={sortedBy}
                                         onsort={onHandleSort}>
                    </lightning-datatable>
 
                </div>                  
     
            </template>
     
            <template if:true = {error}>
 
                {error}>
                 
            </template>
 
        </div>
 
    </lightning-card>
 
</template>

LWC JavaScript:

import { LightningElement, track } from 'lwc';
import fetchAccounts from '@salesforce/apex/AccountController.fetchAccounts';
import { NavigationMixin } from 'lightning/navigation';

const actions = [
    { label: 'View', name: 'view' },
    { label: 'Edit', name: 'edit' },
];
 
const columns = [   
    { label: 'Name', fieldName: 'Name' },
    { label: 'Industry', fieldName: 'Industry', sortable: true },
    {
        type: 'action',
        typeAttributes: { rowActions: actions },
    },
];

export default class Sample extends NavigationMixin( LightningElement ) {
     
    @track accounts;
    @track error;
    @track columns = columns;
    sortedBy;
    defaultSortDirection = 'asc';
    sortDirection = 'asc';
 
    handleKeyChange( event ) {
         
        const searchKey = event.target.value;
 
        if ( searchKey ) {
 
            fetchAccounts( { searchKey } )   
            .then(result => {
 
                this.accounts = result;
 
            })
            .catch(error => {
 
                this.error = error;
 
            });
 
        } else
        this.accounts = undefined;
 
    }

    handleRowAction( event ) {

        const actionName = event.detail.action.name;
        const row = event.detail.row;
        switch ( actionName ) {
            case 'view':
                this[NavigationMixin.Navigate]({
                    type: 'standard__recordPage',
                    attributes: {
                        recordId: row.Id,
                        actionName: 'view'
                    }
                });
                break;
            case 'edit':
                this[NavigationMixin.Navigate]({
                    type: 'standard__recordPage',
                    attributes: {
                        recordId: row.Id,
                        objectApiName: 'Account',
                        actionName: 'edit'
                    }
                });
                break;
            default:
        }

    }
    
    onHandleSort( event ) {

        const { fieldName: sortedBy, sortDirection } = event.detail;
        const cloneData = [...this.accounts];

        cloneData.sort( this.sortBy( sortedBy, sortDirection === 'asc' ? 1 : -1 ) );
        this.accounts = cloneData;
        this.sortDirection = sortDirection;
        this.sortedBy = sortedBy;

    }

    sortBy( field, reverse, primer ) {

        const key = primer
            ? function( x ) {
                  return primer(x[field]);
              }
            : function( x ) {
                  return x[field];
              };

        return function( a, b ) {
            a = key(a);
            b = key(b);
            return reverse * ( ( a > b ) - ( b > a ) );
        };

    }

}

LWC meta.xml:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__Tab</target>
    </targets>
</LightningComponentBundle>

Apex Class:

public with sharing class AccountController {
 
    @AuraEnabled( cacheable = true )
    public static List< Account > fetchAccounts( String searchKey ) {
     
        String strKey = '%' + searchKey + '%';
        return [ SELECT Id, Name, Industry FROM Account WHERE Name LIKE: strKey LIMIT 10 ];
         
    }
     
}

Output:



September 24, 2020

Reusable Custom List View with dynamic datatable for Salesforce Community using LWC

Sample Code:

HTML:


<template>  
      
    <lightning-card title = "{Title}">  

        <lightning-button label="New" slot="actions" onclick = {createNew}></lightning-button>

        <lightning-input type = "search" onblur = {handleKeyChange} class = "slds-m-bottom_small" label = "Search" >  
        </lightning-input>  

        <template if:true = {listRecs}>  
                
            <div style="height: 300px;">  

                <lightning-datatable key-field="Id"  
                                        data={listRecs}  
                                        columns={columns}  
                                        hide-checkbox-column="true"  
                                        show-row-number-column="true"
                                        default-sort-direction={defaultSortDirection}
                                        sorted-direction={sortDirection}
                                        sorted-by={sortedBy}
                                        onsort={onHandleSort}
                                        onrowaction={handleRowAction}>  
                </lightning-datatable>  

            </div>                   
    
        </template>  
    
        <template if:true = {error}>  

            {error}>  
                
        </template>  
 
    </lightning-card>  
 
</template>

JavaScript:

import { api, LightningElement, track, wire } from 'lwc';  
import fetchRecs from '@salesforce/apex/CustomListViewController.fetchRecs';   
import { NavigationMixin } from 'lightning/navigation';
 
export default class customListView extends NavigationMixin( LightningElement ) {  
 
    @track listRecs;  
    @track initialListRecs;
    @track error;  
    @track columns;  
    @api AccountId;
    @api RelatedObject;
    @api Fields;
    @api RelatedField;
    @api TableColumns;
    @api Title;
    sortedBy;
    defaultSortDirection = 'asc';
    sortDirection = 'asc';

    connectedCallback() {

        console.log( 'Columns are ' + this.TableColumns );
        this.columns = JSON.parse( this.TableColumns.replace( /([a-zA-Z0-9]+?):/g, '"$1":' ).replace( /'/g, '"' ) );
        console.log( 'Columns are ' + this.columns );

    }

    get vals() {  

        return this.RelatedObject + '-' + this.Fields + '-' +   
               this.RelatedField + '-' + this.AccountId;  

    }

    @wire(fetchRecs, { listValues: '$vals' })  
    wiredRecs( { error, data } ) {

        if ( data ) {

            console.log( 'Records are ' + JSON.stringify( data ) );
            this.listRecs = data;
            this.initialListRecs = data;

        } else if ( error ) {

            this.listRecs = null;
            this.initialListRecs = null;
            this.error = error;

        }
        
    }
 
    handleKeyChange( event ) {  
          
        const searchKey = event.target.value.toLowerCase();  
        console.log( 'Search Key is ' + searchKey );
 
        if ( searchKey ) {  

            this.listRecs = this.initialListRecs;
 
             if ( this.listRecs ) {

                let recs = [];
                for ( let rec of this.listRecs ) {

                    console.log( 'Rec is ' + JSON.stringify( rec ) );
                    let valuesArray = Object.values( rec );
                    console.log( 'valuesArray is ' + valuesArray );
 
                    for ( let val of valuesArray ) {
                        
                        if ( val.toLowerCase().includes( searchKey ) ) {

                            recs.push( rec );
                            break;
                        
                        }

                    }
                    
                }

                console.log( 'Recs are ' + JSON.stringify( recs ) );
                this.listRecs = recs;

             }
 
        }  else {

            this.listRecs = this.initialListRecs;

        }
 
    }  

    onHandleSort( event ) {

        const { fieldName: sortedBy, sortDirection } = event.detail;
        const cloneData = [...this.listRecs];
        cloneData.sort( this.sortBy( sortedBy, sortDirection === 'asc' ? 1 : -1 ) );
        this.listRecs = cloneData;
        this.sortDirection = sortDirection;
        this.sortedBy = sortedBy;

    }

    sortBy( field, reverse, primer ) {

        const key = primer
            ? function( x ) {
                  return primer(x[field]);
              }
            : function( x ) {
                  return x[field];
              };

        return function( a, b ) {
            a = key(a);
            b = key(b);
            return reverse * ( ( a > b ) - ( b > a ) );
        };

    }

    handleRowAction( event ) {

        const actionName = event.detail.action.name;
        const row = event.detail.row;
        switch ( actionName ) {
            case 'view':
                this[NavigationMixin.GenerateUrl]({
                    type: 'standard__recordPage',
                    attributes: {
                        recordId: row.Id,
                        actionName: 'view',
                    },
                }).then(url => {
                     window.open(url);
                });
                break;
            case 'edit':
                this[NavigationMixin.Navigate]({
                    type: 'standard__recordPage',
                    attributes: {
                        recordId: row.Id,
                        objectApiName: this.RelatedObject,
                        actionName: 'edit'
                    }
                });
                break;
            default:
        }

    }

    createNew() {

        this[NavigationMixin.Navigate]({            
            type: 'standard__objectPage',
            attributes: {
                objectApiName: this.RelatedObject,
                actionName: 'new'                
            }
        });

    }
 
}

meta.xml:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>49.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightningCommunity__Page</target>
        <target>lightningCommunity__Default</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightningCommunity__Default">
            <property name="AccountId" type="String"/>
            <property name="RelatedObject" type="String"/>
            <property name="RelatedField" type="String"/>
            <property name="Fields" type="String"/>
            <property name="TableColumns" type="String"/>
            <property name="Title" type="String"/>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

Apex Class:

public with sharing class CustomListViewController {
    
    @AuraEnabled( cacheable = true )  
    public static List < sObject > fetchRecs( String listValues ) {  
        
        system.debug( 'values are ' + listValues );  
        List < String > strList = listValues.split( '-' );  
        system.debug( 'values are ' + strList );   
        String strObject = strList.get( 0 );
        String strFields = strList.get( 1 );
        String strRelatedField = strList.get( 2 );
        String strAccountId;
        if ( strList.size() == 4)
            strAccountId = strList.get( 3 );
        List < sObject > listRecs = new List < sObject >();
        String strSOQL = 'SELECT Id, ' + strFields + ' FROM ' + strObject;

        if ( String.isNotBlank( strAccountId ) )
            strSOQL  += ' WHERE ' + strRelatedField+ ' = \'' + strAccountId + '\'';
        
        strSOQL += ' LIMIT 25';
        system.debug( 'SOQL is ' + strSOQL );
        listRecs = Database.query( strSOQL );
        return listRecs;
          
    }  

}

Community Builder Configuration:
 
TableColumns
[{label:'First Name',fieldName:'FirstName',sortable:true},{label:'Last Name',fieldName:'LastName'},{ label:'Email',fieldName: 'Email'},{type:'action',typeAttributes:{rowActions:[{label:'View',name:'view'},{label:'Edit',name:'edit'}]}}]


TableColumns
[{label:'Subject',fieldName:'Subject' },{label:'Origin',fieldName: 'Origin',sortable: true},{ label:'Reason',fieldName:'Reason'},{label:'Status', fieldName:'Status'},{type:'action',typeAttributes:{rowActions:[{label:'View',name:'view'},{label:'Edit',name:'edit'}]}}]

Output:
 


 

September 22, 2020

September 21, 2020

How to Use different logos based on profile in Salesforce Community?

 1. Create a custom component for Theme layout.

Component:
<aura:component implements="forceCommunity:themeLayout" controller="UserInfoController">
    
    <aura:attribute name="search" type="Aura.Component[]" required="false"/>
    <aura:attribute name="profileMenu" type="Aura.Component[]" required="false"/>
    <aura:attribute name="navBar" type="Aura.Component[]" required="false"/>
    <aura:attribute name="footer" type="Aura.Component[]" />
    <aura:attribute name="imgSrcVal" type="String"/>
    
    <aura:handler name="init" value="{!this}" action="{!c.init}"/>
    
    <div class="slds">
        <div class="slds-grid slds-grid_vertical-align-center">
            <div class="slds-size_1-of-4">
                <img src="{!v.imgSrcVal}"/>
            </div>
            <div class="slds-size_3-of-4 slds-grid slds-wrap">
                <div class="slds-size_2-of-3">
                 {!v.navBar}
                </div>
                <div class="slds-size_1-of-3">
                 {!v.profileMenu}
                </div>
                <div class="slds-size_3-of-3">
                 {!v.search}
                </div>
            </div>
        </div>
        <div class="mainContentArea">
            {!v.body}
        </div>
        <footer id="footer">
            {!v.footer}
        </footer>
    </div>
    
</aura:component>


Controller:
({
    
     init : function(component, event, helper) {
        
        let action = component.get( "c.fetchUser" );
        action.setCallback( this, function( response ) {
            let state = response.getState();
            if ( state === "SUCCESS" ) {
                
                let strResponse = response.getReturnValue();
                   console.log( 'Profile is ' + strResponse.Profile.Name );
            
                switch( strResponse.Profile.Name ) {
                        
                    case "Community User":
                    component.set( "v.imgSrcVal", $A.get( '$Resource.CommunityUser' ) );
                    break;
                    case "Community Plus User":
                    component.set( "v.imgSrcVal", $A.get( '$Resource.CommunityPlusUser' ) );
                    break;
                    default:                        
                    component.set( "v.imgSrcVal", $A.get( '$Resource.StandardUser' ) );
                        
                }
                
            }
        });
        $A.enqueueAction( action );
        
    }
    
})

Apex Class:
public class UserInfoController {
    
    @AuraEnabled
    public static user fetchUser() {
        
        User objUser = [ SELECT Id, Profile.Name, UserRole.Name FROM User Where id =: userInfo.getUserId() ];
        return objUser;
        
    }
    
}
 
2. Create Custom Theme using the Aura Component.
 
3. Use the Custom Theme layout for the page.
 


4. Store the images(logos) in Static Resources.



Output:



Here the logo is Standard User since I logged in as System Administrator.

How to show Account Team in Contact in Salesforce?

Use Related List - Single standard component on Lightning Record Page.
 
 



September 17, 2020

Order of events when a merge occurs in Salesforce

For example, if two contacts are merged, only the delete and update contact triggers fire. No triggers for records related to the contacts, such as accounts or opportunities, fire.

1. The before delete trigger fires.
2. The system deletes the necessary records due to the merge, assigns new parent records to the child records, and sets the MasterRecordId field on the deleted records.
3. The after delete trigger fires.
4. The system does the specific updates required for the master record. Normal update triggers apply.

Account Trigger:

trigger AccountTrigger on Account ( before delete, after delete, before update, after update ) {
    
    if( trigger.isDelete ) {
        
        if ( trigger.isBefore )
            system.debug( 'Before Delete in Account' );
        else if ( trigger.isAfter )
            system.debug( 'After Delete in Account' );
        
    } else if( trigger.isupdate ) {
        
        if ( trigger.isBefore )
            system.debug( 'Before update in Account' );
        else if ( trigger.isAfter )
            system.debug( 'After update in Account' );
        
    }

}

Opportunity Trigger:

trigger OpportunityTrigger on Opportunity ( before update, after update ) {
    
    if( trigger.isupdate ) {
        
        if ( trigger.isBefore )
            system.debug( 'Before update in Opportunity' );
        else if ( trigger.isAfter )
            system.debug( 'After update in Opportunity' );
        
    }

}

Contact Trigger:

trigger ContactTrigger on Contact ( before update, after update ) {
    
    if( trigger.isupdate ) {
        
        if ( trigger.isBefore )
            system.debug( 'Before update in Contact' );
        else if ( trigger.isAfter )
            system.debug( 'After update in Contact' );
        
    }

}


Output:



September 11, 2020

Salesforce Scheduled Flow

 
1. It runs as Automated Process User. So, use Automated Process User in Debug Logs.
 

 2. When calling Apex Class, it is bulkified.

Sample Code:

global class FlowSchedule {
    
    @InvocableMethod( label='Show Outputs' description='Getting multiple values' )
    global static List < FlowOutput > showOutputs( List < FlowInput > inputs ) {
        
        FlowInput objInput = inputs.get( 0 );
        List < FlowOutput > outputs = new List < FlowOutput >();
        for ( FlowInput inp : inputs ) {
            FlowOutput objOutput = new FlowOutput();
            objOutput.str3 = objInput.str1;
            objOutput.str4 = objInput.str2;
            outputs.add ( objOutput );
        }
        system.debug( 'outputs size is ' + outputs.size() );
        system.debug( 'outputs are ' + outputs );
        return outputs;
        
    }
    
    global class FlowInput {
        
        @InvocableVariable
        global String str1;
        
        @InvocableVariable
        global String str2;
        
    }
    
    global class FlowOutput {
        
        @InvocableVariable
        global String str3;
        
        @InvocableVariable
        global String str4;
        
    }
    
}
 
 
 


September 9, 2020

Freezing and Unfreezing multiple users or how to avoid multiple users logging into Salesforce

 IsFrozen in UserLogin is used for Freeze or unfreeze.

So, fetch the UserLogin records and mass update.

 
Data Loader can be used for Export and Import.

September 7, 2020

JavaScript Interview Questions with Answers Part 2

1. Numeric conversion, unary +
The plus + exists in two forms: the binary form that we used above and the unary form.
The unary plus or, in other words, the plus operator + applied to a single value, doesn’t do anything to numbers. But if the operand is not a number, the unary plus converts it into a number.

Example:
// No effect on numbers
let x = 1;
alert( +x ); // 1
let y = -2;
alert( +y ); // -2
// Converts non-numbers
alert( +true ); // 1
alert( +"" );   // 0

2. Increment/decrement
let counter = 1;
let a = ++counter;
alert(a); // 2
The prefix form ++counter increments counter and returns the new value, 2. So, the alert shows 2.

Now, let’s use the postfix form:
let counter = 1;
let a = counter++;
alert(a); // 1
The postfix form counter++ also increments counter but returns the old value (prior to increment). So, the alert shows 1.

If the result of increment/decrement is not used, there is no difference in which form to use:
let counter = 0;
counter++;
++counter;
alert( counter ); // 2, the lines above did the same

If we’d like to increase a value and immediately use the result of the operator, we need the prefix form:
let counter = 0;
alert( ++counter ); // 1

If we’d like to increment a value but use its previous value, we need the postfix form:
let counter = 0;
alert( counter++ ); // 0

3. Nullish coalescing operator '??'
The nullish coalescing operator ?? provides a short syntax for selecting a first “defined” variable from the list.

The result of a ?? b is:

    a if it’s not null or undefined,
    b, otherwise.

let a = null;
let b = null;
let c = "InfallibleTechie";

// show the first not-null/undefined value
alert(a ?? b ?? c ?? "XYZ"); // InfallibleTechie

4. function with no return statement in JavaScript

1. Without Return
function show() {
  alert( "Hello" );
}

alert( show );
alert( show() );

Output:
It will alert
function show() {
  alert( "Hello" );
}

It will alert
Hello

It will alert
Undefined

2. With Return
function show() {
  alert( "Hello" );
  return "Sample";
}

alert( show );
alert( show() );

Output:
It will alert
function show() {
  alert( "Hello" );
  return "Sample";
}

It will alert
Hello

It will alert
Sample

5. Function Expression vs Function Declaration

Function Declaration: a function, declared as a separate statement, in the main code flow.

// Function Declaration
function sum(a, b) {
  return a + b;
}

A Function Declaration can be called earlier than it is defined.

Function Expression: a function, created inside an expression or inside another syntax construct. Here, the function is created at the right side of the let sum = function(a, b) {
  return a + b;
};

A Function Expression is created when the execution reaches it and is usable only from that moment.

6. backtick quotes `
backtick quotes ` allow to split the string into multiple lines

var x = "testing
test";// is not allowed

var x = `testing
test`;//is allowed

7. What happens when an object variable is copied?
When an object variable is copied, the reference is copied and the object is not duplicated.

let user = { name: "John" };
let admin = user; // copies the reference

Now we have two variables, each one with the reference to the same object.

Two objects are equal only if they are the same object.

Here two variables reference the same object, thus they are equal:
let a = {};
let b = a; // copy the reference
alert( a == b ); // true, both variables reference the same object
alert( a === b ); // true

And here two independent objects are not equal, even though both are empty:
let a = {};
let b = {}; // two independent objects
alert( a == b ); // false

Object.assign should be used to copy without reference.
let user = { name: "John" };
let user1 = Object.assign({}, user);
user1.name = "Cat";
alert(user.name); // John
alert( user1.name ); // Cat

8. this keyword
The value of this is evaluated during the run-time, depending on the context.

Arrow functions have no “this”
Arrow functions are special: they don’t have their “own” this. If we reference this from such a function, it’s taken from the outer “normal” function.

9. Constructor function
1. Constructor functions technically are regular functions.
2. They are named with capital letter first.
3. They should be executed only with "new" operator.

Example:
function User(name) {
  this.name = name;
  this.isAdmin = false;
}
let user = new User("John");
alert(user.name); // John
alert(user.isAdmin); // false

When a function is executed with new, it does the following steps:
1. A new empty object is created and assigned to this.
2. The function body executes. Usually it modifies this, adds new properties to it.
3. The value of this is returned.

But if there is a return statement, then the rule is simple:
1. If return is called with an object, then the object is returned instead of this.
2. If return is called with a primitive, it’s ignored.
In other words, return with an object returns that object, in all other cases this is returned.

Example:
Here return is primitive. So, this will be returned.
function User(name) {
  this.name = name;
  this.isAdmin = false;
  return "sample";
}
let user = new User("John");
alert(user.name); // John
alert(user.isAdmin); // false

Example:
Here return is an object. So, object will be returned.
function User(name) {
  this.name = name;
  this.isAdmin = false;
  return { name : "Frank" };
}
let user = new User("John");
alert(user.name); // Frank
alert(user.isAdmin); // undefined

10. Optional chaining '?.'
The optional chaining ?. is an error-proof way to access nested object properties, even if an intermediate property doesn’t exist.

Example: 
let user = {};
//alert(user.address.street); // Error!. If you uncomment this code. The exectuion will stop due to Type Error
alert( user?.address?.street );// Undefined

11. Symbol in JavaScript
Symbol is a primitive type for unique identifiers.

Object.getOwnPropertySymbols(obj) allows us to get all symbols. Also there is a method named Reflect.ownKeys(obj) returns all keys of an object including symbolic ones.

Symbols allow us to create “hidden” properties of an object, that no other part of code can accidentally access or overwrite.

Example:
let user = { // belongs to another code
  name: "Frank"
};
let id = Symbol("id");
user[id] = 1;
alert( user[id] ); // we can access the data using the symbol as the key

Symbols are skipped by for…in
Symbolic properties do not participate in for..in loop.

let id = Symbol("id");
let user = {
  name: "Frank",
  age: 30,
  [id]: 123
};

for (let key in user) alert(key); // name, age (no symbols)

// the direct access by the symbol works
alert( "Direct: " + user[id] );

12. Difference between Square brackets and charAt in JavaScript
The square brackets are a modern way of getting a character, while charAt exists mostly for historical reasons.

The only difference between them is that if no character is found, [] returns undefined, and charAt returns an empty string:

let str = `Hello`;alert( str[1000] ); // undefined
alert( str.charAt(1000) ); // '' (an empty string)

13. pop/push, shift/unshift methods in JavaScript

Push
push adds an element to the end.

Pop
pop takes an element from the end.
shift

Shift
Extracts/Removes the first element of the array and returns it.

Unshift
Add the element to the beginning of the array.

Methods push/pop run fast, while shift/unshift are slow.

14. splice and slice methods in JavaScript
Splice:
Syntax:

splice(index[, deleteCount, elem1, ..., elemN])

index - start index to delete
deleteCount - how many elements to delete
elem1, ...., elemN - Elements to add it to the Array. Adding starts from the index position mentioned.

Example
let arr = [0, 1, 2, 3, 4, 5];
arr.splice( 2, 2, "a", "b", "c", "d" );
alert( arr );//0, 1, a, b, c, d, 4, 5

Slice:
It returns a new array copying to it all items from index start to end (not including end)
Syntax:
slice([start], [end])

Example:
let arr = ["t", "e", "s", "t"];
alert( arr.slice(1, 3) ); // e,s (copy from 1 to 3)

15. JSON.stringify
To convert objects into JSON.
The method JSON.stringify() takes the object and converts it into a string.

Syntax:
JSON.stringify(value[, replacer, space])

value
    A value to encode.
replacer
    Array of properties to encode or a mapping function function(key, value).
space
    Amount of space to use for formatting

Example:
let user = {
  name: "John",
  interests: [ { sport : "Cricket" }, { sport: "Painting" } ]
};

let user1 = JSON.stringify( user, function replacer( key, value ) {
  if ( key == 'name' )
    return "Mathews";
    return value;
} );

alert( user1 );
Output:
{name: "John", interests: [{sport:"Cricket"},{sport:"Painting"}]}

Example:

let user = {
  name: "John",
  interests: [ { sport : "Cricket"}, { sport: "Painting"} ]
};

alert( JSON.stringify( user, [ "interests" ], 4 ) );
Output:
{
    "interests": [
        {},
        {}
    ]
}


Example:
let user = {
  name: "John",
  interests: [ { sport : "Cricket" }, { sport: "Painting" } ]
};

alert( JSON.stringify( user, [ "interests", "sport" ], 4 ) );
Output:
{
    "interests": [
        {
            "sport": "Cricket"
        },
        {
            "sport": "Painting"
        }
    ]
}

Example:
let user = {
  name: "John",
  interests: [ { sport : "Cricket" }, { sport: "Painting" } ]
};

alert( JSON.stringify( user, [ "interests", "sport", "name" ], 4 ) );
Output:
{
    "interests": [
        {
            "sport": "Cricket"
        },
        {
            "sport": "Painting"
        }
    ],
    "name": "John"
}

JSON.parse
To convert JSON back into an object.

Syntax:
JSON.parse(str, [reviver])

Example:
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';

let meetup = JSON.parse(str, function(key, value) {
  if (key == 'date') return new Date(value);
  return value;
});

alert( meetup.date.getDate() );//30. If the date is not converted using Date(), it will throw an error