EC-CUBEシリーズ！「受注ステータスのカスタマイズ」

![EC-CUBE_受注.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2633992/a7d86b75-aa2d-b610-ae06-2e2c5f047a7c.jpeg)

エスプリフォートでは、ECによる請負開発やプロダクト（自社サービス）開発を長く行ってきました。
そしてECサイトを構築する際には、受注ステータスを自社の運用にあわせて管理できるようにしたいというのはよくある要望として頂いています。

ECサイトを構築する際の一つの選択肢として、エスプリフォートが長年扱ってきましたEC-CUBEで、受注ステータスを追加のアドオン開発に関することをご紹介していきます。

## EC-CUBEの受注ステータス

受注対応状況の流れになります。
<img src="https://doc4.ec-cube.net/images/spec/order-statemachine.png">
※「EC-CUBE 4 開発者向けドキュメントサイト」参照

EC-CUBEは受注ステータスを容易に追加・変更・削除することができる仕組みがあります。
またステータス遷移時のタイミングでイベントを発火する仕組みも備えており、特定のステータスに変更されたタイミングでなにか処理を行うといったカスタマイズも簡単に行うことができます。

受注ステータスの表示は、管理側とユーザー側で分けて表示する仕組みがあるため、
利用者に分かりやすい表現でカスタマイズすることも容易にできます。

それでは、どれだけ簡単に受注ステータスが追加できるのかをこれから体験してみましょう。


## １、テーブルに対応状況（ステータス）を追加する

下記テーブルにデータを追加する

| テーブル物理名 | テーブル論理名 | 追加するデータ |
|:--|:--|:--|
| mtb_order_status | 受注ステータス一覧 | 管理画面に表示するステータスを追加 |
| mtb_order_status_color | 受注ステータス別カラー一覧 | 管理画面の受注一覧で表示されるステータスの色を設定 |
| mtb_customer_order_status | マイページ受注ステータス一覧 | マイページのご注文履歴の注文のステータスとして表示するステータスを追加 |

※「mtb_order_status」、「mtb_customer_order_status」、「mtb_order_status_color」のidに登録する値は同じにすること。
例）mtb_order_statusのidが「50」、nameが「入金待ち」、
mtb_customer_order_statusのidが「50」、nameが「入金確認中」、
mtb_order_status_colorのidが「50」、nameが「#FF0000」（赤色）
と登録されている場合、ステータスが「50」の注文情報は、
管理画面では赤色で「入金待ち」と表示され、マイページでは「入金確認中」と表示されます。

## ２、OrderStatus(Entity)へ追加ステータスの定数定義

「[ec-cubeインストールディレクトリ]/app/Customize/Entity」に「OrderStatus」クラスを作成し、追加したステータスを追記します。

```php5
<?php
namespace Customize\Entity\Master;

use Eccube\Entity\Master\OrderStatus as BaseEntity;

if (!class_exists('Customize\Entity\Master\OrderStatus')) {

    /** 親クラスの定数宣言を拡張する目的に作成しています */
    class OrderStatus extends BaseEntity {
        /** 入金待ち */
        const WAIT_PAYMENT = 100;
    }
}
```
※定数を利用する場合は「trait」はconst による定数宣言は不可能なため、クラスでの実装のみ可能となります。

## ３、ステータスの追加及び、変更可能なステータスを設定します
「[ec-cubeインストールディレクトリ]/app/config/packages/order_state_machine.php」に
「places」配列に２で追加したステータス（WAIT_PAYMENT）を追加します。

次は、「transitions」配列に２で追加したステータス（WAIT_PAYMENT）に変更できるステータスと、２で追加したステータス（WAIT_PAYMENT）から変更できるステータスを設定します。

```php5
<?php

/*
 * This file is part of EC-CUBE
 *
 * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
 *
 * http://www.ec-cube.co.jp/
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

//use Eccube\Entity\Master\OrderStatus as Status;    // ←コメントアウト
use Customize\Entity\Master\OrderStatus as Status;    // ←追加
use Eccube\Service\OrderStateMachineContext;

$container->loadFromExtension('framework', [
    'workflows' => [
        'order' => [
            'type' => 'state_machine',
            'marking_store' => [
                'type' => 'single_state',
                'arguments' => 'status',
            ],
            'supports' => [
                OrderStateMachineContext::class,
            ],
            'initial_place' => (string) Status::NEW,
            'places' => [
                (string) Status::NEW,
                (string) Status::CANCEL,
                (string) Status::IN_PROGRESS,
                (string) Status::DELIVERED,
                (string) Status::PAID,
                (string) Status::PENDING,
                (string) Status::PROCESSING,
                (string) Status::RETURNED,
                (string) Status::WAIT_PAYMENT,    // ←追加
            ],
            'transitions' => [
                'pay' => [
                    'from' => [(string) Status::NEW, (string) Status::WAIT_PAYMENT],    // ←追加
                    'to' => (string) Status::PAID,
                ],
                'packing' => [
                    'from' => [(string) Status::NEW, (string) Status::PAID],
                    'to' => (string) Status::IN_PROGRESS,
                ],
                'cancel' => [
                    'from' => [(string) Status::NEW, (string) Status::IN_PROGRESS, (string) Status::PAID],
                    'to' => (string) Status::CANCEL,
                ],
                'back_to_in_progress' => [
                    'from' => (string) Status::CANCEL,
                    'to' => (string) Status::IN_PROGRESS,
                ],
                'ship' => [
                    'from' => [(string) Status::NEW, (string) Status::PAID, (string) Status::IN_PROGRESS,
                    'to' => [(string) Status::DELIVERED],
                ],
                'return' => [
                    'from' => (string) Status::DELIVERED,
                    'to' => (string) Status::RETURNED,
                ],
                'cancel_return' => [
                    'from' => (string) Status::RETURNED,
                    'to' => (string) Status::DELIVERED,
                ],
                // 追加
                'waiting_for_payment' => [
                    'from' => (string) Status::NEW,
                    'to' => (string) Status::WAIT_PAYMENT,
                ],
            ],
        ],
    ],
]);
```

## ４、ステータス変更時のイベント処理を追加します（任意）

「[ec-cubeインストールディレクトリ]/src/Eccube/Service/OrderStateMachine.php」でステータス変更時のイベント処理が設定されています。
OrderStateMachineクラスの下記メソッドにて「order_state_machine.php」の「transitions」配列で設定されているステータスの変更設定がトリガーとなります。

【デフォルトで設定されているステータス遷移とイベント】
|遷移元 | 遷移先 | イベント |
|:--|:--|:--|
| 新規受付 | 入金済み | workflow.order.transition.pay |
| 新規受付, 入金済み | 対応中 | workflow.order.transition.packing |
| 新規受付, 対応中, 入金済み | キャンセル | workflow.order.transition.cancel |
| キャンセル | 対応中 | workflow.order.transition.back_to_in_progress |
| 新規受付, 対応中, 入金済み | 発送済み | workflow.order.transition.ship |
| 発送済み | 返品 | workflow.order.transition.return |
| 返品 | 発送済み | workflow.order.transition.cancel_return |

OrderStateMachineクラスにて、ステータス更新時にイベントを実装することができ、受注ステータスの遷移時に任意の処理を実行させることが出来ます。

```php5
    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return [
            'workflow.order.completed' => ['onCompleted'],
            'workflow.order.transition.pay' => ['updatePaymentDate'],
            'workflow.order.transition.cancel' => [['rollbackStock'], ['rollbackUsePoint']],
            'workflow.order.transition.back_to_in_progress' => [['commitStock'], ['commitUsePoint']],
            'workflow.order.transition.ship' => [['commitAddPoint']],
            'workflow.order.transition.return' => [['rollbackUsePoint'], ['rollbackAddPoint']],
            'workflow.order.transition.cancel_return' => [['commitUsePoint'], ['commitAddPoint']],
            'workflow.order.transition.waiting_for_payment' => [['onCompleted']],    // ←追加
        ];
    }
```

【EC-CUBE標準にあるイベント一覧】
| イベント名 | 説明 |
|:--|:--|
| updatePaymentDate | 入金日を更新する |
| commitUsePoint | 会員の保有ポイントを減らす |
| rollbackUsePoint | 利用ポイントを会員に戻す |
| commitStock | 在庫を減らす |
| rollbackStock | 在庫を戻す |
| commitAddPoint | 会員に加算ポイントを付与する |
| rollbackAddPoint | 会員に付与した加算ポイントを取り消す |
| onCompleted | 受注ステータスを再設定 |

５、OrderStateMachineクラスに、ステータス変更時の独自の処理を追加する。

OrderStateMachineクラスに下記独自イベント処理追加実装し、OrderStateMachineクラスの「getSubscribedEvents」メソッド内のイベントの設定を、ステータス変更時の任意のタイミングで実行するかを設定します。

【OrderStateMachineクラス内に独自イベント処理を追加】
```php5
　　　　・
　　　　・
　　　　・
    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return [
            'workflow.order.completed' => ['onCompleted'],
            'workflow.order.transition.pay' => ['updatePaymentDate'],
            'workflow.order.transition.cancel' => [['rollbackStock'], ['rollbackUsePoint']],
            'workflow.order.transition.back_to_in_progress' => [['commitStock'], ['commitUsePoint']],
            'workflow.order.transition.ship' => [['commitAddPoint']],
            'workflow.order.transition.return' => [['rollbackUsePoint'], ['rollbackAddPoint']],
            'workflow.order.transition.cancel_return' => [['commitUsePoint'], ['commitAddPoint']],
            'workflow.order.transition.waiting_for_payment' => [['isMail']],    // ←追加
        ];
    }
　　　　・
　　　　・
　　　　・
    // OrderStateMachineクラス内に独自イベント処理を追加
    public function inMail(Event $event)
    {
        $BaseInfo = $this->baseInfoRepository->get();
        $Order = $event->getSubject()->getOrder();
        $subject = '['.$BaseInfo->getShopName().'] メール送信です。';
        $body = 'メール送信中です。';

        $message = (new \Swift_Message())
            ->setSubject($subject)
            ->setFrom([$BaseInfo->getEmail01() => $BaseInfo->getShopName()])
            ->setTo([$Order->getEmail()])
            ->setBcc($BaseInfo->getEmail01())
            ->setReplyTo($BaseInfo->getEmail03())
            ->setReturnPath($BaseInfo->getEmail04());
        $message->setBody($body);
        $this->mailer->send($message);
    }
```


## 最後に
アドオン開発において、受注ステータスのカスタマイズは結構ありますので、こちらの受注ステータスのカスタマイズは役に立ちますので、覚えておいて損はないかと思います。

https://www.esprit-fort.co.jp/

https://www.esprit-fort.co.jp/recruit/