# 通知設計書 3-Dropbox通知

## 概要

本ドキュメントは、Fat Free CRMにおけるDropbox（メールドロップボックス）通知機能の設計を記載する。メールドロップボックス機能でメールが受信・処理された際、関連するユーザーにメール通知を送信する機能について定義する。

### 本通知の処理概要

本通知は、CRMシステムのメールドロップボックス機能でメールが受信・処理された際、処理結果をユーザーに通知する機能である。ドロップボックス機能により、外部メールをCRM内のエンティティ（Account、Contact、Lead等）に自動的に関連付けることができる。

**業務上の目的・背景**：営業担当者は顧客とのメールコミュニケーションをCRMシステムに記録する必要がある。メールドロップボックス機能を使用すると、特定のメールアドレスにCC/転送するだけで、メールが自動的にCRM内の適切なエンティティに関連付けられる。本通知機能により、メールの処理結果（どのエンティティに関連付けられたか）がユーザーに通知され、処理の成否を確認できる。これにより、メール記録の抜け漏れを防ぎ、顧客コミュニケーション履歴の一元管理を支援する。

**通知の送信タイミング**：IMAPサーバーからメールを取得・処理した後、エンティティへの関連付けが完了した時点で非同期で送信される。バッチ処理またはスケジュールジョブとして定期的に実行される。

**通知の受信者**：ドロップボックスにメールを送信したユーザー（送信元メールアドレスに紐づくCRMユーザー）が受信者となる。送信者が特定できない場合、通知は送信されない。

**通知内容の概要**：メール件名には「dropbox - Added email - {元メールの件名}」が含まれる。本文には処理成功メッセージ、関連付けられたエンティティへのリンク、元メールの件名・本文が含まれる。

**期待されるアクション**：受信者は通知メールを確認し、メールが正しいエンティティに関連付けられたことを確認する。必要に応じてリンクからエンティティ詳細画面にアクセスし、追加の操作（コメント追加、ステータス更新等）を行う。

## 通知種別

メール通知

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（deliver_later） |
| 優先度 | 通常 |
| リトライ | Active Jobのデフォルト設定に依存 |

### 送信先決定ロジック

1. 受信メールのFromアドレスからCRMユーザーを検索
   - users.emailまたはusers.alt_emailと一致（大文字小文字を区別しない）
   - suspended_atがNULLのユーザーのみ対象
2. 該当ユーザーが見つかった場合、そのユーザーを通知の受信者とする
3. 該当ユーザーが見つからない場合、メールは破棄され通知は送信されない

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | ドロップボックス設定のアドレス（@settings[:address]） |
| 送信元名称 | 設定による |
| 件名 | dropbox - Added email - {元メールの件名}（I18n: dropbox_notification_subject） |
| 形式 | HTML（text/html） |

### 本文テンプレート

```
Succesfully added the email you sent to dropbox:

Added to:
{関連付けられたエンティティへのリンク（複数可）}

Subject: {元メールの件名}
Body:
{元メールの本文}
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 本通知には添付ファイルはない |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| @mediator_links | 関連付けられたエンティティへのリンク | mediator_links.join("\n") | Yes |
| @subject | 元メールの件名 | email.subject | Yes |
| @body | 元メールの本文 | email.body_plain | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| バッチ処理 | IMAPメール取得・処理完了 | メールがエンティティに関連付けられた | Dropbox.runメソッドの実行時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 送信者不明 | メールのFromアドレスがCRMユーザーに紐づかない場合 |
| 送信者停止中 | ユーザーがsuspended状態の場合 |
| 不正なメール形式 | text/html形式のみのメール（text/plainが含まれない） |
| 処理エラー | メール処理中にエラーが発生した場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[IMAPサーバーに接続] --> B[未読メールを取得]
    B --> C{メール有り?}
    C -->|No| Z[終了]
    C -->|Yes| D[メールを解析]
    D --> E{is_valid?}
    E -->|No| F[破棄]
    E -->|Yes| G{sent_from_known_user?}
    G -->|No| F
    G -->|Yes| H[process: キーワード/受信者解析]
    H --> I[エンティティに関連付け]
    I --> J[Email.create]
    J --> K[notify: 通知送信]
    K --> L[DropboxMailer.dropbox_notification.deliver_later]
    L --> M[archive: メールをアーカイブ]
    F --> N[discard: メールを破棄]
    M --> O{次のメール}
    N --> O
    O -->|あり| D
    O -->|なし| Z
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| users | 送信者の特定 | email、alt_email、suspended_at |
| accounts | エンティティ検索 | email、website |
| contacts | エンティティ検索 | email、alt_email |
| leads | エンティティ検索 | email、alt_email |
| settings | ドロップボックス設定 | email_dropbox |

### テーブル別参照項目詳細

#### users

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | ユーザー識別 | - |
| email | 送信者照合 | lower(email) = :email |
| alt_email | 送信者照合 | lower(alt_email) = :email |
| suspended_at | 有効ユーザー判定 | IS NULL |

#### accounts / contacts / leads

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | エンティティ識別 | - |
| email | メール受信者照合 | lower(email) = :recipient |
| alt_email | メール受信者照合（contacts, leads） | lower(alt_email) = :recipient |
| name / first_name, last_name | エンティティ名 | - |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| emails | INSERT | メール記録の作成 |
| accounts/contacts/leads | UPDATE | updated_at（touch） |
| leads | UPDATE | status（newからcontactedへ） |

#### emailsテーブル

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | imap_message_id | email.message_id | |
| INSERT | user_id | @sender.id | |
| INSERT | mediator_id | asset.id | ポリモーフィック |
| INSERT | mediator_type | asset.class.name | ポリモーフィック |
| INSERT | sent_from | email.from.first | |
| INSERT | sent_to | email.to.join(", ") | |
| INSERT | cc | email.cc.join(", ") | |
| INSERT | subject | email.subject | |
| INSERT | body | email_body | |
| INSERT | received_at | email.date | |
| INSERT | sent_at | email.date | |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| IMAP接続エラー | サーバー接続失敗 | ログ出力、処理中断 |
| 認証エラー | IMAP認証失敗 | ログ出力、処理中断 |
| メール解析エラー | 不正なメール形式 | 破棄フォルダに移動、次のメールへ |
| 送信者不明 | ユーザー特定不可 | 破棄、ログ出力 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Active Jobのデフォルト設定（通知送信） |
| リトライ間隔 | 指数バックオフ |
| リトライ対象エラー | 一時的なネットワークエラー、SMTPサーバーエラー |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 設定なし |
| 1日あたり上限 | 設定なし |

### 配信時間帯

バッチ処理のスケジュール設定に依存。通常は定期的に実行される。

## セキュリティ考慮事項

- メール送信者はCRMの登録ユーザーである必要がある（find_sender）
- 停止中のユーザーからのメールは処理されない
- アセットへのアクセス権限がチェックされる（sender_has_permissions_for?）
- メール本文はsanitize処理なしで通知に含まれるため、信頼できるユーザーからのメールのみ処理される

## 備考

- 現在のコードではnotifyメソッドはcreate_dropbox_notificationを呼び出しているが、DropboxMailer.dropbox_notificationメソッドが定義されている
- 明示的キーワード（account、campaign、contact、lead、opportunity）をメール本文の1行目に記載することで、特定のエンティティタイプに強制的に関連付けることができる
- 新規Contactの自動作成機能があり、メールアドレスのドメインからAccountを推測して関連付けを行う

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

メールドロップボックスの設定構造とEmailモデルを理解することが最初のステップである。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | email.rb | `app/models/entities/email.rb`（存在する場合） | Emailモデルの構造、mediator関連 |
| 1-2 | Setting（設定） | DBまたは設定ファイル | email_dropbox設定の構造 |

**読解のコツ**: ドロップボックスの設定は`Setting.email_dropbox`から取得される。IMAPサーバー接続情報、スキャンフォルダ、アーカイブフォルダ等の設定が含まれる。

#### Step 2: エントリーポイントを理解する

Dropboxクラスのrunメソッドがメール処理の起点となる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | base.rb | `lib/fat_free_crm/mail_processor/base.rb` | 基底クラス、run、connect!、with_new_emailsメソッド |
| 2-2 | dropbox.rb | `lib/fat_free_crm/mail_processor/dropbox.rb` | processメソッド、notify メソッド |

**主要処理フロー**:
1. **52-63行目（base.rb）**: `run` - IMAP接続、未読メール取得、process呼び出し
2. **96-118行目（base.rb）**: `with_new_emails` - メール取得ループ、is_valid?、sent_from_known_user?チェック
3. **30-51行目（dropbox.rb）**: `process` - キーワード解析、受信者解析、関連付け処理

#### Step 3: メール処理ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | dropbox.rb | `lib/fat_free_crm/mail_processor/dropbox.rb` | with_explicit_keyword、with_recipients、attach メソッド |
| 3-2 | base.rb | `lib/fat_free_crm/mail_processor/base.rb` | find_sender、plain_text_body メソッド |

**主要処理フロー**:
- **55-58行目（dropbox.rb）**: `with_explicit_keyword` - メール1行目からキーワード抽出
- **62-77行目（dropbox.rb）**: `with_recipients` - To/CCアドレスでエンティティ検索
- **146-188行目（dropbox.rb）**: `attach` - Email.create、エンティティへの関連付け

#### Step 4: 通知送信を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | dropbox.rb | `lib/fat_free_crm/mail_processor/dropbox.rb` | notifyメソッド（258-262行目） |
| 4-2 | dropbox_mailer.rb | `app/mailers/dropbox_mailer.rb` | dropbox_notificationメソッド |
| 4-3 | dropbox_notification.html.haml | `app/views/dropbox_mailer/dropbox_notification.html.haml` | メールテンプレート |

**主要処理フロー**:
- **258-262行目（dropbox.rb）**: `notify` - DropboxMailer.create_dropbox_notification呼び出し
- **9-18行目（dropbox_mailer.rb）**: `dropbox_notification` - 変数設定、mail送信
- **1-9行目（template）**: I18n翻訳を使用した本文生成

### プログラム呼び出し階層図

```
Rakeタスク / スケジューラ
    │
    └─ FatFreeCRM::MailProcessor::Dropbox.new.run
            │
            ├─ connect! (IMAPサーバー接続)
            │
            └─ with_new_emails (未読メール取得ループ)
                   │
                   ├─ is_valid? (text/plain含むか)
                   │
                   ├─ sent_from_known_user? (送信者特定)
                   │      └─ find_sender
                   │
                   └─ process (メール処理)
                          │
                          ├─ with_explicit_keyword (キーワード解析)
                          │      └─ find_or_create_and_attach
                          │
                          ├─ with_recipients (受信者解析)
                          │      └─ find_and_attach
                          │
                          └─ attach (関連付け)
                                 │
                                 ├─ Email.create
                                 │
                                 └─ notify
                                        │
                                        └─ DropboxMailer.dropbox_notification
                                               └─ mail(subject:, to:, from:)
                                                      └─ dropbox_notification.html.haml
```

### データフロー図

```
[入力]                    [処理]                           [出力]

IMAPサーバー             Dropbox.run
    │                        │
    │  未読メール           ▼
    └─────────────▶ with_new_emails
                           │
                     is_valid? / sent_from_known_user?
                           │
                           ▼
                     process (パイプライン処理)
                           │
                     ┌─────┴─────┐
                     ▼           ▼
               キーワード解析  受信者解析
                     │           │
                     └─────┬─────┘
                           ▼
                     attach
                           │
                     ┌─────┼─────┐
                     ▼     ▼     ▼
               Email.create  asset.touch  Lead.status更新
                           │
                           ▼
                     notify ──────────────────────────────▶ DropboxMailer ──▶ Email
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| dropbox.rb | `lib/fat_free_crm/mail_processor/dropbox.rb` | ソース | Dropboxプロセッサー、メール処理ロジック |
| base.rb | `lib/fat_free_crm/mail_processor/base.rb` | ソース | 基底クラス、IMAP接続、共通処理 |
| dropbox_mailer.rb | `app/mailers/dropbox_mailer.rb` | ソース | メーラークラス、通知メール生成 |
| dropbox_notification.html.haml | `app/views/dropbox_mailer/dropbox_notification.html.haml` | テンプレート | メール本文テンプレート |
| account.rb | `app/models/entities/account.rb` | ソース | Accountエンティティ |
| contact.rb | `app/models/entities/contact.rb` | ソース | Contactエンティティ |
| lead.rb | `app/models/entities/lead.rb` | ソース | Leadエンティティ |
| fat_free_crm.en-US.yml | `config/locales/fat_free_crm.en-US.yml` | 設定 | 翻訳定義（dropbox_notification_*） |
