[SalesForce] Approval by email using custom salesforce email service

I am trying to implement approval or rejection by email functionality. But I can not use salesforce standard 'Enable Email Approval Response' functionality as approval process is assigned to queue also and salesforce 'Enable Email Approval Response' functionality does not support email response approval when we use queue in our approval process.

So I am trying to use visualforce email template with email service. Can anyone provide some sample code or some other help in implementation as I am not able to figure out how it can be done.

Thanks in advance.

Here's what's been coded so far.

String firstLine = email.plainTextBody.split('\n')[0];          
if(firstLine.contains('Approved') || firstLine.contains('approved') || firstLine.contains('Approve') || firstLine.contains('approve') || firstLine.contains('APPROVE') || firstLine.contains('APPROVED'))
action = 'Approve';
  else if(firstLine.contains('Rejected') || firstLine.contains('rejected') || firstLine.contains('Reject') || firstLine.contains('reject') || firstLine.contains('REJECT') || firstLine.contains('REJECTED'))
action = 'Reject';

Approval.ProcessWorkitemRequest req2 = new Approval.ProcessWorkitemRequest();
req2.setComments(comments);
req2.setAction(action);
req2.setWorkitemId(processWorkItemId);
Approval.ProcessResult result2 =  Approval.process(req2);

Best Answer

Welcome to SF.SE Ankush. What the custom email service is primarily going to allow you to do is to send all of the emails at once to everyone in the queue, then when the first one is returned, it can determine if it was approved or rejected using custom code. Is that what you want to do? This would be more like a running a class where emails are returned to an inbound email service that processes them. The first reply is the only one that's needed to approve or reject the request and ends the process for that request.

I believe this basic functionality already exists without needing to recreate the wheel. You can create an approval process where only one in the list of approvers is needed to approve or reject. While you don't assign it to a queue per se, you do assign the approval process to those who can approval or reject either by role or as supervisors, user, etc. With this being the case, I'd highly recommend you see if you can recreate the list of users that would be in your queue using this type of built-in functionality in SF first, before trying to create a queue using Apex code running from a class.

Otherwise, it would almost appear as though you'd need to assign the approval to an API user when you did this to send to your class; something I'm not entirely certain SF will allow you to do (I've not attempted to do this before). With that as input to your class, you'd need to process emails to everyone in the queue. I'd recommend using a list stored in custom settings or that can be easily retrieved by running a query on the members of your queue.

When your emails are returned, you'd then process the approval reply back as a response by the API user based on the 1st email response from those who are in the queue. Any further email responses could simply be ignored or responded to as "already processed" according to logic you'd create in your inbound email handler. That would be the general approach to coding this with a custom email service where all replies are handled according to whatever logic you chose to set up in the inbound service.

There are numerous examples of inbound email services available to you is you use the search function here on SF.SE. I believe you'll also find code for using one along with directions for setting it up in the Apex API. Here's a link to Using Apex for Approval Processing which includes an Example.

I have some better example code I could share, but can't seem to locate it at the moment. I'm confident you'll find some better examples by using the search box here on the site.

I found the code I had which uses custom settings to store an email address. You could store a list of addresses if you wanted and pass it to an array, which is not what this class does. You can also code it to process inbound emails. I can't seem to find the code example that I had from Jeff Douglass and Wes Nolte's book. You might find it on Jeff's Blog.

// This class was used to send a custom alert message when a trigger encountered various data related
// errors that it had trapped. It assembled the message in the body, then sent it via the class to the
// primary admins in the org so they could take appropriate actions to correct the problems with the data

global class InboundEmail_Automation_Error_Handler implements Messaging.InboundEmailHandler {

    global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
            Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();

            //  here's where you'd want to add your code to process inbound email replies to your
            // approval request and call a method to reply if you'd already received a response

            // what's below would become part of a method for sending the outbound messages with the
            // approval request and for sending the reply if you'd received a response

            Messaging.SingleEmailMessage outbound = new Messaging.SingleEmailMessage();
            outbound.ToAddresses = new String[]  { email.replyto };
            outbound.setSenderDisplayName('Apex Automation Error Handler');
            outbound.setSubject('Re: ' + email.subject);
            outbound.setHTMLBody('<p> This is an auto-generated Apex reply message.' + 'You wrote: </p><i>'+ email.plainTextBody +'</i>');
            Messaging.sendEmail(new Messaging.SingleEmailMessage[] {outbound });
            return result;
        }   

    global static void send_Error_Emails (String trigname, map<Id,String>mssgmap) {
        PrimAdmin_CustSttng__c Admin1 = PrimAdmin_CustSttng__c.getValues('PrimAdmin1');
        PrimAdmin_CustSttng__c Admin2 = PrimAdmin_CustSttng__c.getValues('PrimAdmin2');

        set<Id>Idset = new set<Id>();
        string subject;
        Idset = mssgmap.keyset();
        if(trigname == 'SendRecapLinksTrigger' || trigname == 'RecapRollupToOppTrigger'){
            subject = 'Automation Errors Handled by trigger '+ trigname +' on RecapForm Id: ';  
        }else if(trigname == 'CreateAssignmentsTrigger'){
            subject = 'Automation Errors Handled by trigger '+ trigname +' on Event Id: ';
        }else if(trigname == 'FATAL_TRIGGER_ERROR'){
            subject = 'FATAL AUTOMATION ERROR HANDLED: '+ trigname +' on Id: ';
        }else {
            subject = 'Automation Errors Handled by trigger '+ trigname +' on Opp Id: ';
        }
        string body2;
        string body = ' ';
        string subject2;
        for(Id Iss: Idset){

            subject2 = subject + Iss + ', '; // Id

            body2 =  body + mssgmap.get(Iss) +'. \n' ;

            subject = subject2;
            body = body2;
        }
        system.debug('subject = '+ subject );
        system.debug('body = '+ body );

        Messaging.SingleEmailMessage outbound = new Messaging.SingleEmailMessage();
        String[] toAddresses = new String[] {string.valueOf(Admin2.Email__c) };
        string[] ccAddresses = new string[] {string.valueOf(Admin1.Email__c)};
        outbound.setSenderDisplayName('Apex Automation Error Handler');
        outbound.setToAddresses(toAddresses);
        outbound.setCcAddresses(ccAddresses);
        outbound.setSubject(subject);
        outbound.setUseSignature(false);
        outbound.setPlainTextBody(body);
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] {outbound });
        }

}
Related Topic