src/Eccube/Service/OrderStateMachine.php line 243

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of EC-CUBE
  4.  *
  5.  * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
  6.  *
  7.  * http://www.ec-cube.co.jp/
  8.  *
  9.  * For the full copyright and license information, please view the LICENSE
  10.  * file that was distributed with this source code.
  11.  */
  12. namespace Eccube\Service;
  13. use Eccube\Entity\Master\OrderStatus;
  14. use Eccube\Entity\Order;
  15. use Eccube\Repository\Master\OrderStatusRepository;
  16. use Eccube\Service\PurchaseFlow\Processor\PointProcessor;
  17. use Eccube\Service\PurchaseFlow\Processor\StockReduceProcessor;
  18. use Eccube\Service\PurchaseFlow\PurchaseContext;
  19. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  20. use Symfony\Component\Workflow\Event\Event;
  21. use Symfony\Component\Workflow\StateMachine;
  22. class OrderStateMachine implements EventSubscriberInterface {
  23.   /**
  24.    * @var StateMachine
  25.    */
  26.   private $machine;
  27.   /**
  28.    * @var OrderStatusRepository
  29.    */
  30.   private $orderStatusRepository;
  31.   /**
  32.    * @var PointProcessor
  33.    */
  34.   private $pointProcessor;
  35.   /**
  36.    * @var StockReduceProcessor
  37.    */
  38.   private $stockReduceProcessor;
  39.   public function __construct(StateMachine $_orderStateMachineOrderStatusRepository $orderStatusRepositoryPointProcessor $pointProcessorStockReduceProcessor $stockReduceProcessor) {
  40.     $this->machine $_orderStateMachine;
  41.     $this->orderStatusRepository $orderStatusRepository;
  42.     $this->pointProcessor $pointProcessor;
  43.     $this->stockReduceProcessor $stockReduceProcessor;
  44.   }
  45.   /**
  46.    * 指定ステータスに遷移.
  47.    *
  48.    * @param Order $Order 受注
  49.    * @param OrderStatus $OrderStatus 遷移先ステータス
  50.    */
  51.   public function apply(Order $OrderOrderStatus $OrderStatus) {
  52.     $context $this->newContext($Order);
  53.     $transition $this->getTransition($context$OrderStatus);
  54.     if ($transition) {
  55.       // 2025.05.30 stateの遷移条件を止めているので手動で設定
  56.       $status_id intval($OrderStatus->getId());
  57.       // 入金済み
  58.       if ($status_id === 6) {
  59.         $this->updatePaymentDateOrder($Order);
  60.       // 注文取消
  61.       } elseif ($status_id === 3) {
  62.         $this->rollbackStockOrder($Order);
  63.         $this->rollbackUsePointOrder($Order);
  64.       // 対応中
  65.       } elseif ($status_id === 4) {
  66. //        $this->commitStockOrder($Order);
  67. //        $this->commitUsePointOrder($Order);
  68.       // 発送済
  69.       } elseif ($status_id === 5) {
  70.         $this->commitAddPointOrder($Order);
  71.       // 返品
  72.       } elseif ($status_id === 9) {
  73.         $this->rollbackUsePointOrder($Order);
  74.         $this->rollbackAddPointOrder($Order);
  75.       }
  76.       $this->machine->apply($context$transition->getName());
  77.     } else {
  78.       throw new \InvalidArgumentException();
  79.     }
  80.   }
  81.   /**
  82.    * 指定ステータスに遷移できるかどうかを判定.
  83.    *
  84.    * @param Order $Order 受注
  85.    * @param OrderStatus $OrderStatus 遷移先ステータス
  86.    *
  87.    * @return boolean 指定ステータスに遷移できる場合はtrue
  88.    */
  89.   public function can(Order $OrderOrderStatus $OrderStatus) {
  90.     return !is_null($this->getTransition($this->newContext($Order), $OrderStatus));
  91.   }
  92.   private function getTransition(OrderStateMachineContext $contextOrderStatus $OrderStatus) {
  93.     $transitions $this->machine->getEnabledTransitions($context);
  94.     foreach ($transitions as $t) {
  95.       if (in_array($OrderStatus->getId(), $t->getTos())) {
  96.         return $t;
  97.       }
  98.     }
  99.     return null;
  100.   }
  101.   /**
  102.    * {@inheritdoc}
  103.    * 
  104.    * @url https://qiita.com/espritfort_tech/items/912c1ad4b92086650599
  105.    */
  106.   public static function getSubscribedEvents() {
  107.     return [
  108.       'workflow.order.completed' => ['onCompleted'], // 
  109.       'workflow.order.transition.pay' => ['updatePaymentDate'], // 入金済み
  110.       'workflow.order.transition.cancel' => [['rollbackStock'], ['rollbackUsePoint']], // キャンセル
  111.       'workflow.order.transition.back_to_in_progress' => [['commitStock'], ['commitUsePoint']], // 対応中
  112.       'workflow.order.transition.ship' => [['commitAddPoint']], // 発送済み    
  113.       'workflow.order.transition.return' => [['rollbackUsePoint'], ['rollbackAddPoint']], // 返品
  114.       'workflow.order.transition.cancel_return' => [['commitUsePoint'], ['commitAddPoint']], // 発送済み
  115.     ];
  116.   }
  117.   /**
  118.    * 入金日を更新する.
  119.    * @param Event $Order
  120.    */
  121.   public function updatePaymentDateOrder(Order &$Order) {
  122.     $Order->setPaymentDate(new \DateTime());
  123.   }
  124.   /**
  125.    * 在庫を戻す.
  126.    * @param Event $event
  127.    */
  128.   public function rollbackStockOrder(Order &$Order) {
  129.     $this->stockReduceProcessor->rollback($Order, new PurchaseContext());
  130.   }
  131.   /**
  132.    * 利用ポイントを会員に戻す.
  133.    * @param Order $Order
  134.    */
  135.   public function rollbackUsePointOrder(Order &$Order) {
  136.     $this->pointProcessor->rollback($Order, new PurchaseContext());
  137.   }
  138.   /**
  139.    * 在庫を減らす.
  140.    * @param Order $Order
  141.    * @throws PurchaseFlow\PurchaseException
  142.    */
  143.   public function commitStockOrder(Order &$Order) {
  144.     $this->stockReduceProcessor->prepare($Order, new PurchaseContext());
  145.   }
  146.   
  147.   /**
  148.    * 会員の保有ポイントを減らす.
  149.    * @param Order $Order
  150.    * @throws PurchaseFlow\PurchaseException
  151.    */
  152.   public function commitUsePointOrder(Order &$Order) {
  153.     $this->pointProcessor->prepare($Order, new PurchaseContext());
  154.   }
  155.   
  156.   /**
  157.    * 会員に加算ポイントを付与する.
  158.    * @param Order $Order
  159.    */
  160.   public function commitAddPointOrder(Order &$Order) {
  161.     $Customer $Order->getCustomer();
  162.     if ($Customer) {
  163.       $Customer->setPoint(intval($Customer->getPoint()) + intval($Order->getAddPoint()));
  164.     }
  165.   }
  166.   
  167.   /**
  168.    * 会員に付与した加算ポイントを取り消す.
  169.    * @param Order $Order
  170.    */
  171.   public function rollbackAddPointOrder(Order &$Order) {
  172.     $Customer $Order->getCustomer();
  173.     if ($Customer) {
  174.       $Customer->setPoint(intval($Customer->getPoint()) - intval($Order->getAddPoint()));
  175.     }
  176.   }
  177.   
  178.   /*
  179.    * Event handlers.
  180.    */
  181.   /**
  182.    * 入金日を更新する.
  183.    *
  184.    * @param Event $event
  185.    */
  186.   public function updatePaymentDate(Event $event) {
  187.     /* @var Order $Order */
  188.     $Order $event->getSubject()->getOrder();
  189.     $Order->setPaymentDate(new \DateTime());
  190.   }
  191.   /**
  192.    * 会員の保有ポイントを減らす.
  193.    *
  194.    * @param Event $event
  195.    *
  196.    * @throws PurchaseFlow\PurchaseException
  197.    */
  198.   public function commitUsePoint(Event $event) {
  199.     /* @var Order $Order */
  200.     $Order $event->getSubject()->getOrder();
  201.     $this->pointProcessor->prepare($Order, new PurchaseContext());
  202.   }
  203.   /**
  204.    * 利用ポイントを会員に戻す.
  205.    *
  206.    * @param Event $event
  207.    */
  208.   public function rollbackUsePoint(Event $event) {
  209.     /* @var Order $Order */
  210.     $Order $event->getSubject()->getOrder();
  211.     $this->pointProcessor->rollback($Order, new PurchaseContext());
  212.   }
  213.   /**
  214.    * 在庫を減らす.
  215.    *
  216.    * @param Event $event
  217.    *
  218.    * @throws PurchaseFlow\PurchaseException
  219.    */
  220.   public function commitStock(Event $event) {
  221.     /* @var Order $Order */
  222.     $Order $event->getSubject()->getOrder();
  223.     $this->stockReduceProcessor->prepare($Order, new PurchaseContext());
  224.   }
  225.   /**
  226.    * 在庫を戻す.
  227.    *
  228.    * @param Event $event
  229.    */
  230.   public function rollbackStock(Event $event) {
  231.     /* @var Order $Order */
  232.     $Order $event->getSubject()->getOrder();
  233.     $this->stockReduceProcessor->rollback($Order, new PurchaseContext());
  234.   }
  235.   /**
  236.    * 会員に加算ポイントを付与する.
  237.    *
  238.    * @param Event $event
  239.    */
  240.   public function commitAddPoint(Event $event) {
  241.     /* @var Order $Order */
  242.     $Order $event->getSubject()->getOrder();
  243.     $Customer $Order->getCustomer();
  244.     if ($Customer) {
  245.       $Customer->setPoint(intval($Customer->getPoint()) + intval($Order->getAddPoint()));
  246.     }
  247.   }
  248.   /**
  249.    * 会員に付与した加算ポイントを取り消す.
  250.    *
  251.    * @param Event $event
  252.    */
  253.   public function rollbackAddPoint(Event $event) {
  254.     /* @var Order $Order */
  255.     $Order $event->getSubject()->getOrder();
  256.     $Customer $Order->getCustomer();
  257.     if ($Customer) {
  258.       $Customer->setPoint(intval($Customer->getPoint()) - intval($Order->getAddPoint()));
  259.     }
  260.   }
  261.   /**
  262.    * 受注ステータスを再設定.
  263.    * {@link StateMachine}によって遷移が終了したときには{@link Order#OrderStatus}のidが変更されるだけなのでOrderStatusを設定し直す.
  264.    *
  265.    * @param Event $event
  266.    */
  267.   public function onCompleted(Event $event) {
  268.     /** @var $context OrderStateMachineContext */
  269.     $context $event->getSubject();
  270.     $Order $context->getOrder();
  271.     $CompletedOrderStatus $this->orderStatusRepository->find($context->getStatus());
  272.     $Order->setOrderStatus($CompletedOrderStatus);
  273.   }
  274.   private function newContext(Order $Order) {
  275.     return new OrderStateMachineContext((string) $Order->getOrderStatus()->getId(), $Order);
  276.   }
  277. }
  278. class OrderStateMachineContext {
  279.   /** @var string */
  280.   private $status;
  281.   /** @var Order */
  282.   private $Order;
  283.   /**
  284.    * OrderStateMachineContext constructor.
  285.    *
  286.    * @param string $status
  287.    * @param Order $Order
  288.    */
  289.   public function __construct($statusOrder $Order) {
  290.     $this->status $status;
  291.     $this->Order $Order;
  292.   }
  293.   /**
  294.    * @return string
  295.    */
  296.   public function getStatus() {
  297.     return $this->status;
  298.   }
  299.   /**
  300.    * @param string $status
  301.    */
  302.   public function setStatus($status) {
  303.     $this->status $status;
  304.   }
  305.   /**
  306.    * @return Order
  307.    */
  308.   public function getOrder() {
  309.     return $this->Order;
  310.   }
  311.   // order_state_machine.php の marking_store => property は、デフォルト値である marking を使用するよう強く推奨されている.
  312.   // EC-CUBE4.1 までは status を指定していたが、 Symfony5 よりエラーになるためエイリアスを作成して対応する.
  313.   /**
  314.    * Alias of getStatus()
  315.    */
  316.   public function getMarking(): string {
  317.     return $this->getStatus();
  318.   }
  319.   /**
  320.    * Alias of setStatus()
  321.    */
  322.   public function setMarking(string $status): void {
  323.     $this->setStatus($status);
  324.   }
  325. }