1

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);
6
  • What have you tried so far? Did you start implementing an inbound email service? Did you start implementing approvals using Apex?
    – rael_kid
    Oct 4 '14 at 13:36
  • @Lex Yes, I implemented inbound email service. But there are many issues with it. In email service code I am checking whether first line of email response contains approve/reject. Also In my email template I have added record name, So I am querying ProcessSteps record from that record name, then I am simply approving/rejecting the record using following code: Oct 4 '14 at 13:52
  • if(firstLine.contains('Approved') || firstLine.contains('approved') || firstLine.contains('Approve') || firstLine.contains('approve') || firstLine.contains('APPROVE') || firstLine.contains('APPROVED')) action = 'Approve'; Approval.ProcessWorkitemRequest req2 = new Approval.ProcessWorkitemRequest(); req2.setAction(action); req2.setWorkitemId(processWorkItemId); Approval.ProcessResult result2 = Approval.process(req2); Oct 4 '14 at 13:54
  • Ankush, you can easily edit your original post by clicking the edit button, then pasting in your code. Select it, then click on the preformatted text icon and it will display as code. I've done that for you this time. Please visit Help center if you need any additional instructions on how to use the features or how this site works. Thank you.
    – crmprogdev
    Oct 4 '14 at 16:25
  • @Lex I have modified my question with my current code of email service. I am facing issue that when record is approved by email service It always says Actual approval 'Ankush Dureja' as 'Ankush Dureja' is context user for email service. My requirement is that the user who is replying by email should be Actual approval. Is there any way to accomplish that? Oct 4 '14 at 16:41
1

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 });
        }

}
5
  • Thanks for reply. I am already using salesforce approval process to assign approval process to queues. And my approval process contains multiple steps with different queues in each step. I am using email service to process approval user response. Oct 4 '14 at 14:24
  • It works very well when we have single step in approval process. But when we have multiple steps and multiple users in approval process. In that case I am not able to find logic which identify approval record which should be approved/rejected based on user response. Also in single step process, when record is approved. It always says Actual approval 'Ankush Dureja' as 'Ankush Dureja' is context user for email service. My requirement is that the user who is replying by email should be Actual approval. Oct 4 '14 at 14:25
  • Sounds like you may need to use the Apex Approval Process API to accomplish what you desire. I'm editing my reply to include a sample email handler that does some processing similar to what I mentioned.
    – crmprogdev
    Oct 4 '14 at 15:54
  • Thanks for helping me in formatting code. But I am facing issue that when record is approved by email service It always says Actual approval 'Ankush Dureja' as 'Ankush Dureja' is context user for email service. My requirement is that the user who is replying by email should be Actual approval. Is there any way to accomplish that? Oct 4 '14 at 16:40
  • To fix that issue, you'll need to access the Process Object to set the OriginalActorId to match the UserId of the approver whose email was the one your class processed the approval with. There are a number of related objects that comprise the Process Object (Process Definition, ProcessInstance, ProcessInstanceHistory, InstanceStep, InstanceNode, InstanceWorkItem & ProcessNode). See the Object Reference for more details.
    – crmprogdev
    Oct 4 '14 at 17:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.