How To

Update Inactive User to Active User as Contact Owner

Handoff

This blog post was inspired by Trailblazer community member, KC Brotherton.

Special S/O to Jason Teller, Salesforce Process Automation Product Management for the assist on the workaround.

TBCommunityPost.GIF

Many times, people leave a company and you need to update the record ownership to an active internal user. This process can be automated using lightning flow by creating a process that invokes a flow.

Easy peasy, right? Not quite. If the flow is invoked from an immediate action in process builder, when the process executes, it will result in a flow error: “The flow tried to update these records: null. This error occurred: MIXED_DML_OPERATION: DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): Contact, original object: User.”

In Summer ’18, more granular batching of scheduled process actions and waiting flow interviews was introduced to avoid runtime errors when running DML operations on both standard and setup objects. However, the flow runtime errors continue to occur when running DML operations on both standard and setup objects in the same batch transaction. To work around this, we will use a scheduled action to invoke the flow.

Here are a few lessons learned from implementing this use case:

  • Learn how to invoke flow from a process.
  • Avoid having to hardcode IDs in our flows. In this example, we use a custom label. There are alternatives to this design, such as using custom metadata type or performing a record lookup of the user object in the flow to obtain the ID based on the user’s name. (Note: Hardcoded Ids are bad practice.)
  • Do not perform database commits within a loop.
  • Provide descriptions, where provided, in Salesforce. This may be tedious step, I know, but your future self will thank you when you are trying to remember what you configured or assist other/future admins when troubleshooting or enhancing what was built. This includes variables, the purpose of a flow, what each flow element does, etc.

Business Use Case:  Addison Dogster is the system administrator at Universal Containers. Mary Markle is the Director of Operations. She mentioned to Addison that there are contacts in the system owner by people who are no longer with the company. She would like these contacts assigned to designated “generic” user until the contacts are assigned out to actual users. Mary would like to automate the process of assigning the contacts to the generic user account.

The process Mary would like to automate is as follows:

ProcessWalkthrough.GIF

View image full screen

Solution: Being the #AwesomeAdmin that Addison is, she was able to solution this declaratively using Lightning Flow. She builds the first part of the process using Process Builder run on the User Object and the latter part of the process in Cloud Flow Designer by creating a flow.

ProcessWalkthrough-Solution.GIF

View image full screen

The outcome looks like this:

Process created in Process Builder on the User object that runs when the active field is changed and the active field is false. Then, 0 hours after the Last Modified Date, invoke the flow created in Cloud Flow Designer.

ProcessBuilder-UpdateUser.GIF

This flow does the following: (1) looks up all contact records where the contact owner is the inactive user, (2) determine if there are records in the collection, (3) if there is a collection, then take each record and do the following: (3a) assign the new user as the contact owner and (3b) add the contact record to the collection to update and finally, outside the loop, update all contact records in the collection.

Note: The update action is performed OUTSIDE the loop, not inside it. Similar to apex, when DML statements (insert, update, delete, undelete) are placed inside a for loop, database operations are invoked once per iteration of the loop making it very easy to reach these governor limits. Instead, move any database operations outside of for loop. The same applies to flow (remember, Salesforce translates flow into code behind the scenes).

UpdateOwnerFlow.GIF

Quick Steps:

1. We are going to create a custom label to hold the Salesforce Id of the user who will be assigned to the contacts when a user is deactivated*. In Classic, Custom Labels is found under Create | Custom Labels. In Lightning, you can find it under User Interface | Custom Labels.

As mentioned above, there are multiple ways to not hardcode Ids in flow. Custom label is just one of the ways. Custom metadata types, performing a record lookup in flow will also do the trick. We just don’t want to hardcode the Id in flow so we can change the Id without having to make a new version of the flow.

CustomLabel-UserId.GIF

2. Let’s create the flow. For those using Salesforce Classic, flow can be found in Create | Workflows & Approvals | Flows. In Lightning Experience, it is found under Process Automation | Flows.

A. Let’s create our flow resources.

Best practice tip: Provide a description so you and other/future admins know what this flow resource is used for.

Create a variable varUserId to store the inactive user’s Id that will be passed from process builder as an input into flow.

UpdateOwnerFlow-varUserId.GIF

This sObject Variable holds the contact record going through the loop.

UpdateOwnerFlow-sObjVarContactItem.GIF

This sObject Collection Variable will hold all the contact records that will be updated to reflect the new contact owner.

UpdateOwnerFlow-sCollectionUpdateContacts.GIF

This sObject Collection Variable holds the contacts that have the deactivated user as the contact owner.

UpdateOwnerFlow-sobjCollectionContacts.GIF

Lastly, we will create a formula resource to call the UserId custom label into the flow.

UpdateOwnerFlow-Formula.GIF

B. First, we need to perform a lookup to pull all the contacts associated with the deactivated user. We are using the Fast Lookup resource because we are pulling a collection of Salesforce records. We are looking up the Contact object where the OwnerId equals the deactivated userId. Once found, we will store the records in the sObject Collection Variable created in the previous step {!sobjCollectionContacts}.” We will store the Id and the OwnerId.

Best practice tip: Provide a description so you and other/future admins know what this flow element is used for.

UpdateOwnerFlow-FastLookup.GIF

C. Next, we will a Decision flow element to determine whether the fast lookup in the previous step yielded in any contacts. The “Found” outcome looks at the items in the {!sobjCollectionContacts} sObject Collection Variable is null false. Two negatives equal a positive, which means the collection has at least one or more contact records.

Best practice tip: Provide a description so you and other/future admins know what this flow element is used for.

UpdateOwnerFlow-Decision.GIF

D. We now need to create a Loop flow element so that we can go through each contact record in the collection. It looks at the sObject Collection Variable {!sobjCollectionContacts} and put each contact record into a loop variable {!sObjVarContactItem}.

UpdateOwnerFlow-Loop.GIF

E. For each loop record, in the Assignment flow element, we will assign the new user as the contact owner. Variable is {!sobjVarContactItem.OwnerId} (i.e. loop record’s ownerId field) equals {!NewUser}.

Best practice tip: Provide a description so you and other/future admins know what this flow is used for.

UpdateOwnerFlow-Assignment1.GIF

F. For each loop record, in the Assignment flow element, we will add the loop contact record to a new collection that will be updated.

Best practice tip: Provide a description so you and other/future admins know what this flow is used for.

UpdateOwnerFlow-Assignment2.GIF

G. Now, we need to create the Fast Update flow element to update all the contacts in the sObject Collection Variable {!sCollectionUpdateContacts}.

Best practice tip: Provide a description so you and other/future admins know what this flow is used for.

UpdateOwnerFlow-FastUpdate.GIF

H. Add the subflow Send Flow Fault Email. For instructions on how to create this, go to Step 2 of blog post: Maximize Maintainability With Process Builder and Componentized Visual Workflow.

UpdateOwnerFlow-SendFlowFault.GIF

I. Set your flow starting point. And connect the flow elements, outcome connector, loop connector and the fault connectors to match the below…

UpdateOwnerFlow-Connectors.GIF

J. Save/Save As and provide the following properties.

Best practice tip: Provide a description so you and other/future admins know what this flow is used for.

UpdateOwnerFlow-Properties.GIF

K. Click the “Close” button.

L. On the flows screen, activate the flow.

UpdateOwnerFlow-Activate.GIF

3. Now, create a new process using process builder. In setup, go to Create | Workflows & Approvals | Process Builder in Salesforce Classic or Process Automation | Process Builder in Lightning Experience.

A. Click on the New button to create our new process. Complete the information below and select “A record changes.”

Best practice tip: Provide a description so you and other/future admins know what this process is used for.

ProcessBuilder-NewProcess.GIF

B. Specify User as the object, when a record is created or edited.

ProcessBuilder-UpdateUser-UserObject.GIF

C. Specify the Criteria Node. This needs to evaluate to true for it to execute. We will call the criteria “Inactive.” In this situation, we are going to use the formula as we cannot specify the IsChanged condition with the Conditions are met specification. The criteria is if the user’s active field is changed AND the user’s active field is false (unchecked).

We have to check the box to Yes under Advanced “Do you want to execute the actions only when specified changes are made to the record.” If this is not checked, you will not see the Scheduled Actions.

ISCHANGED([User].IsActive) &&
[User].IsActive = false

ProcessBuilder-UpdateUser-CriteriaNode.GIF

D. To avoid the mixed DML action on standard and setup objects that would result if you invoke the flow from an immediate action, we will configure a scheduled action to take place “0 hours after the LastModifiedDate.”

ProcessBuilder-UpdateUser-ScheduledAction.GIF

Select Flow as the action, select the flow created in Step 2. Specific the flow variable varUserId and pass in the field reference [User].Id onto the flow.

ProcessBuilder-UpdateUser-InvokeFLow.GIF

E.  Click on the Activate button to activate the process builder.

Now, before you deploy the changes to Production, don’t forget to test your configuration changes.

  1. Make a user the contact owner for several contact records.
  2. Deactivate the user.
  3. Verify that all the contacts owned by the deactivated user have been updated to the new user.

Deployment Notes/Tips:

  • The process builder, flow and the custom label can be deployed to Production in a change set (or can be deployed using a tool such as Dreamfactory’s Snapshot).
  • You will find the process builder and flow in a change set under the Flow Definition component type.
  • Activate the process builders and flow post deployment as they deploy inactive in Production, unless with Winter ’19, you have opted in on the Process Automation Settings screen, to “Deploy processes and flows as active.” NOTE: With this change, in order to successfully deploy a process or flow, your org’s Apex tests must launch at least 75% of the total number of active processes and active autolaunched flows in your org.

 

 

2 thoughts on “Update Inactive User to Active User as Contact Owner

  1. awesome!!!! love to learn flows like this.
    Maybe a stupid question, but what if you want to transfer ownership also from accounts and opportunities from inactive user to a new user?

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s