Apexでメールを送信する

## Apexでのメール送信の流れ
1. 単一メールクラスまたは一括メールクラスのインスタンスを作成する。
2. 送信先、件名、本文などを設定する。
3. MessagingクラスのsendEmailメソッドを使用してメールを送信する。

Apexのトランザクションがコミットされるまでメールは送信されません。

## 単一メールと一括メールの違い
単一メールクラスを使用したとしても、複数のメールを一度に送信することができます。
単一メールクラスでメールテンプレートを使用する場合、差し込まれるオブジェクト・送信先を個別に指定する必要があります。
一括メールクラスを使用すると、メールテンプレートで差し込まれるオブジェクト・送信先をリストで指定することができます。
その他、下記のような違いがあります。

* 単一メール
    * 件名・本文は、メールテンプレートの他、個別に指定できます。
    * メールテンプレートにテキスト、HTML、Visualforceが使用できます。
    * 送信先をオブジェクトIDで指定できる他、StringでEmailアドレスを指定できます。
    * 送信元に組織のメールアドレスを使用できます。
* 一括メール送信
    * 件名・本文はメールテンプレートで指定します。
    * Visualforceメールテンプレートを使用することができません。
    * 送信先はオブジェクトIDで指定します。
    * 送信元はApex実行者となります。

## 単一メール(SingleEmailMessage)でメールを送信する
### ① メールテンプレートを使用せずにメールを送信する
* `setTargetObjectId`で取引先責任者、リード、ユーザを送信先に設定することができます。ユーザを設定した場合は`msgObj.setSaveAsActivity(false);`を設定する必要があります。指定したユーザが無効ユーザでもメールが送信されます。
* 送信メールを活動レコードに保存する場合は、`setTargetObjectId(recordId)`で取引先責任者ID、リードIDを指定します。

```java
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

mail.setToAddresses(toAddresses); // 送信先(String[])
mail.setCcAddresses(ccAddresses); // CC送信先(String[])
mail.setBccAddresses(bccAddresses); // BCC送信先(String[])

mail.setSubject(subject); // 件名(String)
mail.setPlainTextBody(body); // 本文(String)
mail.setEntityAttachments(ids); // 添付ファイル(Document、ContentVersion、AttachmentのIDのリスト)

mail.setOrgWideEmailAddressId(emailAddressId); // 送信元に設定する組織のアドレスID(設定しない場合はApexの実行者が設定される)
// mail.setSenderDisplayName(displayName); // 送信者表示名。組織のメールアドレス使用時は設定不可。
mail.setUseSignature(false); // 著名の有無。デフォルトはtrue。
mail.setBccSender(true); // 送信者をBCCに追加
mail.setReplyTo(replyAddress); // 返信先メールアドレス(String)

Messaging.sendEmail(new List<Messaging.SingleEmailMessage>{mail});
```

### ② メールテンプレートを使用してメールを送信する
```java
// whoId: メール送信先オブジェクト
// whatId: メールテンプレート差し込み用オブジェクト
Messaging.SingleEmailMessage mail= Messaging.renderStoredEmailTemplate(templateId, whoId, whatId);
mail.setSaveAsActivity(false); // 送信メールを活動に保存する。デフォルトはtrue。
Messaging.sendEmail(new List<Messaging.SingleEmailMessage>{mail});
```

## 一括メール(MassEmailMessage)でメールを送信する
* `targetObjectIds`(送信先)に指定できるオブジェクトは取引先責任者、リード、ユーザのみです。
* `targetObjectIds`(送信先)のIDはすべて同じオブジェクトのレコードIDである必要があります。
* `whatIds`(差し込み用オブジェクト)を指定できるのは`targetObjectIds`(送信先)に取引先責任者のIDのリストを設定したときのみです。
* `whatIds`に指定できるオブジェクトは取引先責任者、ケース、商談、商品のみです。
* `whatIds`を指定する場合は、`targetObjectId`ごとに1つずつ指定します。

```java
Messaging.MassEmailMessage mail = new Messaging.MassEmailMessage();

mail.setTemplateId(templateId); // メールテンプレートId
mail.setTargetObjectIds(targetObjectIds); // 送信先(取引先責任者、リード、ユーザのIDのリスト)。
mail.setWhatIds(whatIds); // メールテンプレート差し込み用オブジェクト

mail.setReplyTo(replyAddress); // 返信先メールアドレス(String)
mail.setSenderDisplayName(displayName); // 送信者表示名。
mail.setSaveAsActivity(false); // 送信メールを活動に保存する。デフォルトはtrue。
mail.setUseSignature(false); // 著名の有無。デフォルトはtrue。

Massaging.SendEmail(new List<Messaging.MassEmailMessage>{mail});
```


## テスト
メールが送信されるかを検証する場合、`System.assertEquals(1, Limits.getEmailInvocations());`を使用することができます。
ただし、`Limits.getEmailInvocations`は`Test.startTest()`と同時に使用できないため、非同期処理のメール送信を検証することはできません。
非同期の処理とメール送信の処理は個別にテストする必要があります。

```java:テスト
@isTest static void sendEmailTest() {
    EmailService.sendEmail('テスト'); // メール送信を行う同期処理

    System.assertEquals(1, Limits.getEmailInvocations());
}

@isTest static void futureTest() {
    Test.startTest();
    EmailService.sendEmailFuture('テスト');
    Test.stopTest();

    System.assertEquals(1, Limits.getFutureCalls());
}
```


## メール送信の制限
* トランザクションあたり`sendEmail`を10回まで使用できます。
* 単一メールは1日に最大5,000個の外部メールアドレスに送信できます。
    * アドレスが一意であるかどうかは考慮されません。
    * 組織の内部ユーザ(ポータルユーザを含む)に送信できるメールには制限はありません。
* 一括メールは1日に最大5,000この外部メールアドレスに送信できます。
    * アドレスが一意であるかどうかは考慮されません。
    * 組織の内部ユーザ(ポータルユーザを含む)に送信できるメールには制限はありません。


> グリニッジ標準時 (GMT) に基づき、各組織は 1 日に最大 5,000 個の外部メールアドレスに単一メールを送信できます。Spring '19 よりも前に作成された組織では、この日次制限は、Apex と Salesforce API (REST API を除く) を介して送信されたメールに対してのみ適用されます。Spring '19 以降に作成された組織では、この日次制限はメールアラート、単純なメールアクション、フロー内の [メールを送信] アクション、および REST API にも適用されます。組織がこの制限に達したため新しくカウントされたいずれかのメールを送信できない場合、通知のメールがユーザに送信され、エントリがデバッグログに追加されます。Salesforce の Email Author やコンポーザを使用して送信した単一メールは、この制限に含まれません。取引先、取引先責任者、リード、商談、ケース、キャンペーン、カスタムオブジェクトの各ページから、組織の取引先責任者、リード、個人取引先、ユーザに単一メールを直接送信する場合は、制限はありません。
単一メールを送信する場合は、次の点に注意してください。
各 SingleEmailMessage では、To、CC、および BCC の各項目全体で最大 150 名の受信者を指定できます。また、各項目は、4,000 バイトに制限されています。
SingleEmailMessage を使用して組織の内部ユーザにメールを送信するときに setTargetObjectId でユーザ ID を指定すると、メールが 1 日あたりの制限値にカウントされません。ただし、setToAddresses で内部ユーザのメールアドレスを指定すると、制限値にカウントされます。

## メールテンプレート
* `{!User.xxx}`はApex実行ユーザが設定されます。受信ユーザを設定する場合は`{!Receiving_User.xxx} `を使います。

## エラー (例外)
### ■ INVALID_SAVE_AS_ACTIVITY_FLAG
「INVALID_SAVE_AS_ACTIVITY_FLAG, saveAsActivity must be false when sending mail to users」

`targetObjectId`または`targetObjectIds`が設定されていると、Emailオブジェクトのプロパティ`saveAsActivity`がデフォルトでtrueが設定されます。この値がtrueで設定されているとメールが活動として保存されます。
しかし、`targetObjectId`または`targetObjectIds`にユーザIDを設定しているとこの機能がサポートされていません。そのため、上記のエラーが発生します。
#### 対応
`setSaveAsActivity(false)`でSaveAsActivityをfalseに設定します。

### ■ INVALID_ID_FIELD
「INVALID_ID_FIELD, WhatId is not available for sending emails to UserIds」

メールテンプレートに差し込み項目を反映させる`whatId`は`targetObjectId`に取引先責任者を設定したときのみ使用可能です。
`targetObjectId`にユーザIDを設定すると上記のエラーが発生します。
#### 対応
`Messaging.renderStoredEmailTemplate(templateId, whoId, whatId)`を使用します。

### ■ DUPLICATE_SENDER_DISPLAY_NAME
`setOrgWideEmailAddressId`(送信元に組織のメールアドレスを設定)と`etSenderDisplayName`(送信者表示名)を同時に使用すると上記のエラーが発生します。



## エラー (送信エラー)
`Messaging.sendEmail(mails)`でメールが送信できなかった際のエラーは例外ではなく、`Messaging.SendEmailResult`で扱います。
エラーとなるのは送信時のエラーで、送信先に到達したか否かは関係ありません。

```java
/** エラーハンドリング例 */
List<Messaging.SendEmailResult> results = Messaging.sendEmail(mails, false);
for (Messaging.SendEmailResult result : results) {
    if (!result.isSuccess()) {
        System.debug(LoggingLevel.ERROR, result.getErrors()[0]);
    }
}
```

### ■ Organization-Wide Email Address has not be verified for use.
送信元に未検証の組織のメールを指定してsendMailメソッドをコールしたときに上記のエラーが発生します。SendEmailResultのgetErrorsメソッドでエラー内容を取得します。

#### 対応
[設定]-[組織のアドレス]から組織のメールの検証メールを再送し、メールアドレスを検証します。


### ■ Not profiled to access this Org-wide Email Address
送信元に設定した組織のメールアドレスが、Apex実行者のプロファイルでの使用が許可されていません。

#### 対応
[設定]-[組織のアドレス]から組織のメールを編集し、Apex実行者のプロファイルを送信元アドレスの使用許可に選択します。



## 参考
* [送信メール | Apex開発者ガイド](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_forcecom_email_outbound.htm?search_text=sendemail)
* [Email クラス (基本メールメソッド) | Apex開発者ガイド](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_classes_email_outbound_base.htm#apex_classes_email_outbound_base)
* [SingleEmailMessage のメソッド | Apex開発者ガイド](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_classes_email_outbound_single.htm)
* [Messaging クラス | Apex開発者ガイド](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_classes_email_outbound_messaging.htm)
* [[Salesforce]メールのガバナ制限まとめ | dackdive's blog](https://dackdive.hateblo.jp/entry/2014/06/30/193757)