function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Jim Parker 7Jim Parker 7 

Update Encrypted Text Field - Compile Error

I have two fields on an account - SSN_TIN_Key__C and SSN_TIN__C;  The SSN_TIN_Key__C is a cleartext text field which will be used as a unique key and wil lonly be accessible to super users.  SSN_TIN__C is what will be displayed to the user and is an encrpyted text field so  I can mask the field for certain users.  What I want to be able to do is have users be able to update the SSN field they see (eg. SSN_TIN__C) and have that update SSN_TIN_Key__C.  Similarly, if on an upsert, if SSN_TIN_Key__C changes (or is created),  I want to update the field users can see SSN_TIN__C.  I created a trigger as process won't let you do ischanged on an encrpyted field.  However, I get a failure message that says 

common.apex.runtime.impl.DmlExecutionException: Update failed. First exception on row 0 with id XXX; first error: SELF_REFERENCE_FROM_TRIGGER, Object (id = XXX) is currently in trigger RelationshipTrigger, therefore it cannot recursively update itself: []"|0x6c21e43e

What am I doing wrong?
public class RelationshipUtil {
    public static void SSNKeyChanged (List<Account> incomingAcct) {
        try {
                System.debug('Got to SSNKeyChanged entered');
                Set<Id> st_ParentId = new Set<Id>();
                for(Account acctsEvaled : incomingAcct)
                {
                   st_ParentId.add(acctsEvaled.Id);
                }
				Map<Id, Account> relatedAcctMap;
            	if(st_ParentId!=null && !st_ParentId.isEmpty())
               	{	
                	relatedAcctMap = new Map<Id, Account>([SELECT Id, SSN_TIN__c, SSN_TIN_Key__c 
													FROM Account
													WHERE Id IN :st_ParentId limit 1]); 
                    System.debug('Got to SSNKeyChanged lookup');
               	}		 
				for (Account acctsEvaled : incomingAcct) 
            	{
                    System.debug('Got to SSNKeyChanged foor loop');
              		if(relatedAcctMap!=null && relatedAcctMap.containsKey(acctsEvaled.Id))
                	{
                        //Updates the displayed SSN based upon the new Key
                         System.debug('Got to SSNKeyChanged entered for if');
                        relatedAcctMap.get(acctsEvaled.Id).SSN_TIN__c = relatedAcctMap.get(acctsEvaled.Id).SSN_TIN_Key__c;
                         System.debug('Got to SSNKeyChanged set');
				    }
             	}
           		if(relatedAcctMap!=null && relatedAcctMap.values()!=null)
                {
			   		update relatedAcctMap.values();
                    System.debug('Got to SSNKeyChanged update');
                }
        
        } catch (Exception e) {
            System.debug('Issue with SSNKeyChanged: ' + e.getMessage());
        }
    }
    
    
        public static void SSNDisplayChanged (List<Account> incomingAcct) {
        try {
                Set<Id> st_ParentId = new Set<Id>();
                for(Account acctsEvaled : incomingAcct)
                {
                   st_ParentId.add(acctsEvaled.Id);
                }
				Map<Id, Account> relatedAcctMap;
            	if(st_ParentId!=null && !st_ParentId.isEmpty())
               	{	
                	relatedAcctMap = new Map<Id, Account>([SELECT Id, SSN_TIN__c, SSN_TIN_Key__c 
													FROM Account
													WHERE Id IN :st_ParentId limit 1]); 
               	}		 
				for (Account acctsEvaled : incomingAcct) 
            	{
              		if(relatedAcctMap!=null && relatedAcctMap.containsKey(acctsEvaled.Id))
                	{
                        //Updates the Key with the new values input by the user
                	     relatedAcctMap.get(acctsEvaled.Id).SSN_TIN_Key__c = relatedAcctMap.get(acctsEvaled.Id).SSN_TIN__c;
                        System.debug('Got to SSNDisplayChanged set');
				    }
             	}
           		if(relatedAcctMap!=null && relatedAcctMap.values()!=null)
                {
                    System.debug('Got to SSNDisplayChanged update');
			   		update relatedAcctMap.values();
                }
        
        } catch (Exception e) {
            System.debug('Issue with SSNDisplayChanged: ' + e.getMessage());
        }
    }
    
}


trigger RelationshipTrigger on Account (before insert, before update) {
        System.debug('Start Relationship Before Trigger');  
    List<Account> changedSSNKey = new List<Account>();
    List<Account> changedSSNDisplay = new List<Account>();
	
	for (Account acct : Trigger.new) {
  
        Account oldAcct = Trigger.oldMap.get(acct.Id);
        String oldSSNDisplay = oldAcct.SSN_TIN__c;
        String oldSSNKey = oldAcct.SSN_TIN_Key__c;
        System.debug('Old SSN_TIN__C = ' + oldSSNDisplay);
        System.debug('Old SSN_TIN_Key__C = ' + oldSSNKey);        
        
        //Checks to see if the displayed TIN changed (eg. user put in new value)
        if(oldSSNDisplay != acct.SSN_TIN__c)
        {
			changedSSNDisplay.add(acct); 
                //If Displayed SSN changed, then need to update the unique key
			if (changedSSNDisplay.isEmpty() == false)
				RelationshipUtil.SSNDisplayChanged(changedSSNDisplay);
        }
        
        //Checks to see if unique key changed
        if(oldSSNKey != acct.SSN_TIN_KEY__C)
        {
            changedSSNKey.add(acct);
           //If SSN Key changed, then need to updated the displayed value
			if (changedSSNKey.isEmpty() == false)    
    			RelationshipUtil.SSNKeyChanged(changedSSNKey);
        }
	}

    

}

 
Best Answer chosen by Jim Parker 7
Amit Singh 1Amit Singh 1
Jim,

Your trigger is on Before Insert and Update Event and in before event we can not update the same records in which trigger is executing.

What you need to do is remove the DML operations and only assign the values to the records it will automaticaly update the records with the latest values

OR

Change the events from before to After.

OR
Use below code for trigger and class.
Heloper Class
public class preventRecursiveTrigger{
	public static Boolean runOnce = false;
}
Main CLass.
public class RelationshipUtil {
    public static void SSNKeyChanged (List<Account> incomingAcct) {
        try {
                System.debug('Got to SSNKeyChanged entered');
				If(preventRecursiveTrigger.runOnce)return;
                Set<Id> st_ParentId = new Set<Id>();
                for(Account acctsEvaled : incomingAcct)
                {
                   st_ParentId.add(acctsEvaled.Id);
                }
				Map<Id, Account> relatedAcctMap;
            	if(st_ParentId!=null && !st_ParentId.isEmpty())
               	{	
                	relatedAcctMap = new Map<Id, Account>([SELECT Id, SSN_TIN__c, SSN_TIN_Key__c 
													FROM Account
													WHERE Id IN :st_ParentId limit 1]); 
                    System.debug('Got to SSNKeyChanged lookup');
               	}		 
				for (Account acctsEvaled : incomingAcct) 
            	{
                    System.debug('Got to SSNKeyChanged foor loop');
              		if(relatedAcctMap!=null && relatedAcctMap.containsKey(acctsEvaled.Id))
                	{
                        //Updates the displayed SSN based upon the new Key
                         System.debug('Got to SSNKeyChanged entered for if');
                        relatedAcctMap.get(acctsEvaled.Id).SSN_TIN__c = relatedAcctMap.get(acctsEvaled.Id).SSN_TIN_Key__c;
                         System.debug('Got to SSNKeyChanged set');
				    }
             	}
           		if(relatedAcctMap!=null && relatedAcctMap.values()!=null)
                {
				    preventRecursiveTrigger.runOnce = true;
			   		update relatedAcctMap.values();
                    System.debug('Got to SSNKeyChanged update');
                }
        
        } catch (Exception e) {
            System.debug('Issue with SSNKeyChanged: ' + e.getMessage());
        }
    }
    
    
        public static void SSNDisplayChanged (List<Account> incomingAcct) {
        try {
		        If(preventRecursiveTrigger.runOnce)return;
                Set<Id> st_ParentId = new Set<Id>();
                for(Account acctsEvaled : incomingAcct)
                {
                   st_ParentId.add(acctsEvaled.Id);
                }
				Map<Id, Account> relatedAcctMap;
            	if(st_ParentId!=null && !st_ParentId.isEmpty())
               	{	
                	relatedAcctMap = new Map<Id, Account>([SELECT Id, SSN_TIN__c, SSN_TIN_Key__c 
													FROM Account
													WHERE Id IN :st_ParentId limit 1]); 
               	}		 
				for (Account acctsEvaled : incomingAcct) 
            	{
              		if(relatedAcctMap!=null && relatedAcctMap.containsKey(acctsEvaled.Id))
                	{
                        //Updates the Key with the new values input by the user
                	     relatedAcctMap.get(acctsEvaled.Id).SSN_TIN_Key__c = relatedAcctMap.get(acctsEvaled.Id).SSN_TIN__c;
                        System.debug('Got to SSNDisplayChanged set');
				    }
             	}
           		if(relatedAcctMap!=null && relatedAcctMap.values()!=null)
                {
                    System.debug('Got to SSNDisplayChanged update');
					preventRecursiveTrigger.runOnce = true;
			   		update relatedAcctMap.values();
                }
        
        } catch (Exception e) {
            System.debug('Issue with SSNDisplayChanged: ' + e.getMessage());
        }
    }
    
}

Trigger
trigger RelationshipTrigger on Account (after insert, after update) {
        System.debug('Start Relationship Before Trigger');  
    List<Account> changedSSNKey = new List<Account>();
    List<Account> changedSSNDisplay = new List<Account>();
	
	for (Account acct : Trigger.new) {
  
        Account oldAcct = Trigger.oldMap.get(acct.Id);
        String oldSSNDisplay = oldAcct.SSN_TIN__c;
        String oldSSNKey = oldAcct.SSN_TIN_Key__c;
        System.debug('Old SSN_TIN__C = ' + oldSSNDisplay);
        System.debug('Old SSN_TIN_Key__C = ' + oldSSNKey);        
        
        //Checks to see if the displayed TIN changed (eg. user put in new value)
        if(oldSSNDisplay != acct.SSN_TIN__c)
        {
			changedSSNDisplay.add(acct); 
                //If Displayed SSN changed, then need to update the unique key
			if (changedSSNDisplay.isEmpty() == false)
				RelationshipUtil.SSNDisplayChanged(changedSSNDisplay);
        }
        
        //Checks to see if unique key changed
        if(oldSSNKey != acct.SSN_TIN_KEY__C)
        {
            changedSSNKey.add(acct);
           //If SSN Key changed, then need to updated the displayed value
			if (changedSSNKey.isEmpty() == false)    
    			RelationshipUtil.SSNKeyChanged(changedSSNKey);
        }
	}

    

}
Also keep the below points about Encrypted fields.
Encrypted Custom Fields are a new field type (released after winter 08) that allows users to store sensitive data in encrypted form and apply a mask when the data is displayed (e.g., Credit Card Number: XXX-XXX-XX-1234).
Some important points :
User profiles who have the “View Encrypted Data” configuration enabled will be able to view the field normally.
Users who do not have the “View Encrypted Data” profile will see the mask.
User profiles that have the “Modify All Data” permission will not be able to see the value of encrypted data fields.
The field length is restricted to 175 characters in size.
Encrypted Field cannot be type cast as Unique or External ID.
An encrypted field cannot be configured with a default value.
You can’t use encrypted fields in report filters and list views.
You can’t use the encrypted fields in SOQL “where/order” clauses.
Also we can not use encrypted field formula fields, workflow rules, workflow field updates, approval process entry criteria, and approval step criteria.
If you clone a record that has encrypted custom fields, Salesforce will copy the data from the field ONLY if the user has the “view encrypted data” permission.
You can access the data of encrypted field in apex, i.e value is always unmasked.

Let me know the outcomes.

Thanks!
Amit Singh

 

All Answers

Amit Singh 1Amit Singh 1
Jim,

Your trigger is on Before Insert and Update Event and in before event we can not update the same records in which trigger is executing.

What you need to do is remove the DML operations and only assign the values to the records it will automaticaly update the records with the latest values

OR

Change the events from before to After.

OR
Use below code for trigger and class.
Heloper Class
public class preventRecursiveTrigger{
	public static Boolean runOnce = false;
}
Main CLass.
public class RelationshipUtil {
    public static void SSNKeyChanged (List<Account> incomingAcct) {
        try {
                System.debug('Got to SSNKeyChanged entered');
				If(preventRecursiveTrigger.runOnce)return;
                Set<Id> st_ParentId = new Set<Id>();
                for(Account acctsEvaled : incomingAcct)
                {
                   st_ParentId.add(acctsEvaled.Id);
                }
				Map<Id, Account> relatedAcctMap;
            	if(st_ParentId!=null && !st_ParentId.isEmpty())
               	{	
                	relatedAcctMap = new Map<Id, Account>([SELECT Id, SSN_TIN__c, SSN_TIN_Key__c 
													FROM Account
													WHERE Id IN :st_ParentId limit 1]); 
                    System.debug('Got to SSNKeyChanged lookup');
               	}		 
				for (Account acctsEvaled : incomingAcct) 
            	{
                    System.debug('Got to SSNKeyChanged foor loop');
              		if(relatedAcctMap!=null && relatedAcctMap.containsKey(acctsEvaled.Id))
                	{
                        //Updates the displayed SSN based upon the new Key
                         System.debug('Got to SSNKeyChanged entered for if');
                        relatedAcctMap.get(acctsEvaled.Id).SSN_TIN__c = relatedAcctMap.get(acctsEvaled.Id).SSN_TIN_Key__c;
                         System.debug('Got to SSNKeyChanged set');
				    }
             	}
           		if(relatedAcctMap!=null && relatedAcctMap.values()!=null)
                {
				    preventRecursiveTrigger.runOnce = true;
			   		update relatedAcctMap.values();
                    System.debug('Got to SSNKeyChanged update');
                }
        
        } catch (Exception e) {
            System.debug('Issue with SSNKeyChanged: ' + e.getMessage());
        }
    }
    
    
        public static void SSNDisplayChanged (List<Account> incomingAcct) {
        try {
		        If(preventRecursiveTrigger.runOnce)return;
                Set<Id> st_ParentId = new Set<Id>();
                for(Account acctsEvaled : incomingAcct)
                {
                   st_ParentId.add(acctsEvaled.Id);
                }
				Map<Id, Account> relatedAcctMap;
            	if(st_ParentId!=null && !st_ParentId.isEmpty())
               	{	
                	relatedAcctMap = new Map<Id, Account>([SELECT Id, SSN_TIN__c, SSN_TIN_Key__c 
													FROM Account
													WHERE Id IN :st_ParentId limit 1]); 
               	}		 
				for (Account acctsEvaled : incomingAcct) 
            	{
              		if(relatedAcctMap!=null && relatedAcctMap.containsKey(acctsEvaled.Id))
                	{
                        //Updates the Key with the new values input by the user
                	     relatedAcctMap.get(acctsEvaled.Id).SSN_TIN_Key__c = relatedAcctMap.get(acctsEvaled.Id).SSN_TIN__c;
                        System.debug('Got to SSNDisplayChanged set');
				    }
             	}
           		if(relatedAcctMap!=null && relatedAcctMap.values()!=null)
                {
                    System.debug('Got to SSNDisplayChanged update');
					preventRecursiveTrigger.runOnce = true;
			   		update relatedAcctMap.values();
                }
        
        } catch (Exception e) {
            System.debug('Issue with SSNDisplayChanged: ' + e.getMessage());
        }
    }
    
}

Trigger
trigger RelationshipTrigger on Account (after insert, after update) {
        System.debug('Start Relationship Before Trigger');  
    List<Account> changedSSNKey = new List<Account>();
    List<Account> changedSSNDisplay = new List<Account>();
	
	for (Account acct : Trigger.new) {
  
        Account oldAcct = Trigger.oldMap.get(acct.Id);
        String oldSSNDisplay = oldAcct.SSN_TIN__c;
        String oldSSNKey = oldAcct.SSN_TIN_Key__c;
        System.debug('Old SSN_TIN__C = ' + oldSSNDisplay);
        System.debug('Old SSN_TIN_Key__C = ' + oldSSNKey);        
        
        //Checks to see if the displayed TIN changed (eg. user put in new value)
        if(oldSSNDisplay != acct.SSN_TIN__c)
        {
			changedSSNDisplay.add(acct); 
                //If Displayed SSN changed, then need to update the unique key
			if (changedSSNDisplay.isEmpty() == false)
				RelationshipUtil.SSNDisplayChanged(changedSSNDisplay);
        }
        
        //Checks to see if unique key changed
        if(oldSSNKey != acct.SSN_TIN_KEY__C)
        {
            changedSSNKey.add(acct);
           //If SSN Key changed, then need to updated the displayed value
			if (changedSSNKey.isEmpty() == false)    
    			RelationshipUtil.SSNKeyChanged(changedSSNKey);
        }
	}

    

}
Also keep the below points about Encrypted fields.
Encrypted Custom Fields are a new field type (released after winter 08) that allows users to store sensitive data in encrypted form and apply a mask when the data is displayed (e.g., Credit Card Number: XXX-XXX-XX-1234).
Some important points :
User profiles who have the “View Encrypted Data” configuration enabled will be able to view the field normally.
Users who do not have the “View Encrypted Data” profile will see the mask.
User profiles that have the “Modify All Data” permission will not be able to see the value of encrypted data fields.
The field length is restricted to 175 characters in size.
Encrypted Field cannot be type cast as Unique or External ID.
An encrypted field cannot be configured with a default value.
You can’t use encrypted fields in report filters and list views.
You can’t use the encrypted fields in SOQL “where/order” clauses.
Also we can not use encrypted field formula fields, workflow rules, workflow field updates, approval process entry criteria, and approval step criteria.
If you clone a record that has encrypted custom fields, Salesforce will copy the data from the field ONLY if the user has the “view encrypted data” permission.
You can access the data of encrypted field in apex, i.e value is always unmasked.

Let me know the outcomes.

Thanks!
Amit Singh

 
This was selected as the best answer
Jim Parker 7Jim Parker 7
Switching from before to after fixed it.  Thanks!