スケジュール送信バッチ機能

### スケジュール送信バッチ機能のソース例：

```
ソースの概要：
１．組織メールアドレスの取得機能　⇒　getOrgWideEmailAddressId()
２．メールの送信機能　⇒　sendMail()
３．execute()メソッド内、approveConfirmRequest()を呼び出す
    ※条件満たされた場合、該当ユーザに送信機能を呼び出す
    ※メールテンプレートの取得も必要
４．クラスが　implements　Schedulable, Database.Stateful
５．public void execute(SchedulableContext sc)の実装必要
```

```
/**
 * 承認依頼通知メールバッチ処理
 *  
*/
public without Sharing class Y15_ApprovalRequestNotificationBatch implements Database.batchable<sObject>,Schedulable, Database.Stateful {
    
	private Integer executeCount = 0;	// execute がコールされた回数
    private static final String ORG_EMAIL_DISPLAYNAME = '契約更新システム'; //組織メールアドレス
    private static final String MAIL_TEMPLATE_Y15_APPROVAL = 'Y15_NewArrival'; //メールテンプレート：Y15_NewArrival
    private EmailTemplate emailTemplates_Y15_NewArrival;//メールテンプレート
    private Id orgAddressId; //組織のメールアドレス
    
    // 契約更新報告DAO
    private Y15_ContractRenewReportDao crrMRec = new Y15_ContractRenewReportDao('ContractRenewReport_M__c');
    //契約更新報告書IDセット
    private Set<String> ids = new Set<String>();
    //DO・所属部署対応マスタDAO
    DoToDepartmentMDao doToDepartmentMDao = new DoToDepartmentMDao();
        
	 /**
     * バッチ処理開始
     * @param BC バッチコンテキスト
     * @return バッチ処理対象
     */
    public Iterable<sObject> start(Database.BatchableContext BC) {
        //すべての契約更新報告書を取得
        List<ContractRenewReport_M__c> contractRenewReportMList = crrMRec.getRecs();
        System.debug('@@contractRenewReportMList=>:'+contractRenewReportMList);
        
        return contractRenewReportMList;
    }
    
    
    /**
     * バッチ処理実行
     * @param BC バッチコンテキスト
     * @param scope バッチ処理対象
     */
    public void execute(Database.BatchableContext BC, List<ContractRenewReport_M__c> contractRenewReportMList) {
		executeCount++;
        approveConfirmRequest(contractRenewReportMList);	        
    }
    
    /**
     * バッチ処理終了
     * @param BC バッチコンテキスト
     */
    public void finish(Database.BatchableContext BC) {
        
    }
    
    /**
     * 契約更新報告書の6部門の提出日、全体記入状況、全体承認状況より、承認依頼処理を行う
     * @param 契約更新報告書リスト
     * @return なし
     */ 
    @TestVisible
    private void approveConfirmRequest(List<ContractRenewReport_M__c> contractRenewReportList) {
        
        //メールテンプレートを取得
        emailTemplates_Y15_NewArrival = [SELECT Id,Name,Subject, Body, HtmlValue FROM EmailTemplate WHERE developerName = :MAIL_TEMPLATE_Y15_APPROVAL limit 1];
        //組織のメールアドレスを取得
        orgAddressId = getOrgWideEmailAddressId();
        
        //所属コード→ユーザセットのマップ構築
        Map<String, Set<User>> deptCodeToUsersMap = createUsersMap();
        System.debug('所属コード→ユーザセットのマップ deptCodeToUsersMap=>:'+ deptCodeToUsersMap);
        
        //今日の日付
        Date to_day = System.today();
        //昨日の日付
        Date yester_day = to_day.addDays(-1);
        
        //契約更新報告書
        for(ContractRenewReport_M__c contractRenewReport_M : contractRenewReportList){
            
            //法務部GM承認日が前日日付または1週間前以上であること
            //全体記入状況 = 最終提出済  EntryStatus__c
            //全体承認状況 = DM承認済　ApprovalStatus__c
            if(contractRenewReport_M.LegalGMApprovalDate__c == yester_day && contractRenewReport_M.EntryStatus__c == Y15_Const.STATUS_LAST_FILLED && contractRenewReport_M.ApprovalStatus__c == '法務部GM承認済') {
                //送信先ユーザリスト
                List<User> targetUserList = new List<User>();
                   
                //総合渉外部GMを取得 所属コード :957400  職種コード:101 or 102
                Set<User> users= deptCodeToUsersMap.get(Y15_Const.DEP_CODE_957400);
                
                if(users != null && users.size()>0){
                    for(User user : users){
                        if(user.JobCategoryCode__c == Y15_Const.JOB_GM || user.JobCategoryCode__c == Y15_Const.JOB_AGM){
                            targetUserList.add(user);
                        }
                    }
                }

                //総合渉外部GMに承認依頼する
                  sendMail(targetUserList,contractRenewReport_M.Id);
            }            
        }
    }   
    

    /**
    * ユーザ関連マップ構築
    * @param 所属コード→ユーザセットのマップ
    * @return ユーザセットのマップ
    */ 
    @TestVisible
    private Map<String, Set<User>> createUsersMap() {
        // ユーザマスタ検索
        List<User> users = new UserDaoWithoutSharing().getRecsForConRenew();
        
        //所属コード→ユーザセット
        Map<String, Set<User>> deptCodeToUsersMap = new Map<String, Set<User>>();
        for (User user : users) {
            if (!deptCodeToUsersMap.containsKey(user.departmentCode__c)) {
                deptCodeToUsersMap.put(user.departmentCode__c, new Set<User>());
            }
            deptCodeToUsersMap.get(user.departmentCode__c).add(user);
 
        }
        return deptCodeToUsersMap;
    }    
    
    
	/**
	 * メール送信
	 *
	 * @param users 送信対象ユーザリスト
	 * @param targetObjectId メール本文の対象オブジェクトId
	 * @param activityFlag 活動履歴に保存する場合true
	 */
    public void sendMail(List<User> users,ID targetObjectId){
        System.debug('sendMail 処理開始');
        //送信先ユーザIDセット
        Set<Id> toSendUserIds = new Set<Id>();
        System.debug('sendMail　送信先ユーザ=>:' + users);
        
        try{
            if (users == null || users.size()== 0 ) {
                throw new MailException('送信対象のユーザーがいません。');
            }else{
                for(User user : users){
                    toSendUserIds.add(user.Id);
                }
            }
            
            ID templateId =emailTemplates_Y15_NewArrival.Id;
            String templateName = emailTemplates_Y15_NewArrival.Name;
            
            if(templateId == null) {
                throw new MailException('メールテンプレートが登録されていません[' + templateName + ']');
            }
            if (orgAddressId == null) {
                throw new MailException('組織のメールアドレスが登録されていません[' + ORG_EMAIL_DISPLAYNAME + ']');
            }
            Messaging.SingleEmailMessage mailBody = Messaging.renderStoredEmailTemplate(templateId, null,targetObjectId);
            String strSubject = mailBody.getSubject();
            String strPlainTextBody = mailBody.getPlainTextBody();
            System.debug('======================================');
            System.debug(strPlainTextBody);
            System.debug('======================================');
            
            Messaging.Email[] emails = new List<Messaging.Email>();
            for (Id userId: toSendUserIds) {
                Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                mail.setTargetObjectId(userId);
                //mail.setSaveAsActivity(activityFlag); // 活動履歴に保存
                mail.setSaveAsActivity(false); // 活動履歴に保存
                mail.setSubject(strSubject);
                mail.setPlainTextBody(strPlainTextBody);
                mail.setOrgWideEmailAddressId(orgAddressId);
                emails.add(mail);
            }
            
            boolean mailResult = true;
            String mailErr = '';
            for (Messaging.SendEmailResult result: Messaging.sendEmail(emails, false)) {
                if(!result.isSuccess()) {
                    mailResult = false;
                    String msg = result.getErrors()[0].getMessage();
                    if(mailErr.length() > 0) {
                        mailErr += '\n';
                    }
                    mailErr += msg;
                }
            }
            if (!mailResult) {
                throw new MailException('メール送信に失敗しました[' + mailErr + ']');
            }
        } catch (Exception e) {
            e.setMessage('メール送信に失敗しました\n' + e.getMessage());
            throw e;
        }
    }

	/**
	* 組織の共有アドレスを取得します
	* return 取得した組織の共有アドレス
	*/
	private static Id getOrgWideEmailAddressId() {
        
        // 「メール：組織のアドレス」に登録したメールアドレスを取得
        OrgWideEmailAddress sObj =  [SELECT Address,DisplayName FROM OrgWideEmailAddress WHERE DisplayName = :ORG_EMAIL_DISPLAYNAME LIMIT 1];
        
		if (sObj != null) {
			return sObj.Id;
		}
		return null;
	}
    
	/*
	 * スケジュール用メソッド
	 * @param SchedulableContext sc
	 */
	public void execute(SchedulableContext sc) {
		Y15_ApprovalRequestNotificationBatch batch = new Y15_ApprovalRequestNotificationBatch ();
		ID batchProcessId = Database.executeBatch(batch, 200);
	}
       
}
```