[SalesForce] Methods to Check if Sobjects are Equal

Use Case:

Need to allow changes to only one specific field on an opportunity when at a certain stage.

Problem:

How to efficiently determine within a trigger context that a only the specific field has changed and no other values have changed.

Info:

Salesforce's documentation on Sobject equality states that Sobject1 == Sobject2 will perform a field by field comparison of the two sobjects. Though my testing seems to indicate that Salesforce's is performing this field by field comparison with more fields than is returned by performing a metadata describe. As when I perform a metadata describe to get the Sobjects set of fields and iterate said set comparing the two Sobjects values I get a result indicating the two Sobjects are equal. Yet when I do the check of Sobject1 == Sobject2 it comes back false.

Questions:

Does a trigger context Sobject(as in the Sobjects in trigger.new) contain extra values that would cause an equality check to fail despite all metadata describe visible fields being equal?

Conversely does a Sobject clone contain extra values that would cause an equality check to fail despite all metadata describe visible fields being equal?

Why would the following not result as being equal assuming only the Opportunity Type field has changed?

Opportunity oldOppClone = trigger.old[0].clone(true,true,true,true);

oldOppClone.Type = trigger.new[0].Type;

system.assert(trigger.new[0]==oldOppClone);

Is there any other way to facilitate obtaining a set of fields which have changed between two Sobjects than metadata describing and iterating the fields and checking each individually?

Best Answer

The Trigger.old and Trigger.new values will always differ; even after running SObject.clone(false, false, false, false), which clears out autonumber fields, and the four audit fields, the SystemModStamp will survive the cloning process, meaning that a simple equality check (oldRecord == newRecord) will fail. A better idea would be to set up a Field Set, and check just the values in the field set against each other:

for(Integer index = 0, size = Trigger.new.size(); index < size; index++) {
  Opportunity oldRecord = Trigger.old[index], newRecord = Trigger.new[index];
  for(FieldSetMember m: Schema.Opportunity.fieldSets.someFieldSet.getFields()) {
    if(oldRecord.get(m.getFieldPath()) != newRecord.get(m.getFieldPath()) {
      // Something is different here
Related Topic