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
SFDCAdmin73SFDCAdmin73 

Approval Comments from a approval process not using a apex class & trigger?

Is there a way to update a custom field on a the opportunity record with the approval comments from the fina approver?

 
Ketankumar PatelKetankumar Patel
Hi, 
You can't copy approval comments from approval unless you use apex trigger. I've done that already for my org and I can help you with that if you want.
SFDCAdmin73SFDCAdmin73
That would be great. Thank you. It is an Opportunity approval process. What information do I need to get started?

Beth
Ketankumar PatelKetankumar Patel
Create one Long Text Area field (Label = Approver Comment, API Name = Approver_Comment__c) and hidden checkbox field (Label = copy comment, API Name = copy_comment__c) on opportunity.

Please create field update to check copy comment field for final approval and final rejection action. This field would trigger approval comments to copy on opportunity. if you would like to copy existing approval comments then you can also use dataloader and mark this field to true.

Here is the opportunity Trigger code. Modify this code based upon your requirment. 
trigger TriggerApprover on opportunity (before update) {
	
	   if(trigger.isUpdate){
             List<Opportunity> opptyList =  [Select id,
                                                   (Select Id, 
                                                         IsPending, 
                                                         ProcessInstanceId, 
                                                         TargetObjectId, 
                                                         StepStatus, 
                                                         OriginalActorId, 
                                                         ActorId, 
                                                         RemindersSent, 
                                                         Comments, 
                                                         IsDeleted, 
                                                         CreatedDate, 
                                                         CreatedById, 
                                                         SystemModstamp 
                                                    FROM ProcessSteps
                                                ORDER BY CreatedDate DESC) 
                                                    From opportunity
                                                WHERE Id IN : Trigger.new];

             if(opptyList.size() > 0){

               for(Opportunity opp : opptyList){
              
				for(Opportunity opp1 : Trigger.new) {
				  
                         //check copy comment is true
                         if(opp.id == opp1.id && opp1.copy_comment__c) {
 
                           if (opp.ProcessSteps.size() > 0) {
                            
                         opp1.Approver_Comment__c = opp.ProcessSteps[0].Comments;
                         opp1.copy_comment__c = false;
                
                           }

                        }
                 
                    }
               }
             }   
		}  
	}
SFDCAdmin73SFDCAdmin73
Thank you. I have modified the code. I have two conditions in which the approval comments need to be copy. Depending in the conditions I will determine which field the comments should be copied in. See modified code. I need to some how write OR statment between the two conditions. How do I write that?
 
trigger CopyBUApprovalComments on Opportunity (before insert) {
if(trigger.isUpdate){
             List<Opportunity> opptyList =  [Select id,
                                                   (Select Id, 
                                                         IsPending, 
                                                         ProcessInstanceId, 
                                                         TargetObjectId, 
                                                         StepStatus, 
                                                         OriginalActorId, 
                                                         ActorId, 
                                                         RemindersSent, 
                                                         Comments, 
                                                         IsDeleted, 
                                                         CreatedDate, 
                                                         CreatedById, 
                                                         SystemModstamp 
                                                    FROM ProcessSteps
                                                ORDER BY CreatedDate DESC) 
                                                    From opportunity
                                                WHERE Id IN : Trigger.new];

             if(opptyList.size() > 0){

               for(Opportunity opp : opptyList){
              
                for(Opportunity opp1 : Trigger.new) {
                  
                         //check copy comment is true
                         if(opp.id == opp1.id && opp1.copy_comment__c && opp1.EPG__c) {
 
                           if (opp.ProcessSteps.size() > 0) {
                            
                         opp1.EPG_BU_Approval_Comments__c = opp.ProcessSteps[0].Comments;
                         opp1.copy_comment__c = false;
                
                           }

        //check copy comment is true
                         if(opp.id == opp1.id && opp1.copy_comment__c) {
 
                           if (opp.ProcessSteps.size() > 0) {
                            
                         opp1.BU_Approval_Comments__c = opp.ProcessSteps[0].Comments;
                         opp1.copy_comment__c = false;
                

                        }
                 
                    }
               }
             }   
        }  
    }
   }
   }

 
Ketankumar PatelKetankumar Patel
You can use if and else statement block or if you have more conditions you can use if, else if  block.

I hope this would help. 
if (opp.ProcessSteps.size() > 0) {
 
    // check first condition
    // change condition based upon your logic
    if(opp.id == opp1.id && opp1.copy_comment__c && opp1.EPG__c) 
       {
           // if first condition true then process below logic 
           opp1.EPG_BU_Approval_Comments__c = opp.ProcessSteps[0].Comments;
           opp1.copy_comment__c = false;
       }
     //check second condition
     // change condition based upon your logic
     else if(opp.id == opp1.id && opp1.copy_comment__c && opp1.BU__C)
      {
           // if second condition true then process below logic
           opp1.BU_Approval_Comments__c = opp.ProcessSteps[0].Comments;
           opp1.copy_comment__c = false;
      }
}

if you want to write OR condition in if statement then you should use || and for AND condition you should use &&.
if(opp.id == opp1.id && opp1.copy_comment__c || opp1.EPG__c)


 
SFDCAdmin73SFDCAdmin73
I created a test opportunity records and submitted it for approval. Once the record was approved. The Copy Comment field is checked. However the comments are not appearing EPG or BU Approval Comments fields. 

Not sure why. Here is the code I have for the trigger:
 
trigger CopyBUApprovalComments on Opportunity (before insert) {
if(trigger.isUpdate){
             List<Opportunity> opptyList =  [Select id,
                                                   (Select Id, 
                                                         IsPending, 
                                                         ProcessInstanceId, 
                                                         TargetObjectId, 
                                                         StepStatus, 
                                                         OriginalActorId, 
                                                         ActorId, 
                                                         RemindersSent, 
                                                         Comments, 
                                                         IsDeleted, 
                                                         CreatedDate, 
                                                         CreatedById, 
                                                         SystemModstamp 
                                                    FROM ProcessSteps
                                                ORDER BY CreatedDate DESC) 
                                                    From opportunity
                                                WHERE Id IN : Trigger.new];

             if(opptyList.size() > 0){

               for(Opportunity opp : opptyList){
              
                for(Opportunity opp1 : Trigger.new) {
                  
                         // check first condition
                        // change condition based upon your logic
                         if(opp.id == opp1.id && opp1.copy_comment__c && opp1.EPG__c) {
 
                           if (opp.ProcessSteps.size() > 0) {
                         // if first condition true then process below logic 
                         opp1.EPG_BU_Approval_Comments__c = opp.ProcessSteps[0].Comments;
                         opp1.copy_comment__c = false;
                         }
                        //check second condition
                        // change condition based upon your logic
                             else if(opp.id == opp1.id && opp1.copy_comment__c){

                       // if second condition true then process below logic
                       opp1.BU_Approval_Comments__c = opp.ProcessSteps[0].Comments;
                       opp1.copy_comment__c = false;
                          }
                        }

                        //check copy comment is true
                         if(opp.id == opp1.id && opp1.copy_comment__c) {
 
                           if (opp.ProcessSteps.size() > 0) {
                            
                         opp1.BU_Approval_Comments__c = opp.ProcessSteps[0].Comments;
                         opp1.copy_comment__c = false;
                
                        }
                 
                    }
               }
             }   
        }  
    }
   }

 
Forrest MulduneForrest Muldune
Ketankumar Patel,

I used your code above and modified it for my requirements on my requirement for https://developer.salesforce.com/forums/ForumsMain?id=906F00000005K3P 

The only issue is we have 2 approvers for one Matter__c record. when the first approver writes a comment , the comment does not go into the 
Approver_Comment__c (long text ) field in the custom Matter__c object. When the second or last approver writes a comment, it goes into the Approver_Comment__c (long text ) field in the custom Matter__c object.  

This there a way to modify the code below where when both approver writes a comment, it goes into the Approver_Comment__c (long text ) field in the custom Matter__c object? 


trigger TriggerApprover on Matter__c (before update) {
    
       if(trigger.isUpdate){
             List<Matter__c> MatterList =  [Select id,
                                                   (Select Id, 
                                                         IsPending, 
                                                         ProcessInstanceId, 
                                                         TargetObjectId, 
                                                         StepStatus, 
                                                         OriginalActorId, 
                                                         ActorId, 
                                                         RemindersSent, 
                                                         Comments, 
                                                         IsDeleted, 
                                                         CreatedDate, 
                                                         CreatedById, 
                                                         SystemModstamp 
                                                    FROM ProcessSteps
                                                ORDER BY CreatedDate DESC) 
                                                    From Matter__c
                                                WHERE Id IN : Trigger.new];

             if(MatterList.size() > 0){

               for(Matter__c mat : MatterList){
              
                for(Matter__c mat1 : Trigger.new) {
                  
                         //check copy comment is true
                         if(mat.id == mat1.id && mat1.Copy_Comment__c) {
 
                           if (mat.ProcessSteps.size() > 0) {
                            
                         mat1.Approver_Comment__c = mat.ProcessSteps[0].Comments;
                         mat1.copy_comment__c = false;
                
                           }

                        }
                 
                    }
               }
             }   
        }  
    }
Forrest MulduneForrest Muldune
Actually,

When both approvers write a comment, I want all their comments from the Approval process to be combined ( concatenate seperated by a comma, ) into the Approver_Comment__c (long text ) field in the custom Matter__c object.

Thanks
 
Todd KadasTodd Kadas
Any chance you can provide the test class you wrote for this?
Todd KadasTodd Kadas
My approval process is on opportunity.  This worked for me.  
@isTest
private class ApprovalComments_Test{
 static testMethod void testMethodAppCmmt() 
  {
        Account acct = new Account(Name = 'Test Account');
        insert acct;

//Create your pricebook entry
// Instantiate the Pricebook2 record first, setting the Id
Pricebook2 standardPricebook = new Pricebook2(
  Id = Test.getStandardPricebookId(),
    IsActive=true

);

// Run an update DML on the Pricebook2 record
// This enables IsStandard to become true
// on the PricebookEntry record
update standardPricebook;

// Re-Query for the Pricebook2 record, for debugging
standardPricebook = [SELECT IsStandard FROM Pricebook2 WHERE Id = :standardPricebook.Id];

// This should return true now
system.assertEquals(true, standardPricebook.IsStandard, 'The Standard Pricebook should now return IsStandard = true');

// Create the Product
Product2 testProduct = new Product2(
  Name = 'Test Product', 
  IsActive = true
);
insert testProduct;

            PricebookEntry pbe = new PricebookEntry(Pricebook2Id=standardPricebook.Id, Product2Id=testProduct.Id, UnitPrice=99);
    insert pbe;
        
// Create the PricebookEntry
PricebookEntry testPbe = new PricebookEntry(
  Pricebook2Id = '01so0000003MaLt',
  Product2Id = testProduct.Id,
  UnitPrice = 100,
  IsActive = true
);

insert testPbe;

// Re-Query the PBE
testPbe = [SELECT Id, Pricebook2.IsStandard FROM PricebookEntry where Pricebook2Id = '01so0000003MaLt'];

// Should also return true
system.assertEquals(false, testPbe.Pricebook2.IsStandard, 'The Standard Pricebook should return true from the PBE as well.');
        
         List<Opportunity> opps = new List<Opportunity>();
        
      Opportunity testOpportunity = new Opportunity(
            StageName = 'Prospecting',
              CloseDate=System.today().addMonths(1),       
            AccountId = acct.Id,
            OwnerId='0051J000005xUguQAE',
            Name = 'Test Opportunity',
            pricebook2Id = testPbe.Pricebook2Id,
                  Amount=150000.0,
             Service_Line__c='FCL - Standard'
        );
        insert testOpportunity;
        opps.add(testOpportunity);
         system.debug(LoggingLevel.FINE, '<content>');  
        system.debug(opps.size());

       // return acct;
        OpportunityLineItem op=new OpportunityLineItem (quantity=1.0,Opportunityid=opps[0].id,   PricebookEntryId=testPbe.Id, Product2Id = testProduct.Id,
TotalPrice=500000.0 ,CM__c=100.0, UoM__c='KG' ,volume__c=10.0);
      insert op;

          opps =[SELECT AccountId, Amount, Id, Name, CloseDate, StageName, Service_Line__c,(SELECT Quantity, ListPrice,PriceBookEntry.UnitPrice, PricebookEntry.Name,
Estimated_CM__c,CM__c,  UnitPrice FROM OpportunityLineItems) FROM Opportunity where AccountId=: acct.Id ORDER BY CloseDate DESC
         LIMIT 10 ];
         
 User user1 = [SELECT Id FROM User WHERE Alias='aAbou'];
            
        // Create an approval request for the account
        Approval.ProcessSubmitRequest req1 = 
            new Approval.ProcessSubmitRequest();
        req1.setComments('Submitting request for approval.');
        req1.setObjectId(testOpportunity.id);
        
        // Submit on behalf of a specific submitter
        req1.setSubmitterId(user1.Id); 
        
        // Submit the record to specific process and skip the criteria evaluation
        req1.setProcessDefinitionNameOrId('Approval_Test');
        req1.setSkipEntryCriteria(true);
        
        // Submit the approval request for the account
        Approval.ProcessResult result = Approval.process(req1);
        
        // Verify the result
        System.assert(result.isSuccess());
        
        System.assertEquals(
            'Pending', result.getInstanceStatus(), 
            'Instance Status'+result.getInstanceStatus());
        
        // Approve the submitted request
        // First, get the ID of the newly created item
        List<Id> newWorkItemIds = result.getNewWorkitemIds();
        
        // Instantiate the new ProcessWorkitemRequest object and populate it
        Approval.ProcessWorkitemRequest req2 = 
            new Approval.ProcessWorkitemRequest();
        req2.setComments('Approving request.');
        req2.setAction('Approve');
        req2.setNextApproverIds(new Id[] {UserInfo.getUserId()});
        
        // Use the ID from the newly created item to specify the item to be worked
        req2.setWorkitemId(newWorkItemIds.get(0));
        
        // Submit the request for approval
        Approval.ProcessResult result2 =  Approval.process(req2);
        
        // Verify the results
        System.assert(result2.isSuccess(), 'Result Status:'+result2.isSuccess());
        
        System.assertEquals(
            'Approved', result2.getInstanceStatus(), 
            'Instance Status'+result2.getInstanceStatus());
    }
}
KCLKCL
I have 2 UNANIMOUS  approvers for the step, how to store thoser 2 users commnts.