From 7b1c5676cca294b1f0cbe3427ba6602a74f02c20 Mon Sep 17 00:00:00 2001
From: Jonathan Sacksick <jonathan.sacksick@gmail.com>
Date: Mon, 14 Apr 2025 18:27:21 +0300
Subject: [PATCH 1/2] Issue #2931044 by megachriz, istavros, goz, s.messaris,
 jsacksick: Trying to get checkout steps of an order outside of checkout pages
 throws an error.

---
 modules/checkout/src/CheckoutOrderManager.php |  3 +
 .../CheckoutFlow/CheckoutFlowBase.php         |  8 +++
 .../CheckoutFlow/CheckoutFlowInterface.php    |  9 +++
 .../src/Kernel/CheckoutOrderManagerTest.php   | 58 ++++++++++++++++---
 4 files changed, 71 insertions(+), 7 deletions(-)

diff --git a/modules/checkout/src/CheckoutOrderManager.php b/modules/checkout/src/CheckoutOrderManager.php
index 766d1f942..5217b40c7 100644
--- a/modules/checkout/src/CheckoutOrderManager.php
+++ b/modules/checkout/src/CheckoutOrderManager.php
@@ -28,6 +28,9 @@ class CheckoutOrderManager implements CheckoutOrderManagerInterface {
       $order->save();
     }
 
+    // Set the order on the checkout flow's checkout flow plugin.
+    $order->get('checkout_flow')->entity->getPlugin()->setOrder($order);
+
     return $order->get('checkout_flow')->entity;
   }
 
diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowBase.php b/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowBase.php
index 96bca7e7d..77ec52ca6 100644
--- a/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowBase.php
+++ b/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowBase.php
@@ -15,6 +15,7 @@ use Drupal\Core\Url;
 use Drupal\commerce\AjaxFormTrait;
 use Drupal\commerce\Response\NeedsRedirectException;
 use Drupal\commerce_checkout\Event\CheckoutEvents;
+use Drupal\commerce_order\Entity\OrderInterface;
 use Drupal\commerce_order\Event\OrderEvent;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -160,6 +161,13 @@ abstract class CheckoutFlowBase extends PluginBase implements CheckoutFlowInterf
     }
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function setOrder(OrderInterface $order) {
+    $this->order = $order;
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowInterface.php b/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowInterface.php
index 3c1b0416e..d70261f4a 100644
--- a/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowInterface.php
+++ b/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowInterface.php
@@ -9,6 +9,7 @@ use Drupal\Component\Plugin\PluginInspectionInterface;
 use Drupal\Core\Form\BaseFormIdInterface;
 use Drupal\Core\Form\FormInterface;
 use Drupal\Core\Plugin\PluginFormInterface;
+use Drupal\commerce_order\Entity\OrderInterface;
 
 /**
  * Places an order through a series of steps.
@@ -19,6 +20,14 @@ use Drupal\Core\Plugin\PluginFormInterface;
  */
 interface CheckoutFlowInterface extends FormInterface, BaseFormIdInterface, ConfigurableInterface, DependentPluginInterface, PluginFormInterface, PluginInspectionInterface, DerivativeInspectionInterface {
 
+  /**
+   * Sets the current order.
+   *
+   * @param \Drupal\commerce_order\Entity\OrderInterface $order
+   *   The order.
+   */
+  public function setOrder(OrderInterface $order);
+
   /**
    * Gets the current order.
    *
diff --git a/modules/checkout/tests/src/Kernel/CheckoutOrderManagerTest.php b/modules/checkout/tests/src/Kernel/CheckoutOrderManagerTest.php
index 2dd2ed11c..44167a77e 100644
--- a/modules/checkout/tests/src/Kernel/CheckoutOrderManagerTest.php
+++ b/modules/checkout/tests/src/Kernel/CheckoutOrderManagerTest.php
@@ -51,21 +51,25 @@ class CheckoutOrderManagerTest extends OrderKernelTestBase {
     $this->installConfig('commerce_checkout');
 
     $user = $this->createUser();
-    $order = Order::create([
+    $this->order = Order::create([
       'type' => 'default',
       'mail' => $user->getEmail(),
       'uid' => $user->id(),
       'store_id' => $this->store->id(),
     ]);
-    $order->save();
-    $this->order = $order;
+    $this->order->save();
 
     $this->checkoutOrderManager = $this->container->get('commerce_checkout.checkout_order_manager');
+  }
 
-    // Fake a request so that the current_route_match works.
-    // @todo Remove this when CheckoutFlowBase stops using the route match.
+  /**
+   * Fakes a request so that the current_route_match works.
+   *
+   * @todo Remove this when CheckoutFlowBase stops using the route match.
+   */
+  protected function setupRequestWithOrderParameter() {
     $url = Url::fromRoute('commerce_checkout.form', [
-      'commerce_order' => $order->id(),
+      'commerce_order' => $this->order->id(),
     ]);
     $route_provider = $this->container->get('router.route_provider');
     $route = $route_provider->getRouteByName($url->getRouteName());
@@ -73,7 +77,7 @@ class CheckoutOrderManagerTest extends OrderKernelTestBase {
     $request->setSession(new Session(new MockArraySessionStorage()));
     $request->attributes->add([
       RouteObjectInterface::ROUTE_OBJECT => $route,
-      'commerce_order' => $order,
+      'commerce_order' => $this->order,
     ]);
     $this->container->get('request_stack')->push($request);
   }
@@ -82,6 +86,8 @@ class CheckoutOrderManagerTest extends OrderKernelTestBase {
    * Tests getting the order's checkout flow.
    */
   public function testGetCheckoutFlow() {
+    $this->setupRequestWithOrderParameter();
+
     $checkout_flow = $this->checkoutOrderManager->getCheckoutFlow($this->order);
     $this->assertInstanceOf(CheckoutFlow::class, $checkout_flow);
     $this->assertEquals('default', $checkout_flow->id());
@@ -96,6 +102,8 @@ class CheckoutOrderManagerTest extends OrderKernelTestBase {
    * Tests getting the order's checkout step ID.
    */
   public function testGetCheckoutStepId() {
+    $this->setupRequestWithOrderParameter();
+
     // Empty requested step ID when no checkout step was set.
     $step_id = $this->checkoutOrderManager->getCheckoutStepId($this->order);
     $this->assertEquals('login', $step_id);
@@ -137,4 +145,40 @@ class CheckoutOrderManagerTest extends OrderKernelTestBase {
 
   }
 
+  /**
+   * Tests getting checkout's visible steps.
+   */
+  public function testGetVisibleSteps() {
+    /** @var \Drupal\commerce_checkout\Entity\CheckoutFlowInterface $checkout_flow */
+    $checkout_flow = $this->checkoutOrderManager->getCheckoutFlow($this->order);
+
+    /** @var \Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowInterface $checkout_flow_plugin */
+    $checkout_flow_plugin = $checkout_flow->getPlugin();
+
+    /** @var array $steps */
+    $steps = $checkout_flow_plugin->getVisibleSteps();
+
+    $expected_steps = [
+      'login',
+      'order_information',
+      'review',
+      'complete',
+    ];
+    $this->assertEquals($expected_steps, array_keys($steps));
+  }
+
+  /**
+   * Tests getting the order from the checkout flow plugin.
+   */
+  public function testGetOrderFromCheckoutPane() {
+    /** @var \Drupal\commerce_checkout\Entity\CheckoutFlowInterface $checkout_flow */
+    $checkout_flow = $this->checkoutOrderManager->getCheckoutFlow($this->order);
+
+    /** @var \Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowInterface $checkout_flow_plugin */
+    $checkout_flow_plugin = $checkout_flow->getPlugin();
+
+    // Assert that the checkout flow plugin contains the order.
+    $this->assertSame($this->order, $checkout_flow_plugin->getOrder());
+  }
+
 }
-- 
GitLab


From 8e0c831db5e6d3d37ceba63f0a94d09325fae343 Mon Sep 17 00:00:00 2001
From: Jonathan Sacksick <jonathan.sacksick@gmail.com>
Date: Mon, 14 Apr 2025 18:35:16 +0300
Subject: [PATCH 2/2] Return  from setOrder().

---
 .../src/Plugin/Commerce/CheckoutFlow/CheckoutFlowBase.php     | 3 ++-
 .../Plugin/Commerce/CheckoutFlow/CheckoutFlowInterface.php    | 4 +++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowBase.php b/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowBase.php
index 77ec52ca6..059d1febe 100644
--- a/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowBase.php
+++ b/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowBase.php
@@ -164,8 +164,9 @@ abstract class CheckoutFlowBase extends PluginBase implements CheckoutFlowInterf
   /**
    * {@inheritdoc}
    */
-  public function setOrder(OrderInterface $order) {
+  public function setOrder(OrderInterface $order): static {
     $this->order = $order;
+    return $this;
   }
 
   /**
diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowInterface.php b/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowInterface.php
index d70261f4a..33b85870a 100644
--- a/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowInterface.php
+++ b/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowInterface.php
@@ -25,8 +25,10 @@ interface CheckoutFlowInterface extends FormInterface, BaseFormIdInterface, Conf
    *
    * @param \Drupal\commerce_order\Entity\OrderInterface $order
    *   The order.
+   *
+   * @return $this
    */
-  public function setOrder(OrderInterface $order);
+  public function setOrder(OrderInterface $order): static;
 
   /**
    * Gets the current order.
-- 
GitLab