# 通知設計書 20-Order Delivery State (Shipped)

## 概要

本ドキュメントは、Shopwareにおける配送出荷完了通知（Order Delivery State Shipped）の設計仕様を記載したものです。配送ステータスが「出荷済み」に変更された際に顧客に送信されるメール通知について定義します。

### 本通知の処理概要

この通知は、注文の配送ステータスがステートマシンにより「shipped（出荷済み）」に遷移した際に発火するイベントです。配送センターで商品の出荷処理が完了し、ステータスが更新されたときに顧客に通知が送信されます。

**業務上の目的・背景**：顧客に商品が出荷されたことを通知し、配送の追跡や到着予定の確認を促します。ECサイトにおいて、出荷通知は顧客体験の重要な要素であり、顧客の安心感と信頼感を向上させます。

**通知の送信タイミング**：`state_enter.order_delivery.state.shipped`イベントがディスパッチされた際に、Flowビルダーで設定されたメール送信アクションがトリガーされます。

**通知の受信者**：注文を行った顧客のメールアドレス（`order.orderCustomer.email`）。

**通知内容の概要**：注文番号、注文日時、配送ステータス（出荷済み）、注文状況確認用リンクが含まれます。

**期待されるアクション**：顧客は商品が出荷されたことを確認し、配送の追跡や受け取り準備を行います。

## 通知種別

メール通知

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（Flow経由） |
| 優先度 | 高 |
| リトライ | Flowの設定に依存 |

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

イベント発生時に`MailRecipientStruct`が構築されます。`OrderStateMachineStateChangeEvent`クラスの`getMailStruct()`メソッドを使用します。

```php
// OrderStateMachineStateChangeEvent.php 行47-61
public function getMailStruct(): MailRecipientStruct
{
    if (!$this->mailRecipientStruct instanceof MailRecipientStruct) {
        $orderCustomer = $this->order->getOrderCustomer();
        if (!$orderCustomer) {
            throw new MailEventConfigurationException('Data for mailRecipientStruct not available.', self::class);
        }

        $this->mailRecipientStruct = new MailRecipientStruct([
            $orderCustomer->getEmail() => $orderCustomer->getFirstName() . ' ' . $orderCustomer->getLastName(),
        ]);
    }

    return $this->mailRecipientStruct;
}
```

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | ショップ設定に依存 |
| 送信元名称 | ショップ設定に依存 |
| 件名 | `Delivery status update`（EN）/ `Aktualisierung des Lieferstatus`（DE） |
| 形式 | HTML/テキスト |

### 本文テンプレート

**英語版（HTML）**:
```html
<div style="font-family:arial; font-size:12px;">
    <br/>
    <p>
        {{order.orderCustomer.salutation.letterName }} {{order.orderCustomer.firstName}} {{order.orderCustomer.lastName}},<br/>
        <br/>
        the status of your delivery at {{ salesChannel.name }} (Number: {{order.orderNumber}}) on {{ order.orderDateTime|date }} has changed.<br/>
        <strong>The new status is as follows: {{order.deliveries.first.stateMachineState.name}}.</strong><br/>
        <br/>
        You can check the current status of your order on our website under "My account" - "My orders" anytime: {{ rawUrl('frontend.account.order.single.page', { 'deepLinkCode': order.deepLinkCode}, salesChannel.domains|first.url) }}
        </br>
        However, in case you have purchased without a registration or a customer account, you do not have this option.
    </p>
</div>
```

**ドイツ語版（HTML）**:
```html
<div style="font-family:arial; font-size:12px;">
    <br/>
    <p>
        {{order.orderCustomer.salutation.letterName }} {{order.orderCustomer.firstName}} {{order.orderCustomer.lastName}},<br/>
        <br/>
        der Lieferstatus für Ihre Bestellung bei {{ salesChannel.name }} (Number: {{order.orderNumber}}) vom {{ order.orderDateTime|date }} hat sich geändert.<br/>
        <strong>Die Bestellung hat jetzt den Lieferstatus: {{order.deliveries.first.stateMachineState.name}}.</strong><br/>
        <br/>
        Den aktuellen Status Ihrer Bestellung können Sie auch jederzeit auf unserer Webseite im  Bereich "Mein Konto" - "Meine Bestellungen" abrufen: {{ rawUrl('frontend.account.order.single.page', { 'deepLinkCode': order.deepLinkCode}, salesChannel.domains|first.url) }}
        </br>
        Sollten Sie allerdings den Kauf ohne Registrierung, also ohne Anlage eines Kundenkontos, gewählt haben, steht Ihnen diese Möglichkeit nicht zur Verfügung.
    </p>
</div>
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 通常は添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| `order` | 注文エンティティ | OrderEntity | Yes |
| `order.orderNumber` | 注文番号 | order.order_number | Yes |
| `order.orderDateTime` | 注文日時 | order.order_date_time | Yes |
| `order.orderCustomer` | 注文顧客情報 | order_customer | Yes |
| `order.deliveries` | 配送情報コレクション | order_delivery | Yes |
| `order.deliveries.first.stateMachineState` | 配送ステータス | state_machine_state | Yes |
| `order.deliveries.first.stateMachineState.name` | 配送ステータス名 | state_machine_state_translation.name | Yes |
| `order.deepLinkCode` | 注文詳細へのディープリンクコード | order.deep_link_code | Yes |
| `salesChannel` | 販売チャネル | SalesChannelContext | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ステートマシン遷移 | `state_enter.order_delivery.state.shipped` | 配送ステータスがshippedに遷移 | OrderStateChangeEventListener経由 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| Flowが設定されていない | Flowビルダーでメール送信アクションが設定されていない場合 |
| 注文顧客情報なし | orderCustomerがnullの場合 |
| 配送情報なし | order.deliveriesが空の場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[配送ステータス変更リクエスト] --> B[StateMachine::transition]
    B --> C{遷移先 = shipped?}
    C -->|Yes| D[state_machine.order_delivery.state_changed発火]
    C -->|No| E[他のステータス処理]
    D --> F[OrderStateChangeEventListener::onOrderDeliveryStateChange]
    F --> G[配送情報から注文を取得]
    G --> H[OrderStateMachineStateChangeEvent作成]
    H --> I[state_enter.order_delivery.state.shippedイベント発火]
    I --> J[Flowエンジンがイベントを検知]
    J --> K[SendMailAction実行]
    K --> L[メール送信]
    L --> M[終了]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| `order` | 注文基本情報 | 主要データソース |
| order_customer | 注文顧客情報 | メール宛先 |
| order_delivery | 配送情報 | ステータス変更対象 |
| state_machine_state | ステータス情報 | 配送ステータス |
| state_machine_state_translation | ステータス翻訳 | ステータス名表示 |
| sales_channel | 販売チャネル | コンテキスト |

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

#### order

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | 注文識別 | 配送情報から取得 |
| order_number | 注文番号表示 | テンプレート変数 |
| deep_link_code | マイアカウントリンク | テンプレート変数 |

#### order_delivery

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | 配送識別 | イベントから取得 |
| order_id | 注文との関連 | JOIN order |
| state_id | 配送ステータス | JOIN state_machine_state |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| order_delivery | UPDATE | state_idの更新（イベント発火のトリガー） |
| state_machine_history | INSERT | 遷移履歴の記録 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 配送情報なし | orderDeliveryが取得できない | OrderException::orderDeliveryNotFound |
| 注文情報なし | orderがnull | OrderException::orderNotFound |
| 注文顧客なし | orderCustomerがnull | MailEventConfigurationException |
| メール送信失敗 | SMTPエラー等 | ログ記録、リトライ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Flowの設定に依存 |
| リトライ間隔 | Flowの設定に依存 |
| リトライ対象エラー | 一時的なネットワークエラー |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | システム設定に依存 |
| 1日あたり上限 | システム設定に依存 |

### 配信時間帯

制限なし

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

- 配送先住所などの個人情報が含まれる可能性があるため、適切な取り扱いが必要
- 配送追跡番号を含める場合は、URLの安全性を確認

## 備考

- このイベントは`OrderStateMachineStateChangeEvent`クラスを使用し、他の配送ステータス変更と同じ仕組みで動作
- 配送ステータスは`order_delivery`ステートマシンで管理される
- 出荷済み通知に配送追跡番号を含める場合は、カスタムテンプレートで`order.deliveries.first.trackingCodes`を使用可能
- `shipped_partially`（一部出荷）とは異なるイベントであることに注意

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | OrderStateMachineStateChangeEvent.php | `src/Core/Checkout/Order/Event/OrderStateMachineStateChangeEvent.php` | イベントクラス、動的イベント名 |
| 1-2 | OrderDeliveryEntity.php | `src/Core/Checkout/Order/Aggregate/OrderDelivery/OrderDeliveryEntity.php` | 配送エンティティ |

**読解のコツ**: `OrderStateMachineStateChangeEvent`は注文、配送、決済トランザクションの全てのステータス変更で共通利用される。`getName()`で動的にイベント名を取得する点に注目。

#### Step 2: イベント発火の仕組みを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | OrderStateChangeEventListener.php | `src/Core/Checkout/Order/Listener/OrderStateChangeEventListener.php` | onOrderDeliveryStateChange（行61-77） |

**読解のコツ**: `state_machine.order_delivery.state_changed`イベントをリッスンし、配送IDから注文を取得してからイベントをディスパッチする流れを理解する。

#### Step 3: テンプレートを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | MailTemplateContent.php | `src/Core/Migration/Fixtures/MailTemplateContent.php` | DeliveryShippedテンプレート（行609-660） |
| 3-2 | MailTemplateTypes.php | `src/Core/Content/MailTemplate/MailTemplateTypes.php` | テンプレートタイプ定数（行70） |

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

```
配送ステータス変更（shipped）
    │
    ▼
StateMachine::transition()
    │
    ▼
state_machine.order_delivery.state_changed イベント
    │
    ▼
OrderStateChangeEventListener::onOrderDeliveryStateChange()
    │
    ├─ deliveryRepository->search() で配送情報取得
    │
    ├─ getOrder() で注文情報取得
    │
    └─ dispatchEvent()
           │
           └─ eventDispatcher->dispatch(OrderStateMachineStateChangeEvent)
                  │
                  └─ FlowExecutor (Subscriber)
                         │
                         └─ SendMailAction::handleFlow()
```

### データフロー図

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

配送ステータス          state_machine.order_delivery
変更リクエスト ────────▶ .state_changed
                              │
                              ▼
                       OrderStateChange
                       EventListener
                              │
                              ▼
                       OrderStateMachine
                       StateChangeEvent発火
                       (name: state_enter.
                        order_delivery.state.shipped)
                              │
                              ▼
                       Flow Engine
                              │
                              ▼
                       SendMailAction
                              │
                              ▼
                       メール送信 ─────────▶ 顧客へ出荷通知
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| OrderStateMachineStateChangeEvent.php | `src/Core/Checkout/Order/Event/OrderStateMachineStateChangeEvent.php` | ソース | イベントクラス定義 |
| OrderStateChangeEventListener.php | `src/Core/Checkout/Order/Listener/OrderStateChangeEventListener.php` | ソース | イベントリスナー |
| OrderDeliveryEntity.php | `src/Core/Checkout/Order/Aggregate/OrderDelivery/OrderDeliveryEntity.php` | ソース | 配送エンティティ |
| MailTemplateContent.php | `src/Core/Migration/Fixtures/MailTemplateContent.php` | フィクスチャ | テンプレート内容 |
| MailTemplateTypes.php | `src/Core/Content/MailTemplate/MailTemplateTypes.php` | ソース | テンプレートタイプ定数 |
| SendMailAction.php | `src/Core/Content/Flow/Dispatching/Action/SendMailAction.php` | ソース | メール送信アクション |
