•  

    How do You Manage User Replacement or Mirror User in your Org?

    Do you work in a Salesforce org which has very complex security Model with multiple permission sets ,profiles,roles,public groups, queues.

    What do you do when some user replacement comes in or some user has to be mirrored as an existing user? 

    Please see my POC this provides a page where you can mention who is being replaced or mirrored and who is replacing .

    Check the check boxes you need and all job will be done at a go:

    1) You can add who is replacing to all the Public groups and queues where who is replaced or mirrored is a part of.

    2) You can assign the permission sets,role,profile of who is replaced or mirrored to who is replacing .

    3) This is more cool- You can even change the owner of multiple records in multiple objects at a go to who is replacing from who is replaced.

    In this Scenario Bruce is getting replaced by Chris. Chris should have all the permission sets the role and the profile same as Bruce.
    He should get assigned to all the Public Groups and Queues and should also own all the Accounts and Contacts in which Bruce is part off.

    Author :Avik Mukherjee

     

    transferOwnership.html

    ========================================

    <template>
    
      <div style="height: 11px !important;">
    
        <lightning-card>
    
          <div style="text-align: center; font-size: xx-large;  ">
    
            <lightning-icon icon-name="action:user" size="xx-small" variant="border-inverse"></lightning-icon>
    
            User Replacement and User Mirror
    
          </div>
    
          <div class="slds-grid slds-gutters" style="margin-top: 4%;">
    
            <div class="slds-col" style="margin-left: 19%;">
    
              <span>
    
                <b>User who is Replaced</b>
    
                <c-lwclookup objectname="User" onselected={handleselect} fieldname="Name" numrecords="10"
    
                  iconname="standard:user" usercategory="usergettingreplaced"> </c-lwclookup>
    
              </span>
    
            </div>
    
            <div class="slds-col">
    
              <span>
    
                <b>User who is Replacing</b>
    
                <c-lwclookup objectname="User" onselected={handleselect} fieldname="Name" numrecords="10"
    
                  iconname="standard:user" usercategory="userwhoisreplacing"> </c-lwclookup>
    
              </span>
    
            </div>
    
          </div>
    
          <div class="slds-grid slds-gutters" style="margin-top: 3%;">
    
            <div class="slds-col" style="margin-left: 13%;">
    
              <b>
    
                <span>
    
                  <lightning-input label="Deactivate User who is Leaving" class="slds-p-left_xx-large" type="checkbox"
    
                    onchange={handledeactivateuserchange}>
    
                  </lightning-input>
    
    
                  <lightning-input label="Copy Role and Profile" class="slds-p-left_xx-large" type="checkbox"
    
                    onchange={handleroleprofilechange}>
    
                  </lightning-input>
    
    
                  <lightning-input label="Copy Permission Set" class="slds-p-left_xx-large" type="checkbox"
    
                    onchange={handlepermissionsetchange}>
    
                  </lightning-input>
    
                </span>
    
              </b>
    
            </div>
    
            <div class="slds-col">
    
              <b>
    
                <span>
    
                  <lightning-input label="Copy Public Groups and Queue" class="slds-p-left_xx-large" type="checkbox"
    
                    onchange={handlepublicgroupqueuechange}>
    
                  </lightning-input>
    
    
                  <lightning-input label="Change Ownership of Records" class="slds-p-left_xx-large" type="checkbox"
    
                    onchange={handleselectobject}>
    
                  </lightning-input>
    
    
                  <div style="margin-top: 3%;">
    
                    <template if:true={showObjects}>
    
                      <lightning-dual-listbox name="Objects" label="Select the Objects" source-label="Available"
    
                        selected-label="Selected" field-level-help="Select the Objects" options={options}
    
                        onchange={handleChange}></lightning-dual-listbox>
    
                    </template>
    
                  </div>
    
                </span>
    
              </b>
    
            </div>
    
          </div>
    
          <div class="slds-align_absolute-center" style="margin-top: 3%;">
    
            <lightning-button variant="success" label="Save" title="Save action" onclick={handleSave}
    
              class="slds-m-left_x-small"></lightning-button>
    
            <lightning-button variant="destructive" label="Cancel" title="Cancel action" onclick={handleCancel}
    
              class="slds-m-left_x-small"></lightning-button>
    
          </div>
    
        </lightning-card>
    
      </div>
    
      <template if:true={openModal}>
    
        <lightning-card>
    
          <div class="demo-only" style="height: 640px;">
    
            <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true"
    
              aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open">
    
              <div class="slds-modal__container">
    
                x
    
                <header class="slds-modal__header">
    
                  <lightning-button-icon alternative-text="Close" variant="bare-inverse" onclick={closePopUp}
    
                    class="slds-button slds-modal__close closeIcon slds-button_icon-bare slds-button_icon-inverse"
    
                    icon-name="utility:close"></lightning-button-icon>
    
                  <h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Success</h2>
    
                  <p class="slds-m-top_x-small">Done and Dusted </p>
    
                </header>
    
              </div>
    
            </section>
    
            <div class="slds-backdrop slds-backdrop_open"></div>
    
          </div>
    
        </lightning-card>
    
      </template>
    
    </template>

    transferOwnership.js

    ===========================================

    import { LightningElement, api, track } from 'lwc';
    
    import getonwertransfer from '@salesforce/apex/userReplacementHandler.copyuservalues';
    
    import { ShowToastEvent } from 'lightning/platformShowToastEvent';
    
    
    export default class TransferOnwership extends LightningElement {
    
        _selected = [];
    
        @api userwhoisreplacing;
    
        @api usergettingreplaced;
    
        @track showObjects = false;
    
        @track roleProfileChange = false;
    
        @track permissionsetcheck = false;
    
        @track publicgroupqueue = false;
    
        @track deactivateuser = false;
    
        @track openModal = false;
    
    
        get options() {
    
            return [
    
                { label: 'Account', value: 'Account' },
    
                { label: 'Opportunity', value: 'Opportunity' },
    
                { label: 'Case', value: 'Case' },
    
                { label: 'Lead', value: 'Lead' },
    
                { label: 'Contact', value: 'Contact' }
    
            ];
    
        }
    
    
        get selected() {
    
            return this._selected.length ? this._selected : 'none';
    
        }
    
    
        handleChange(e) {
    
            this._selected = e.detail.value;
    
    
            console.log('Selected values' + this._selected);
    
        }
    
    
        handledeactivateuserchange(event) {
    
            if (event.target.checked) {
    
                this.deactivateuser = true;
    
            } else {
    
                this.deactivateuser = false;
    
            }
    
        }
    
    
        handleroleprofilechange(event) {
    
            if (event.target.checked) {
    
                this.roleProfileChange = true;
    
            } else {
    
                this.roleProfileChange = false;
    
            }
    
        }
    
    
        handlepermissionsetchange(event) {
    
            if (event.target.checked) {
    
                this.permissionsetcheck = true;
    
            } else {
    
                this.permissionsetcheck = false;
    
            }
    
        }
    
    
        handlepublicgroupqueuechange(event) {
    
            if (event.target.checked) {
    
                this.publicgroupqueue = true;
    
            } else {
    
                this.publicgroupqueue = false;
    
            }
    
        }
    
    
        handleselectobject(event) {
    
            if (event.target.checked) {
    
                this.showObjects = true;
    
            } else {
    
                this.showObjects = false;
    
            }
    
        }
    
    
        handleselect(event) {
    
    
            if (event.detail.usercategory == 'usergettingreplaced') {
    
                this.usergettingreplaced = event.detail.recordId;
    
    
            } else if (event.detail.usercategory == 'userwhoisreplacing') {
    
                this.userwhoisreplacing = event.detail.recordId;
    
            }
    
        }
    
    
        handleSave() {
    
            console.log('here');
    
            getonwertransfer({
    
                deactivateuser: this.deactivateuser,
    
                copyroleprofile: this.roleProfileChange,
    
                copypermissionset: this.permissionsetcheck,
    
                copyobjectownership: this.showObjects,
    
                copypublicgroupandqueue: this.publicgroupqueue,
    
                userwhoisleaving: this.usergettingreplaced,
    
                userwhoisreplacing: this.userwhoisreplacing,
    
                selectedobjects: this._selected
    
            })
    
                .then(result => {
    
                    console.log('here' + JSON.stringify(result));
    
                    this.openModal = true;
    
                })
    
                .catch(error => {
    
    
                    console.log('here error' + JSON.stringify(error));
    
    
                    // display server exception in toast msg 
    
                    const event = new ShowToastEvent({
    
                        title: 'Error',
    
                        variant: 'error',
    
                    });
    
                    this.dispatchEvent(event);
    
                });
    
        }
    
    
    }

    transferOwnership.metadataxml

    ============================================

    <?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__AppPage</target>
    
            <target>lightning__RecordPage</target>
    
            <target>lightning__HomePage</target>
    
            <target>lightning__Tab</target>
    
        </targets>
    
    </LightningComponentBundle>

     

    lwclookup.html

    =========================================

    <template>
    
        <div style={lookupstyle}>
    
            <div class="slds-form-element">
    
    
                <div class="slds-form-element__control">
    
                    <div class="slds-combobox_container">
    
    
                        <div class={txtclassname} data-id="resultBox" aria-expanded="false" aria-haspopup="listbox"
    
                            role="combobox">
    
                            <div class="slds-form-element__control slds-input-has-icon slds-input-has-icon slds-input-has-icon_left-right"
    
                                role="none">
    
                                <lightning-input read-only={inputReadOnly} data-id="userinput" label={Label}
    
                                    name="customLookup" onchange={searchField} value={selectRecordName} class="leftspace">
    
                                </lightning-input>
    
    
                                <div if:true={iconFlag}>
    
                                    <span
    
                                        class="slds-icon_container slds-icon-utility-search slds-input__icon slds-input__icon_right iconheight">
    
                                        <lightning-icon class="slds-icon slds-icon slds-icon_small slds-icon-text-default"
    
                                            icon-name="utility:search" size="x-small" alternative-text="icon">
    
                                        </lightning-icon>
    
                                    </span>
    
                                </div>
    
                                <div if:true={clearIconFlag}>
    
                                    <button
    
                                        class="slds-input__icon slds-input__icon_right slds-button slds-button_icon iconheight"
    
                                        onclick={resetData}>
    
                                        <lightning-icon class="slds-icon slds-icon slds-icon_small slds-icon-text-default"
    
                                            icon-name="utility:clear" size="x-small" alternative-text="icon">
    
                                        </lightning-icon>
    
                                        <span class="slds-assistive-text">Clear</span>
    
                                    </button>
    
                                </div>
    
                            </div>
    
    
                            <!-- Second part display result -->
    
                            <div id="listbox-id-1"
    
                                class="slds-dropdown slds-dropdown_length-with-icon-7 slds-dropdown_fluid" role="listbox">
    
                                <ul class="slds-listbox slds-listbox_vertical" role="presentation">
    
                                    <template for:each={searchRecords} for:item="serecord">
    
                                        <li role="presentation" class="slds-listbox__item" key={serecord.Id}>
    
    
                                            <div data-id={serecord.Id} data-name={serecord.val} onclick={setSelectedRecord}
    
                                                class="slds-media slds-listbox__option slds-listbox__option_entity slds-listbox__option_has-meta"
    
                                                role="option">
    
                                                <span class="slds-media__body">
    
                                                    <span
    
                                                        class="slds-listbox__option-text slds-listbox__option-text_entity">
    
                                                        <lightning-icon
    
                                                            class="slds-icon slds-icon slds-icon_small slds-icon-text-default"
    
                                                            icon-name={iconname} size="x-small" alternative-text="icon">
    
                                                        </lightning-icon>
    
                                                        &nbsp; &nbsp;{serecord.val}</span>
    
                                                    <span
    
                                                        class="slds-listbox__option-meta slds-listbox__option-meta_entity">{objectname}
    
                                                        • {serecord.val}</span>
    
                                                </span>
    
                                            </div>
    
                                        </li>
    
                                    </template>
    
                                </ul>
    
                            </div>
    
                            <div if:true={noResultFound}>
    
                                No result found.
    
                            </div>
    
                            <div if:true={LoadingText}>
    
                                <div class="demo-only demo-only demo-only_viewport" style="height:6rem;">
    
                                    <div role="status" class="slds-spinner slds-spinner_x-small slds-spinner_brand">
    
                                        <span class="slds-assistive-text">Loading</span>
    
                                        <div class="slds-spinner__dot-a"></div>
    
                                        <div class="slds-spinner__dot-b"></div>
    
                                    </div>
    
                                </div>
    
                            </div>
    
                        </div>
    
                    </div>
    
                </div>
    
            </div>
    
        </div>
    
    </template>

    lwclookup.js

    ================================================

    /* eslint-disable no-debugger */
    
    /* eslint-disable no-console */
    
    /* eslint-disable eqeqeq */
    
    import { LightningElement,api,track } from 'lwc';
    
    import fetchRecords from '@salesforce/apex/userReplacementHandler.fetchRecords';
    
    export default class LwcCustomLookup extends LightningElement {
    
        @api objectname = '';  // default is Account. Can be modified by parent cmp
    
        @api fieldname = '';      //default field is Name. Can be modified by parent cmp
    
        @api numrecords=10; // Number of records to be fetched from Apex while searching
    
        @api iconname = ''
    
        @api selectRecordId = '';
    
        @api selectRecordName;
    
        @api lookupstyle = 'width : 50%;';
    
        @track Label;
    
        @track searchRecords = [];
    
        @track LoadingText = false;
    
        @track txtclassname = 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click';
    
        @track noResultFound = false;
    
        @track iconFlag =  true;
    
        @track clearIconFlag = false;
    
        @track inputReadOnly = false;
    
        @api usercategory;
    
       
    
      
    
        searchField(event) {
    
            var searchTxt = event.target.value;
    
            this.LoadingText = true;
    
           
    
            fetchRecords({ ObjectName: this.objectname, field: this.fieldname, searchString: searchTxt , NumRecords : this.numrecords})
    
            .then(result => {
    
                this.searchRecords= result;
    
                this.LoadingText = false;
    
               
    
                this.txtclassname =  result.length > 0 ? 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-is-open' : 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click';
    
                if(searchTxt.length > 0 && result.length == 0) {
    
                    this.noResultFound = true;
    
                }
    
                else {
    
                    this.noResultFound = false;
    
                }
    
                if(this.selectRecordId != null && this.selectRecordId.length > 0) {
    
                    this.iconFlag = false;
    
                    this.clearIconFlag = true;
    
                }
    
                else {
    
                    this.iconFlag = true;
    
                    this.clearIconFlag = false;
    
                }
    
            })
    
            .catch(error => {
    
                console.log('-------error-------------'+error);
    
                console.log(error);
    
            });        
    
        }
    
       
    
       setSelectedRecord(event) { 
    
            this.txtclassname =  'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click';
    
            this.iconFlag = false;
    
            this.clearIconFlag = true;
    
            this.selectRecordName = event.currentTarget.dataset.name;        
    
            this.selectRecordId = event.currentTarget.dataset.id;
    
            this.inputReadOnly = true;
    
            console.log('this.selectRecordId' + this.selectRecordId + ' ' + this.usercategory);
    
            const selectedEvent = new CustomEvent('selected', { detail: 
    
                {recordId : event.currentTarget.dataset.id,
    
                fieldValue : event.currentTarget.dataset.name,
    
                usercategory : this.usercategory}
    
            });
    
            this.dispatchEvent(selectedEvent);
    
        }
    
       
    
        resetData() {
    
            this.selectRecordName = "";
    
            this.selectRecordId = "";
    
            this.inputReadOnly = false;
    
            this.iconFlag = true;
    
            this.clearIconFlag = false;
    
            const selectedEvent = new CustomEvent('unselected', { detail: 
    
                {recordId : null,
    
                fieldValue : null}
    
            });
    
            this.dispatchEvent(selectedEvent);
    
        }
    
    }

    lwclookup.css

    ============================================

    div input[readonly] {
    
        padding-left: 1.75rem !important;  
    
    }
    
    
    .slds-input{
    
        padding-left: 1.75rem !important;
    
    }
    
    .slds-input-has-icon .slds-input__icon {
    
        top: 56% !important;
    
    }
    
    .slds-icon_small, .slds-icon--small {
    
        width: 1.5rem;
    
        height: 1.5rem;
    
        line-height: 2;
    
    }

    Apex Class- userReplacementHandler.cls

    ====================================================

    public with sharing class userReplacementHandler {
    
    
        @AuraEnabled(cacheable=true)
    
        public static List<Records> fetchRecords( String ObjectName, String field, String searchString, Integer NumRecords ) {
    
            List<Records> recordsList = new List<Records>();
    
    
            try {
    
                String query = 'SELECT Id, ' + field + ' FROM '+ objectName + ' WHERE '+ field +' LIKE ' +
    
                 '\'' + String.escapeSingleQuotes(searchString.trim()) + '%\'' + ' LIMIT ' + NumRecords;
    
                System.debug(query);
    
                for(SObject s : Database.query(query)){
    
                    Records record = new Records();
    
    
                    record.Id = String.valueOf(s.get('id'));
    
                    record.val = String.valueOf(s.get(field));
    
                    recordsList.add(record);
    
                } 
    
            } catch (Exception err) {           
    
                    throw new AuraHandledException(err.getMessage());
    
            }
    
            
    
            return recordsList;
    
        }
    
    
        public class Records{
    
            @AuraEnabled public String Id {get;set;}
    
            @AuraEnabled public String val {get;set;}
    
        }
    
    
        @AuraEnabled
    
        public static void copyuservalues(boolean deactivateuser,
    
                                          boolean copyroleprofile,
    
                                          boolean copypermissionset,
    
                                          boolean copyobjectownership,
    
                                          boolean copypublicgroupandqueue,
    
                                          Id userwhoisleaving,
    
                                          Id userwhoisreplacing,
    
                                          String[] selectedobjects
    
                                          ){
    
    
        system.debug('I Am here');
    
        if(copyroleprofile== true){
    
            list<User> existingRoleProfile= [Select id,ProfileId,UserRoleId FROM User where id =:userwhoisleaving];
    
            
    
            User updateUser= new User(id= userwhoisreplacing, ProfileId=existingRoleProfile[0].ProfileId,UserRoleId= existingRoleProfile[0].UserRoleId);
    
            database.update(updateUser,false);
    
        }
    
        if(copypermissionset==true){
    
            list<PermissionSetAssignment> existingPermissionSetData=[SELECT AssigneeId,Id,PermissionSetId  FROM PermissionSetAssignment where AssigneeId =:userwhoisleaving];
    
            list<PermissionSetAssignment> createNewPermissionSetAssignment= new list<PermissionSetAssignment>();
    
            if(existingPermissionSetData.size()> 0){
    
                for(PermissionSetAssignment pr: existingPermissionSetData ){
    
                   
    
                    createNewPermissionSetAssignment.add(new PermissionSetAssignment(AssigneeId= userwhoisreplacing,PermissionSetId = pr.PermissionSetId  ));
    
                }
    
               
    
                database.insert(createNewPermissionSetAssignment,false);
    
            }
    
            database.delete(existingPermissionSetData);
    
        }
    
        if(copypublicgroupandqueue==true){
    
            list<GroupMember> existingPublicGroupAndQueueData=[SELECT GroupId,Id,UserOrGroupId FROM GroupMember where UserOrGroupId=:userwhoisleaving];
    
            list<GroupMember> createPublicGroupAndQueueDat= new list<GroupMember>();
    
            if(existingPublicGroupAndQueueData.size()> 0){
    
                for(GroupMember gr: existingPublicGroupAndQueueData){
    
                    createPublicGroupAndQueueDat.add(new GroupMember(GroupId=gr.GroupId,UserOrGroupId=userwhoisreplacing));
    
                }
    
    
                database.insert(createPublicGroupAndQueueDat,true);
    
            }
    
            database.delete(existingPublicGroupAndQueueData);
    
        }
    
        if(copyobjectownership==true && selectedobjects.size()>0 ){
    
            updateRecordOwner(userwhoisreplacing,userwhoisleaving,selectedobjects);
    
        }
    
    
        }
    
    // Can be moved to queable class if there is a huge data.
    
        @future
    
        public static void updateRecordOwner(Id userwhoisreplacing,Id userwhoisleaving,String[] selectedobjects){
    
    
           
    
            list<SObject> allobjectrecord= new list<SObject>();
    
            for(String s: selectedobjects){
    
                list<sObject> currentobject= new list<sobject>();
    
                String query='Select id,OwnerId from '+ s +' where Ownerid=:userwhoisleaving';
    
                currentobject=Database.query(query);
    
                for(sObject sobj: currentobject){
    
                    sobj.put('OwnerId',userwhoisreplacing);
    
                }
    
                allobjectrecord.addall(currentobject);
    
            }
    
    
            if(allobjectrecord.size()>0){
    
                Database.update(allobjectrecord,false);
    
            }
    
        }
    
    }

     

Comments

  •  
    icon

    Amit says (Jul 25, 2020):

    Very nice implementation and demo. Thank you for sharing. I would add to the code the ability to change user only for Leads that are not Converted / Disqualified. Also for Opportunities add the ability to change user only for Opps that are no Closed. This can be super helpful! Once again, thanks!

Post Comments