Monthly Archives: December 2014

How to implement edit locking

When 2 users start editing the same record, the first user who saves the record wins, and the other user will lose the changes with an error message similar to “The record you were editing was modified by *someone* during your edit session. Please re-display the record before editing again.”

This can be an inconvenience:  if an integration process updates the records, any user who opened the record for editing will lose his changes if he/she doesn’t hit Save quick enough.

To lock the record when the first user opens it for editing, you can implement the following 4 steps:

  1. Create a date/time field “Last Edit Date Time” – this field will store the date and time when the record was opened for editing.
  2. Create a validation rule to prevent any updates to the record during the period when it is locked (a period of 5 minutes so that no one can keep the record locked indefinitely).
  3. Create a Javascript “Edit” button that updates the “Last Edit Date Time” field and redirects to the record editing page.
  4. Replace the standard Edit button on the page layout with the custom Edit button.

First we create the field (this example uses Accounts):

Screen Shot 2014-12-30 at 6.29.17 PM

Then we create the validation rule as follows:

AND( NOW() – PRIORVALUE( Last_Edit_Date_Time__c ) < ( 5/1440 )
/* locks for 5 minutes */
, PRIORVALUE( LastModifiedById ) != $User.Id )

The first line in this validation rule calculates how long since the last time the record was locked and compares the number to 5/1440 (5 minutes divided by the number of minutes in a day).

If it is less than 5 minutes, then, on the second line, it checks whether the user doing the update is the same user that set the edit lock.

If it is the same user, it will allow the update to go through, but if it is a different user, it shows an error message. I chose the error message to be “The record is locked for editing by another user (5 minutes duration).”

Lastly, we need to create a new Button on Account to run Javascript code that updates the Last_Edit_Date_Time__c and redirects to the the account standard editing page:

Screen Shot 2014-12-30 at 7.20.18 PM

This is the Javascript code:

{!REQUIRESCRIPT("/soap/ajax/29.0/connection.js")}

// prepare account for updating the last edit date time
var anAccount = new sforce.SObject( "Account" );
anAccount.id = '{!Account.Id}';
var dtNow = new Date();
var nowInJSON = dtNow.toJSON();
anAccount.Last_Edit_Date_Time__c = nowInJSON;

// update the account
var result = sforce.connection.update( [ anAccount ] );

window.location.href = '/{!Account.Id}/e';

Now we need to just remove the standard Edit button and place the new custom Edit button on the page layout.

Screen Shot 2014-12-30 at 7.25.41 PM

This makes it so that if an user clicks Edit, it will set the Last Edit Date Time. The validation rule will prevent updates to the record from anyone else during the next 5 minutes. If another user attempts to save the same record, the validation rule will cause this error message to appear:

Screen Shot 2014-12-30 at 6.02.45 PM

 

 

Advertisements

Another trick: how to obtain a set from any field in a list – faster and without loops!

This is like the previous post about getting maps out of a list, but with the same trick applied for getting a set.

In triggers, we usually code a loop like below to collect certain IDs in a set and later use the set in a query.


Set<String> acctIDSet = new Set<String>();
for( Contact aContact : trigger.new ) {
    acctIDSet.add( aContact.AccountID );
}

The new SetCreator class does that without any loops and in a single line of code.

Set<String> acctIDSet =
      SetCreator.createSetFromField( trigger.new, ‘AccountID’ );

Below is the definition of the SetCreator class that allows that.

public class SetCreator {
    public static Set<String> createSetFromField(
        List<SObject> aList, String fieldName ) {

    // get the list in JSON format
    String jsonList = JSON.serialize( aList );

    // copy only the fieldName value using RegEx substitution
    jsonList = jsonList.replaceAll(
        '(\\{.*?"' + fieldName + '":"(.*?)"(,".*?")*\\},?)'
        , ',"$2"' ).replace( '[,', '[' );

    // create set from the modified JSON
    Set<String> extractedSet =
        (Set<String>) JSON.deserialize(
            jsonList, Set<String>.class );

    return extractedSet;
    }

}

It converts the list to a JSON string and modifies the string to look as if it was a serialized Set. It uses RegEx to keep the specified field values and remove everything else. It then deserializes the modified JSON string into a Set.
Again, there are no explicit loops – all of them are internal to the RegEx/JSON implementations, and are much faster.

Even if there are duplicates, they are automatically eliminated during the deserialization of the JSON string.

 

Trick: How to obtain a map indexed by any field (not just ID) faster and without loops!

Often we query an object and need to create a map out of the list of records retrieved. The most straightforward way is to declare a Map<ID, sObject> and initialize it with a list resulting from a SOQL query.

List<Account> acctList = [ SELECT ID, Name, AccountNumber
            FROM Account ];

Map<ID, Account> acctMapByID = new Map<ID, Account>( acctList );

The code shown above will populate the acctMapByID with accounts using their IDs as the key.

However, what if we wanted to create a map indexed by AccountNumber instead? We would have to loop through the list and add the records to the map one by one specifying theAccountNumber as the key.

List<Account> acctList = [ SELECT ID, Name, AccountNumber
            FROM Account ];

Map<String, Account> acctMapByNumber =
            new Map<String, Account>();

for( Account anAccount : acctList ) {
    acctMapByNumber.put( anAccount.AccountNumber, anAccount );
}

Now what if there was a way to do that without any loops and in a single line of code?

Map<String, SObject> acctMapByNumber =
       MapCreator.createMapByIndex( acctList, 'AccountNumber' );

Below is the definition of the MapCreator class that allows that.

public class MapCreator {
    public static Map<String, SObject> createMapByIndex(
        List<SObject> aList, String indexField ) {

    // get the list in JSON format
    String jsonList = JSON.serialize( aList );

    // remove enclosing []
    jsonList = jsonList.substring( 1, jsonList.length() - 1 );

    // copy the indexField value in front of each
    // {} group using RegEx substitution
    // example result: value:{..."indexField":"value"...}
    jsonList = '{' + jsonList.replaceAll(
        '(\\{.*?"' + indexField + '":"(.*?)"(,".*?")*\\},?)'
        , '"$2":$1' ) + '}';

    // create map from the modified JSON
    Map<String, SObject> changedMap =
        (Map<String, SObject>) JSON.deserialize(
            jsonList, Map<String, SObject>.class );

    return changedMap;
    }

}

It first converts the list to a JSON string, then modifies the string to look as if it was a serialized Map. It does that by using RegEx to insert the index in front of each record in the string. It then deserializes the modified JSON string into a map.
It doesn’t use any loops explicitly although there must be loops hidden in the RegEx and JSON method internal implementations, which are much faster.

What would be the advantages of using this class instead of coding the loop?

The MapCreator class uses string replacement/RegEx, which is faster than looping and creating map elements one by one.

According to my tests, it runs faster than looping for lists of roughly 8 records or more. For 63 records, it should be 5 to 6 times faster depending on the size of the records.

It might not be relevant for most of the situations but if you have a long running code that needs to be optimized, it might be worth a try.

PS.: I fixed a problem with the way WordPress displays code that made it convert quotes and double-quotes to special characters. Now the code can be copied and pasted onto Salesforce.