項目中,審批操作無處不在。配置審批流時,我們有時候會用到queue,related user設置當前步驟的審批人,審批人可以一個或者多個。當審批人有多個時,郵件中獲取當前記錄的審批人和審批意見就不能隨便的取一個審批人了,有以下方式針對不同的場景可以獲取到當前記錄的最終審批人以及審批意見。
郵件內容使用以下幾種方式實現:
1.代碼里面實現郵件發送
2.email template(text/html/custom)
3.visualforce emailTemplate
對發送郵件方式不清楚的,可以參看:salesforce 零基礎學習(六十七)SingleEmailMessage 那點事
為方便查看效果,設置一下場景:針對Account更新操作,如果Account中Type進行了改變,提交一個更新申請的審批流程,如果審批流程在審批過程中,再次更改Type則提示有審批中的記錄,不允許再次修改。審批通過或者失敗則發送給創建人。郵件內容包括最終審批人以及審批意見。
准備工作
1.在Account上新增兩個字段 Type New用來記錄新更改的Type值,Type更改以后是不直接回寫的,只有審批通過以后才能回寫,Update Status用來記錄審批狀態
2.增加Account上的validation rule,避免已經有修改申請單情況下重復更改Type
3.增加申請單表以及相關的字段
4.增加審批流以及審批人對應的Queue,當Status是Pending Approval時,進入審批流,審批通過或者拒絕更新狀態
5.配置Account以及Main_Info_Update__c的trigger,實現相關的賦值以及自動進入審批流操作
AccountTrigger:實現更新前對關鍵字段賦值以及更新后的創建更新申請數據以及自動提交審批流
1 trigger AccountTrigger on Account(before update,after update) { 2 3 if(Trigger.isBefore) { 4 if(Trigger.isUpdate) { 5 for(Account acc : trigger.new) { 6 String oldType = trigger.oldMap.get(acc.Id).Type; 7 System.debug(LoggingLevel.INFO, '*** acc.Update_Status__c: ' + acc.Update_Status__c); 8 if(acc.Update_Status__c <> 'Pending Approval') { 9 if(acc.Type <> oldType) { 10 //將Account的Type_New__c賦值,Account的值回滾到以前的值 11 acc.Type_New__c = acc.Type; 12 acc.Type = oldType; 13 acc.Update_Status__c = 'Pending Approval'; 14 } 15 } else { 16 //TODO 17 //add error or do something 18 } 19 } 20 } 21 } 22 23 if(Trigger.isAfter) { 24 if(Trigger.isUpdate) { 25 List<Main_Information_Update__c> updateList = new List<Main_Information_Update__c>(); 26 for(Account acc : trigger.new) { 27 if(acc.Type_New__c <> null) { 28 Main_Information_Update__c updateItem = new Main_Information_Update__c(); 29 updateItem.Type__c = acc.Type_New__c; 30 updateItem.Type_Old__c = acc.Type; 31 updateItem.Update_Status__c = 'Pending Approval'; 32 updateItem.Account__c = acc.Id; 33 updateList.add(updateItem); 34 } 35 } 36 37 //插入數據並提交到審批流 38 if(updateList.size() > 0) { 39 insert updateList; 40 List< Approval.ProcessSubmitRequest> requestList = new List< Approval.ProcessSubmitRequest>(); 41 for(Main_Information_Update__c item : updateList) { 42 Approval.ProcessSubmitRequest request = new Approval.ProcessSubmitRequest(); 43 request.setObjectId(item.Id); 44 requestList.add(request); 45 //List<Approval.ProcessResult> requestResultList = Approval.process(requestList,false); 46 47 //TODO 48 //對於提交審批流失敗的數據處理, 49 } 50 Approval.process(requestList); 51 } 52 } 53 } 54 55 }
MainInformationUpdateTrigger:實現審批通過回寫Account以及發送郵件操作
1 trigger MainInformationUpdateTrigger on Main_Information_Update__c (after update) { 2 if(Trigger.isAfter) { 3 if(Trigger.isUpdate) { 4 List<Account> updateAccountList = new List<Account>(); 5 //記錄哪些需要發送郵件 6 Map<Id,Main_Information_Update__c> mainId2ObjMap = new Map<Id,Main_Information_Update__c>(); 7 for(Main_Information_Update__c updateItem : Trigger.new) { 8 if(updateItem.Update_Status__c == 'Approved' || updateItem.Update_Status__c == 'Rejected') { 9 Account acc = new Account(); 10 acc.Id = updateItem.Account__c; 11 acc.Type_New__c = null; 12 //acc.Update_Status__c = updateItem.Update_Status__c; 13 acc.Update_Status__c = null; 14 updateAccountList.add(acc); 15 mainId2ObjMap.put(updateItem.Id, updateItem); 16 } 17 } 18 19 if(updateAccountList.size() > 0) { 20 //TODO 21 //try catch處理 22 update updateAccountList; 23 24 //發送郵件 25 for(Id mainId : mainId2ObjMap.keySet()) { 26 if(mainId2ObjMap.get(mainId).Update_Status__c == 'Approved') { 27 EmailUtil.sendEmail('Approved', mainId2ObjMap.get(mainId)); 28 } else { 29 EmailUtil.sendEmail('Rejected', mainId2ObjMap.get(mainId)); 30 } 31 32 } 33 } 34 } 35 } 36 }
至此准備工作結束,下面是幾種方式尋找審批人
一.在代碼里面處理郵件功能(不使用email template)
email template可以配置,更加利於維護,但是有時需要在email template進行相關的特殊操作,比如某些計算,匯總以及日期格式轉換等操作是email template(text/html/custom)無法搞定的功能,所以有時候需要在代碼里面寫郵件的body部分。通過代碼獲取。通過代碼獲取審批人以及審批意見主要需要ProcessInstance以及ProcessInstanceStep兩個表。具體實現如下:
1 public without sharing class EmailUtil { 2 3 //獲取審批意見,審批人,以及其他簡單信息 4 public static String getAccountUpdateAprovedEmailBody(Main_Information_Update__c updateItem) { 5 String returnBody = ''; 6 7 ProcessInstance processResult = [SELECT Id, 8 (SELECT StepStatus, Comments,ActorId FROM Steps order by createddate desc) 9 FROM ProcessInstance 10 WHERE targetObjectId = :updateItem.Id limit 1]; 11 String actorId = processResult.Steps[0].ActorId; 12 String comments = processResult.Steps[0].comments; 13 User actor = [select Id,Name from User where Id = :actorId limit 1]; 14 returnBody += '申請單編號為 : ' + updateItem.Id + '<br/>'; 15 returnBody += '審批人:' + actor.Name + '<br/>'; 16 returnBody += '審批意見:' + comments; 17 return returnBody; 18 } 19 20 21 public static void sendEmail(String type,Main_Information_Update__c updateItem) { 22 String htmlBody; 23 htmlBody = getAccountUpdateAprovedEmailBody(updateItem); 24 Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage(); 25 email.setSenderDisplayName('System Admin'); 26 email.setHtmlBody(htmlBody); 27 if(type == 'Approved') { 28 email.setSubject('審批通過提示'); 29 } else { 30 email.setSubject('審批失敗提示'); 31 } 32 33 email.setTargetObjectId(updateItem.Account__r.OwnerId);//使用此種方式給org內部User/Contact/Lead發郵件,email limit的count不加1 34 email.setSaveAsActivity(false);//如果設置targetObjectId,則必須設置setSaveAsActivity為false 35 Messaging.sendEmail(new List<Messaging.SingleEmailMessage>{email}); 36 } 37 }
二.使用text/html/custom類型的email template:email template這三種類型可以使用merge field,提供了獲取審批流的相關屬性信息,比如審批人,審批意見,審批狀態等,可以直接獲取到。
在審批流中的final approve配置email alert即可。
三.使用visualforce EmailTemplate方式獲取審批人和審批意見:上面也說到了,有一些情況下,html,text,custom實現不了,這個時候visualforce emailTemplate就可以上了,但是email template中無法使用到Approval相關的merge field,而且沒有controller,這種情況下,可以使用兩種方式進行解決。
1)在email template中使用apex component,通過component的controller方法獲取需要的相關信息。
1.在email template中使用component,component傳遞當前記錄的ID,用來處理獲取審批人和審批意見的邏輯。
1 <messaging:emailTemplate subject="審批通過提示" recipientType="User" relatedToType="Main_Information_Update__c"> 2 <messaging:htmlEmailBody> 3 <p>申請單編號:{!relatedTo.Id}</p> 4 <p><c:approvalResult showComponent="ActorName" objId="{!relatedTo.Id}"/></p> 5 <p><c:approvalResult showComponent="Comments" objId="{!relatedTo.Id}"/></p> 6 </messaging:htmlEmailBody> 7 </messaging:emailTemplate>
2.approvalResult.Component用來顯示UI
1 <apex:component controller="ApprovalResultClr" access="global" allowDML="true"> 2 <apex:attribute name="showComponent" description="approval comments" type="String"/> 3 <apex:attribute name="objId" description="approval comments" type="String" assignTo="{!targetObjId}"/> 4 5 <apex:outputPanel rendered="{!showComponent == 'Comments'}"> 6 審批意見:{!comments} 7 </apex:outputPanel> 8 <apex:outputPanel rendered="{!showComponent == 'ActorName'}"> 9 審批人:{!actorName} 10 </apex:outputPanel> 11 </apex:component>
3.ApprovalResultClr用來獲取審批人和審批意見。使用apex class時應該注意,component中綁定的attribute在后台的變量是沒法使用在controller中的,所以不能再構造函數中使用targetObjId.
1 global without sharing class ApprovalResultClr { 2 3 public String targetObjId{get;set;} 4 5 public String comments{get{ 6 ProcessInstance pi = [SELECT Id, 7 (SELECT StepStatus, Comments,ActorId FROM Steps order by createddate desc) 8 FROM ProcessInstance 9 WHERE targetObjectId = :targetObjId limit 1]; 10 if(pi <> null) { 11 comments = pi.Steps[0].Comments; 12 } 13 return comments; 14 }set;} 15 16 public String actorName{get{ 17 ProcessInstance pi = [SELECT Id, 18 (SELECT StepStatus, Comments,ActorId FROM Steps order by createddate desc) 19 FROM ProcessInstance 20 WHERE targetObjectId = :targetObjId limit 1]; 21 if(pi <> null) { 22 String actorId = pi.Steps[0].ActorId; 23 User actor = [select Id,Name from User where Id = :actorId limit 1]; 24 actorName = actor.Name; 25 } 26 return actorName; 27 }set;} 28 29 }
4.配置在審批流中,使用email template
2)在Main_Information_Update__c增加Approver__c字段以及comments,在after update的trigger中獲取審批人的信息放到相關字段上,然后配置workflow,當approver__c存在值情況下,發送郵件,郵件模板中的審批人使用Approver__c即可,此種方式不在下面體現了,有興趣的可以自行嘗試。
效果展示
1.對客戶類型進行更改
2.保存后生成申請單
3.使用審批隊列中名稱為test1的審批人進行審批
4.發送郵件內容展示
總結:此篇通過一個簡單的審批流的例子來展示出幾種不同的方式獲取審批人審批意見信息的方法,使用email template的text/html/custom最為簡單,如果需求的郵件可以使用這些方式實現,建議使用此種方式,便於維護;如果實現不了情況下,也可以使用visual force template方式,不能獲取到的內容可以內嵌apex:component搞定,如果最終這些都不太好操作情況下,退而求其次在代碼里面寫郵件發送的body。