Update of Locked Records works in Future Calls

We have seen an interesting behaviour. When we call a method which tries to update locked records it failes with ENTITY_IS_LOCKED. When we call the same method as a future method it works! By design? An accident we shouldn’t rely on? Anyone else noticed this?

The class is not marked as “with sharing”, the only difference is the @future annotation.

Answer

Make sure the future method is in a class “with sharing” or it will be allowed to perform the update– the default mode of operation for unspecified classes is “without sharing” (with exceptions). Without sharing means the act is performed “as an administrator”, thus bypassing the lock.

Edit #2

A better understanding of the forces at play are mentioned on SFDC: Understanding With Sharing, Without Sharing & Unspecified Sharing Classes.

Edit #1

First, see: Using the with sharing or without sharing Keywords.

Next, you’ll this answer is correct. I shall offer proof in a pattern that can be replicated in about 10 minutes, depending on your Internet connection.

Step 1: Custom Object

Name: Limited

Organization Wide Defaults: Public Read Only

Custom Fields:

Account__c Lookup(Account__c)
Counter__c Number(18, 0)

Triggers:

trigger updateCounter on Limited__c (before insert, before update) {
    for(limited__c record: trigger.new)
        if(record.counter__c == null)
            record.counter__c = 0;
        else
            record.counter__c++;
}

Step 2: Custom Profile

Create a new profile from the Read Only profile. Add “Create Edit Delete” permissions to Account and Limited.

Step 3: Future handler

public class FutureHandler {
    @future
    public static void updateContacts(Set<Id> accountIds) {
        update [select id from limited__c where account__c in :accountIds];
    }
}

Step 4: Accounts

Organization Wide Defaults: Public Read/Write

Triggers:

trigger InvokeFuture on Account (after update) {
    FutureHandler.updateContacts(Trigger.newMap.keySet());
}

Step 5: Set up test data

Create a new account, and add a new “limited to the account. The counter should read 0.

Edit the account, making any change that successfully saves. Look back at the “limited” record, the counter should now read 1.

Step 6: New User

Create a new user with the custom profile from before. Login to the account, find the test data, verify that the user cannot manually edit the “limited” record (click edit, you’ll get insufficient privileges). Edit the account, and go back to the “limited”, the counter should now read 2. Note This occurs because we are “without sharing”, the default model.

Step 7: Update Future handler

As the administrator, update the FutureHandler class:

public with sharing class FutureHandler {
    @future
    public static void updateContacts(Set<Id> accountIds) {
        update [select id from limited__c where account__c in :accountIds];
    }
}

Step 8: Test Again

Log back in to the test user, verify they still cannot edit the “limited”. Now, edit the account. Once saved, check the “limited”. The counter should still be at 2.

Step 9: Verify with debugging

Turn on the Debug Logs for the test user, then log back in as the test user. Try editing the account again. The counter will remain at 2 after this edit. Check the debug logs:

Update failed. First exception on row 0 with id a0J5000000IrBosEAF; first error: INSUFFICIENT_ACCESS_OR_READONLY, insufficient access rights on object id: []   

Q.E.D.

Attribution
Source : Link , Question Author : Marc , Answer Author : Community

Leave a Comment