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
sfadm sfdevsfadm sfdev 

Avoid Error: MIXED_DML_OPERATION

Hello,

I'm using the following code to unlock the Opportunity record:
public class UnlockRecordDuringApprovalController {
    Opportunity objOpportunity;
    // String objOpp;
    String currentuserId = UserInfo.getUserId();
	
	public UnlockRecordDuringApprovalController(ApexPages.StandardController controller) {
    	objOpportunity = (Opportunity)controller.getRecord();
	}
	
    public void processRecord() {
        Approval.UnlockResult unlockedRersult = Approval.unlock(objOpportunity);
        // Iterate through each returned result
        if (unlockedRersult.isSuccess()) {
            // Operation was successful, so get the ID of the record that was processed
            System.debug('Successfully unlocked opportunity with ID: ' + unlockedRersult.getId());
        }
        else {
            // Operation failed, so get all errors                
            for(Database.Error err : unlockedRersult.getErrors()) {
                System.debug('The following error has occurred.');                    
                System.debug(err.getStatusCode() + ': ' + err.getMessage());
                System.debug('opportunity fields that affected this error: ' + err.getFields());
            }
        }
        System.debug('currentuserId ' + currentuserId);
        List<PermissionSetAssignment> listPermissionSets = [SELECT Id, AssigneeId, PermissionSetId FROM PermissionSetAssignment WHERE AssigneeId = :currentuserId AND PermissionSetId IN (SELECT Id FROM PermissionSet WHERE IsOwnedByProfile = false)];
        delete listPermissionSets;
        PermissionSetAssignment psa = new PermissionSetAssignment(PermissionSetId = '0PS7E000000DBGA', AssigneeId = currentuserId);		
        System.debug('psa ' + psa);
        insert psa;
		System.debug('psa7 ' + psa);
    }   
}
After the Opportunity record is unlocked I want to:
1. store all permission sets assigned to the current user
2. remove all permission sets associated to the currently logged user
3. give to the user specific and custom permission set.
My custom permission set is restricting the current user to edit certain fields in the Opportunity object.
When the approval process finish I need to retrieve the removed permission sets and remove my custom permission set.
I have created my own custom button and when the button is pressed the above code is executed but I receive the following error message:
Delete failed. First exception on row 0 with id 0Pa7E000001PBlTSAW; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): PermissionSetAssignment, original object: EntityLock: [] Error is in expression '{!processRecord}' in component in page unlockopportunityrecord: Class.UnlockRecordDuringApprovalController.processRecord: line 27, column 1
Could you please advise what is the cause of such error message and how it can be avoided?

Thank you

 
Best Answer chosen by sfadm sfdev
karthikeyan perumalkarthikeyan perumal
Hello sfadm, 
Sorry my previous was not tested. now i have Code already i check in my Org.

Kindly use the below code.
 
public class UnlockRecordDuringApprovalController {
 
   static Opportunity objOpportunity; 
     
    
    public UnlockRecordDuringApprovalController(ApexPages.StandardController controller) {
        objOpportunity = (Opportunity)controller.getRecord();
    }
    
    public static void processRecord() {
        String currentuserId = UserInfo.getUserId();
        Approval.UnlockResult unlockedRersult = Approval.unlock(objOpportunity);
        // Iterate through each returned result
        if (unlockedRersult.isSuccess()) {
            // Operation was successful, so get the ID of the record that was processed
            System.debug('Successfully unlocked opportunity with ID: ' + unlockedRersult.getId());
        }
        else {
            // Operation failed, so get all errors                
            for(Database.Error err : unlockedRersult.getErrors()) {
                System.debug('The following error has occurred.');                    
                System.debug(err.getStatusCode() + ': ' + err.getMessage());
                System.debug('opportunity fields that affected this error: ' + err.getFields());
            }
        }
        System.debug('currentuserId ' + currentuserId);
        OppHandler.DeletePErmissionSetAssignemt();    
      
    }   
}

2. Create New Apex class with following code. 
 
Public class OppHandler {
  @future
  public static void DeletePErmissionSetAssignemt() {
     String currentuserId = UserInfo.getUserId();       
     List<PermissionSetAssignment>  listPermissionSets =[SELECT Id, AssigneeId, PermissionSetId FROM PermissionSetAssignment WHERE AssigneeId = :currentuserId AND PermissionSetId IN (SELECT Id FROM PermissionSet WHERE IsOwnedByProfile = false)];
     delete listPermissionSets;      
     PermissionSetAssignment psa = new PermissionSetAssignment(PermissionSetId = '0PS7E000000DBGA', AssigneeId = currentuserId);
       System.debug('psa ' + psa);
        insert psa;
        System.debug('psa7 ' + psa);     
  }
}
Hope it may Help you. Mark Best ANSWEER if its work for you. Otherwise kinldy send your screenshots which way your trying. from the Begin to end.

Thanks
karthik
 

All Answers

karthikeyan perumalkarthikeyan perumal
Hello, 

First Create Class Called " MixedDMLOps" the code Below
 
/**
Handles mixed dml situations in code. It runs the DML operation in different context for Test code only, so that conflict between DML on setup and non-setup object is gone.

PLEASE NOTE:
============
methods are not named as delete, insert because they are reserved words by Apex
*/
public without sharing class MixedDMLOps {
  // DML UPDATE operation 
  public static Database.SaveResult[] up (Sobject[] objs) {
    Database.Saveresult[] updateRes;
     if (Test.isRunningTest()) {
          System.runAs(new User(Id = Userinfo.getUserId())) {
          updateRes = database.update(objs);
          }
        } else {
        updateRes = database.update(objs);
        }  
    return updateRes;
  }
  
  // DML DELETE
  public static Database.DeleteResult[] del (Sobject[] objs) {
    Database.DeleteResult[] delRes;
     if (Test.isRunningTest()) {
          System.runAs(new User(Id = Userinfo.getUserId())) {
          delRes = database.delete(objs);
          }
        } else {
        delRes = database.delete(objs);
        }  
    return delRes;
  }
  
  
  // DML INSERT
  public static Database.Saveresult[] ins (Sobject[] objs) {
    Database.Saveresult[] res;
     if (Test.isRunningTest()) {
          System.runAs(new User(Id = Userinfo.getUserId())) {
          res = database.insert(objs);
          }
        } else {
        res = database.insert(objs);
        }  
    return res;
  }
  
  
}
 Next Step :you have to use @future before the method to avoid mixed DML Opration.  Try to use below code it may solve your problem.
 
public class UnlockRecordDuringApprovalController {
    Opportunity objOpportunity;
    // String objOpp;
    String currentuserId = UserInfo.getUserId();
	
	public UnlockRecordDuringApprovalController(ApexPages.StandardController controller) {
    	objOpportunity = (Opportunity)controller.getRecord();
	}
    @future
    public static void processRecord() {
        Approval.UnlockResult unlockedRersult = Approval.unlock(objOpportunity);
        // Iterate through each returned result
        if (unlockedRersult.isSuccess()) {
            // Operation was successful, so get the ID of the record that was processed
            System.debug('Successfully unlocked opportunity with ID: ' + unlockedRersult.getId());
        }
        else {
            // Operation failed, so get all errors                
            for(Database.Error err : unlockedRersult.getErrors()) {
                System.debug('The following error has occurred.');                    
                System.debug(err.getStatusCode() + ': ' + err.getMessage());
                System.debug('opportunity fields that affected this error: ' + err.getFields());
            }
        }
        System.debug('currentuserId ' + currentuserId);
        List<PermissionSetAssignment> listPermissionSets = [SELECT Id, AssigneeId, PermissionSetId FROM PermissionSetAssignment WHERE AssigneeId = :currentuserId AND PermissionSetId IN (SELECT Id FROM PermissionSet WHERE IsOwnedByProfile = false)];
		
		//MixedDMLOps.del(Object) Make sure your passing Object insted off something.
         MixedDMLOps.del(listPermissionSets);
        PermissionSetAssignment psa = new PermissionSetAssignment(PermissionSetId = '0PS7E000000DBGA', AssigneeId = currentuserId);		
        System.debug('psa ' + psa);
        insert psa;
		System.debug('psa7 ' + psa);
    }   
}

Hope it will help you. 

Mark Best ANSWER if its work for you. 

Thanks
karthik


 
sfadm sfdevsfadm sfdev
Hello,

Thank you for your comment and suggestion.

I've tried to execute what you advised but I still get the followig error message:
Delete failed. First exception on row 0 with id 0Pa7E000001PBlTSAW; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): PermissionSetAssignment, original object: EntityLock: []
Error is in expression '{!processRecord}' in component <apex:page> in page unlockopportunityrecord: Class.MixedDMLOps.del: line 30, column 1
Class.UnlockRecordDuringApprovalController.processRecord: line 29, column 1
I created new public without sharing class MixedDMLOps and modified the implementation of the code in class UnlockRecordDuringApprovalController the way you suggested but still get the same error message.

Could you please advise how to resolve and avoid this error?

Thank you
 
sfadm sfdevsfadm sfdev
Hello,

I've tried and I used for loop(in bold) the way you suggested.

Here is my code:
 
public class UnlockRecordDuringApprovalController {
    public static Opportunity objOpportunity;
    // String objOpp;
    public static String currentuserId = UserInfo.getUserId();
	
	public UnlockRecordDuringApprovalController(ApexPages.StandardController controller) {
    	objOpportunity = (Opportunity)controller.getRecord();
	}
    
    @future
    public static void processRecord() {
        Approval.UnlockResult unlockedRersult = Approval.unlock(objOpportunity);
        // Iterate through each returned result
        if (unlockedRersult.isSuccess()) {
            // Operation was successful, so get the ID of the record that was processed
            System.debug('Successfully unlocked opportunity with ID: ' + unlockedRersult.getId());
        }
        else {
            // Operation failed, so get all errors                
            for(Database.Error err : unlockedRersult.getErrors()) {
                System.debug('The following error has occurred.');                    
                System.debug(err.getStatusCode() + ': ' + err.getMessage());
                System.debug('opportunity fields that affected this error: ' + err.getFields());
            }
        }
        System.debug('currentuserId ' + currentuserId);
        List<PermissionSetAssignment> listPermissionSets = [SELECT Id, AssigneeId, PermissionSetId FROM PermissionSetAssignment WHERE AssigneeId = :currentuserId AND PermissionSetId IN (SELECT Id FROM PermissionSet WHERE IsOwnedByProfile = false)];
        
        /*
        for(PermissionSetAssignment permissionSetAssignment :listPermissionSets) {
            MixedDMLOps.del(new SObject[] {permissionSetAssignment});
        }
        */

        SObject[] psaSObj = new SObject[listPermissionSets.size()];
        System.debug('listPermissionSets.size() ' + listPermissionSets.size());
        for(integer i = 0; i < listPermissionSets.size(); i++) {
            psaSObj[i] = listPermissionSets.get(i);
        }
        MixedDMLOps.del(psaSObj);
        
		 // <b>MixedDMLOps.del(Object) Make sure your passing Object insted off something.</b>
        PermissionSetAssignment psa = new PermissionSetAssignment(PermissionSetId = '0PS7E000000DBGA', AssigneeId = currentuserId);		
        System.debug('psa ' + psa);
        insert psa;
		System.debug('psa7 ' + psa);
    }   
}
but I still receive the following error message:
Delete failed. First exception on row 0 with id 0Pa7E000001PBlTSAW; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): PermissionSetAssignment, original object: EntityLock: []
Error is in expression '{!processRecord}' in component <apex:page> in page unlockopportunityrecord: Class.MixedDMLOps.del: line 30, column 1
Class.UnlockRecordDuringApprovalController.processRecord: line 39, column 1
Could you please explain how to pass SObject to del method provided there are only PermissionSetAssignment objects in processRecord method?

Thank you


 
karthikeyan perumalkarthikeyan perumal
Hello

kinldy use this Code.
 
public class UnlockRecordDuringApprovalController {
    Opportunity objOpportunity;
    // String objOpp;
    String currentuserId = UserInfo.getUserId();
    
    public UnlockRecordDuringApprovalController(ApexPages.StandardController controller) {
        objOpportunity = (Opportunity)controller.getRecord();
    }
    
    public void processRecord() {
        Approval.UnlockResult unlockedRersult = Approval.unlock(objOpportunity);
        // Iterate through each returned result
        if (unlockedRersult.isSuccess()) {
            // Operation was successful, so get the ID of the record that was processed
            System.debug('Successfully unlocked opportunity with ID: ' + unlockedRersult.getId());
        }
        else {
            // Operation failed, so get all errors                
            for(Database.Error err : unlockedRersult.getErrors()) {
                System.debug('The following error has occurred.');                    
                System.debug(err.getStatusCode() + ': ' + err.getMessage());
                System.debug('opportunity fields that affected this error: ' + err.getFields());
            }
        }
        System.debug('currentuserId ' + currentuserId);
        List<PermissionSetAssignment> listPermissionSets = [SELECT Id, AssigneeId, PermissionSetId FROM PermissionSetAssignment WHERE AssigneeId = :currentuserId AND PermissionSetId IN (SELECT Id FROM PermissionSet WHERE IsOwnedByProfile = false)];
        
        System.runAs ( new User(Id = UserInfo.getUserId()) ) {
        
         delete listPermissionSets;
    
            }

       
        PermissionSetAssignment psa = new PermissionSetAssignment(PermissionSetId = '0PS7E000000DBGA', AssigneeId = currentuserId);     
        System.debug('psa ' + psa);
        insert psa;
        System.debug('psa7 ' + psa);
    }   
}

Thanks
Karthik
sfadm sfdevsfadm sfdev
Hello,

Thank you for your comment but using the suggested code do not resovle the issue.

This time I get the following error message:
System.runAs can only be used within a test method:
Error is in expression '{!processRecord}' in component <apex:page> in page unlockopportunityrecord: Class.UnlockRecordDuringApprovalController.processRecord: line 28, column 1
Could you please give an appropriate code example of a test method where the System.runAs should be used and implemented.

Thank you
 
karthikeyan perumalkarthikeyan perumal
Hello sfadm, 
Sorry my previous was not tested. now i have Code already i check in my Org.

Kindly use the below code.
 
public class UnlockRecordDuringApprovalController {
 
   static Opportunity objOpportunity; 
     
    
    public UnlockRecordDuringApprovalController(ApexPages.StandardController controller) {
        objOpportunity = (Opportunity)controller.getRecord();
    }
    
    public static void processRecord() {
        String currentuserId = UserInfo.getUserId();
        Approval.UnlockResult unlockedRersult = Approval.unlock(objOpportunity);
        // Iterate through each returned result
        if (unlockedRersult.isSuccess()) {
            // Operation was successful, so get the ID of the record that was processed
            System.debug('Successfully unlocked opportunity with ID: ' + unlockedRersult.getId());
        }
        else {
            // Operation failed, so get all errors                
            for(Database.Error err : unlockedRersult.getErrors()) {
                System.debug('The following error has occurred.');                    
                System.debug(err.getStatusCode() + ': ' + err.getMessage());
                System.debug('opportunity fields that affected this error: ' + err.getFields());
            }
        }
        System.debug('currentuserId ' + currentuserId);
        OppHandler.DeletePErmissionSetAssignemt();    
      
    }   
}

2. Create New Apex class with following code. 
 
Public class OppHandler {
  @future
  public static void DeletePErmissionSetAssignemt() {
     String currentuserId = UserInfo.getUserId();       
     List<PermissionSetAssignment>  listPermissionSets =[SELECT Id, AssigneeId, PermissionSetId FROM PermissionSetAssignment WHERE AssigneeId = :currentuserId AND PermissionSetId IN (SELECT Id FROM PermissionSet WHERE IsOwnedByProfile = false)];
     delete listPermissionSets;      
     PermissionSetAssignment psa = new PermissionSetAssignment(PermissionSetId = '0PS7E000000DBGA', AssigneeId = currentuserId);
       System.debug('psa ' + psa);
        insert psa;
        System.debug('psa7 ' + psa);     
  }
}
Hope it may Help you. Mark Best ANSWEER if its work for you. Otherwise kinldy send your screenshots which way your trying. from the Begin to end.

Thanks
karthik
 
This was selected as the best answer
sfadm sfdevsfadm sfdev
Hi Karthik,

Thank you for your comment and advise. The code really worked this time.

What I wanted to ask you though is I would like to know how to set the permission set
PermissionSetAssignment psa = new PermissionSetAssignment(PermissionSetId = '0PS7E000000DBGA', AssigneeId = currentuserId);
when the Approval process is over.

The Approval process is actually over when the stage of the current Opportunity is changed to "Proposal".
Opportunity Stage

In order to find out when the Opportunity stage will be changed from "Discovery" to "Proposal" I was thinking of implementing an event listener which will monitor the Opportinity stage. And when the Opportunity stage is changed to "Proposal" the listener will invoke the DeletePErmissionSetAssignemt() method. I know this can be achived in Java but in I'm not fully aware how to implement such an event listener in Salesforce. Could you please advise and give an example of how to implement an event listener in Salesforce? If there is no such way can you suggest an appropriate solution to what I'm trying to achive?

Thank you